1use crate::prelude::*;
2
3#[derive(Clone, Debug, PartialEq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize))]
42pub struct DeltaEncoder {
43 last_values: Vec<f32>,
44 threshold: f32,
45}
46
47impl DeltaEncoder {
48 pub fn new(threshold: f32, num_channels: usize) -> Self {
52 Self::try_new(threshold, num_channels).expect("invalid DeltaEncoder configuration")
53 }
54
55 pub fn try_new(threshold: f32, num_channels: usize) -> Result<Self, EncoderError> {
60 crate::error::validate_non_negative_finite("threshold", threshold)?;
61 crate::error::validate_channel_count(num_channels)?;
62 Ok(Self {
63 last_values: vec![0.0; num_channels],
64 threshold,
65 })
66 }
67
68 fn encode_with_threshold_scale(
69 &mut self,
70 input: &[f32],
71 threshold_scale: f32,
72 ) -> EncodedOutput {
73 let mut output = EncodedOutput::new();
74 let effective_threshold = (self.threshold * threshold_scale).max(0.0);
75
76 for (i, &value) in input.iter().enumerate() {
77 if i >= self.last_values.len() {
78 break;
79 }
80 let Ok(channel) = u16::try_from(i) else {
81 break;
83 };
84 let delta = (value - self.last_values[i]).abs();
85 if delta > effective_threshold {
86 output.spikes.push(SpikeEvent {
87 channel,
88 timestamp: 0,
89 polarity: value > self.last_values[i],
90 });
91 self.last_values[i] = value;
92 }
93 }
94 output
95 }
96
97 pub fn encode_with_modulators(
101 &mut self,
102 input: &[f32],
103 modulators: &NeuroModulators,
104 gain_curves: &NeuromodulatorGainCurves,
105 ) -> EncodedOutput {
106 <Self as ModulatedEncoder>::encode_with_modulators(self, input, modulators, gain_curves)
107 }
108
109 pub fn encode_step_with_modulators(
111 &mut self,
112 input: &[f32],
113 modulators: &NeuroModulators,
114 gain_curves: &NeuromodulatorGainCurves,
115 ) -> EncodedOutput {
116 <Self as ModulatedEncoder>::encode_step_with_modulators(
117 self,
118 input,
119 modulators,
120 gain_curves,
121 )
122 }
123}
124
125#[cfg(feature = "serde")]
126impl<'de> serde::Deserialize<'de> for DeltaEncoder {
127 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128 where
129 D: serde::Deserializer<'de>,
130 {
131 #[derive(serde::Deserialize)]
132 struct Helper {
133 last_values: Vec<f32>,
134 threshold: f32,
135 }
136 let helper = Helper::deserialize(deserializer)?;
137 let mut encoder = Self::try_new(helper.threshold, helper.last_values.len())
138 .map_err(serde::de::Error::custom)?;
139 if helper.last_values.iter().any(|value| !value.is_finite()) {
140 return Err(serde::de::Error::custom("last_values must be finite"));
141 }
142 encoder.last_values = helper.last_values;
143 Ok(encoder)
144 }
145}
146
147impl Encoder for DeltaEncoder {
148 fn encode(&mut self, input: &[f32]) -> EncodedOutput {
149 self.encode_with_threshold_scale(input, 1.0)
150 }
151
152 fn encode_step(&mut self, input: &[f32]) -> EncodedOutput {
153 let safe_input = if input.len() > self.last_values.len() {
154 &input[..self.last_values.len()]
155 } else {
156 input
157 };
158 self.encode(safe_input)
159 }
160
161 fn reset(&mut self) {
162 for val in self.last_values.iter_mut() {
163 *val = 0.0;
164 }
165 }
166}
167
168impl ModulatedEncoder for DeltaEncoder {
169 fn encode_with_gains(&mut self, input: &[f32], gains: EncodingGains) -> EncodedOutput {
170 self.encode_with_threshold_scale(input, gains.sanitize().threshold_scale)
171 }
172}
173
174pub fn encode_deltas_to_spikes(deltas: &[f32], threshold: f32) -> Vec<bool> {
179 deltas.iter().map(|&d| d.abs() > threshold).collect()
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_delta_encoder() {
188 let mut encoder = DeltaEncoder::new(2.0, 1);
189 let output = encoder.encode(&[1.0]); assert!(output.spikes.is_empty());
191
192 let output = encoder.encode(&[3.5]); assert!(!output.spikes.is_empty());
194 assert!(output.spikes[0].polarity);
195
196 let output = encoder.encode(&[4.0]); assert!(output.spikes.is_empty());
198
199 let output = encoder.encode(&[1.0]); assert!(!output.spikes.is_empty());
201 assert!(!output.spikes[0].polarity);
202 }
203
204 #[test]
205 fn test_delta_encoder_encode_step() {
206 let mut encoder = DeltaEncoder::new(2.0, 2);
207 let output = encoder.encode_step(&[3.0, 3.0, 3.0]); assert_eq!(output.spikes.len(), 2);
209 }
210
211 #[test]
212 fn test_delta_encoder_multi_channel_reset() {
213 let mut encoder = DeltaEncoder::new(1.0, 2);
214 encoder.encode(&[2.0, 2.0]);
215 assert_eq!(encoder.last_values, vec![2.0, 2.0]);
216 encoder.reset();
217 assert_eq!(encoder.last_values, vec![0.0, 0.0]);
218 }
219
220 #[test]
221 fn test_delta_encoder_empty_input() {
222 let mut encoder = DeltaEncoder::new(1.0, 5);
223 let output = encoder.encode(&[]);
224 assert!(output.spikes.is_empty());
225 }
226
227 #[test]
228 fn test_encode_deltas_to_spikes() {
229 let deltas = [0.1, 0.5, -0.8, 1.2];
230 let threshold = 0.7;
231 let spikes = encode_deltas_to_spikes(&deltas, threshold);
232 assert_eq!(spikes, vec![false, false, true, true]);
233 }
234
235 #[test]
236 fn test_delta_encoder_modulators_reduce_threshold() {
237 let mut encoder = DeltaEncoder::new(1.0, 1);
238 let modulators = NeuroModulators {
239 dopamine: 1.0,
240 ..Default::default()
241 };
242 let gain_curves = NeuromodulatorGainCurves {
243 dopamine: ModulatorGainCurves {
244 threshold: Some(GainCurve::new((0.0, 1.0), (1.0, 0.5))),
245 ..Default::default()
246 },
247 ..Default::default()
248 };
249
250 assert!(encoder.encode(&[0.75]).spikes.is_empty());
251 encoder.reset();
252
253 let modulated = encoder.encode_with_modulators(&[0.75], &modulators, &gain_curves);
254 assert_eq!(modulated.spikes.len(), 1);
255 }
256
257 #[test]
258 fn test_delta_encoder_encode_step_with_modulators() {
259 let mut encoder = DeltaEncoder::new(1.0, 1);
260 let modulators = NeuroModulators {
261 dopamine: 1.0,
262 ..Default::default()
263 };
264 let gain_curves = NeuromodulatorGainCurves {
265 dopamine: ModulatorGainCurves {
266 threshold: Some(GainCurve::new((0.0, 1.0), (1.0, 0.5))),
267 ..Default::default()
268 },
269 ..Default::default()
270 };
271
272 assert!(
273 encoder
274 .encode_step_with_modulators(&[0.0], &modulators, &gain_curves)
275 .spikes
276 .is_empty()
277 );
278 let modulated = encoder.encode_step_with_modulators(&[0.75], &modulators, &gain_curves);
279 assert_eq!(modulated.spikes.len(), 1);
280 }
281
282 #[test]
283 fn test_delta_encoder_step_shorter_input() {
284 let mut encoder = DeltaEncoder::new(1.0, 2);
285 let output = encoder.encode_step(&[2.0]);
286 assert_eq!(output.spikes.len(), 1);
287 }
288
289 #[test]
290 fn test_delta_encoder_truncates_excess_channels() {
291 let mut encoder = DeltaEncoder::new(1.0, 1);
292 let output = encoder.encode(&[2.0, 3.0]);
293 assert_eq!(output.spikes.len(), 1);
294 }
295
296 #[test]
297 fn test_delta_encoder_zero_threshold_scale_spikes_on_any_change() {
298 let mut encoder = DeltaEncoder::new(1.0, 1);
299 encoder.encode(&[0.0]);
300 let output = encoder.encode_with_threshold_scale(&[0.01], 0.0);
301 assert_eq!(output.spikes.len(), 1);
302 }
303 #[test]
304 fn test_delta_encoder_zero_threshold_spikes_on_any_change() {
305 let mut encoder = DeltaEncoder::new(0.0, 1);
306 encoder.encode(&[0.0]);
307 let output = encoder.encode(&[0.01]);
308 assert_eq!(output.spikes.len(), 1);
309 let quiet = encoder.encode(&[0.01]);
310 assert!(quiet.spikes.is_empty());
311 }
312
313 #[test]
314 fn test_delta_encoder_try_new_validation() {
315 assert!(DeltaEncoder::try_new(0.0, 1).is_ok());
316 assert_eq!(
317 DeltaEncoder::try_new(-1.0, 1).err(),
318 Some(EncoderError::NonNegativeFinite {
319 parameter: "threshold"
320 })
321 );
322 assert_eq!(
323 DeltaEncoder::try_new(f32::NAN, 1).err(),
324 Some(EncoderError::NonNegativeFinite {
325 parameter: "threshold"
326 })
327 );
328 assert_eq!(
329 DeltaEncoder::try_new(1.0, u16::MAX as usize + 2).err(),
330 Some(EncoderError::NumChannelsTooLarge)
331 );
332 }
333}