evolution_ternary/
fitness.rs1#[derive(Clone, Debug)]
5pub struct Environment {
6 pub name: String,
7 pub weights: Vec<[f64; 3]>,
10}
11
12impl Environment {
13 pub fn simple(len: usize) -> Self {
15 Self {
16 name: "simple".into(),
17 weights: vec![[0.0, 1.0, 2.0]; len],
18 }
19 }
20
21 pub fn diagonal(len: usize) -> Self {
23 let weights: Vec<[f64; 3]> = (0..len)
24 .map(|i| {
25 let mut w = [0.0, 0.0, 0.0];
26 w[i % 3] = 2.0;
27 w[(i + 1) % 3] = 0.5;
29 w[(i + 2) % 3] = 0.5;
30 w
31 })
32 .collect();
33 Self { name: "diagonal".into(), weights }
34 }
35
36 pub fn zeros(len: usize) -> Self {
38 Self {
39 name: "zeros".into(),
40 weights: vec![[2.0, 0.5, 0.5]; len],
41 }
42 }
43
44 pub fn random_seeded(len: usize, seed: u64) -> Self {
46 use crate::population::SimpleRng;
47 let mut rng = SimpleRng::from_seed(seed);
48 let weights: Vec<[f64; 3]> = (0..len)
49 .map(|_| {
50 let mut w = [0.0; 3];
51 for v in &mut w {
52 *v = rng.next_f64();
53 }
54 w
55 })
56 .collect();
57 Self { name: format!("random-{}", seed), weights }
58 }
59
60 pub fn evaluate(&self, strategy: &[u8]) -> f64 {
62 assert_eq!(strategy.len(), self.weights.len(), "Strategy length must match environment");
63 strategy.iter().zip(self.weights.iter())
64 .map(|(&v, w)| w[v as usize])
65 .sum()
66 }
67}
68
69#[derive(Clone, Debug)]
71pub struct FitnessFunction {
72 pub environments: Vec<Environment>,
73 pub combine: String,
75}
76
77impl FitnessFunction {
78 pub fn single(env: Environment) -> Self {
80 Self { environments: vec![env], combine: "sum".into() }
81 }
82
83 pub fn multi_sum(envs: Vec<Environment>) -> Self {
85 Self { environments: envs, combine: "sum".into() }
86 }
87
88 pub fn multi_max(envs: Vec<Environment>) -> Self {
90 Self { environments: envs, combine: "max".into() }
91 }
92
93 pub fn evaluate(&self, strategy: &[u8]) -> f64 {
95 let scores: Vec<f64> = self.environments.iter()
96 .map(|e| e.evaluate(strategy))
97 .collect();
98 match self.combine.as_str() {
99 "sum" => scores.iter().sum(),
100 "max" => scores.iter().cloned().fold(f64::NEG_INFINITY, f64::max),
101 "avg" => scores.iter().sum::<f64>() / scores.len().max(1) as f64,
102 _ => scores.iter().sum(),
103 }
104 }
105}