pub struct Serialiser;Expand description
Serialiser — converts ProfileState ↔ key-value string map. Portable to any storage layer with no external deps.
Implementations§
Source§impl Serialiser
impl Serialiser
Sourcepub fn encode(state: &ProfileState) -> HashMap<String, String>
pub fn encode(state: &ProfileState) -> HashMap<String, String>
Encode ProfileState to flat string key-value map. Callers can JSON-encode this map with their own serialiser.
Examples found in repository?
examples/learning_simulation.rs (line 111)
12fn main() {
13 println!("=== aria-core: Learning Domain Simulation ===\n");
14
15 // --- Build engine ---
16 let mut engine = Engine::new(EngineConfig {
17 exploration_rate: 0.0, // deterministic for demo
18 alpha: 0.1, // faster skill movement so visible in 15 steps
19 });
20
21 // Register factors — caller's choice
22 engine.add_factor(Box::new(ChallengeFactor::new(0.2)));
23 engine.add_factor(Box::new(SpacingFactor::new(10))); // 10s interval for demo
24 engine.add_factor(Box::new(CoverageFactor));
25 engine.seed_rng(42);
26
27 // Register items — caller's curriculum
28 engine.add_items(vec![
29 // Math track
30 Item::new("counting", 0.05, "math"),
31 Item::new("addition", 0.15, "math"),
32 Item::new("multiplication", 0.30, "math"),
33 Item::new("fractions", 0.45, "math"),
34 Item::new("algebra_basics", 0.60, "math"),
35 Item::new("quadratics", 0.75, "math")
36 .with_prereqs(vec!["algebra_basics".into()]),
37 Item::new("calculus_intro", 0.90, "math")
38 .with_prereqs(vec!["quadratics".into()]),
39
40 // Science track (no prereqs — runs in parallel)
41 Item::new("sci_observation", 0.10, "science"),
42 Item::new("sci_hypothesis", 0.25, "science"),
43 Item::new("sci_experiment", 0.50, "science"),
44 Item::new("sci_analysis", 0.70, "science"),
45 Item::new("sci_research", 0.90, "science"),
46 ]).unwrap();
47
48 println!("Items registered: {}", engine.item_count());
49 println!("Factors registered: {}\n", engine.factor_count());
50
51 println!("{:<5} {:<22} {:<10} {:<8} {:<8} {:<10}",
52 "Step", "Item", "Category", "Success", "Effort", "Skill");
53 println!("{}", "-".repeat(70));
54
55 // --- Simulate 15 interactions ---
56 // Alternates easy successes and a few failures to show adaptation
57 let interactions: Vec<(bool, f32)> = vec![
58 (true, 0.8), // 1 hard success
59 (true, 0.3), // 2 easy success → optimism up
60 (true, 0.2), // 3 easy
61 (false, 0.9), // 4 failure → optimism eases
62 (true, 0.5), // 5
63 (true, 0.4), // 6
64 (true, 0.2), // 7 easy → optimism climbs
65 (true, 0.3), // 8
66 (false, 0.7), // 9 failure
67 (true, 0.5), // 10
68 (true, 0.4), // 11
69 (true, 0.3), // 12
70 (true, 0.2), // 13
71 (true, 0.5), // 14
72 (true, 0.3), // 15
73 ];
74
75 for (step, (success, effort)) in interactions.iter().enumerate() {
76 let item = engine.suggest("alice").unwrap();
77 let item_id = item.id().to_string();
78 let category = item.category().to_string();
79
80 engine.feedback("alice", &item_id, Signal::new(*success, *effort)).unwrap();
81
82 let state = engine.get_state("alice").unwrap();
83 println!("{:<5} {:<22} {:<10} {:<8} {:<8.2} {:<.4}",
84 step + 1,
85 item_id,
86 category,
87 if *success { "✓" } else { "✗" },
88 effort,
89 state.skill,
90 );
91 }
92
93 // --- Show final state ---
94 let state = engine.get_state("alice").unwrap();
95 println!("\n=== Final State ===");
96 println!("Skill: {:.4}", state.skill);
97 println!("Optimism bias: {:.4}", state.optimism_bias);
98 println!("Target: {:.4}", state.target());
99 println!("Interactions: {}", state.interaction_count);
100 println!("Resolved items: {:?}", state.resolved_set);
101
102 println!("\n=== Category Coverage ===");
103 let mut cats: Vec<_> = state.category_count.iter().collect();
104 cats.sort_by_key(|(k, _)| k.as_str());
105 for (cat, count) in cats {
106 println!(" {:<12} → {} interactions", cat, count);
107 }
108
109 // --- Serialise / deserialise round-trip ---
110 println!("\n=== Serialisation Round-Trip ===");
111 let encoded = Serialiser::encode(state);
112 println!("Encoded keys: {}", encoded.len());
113 let decoded = Serialiser::decode(&encoded).unwrap();
114 assert!((decoded.skill - state.skill).abs() < 1e-5, "skill mismatch after round-trip");
115 assert_eq!(decoded.interaction_count, state.interaction_count);
116 println!("Round-trip: ✓ skill={:.4} interactions={}", decoded.skill, decoded.interaction_count);
117
118 // --- Prereq demonstration ---
119 println!("\n=== Prerequisite Gating ===");
120 println!("calculus_intro requires: quadratics → algebra_basics");
121 let can_see_calculus = state.resolved_set.contains("quadratics");
122 println!("User resolved quadratics: {}", can_see_calculus);
123 println!("(calculus_intro will unlock once quadratics is resolved)");
124
125 println!("\n=== Custom Domain Example (ecommerce) ===");
126 demo_ecommerce();
127}Sourcepub fn decode(map: &HashMap<String, String>) -> Result<ProfileState, AriaError>
pub fn decode(map: &HashMap<String, String>) -> Result<ProfileState, AriaError>
Decode flat string map back to ProfileState.
Examples found in repository?
examples/learning_simulation.rs (line 113)
12fn main() {
13 println!("=== aria-core: Learning Domain Simulation ===\n");
14
15 // --- Build engine ---
16 let mut engine = Engine::new(EngineConfig {
17 exploration_rate: 0.0, // deterministic for demo
18 alpha: 0.1, // faster skill movement so visible in 15 steps
19 });
20
21 // Register factors — caller's choice
22 engine.add_factor(Box::new(ChallengeFactor::new(0.2)));
23 engine.add_factor(Box::new(SpacingFactor::new(10))); // 10s interval for demo
24 engine.add_factor(Box::new(CoverageFactor));
25 engine.seed_rng(42);
26
27 // Register items — caller's curriculum
28 engine.add_items(vec![
29 // Math track
30 Item::new("counting", 0.05, "math"),
31 Item::new("addition", 0.15, "math"),
32 Item::new("multiplication", 0.30, "math"),
33 Item::new("fractions", 0.45, "math"),
34 Item::new("algebra_basics", 0.60, "math"),
35 Item::new("quadratics", 0.75, "math")
36 .with_prereqs(vec!["algebra_basics".into()]),
37 Item::new("calculus_intro", 0.90, "math")
38 .with_prereqs(vec!["quadratics".into()]),
39
40 // Science track (no prereqs — runs in parallel)
41 Item::new("sci_observation", 0.10, "science"),
42 Item::new("sci_hypothesis", 0.25, "science"),
43 Item::new("sci_experiment", 0.50, "science"),
44 Item::new("sci_analysis", 0.70, "science"),
45 Item::new("sci_research", 0.90, "science"),
46 ]).unwrap();
47
48 println!("Items registered: {}", engine.item_count());
49 println!("Factors registered: {}\n", engine.factor_count());
50
51 println!("{:<5} {:<22} {:<10} {:<8} {:<8} {:<10}",
52 "Step", "Item", "Category", "Success", "Effort", "Skill");
53 println!("{}", "-".repeat(70));
54
55 // --- Simulate 15 interactions ---
56 // Alternates easy successes and a few failures to show adaptation
57 let interactions: Vec<(bool, f32)> = vec![
58 (true, 0.8), // 1 hard success
59 (true, 0.3), // 2 easy success → optimism up
60 (true, 0.2), // 3 easy
61 (false, 0.9), // 4 failure → optimism eases
62 (true, 0.5), // 5
63 (true, 0.4), // 6
64 (true, 0.2), // 7 easy → optimism climbs
65 (true, 0.3), // 8
66 (false, 0.7), // 9 failure
67 (true, 0.5), // 10
68 (true, 0.4), // 11
69 (true, 0.3), // 12
70 (true, 0.2), // 13
71 (true, 0.5), // 14
72 (true, 0.3), // 15
73 ];
74
75 for (step, (success, effort)) in interactions.iter().enumerate() {
76 let item = engine.suggest("alice").unwrap();
77 let item_id = item.id().to_string();
78 let category = item.category().to_string();
79
80 engine.feedback("alice", &item_id, Signal::new(*success, *effort)).unwrap();
81
82 let state = engine.get_state("alice").unwrap();
83 println!("{:<5} {:<22} {:<10} {:<8} {:<8.2} {:<.4}",
84 step + 1,
85 item_id,
86 category,
87 if *success { "✓" } else { "✗" },
88 effort,
89 state.skill,
90 );
91 }
92
93 // --- Show final state ---
94 let state = engine.get_state("alice").unwrap();
95 println!("\n=== Final State ===");
96 println!("Skill: {:.4}", state.skill);
97 println!("Optimism bias: {:.4}", state.optimism_bias);
98 println!("Target: {:.4}", state.target());
99 println!("Interactions: {}", state.interaction_count);
100 println!("Resolved items: {:?}", state.resolved_set);
101
102 println!("\n=== Category Coverage ===");
103 let mut cats: Vec<_> = state.category_count.iter().collect();
104 cats.sort_by_key(|(k, _)| k.as_str());
105 for (cat, count) in cats {
106 println!(" {:<12} → {} interactions", cat, count);
107 }
108
109 // --- Serialise / deserialise round-trip ---
110 println!("\n=== Serialisation Round-Trip ===");
111 let encoded = Serialiser::encode(state);
112 println!("Encoded keys: {}", encoded.len());
113 let decoded = Serialiser::decode(&encoded).unwrap();
114 assert!((decoded.skill - state.skill).abs() < 1e-5, "skill mismatch after round-trip");
115 assert_eq!(decoded.interaction_count, state.interaction_count);
116 println!("Round-trip: ✓ skill={:.4} interactions={}", decoded.skill, decoded.interaction_count);
117
118 // --- Prereq demonstration ---
119 println!("\n=== Prerequisite Gating ===");
120 println!("calculus_intro requires: quadratics → algebra_basics");
121 let can_see_calculus = state.resolved_set.contains("quadratics");
122 println!("User resolved quadratics: {}", can_see_calculus);
123 println!("(calculus_intro will unlock once quadratics is resolved)");
124
125 println!("\n=== Custom Domain Example (ecommerce) ===");
126 demo_ecommerce();
127}Auto Trait Implementations§
impl Freeze for Serialiser
impl RefUnwindSafe for Serialiser
impl Send for Serialiser
impl Sync for Serialiser
impl Unpin for Serialiser
impl UnsafeUnpin for Serialiser
impl UnwindSafe for Serialiser
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more