1use clap::Args;
2use derivative::Derivative;
3use derive_builder::Builder;
4use rand::distributions::Standard;
5use rand::prelude::Distribution;
6use rand::Rng;
7use serde::{Deserialize, Serialize};
8use std::fmt::Debug;
9use tracing::trace;
10
11use crate::utils::random::generator;
12
13use super::engines::generate_engine::{Generate, GenerateEngine};
14use super::engines::mutate_engine::{Mutate, MutateEngine};
15use super::environment::State;
16use super::registers::Registers;
17use derive_more::Display;
18
19#[derive(Clone, Debug, PartialEq, Eq, Serialize, Copy, Deserialize)]
20pub enum Mode {
21 External,
22 Internal,
23}
24
25#[derive(Clone, Copy, Debug, Display, Serialize, PartialEq, Eq, Deserialize)]
26pub enum Op {
27 #[display(fmt = "+")]
28 Add,
29 #[display(fmt = "*")]
30 Mult,
31 #[display(fmt = "/")]
32 Divide,
33 #[display(fmt = "-")]
34 Sub,
35}
36
37impl Op {
38 pub fn apply(&self, a: f64, b: f64) -> f64 {
39 match *self {
40 Op::Add => a + b,
41 Op::Mult => a * b,
42 Op::Divide => a / b, Op::Sub => a - b,
44 }
45 }
46}
47
48impl Distribution<Op> for Standard {
49 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Op {
50 match rng.gen_range(0..=3) {
51 0 => Op::Add,
52 1 => Op::Mult,
53 2 => Op::Divide,
54 _ => Op::Sub,
55 }
56 }
57}
58
59impl Distribution<Mode> for Standard {
60 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mode {
61 match rng.gen_bool(0.5) {
62 false => Mode::External,
63 true => Mode::Internal,
64 }
65 }
66}
67
68#[derive(Clone, Derivative, Debug, Serialize, Args, PartialEq, Deserialize, Builder)]
69#[derivative(Copy)]
70pub struct InstructionGeneratorParameters {
71 #[arg(long, default_value = "1")]
72 #[builder(default = "1")]
73 pub n_extras: usize,
74 #[arg(long, default_value = "10.")]
75 #[builder(default = "10.")]
76 pub external_factor: f64,
77 #[arg(skip)]
78 pub n_actions: usize,
79 #[arg(skip)]
80 pub n_inputs: usize,
81}
82
83impl InstructionGeneratorParameters {
84 pub fn n_registers(&self) -> usize {
85 self.n_actions + self.n_extras
87 }
88}
89
90#[derive(Serialize, PartialEq, Debug, Deserialize, Derivative)]
91#[derivative(Copy, Clone)]
92pub struct Instruction {
93 src_idx: usize,
94 tgt_idx: usize,
95 mode: Mode,
96 op: Op,
97 external_factor: f64,
98}
99
100impl Generate<InstructionGeneratorParameters, Instruction> for GenerateEngine {
101 fn generate(using: InstructionGeneratorParameters) -> Instruction {
102 let src_idx = generator().gen_range(0..using.n_registers());
103
104 let mode = generator().gen();
105
106 let upper_bound_target_index = if mode == Mode::External {
107 using.n_inputs
108 } else {
109 using.n_registers()
110 };
111
112 let target_index = generator().gen_range(0..upper_bound_target_index);
113
114 let executable = generator().gen();
115
116 Instruction {
117 src_idx,
118 tgt_idx: target_index,
119 mode,
120 op: executable,
121 external_factor: using.external_factor,
122 }
123 }
124}
125
126impl Mutate<InstructionGeneratorParameters, Instruction> for MutateEngine {
127 fn mutate(instruction: &mut Instruction, using: InstructionGeneratorParameters) {
128 let mutated = GenerateEngine::generate(using);
129
130 let swap_target = generator().gen();
131 let swap_source = generator().gen();
132 let swap_exec = generator().gen();
133
134 if swap_target {
136 instruction.mode = mutated.mode;
137 instruction.tgt_idx = mutated.tgt_idx;
138 }
139
140 if swap_source {
142 instruction.src_idx = mutated.src_idx;
143 }
144
145 if swap_exec {
147 instruction.op = mutated.op;
148 }
149 }
150}
151
152impl Instruction {
153 pub fn apply(&self, registers: &mut Registers, input: &impl State) {
154 let target_value = match self.mode {
155 Mode::External => self.external_factor * input.get_value(self.tgt_idx),
156 _ => *registers.get(self.tgt_idx),
157 };
158
159 let source_value = *registers.get(self.src_idx);
160 let new_source_value = self.op.apply(source_value, target_value);
161
162 trace!(
163 op = %self.op,
164 src_idx = self.src_idx,
165 tgt_idx = self.tgt_idx,
166 mode = ?self.mode,
167 source_value = source_value,
168 target_value = target_value,
169 result = new_source_value,
170 "Instruction applied"
171 );
172
173 registers.update(self.src_idx, new_source_value);
174 }
175}