cortex_memory/
salience.rs1#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Salience {
9 pub recency: f32,
11 pub recurrence: f32,
13 pub reusability: f32,
15 pub validation: f32,
17 pub consequence: f32,
19 pub emotional_charge: f32,
21 pub identity_relevance: f32,
23 pub contradiction_penalty: f32,
25 pub use_count: u32,
27}
28
29impl Default for Salience {
30 fn default() -> Self {
31 Self {
32 recency: 0.0,
33 recurrence: 0.0,
34 reusability: 0.0,
35 validation: 0.0,
36 consequence: 0.0,
37 emotional_charge: 0.0,
38 identity_relevance: 0.0,
39 contradiction_penalty: 0.0,
40 use_count: 0,
41 }
42 }
43}
44
45#[must_use]
51pub fn brightness(salience: &Salience) -> f32 {
52 let recurrence = bounded(salience.recurrence);
53 let validation = bounded(salience.validation);
54 let unvalidated_use_penalty = if validation <= f32::EPSILON && salience.use_count > 5 {
55 ((salience.use_count - 5) as f32 * 0.01).min(0.10)
56 } else {
57 0.0
58 };
59
60 let score = 0.10
61 + 0.08 * bounded(salience.recency)
62 + 0.14 * bounded(salience.reusability)
63 + 0.30 * validation
64 + 0.14 * bounded(salience.consequence)
65 + 0.06 * bounded(salience.emotional_charge)
66 + 0.12 * bounded(salience.identity_relevance)
67 + 0.06 * recurrence * validation
68 - 0.35 * bounded(salience.contradiction_penalty)
69 - unvalidated_use_penalty;
70
71 bounded(score)
72}
73
74fn bounded(value: f32) -> f32 {
75 if value.is_nan() {
76 return 0.0;
77 }
78 value.clamp(0.0, 1.0)
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 fn baseline() -> Salience {
86 Salience {
87 recency: 0.4,
88 recurrence: 0.5,
89 reusability: 0.6,
90 validation: 0.0,
91 consequence: 0.4,
92 emotional_charge: 0.2,
93 identity_relevance: 0.3,
94 contradiction_penalty: 0.0,
95 use_count: 0,
96 }
97 }
98
99 #[test]
100 fn brightness_is_monotonic_in_validation() {
101 let mut previous = 0.0;
102 for step in 0..=20 {
103 let mut salience = baseline();
104 salience.validation = step as f32 / 20.0;
105 let current = brightness(&salience);
106 assert!(
107 current >= previous,
108 "brightness decreased from {previous} to {current} at validation step {step}"
109 );
110 previous = current;
111 }
112 }
113
114 #[test]
115 fn brightness_decreases_with_contradiction_penalty() {
116 let mut previous = 1.0;
117 for step in 0..=20 {
118 let mut salience = baseline();
119 salience.validation = 0.8;
120 salience.contradiction_penalty = step as f32 / 20.0;
121 let current = brightness(&salience);
122 assert!(
123 current <= previous,
124 "brightness increased from {previous} to {current} at penalty step {step}"
125 );
126 previous = current;
127 }
128 }
129
130 #[test]
131 fn repeated_use_without_validation_is_not_positive_uplift() {
132 let mut low_use = baseline();
133 low_use.use_count = 5;
134 let mut repeated = low_use;
135 repeated.use_count = 20;
136
137 assert!(brightness(&repeated) < brightness(&low_use));
138 }
139
140 #[test]
141 fn brightness_is_bounded_and_nan_safe() {
142 let salience = Salience {
143 recency: f32::NAN,
144 recurrence: 99.0,
145 reusability: 99.0,
146 validation: 99.0,
147 consequence: 99.0,
148 emotional_charge: 99.0,
149 identity_relevance: 99.0,
150 contradiction_penalty: -99.0,
151 use_count: 0,
152 };
153
154 let score = brightness(&salience);
155 assert!((0.0..=1.0).contains(&score));
156 }
157}