egp/
operators.rs

1use rand;
2use rand::Rng;
3
4use crate::blueprints::Blueprints;
5use crate::chromosome::EgpChromosome;
6use crate::component::Component;
7
8/// Performs the mutation genetic operator in-place
9pub fn mutate(blueprints: &Blueprints, chromosome: &mut EgpChromosome) {
10    let mut rng = rand::thread_rng();
11
12    if rng.gen_range(0., 1.) < 0.5 {
13        mutate_activity(blueprints, chromosome);
14    } else {
15        let n_regulars: usize = chromosome.regular.iter().map(|group| group.len()).sum();
16
17        if rng.gen_range(0., 1.) < 1. / (n_regulars as f32) {
18            mutate_binding_site_output(blueprints, chromosome);
19        } else {
20            mutate_binding_site(blueprints, chromosome);
21        }
22    }
23}
24
25/// Performs the recombination / crossover genetic operator
26///
27/// The crossover event is either a transfer from parent_a to parent_b, or a
28/// removal from parent_a, with equal probability. This is done for balancing
29/// the overall chromosome size in the population.
30pub fn recombine(
31    blueprints: &Blueprints,
32    n_transfer: usize,
33    parent_a: &EgpChromosome,
34    parent_b: &EgpChromosome,
35) -> EgpChromosome {
36    let mut rng = rand::thread_rng();
37
38    if rng.gen_range(0., 1.) < 0.5 {
39        recombine_remove(blueprints, n_transfer, parent_a)
40    } else {
41        recombine_transfer(blueprints, n_transfer, parent_a, parent_b)
42    }
43}
44
45fn recombine_transfer(
46    blueprints: &Blueprints,
47    n_transfer: usize,
48    parent: &EgpChromosome,
49    donor: &EgpChromosome,
50) -> EgpChromosome {
51    let mut child = parent.clone();
52
53    let mut rng = rand::thread_rng();
54
55    let nonempty_group = nonempty_group(blueprints);
56    let group_len = donor.regular[nonempty_group].len();
57
58    let skip = rng.gen_range(0, group_len);
59
60    let mut n_transfer = if n_transfer < group_len {
61        n_transfer
62    } else {
63        group_len
64    };
65
66    for i in skip..group_len {
67        if n_transfer <= 0 {
68            break;
69        }
70
71        child.regular[nonempty_group].push(donor.regular[nonempty_group][i].clone());
72
73        n_transfer -= 1;
74    }
75
76    child
77}
78
79fn recombine_remove(
80    blueprints: &Blueprints,
81    n_remove: usize,
82    parent: &EgpChromosome,
83) -> EgpChromosome {
84    let mut child = parent.clone();
85
86    let mut rng = rand::thread_rng();
87
88    let nonempty_group = nonempty_group(blueprints);
89    let group_len = child.regular[nonempty_group].len();
90
91    let mut n_remove = if n_remove < group_len {
92        n_remove
93    } else {
94        group_len
95    };
96
97    let skip = rng.gen_range(0, group_len);
98
99    for i in skip..group_len {
100        if n_remove == 0 || i < child.regular[nonempty_group].len() {
101            break;
102        }
103
104        child.regular[nonempty_group].remove(i);
105
106        n_remove -= 1;
107    }
108
109    child
110}
111
112fn mutate_activity(blueprints: &Blueprints, chromosome: &mut EgpChromosome) {
113    let (group, member) = pick_group_and_member(blueprints);
114
115    let new_component = Component::from_blueprint(
116        &blueprints.regular[group][member],
117        blueprints.total_activities,
118    );
119
120    let n_compatible = chromosome.regular[group]
121        .iter()
122        .filter(|component| component.activity == new_component.activity)
123        .count();
124
125    if n_compatible == 0 {
126        return;
127    }
128
129    let mut rng = rand::thread_rng();
130
131    let to_replace = rng.gen_range(0, n_compatible);
132
133    let mut n_encountered = 0;
134
135    for i in 0..chromosome.regular[group].len() {
136        if chromosome.regular[group][i].activity == new_component.activity {
137            if to_replace == n_encountered {
138                chromosome.regular[group][i].activity = new_component.activity;
139                chromosome.regular[group][i].label = new_component.label.clone();
140                break;
141            }
142
143            n_encountered += 1;
144        }
145    }
146}
147
148fn mutate_binding_site(blueprints: &Blueprints, chromosome: &mut EgpChromosome) {
149    let mut rng = rand::thread_rng();
150
151    let nonempty_group = nonempty_group(blueprints);
152    let group_len = chromosome.regular[nonempty_group].len();
153
154    let component = &mut chromosome.regular[nonempty_group][rng.gen_range(0, group_len)];
155
156    let binding_site_index = rng.gen_range(0, component.binding_sites.len());
157    let dimension = rng.gen_range(0, blueprints.total_activities);
158
159    component.binding_sites[binding_site_index][dimension] = rng.gen::<f32>();
160}
161
162fn mutate_binding_site_output(blueprints: &Blueprints, chromosome: &mut EgpChromosome) {
163    let mut rng = rand::thread_rng();
164
165    let component = &mut chromosome.output;
166
167    let binding_site_index = rng.gen_range(0, component.binding_sites.len());
168    let dimension = rng.gen_range(0, blueprints.total_activities);
169
170    component.binding_sites[binding_site_index][dimension] = rng.gen::<f32>();
171}
172
173fn nonempty_group(blueprints: &Blueprints) -> usize {
174    let mut rng = rand::thread_rng();
175    let nonempty_groups = nonempty_groups(blueprints);
176    nonempty_groups[rng.gen_range(0, nonempty_groups.len())]
177}
178
179fn nonempty_groups(blueprints: &Blueprints) -> Vec<usize> {
180    blueprints
181        .regular
182        .iter()
183        .enumerate()
184        .filter(|(_group_index, group)| group.len() > 0)
185        .map(|(group_index, _group)| group_index)
186        .collect()
187}
188
189fn pick_group_and_member(blueprints: &Blueprints) -> (usize, usize) {
190    let mut rng = rand::thread_rng();
191
192    let group = nonempty_group(blueprints);
193    let member = rng.gen_range(0, blueprints.regular[group].len());
194
195    (group, member)
196}