1use crate::brain::Brain;
2use crate::neuron::{NeuronID, Position};
3use crate::Scalar;
4use rand::{thread_rng, Rng};
5use serde::{Deserialize, Serialize};
6use std::f64::consts::PI;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct OffspringBuilder {
10 new_neurons: usize,
11 new_connections: usize,
12 radius: Scalar,
13 min_neurogenesis_range: Scalar,
14 max_neurogenesis_range: Scalar,
15 new_sensors: usize,
16 new_effectors: usize,
17 no_loop_connections: bool,
18 max_connecting_tries: usize,
19}
20
21impl Default for OffspringBuilder {
22 fn default() -> Self {
23 Self {
24 new_neurons: 1,
25 new_connections: 1,
26 radius: 10.0,
27 min_neurogenesis_range: 0.1,
28 max_neurogenesis_range: 1.0,
29 new_sensors: 1,
30 new_effectors: 1,
31 no_loop_connections: true,
32 max_connecting_tries: 10,
33 }
34 }
35}
36
37impl OffspringBuilder {
38 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn new_neurons(mut self, value: usize) -> Self {
43 self.new_neurons = value;
44 self
45 }
46
47 pub fn new_connections(mut self, value: usize) -> Self {
48 self.new_connections = value;
49 self
50 }
51
52 pub fn radius(mut self, value: Scalar) -> Self {
53 self.radius = value;
54 self
55 }
56
57 pub fn min_neurogenesis_range(mut self, value: Scalar) -> Self {
58 self.min_neurogenesis_range = value;
59 self
60 }
61
62 pub fn max_neurogenesis_range(mut self, value: Scalar) -> Self {
63 self.max_neurogenesis_range = value;
64 self
65 }
66
67 pub fn new_sensors(mut self, value: usize) -> Self {
68 self.new_sensors = value;
69 self
70 }
71
72 pub fn new_effectors(mut self, value: usize) -> Self {
73 self.new_effectors = value;
74 self
75 }
76
77 pub fn no_loop_connections(mut self, value: bool) -> Self {
78 self.no_loop_connections = value;
79 self
80 }
81
82 pub fn max_connecting_tries(mut self, value: usize) -> Self {
83 self.max_connecting_tries = value;
84 self
85 }
86
87 pub fn build_mutated(mut self, source: &Brain) -> Brain {
88 let mut brain = source.duplicate();
89 let mut rng = thread_rng();
90
91 let mut neurons = brain.get_neurons();
92 for _ in 0..self.new_neurons {
93 neurons.push(self.make_neighbor_neuron(&neurons, &mut brain, &mut rng));
94 }
95
96 let neuron_positions = neurons
97 .iter()
98 .map(|id| (*id, brain.neuron(*id).unwrap().position()))
99 .collect::<Vec<_>>();
100 for _ in 0..self.new_sensors {
101 let mut tries = self.max_connecting_tries + 1;
102 while tries > 0 && !self.make_peripheral_sensor(&neuron_positions, &mut brain, &mut rng)
103 {
104 tries -= 1;
105 }
106 }
107 for _ in 0..self.new_effectors {
108 let mut tries = self.max_connecting_tries + 1;
109 while tries > 0
110 && !self.make_peripheral_effector(&neuron_positions, &mut brain, &mut rng)
111 {
112 tries -= 1;
113 }
114 }
115 for _ in 0..self.new_connections {
116 let mut tries = self.max_connecting_tries + 1;
117 while tries > 0
118 && self.connect_neighbor_neurons(&neuron_positions, &mut brain, &mut rng)
119 {
120 tries -= 1;
121 }
122 }
123 for id in brain.get_neurons() {
124 if !brain.does_neuron_has_connections(id) {
125 drop(brain.kill_neuron(id));
126 }
127 }
128
129 brain
130 }
131
132 pub fn build_merged(mut self, source_a: &Brain, source_b: &Brain) -> Brain {
133 let mut brain = source_a.merge(source_b);
134 let mut rng = thread_rng();
135
136 let mut neurons = brain.get_neurons();
137 for _ in 0..self.new_neurons {
138 neurons.push(self.make_neighbor_neuron(&neurons, &mut brain, &mut rng));
139 }
140
141 self.new_sensors += (source_a.get_sensors().len() + source_b.get_sensors().len()) / 2
142 - brain.get_sensors().len();
143 self.new_effectors += (source_a.get_effectors().len() + source_b.get_effectors().len()) / 2
144 - brain.get_effectors().len();
145 let neuron_positions = neurons
146 .iter()
147 .map(|id| (*id, brain.neuron(*id).unwrap().position()))
148 .collect::<Vec<_>>();
149 for _ in 0..self.new_sensors {
150 let mut tries = self.max_connecting_tries + 1;
151 while tries > 0 && !self.make_peripheral_sensor(&neuron_positions, &mut brain, &mut rng)
152 {
153 tries -= 1;
154 }
155 }
156 for _ in 0..self.new_effectors {
157 let mut tries = self.max_connecting_tries + 1;
158 while tries > 0
159 && !self.make_peripheral_effector(&neuron_positions, &mut brain, &mut rng)
160 {
161 tries -= 1;
162 }
163 }
164 for _ in 0..self.new_connections {
165 let mut tries = self.max_connecting_tries + 1;
166 while tries > 0
167 && !self.connect_neighbor_neurons(&neuron_positions, &mut brain, &mut rng)
168 {
169 tries -= 1;
170 }
171 }
172
173 brain
174 }
175
176 fn make_peripheral_sensor<R>(
177 &self,
178 neuron_positions: &[(NeuronID, Position)],
179 brain: &mut Brain,
180 rng: &mut R,
181 ) -> bool
182 where
183 R: Rng,
184 {
185 let pos = self.make_new_peripheral_position(rng);
186 let index = neuron_positions
187 .iter()
188 .map(|(_, p)| p.distance_sqr(pos))
189 .enumerate()
190 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
191 .unwrap()
192 .0;
193 brain.create_sensor(neuron_positions[index].0).is_ok()
194 }
195
196 fn make_peripheral_effector<R>(
197 &self,
198 neuron_positions: &[(NeuronID, Position)],
199 brain: &mut Brain,
200 rng: &mut R,
201 ) -> bool
202 where
203 R: Rng,
204 {
205 let pos = self.make_new_peripheral_position(rng);
206 let index = neuron_positions
207 .iter()
208 .map(|(_, p)| p.distance_sqr(pos))
209 .enumerate()
210 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
211 .unwrap()
212 .0;
213 brain.create_effector(neuron_positions[index].0).is_ok()
214 }
215
216 fn make_neighbor_neuron<R>(
217 &mut self,
218 neurons: &[NeuronID],
219 brain: &mut Brain,
220 rng: &mut R,
221 ) -> NeuronID
222 where
223 R: Rng,
224 {
225 let distance = rng.gen_range(self.min_neurogenesis_range, self.max_neurogenesis_range);
226 let origin = neurons[rng.gen_range(0, neurons.len()) % neurons.len()];
227 let origin_pos = brain.neuron(origin).unwrap().position();
228 let new_position = self.make_new_position(origin_pos, distance, rng);
229 brain.create_neuron(new_position)
230 }
231
232 fn connect_neighbor_neurons<R>(
233 &mut self,
234 neuron_positions: &[(NeuronID, Position)],
235 brain: &mut Brain,
236 rng: &mut R,
237 ) -> bool
238 where
239 R: Rng,
240 {
241 let origin =
242 neuron_positions[rng.gen_range(0, neuron_positions.len()) % neuron_positions.len()];
243 let filtered = neuron_positions
244 .iter()
245 .filter_map(|(id, p)| {
246 if p.distance(origin.1) <= self.max_neurogenesis_range {
247 Some(id)
248 } else {
249 None
250 }
251 })
252 .collect::<Vec<_>>();
253 let target = *filtered[rng.gen_range(0, filtered.len()) % filtered.len()];
254 origin.0 != target
255 && (!self.no_loop_connections
256 || (!brain.are_neurons_connected(origin.0, target)
257 && !brain.are_neurons_connected(target, origin.0)))
258 && brain.bind_neurons(origin.0, target).is_ok()
259 }
260
261 fn make_new_position<R>(&self, pos: Position, scale: Scalar, rng: &mut R) -> Position
262 where
263 R: Rng,
264 {
265 let phi = rng.gen_range(0.0, PI * 2.0);
266 let theta = rng.gen_range(-PI, PI);
267 let pos = Position {
268 x: pos.x + theta.cos() * phi.cos() * scale,
269 y: pos.y + theta.cos() * phi.sin() * scale,
270 z: pos.z + theta.sin() * scale,
271 };
272 let magnitude = pos.magnitude();
273 if magnitude > self.radius {
274 Position {
275 x: self.radius * pos.x / magnitude,
276 y: self.radius * pos.y / magnitude,
277 z: self.radius * pos.z / magnitude,
278 }
279 } else {
280 pos
281 }
282 }
283
284 fn make_new_peripheral_position<R>(&self, rng: &mut R) -> Position
285 where
286 R: Rng,
287 {
288 let phi = rng.gen_range(0.0, PI * 2.0);
289 let theta = rng.gen_range(-PI, PI);
290 Position {
291 x: theta.cos() * phi.cos() * self.radius,
292 y: theta.cos() * phi.sin() * self.radius,
293 z: theta.sin() * self.radius,
294 }
295 }
296}