pub struct Agent<Gene> { /* private fields */ }Expand description
Carries a set of genes.
Implementations§
Source§impl<Gene> Agent<Gene>
impl<Gene> Agent<Gene>
Sourcepub fn with_genes(number_of_genes: usize) -> Self
pub fn with_genes(number_of_genes: usize) -> Self
Creates a new Agent with random set of genes.
Sourcepub fn get_genes(&self) -> &Vec<Gene>
pub fn get_genes(&self) -> &Vec<Gene>
Examples found in repository?
examples/simplest.rs (line 33)
21fn main() {
22
23 let mut manager = create_manager(fitness_function, 0);
24 manager.set_number_of_genes(5, true);
25 manager.run(1250);
26 let agents = manager.get_population().get_agents();
27
28 println!("Population: {}", agents.len());
29
30 let mut viewing = 10;
31 for (score_index, agent) in agents.iter().rev() {
32 println!("Score: {}", score_index);
33 println!("{:?}", agent.get_genes());
34
35 viewing -= 1;
36 if viewing == 0 {
37 break;
38 }
39 }
40}
41
42fn fitness_function(agent: &Agent<u8>, _data: &u8) -> Result<u64, ScoreError> {
43 let mut score = 0;
44
45 for gene in agent.get_genes() {
46 score += *gene as u64;
47 }
48
49 Ok(score)
50}More examples
examples/sequence.rs (line 72)
47pub fn main() {
48 let now = Instant::now();
49
50 let data = vec![0; 10];
51
52 let mut manager = create_manager(fitness_function, data.clone());
53 manager.set_number_of_genes(30, false);
54 manager.run(9999);
55 let agents = manager.get_population().get_agents();
56
57 println!("Duration: {}", now.elapsed().as_secs() as f64 + now.elapsed().subsec_nanos() as f64 * 1e-9);
58 println!("Population: {}", agents.len());
59
60 let mut first = true;
61 let mut first_score = 0;
62
63 for (score_index, agent) in agents.iter().rev() {
64 if first {
65 first = false;
66 first_score = *score_index;
67 }
68 if score_index < &(first_score - 20) {
69 break;
70 }
71 println!("{}", score_index);
72 println!("{:?}", get_processed_data(agent.get_genes(), &data));
73 }
74}
75
76fn get_processed_data(genes: &Vec<Gene>, data: &Vec<u8>) -> Vec<u8> {
77 let mut copy = data.clone();
78 let mut pointer = 0;
79 for gene in genes {
80 match gene {
81 MovePointerLeft => move_pointer_left(&mut pointer, &mut copy),
82 MovePointerRight => move_pointer_right(&mut pointer, &mut copy),
83 IncreaseValueByOne => increase_value_by_one(&mut pointer, &mut copy),
84 DecreaseValueByOne => decrease_value_by_one(&mut pointer, &mut copy),
85 CopyValueFromLeft => copy_value_from_left(&mut pointer, &mut copy),
86 CopyValueFromRight => copy_value_from_right(&mut pointer, &mut copy),
87 }
88 }
89
90 return copy;
91}
92
93fn move_pointer_left(pointer: &mut usize, _data: &mut Vec<u8>) {
94 if *pointer == 0 {
95 return;
96 }
97
98 *pointer -= 1;
99}
100
101fn move_pointer_right(pointer: &mut usize, data: &mut Vec<u8>) {
102 if *pointer == data.len() - 1 {
103 return;
104 }
105
106 *pointer += 1;
107}
108
109fn increase_value_by_one(pointer: &mut usize, data: &mut Vec<u8>) {
110 data[*pointer] += 1;
111}
112
113fn decrease_value_by_one(pointer: &mut usize, data: &mut Vec<u8>) {
114 if data[*pointer] == 0 {
115 return;
116 }
117 data[*pointer] -= 1;
118}
119
120fn copy_value_from_left(pointer: &mut usize, data: &mut Vec<u8>) {
121 if *pointer == 0 {
122 return;
123 }
124
125 data[*pointer] = data[*pointer-1];
126}
127
128fn copy_value_from_right(pointer: &mut usize, data: &mut Vec<u8>) {
129 if *pointer == data.len() - 1 {
130 return;
131 }
132
133 data[*pointer] = data[*pointer+1];
134}
135
136impl Distribution<Gene> for Standard {
137 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Gene {
138 match rng.gen_range(0, 6) {
139 0 => MovePointerLeft,
140 1 => MovePointerRight,
141 2 => IncreaseValueByOne,
142 3 => DecreaseValueByOne,
143 4 => CopyValueFromLeft,
144 _ => CopyValueFromRight
145 }
146 }
147}
148
149fn score_data(candidate: &Vec<u8>) -> u64 {
150 let mut score = 1.0;
151 let candidate_length_squared = candidate.len().pow(2) as f64;
152 let max_loss = 1.0 / candidate_length_squared;
153
154 for i in 1..candidate.len() {
155 let previous = candidate[i - 1];
156 let expected = previous + 1;
157 let value = candidate[i];
158 if value == 0 {
159 score = score - max_loss;
160 continue;
161 }
162
163 let diff = value as f64 - expected as f64;
164 score = score - (diff.abs() / candidate_length_squared);
165
166 if score < 0.0 {
167 score = 0.0;
168 break;
169 }
170 }
171
172 (score * 10000.0) as u64
173}
174
175fn fitness_function(agent: &Agent<Gene>, data: &Vec<u8>) -> Result<u64, ScoreError> {
176 let processed = get_processed_data(agent.get_genes(), data);
177 Ok(score_data(&processed))
178}examples/travelling_salesman.rs (line 144)
64pub fn main() {
65 // We'll be interested in how long the process takes.
66 let now = Instant::now();
67
68 let all_cities = vec![
69 Wellington,
70 PalmerstonNorth,
71 NewPlymouth,
72 Hastings,
73 Gisborne,
74 Taupo,
75 Rotorua,
76 Hamilton,
77 Tauranga,
78 Auckland
79 ];
80 let cities_clone = all_cities.clone();
81
82 // There is always a data variable, for which the type is quite flexible and what you do with it is up to you.
83 // In this case, we're using it cache the distances between each city rather than doing that calculation every time
84 // we score a set of genes.
85 let mut data = HashMap::new();
86 for city in all_cities {
87 for other in &cities_clone {
88 if city != *other {
89 data.insert((city.clone(), other.clone()), distance_between_points(get_coordinates(&city), get_coordinates(&other)));
90 }
91 }
92 }
93
94 // Here we define what happens for each "generation" of the process.
95 let operations = vec![
96 // We will mutate a random selection of 10% (that's the 0.1 in the Selection) of the population, but also a minimum of 1.
97 Operation::with_values(
98 Selection::with_values(SelectionType::RandomAny, 0.1, 1),
99 OperationType::Mutate),
100 // We will get highest scored 20% and randomly pair them, creating children with crossed over genes out of those.
101 Operation::with_values(
102 Selection::with_values(SelectionType::HighestScore, 0.2, 1),
103 OperationType::Crossover),
104 // We will take a random set of 50% of the population, randomly pair them and produce children with crossed over
105 // genes out of those.
106 Operation::with_values(
107 Selection::with_values(SelectionType::RandomAny, 0.5, 1),
108 OperationType::Crossover),
109 // We will take the lowest 2% of the population and get rid of them. Note that just like in the previous operations,
110 // the minimum is set to 1. So there'll always be at least 1 agent culled.
111 Operation::with_values(
112 Selection::with_values(SelectionType::LowestScore, 0.02, 1),
113 OperationType::Cull)
114 ];
115
116 let mut score_provider = GeneralScoreProvider::new(fitness_function, 25);
117
118 // Create a population of 20 agents which each have a set of 10 randomly chosen genes.
119 // We need to pass in the data as this is used for scoring the agents.
120 // We also pass in a reference to the scoring function defined towards the end of this file.
121 let population = Population::new(20, 10, false, &data, &mut score_provider);
122
123 // Now we run 50 iterations (or generations) on this population, meaning we run the operations we defined above
124 // 50 times over. Again, we need the data and scoring function references as these are used for scoring new agents.
125 let population = run_iterations(population, 50, &data, &operations, &mut score_provider);
126
127 let agents = population.get_agents();
128
129 println!("Population: {}", agents.len());
130 println!("Duration: {}", now.elapsed().as_secs() as f64 + now.elapsed().subsec_nanos() as f64 * 1e-9);
131
132 // This will the print the highest score and those that follow.
133 let mut first = true;
134 let mut first_score = 0;
135 for (score_index, agent) in agents.iter().rev() {
136 if first {
137 first = false;
138 first_score = *score_index;
139 }
140 if score_index < &(first_score - 20) {
141 break;
142 }
143 println!("Score: {}", score_index);
144 println!("{:?}", agent.get_genes());
145 }
146}
147
148// We need to define how random values are generated for the genes.
149// If you have a set of genes where you want the likelihood of each of them being returned to be equal,
150// you would probably define it a lot like this.
151impl Distribution<City> for Standard {
152 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> City {
153 match rng.gen_range(0, 10) {
154 0 => Wellington,
155 1 => PalmerstonNorth,
156 2 => NewPlymouth,
157 3 => Hastings,
158 4 => Gisborne,
159 5 => Taupo,
160 6 => Rotorua,
161 7 => Hamilton,
162 8 => Tauranga,
163 _ => Auckland
164 }
165 }
166}
167
168// This just gives us the simple distance between 2 points on a 2d plane.
169// I could have been more technically correct and used a formula that determines
170// the distance between points on a globe (called the "haversine formula").
171// I also could have got something like the driving distance or the travel time from somewhere,
172// but for a demonstration of how the library works, I'm keeping things simple.
173fn distance_between_points(first: (f64, f64), second: (f64, f64)) -> f64 {
174 let (x1, y1) = first;
175 let (x2, y2) = second;
176 let distance = ((x2 - x1).powi(2) - (y2 - y1).powi(2)).abs().sqrt();
177 distance
178}
179
180// These are coordinates chosen from some point within or close to these cities.
181fn get_coordinates(city: &City) -> (f64, f64) {
182 match city {
183 Wellington => (-41.30, 174.77),
184 PalmerstonNorth => (-40.35, 175.61),
185 NewPlymouth => (-39.07, 174.11),
186 Hastings => (-39.64, 176.85),
187 Gisborne => (-38.67, 178.01),
188 Taupo => (-38.68, 176.08),
189 Rotorua => (-38.14, 176.24),
190 Hamilton => (-37.79, 175.28),
191 Tauranga => (-37.69, 176.16),
192 Auckland => (-36.85, 174.76)
193 }
194}
195
196// Given an agent, and therefore it's full set of genes, or cities. Calculate the total distance
197// travelled when going through all those cities in that order.
198fn get_distance(agent: &Agent<City>, data: &HashMap<(City, City), f64>) -> f64 {
199 let mut distance = 0.0;
200 let mut previous_city = Wellington; // Initialise with something.
201 let mut first = true;
202 for gene in agent.get_genes() {
203 if first {
204 previous_city = gene.clone();
205 first = false;
206 continue;
207 }
208
209 if &previous_city != gene {
210 distance += data.get(&(previous_city, gene.clone())).unwrap();
211 }
212
213 previous_city = gene.clone();
214 }
215
216 distance
217}
218
219// The fitness function used to determine the score on an agent, based on its genes.
220fn fitness_function(agent: &Agent<City>, data: &HashMap<(City, City), f64>) -> Result<u64, ScoreError> {
221 let distance = get_distance(agent, data);
222
223 let mut repeats = 0;
224 let mut cities = HashSet::new();
225 for city in agent.get_genes() {
226 if !cities.insert(city) {
227 // False returned if HashSet did have value.
228 repeats += 1;
229 }
230 }
231
232 // To talk through this:
233 // 6.0 is about the distance between the two furthest cities (using the coordinates as units, I'm not actually even bothering to convert to km or miles).
234 // The above is multiplied by the length of the genes, because you could have a set that goes back and forth between the two furthest citis.
235 // So that gives the longest possible distance, now subtract the distance calculated for the set of genes we're scoring.
236 // Multiply by 100.0 - this is actually just to ensure the scores have a decent spread.
237 // The last set of brackets is a penalty on the score for any cities visited twice, the idea of this example is that
238 // the salesman should be visiting each city once.
239 let score = (6.0 * agent.get_genes().len() as f64 - distance) * 100.0 * (1.0 - repeats as f64 * 0.1);
240
241 Ok(score as u64)
242}Sourcepub fn crossover_some_genes(&mut self, other: &Self)
pub fn crossover_some_genes(&mut self, other: &Self)
Chooses a random point on genes of self and uses that as its crossover point. Maintains the number of genes of self if the other has a different gene length.
pub fn mutate(&mut self)
pub fn has_same_genes(&self, other: &Self) -> bool
Trait Implementations§
Auto Trait Implementations§
impl<Gene> Freeze for Agent<Gene>
impl<Gene> RefUnwindSafe for Agent<Gene>where
Gene: RefUnwindSafe,
impl<Gene> Send for Agent<Gene>where
Gene: Send,
impl<Gene> Sync for Agent<Gene>where
Gene: Sync,
impl<Gene> Unpin for Agent<Gene>where
Gene: Unpin,
impl<Gene> UnwindSafe for Agent<Gene>where
Gene: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more