1#[cfg(feature = "ppl")]
7use fugue::{addr, ChoiceValue, Trace};
8use rand::Rng;
9use serde::{Deserialize, Serialize};
10
11use crate::error::GenomeError;
12use crate::genome::bounds::MultiBounds;
13use crate::genome::traits::{EvolutionaryGenome, RealValuedGenome};
14
15#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
20pub struct RealVector {
21 genes: Vec<f64>,
23}
24
25impl RealVector {
26 pub fn new(genes: Vec<f64>) -> Self {
28 Self { genes }
29 }
30
31 pub fn zeros(dimension: usize) -> Self {
33 Self {
34 genes: vec![0.0; dimension],
35 }
36 }
37
38 pub fn filled(dimension: usize, value: f64) -> Self {
40 Self {
41 genes: vec![value; dimension],
42 }
43 }
44
45 pub fn collect_from<I: IntoIterator<Item = f64>>(iter: I) -> Self {
47 Self {
48 genes: iter.into_iter().collect(),
49 }
50 }
51
52 pub fn into_inner(self) -> Vec<f64> {
54 self.genes
55 }
56
57 pub fn as_vec(&self) -> &Vec<f64> {
59 &self.genes
60 }
61
62 pub fn norm(&self) -> f64 {
64 self.genes.iter().map(|x| x * x).sum::<f64>().sqrt()
65 }
66
67 pub fn norm_squared(&self) -> f64 {
69 self.genes.iter().map(|x| x * x).sum::<f64>()
70 }
71
72 pub fn add(&self, other: &Self) -> Result<Self, GenomeError> {
74 if self.genes.len() != other.genes.len() {
75 return Err(GenomeError::DimensionMismatch {
76 expected: self.genes.len(),
77 actual: other.genes.len(),
78 });
79 }
80 Ok(Self {
81 genes: self
82 .genes
83 .iter()
84 .zip(other.genes.iter())
85 .map(|(a, b)| a + b)
86 .collect(),
87 })
88 }
89
90 pub fn sub(&self, other: &Self) -> Result<Self, GenomeError> {
92 if self.genes.len() != other.genes.len() {
93 return Err(GenomeError::DimensionMismatch {
94 expected: self.genes.len(),
95 actual: other.genes.len(),
96 });
97 }
98 Ok(Self {
99 genes: self
100 .genes
101 .iter()
102 .zip(other.genes.iter())
103 .map(|(a, b)| a - b)
104 .collect(),
105 })
106 }
107
108 pub fn scale(&self, scalar: f64) -> Self {
110 Self {
111 genes: self.genes.iter().map(|x| x * scalar).collect(),
112 }
113 }
114}
115
116impl EvolutionaryGenome for RealVector {
117 type Allele = f64;
118 type Phenotype = Vec<f64>;
119
120 fn decode(&self) -> Self::Phenotype {
121 self.genes.clone()
122 }
123
124 fn dimension(&self) -> usize {
125 self.genes.len()
126 }
127
128 fn generate<R: Rng>(rng: &mut R, bounds: &MultiBounds) -> Self {
129 let genes = bounds
130 .bounds
131 .iter()
132 .map(|b| rng.gen_range(b.min..=b.max))
133 .collect();
134 Self { genes }
135 }
136
137 fn as_slice(&self) -> Option<&[f64]> {
138 Some(&self.genes)
139 }
140
141 fn as_mut_slice(&mut self) -> Option<&mut [f64]> {
142 Some(&mut self.genes)
143 }
144
145 fn distance(&self, other: &Self) -> f64 {
146 self.try_distance(other).unwrap_or_else(|e| {
147 panic!("RealVector::distance: {e}; use try_distance for a fallible comparison")
148 })
149 }
150
151 fn try_distance(&self, other: &Self) -> Result<f64, GenomeError> {
152 if self.genes.len() != other.genes.len() {
153 return Err(GenomeError::DimensionMismatch {
154 expected: self.genes.len(),
155 actual: other.genes.len(),
156 });
157 }
158 Ok(self
159 .genes
160 .iter()
161 .zip(other.genes.iter())
162 .map(|(a, b)| (a - b).powi(2))
163 .sum::<f64>()
164 .sqrt())
165 }
166}
167
168#[cfg(feature = "ppl")]
169impl crate::genome::trace_genome::TraceGenome for RealVector {
170 fn to_trace(&self) -> Trace {
174 let mut trace = Trace::default();
175 for (i, &gene) in self.genes.iter().enumerate() {
176 trace.insert_choice(addr!("gene", i), ChoiceValue::F64(gene), 0.0);
177 }
178 trace
179 }
180
181 fn from_trace(trace: &Trace) -> Result<Self, GenomeError> {
189 let mut genes = Vec::new();
190 let mut i = 0;
191 loop {
192 match trace.choices.get(&addr!("gene", i)) {
193 None => break,
195 Some(choice) => match choice.value.as_f64() {
196 Some(val) => {
197 genes.push(val);
198 i += 1;
199 }
200 None => {
202 return Err(GenomeError::TypeMismatch {
203 address: format!("gene#{i}"),
204 expected: "f64".to_string(),
205 actual: choice.value.type_name().to_string(),
206 });
207 }
208 },
209 }
210 }
211 if genes.is_empty() {
212 return Err(GenomeError::InvalidStructure(
213 "No genes found in trace".to_string(),
214 ));
215 }
216 Ok(Self { genes })
217 }
218}
219
220impl RealValuedGenome for RealVector {
221 fn genes(&self) -> &[f64] {
222 &self.genes
223 }
224
225 fn genes_mut(&mut self) -> &mut [f64] {
226 &mut self.genes
227 }
228
229 fn from_genes(genes: Vec<f64>) -> Result<Self, GenomeError> {
230 Ok(Self { genes })
231 }
232}
233
234impl std::ops::Index<usize> for RealVector {
235 type Output = f64;
236
237 fn index(&self, index: usize) -> &Self::Output {
238 &self.genes[index]
239 }
240}
241
242impl std::ops::IndexMut<usize> for RealVector {
243 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
244 &mut self.genes[index]
245 }
246}
247
248impl From<Vec<f64>> for RealVector {
249 fn from(genes: Vec<f64>) -> Self {
250 Self { genes }
251 }
252}
253
254impl From<RealVector> for Vec<f64> {
255 fn from(genome: RealVector) -> Self {
256 genome.genes
257 }
258}
259
260impl<const N: usize> From<[f64; N]> for RealVector {
261 fn from(arr: [f64; N]) -> Self {
262 Self {
263 genes: arr.to_vec(),
264 }
265 }
266}
267
268impl IntoIterator for RealVector {
269 type Item = f64;
270 type IntoIter = std::vec::IntoIter<f64>;
271
272 fn into_iter(self) -> Self::IntoIter {
273 self.genes.into_iter()
274 }
275}
276
277impl<'a> IntoIterator for &'a RealVector {
278 type Item = &'a f64;
279 type IntoIter = std::slice::Iter<'a, f64>;
280
281 fn into_iter(self) -> Self::IntoIter {
282 self.genes.iter()
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use approx::assert_relative_eq;
290 #[cfg(feature = "ppl")]
291 use fugue::addr;
292
293 #[test]
294 fn test_real_vector_new() {
295 let v = RealVector::new(vec![1.0, 2.0, 3.0]);
296 assert_eq!(v.dimension(), 3);
297 assert_eq!(v.genes(), &[1.0, 2.0, 3.0]);
298 }
299
300 #[test]
301 fn test_real_vector_zeros() {
302 let v = RealVector::zeros(5);
303 assert_eq!(v.dimension(), 5);
304 assert!(v.genes().iter().all(|&x| x == 0.0));
305 }
306
307 #[test]
308 fn test_real_vector_filled() {
309 let v = RealVector::filled(3, 42.0);
310 assert_eq!(v.genes(), &[42.0, 42.0, 42.0]);
311 }
312
313 #[test]
314 fn test_real_vector_from_array() {
315 let v: RealVector = [1.0, 2.0, 3.0].into();
316 assert_eq!(v.genes(), &[1.0, 2.0, 3.0]);
317 }
318
319 #[test]
320 fn test_real_vector_decode() {
321 let v = RealVector::new(vec![1.0, 2.0, 3.0]);
322 let phenotype = v.decode();
323 assert_eq!(phenotype, vec![1.0, 2.0, 3.0]);
324 }
325
326 #[test]
327 fn test_real_vector_generate() {
328 let mut rng = rand::thread_rng();
329 let bounds = MultiBounds::symmetric(5.0, 10);
330 let v = RealVector::generate(&mut rng, &bounds);
331
332 assert_eq!(v.dimension(), 10);
333 for gene in v.genes() {
334 assert!(*gene >= -5.0 && *gene <= 5.0);
335 }
336 }
337
338 #[test]
339 fn test_real_vector_norm() {
340 let v = RealVector::new(vec![3.0, 4.0]);
341 assert_relative_eq!(v.norm(), 5.0);
342 assert_relative_eq!(v.norm_squared(), 25.0);
343 }
344
345 #[test]
346 fn test_real_vector_distance() {
347 let v1 = RealVector::new(vec![0.0, 0.0]);
348 let v2 = RealVector::new(vec![3.0, 4.0]);
349 assert_relative_eq!(v1.distance(&v2), 5.0);
350 }
351
352 #[test]
353 fn test_real_vector_add() {
354 let v1 = RealVector::new(vec![1.0, 2.0, 3.0]);
355 let v2 = RealVector::new(vec![4.0, 5.0, 6.0]);
356 let sum = v1.add(&v2).unwrap();
357 assert_eq!(sum.genes(), &[5.0, 7.0, 9.0]);
358 }
359
360 #[test]
361 fn test_real_vector_add_dimension_mismatch() {
362 let v1 = RealVector::new(vec![1.0, 2.0]);
363 let v2 = RealVector::new(vec![1.0, 2.0, 3.0]);
364 let result = v1.add(&v2);
365 assert!(result.is_err());
366 assert!(matches!(
367 result.unwrap_err(),
368 GenomeError::DimensionMismatch {
369 expected: 2,
370 actual: 3
371 }
372 ));
373 }
374
375 #[test]
376 fn test_real_vector_sub() {
377 let v1 = RealVector::new(vec![5.0, 7.0, 9.0]);
378 let v2 = RealVector::new(vec![1.0, 2.0, 3.0]);
379 let diff = v1.sub(&v2).unwrap();
380 assert_eq!(diff.genes(), &[4.0, 5.0, 6.0]);
381 }
382
383 #[test]
384 fn test_real_vector_scale() {
385 let v = RealVector::new(vec![1.0, 2.0, 3.0]);
386 let scaled = v.scale(2.0);
387 assert_eq!(scaled.genes(), &[2.0, 4.0, 6.0]);
388 }
389
390 #[test]
391 fn test_real_vector_indexing() {
392 let mut v = RealVector::new(vec![1.0, 2.0, 3.0]);
393 assert_eq!(v[0], 1.0);
394 assert_eq!(v[1], 2.0);
395 assert_eq!(v[2], 3.0);
396
397 v[1] = 42.0;
398 assert_eq!(v[1], 42.0);
399 }
400
401 #[test]
402 fn test_real_vector_apply_bounds() {
403 let mut v = RealVector::new(vec![-10.0, 0.0, 10.0]);
404 let bounds = MultiBounds::symmetric(5.0, 3);
405 v.apply_bounds(&bounds);
406 assert_eq!(v.genes(), &[-5.0, 0.0, 5.0]);
407 }
408
409 #[test]
410 fn test_real_vector_iteration() {
411 let v = RealVector::new(vec![1.0, 2.0, 3.0]);
412 let sum: f64 = v.into_iter().sum();
413 assert_relative_eq!(sum, 6.0);
414 }
415
416 #[test]
417 fn test_real_vector_into_inner() {
418 let v = RealVector::new(vec![1.0, 2.0, 3.0]);
419 let inner: Vec<f64> = v.into_inner();
420 assert_eq!(inner, vec![1.0, 2.0, 3.0]);
421 }
422
423 #[test]
424 fn test_real_vector_serialization() {
425 let v = RealVector::new(vec![1.0, 2.0, 3.0]);
426 let serialized = serde_json::to_string(&v).unwrap();
427 let deserialized: RealVector = serde_json::from_str(&serialized).unwrap();
428 assert_eq!(v, deserialized);
429 }
430
431 #[test]
432 #[cfg(feature = "ppl")]
433 fn test_real_vector_to_trace() {
434 use crate::genome::trace_genome::TraceGenome;
435 let v = RealVector::new(vec![1.5, 2.5, 3.5]);
436 let trace = v.to_trace();
437
438 assert_eq!(trace.get_f64(&addr!("gene", 0)), Some(1.5));
439 assert_eq!(trace.get_f64(&addr!("gene", 1)), Some(2.5));
440 assert_eq!(trace.get_f64(&addr!("gene", 2)), Some(3.5));
441 assert_eq!(trace.get_f64(&addr!("gene", 3)), None);
442 }
443
444 #[test]
445 #[cfg(feature = "ppl")]
446 fn test_real_vector_from_trace() {
447 use crate::genome::trace_genome::TraceGenome;
448 let mut trace = Trace::default();
449 trace.insert_choice(addr!("gene", 0), ChoiceValue::F64(1.0), 0.0);
450 trace.insert_choice(addr!("gene", 1), ChoiceValue::F64(2.0), 0.0);
451 trace.insert_choice(addr!("gene", 2), ChoiceValue::F64(3.0), 0.0);
452
453 let v = RealVector::from_trace(&trace).unwrap();
454 assert_eq!(v.genes(), &[1.0, 2.0, 3.0]);
455 }
456
457 #[test]
458 #[cfg(feature = "ppl")]
459 fn test_real_vector_trace_roundtrip() {
460 use crate::genome::trace_genome::TraceGenome;
461 let original = RealVector::new(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
462 let trace = original.to_trace();
463 let recovered = RealVector::from_trace(&trace).unwrap();
464 assert_eq!(original, recovered);
465 }
466
467 #[test]
468 #[cfg(feature = "ppl")]
469 fn test_real_vector_from_trace_empty() {
470 use crate::genome::trace_genome::TraceGenome;
471 let trace = Trace::default();
472 let result = RealVector::from_trace(&trace);
473 assert!(result.is_err());
474 }
475
476 #[test]
477 fn test_real_vector_try_distance_dimension_mismatch() {
478 let v1 = RealVector::new(vec![0.0, 0.0]);
481 let v2 = RealVector::new(vec![0.0, 0.0, 1_000_000.0]);
482 let result = v1.try_distance(&v2);
483 assert!(matches!(
484 result,
485 Err(GenomeError::DimensionMismatch {
486 expected: 2,
487 actual: 3
488 })
489 ));
490 }
491
492 #[test]
493 #[should_panic(expected = "Dimension mismatch")]
494 fn test_real_vector_distance_dimension_mismatch_panics() {
495 let v1 = RealVector::new(vec![0.0, 0.0]);
498 let v2 = RealVector::new(vec![0.0, 0.0, 1_000_000.0]);
499 let _ = v1.distance(&v2);
500 }
501
502 #[test]
503 #[cfg(feature = "ppl")]
504 fn test_real_vector_from_trace_type_mismatch() {
505 use crate::genome::trace_genome::TraceGenome;
508 let mut trace = Trace::default();
509 trace.insert_choice(addr!("gene", 0), ChoiceValue::F64(1.0), 0.0);
510 trace.insert_choice(addr!("gene", 1), ChoiceValue::Bool(true), 0.0); trace.insert_choice(addr!("gene", 2), ChoiceValue::F64(3.0), 0.0);
512
513 let result = RealVector::from_trace(&trace);
514 match result {
515 Err(GenomeError::TypeMismatch {
516 address,
517 expected,
518 actual,
519 }) => {
520 assert_eq!(address, "gene#1");
521 assert_eq!(expected, "f64");
522 assert_eq!(actual, "bool");
523 }
524 other => panic!("expected TypeMismatch, got {other:?}"),
525 }
526 }
527
528 #[test]
529 #[cfg(feature = "ppl")]
530 fn test_real_vector_from_trace_missing_stops_cleanly() {
531 use crate::genome::trace_genome::TraceGenome;
533 let mut trace = Trace::default();
534 trace.insert_choice(addr!("gene", 0), ChoiceValue::F64(1.0), 0.0);
535 trace.insert_choice(addr!("gene", 1), ChoiceValue::F64(2.0), 0.0);
536 let v = RealVector::from_trace(&trace).unwrap();
537 assert_eq!(v.genes(), &[1.0, 2.0]);
538 }
539}