Agent

Struct Agent 

Source
pub struct Agent<Gene> { /* private fields */ }
Expand description

Carries a set of genes.

Implementations§

Source§

impl<Gene> Agent<Gene>

Source

pub fn new() -> Self
where Standard: Distribution<Gene>, Gene: Hash,

Creates an agents with no genes.

Source

pub fn with_genes(number_of_genes: usize) -> Self
where Standard: Distribution<Gene>, Gene: Hash,

Creates a new Agent with random set of genes.

Source

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
Hide additional 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}
Source

pub fn crossover_some_genes(&mut self, other: &Self)
where Gene: Clone + Hash,

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.

Source

pub fn mutate(&mut self)
where Standard: Distribution<Gene>, Gene: Hash,

Source

pub fn has_same_genes(&self, other: &Self) -> bool

Source

pub fn get_hash(&self) -> u64

Gets a hash representing this agents gene sequence.

Trait Implementations§

Source§

impl<Gene: Clone> Clone for Agent<Gene>

Source§

fn clone(&self) -> Agent<Gene>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.