| #![cfg(feature = "developmental-evidence")] |
|
|
| use star::developmental::{ |
| compare_numeric_transition, EvidenceValidationPolicy, LearnedEvidence, |
| LearnedPayload, |
| }; |
|
|
| #[test] |
| fn infant_prediction_and_independent_observation_produce_known_residual() { |
| let prediction_raw = include_str!( |
| "fixtures/infant_numeric_transition_prediction_v1.json" |
| ) |
| .trim(); |
| let observation_raw = include_str!( |
| "fixtures/infant_numeric_state_observation_v1.json" |
| ) |
| .trim(); |
|
|
| let prediction_evidence: LearnedEvidence = serde_json::from_str(prediction_raw) |
| .expect("Infant numeric prediction fixture must deserialize"); |
| let observation_evidence: LearnedEvidence = serde_json::from_str(observation_raw) |
| .expect("independent numeric observation fixture must deserialize"); |
|
|
| prediction_evidence |
| .validate(&EvidenceValidationPolicy::replay(1_700_000_101)) |
| .expect("prediction fixture must pass replay validation"); |
| observation_evidence |
| .validate(&EvidenceValidationPolicy::replay(1_700_000_101)) |
| .expect("observation fixture must pass replay validation"); |
|
|
| assert!(prediction_evidence.timestamp < observation_evidence.timestamp); |
| assert_ne!(prediction_evidence.source_model, observation_evidence.source_model); |
| assert_ne!( |
| prediction_evidence.provenance.producer, |
| observation_evidence.provenance.producer |
| ); |
|
|
| let prediction = match &prediction_evidence.payload { |
| LearnedPayload::NumericTransitionPrediction(prediction) => prediction, |
| other => panic!("expected numeric transition prediction, got {other:?}"), |
| }; |
| let observation = match &observation_evidence.payload { |
| LearnedPayload::NumericStateObservation(observation) => observation, |
| other => panic!("expected numeric state observation, got {other:?}"), |
| }; |
|
|
| let residual = compare_numeric_transition(prediction, observation) |
| .expect("matching fixture pair must produce a residual"); |
|
|
| assert_eq!(residual.transition_id, "transition-numeric-0001"); |
| assert_eq!(residual.state_space, "synthetic.proprio.v1"); |
| assert_eq!(residual.dimension, 3); |
|
|
| let expected_signed_error = [0.1_f32, -0.2_f32, 0.3_f32]; |
| for (actual, expected) in residual |
| .signed_error |
| .iter() |
| .zip(expected_signed_error.iter()) |
| { |
| assert!((actual - expected).abs() < 1e-6); |
| } |
|
|
| assert!((residual.mean_absolute_error - 0.2).abs() < 1e-6); |
| assert!((residual.mean_squared_error - (0.14 / 3.0)).abs() < 1e-6); |
| assert!( |
| (residual.root_mean_squared_error - (0.14_f64 / 3.0).sqrt()).abs() |
| < 1e-6 |
| ); |
| assert!((residual.max_absolute_error - 0.3).abs() < 1e-6); |
| } |
|
|