Skip to content

Commit f169562

Browse files
committed
remove 50/50 direction approach for pre-clamped Range bandwidth
mutations, as it oversampled the direction with the smaller range. Now uniformly sample over complete pre-clamped bandwidth, without directionallity concerns
1 parent e8d601d commit f169562

6 files changed

Lines changed: 40 additions & 67 deletions

File tree

README.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,6 @@ Run with `cargo run --example profile_evolve_binary --release -- --bench --profi
188188
Find the flamegraph in: `./target/criterion/profile_evolve_binary/profile/flamegraph.svg`
189189
190190
## TODO
191-
* There is an issue with MutationType::RangeScaled. The pre-clamping solves the
192-
issue of oversampling the borders when using a large intermediate bandwidth.
193-
But it doesn't solve the 50/50 probability of sampling up or down. If the
194-
gene is nearer to an edge, the effective bandwidth is smaller due to
195-
pre-clamping, but that oversamples the bandwidth with regard to the other
196-
direction's bandwidth size. This effect gets stronger nearer to the edge. 50%
197-
of the samples fall between the current value and the edge, which feels very
198-
unbalanced. Need to sample the whole pre-clamped bandwidth as a whole (how to
199-
do that for unsigned int?) or need to balance the probability by bandwidth
200-
size
201191
202192
## MAYBE
203193
* Apply precision (to f32/f64) during hashing in order to converge and hit

src/genotype/multi_range.rs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -309,23 +309,14 @@ where
309309
} else {
310310
// Bandwidth
311311
let current_value = chromosome.genes[index];
312-
if rng.gen() {
313-
let max_delta_up = allele_range_end - current_value;
314-
let working_delta_up = T::min(bandwidth, max_delta_up);
315-
if working_delta_up >= T::smallest_increment() {
316-
let delta =
317-
rng.gen_range(T::smallest_increment()..=working_delta_up);
318-
chromosome.genes[index] += delta; // no need to check again
319-
}
320-
} else {
321-
let max_delta_down = current_value - allele_range_start;
322-
let working_delta_down = T::min(bandwidth, max_delta_down);
323-
if working_delta_down >= T::smallest_increment() {
324-
let delta =
325-
rng.gen_range(T::smallest_increment()..=working_delta_down);
326-
chromosome.genes[index] -= delta; // no need to check again
327-
}
328-
}
312+
let max_delta_up = allele_range_end - current_value;
313+
let max_delta_down = current_value - allele_range_start;
314+
let working_delta_up = T::min(bandwidth, max_delta_up);
315+
let working_delta_down = T::min(bandwidth, max_delta_down);
316+
let working_range_end = current_value + working_delta_up;
317+
let working_range_start = current_value - working_delta_down;
318+
chromosome.genes[index] =
319+
rng.gen_range(working_range_start..=working_range_end);
329320
}
330321
}
331322
}

src/genotype/mutation_type.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ use crate::allele::Allele;
9393
///
9494
/// **Behavior by phase:**
9595
/// - Phase 0: Mutations uniformly within full allele range (same as Random, pre-clamped)
96-
/// - Phase 1: Mutations uniformly within full allele range (extended round, more exploration)
96+
/// - Phase 1: Mutations uniformly within full allele range (another Random round)
9797
/// - Phase 2: Mutations uniformly within ±50 (pre-clamped)
9898
/// - Phase 3: Mutations uniformly within ±20 (pre-clamped)
9999
/// - Phase 4: Mutations uniformly within ±5 (pre-clamped)
@@ -187,14 +187,16 @@ use crate::allele::Allele;
187187
///
188188
/// ## Boundary Sampling Summary
189189
///
190-
/// - `Random`: Undersamples boundaries (infinitesimal probability)
191-
/// - `Range`: Post-clamped, slight boundary oversampling when near edges
192-
/// - `Step`: Always clamped, slight boundary oversampling when near edges
193-
/// - `RangeScaled`:
194-
/// - Non-final phases: Pre-clamped, boundaries undersampled
195-
/// - Final phase: Post-clamped, slight boundary oversampling
196-
/// - `StepScaled`: Always clamped, slight boundary oversampling
197-
/// - `Discrete`: Uniform sampling, no boundary bias
190+
/// * `Random`: Undersamples boundaries (infinitesimal probability)
191+
/// * `Range`: Post-clamped, slight boundary oversampling when near edges (assuming small bandwidth)
192+
/// * `Step`: Always clamped, slight boundary oversampling when near edges (assuming small step)
193+
/// * `RangeScaled`:
194+
/// * Non-final phases: Pre-clamped, boundaries undersampled
195+
/// * Final phase: Post-clamped, slight boundary oversampling (assuming small final bandwidth)
196+
/// * `StepScaled`: Always clamped
197+
/// * First phase: Given potentially large step in the first phase, the boundary can become quite oversampled
198+
/// * Non-first phases: slight boundary oversampling (assuming small steps)
199+
/// * `Discrete`: Uniform sampling, no boundary bias
198200
///
199201
/// # Phase Management
200202
///

src/genotype/range.rs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -216,23 +216,14 @@ where
216216
} else {
217217
// Bandwidth
218218
let current_value = chromosome.genes[index];
219-
if rng.gen() {
220-
let max_delta_up = allele_range_end - current_value;
221-
let working_delta_up = T::min(bandwidth, max_delta_up);
222-
if working_delta_up >= T::smallest_increment() {
223-
let delta =
224-
rng.gen_range(T::smallest_increment()..=working_delta_up);
225-
chromosome.genes[index] += delta; // no need to check again
226-
}
227-
} else {
228-
let max_delta_down = current_value - allele_range_start;
229-
let working_delta_down = T::min(bandwidth, max_delta_down);
230-
if working_delta_down >= T::smallest_increment() {
231-
let delta =
232-
rng.gen_range(T::smallest_increment()..=working_delta_down);
233-
chromosome.genes[index] -= delta; // no need to check again
234-
}
235-
}
219+
let max_delta_up = allele_range_end - current_value;
220+
let max_delta_down = current_value - allele_range_start;
221+
let working_delta_up = T::min(bandwidth, max_delta_up);
222+
let working_delta_down = T::min(bandwidth, max_delta_down);
223+
let working_range_end = current_value + working_delta_up;
224+
let working_range_start = current_value - working_delta_down;
225+
chromosome.genes[index] =
226+
rng.gen_range(working_range_start..=working_range_end);
236227
}
237228
}
238229
}

tests/genotype/multi_range_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ fn float_mutate_chromosome_single_range_scaled() {
114114
genotype.mutate_chromosome_genes(2, false, &mut chromosome, &mut rng);
115115
assert!(relative_chromosome_eq(
116116
inspect::chromosome(&chromosome),
117-
vec![0.447, 1.219, 15.704],
117+
vec![0.447, 2.833, 16.854],
118118
0.001
119119
));
120120

@@ -123,7 +123,7 @@ fn float_mutate_chromosome_single_range_scaled() {
123123
genotype.mutate_chromosome_genes(2, false, &mut chromosome, &mut rng);
124124
assert!(relative_chromosome_eq(
125125
inspect::chromosome(&chromosome),
126-
vec![0.417, 1.331, 15.704],
126+
vec![0.392, 2.833, 17.697],
127127
0.001
128128
));
129129

@@ -132,7 +132,7 @@ fn float_mutate_chromosome_single_range_scaled() {
132132
genotype.mutate_chromosome_genes(2, false, &mut chromosome, &mut rng);
133133
assert!(relative_chromosome_eq(
134134
inspect::chromosome(&chromosome),
135-
vec![0.417, 1.288, 15.685],
135+
vec![0.399, 2.833, 17.678],
136136
0.001
137137
));
138138
}

tests/genotype/range_test.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,35 +96,34 @@ fn float_mutate_chromosome_single_range_scaled() {
9696
));
9797

9898
assert_eq!(genotype.current_scale_index, 0);
99-
genotype.mutate_chromosome_genes(1, true, &mut chromosome, &mut rng);
99+
genotype.mutate_chromosome_genes(2, false, &mut chromosome, &mut rng);
100100
assert!(relative_chromosome_eq(
101101
inspect::chromosome(&chromosome),
102-
vec![0.447, 0.897, 0.979],
102+
vec![0.447, 0.818, 0.395],
103103
0.001,
104104
));
105105

106106
assert!(genotype.increment_scale_index());
107107
assert_eq!(genotype.current_scale_index, 1);
108-
genotype.mutate_chromosome_genes(1, true, &mut chromosome, &mut rng);
109-
genotype.mutate_chromosome_genes(1, true, &mut chromosome, &mut rng);
108+
genotype.mutate_chromosome_genes(2, false, &mut chromosome, &mut rng);
110109
assert!(relative_chromosome_eq(
111110
inspect::chromosome(&chromosome),
112-
vec![0.447, 0.921, 0.989],
111+
vec![0.392, 0.818, 0.479],
113112
0.001,
114113
));
115114

116115
assert!(genotype.increment_scale_index());
117116
assert_eq!(genotype.current_scale_index, 2);
118-
genotype.mutate_chromosome_genes(1, true, &mut chromosome, &mut rng);
117+
genotype.mutate_chromosome_genes(2, false, &mut chromosome, &mut rng);
119118
assert!(relative_chromosome_eq(
120119
inspect::chromosome(&chromosome),
121-
vec![0.447, 0.921, 0.982],
120+
vec![0.399, 0.818, 0.477],
122121
0.001,
123122
));
124123
}
125124

126125
#[test]
127-
fn float_mutate_chromosome_single_range_scaled_on_edge() {
126+
fn float_mutate_chromosome_single_range_scaled_on_edge_and_zero_bandwidth() {
128127
let mut rng = SmallRng::seed_from_u64(1);
129128
let mut genotype = RangeGenotype::builder()
130129
.with_genes_size(3)
@@ -140,7 +139,7 @@ fn float_mutate_chromosome_single_range_scaled_on_edge() {
140139
genotype.mutate_chromosome_genes(3, false, &mut chromosome, &mut rng);
141140
assert!(relative_chromosome_eq(
142141
inspect::chromosome(&chromosome),
143-
vec![0.970, 0.0, 0.904],
142+
vec![0.911, 0.095, 0.947],
144143
0.001,
145144
));
146145

@@ -149,7 +148,7 @@ fn float_mutate_chromosome_single_range_scaled_on_edge() {
149148
genotype.mutate_chromosome_genes(3, false, &mut chromosome, &mut rng);
150149
assert!(relative_chromosome_eq(
151150
inspect::chromosome(&chromosome),
152-
vec![0.970, 0.0, 0.904],
151+
vec![0.911, 0.095, 0.947],
153152
0.001,
154153
));
155154

@@ -158,7 +157,7 @@ fn float_mutate_chromosome_single_range_scaled_on_edge() {
158157
genotype.mutate_chromosome_genes(3, false, &mut chromosome, &mut rng);
159158
assert!(relative_chromosome_eq(
160159
inspect::chromosome(&chromosome),
161-
vec![0.970, 0.0, 0.904],
160+
vec![0.911, 0.095, 0.947],
162161
0.001,
163162
));
164163
}

0 commit comments

Comments
 (0)