1#![allow(clippy::needless_range_loop, clippy::manual_memcpy)]
4
5use std::collections::VecDeque;
16
17use crate::fitness::Objective;
18
19const INT_MANT_BITS: u32 = 58;
20const INT_MANT_MULT: i64 = 1i64 << INT_MANT_BITS;
21const INT_MANT_MASK: i64 = INT_MANT_MULT - 1;
22const BAD_COST: f64 = 1e300;
23const MAX_DEEP_OPTIMIZERS: i32 = 36;
24
25#[inline]
26fn sanitize_cost(cost: f64) -> f64 {
27 if cost.is_finite() { cost } else { BAD_COST }
28}
29
30#[inline]
31fn objective_cost(objective: &dyn Objective, values: &[f64]) -> f64 {
32 sanitize_cost(objective.eval_scalar(values))
33}
34
35pub fn validate_bite_inputs(
37 lower: &[f64],
38 upper: &[f64],
39 init: Option<&[f64]>,
40 params: &BiteParams,
41 depth: i32,
42) -> Result<(), String> {
43 if lower.is_empty() || lower.len() != upper.len() {
44 return Err("bounds must be non-empty and have equal lengths".into());
45 }
46 for (&lo, &hi) in lower.iter().zip(upper) {
47 if !lo.is_finite() || !hi.is_finite() || lo >= hi || !(hi - lo).is_finite() {
48 return Err("bounds must contain finite intervals with lower < upper".into());
49 }
50 }
51 if let Some(values) = init {
52 if values.len() != lower.len() {
53 return Err("initial guess must match the bounds dimension".into());
54 }
55 if values.iter().any(|value| !value.is_finite()) {
56 return Err("initial guess must contain only finite values".into());
57 }
58 }
59 if params.stop_fitness.is_nan() {
60 return Err("stop_fitness must not be NaN".into());
61 }
62 if (1..4).contains(¶ms.popsize) {
63 return Err("popsize must be non-positive (automatic) or at least 4".into());
64 }
65 if depth > MAX_DEEP_OPTIMIZERS {
66 return Err(format!(
67 "M must not exceed {MAX_DEEP_OPTIMIZERS} deep optimizers"
68 ));
69 }
70 Ok(())
71}
72
73struct Candidate {
75 enc: Vec<i64>,
76 real: Vec<f64>,
77 sels: Vec<SelUse>,
78 is_init: bool,
79 precomputed_cost: Option<f64>,
81}
82
83#[derive(Clone, Copy)]
85enum PopSel {
86 Main,
87 Par(usize),
88 ParOpt,
89 ParOpt2,
90}
91
92#[derive(Clone, Debug)]
94pub struct BiteResult {
95 pub x: Vec<f64>,
96 pub y: f64,
97 pub evaluations: u64,
98 pub iterations: i32,
99 pub stop: i32,
100}
101
102#[derive(Clone, Debug)]
104pub struct BiteParams {
105 pub popsize: i32,
106 pub max_evaluations: u64,
107 pub stop_fitness: f64,
108 pub stall_criterion: i32,
109 pub seed: u64,
110 pub runid: i64,
111}
112
113impl Default for BiteParams {
114 fn default() -> Self {
115 Self {
116 popsize: 0,
117 max_evaluations: 100_000,
118 stop_fitness: f64::NEG_INFINITY,
119 stall_criterion: 0,
120 seed: 0,
121 runid: 0,
122 }
123 }
124}
125
126pub struct BiteRnd {
131 seed: u64,
132 lcg: u64,
133 hash: u64,
134 bit_pool: u64,
135 bits_left: i32,
136}
137
138impl BiteRnd {
139 pub fn new(seed: u64) -> Self {
140 let mut r = BiteRnd {
141 seed,
142 lcg: 0,
143 hash: 0,
144 bit_pool: 0,
145 bits_left: 0,
146 };
147 for _ in 0..5 {
148 r.advance();
149 }
150 r
151 }
152
153 #[inline]
154 fn advance(&mut self) -> u64 {
155 self.seed = self
156 .seed
157 .wrapping_mul(self.lcg.wrapping_mul(2).wrapping_add(1));
158 let rs = self.seed.rotate_left(32);
159 self.hash = self.hash.wrapping_add(rs).wrapping_add(0xAAAAAAAAAAAAAAAA);
160 self.lcg = self
161 .lcg
162 .wrapping_add(self.seed)
163 .wrapping_add(0x5555555555555555);
164 self.seed ^= self.hash;
165 self.lcg ^ rs
166 }
167
168 #[inline]
169 pub fn get(&mut self) -> f64 {
170 (self.advance() >> (64 - 53)) as f64 * (-53f64).exp2()
171 }
172 #[inline]
173 pub fn get_int(&mut self, n: i32) -> i32 {
174 (self.get() * n as f64) as i32
175 }
176 #[inline]
177 fn get_sqr(&mut self) -> f64 {
178 let v = self.get();
179 v * v
180 }
181 #[inline]
182 pub fn get_sqr_int(&mut self, n: i32) -> i32 {
183 (self.get_sqr() * n as f64) as i32
184 }
185 fn get_pow(&mut self, p: f64) -> f64 {
186 let v = self.get();
187 match p {
190 0.25 => v.sqrt().sqrt(),
191 0.5 => v.sqrt(),
192 1.0 => v,
193 1.5 => v * v.sqrt(),
194 1.75 => {
195 let sv = v.sqrt();
196 v * sv * sv.sqrt()
197 }
198 2.0 => v * v,
199 3.0 => v * v * v,
200 4.0 => {
201 let v2 = v * v;
202 v2 * v2
203 }
204 _ => v.powf(p),
205 }
206 }
207 #[inline]
208 pub fn get_pow_int(&mut self, p: f64, n: i32) -> i32 {
209 (self.get_pow(p) * n as f64) as i32
210 }
211 #[inline]
212 pub fn get_raw(&mut self) -> u64 {
213 self.advance()
214 }
215 #[inline]
216 pub fn get_tpdf(&mut self) -> f64 {
217 let v1 = (self.advance() >> (64 - 53)) as i64;
218 let v2 = (self.advance() >> (64 - 53)) as i64;
219 (v1 - v2) as f64 * (-53f64).exp2()
220 }
221 #[inline]
222 pub fn get_bit(&mut self) -> i32 {
223 if self.bits_left == 0 {
224 self.bit_pool = self.advance();
225 let b = (self.bit_pool & 1) as i32;
226 self.bits_left = 63;
227 self.bit_pool >>= 1;
228 return b;
229 }
230 let b = (self.bit_pool & 1) as i32;
231 self.bits_left -= 1;
232 self.bit_pool >>= 1;
233 b
234 }
235 fn get_gaussian(&mut self) -> f64 {
237 loop {
238 let mut u = self.get();
239 let mut v = self.get();
240 if u == 0.0 || v == 0.0 {
241 u = 1.0;
242 v = 1.0;
243 }
244 v = 1.7156 * (v - 0.5);
245 let x = u - 0.449871;
246 let y = v.abs() + 0.386595;
247 let q = x * x + y * (0.19600 * y - 0.25472 * x);
248 if q < 0.27597 {
249 return v / u;
250 }
251 if q <= 0.27846 && v * v <= -4.0 * u.ln() * u * u {
252 return v / u;
253 }
254 }
255 }
256}
257
258fn wrap_param(rnd: &mut BiteRnd, v: i64) -> i64 {
259 if v < 0 {
260 if v > -INT_MANT_MULT {
261 (rnd.get() * (-v) as f64) as i64
262 } else {
263 (rnd.get_raw() as i64) & INT_MANT_MASK
264 }
265 } else if v > INT_MANT_MULT {
266 if v < INT_MANT_MULT * 2 {
267 (INT_MANT_MULT as f64 - rnd.get() * (v - INT_MANT_MULT) as f64) as i64
268 } else {
269 (rnd.get_raw() as i64) & INT_MANT_MASK
270 }
271 } else {
272 v
273 }
274}
275
276fn gaussian_int(rnd: &mut BiteRnd, sd: f64, mean: i64) -> i64 {
277 loop {
278 let r = rnd.get_gaussian() * sd;
279 if r > -8.0 && r < 8.0 {
280 return (r * INT_MANT_MULT as f64) as i64 + mean;
281 }
282 }
283}
284
285const SLOT_COUNT: usize = 5;
290
291#[derive(Clone, Copy)]
295struct SelUse {
296 index: usize,
297 value: i32,
298 position: usize,
299 slot_id: u8,
300 entry_id: u8,
301}
302
303struct BiteSel {
304 count: usize,
305 count_sp: usize,
306 count_sp1: usize,
307 accum_coeff: f64,
308 slot_accums: [f64; SLOT_COUNT],
309 slot_ids: [u8; SLOT_COUNT],
310 sels: [Vec<i32>; SLOT_COUNT],
311 entry_ids: [Vec<u8>; SLOT_COUNT],
312 sel: i32,
313 sel_id: u8,
314 selp: usize,
315 slot: usize,
316}
317
318impl BiteSel {
319 fn new(count: usize) -> Self {
320 BiteSel {
321 count,
322 count_sp: 0,
323 count_sp1: 0,
324 accum_coeff: 0.0,
325 slot_accums: [0.0; SLOT_COUNT],
326 slot_ids: [0, 1, 2, 3, 4],
327 sels: Default::default(),
328 entry_ids: Default::default(),
329 sel: 0,
330 sel_id: 0,
331 selp: 0,
332 slot: 0,
333 }
334 }
335
336 fn reset(&mut self, rnd: &mut BiteRnd, param_count: usize) {
337 let sparse_mul = 5usize;
338 self.count_sp = self.count * sparse_mul;
339 self.count_sp1 = self.count_sp - 1;
340 self.accum_coeff = 1.0 / (param_count as f64).sqrt();
341 for j in 0..SLOT_COUNT {
342 let mut sp = vec![0i32; self.count_sp];
343 let mut ids: Vec<u8> = (0..self.count_sp as u8).collect();
344 for i in 0..self.count {
345 for k in 0..sparse_mul {
346 sp[i * sparse_mul + k] = i as i32;
347 }
348 }
349 for _ in 0..self.count_sp * 5 {
350 let i1 = rnd.get_int(self.count_sp as i32) as usize;
351 let i2 = rnd.get_int(self.count_sp as i32) as usize;
352 sp.swap(i1, i2);
353 ids.swap(i1, i2);
354 }
355 self.sels[j] = sp;
356 self.entry_ids[j] = ids;
357 self.slot_accums[j] = 0.0;
358 self.slot_ids[j] = j as u8;
359 }
360 self.slot = 0;
361 self.select(rnd);
362 }
363
364 fn select(&mut self, rnd: &mut BiteRnd) -> i32 {
365 self.slot = rnd.get_pow_int(1.5, SLOT_COUNT as i32) as usize;
366 self.selp = rnd.get_pow_int(1.5, self.count_sp as i32) as usize;
367 self.sel = self.sels[self.slot][self.selp];
368 self.sel_id = self.entry_ids[self.slot][self.selp];
369 self.sel
370 }
371
372 fn incr(&mut self, v: f64) {
373 let dp = (-(self.selp as f64) * v * v) as i64;
374 if dp < 0 {
375 if dp == -1 {
376 self.sels[self.slot].swap(self.selp, self.selp - 1);
377 self.entry_ids[self.slot].swap(self.selp, self.selp - 1);
378 } else {
379 let np = (self.selp as i64 + dp) as usize;
380 self.sels[self.slot].copy_within(np..self.selp, np + 1);
381 self.entry_ids[self.slot].copy_within(np..self.selp, np + 1);
382 self.sels[self.slot][np] = self.sel;
383 self.entry_ids[self.slot][np] = self.sel_id;
384 }
385 }
386 self.slot_accums[self.slot] += self.accum_coeff;
387 if self.slot_accums[self.slot] >= 1.0 {
388 let a = self.slot_accums[self.slot] - 1.0;
389 if self.slot > 0 {
390 self.sels.swap(self.slot, self.slot - 1);
391 self.entry_ids.swap(self.slot, self.slot - 1);
392 self.slot_ids.swap(self.slot, self.slot - 1);
393 self.slot_accums[self.slot] = self.slot_accums[self.slot - 1];
394 self.slot_accums[self.slot - 1] = a;
395 } else {
396 self.slot_accums[self.slot] = a;
397 }
398 }
399 }
400
401 fn decr(&mut self) {
402 if self.selp < self.count_sp1 {
403 self.sels[self.slot].swap(self.selp, self.selp + 1);
404 self.entry_ids[self.slot].swap(self.selp, self.selp + 1);
405 }
406 self.slot_accums[self.slot] -= self.accum_coeff;
407 if self.slot_accums[self.slot] <= -1.0 {
408 let a = self.slot_accums[self.slot] + 1.0;
409 if self.slot < SLOT_COUNT - 1 {
410 self.sels.swap(self.slot, self.slot + 1);
411 self.entry_ids.swap(self.slot, self.slot + 1);
412 self.slot_ids.swap(self.slot, self.slot + 1);
413 self.slot_accums[self.slot] = self.slot_accums[self.slot + 1];
414 self.slot_accums[self.slot + 1] = a;
415 } else {
416 self.slot_accums[self.slot] = a;
417 }
418 }
419 }
420
421 fn captured(&self, index: usize) -> SelUse {
422 SelUse {
423 index,
424 value: self.sel,
425 position: self.selp,
426 slot_id: self.slot_ids[self.slot],
427 entry_id: self.sel_id,
428 }
429 }
430
431 fn restore(&mut self, selection: SelUse) {
432 self.slot = self
433 .slot_ids
434 .iter()
435 .position(|&id| id == selection.slot_id)
436 .unwrap_or(0);
437 self.sel = selection.value;
438 self.sel_id = selection.entry_id;
439 self.selp = self.entry_ids[self.slot]
440 .iter()
441 .position(|&id| id == selection.entry_id)
442 .or_else(|| {
443 self.sels[self.slot]
444 .iter()
445 .enumerate()
446 .filter(|(_, value)| **value == selection.value)
447 .min_by_key(|(position, _)| position.abs_diff(selection.position))
448 .map(|(position, _)| position)
449 })
450 .unwrap_or(selection.position.min(self.count_sp1));
451 }
452
453 fn incr_captured(&mut self, selection: SelUse, value: f64) {
454 self.restore(selection);
455 self.incr(value);
456 }
457
458 fn decr_captured(&mut self, selection: SelUse) {
459 self.restore(selection);
460 self.decr();
461 }
462}
463
464mod sel {
466 pub const METHOD: usize = 0;
467 pub const M1: usize = 1;
468 pub const M1A: usize = 2;
469 pub const M1B: usize = 3;
470 pub const M1C: usize = 4;
471 pub const M2: usize = 5;
472 pub const M2B: usize = 6;
473 pub const POP_CHANGE_INCR: usize = 7;
474 pub const POP_CHANGE_DECR: usize = 8;
475 pub const PAR_OPT2: usize = 9;
476 pub const PAR_POP_P: usize = 10; pub const ALT_POP_P: usize = 14;
478 pub const ALT_POP: usize = 15; pub const MIN_SOL_PWR: usize = 19; pub const MIN_SOL_MUL: usize = 23; pub const GEN1_ALLP: usize = 27;
482 pub const GEN1_MOVE_ASYNC: usize = 28;
483 pub const GEN1_MOVE_SPAN: usize = 29;
484 pub const GEN2_MODE: usize = 30;
485 pub const GEN2B_MODE: usize = 31;
486 pub const GEN2C_MODE: usize = 32;
487 pub const GEN2D_MODE: usize = 33;
488 pub const GEN3_MODE: usize = 34;
489 pub const GEN4_MIX_FAC: usize = 35;
490 pub const GEN5B_MODE: usize = 36;
491 pub const GEN7_POW_FAC: usize = 37;
492 pub const GEN8_MODE: usize = 38;
493 pub const GEN8_NUM: usize = 39;
494 pub const GEN8_SPAN: usize = 40; pub const COUNT: usize = 42;
496}
497
498fn build_selectors() -> Vec<BiteSel> {
499 let mut s = Vec::with_capacity(sel::COUNT);
500 s.push(BiteSel::new(4)); s.push(BiteSel::new(4)); s.push(BiteSel::new(3)); s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); s.push(BiteSel::new(4)); s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); for _ in 0..4 {
511 s.push(BiteSel::new(2)); }
513 s.push(BiteSel::new(2)); for _ in 0..4 {
515 s.push(BiteSel::new(2)); }
517 for _ in 0..4 {
518 s.push(BiteSel::new(4)); }
520 for _ in 0..4 {
521 s.push(BiteSel::new(4)); }
523 s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); s.push(BiteSel::new(4)); s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); s.push(BiteSel::new(2)); s.push(BiteSel::new(4)); s.push(BiteSel::new(4)); s.push(BiteSel::new(2)); s.push(BiteSel::new(4)); s.push(BiteSel::new(2)); s.push(BiteSel::new(4)); for _ in 0..2 {
537 s.push(BiteSel::new(4)); }
539 s
540}
541
542#[derive(Clone)]
547struct BitePop {
548 param_count: usize,
549 pop_size: usize,
550 params: Vec<Vec<i64>>, costs: Vec<f64>,
552 cent: Vec<i64>,
553 cur_pop_pos: usize,
554 cur_pop_size: usize,
556 cur_pop_size1: usize,
557 cur_pop_size_i: f64,
558 need_cent: bool,
559 cent_lpc: f64,
560}
561
562fn calc_lp1_coeff(count: f64) -> f64 {
563 let theta = 2.8 / count;
564 let costheta2 = 2.0 - theta.cos();
565 1.0 - (costheta2 - (costheta2 * costheta2 - 1.0).sqrt())
566}
567
568impl BitePop {
569 fn new(param_count: usize, pop_size: usize) -> Self {
570 BitePop {
571 param_count,
572 pop_size,
573 params: vec![vec![0i64; param_count]; pop_size],
574 costs: vec![1e300; pop_size],
575 cent: vec![0i64; param_count],
576 cur_pop_pos: 0,
577 cur_pop_size: pop_size,
578 cur_pop_size1: pop_size - 1,
579 cur_pop_size_i: 1.0 / pop_size as f64,
580 need_cent: false,
581 cent_lpc: calc_lp1_coeff(pop_size as f64),
582 }
583 }
584
585 fn reset_cur_pop_pos(&mut self) {
586 self.cur_pop_pos = 0;
587 self.cur_pop_size = self.pop_size;
588 self.cur_pop_size1 = self.pop_size - 1;
589 self.cur_pop_size_i = 1.0 / self.pop_size as f64;
590 self.need_cent = false;
591 self.cent_lpc = calc_lp1_coeff(self.pop_size as f64);
592 }
593
594 fn incr_cur_pop_size(&mut self) {
595 self.cur_pop_size += 1;
596 self.cur_pop_size1 += 1;
597 self.cur_pop_size_i = 1.0 / self.cur_pop_size as f64;
598 self.need_cent = true;
599 self.cent_lpc = calc_lp1_coeff(self.cur_pop_size as f64);
600 }
601
602 fn decr_cur_pop_size(&mut self) {
603 self.cur_pop_size -= 1;
604 self.cur_pop_size1 -= 1;
605 self.cur_pop_size_i = 1.0 / self.cur_pop_size as f64;
606 self.need_cent = true;
607 self.cent_lpc = calc_lp1_coeff(self.cur_pop_size as f64);
608 }
609
610 fn update_pop(&mut self, mut cost: f64, up: &[i64], do_update_centroid: bool) -> usize {
613 let ri;
614 if self.cur_pop_pos < self.pop_size {
615 ri = self.cur_pop_pos;
616 if cost.is_nan() {
617 cost = 1e300;
618 }
619 } else {
620 ri = self.pop_size - 1;
621 if cost.is_nan() || cost >= self.costs[ri] {
622 return self.pop_size;
623 }
624 }
625 let mut p = 0usize;
627 let mut i = ri;
628 while p < i {
629 let mid = (p + i) >> 1;
630 if self.costs[mid] >= cost {
631 i = mid;
632 } else {
633 p = mid + 1;
634 }
635 }
636 if self.cur_pop_pos < self.pop_size {
637 self.cur_pop_pos += 1;
638 }
639 for k in (p + 1..=ri).rev() {
641 self.params.swap(k, k - 1);
642 self.costs[k] = self.costs[k - 1];
643 }
644 self.costs[p] = cost;
645 if self.params[p] != up {
646 if do_update_centroid {
647 for (c, &u) in self.cent.iter_mut().zip(up) {
648 *c += ((u - *c) as f64 * self.cent_lpc) as i64;
649 }
650 self.params[p].copy_from_slice(up);
651 } else {
652 self.params[p].copy_from_slice(up);
653 self.need_cent = true;
654 }
655 } else {
656 self.need_cent = true;
657 }
658 p
659 }
660
661 fn update_centroid(&mut self) {
662 self.need_cent = false;
663 let cm = 1.0 / self.pop_size as f64;
664 for j in 0..self.param_count {
665 let mut sum = 0i128;
666 for row in &self.params {
667 sum += row[j] as i128;
668 }
669 self.cent[j] = (sum as f64 * cm) as i64;
670 }
671 }
672
673 #[inline]
674 fn ordered(&self, i: usize) -> &[i64] {
675 &self.params[i]
676 }
677
678 #[inline]
679 fn cur_pop_size(&self) -> usize {
680 self.cur_pop_size
681 }
682
683 fn get_centroid(&mut self) -> &[i64] {
684 if self.need_cent {
685 self.update_centroid();
686 }
687 &self.cent
688 }
689
690 fn copy_from(&mut self, src: &BitePop) {
692 for (d, s) in self.params.iter_mut().zip(&src.params) {
693 d.copy_from_slice(s);
694 }
695 self.costs.copy_from_slice(&src.costs);
696 self.cent.copy_from_slice(&src.cent);
697 self.cur_pop_pos = src.cur_pop_pos;
698 self.cur_pop_size = src.cur_pop_size;
699 self.cur_pop_size1 = src.cur_pop_size1;
700 self.cur_pop_size_i = src.cur_pop_size_i;
701 self.need_cent = src.need_cent;
702 self.cent_lpc = src.cent_lpc;
703 }
704}
705
706fn wrap01(rnd: &mut BiteRnd, v: f64) -> f64 {
712 if v < 0.0 {
713 if v > -1.0 { rnd.get() * -v } else { rnd.get() }
714 } else if v > 1.0 {
715 if v < 2.0 {
716 1.0 - rnd.get() * (v - 1.0)
717 } else {
718 rnd.get()
719 }
720 } else {
721 v
722 }
723}
724
725fn wrap_param_real(rnd: &mut BiteRnd, v: f64, minv: f64, diffv: f64) -> f64 {
727 if v < minv {
728 if v > minv - diffv {
729 minv + rnd.get() * (minv - v)
730 } else {
731 minv + rnd.get() * diffv
732 }
733 } else {
734 let maxv = minv + diffv;
735 if v > maxv {
736 if v < maxv + diffv {
737 maxv - rnd.get() * (v - maxv)
738 } else {
739 maxv - rnd.get() * diffv
740 }
741 } else {
742 v
743 }
744 }
745}
746
747struct ParStep {
749 stall: i64,
750 cost: f64,
751 values: Vec<f64>,
752}
753
754struct SpherOpt {
756 dim: usize,
757 pop_size: usize,
758 params: Vec<Vec<f64>>, costs: Vec<f64>,
760 cur_pop_pos: usize,
761 cent: Vec<f64>,
762 min_values: Vec<f64>,
763 diff_values: Vec<f64>,
764 sels: [BiteSel; 3], apply: Vec<usize>,
766 radius: f64,
767 eval_fac: f64,
768 cure: i32,
769 curem: i32,
770 do_cent_eval: bool,
771 jit_mult: f64,
772 jit_offs: f64,
773 avg_cost: f64,
774 hi_bound: f64,
775 stall_count: i64,
776 best_cost: f64,
777 best_values: Vec<f64>,
778}
779
780impl SpherOpt {
781 fn new(dim: usize, min_values: Vec<f64>, diff_values: Vec<f64>, pop_size: usize) -> Self {
782 let dim_i = 1.0 / dim as f64;
783 SpherOpt {
784 dim,
785 pop_size,
786 params: vec![vec![0.0; dim]; pop_size],
787 costs: vec![1e300; pop_size],
788 cur_pop_pos: 0,
789 cent: vec![0.5; dim],
790 min_values,
791 diff_values,
792 sels: [BiteSel::new(4), BiteSel::new(4), BiteSel::new(3)],
793 apply: Vec::new(),
794 radius: 0.5,
795 eval_fac: 2.0,
796 cure: 0,
797 curem: 0,
798 do_cent_eval: false,
799 jit_mult: 5.0 * dim_i,
800 jit_offs: 1.0 - 5.0 * dim_i * 0.5,
801 avg_cost: 0.0,
802 hi_bound: 1e300,
803 stall_count: 0,
804 best_cost: 1e300,
805 best_values: vec![0.0; dim],
806 }
807 }
808
809 fn real_value(&self, norm: &[f64], i: usize) -> f64 {
810 self.min_values[i] + self.diff_values[i] * norm[i]
811 }
812
813 fn init(&mut self, rnd: &mut BiteRnd, init_params: Option<&[f64]>, radius: f64) {
814 self.best_cost = 1e300;
815 self.stall_count = 0;
816 self.hi_bound = 1e300;
817 self.avg_cost = 0.0;
818 for sel in self.sels.iter_mut() {
819 sel.reset(rnd, self.dim);
820 }
821 self.cur_pop_pos = 0;
822 self.radius = 0.5 * radius;
823 self.eval_fac = 2.0;
824 self.cure = 0;
825 self.curem = (self.pop_size as f64 * self.eval_fac).ceil() as i32;
826 match init_params {
827 None => {
828 self.cent = vec![0.5; self.dim];
829 self.do_cent_eval = false;
830 }
831 Some(ip) => {
832 for i in 0..self.dim {
833 self.cent[i] = wrap01(rnd, (ip[i] - self.min_values[i]) / self.diff_values[i]);
834 }
835 self.do_cent_eval = true;
836 }
837 }
838 }
839
840 fn update_pop(&mut self, cost: f64, params: &[f64]) {
841 let ri;
842 if self.cur_pop_pos < self.pop_size {
843 ri = self.cur_pop_pos;
844 } else {
845 ri = self.pop_size - 1;
846 if cost >= self.costs[ri] {
847 return;
848 }
849 }
850 let mut p = 0usize;
851 let mut i = ri;
852 while p < i {
853 let mid = (p + i) >> 1;
854 if self.costs[mid] >= cost {
855 i = mid;
856 } else {
857 p = mid + 1;
858 }
859 }
860 if self.cur_pop_pos < self.pop_size {
861 self.cur_pop_pos += 1;
862 }
863 for k in (p + 1..=ri).rev() {
864 self.params.swap(k, k - 1);
865 self.costs[k] = self.costs[k - 1];
866 }
867 self.params[p].copy_from_slice(params);
868 self.costs[p] = cost;
869 }
870
871 fn optimize(&mut self, rnd: &mut BiteRnd, obj: &dyn Objective) -> ParStep {
872 let mut params = vec![0.0; self.dim];
873 let mut new_values = vec![0.0; self.dim];
874 if self.do_cent_eval {
875 self.do_cent_eval = false;
876 for i in 0..self.dim {
877 params[i] = self.cent[i];
878 new_values[i] = self.real_value(&self.cent, i);
879 }
880 } else {
881 let mut s2 = 1e-300;
882 for pi in params.iter_mut() {
883 *pi = rnd.get() - 0.5;
884 s2 += *pi * *pi;
885 }
886 let d = self.radius / s2.sqrt();
887 if self.dim > 4 {
888 for i in 0..self.dim {
889 params[i] = wrap01(rnd, self.cent[i] + params[i] * d);
890 new_values[i] = self.real_value(¶ms, i);
891 }
892 } else {
893 for i in 0..self.dim {
894 let m = self.jit_offs + rnd.get() * self.jit_mult;
895 params[i] = wrap01(rnd, self.cent[i] + params[i] * d * m);
896 new_values[i] = self.real_value(¶ms, i);
897 }
898 }
899 }
900 let cost = objective_cost(obj, &new_values);
901 self.update_pop(cost, ¶ms);
902 if cost <= self.best_cost {
903 self.best_cost = cost;
904 self.best_values.copy_from_slice(&new_values);
905 }
906 self.avg_cost += cost;
907 self.cure += 1;
908 if self.cure >= self.curem {
909 self.avg_cost /= self.cure as f64;
910 if self.avg_cost < self.hi_bound {
911 self.hi_bound = self.avg_cost;
912 self.stall_count = 0;
913 for &s in &self.apply {
914 self.sels[s].incr(1.0);
915 }
916 } else {
917 self.stall_count += self.cure as i64;
918 for &s in &self.apply {
919 self.sels[s].decr();
920 }
921 }
922 self.apply.clear();
923 self.cur_pop_pos = 0;
924 self.avg_cost = 0.0;
925 self.cure = 0;
926 self.update(rnd);
927 self.curem = (self.pop_size as f64 * self.eval_fac).ceil() as i32;
928 }
929 ParStep {
930 stall: self.stall_count,
931 cost,
932 values: new_values,
933 }
934 }
935
936 fn sel(&mut self, idx: usize, rnd: &mut BiteRnd) -> i32 {
937 self.apply.push(idx);
938 self.sels[idx].select(rnd)
939 }
940
941 fn update(&mut self, rnd: &mut BiteRnd) {
942 const WCENT: [f64; 4] = [4.5, 6.0, 7.5, 10.0];
943 const WRAD: [f64; 4] = [14.0, 16.0, 18.0, 20.0];
944 const EVAL_FACS: [f64; 3] = [2.1, 2.0, 1.9];
945 let cent_fac = WCENT[self.sel(0, rnd) as usize];
946 let rad_fac = WRAD[self.sel(1, rnd) as usize];
947 self.eval_fac = EVAL_FACS[self.sel(2, rnd) as usize];
948
949 let lm = 1.0 / self.curem as f64;
950 let mut wc = vec![0.0; self.pop_size];
951 let mut wr = vec![0.0; self.pop_size];
952 let mut s1 = 0.0;
953 let mut s2 = 0.0;
954 for i in 0..self.pop_size {
955 let l = 1.0 - i as f64 * lm;
956 let v1 = l.powf(cent_fac);
957 wc[i] = v1;
958 s1 += v1;
959 let v2 = l.powf(rad_fac);
960 wr[i] = v2;
961 s2 += v2;
962 }
963 s1 = 1.0 / s1;
964 s2 = 1.0 / s2;
965 for j in 0..self.dim {
966 let mut acc = 0.0;
967 for i in 0..self.pop_size {
968 acc += self.params[i][j] * wc[i] * s1;
969 }
970 self.cent[j] = acc;
971 }
972 let mut radius = 0.0;
973 for i in 0..self.pop_size {
974 let mut s = 0.0;
975 for j in 0..self.dim {
976 let d = self.params[i][j] - self.cent[j];
977 s += d * d;
978 }
979 radius += s * wr[i];
980 }
981 self.radius = (radius * s2).sqrt();
982 }
983}
984
985struct NMSeqOpt {
987 n: usize,
988 m: usize,
989 m1: usize,
990 m1i: f64,
991 param_count_i: f64,
992 x: Vec<Vec<f64>>, y: Vec<f64>, x0: Vec<f64>, x1: Vec<f64>,
996 x2: Vec<f64>,
997 y1: f64,
998 xlo: usize,
999 xhi: usize,
1000 xhi2: usize,
1001 rx: usize, rj: usize,
1003 do_init_evals: bool,
1004 cur_pop_pos: usize,
1005 state: NmState,
1006 stall_count: i64,
1007 min_values: Vec<f64>,
1008 diff_values: Vec<f64>,
1009 best_cost: f64,
1010 best_values: Vec<f64>,
1011}
1012
1013#[derive(Clone, Copy, PartialEq)]
1014enum NmState {
1015 Reflection,
1016 Expansion,
1017 Contraction,
1018 Reduction,
1019}
1020
1021impl NMSeqOpt {
1022 fn new(dim: usize, min_values: Vec<f64>, diff_values: Vec<f64>) -> Self {
1023 let m = (dim + 1) * 4;
1024 NMSeqOpt {
1025 n: dim,
1026 m,
1027 m1: m - 1,
1028 m1i: 1.0 / (m - 1) as f64,
1029 param_count_i: 1.0 / dim as f64,
1030 x: vec![vec![0.0; dim]; m],
1031 y: vec![1e300; m],
1032 x0: vec![0.0; dim],
1033 x1: vec![0.0; dim],
1034 x2: vec![0.0; dim],
1035 y1: 0.0,
1036 xlo: 0,
1037 xhi: 0,
1038 xhi2: 0,
1039 rx: 0,
1040 rj: 0,
1041 do_init_evals: true,
1042 cur_pop_pos: 0,
1043 state: NmState::Reflection,
1044 stall_count: 0,
1045 min_values,
1046 diff_values,
1047 best_cost: 1e300,
1048 best_values: vec![0.0; dim],
1049 }
1050 }
1051
1052 fn init(&mut self, rnd: &mut BiteRnd, init_params: Option<&[f64]>, radius: f64) {
1053 self.best_cost = 1e300;
1054 self.stall_count = 0;
1055 match init_params {
1056 Some(ip) => self.x[0].copy_from_slice(ip),
1057 None => {
1058 for i in 0..self.n {
1059 self.x[0][i] = self.min_values[i] + self.diff_values[i] * 0.5;
1060 }
1061 }
1062 }
1063 self.xlo = 0;
1064 let base = self.x[0].clone();
1065 if radius <= 0.0 {
1066 for j in 1..self.m {
1067 for i in 0..self.n {
1068 self.x[j][i] = self.min_values[i] + self.diff_values[i] * rnd.get();
1069 }
1070 }
1071 } else {
1072 let sd = 0.25 * radius;
1073 for j in 1..self.m {
1074 for i in 0..self.n {
1075 self.x[j][i] = base[i] + self.diff_values[i] * rnd.get_gaussian() * sd;
1076 }
1077 }
1078 }
1079 self.state = NmState::Reflection;
1080 self.do_init_evals = true;
1081 self.cur_pop_pos = 0;
1082 }
1083
1084 fn eval(&mut self, rnd: &mut BiteRnd, params: &[f64], obj: &dyn Objective) -> (f64, Vec<f64>) {
1085 let mut nv = vec![0.0; self.n];
1086 for i in 0..self.n {
1087 nv[i] = wrap_param_real(rnd, params[i], self.min_values[i], self.diff_values[i]);
1088 }
1089 let cost = objective_cost(obj, &nv);
1090 if cost <= self.best_cost {
1091 self.best_cost = cost;
1092 self.best_values.copy_from_slice(&nv);
1093 }
1094 (cost, nv)
1095 }
1096
1097 fn find_hi(&mut self) {
1098 self.xhi2 = if self.y[0] > self.y[1] { 0 } else { 1 };
1099 self.xhi = 1 - self.xhi2;
1100 for j in 2..self.m {
1101 if self.y[j] > self.y[self.xhi] {
1102 self.xhi2 = self.xhi;
1103 self.xhi = j;
1104 } else if self.y[j] > self.y[self.xhi2] {
1105 self.xhi2 = j;
1106 }
1107 }
1108 }
1109
1110 fn calc_cent(&mut self) {
1111 self.find_hi();
1112 let mut xc = vec![0.0; self.n];
1113 for (j, xj) in self.x.iter().enumerate() {
1114 if j == self.xhi {
1115 continue;
1116 }
1117 for i in 0..self.n {
1118 xc[i] += xj[i];
1119 }
1120 }
1121 for c in xc.iter_mut() {
1122 *c *= self.m1i;
1123 }
1124 self.x0 = xc;
1125 }
1126
1127 fn copy(&mut self, ip: &[f64], cost: f64) {
1128 let replaced_index = self.xhi;
1129 self.y[replaced_index] = cost;
1130 self.x[replaced_index].copy_from_slice(ip);
1131 let replacement = self.x[replaced_index].clone();
1132 self.find_hi();
1133 if replaced_index != self.xhi {
1134 for i in 0..self.n {
1135 self.x0[i] += (replacement[i] - self.x[self.xhi][i]) * self.m1i;
1136 }
1137 }
1138 self.stall_count = 0;
1139 }
1140
1141 fn optimize(&mut self, rnd: &mut BiteRnd, obj: &dyn Objective) -> ParStep {
1142 if self.do_init_evals {
1143 let xp = self.x[self.cur_pop_pos].clone();
1144 let (out_cost, out_values) = self.eval(rnd, &xp, obj);
1145 self.y[self.cur_pop_pos] = out_cost;
1146 if self.y[self.cur_pop_pos] < self.y[self.xlo] {
1147 self.xlo = self.cur_pop_pos;
1148 }
1149 self.cur_pop_pos += 1;
1150 if self.cur_pop_pos == self.m {
1151 self.do_init_evals = false;
1152 self.calc_cent();
1153 }
1154 return ParStep {
1155 stall: 0,
1156 cost: out_cost,
1157 values: out_values,
1158 };
1159 }
1160
1161 let out_cost;
1162 let out_values;
1163 self.stall_count += 1;
1164 let sn = 0.5 * self.param_count_i.sqrt();
1165 let alpha = 1.0;
1166 let gamma = 1.5 + sn;
1167 let rho = -0.75 + sn;
1168 let sigma = 1.0 - sn;
1169 let xh = self.x[self.xhi].clone();
1170
1171 match self.state {
1172 NmState::Reflection => {
1173 for i in 0..self.n {
1174 self.x1[i] = self.x0[i] + alpha * (self.x0[i] - xh[i]);
1175 }
1176 let x1 = self.x1.clone();
1177 let (c, v) = self.eval(rnd, &x1, obj);
1178 self.y1 = c;
1179 out_cost = c;
1180 out_values = v;
1181 if self.y1 > self.y[self.xlo] && self.y1 < self.y[self.xhi2] {
1182 let x1c = self.x1.clone();
1183 self.copy(&x1c, self.y1);
1184 } else if self.y1 < self.y[self.xlo] {
1185 self.state = NmState::Expansion;
1186 self.stall_count -= 1;
1187 } else {
1188 self.state = NmState::Contraction;
1189 }
1190 }
1191 NmState::Expansion => {
1192 for i in 0..self.n {
1193 self.x2[i] = self.x0[i] + gamma * (self.x0[i] - xh[i]);
1194 }
1195 let x2 = self.x2.clone();
1196 let (y2, v) = self.eval(rnd, &x2, obj);
1197 out_cost = y2;
1198 out_values = v;
1199 self.xlo = self.xhi;
1200 if y2 < self.y1 {
1201 let x2c = self.x2.clone();
1202 self.copy(&x2c, y2);
1203 } else {
1204 let x1c = self.x1.clone();
1205 self.copy(&x1c, self.y1);
1206 }
1207 self.state = NmState::Reflection;
1208 }
1209 NmState::Contraction => {
1210 for i in 0..self.n {
1211 self.x2[i] = self.x0[i] + rho * (self.x0[i] - xh[i]);
1212 }
1213 let x2 = self.x2.clone();
1214 let (y2, v) = self.eval(rnd, &x2, obj);
1215 out_cost = y2;
1216 out_values = v;
1217 if y2 < self.y[self.xhi] {
1218 if y2 < self.y[self.xlo] {
1219 self.xlo = self.xhi;
1220 }
1221 let x2c = self.x2.clone();
1222 self.copy(&x2c, y2);
1223 self.state = NmState::Reflection;
1224 } else {
1225 self.rx = self.xlo;
1226 self.rj = 0;
1227 self.state = NmState::Reduction;
1228 }
1229 }
1230 NmState::Reduction => {
1231 if self.rj == self.rx {
1232 self.rj += 1;
1233 }
1234 let rxv = self.x[self.rx].clone();
1235 for i in 0..self.n {
1236 self.x[self.rj][i] = rxv[i] + sigma * (self.x[self.rj][i] - rxv[i]);
1237 }
1238 let xx = self.x[self.rj].clone();
1239 let (c, v) = self.eval(rnd, &xx, obj);
1240 self.y[self.rj] = c;
1241 out_cost = c;
1242 out_values = v;
1243 if self.y[self.rj] < self.y[self.xlo] {
1244 self.xlo = self.rj;
1245 self.stall_count = 0;
1246 }
1247 self.rj += 1;
1248 if self.rj == self.m || (self.rj == self.m1 && self.rj == self.rx) {
1249 self.calc_cent();
1250 self.state = NmState::Reflection;
1251 }
1252 }
1253 }
1254
1255 ParStep {
1256 stall: self.stall_count,
1257 cost: out_cost,
1258 values: out_values,
1259 }
1260 }
1261}
1262
1263pub struct BiteOpt {
1268 param_count: usize,
1269 param_count_i: f64,
1270 pop_size: usize,
1271 min_values: Vec<f64>,
1272 diff_values: Vec<f64>,
1273 diff_values_i: Vec<f64>,
1274
1275 pop: BitePop,
1276 old_pop: BitePop,
1277 par_pop_count: usize,
1278 par_pops: Vec<BitePop>,
1279 par_opt_pop: BitePop,
1280 par_opt2_pop: BitePop,
1281 spher: SpherOpt,
1282 nmseq: NMSeqOpt,
1283 use_par_opt: i32,
1284
1285 sels: Vec<BiteSel>,
1286 apply_sels: Vec<SelUse>,
1287 deferred_sels: VecDeque<SelUse>,
1288 rnd: BiteRnd,
1289
1290 tmp: Vec<i64>,
1291 real_tmp: Vec<f64>,
1292 best_cost: f64,
1293 best_values: Vec<f64>,
1294 stall_count: i64,
1295
1296 init_queue: VecDeque<Vec<i64>>,
1298 asked: Vec<Candidate>,
1299
1300 max_evaluations: u64,
1301 stopfitness: f64,
1302 stall_criterion: i32,
1303 evaluations: u64,
1304 iterations: i32,
1305 stop: i32,
1306}
1307
1308impl BiteOpt {
1309 pub fn new(lower: &[f64], upper: &[f64], init: Option<&[f64]>, p: &BiteParams) -> Self {
1310 validate_bite_inputs(lower, upper, init, p, 1).expect("invalid BiteOpt configuration");
1311 let param_count = lower.len();
1312 let pop_size = if p.popsize > 0 {
1313 p.popsize as usize
1314 } else {
1315 9 + param_count * 3
1316 };
1317 let min_values = lower.to_vec();
1318 let diff_values: Vec<f64> = upper
1319 .iter()
1320 .zip(lower)
1321 .map(|(u, l)| (u - l) / INT_MANT_MULT as f64)
1322 .collect();
1323 let diff_values_i: Vec<f64> = diff_values.iter().map(|d| 1.0 / d).collect();
1324 let real_diff: Vec<f64> = upper.iter().zip(lower).map(|(u, l)| u - l).collect();
1325 let par_pop_count = 4;
1326 let mut b = BiteOpt {
1327 param_count,
1328 param_count_i: 1.0 / param_count as f64,
1329 pop_size,
1330 min_values: min_values.clone(),
1331 diff_values,
1332 diff_values_i,
1333 pop: BitePop::new(param_count, pop_size),
1334 old_pop: BitePop::new(param_count, pop_size),
1335 par_pop_count,
1336 par_pops: (0..par_pop_count)
1337 .map(|_| BitePop::new(param_count, pop_size))
1338 .collect(),
1339 par_opt_pop: BitePop::new(param_count, pop_size),
1340 par_opt2_pop: BitePop::new(param_count, pop_size),
1341 spher: SpherOpt::new(
1342 param_count,
1343 min_values.clone(),
1344 real_diff.clone(),
1345 14 + param_count,
1346 ),
1347 nmseq: NMSeqOpt::new(param_count, min_values, real_diff),
1348 use_par_opt: 0,
1349 sels: build_selectors(),
1350 apply_sels: Vec::with_capacity(32),
1351 deferred_sels: VecDeque::with_capacity(2),
1352 rnd: BiteRnd::new(p.seed.wrapping_add(p.runid as u64)),
1353 tmp: vec![0; param_count],
1354 real_tmp: vec![0.0; param_count],
1355 best_cost: 1e300,
1356 best_values: vec![0.0; param_count],
1357 stall_count: 0,
1358 init_queue: VecDeque::new(),
1359 asked: Vec::new(),
1360 max_evaluations: if p.max_evaluations > 0 {
1361 p.max_evaluations
1362 } else {
1363 50_000
1364 },
1365 stopfitness: p.stop_fitness,
1366 stall_criterion: p.stall_criterion.max(0),
1367 evaluations: 0,
1368 iterations: 0,
1369 stop: 0,
1370 };
1371 b.init(init);
1372 b
1373 }
1374
1375 fn init(&mut self, init: Option<&[f64]>) {
1376 let seed_reset: Vec<usize> = (0..self.sels.len()).collect();
1377 for i in seed_reset {
1378 self.sels[i].reset(&mut self.rnd, self.param_count);
1379 }
1380 self.pop.reset_cur_pop_pos();
1381 self.old_pop.reset_cur_pop_pos();
1382 self.par_opt_pop.reset_cur_pop_pos();
1383 self.par_opt2_pop.reset_cur_pop_pos();
1384 let init_slice = init.map(|x| x.to_vec());
1385 self.spher.init(&mut self.rnd, init_slice.as_deref(), 1.0);
1386 self.nmseq.init(&mut self.rnd, init_slice.as_deref(), 1.0);
1387 self.use_par_opt = 0;
1388 self.init_queue.clear();
1389 self.asked.clear();
1390 self.deferred_sels.clear();
1391 let sd = 0.25;
1392 let mut members: Vec<Vec<i64>> = vec![vec![0i64; self.param_count]; self.pop_size];
1393 match init {
1394 None => {
1395 for member in members.iter_mut() {
1396 for slot in member.iter_mut() {
1397 let g = gaussian_int(&mut self.rnd, sd, INT_MANT_MULT >> 1);
1398 *slot = wrap_param(&mut self.rnd, g);
1399 }
1400 }
1401 }
1402 Some(x0) => {
1403 for i in 0..self.param_count {
1404 let v = ((x0[i] - self.min_values[i]) / self.diff_values[i]) as i64;
1405 members[0][i] = wrap_param(&mut self.rnd, v);
1406 }
1407 for j in 1..self.pop_size {
1408 #[allow(clippy::needless_range_loop)]
1409 for i in 0..self.param_count {
1410 let mean = members[0][i];
1411 let g = gaussian_int(&mut self.rnd, sd, mean);
1412 members[j][i] = wrap_param(&mut self.rnd, g);
1413 }
1414 }
1415 }
1416 }
1417 self.init_queue = members.into_iter().collect();
1418 self.best_cost = 1e300;
1419 self.stall_count = 0;
1420 }
1421
1422 #[inline]
1423 fn real_value(&self, params: &[i64], i: usize) -> f64 {
1424 self.min_values[i] + self.diff_values[i] * params[i] as f64
1425 }
1426
1427 #[inline]
1428 fn take_tmp(&mut self) -> Vec<i64> {
1429 let mut params = std::mem::take(&mut self.tmp);
1430 params.resize(self.param_count, 0);
1431 params.fill(0);
1432 params
1433 }
1434
1435 fn recycle_candidate(&mut self, mut candidate: Candidate) {
1436 if candidate.enc.capacity() >= self.tmp.capacity() {
1437 candidate.enc.resize(self.param_count, 0);
1438 self.tmp = candidate.enc;
1439 }
1440 if candidate.real.capacity() >= self.real_tmp.capacity() {
1441 candidate.real.resize(self.param_count, 0.0);
1442 self.real_tmp = candidate.real;
1443 }
1444 if candidate.sels.capacity() >= self.apply_sels.capacity() {
1445 candidate.sels.clear();
1446 self.apply_sels = candidate.sels;
1447 }
1448 }
1449
1450 fn select(&mut self, sel_idx: usize) -> i32 {
1451 let value = self.sels[sel_idx].select(&mut self.rnd);
1452 self.apply_sels.push(self.sels[sel_idx].captured(sel_idx));
1453 value
1454 }
1455
1456 fn get_min_sol_index(&mut self, gi: usize, ps: usize) -> usize {
1457 const PP: [f64; 4] = [0.05, 0.125, 0.25, 0.5];
1458 const RM: [f64; 4] = [0.0, 0.125, 0.25, 0.5];
1459 let pwr = self.select(sel::MIN_SOL_PWR + gi) as usize;
1460 let r = ps as f64 * self.rnd.get_pow(ps as f64 * PP[pwr]);
1461 let mul = self.select(sel::MIN_SOL_MUL + gi) as usize;
1462 (r * RM[mul]) as usize
1463 }
1464
1465 fn update_best_cost(&mut self, cost: f64, values: &[f64], p: i64) {
1466 if cost.is_nan() {
1467 return;
1468 }
1469 if p == 0 || (p < 0 && cost <= self.best_cost) {
1470 self.best_cost = cost;
1471 self.best_values.copy_from_slice(values);
1472 }
1473 }
1474
1475 fn pop_ref(&self, s: PopSel) -> &BitePop {
1478 match s {
1479 PopSel::Main => &self.pop,
1480 PopSel::Par(i) => &self.par_pops[i],
1481 PopSel::ParOpt => &self.par_opt_pop,
1482 PopSel::ParOpt2 => &self.par_opt2_pop,
1483 }
1484 }
1485
1486 fn ordered_of(&self, s: PopSel, i: usize) -> Vec<i64> {
1487 self.pop_ref(s).ordered(i).to_vec()
1488 }
1489
1490 fn cur_pop_size_of(&self, s: PopSel) -> usize {
1491 self.pop_ref(s).cur_pop_size()
1492 }
1493
1494 fn select_par_pop(&mut self, gi: usize) -> PopSel {
1495 if self.select(sel::PAR_POP_P + gi) != 0 {
1496 PopSel::Par(self.rnd.get_int(self.par_pop_count as i32) as usize)
1497 } else {
1498 PopSel::Main
1499 }
1500 }
1501
1502 fn select_alt_pop(&mut self, gi: usize) -> PopSel {
1503 if self.select(sel::ALT_POP_P) != 0 {
1504 if self.select(sel::ALT_POP + gi) != 0 {
1505 if self.par_opt_pop.cur_pop_pos >= self.pop.cur_pop_size() {
1506 return PopSel::ParOpt;
1507 }
1508 } else if self.par_opt2_pop.cur_pop_pos >= self.pop.cur_pop_size() {
1509 return PopSel::ParOpt2;
1510 }
1511 }
1512 PopSel::Main
1513 }
1514
1515 fn update_par_pop(&mut self, cost: f64, params: &[i64]) {
1516 let p = self.get_min_dist_par_pop(params);
1517 self.par_pops[p].update_pop(cost, params, true);
1518 }
1519
1520 fn get_min_dist_par_pop(&mut self, params: &[i64]) -> usize {
1521 let mut best = 0usize;
1522 let mut best_d = f64::MAX;
1523 for pi in 0..self.par_pop_count {
1524 let c = self.par_pops[pi].get_centroid();
1525 let mut s = 0.0;
1526 for i in 0..self.param_count {
1527 let d = (c[i] - params[i]) as f64;
1528 s += d * d;
1529 }
1530 if s <= best_d {
1531 best_d = s;
1532 best = pi;
1533 }
1534 }
1535 best
1536 }
1537
1538 fn generate_sol1(&mut self) {
1541 let par = self.select_par_pop(0);
1542 let par_ps = self.cur_pop_size_of(par);
1543 let si = self.get_min_sol_index(0, par_ps);
1544 let mut params = self.take_tmp();
1545 params.copy_from_slice(self.pop_ref(par).ordered(si));
1546
1547 let mut a;
1548 let mut b;
1549 let mut do_allp = false;
1550 if self.rnd.get() < 1.8 * self.param_count_i && self.select(sel::GEN1_ALLP) != 0 {
1551 do_allp = true;
1552 }
1553 if do_allp {
1554 a = 0;
1555 b = self.param_count;
1556 } else {
1557 a = self.rnd.get_int(self.param_count as i32) as usize;
1558 b = a + 1;
1559 }
1560
1561 let r1 = self.rnd.get();
1562 let r12 = r1 * r1;
1563 let ims = (r12 * r12 * 48.0) as u32;
1564 let imask = INT_MANT_MASK >> ims;
1565 let im2s = self.rnd.get_sqr_int(96);
1566 let imask2 = if im2s > 63 { 0 } else { INT_MANT_MASK >> im2s };
1567 let si1 = (r1 * r12 * par_ps as f64) as usize;
1568 {
1569 let rp1 = self.pop_ref(par).ordered(si1);
1570 for i in a..b {
1571 params[i] = ((params[i] ^ imask) + (rp1[i] ^ imask2)) >> 1;
1572 }
1573 }
1574 if self.rnd.get() < 1.0 - self.param_count_i {
1575 let ri = self.rnd.get_sqr_int(self.pop.cur_pop_size as i32) as usize;
1576 if self.rnd.get() < self.param_count_i.sqrt() && self.select(sel::GEN1_MOVE_ASYNC) != 0
1577 {
1578 a = 0;
1579 b = self.param_count;
1580 }
1581 const SPAN_MULTS: [f64; 4] = [0.5, 1.5, 2.0, 2.5];
1582 let m = SPAN_MULTS[self.select(sel::GEN1_MOVE_SPAN) as usize];
1583 let m1 = self.rnd.get_tpdf() * m;
1584 let m2 = self.rnd.get_tpdf() * m;
1585 let rp2 = self.pop.ordered(ri);
1586 for i in a..b {
1587 params[i] += ((rp2[i] - params[i]) as f64 * m1) as i64;
1588 params[i] += ((rp2[i] - params[i]) as f64 * m2) as i64;
1589 }
1590 }
1591 self.tmp = params;
1592 }
1593
1594 fn generate_sol2(&mut self) {
1595 let ps = self.pop.cur_pop_size;
1596 let ps1 = self.pop.cur_pop_size1;
1597 let si1 = self.get_min_sol_index(1, ps);
1598 let si2 = 1 + self.rnd.get_int(ps1 as i32) as usize;
1599 let si4 = self.rnd.get_sqr_int(ps as i32) as usize;
1600 let mode = self.select(sel::GEN2_MODE);
1601 let si1b = (mode != 0).then(|| self.rnd.get_sqr_int(ps as i32) as usize);
1602 let mut params = self.take_tmp();
1603 let rp1 = self.pop.ordered(si1);
1604 let rp2 = self.pop.ordered(si2);
1605 let rp3 = self.pop.ordered(ps1 - si1);
1606 let rp4 = self.pop.ordered(si4);
1607 let rp5 = self.pop.ordered(ps1 - si4);
1608 if mode == 0 {
1609 for i in 0..self.param_count {
1610 params[i] = rp1[i] + (((rp2[i] - rp3[i]) + (rp4[i] - rp5[i])) >> 1);
1611 }
1612 } else {
1613 let rp1b = self.pop.ordered(si1b.unwrap());
1614 for i in 0..self.param_count {
1615 params[i] = ((rp1[i] + rp1b[i]) + (rp2[i] - rp3[i]) + (rp4[i] - rp5[i])) >> 1;
1616 }
1617 }
1618 self.tmp = params;
1619 }
1620
1621 fn generate_sol2b(&mut self) {
1622 let ps = self.pop.cur_pop_size;
1623 let ps1 = self.pop.cur_pop_size1;
1624 let si1 = self.get_min_sol_index(2, ps);
1625 let si2 = self.rnd.get_int(ps as i32) as usize;
1626 let alt = self.select_alt_pop(0);
1627 let si4 = self.rnd.get_int(ps as i32) as usize;
1628 let mode = self.select(sel::GEN2B_MODE);
1629 let si1b = (mode != 0).then(|| self.rnd.get_sqr_int(ps as i32) as usize);
1630 let mut params = self.take_tmp();
1631 let rp1 = self.pop.ordered(si1);
1632 let rp2 = self.pop.ordered(si2);
1633 let rp3 = self.pop.ordered(ps1 - si2);
1634 let rp4 = self.pop_ref(alt).ordered(si4);
1635 let rp5 = self.pop_ref(alt).ordered(ps1 - si4);
1636 if mode == 0 {
1637 for i in 0..self.param_count {
1638 params[i] = rp1[i] + ((rp2[i] - rp3[i]) + (rp4[i] - rp5[i]));
1639 }
1640 } else {
1641 let rp1b = self.pop.ordered(si1b.unwrap());
1642 for i in 0..self.param_count {
1643 params[i] = ((rp1[i] + rp1b[i]) >> 1) + (rp2[i] - rp3[i]) + (rp4[i] - rp5[i]);
1644 }
1645 }
1646 self.tmp = params;
1647 }
1648
1649 fn generate_sol2c(&mut self) {
1650 let ps = self.pop.cur_pop_size;
1651 let mut params = self.take_tmp();
1652 let si1 = self.rnd.get_pow_int(4.0, (ps / 2) as i32) as usize;
1653 let pc = 7usize; let mut pop_idx = [0usize; 7];
1655 pop_idx[0] = si1;
1656 let mut pp = 1;
1657 if self.pop.cur_pop_size1 <= pc {
1658 while pp < pc {
1659 pop_idx[pp] = self.rnd.get_int(ps as i32) as usize;
1660 pp += 1;
1661 }
1662 } else {
1663 while pp < pc {
1664 let sii = self.rnd.get_int(ps as i32) as usize;
1665 if !pop_idx[..pp].contains(&sii) {
1666 pop_idx[pp] = sii;
1667 pp += 1;
1668 }
1669 }
1670 }
1671 for i in 0..self.param_count {
1672 params[i] = (self.pop.ordered(pop_idx[1])[i] - self.pop.ordered(pop_idx[2])[i])
1673 + (self.pop.ordered(pop_idx[3])[i] - self.pop.ordered(pop_idx[4])[i])
1674 + (self.pop.ordered(pop_idx[5])[i] - self.pop.ordered(pop_idx[6])[i]);
1675 }
1676 if self.rnd.get_bit() != 0 && self.rnd.get_bit() != 0 {
1677 let k = self.rnd.get_int(self.param_count as i32) as usize;
1678 let v1 = (self.rnd.get_raw()
1679 & self.rnd.get_raw()
1680 & self.rnd.get_raw()
1681 & self.rnd.get_raw()
1682 & self.rnd.get_raw()) as i64
1683 & INT_MANT_MASK;
1684 let v2 = (self.rnd.get_raw()
1685 & self.rnd.get_raw()
1686 & self.rnd.get_raw()
1687 & self.rnd.get_raw()
1688 & self.rnd.get_raw()) as i64
1689 & INT_MANT_MASK;
1690 params[k] += v1 - v2;
1691 }
1692 let mode = self.select(sel::GEN2C_MODE);
1693 if mode == 0 {
1694 let mut si2 = si1 as i64 + self.rnd.get_bit() as i64 * 2 - 1;
1695 if si2 < 0 {
1696 si2 = 1;
1697 }
1698 for i in 0..self.param_count {
1699 params[i] =
1700 (self.pop.ordered(si1)[i] + self.pop.ordered(si2 as usize)[i] + params[i]) >> 1;
1701 }
1702 } else {
1703 for i in 0..self.param_count {
1704 params[i] = self.pop.ordered(si1)[i] + (params[i] >> 1);
1705 }
1706 }
1707 self.tmp = params;
1708 }
1709
1710 fn generate_sol2d(&mut self) {
1711 if self.old_pop.cur_pop_pos < 3 {
1712 self.generate_sol2c();
1713 return;
1714 }
1715 let ps = self.pop.cur_pop_size;
1716 let i1 = self.rnd.get_sqr_int(ps as i32) as usize;
1717 let i2 = self.rnd.get_int(ps as i32) as usize;
1718 let old_pos = self.old_pop.cur_pop_pos;
1719 let i3 = self.rnd.get_int(old_pos as i32) as usize;
1720 let mode = self.select(sel::GEN2D_MODE);
1721 let i1b = (mode != 0).then(|| self.rnd.get_sqr_int(ps as i32) as usize);
1722 let mut params = self.take_tmp();
1723 let rp1 = self.pop.ordered(i1);
1724 let rp2 = self.pop.ordered(i2);
1725 let rp3 = self.old_pop.ordered(i3);
1726 if mode == 0 {
1727 for i in 0..self.param_count {
1728 params[i] = rp1[i] + ((rp2[i] - rp3[i]) >> 1);
1729 }
1730 } else {
1731 let rp1b = self.pop.ordered(i1b.unwrap());
1732 for i in 0..self.param_count {
1733 params[i] = ((rp1[i] + rp1b[i]) + (rp2[i] - rp3[i])) >> 1;
1734 }
1735 }
1736 self.tmp = params;
1737 }
1738
1739 fn generate_sol4(&mut self) {
1740 let alt = self.select_alt_pop(1);
1741 let par = self.select_par_pop(1);
1742 let use_size = [self.pop.cur_pop_size, self.cur_pop_size_of(par)];
1743 let km = 5 + (self.select(sel::GEN4_MIX_FAC) << 1);
1744 let mut p = self.rnd.get_bit() as usize;
1745 let idx = self.rnd.get_sqr_int(use_size[p] as i32) as usize;
1746 let mut params = self.take_tmp();
1747 params.copy_from_slice(self.pop_ref(if p == 0 { alt } else { par }).ordered(idx));
1748 for _ in 1..km {
1749 p = self.rnd.get_bit() as usize;
1750 let idx = self.rnd.get_sqr_int(use_size[p] as i32) as usize;
1751 let rp = self.pop_ref(if p == 0 { alt } else { par }).ordered(idx);
1752 for i in 0..self.param_count {
1753 params[i] ^= rp[i];
1754 }
1755 }
1756 self.tmp = params;
1757 }
1758
1759 fn generate_sol5(&mut self) {
1760 let par = self.select_par_pop(2);
1761 let par_ps = self.cur_pop_size_of(par);
1762 let si1 = self.rnd.get_sqr_int(par_ps as i32) as usize;
1763 let cp1 = self.ordered_of(par, si1);
1764 let alt = self.select_alt_pop(2);
1765 let si2 = self.rnd.get_sqr_int(self.pop.cur_pop_size as i32) as usize;
1766 let cp2 = self.ordered_of(alt, si2);
1767 let mut params = self.take_tmp();
1768 for i in 0..self.param_count {
1769 let crpl = (self.rnd.get_raw() as i64) & INT_MANT_MASK;
1770 params[i] = (cp1[i] & crpl) | (cp2[i] & !crpl);
1771 let bshift = self.rnd.get_int(INT_MANT_BITS as i32);
1772 params[i] +=
1773 ((self.rnd.get_bit() as i64) << bshift) - ((self.rnd.get_bit() as i64) << bshift);
1774 }
1775 self.tmp = params;
1776 }
1777
1778 fn generate_sol5b(&mut self) {
1779 let par = self.select_par_pop(3);
1780 let par_ps = self.cur_pop_size_of(par);
1781 let i0 = self.rnd.get_sqr_int(par_ps as i32) as usize;
1782 let cp0 = self.ordered_of(par, i0);
1783 let alt = self.select_alt_pop(3);
1784 let ps = self.pop.cur_pop_size;
1785 let ps1 = self.pop.cur_pop_size1;
1786 let cp1 = if self.rnd.get_bit() != 0 {
1787 let i1 = ps1 - self.rnd.get_sqr_int(ps as i32) as usize;
1788 self.ordered_of(alt, i1)
1789 } else {
1790 let i1 = self.rnd.get_sqr_int(ps as i32) as usize;
1791 self.ordered_of(alt, i1)
1792 };
1793 let mode = self.select(sel::GEN5B_MODE);
1794 let mut params = self.take_tmp();
1795 if mode == 0 {
1796 for i in 0..self.param_count {
1797 params[i] = if self.rnd.get_bit() != 0 {
1798 cp1[i]
1799 } else {
1800 cp0[i]
1801 };
1802 }
1803 } else {
1804 let i2 = self.rnd.get_sqr_int(par_ps as i32) as usize;
1805 let cp2 = self.ordered_of(par, i2);
1806 let i3 = self.rnd.get_sqr_int(ps as i32) as usize;
1807 let cp3 = self.ordered_of(alt, i3);
1808 let cps = [cp0, cp1, cp2, cp3];
1809 for i in 0..self.param_count {
1810 let sel = ((self.rnd.get_bit() << 1) | self.rnd.get_bit()) as usize;
1811 params[i] = cps[sel][i];
1812 }
1813 }
1814 self.tmp = params;
1815 }
1816
1817 fn generate_sol6(&mut self) {
1818 let ps = self.pop.cur_pop_size;
1819 let r = self.rnd.get_pow(4.0);
1820 let si = (r * ps as f64) as usize;
1821 let mut v = [0.0f64; 2];
1822 let k0 = self.rnd.get_int(self.param_count as i32) as usize;
1823 let use_second = self.rnd.get_bit() != 0;
1824 let k1 = use_second.then(|| self.rnd.get_int(self.param_count as i32) as usize);
1825 let row = self.pop.ordered(si);
1826 v[0] = self.real_value(row, k0);
1827 if let Some(k1) = k1 {
1828 v[1] = self.real_value(row, k1);
1829 } else {
1830 v[1] = v[0];
1831 }
1832 let m = 1.0 - r * r;
1833 v[0] *= m;
1834 v[1] *= m;
1835 let mut params = self.take_tmp();
1836 for i in 0..self.param_count {
1837 let pick = v[self.rnd.get_bit() as usize];
1838 params[i] = ((pick - self.min_values[i]) * self.diff_values_i[i]) as i64;
1839 }
1840 self.tmp = params;
1841 }
1842
1843 fn generate_sol7(&mut self) {
1844 let ps = self.pop.cur_pop_size;
1845 let use_old = self.old_pop.cur_pop_pos > 2;
1846 const P: [f64; 4] = [1.5, 1.75, 2.0, 2.25];
1847 let pwr = P[self.select(sel::GEN7_POW_FAC) as usize];
1848 let mut params = self.take_tmp();
1849 for i in 0..self.param_count {
1850 let rv = self.rnd.get_pow(pwr);
1851 if use_old && self.rnd.get_bit() != 0 && self.rnd.get_bit() != 0 {
1852 let idx = (rv * self.old_pop.cur_pop_pos as f64) as usize;
1853 params[i] = self.old_pop.ordered(idx)[i];
1854 } else {
1855 let idx = (rv * ps as f64) as usize;
1856 params[i] = self.pop.ordered(idx)[i];
1857 }
1858 }
1859 self.tmp = params;
1860 }
1861
1862 fn generate_sol8(&mut self) {
1863 let ps = self.pop.cur_pop_size;
1864 let mode = self.select(sel::GEN8_MODE);
1865 let num_sols = 5 + self.select(sel::GEN8_NUM) as usize;
1866 let mut rp = [0usize; 8];
1867 let first = self.rnd.get_sqr_int(ps as i32) as usize;
1868 rp[0] = first;
1869 let mut params = self.take_tmp();
1870 params.copy_from_slice(self.pop.ordered(first));
1871 for slot in rp.iter_mut().take(num_sols).skip(1) {
1872 let idx = self.rnd.get_sqr_int(ps as i32) as usize;
1873 *slot = idx;
1874 let r0 = self.pop.ordered(idx);
1875 for i in 0..self.param_count {
1876 params[i] = params[i].wrapping_add(r0[i]);
1877 }
1878 }
1879 let m = 1.0 / num_sols as f64;
1880 let mut cent = std::mem::take(&mut self.real_tmp);
1881 cent.resize(self.param_count, 0.0);
1882 for i in 0..self.param_count {
1883 cent[i] = params[i] as f64 * m;
1884 params[i] = cent[i] as i64;
1885 }
1886 if mode == 0 {
1887 const SPANS: [f64; 4] = [1.5, 2.5, 3.5, 4.5];
1888 let gm = SPANS[self.select(sel::GEN8_SPAN) as usize] * m.sqrt();
1889 for &index in &rp[..num_sols] {
1890 let r = self.rnd.get_gaussian() * gm;
1891 let rj = self.pop.ordered(index);
1892 for i in 0..self.param_count {
1893 params[i] = params[i].wrapping_add(((cent[i] - rj[i] as f64) * r) as i64);
1894 }
1895 }
1896 } else {
1897 const SPANS: [f64; 4] = [0.5, 1.5, 2.5, 3.5];
1898 let gm = SPANS[self.select(sel::GEN8_SPAN + 1) as usize];
1899 for &index in &rp[..num_sols] {
1900 let r = self.rnd.get_gaussian() * gm;
1901 let rj = self.pop.ordered(index);
1902 for i in 0..self.param_count {
1903 let delta = (params[i].wrapping_sub(rj[i]) as f64 * r) as i64;
1904 params[i] = params[i].wrapping_add(delta);
1905 }
1906 }
1907 }
1908 self.real_tmp = cent;
1909 self.tmp = params;
1910 }
1911
1912 fn generate_sol9(&mut self) {
1913 let ps = self.pop.cur_pop_size;
1914 let ps1 = self.pop.cur_pop_size1;
1915 let si1 = self.rnd.get_int(ps as i32) as usize;
1916 let si2 = self.rnd.get_sqr_int(ps as i32) as usize;
1917 let subtract = self.rnd.get_bit() != 0;
1918 let mut params = self.take_tmp();
1919 let rp1 = self.pop.ordered(si1);
1920 let rp2 = self.pop.ordered(ps1 - si2);
1921 if subtract {
1922 for i in 0..self.param_count {
1923 params[i] = rp1[i] - ((rp2[i] - rp1[i]) >> 1) * (1 - 2 * self.rnd.get_bit() as i64);
1924 }
1925 } else {
1926 for i in 0..self.param_count {
1927 params[i] = rp1[i] + ((rp2[i] - rp1[i]) >> 1) * (1 - 2 * self.rnd.get_bit() as i64);
1928 }
1929 }
1930 self.tmp = params;
1931 }
1932
1933 fn generate_sol10(&mut self) {
1934 let ps = self.pop.cur_pop_size;
1935 let ps1 = self.pop.cur_pop_size1;
1936 let si1 = self.rnd.get_sqr_int(ps as i32) as usize;
1937 let si2 = self.rnd.get_sqr_int(ps as i32) as usize;
1938 let mut params = self.take_tmp();
1939 {
1940 let rp1 = self.pop.ordered(si1);
1941 let rp2 = self.pop.ordered(ps1 - si2);
1942 for i in 0..self.param_count {
1943 params[i] = (rp1[i] + rp2[i]) >> 1;
1944 }
1945 }
1946 let mut radius = 0.0;
1947 {
1948 let rp1 = self.pop.ordered(si1);
1949 let rp2 = self.pop.ordered(ps1 - si2);
1950 for i in 0..self.param_count {
1951 let v1 = (rp1[i] - params[i]) as f64;
1952 let v2 = (rp2[i] - params[i]) as f64;
1953 radius += v1 * v1 + 0.45 * v2 * v2;
1954 }
1955 }
1956 let mut s2 = 1e-300;
1957 let mut nv = std::mem::take(&mut self.real_tmp);
1958 nv.resize(self.param_count, 0.0);
1959 for n in nv.iter_mut() {
1960 *n = self.rnd.get() - 0.5;
1961 s2 += *n * *n;
1962 }
1963 let d = (radius / s2).sqrt();
1964 for i in 0..self.param_count {
1965 params[i] += (nv[i] * d) as i64;
1966 }
1967 self.real_tmp = nv;
1968 self.tmp = params;
1969 }
1970
1971 fn generate_sol3(&mut self) {
1972 let ps = self.pop.cur_pop_size;
1973 let si1 = self.get_min_sol_index(3, ps);
1974 let si2 = self.rnd.get_sqr_int(ps as i32) as usize;
1975 let mode = self.select(sel::GEN3_MODE);
1976 if mode != 0 && self.pop.need_cent {
1977 self.pop.update_centroid();
1978 }
1979 let mut params = self.take_tmp();
1980 let rp1 = self.pop.ordered(si1);
1981 let rp2 = self.pop.ordered(si2);
1982 if mode == 0 {
1983 for i in 0..self.param_count {
1984 params[i] = rp1[i] + (rp1[i] - rp2[i]);
1985 }
1986 } else {
1987 const CENT_PROB: [f64; 4] = [0.0, 0.25, 0.5, 0.75];
1988 let prob = CENT_PROB[mode as usize];
1989 for i in 0..self.param_count {
1990 params[i] = if self.rnd.get() < prob {
1991 self.pop.cent[i]
1992 } else {
1993 rp1[i] + (rp1[i] - rp2[i])
1994 };
1995 }
1996 }
1997 self.tmp = params;
1998 }
1999
2000 fn generate_sol_par(&mut self, obj: &dyn Objective) -> (f64, Vec<f64>) {
2003 if self.use_par_opt == 1 {
2004 self.use_par_opt = self.select(sel::PAR_OPT2);
2005 }
2006 let step;
2007 let which_pop;
2008 if self.use_par_opt == 0 {
2009 step = self.spher.optimize(&mut self.rnd, obj);
2010 if step.stall > 0 {
2011 self.use_par_opt = 1;
2012 }
2013 if step.stall > self.param_count as i64 * 64 {
2014 let best = self.best_values.clone();
2015 self.spher.init(&mut self.rnd, Some(&best), 0.5);
2016 self.par_opt_pop.reset_cur_pop_pos();
2017 }
2018 which_pop = 0;
2019 } else {
2020 step = self.nmseq.optimize(&mut self.rnd, obj);
2021 if step.stall > 0 {
2022 self.use_par_opt = 0;
2023 }
2024 if step.stall > self.param_count as i64 * 16 {
2025 let best = self.best_values.clone();
2026 self.nmseq.init(&mut self.rnd, Some(&best), 1.0);
2027 self.par_opt2_pop.reset_cur_pop_pos();
2028 }
2029 which_pop = 1;
2030 }
2031 let mut tmp = self.take_tmp();
2032 for i in 0..self.param_count {
2033 tmp[i] = ((step.values[i] - self.min_values[i]) * self.diff_values_i[i]) as i64;
2034 }
2035 if which_pop == 0 {
2036 self.par_opt_pop.update_pop(step.cost, &tmp, false);
2037 } else {
2038 self.par_opt2_pop.update_pop(step.cost, &tmp, false);
2039 }
2040 self.tmp = tmp;
2041 (step.cost, step.values)
2042 }
2043
2044 fn generate(&mut self, obj: Option<&dyn Objective>) -> Option<(f64, Vec<f64>)> {
2051 let mut method = self.select(sel::METHOD);
2052 while method == 3 && obj.is_none() {
2053 method = self.sels[sel::METHOD].select(&mut self.rnd);
2054 }
2055 if obj.is_none() {
2056 let final_method = self.sels[sel::METHOD].captured(sel::METHOD);
2057 if let Some(selection) = self
2058 .apply_sels
2059 .iter_mut()
2060 .find(|selection| selection.index == sel::METHOD)
2061 {
2062 *selection = final_method;
2063 }
2064 }
2065 match method {
2066 0 => self.generate_sol2(),
2067 1 => {
2068 let m1 = self.select(sel::M1);
2069 match m1 {
2070 0 => {
2071 let m1a = self.select(sel::M1A);
2072 match m1a {
2073 0 => self.generate_sol2b(),
2074 1 => self.generate_sol2c(),
2075 _ => self.generate_sol2d(),
2076 }
2077 }
2078 1 => {
2079 if self.select(sel::M1B) != 0 {
2080 self.generate_sol4();
2081 } else {
2082 self.generate_sol5b();
2083 }
2084 }
2085 2 => {
2086 if self.select(sel::M1C) != 0 {
2087 self.generate_sol5();
2088 } else {
2089 self.generate_sol10();
2090 }
2091 }
2092 _ => self.generate_sol6(),
2093 }
2094 }
2095 2 => {
2096 if self.select(sel::M2) != 0 {
2097 self.generate_sol1();
2098 } else {
2099 let m2b = self.select(sel::M2B);
2100 match m2b {
2101 0 => self.generate_sol3(),
2102 1 => self.generate_sol7(),
2103 2 => self.generate_sol8(),
2104 _ => self.generate_sol9(),
2105 }
2106 }
2107 }
2108 _ => {
2109 let (cost, real) =
2110 self.generate_sol_par(obj.expect("parallel generator requires an objective"));
2111 return Some((cost, real));
2112 }
2113 }
2114 None
2115 }
2116
2117 fn in_init(&self) -> bool {
2118 !self.init_queue.is_empty()
2119 }
2120
2121 fn gen_one(&mut self, obj: Option<&dyn Objective>) -> Candidate {
2125 if let Some(enc) = self.init_queue.pop_front() {
2126 let mut real = std::mem::take(&mut self.real_tmp);
2127 real.resize(self.param_count, 0.0);
2128 for i in 0..self.param_count {
2129 real[i] = self.real_value(&enc, i);
2130 }
2131 return Candidate {
2132 enc,
2133 real,
2134 sels: vec![],
2135 is_init: true,
2136 precomputed_cost: None,
2137 };
2138 }
2139 self.apply_sels.clear();
2140 if let Some(selection) = self.deferred_sels.pop_front() {
2141 self.apply_sels.push(selection);
2142 }
2143 let precomputed = self.generate(obj);
2144 let mut enc = std::mem::take(&mut self.tmp);
2145 for e in enc.iter_mut() {
2146 *e = wrap_param(&mut self.rnd, *e);
2147 }
2148 let (precomputed_cost, real) = match precomputed {
2149 Some((cost, values)) => (Some(cost), values),
2150 None => {
2151 let mut values = std::mem::take(&mut self.real_tmp);
2152 values.resize(self.param_count, 0.0);
2153 for i in 0..self.param_count {
2154 values[i] = self.real_value(&enc, i);
2155 }
2156 (None, values)
2157 }
2158 };
2159 let sels = std::mem::take(&mut self.apply_sels);
2160 Candidate {
2161 enc,
2162 real,
2163 sels,
2164 is_init: false,
2165 precomputed_cost,
2166 }
2167 }
2168
2169 fn select_deferred(&mut self, index: usize) -> i32 {
2170 let value = self.sels[index].select(&mut self.rnd);
2171 self.deferred_sels
2172 .push_back(self.sels[index].captured(index));
2173 value
2174 }
2175
2176 fn apply_one(&mut self, cand: &Candidate, cost: f64, collect_push: bool) -> bool {
2180 let cost = sanitize_cost(cost);
2181 if cand.is_init {
2182 let p = self.pop.update_pop(cost, &cand.enc, false) as i64;
2183 self.update_best_cost(cost, &cand.real, p);
2184 if self.init_queue.is_empty() && self.pop.cur_pop_pos == self.pop_size {
2185 self.pop.update_centroid();
2186 for parallel in &mut self.par_pops {
2188 parallel.copy_from(&self.pop);
2189 }
2190 }
2191 return false;
2192 }
2193 let do_eval = cand.precomputed_cost.is_none();
2194 let p = self.pop.update_pop(cost, &cand.enc, true);
2195 let mut push = false;
2196 if p > self.pop.cur_pop_size1 {
2197 for &selection in &cand.sels {
2198 self.sels[selection.index].decr_captured(selection);
2199 }
2200 self.stall_count += 1;
2201 if do_eval
2203 && self.pop.cur_pop_size < self.pop_size
2204 && self.select_deferred(sel::POP_CHANGE_INCR) != 0
2205 {
2206 self.pop.incr_cur_pop_size();
2207 }
2208 } else {
2209 self.update_best_cost(cost, &cand.real, p as i64);
2210 let v = 1.0 - p as f64 * self.pop.cur_pop_size_i;
2211 for &selection in &cand.sels {
2212 self.sels[selection.index].incr_captured(selection, v);
2213 }
2214 self.stall_count = 0;
2215 if collect_push && p > 0 {
2216 push = true;
2217 }
2218 if self.rnd.get() < self.param_count_i {
2220 let w = self.pop.cur_pop_size1;
2221 let worst_cost = self.pop.costs[w];
2222 self.old_pop
2223 .update_pop(worst_cost, self.pop.ordered(w), false);
2224 }
2225 if do_eval
2227 && self.pop.cur_pop_size > self.pop_size / 2
2228 && self.select_deferred(sel::POP_CHANGE_DECR) != 0
2229 {
2230 self.pop.decr_cur_pop_size();
2231 }
2232 }
2233 self.update_par_pop(cost, &cand.enc);
2235 push
2236 }
2237
2238 fn push_solution(&mut self, cost: f64, enc: &[i64]) {
2240 if self.in_init() {
2241 return;
2242 }
2243 self.pop.update_pop(cost, enc, true);
2244 self.update_par_pop(cost, enc);
2245 }
2246
2247 fn optimize_step(&mut self, obj: &impl Objective) {
2249 let cand = self.gen_one(Some(obj));
2250 let cost = cand
2251 .precomputed_cost
2252 .unwrap_or_else(|| objective_cost(obj, &cand.real));
2253 self.apply_one(&cand, cost, false);
2254 self.recycle_candidate(cand);
2255 }
2256
2257 fn step_collect(
2260 &mut self,
2261 obj: &impl Objective,
2262 collect_push: bool,
2263 ) -> (i64, Option<(f64, Vec<i64>)>) {
2264 let mut cand = self.gen_one(Some(obj));
2265 let cost = cand
2266 .precomputed_cost
2267 .unwrap_or_else(|| objective_cost(obj, &cand.real));
2268 let should_push = self.apply_one(&cand, cost, collect_push);
2269 let push = should_push.then(|| (cost, std::mem::take(&mut cand.enc)));
2270 self.iterations += 1;
2271 self.evaluations += 1;
2272 self.recycle_candidate(cand);
2273 (self.stall_count, push)
2274 }
2275
2276 fn best_cost(&self) -> f64 {
2277 self.best_cost
2278 }
2279
2280 fn in_init_phase(&self) -> bool {
2281 self.in_init()
2282 }
2283
2284 fn init_remaining(&self) -> usize {
2285 self.init_queue.len()
2286 }
2287
2288 pub fn ask(&mut self, batch: usize) -> Vec<Vec<f64>> {
2293 if !self.asked.is_empty() {
2294 return self
2295 .asked
2296 .iter()
2297 .map(|candidate| candidate.real.clone())
2298 .collect();
2299 }
2300 if batch == 0 || self.stop != 0 || self.evaluations >= self.max_evaluations {
2301 return Vec::new();
2302 }
2303 let remaining_budget = (self.max_evaluations - self.evaluations) as usize;
2304 let requested = batch.min(remaining_budget);
2305 let n = if self.in_init() {
2306 requested.min(self.init_queue.len())
2307 } else {
2308 requested
2309 };
2310 for _ in 0..n {
2311 let cand = self.gen_one(None);
2312 self.asked.push(cand);
2313 }
2314 self.asked.iter().map(|c| c.real.clone()).collect()
2315 }
2316
2317 pub fn tell(&mut self, costs: &[f64]) -> i32 {
2319 if self.asked.is_empty() || costs.len() != self.asked.len() {
2320 return -1;
2321 }
2322 let asked = std::mem::take(&mut self.asked);
2323 for (cand, &cost) in asked.into_iter().zip(costs) {
2324 self.apply_one(&cand, cost, false);
2325 self.iterations += 1;
2326 self.evaluations += 1;
2327 self.recycle_candidate(cand);
2328 }
2329 self.update_stop();
2330 self.stop
2331 }
2332
2333 pub fn current_batch_size(&self) -> usize {
2334 self.asked.len()
2335 }
2336 pub fn dim(&self) -> usize {
2337 self.param_count
2338 }
2339 pub fn population_size(&self) -> usize {
2340 self.pop_size
2341 }
2342 pub fn stop_code(&self) -> i32 {
2343 self.stop
2344 }
2345 pub fn result_public(&self) -> BiteResult {
2346 self.result()
2347 }
2348
2349 fn update_stop(&mut self) {
2350 if self.best_cost < self.stopfitness {
2351 self.stop = 1;
2352 } else if self.stall_criterion > 0
2353 && self.stall_count > self.stall_criterion as i64 * 128 * self.param_count as i64
2354 {
2355 self.stop = 2;
2356 }
2357 }
2358
2359 pub fn optimize(&mut self, obj: &impl Objective) -> BiteResult {
2361 while self.evaluations < self.max_evaluations && self.stop == 0 {
2362 self.optimize_step(obj);
2363 self.iterations += 1;
2364 self.evaluations += 1;
2365 self.update_stop();
2366 }
2367 self.result()
2368 }
2369
2370 fn result(&self) -> BiteResult {
2371 BiteResult {
2372 x: self.best_values.clone(),
2373 y: self.best_cost,
2374 evaluations: self.evaluations,
2375 iterations: self.iterations,
2376 stop: self.stop,
2377 }
2378 }
2379}
2380
2381pub struct DeepBiteOpt {
2388 opts: Vec<BiteOpt>,
2389 m: usize,
2390 cur_opt: usize,
2391 push_opt: usize,
2392 best_opt: usize,
2393 at_stall_count: i64,
2394 batch_cur_opt: usize,
2395 rnd: BiteRnd,
2396 max_evaluations: u64,
2397 stopfitness: f64,
2398 stall_criterion: i32,
2399 param_count: usize,
2400 evaluations: u64,
2401 iterations: i32,
2402 stop: i32,
2403}
2404
2405impl DeepBiteOpt {
2406 pub fn new(lower: &[f64], upper: &[f64], init: Option<&[f64]>, p: &BiteParams, m: i32) -> Self {
2407 validate_bite_inputs(lower, upper, init, p, m).expect("invalid deep BiteOpt configuration");
2408 let m = m.max(1) as usize;
2409 let opts: Vec<BiteOpt> = (0..m)
2410 .map(|i| {
2411 let mut pi = p.clone();
2412 pi.runid = p.runid.wrapping_add((i as i64).wrapping_mul(0x9E3779B1));
2414 BiteOpt::new(lower, upper, init, &pi)
2415 })
2416 .collect();
2417 let mut d = DeepBiteOpt {
2418 opts,
2419 m,
2420 cur_opt: 0,
2421 push_opt: 0,
2422 best_opt: 0,
2423 at_stall_count: 0,
2424 batch_cur_opt: 0,
2425 rnd: BiteRnd::new(p.seed.wrapping_add(p.runid as u64).wrapping_add(0xB17E)),
2426 max_evaluations: if p.max_evaluations > 0 {
2427 p.max_evaluations
2428 } else {
2429 50_000
2430 },
2431 stopfitness: p.stop_fitness,
2432 stall_criterion: p.stall_criterion.max(0),
2433 param_count: lower.len(),
2434 evaluations: 0,
2435 iterations: 0,
2436 stop: 0,
2437 };
2438 d.pick_push();
2439 d
2440 }
2441
2442 fn pick_push(&mut self) {
2443 if self.m == 1 {
2444 self.push_opt = self.cur_opt;
2445 } else if self.m == 2 {
2446 self.push_opt = 1 - self.cur_opt;
2447 } else {
2448 loop {
2449 let p = self.rnd.get_int(self.m as i32) as usize;
2450 if p != self.cur_opt {
2451 self.push_opt = p;
2452 break;
2453 }
2454 }
2455 }
2456 }
2457
2458 fn total_evaluations(&self) -> u64 {
2459 self.evaluations
2460 }
2461
2462 fn best(&self) -> &BiteOpt {
2463 &self.opts[self.best_opt]
2464 }
2465
2466 fn update_stop(&mut self) {
2467 if self.best().best_cost < self.stopfitness {
2468 self.stop = 1;
2469 } else if self.stall_criterion > 0
2470 && self.at_stall_count > self.stall_criterion as i64 * 128 * self.param_count as i64
2471 {
2472 self.stop = 2;
2473 }
2474 }
2475
2476 fn track_best(&mut self, idx: usize) {
2477 if self.opts[idx].best_cost() <= self.opts[self.best_opt].best_cost() {
2478 self.best_opt = idx;
2479 }
2480 }
2481
2482 pub fn optimize(&mut self, obj: &impl Objective) -> BiteResult {
2484 while self.total_evaluations() < self.max_evaluations && self.stop == 0 {
2485 self.pick_push();
2486 let opt_idx = self.cur_opt;
2487 let (sc, push) = self.opts[opt_idx].step_collect(obj, self.m > 1);
2488 self.evaluations += 1;
2489 self.iterations += 1;
2490 if let Some((cost, enc)) = push {
2491 self.opts[self.push_opt].push_solution(cost, &enc);
2492 self.opts[opt_idx].tmp = enc;
2493 }
2494 self.track_best(opt_idx);
2495 if sc == 0 {
2496 self.at_stall_count = 0;
2497 } else {
2498 self.cur_opt = self.push_opt;
2499 self.at_stall_count += 1;
2500 }
2501 self.update_stop();
2502 }
2503 self.result()
2504 }
2505
2506 pub fn ask(&mut self, batch: usize) -> Vec<Vec<f64>> {
2508 if self.current_batch_size() != 0 {
2509 return self.opts[self.batch_cur_opt]
2510 .asked
2511 .iter()
2512 .map(|candidate| candidate.real.clone())
2513 .collect();
2514 }
2515 let evaluations = self.total_evaluations();
2516 if batch == 0 || self.stop != 0 || evaluations >= self.max_evaluations {
2517 return Vec::new();
2518 }
2519 let mut b = batch.min((self.max_evaluations - evaluations) as usize);
2520 if self.opts[self.cur_opt].in_init_phase() {
2521 let rem = self.opts[self.cur_opt].init_remaining();
2522 if b > rem {
2523 b = rem.max(1);
2524 }
2525 }
2526 self.batch_cur_opt = self.cur_opt;
2527 self.opts[self.cur_opt].ask(b)
2528 }
2529
2530 pub fn tell(&mut self, costs: &[f64]) -> i32 {
2533 let opt_idx = self.batch_cur_opt;
2534 if costs.len() != self.opts[opt_idx].asked.len() || costs.is_empty() {
2535 return -1;
2536 }
2537 let mut asked = std::mem::take(&mut self.opts[opt_idx].asked);
2538 let mut order: Vec<usize> = (0..asked.len()).collect();
2539 order.sort_by(|&a, &b| {
2540 let ca = sanitize_cost(costs[a]);
2541 let cb = sanitize_cost(costs[b]);
2542 ca.partial_cmp(&cb)
2543 .unwrap_or(std::cmp::Ordering::Equal)
2544 .then(a.cmp(&b))
2545 });
2546 for &i in &order {
2547 let push = self.opts[opt_idx].apply_one(&asked[i], costs[i], self.m > 1);
2548 self.opts[opt_idx].iterations += 1;
2549 self.opts[opt_idx].evaluations += 1;
2550 self.iterations += 1;
2551 self.evaluations += 1;
2552 let sc = self.opts[opt_idx].stall_count;
2553 if push {
2554 self.opts[self.push_opt].push_solution(sanitize_cost(costs[i]), &asked[i].enc);
2555 }
2556 self.track_best(opt_idx);
2557 if self.m > 1 {
2558 if sc == 0 {
2559 self.at_stall_count = 0;
2560 } else {
2561 self.at_stall_count += 1;
2562 self.cur_opt = self.push_opt;
2563 self.pick_push();
2564 }
2565 } else {
2566 self.at_stall_count = sc;
2567 }
2568 }
2569 if let Some(candidate) = asked.pop() {
2570 self.opts[opt_idx].recycle_candidate(candidate);
2571 }
2572 self.update_stop();
2573 self.stop
2574 }
2575
2576 pub fn dim(&self) -> usize {
2577 self.param_count
2578 }
2579 pub fn population_size(&self) -> usize {
2580 self.opts[0].population_size()
2581 }
2582 pub fn current_batch_size(&self) -> usize {
2583 self.opts[self.batch_cur_opt].current_batch_size()
2584 }
2585 pub fn stop_code(&self) -> i32 {
2586 self.stop
2587 }
2588
2589 pub fn result(&self) -> BiteResult {
2590 let b = self.best();
2591 BiteResult {
2592 x: b.best_values.clone(),
2593 y: b.best_cost,
2594 evaluations: self.evaluations,
2595 iterations: self.iterations,
2596 stop: self.stop,
2597 }
2598 }
2599
2600 pub fn result_public(&self) -> BiteResult {
2601 self.result()
2602 }
2603}
2604
2605pub fn optimize_bite(
2608 obj: &impl Objective,
2609 lower: &[f64],
2610 upper: &[f64],
2611 init: Option<&[f64]>,
2612 p: &BiteParams,
2613 m: i32,
2614) -> BiteResult {
2615 let mut opt = DeepBiteOpt::new(lower, upper, init, p, m);
2616 opt.optimize(obj)
2617}
2618
2619#[cfg(test)]
2620mod tests {
2621 use super::*;
2622
2623 fn sphere(x: &[f64]) -> f64 {
2624 x.iter().map(|v| v * v).sum()
2625 }
2626 fn rosen(x: &[f64]) -> f64 {
2627 (0..x.len() - 1)
2628 .map(|i| 100.0 * (x[i + 1] - x[i] * x[i]).powi(2) + (1.0 - x[i]).powi(2))
2629 .sum()
2630 }
2631 fn rastrigin(x: &[f64]) -> f64 {
2632 let n = x.len() as f64;
2633 10.0 * n
2634 + x.iter()
2635 .map(|v| v * v - 10.0 * (2.0 * std::f64::consts::PI * v).cos())
2636 .sum::<f64>()
2637 }
2638
2639 fn run(obj: impl Objective, dim: usize, seed: u64, evals: u64) -> f64 {
2640 let params = BiteParams {
2641 max_evaluations: evals,
2642 seed,
2643 ..Default::default()
2644 };
2645 optimize_bite(&obj, &vec![-5.0; dim], &vec![5.0; dim], None, ¶ms, 1).y
2646 }
2647
2648 #[test]
2649 fn deep_minimizes_rosenbrock() {
2650 let params = BiteParams {
2651 max_evaluations: 30000,
2652 seed: 4,
2653 ..Default::default()
2654 };
2655 let r = optimize_bite(
2657 &(rosen as fn(&[f64]) -> f64),
2658 &[-5.0; 6],
2659 &[5.0; 6],
2660 None,
2661 ¶ms,
2662 3,
2663 );
2664 assert!(r.y < 1e-2, "deep rosen: {}", r.y);
2665 }
2666
2667 #[test]
2668 fn minimizes_sphere() {
2669 assert!(run(sphere as fn(&[f64]) -> f64, 5, 1, 15000) < 1e-6);
2670 }
2671
2672 #[test]
2673 fn minimizes_rosenbrock() {
2674 let mut v: Vec<f64> = (0..5)
2675 .map(|s| run(rosen as fn(&[f64]) -> f64, 5, s, 30000))
2676 .collect();
2677 v.sort_by(|a, b| a.partial_cmp(b).unwrap());
2678 assert!(v[2] < 1e-2, "rosen median too large: {v:?}");
2679 }
2680
2681 #[test]
2682 fn minimizes_rastrigin() {
2683 let mut v: Vec<f64> = (0..5)
2684 .map(|s| run(rastrigin as fn(&[f64]) -> f64, 5, s, 30000))
2685 .collect();
2686 v.sort_by(|a, b| a.partial_cmp(b).unwrap());
2687 assert!(v[2] < 5.0, "rastrigin median too large: {v:?}");
2688 }
2689
2690 #[test]
2691 fn ask_tell_converges() {
2692 let params = BiteParams {
2693 max_evaluations: 15000,
2694 seed: 7,
2695 ..Default::default()
2696 };
2697 let mut opt = BiteOpt::new(&[-5.0; 5], &[5.0; 5], None, ¶ms);
2698 while opt.evaluations < 15000 && opt.stop == 0 {
2699 let xs = opt.ask(8);
2700 let ys: Vec<f64> = xs.iter().map(|x| sphere(x)).collect();
2701 opt.tell(&ys);
2702 }
2703 assert!(
2704 opt.result_public().y < 1e-4,
2705 "ask/tell: {}",
2706 opt.result_public().y
2707 );
2708 }
2709
2710 #[test]
2711 fn validates_configuration() {
2712 let params = BiteParams::default();
2713 assert!(validate_bite_inputs(&[], &[], None, ¶ms, 1).is_err());
2714 assert!(validate_bite_inputs(&[0.0], &[1.0, 2.0], None, ¶ms, 1).is_err());
2715 assert!(validate_bite_inputs(&[1.0], &[1.0], None, ¶ms, 1).is_err());
2716 assert!(validate_bite_inputs(&[2.0], &[1.0], None, ¶ms, 1).is_err());
2717 assert!(validate_bite_inputs(&[f64::NAN], &[1.0], None, ¶ms, 1).is_err());
2718 assert!(validate_bite_inputs(&[0.0], &[f64::INFINITY], None, ¶ms, 1).is_err());
2719 assert!(validate_bite_inputs(&[-f64::MAX], &[f64::MAX], None, ¶ms, 1).is_err());
2720 assert!(validate_bite_inputs(&[0.0], &[1.0], Some(&[0.5, 0.5]), ¶ms, 1).is_err());
2721 assert!(validate_bite_inputs(&[0.0], &[1.0], Some(&[f64::NAN]), ¶ms, 1).is_err());
2722 assert!(validate_bite_inputs(&[0.0], &[1.0], None, ¶ms, 37).is_err());
2723
2724 let invalid_pop = BiteParams {
2725 popsize: 3,
2726 ..Default::default()
2727 };
2728 assert!(validate_bite_inputs(&[0.0], &[1.0], None, &invalid_pop, 1).is_err());
2729 let invalid_stop = BiteParams {
2730 stop_fitness: f64::NAN,
2731 ..Default::default()
2732 };
2733 assert!(validate_bite_inputs(&[0.0], &[1.0], None, &invalid_stop, 1).is_err());
2734
2735 let defaults = BiteParams {
2738 popsize: -1,
2739 ..Default::default()
2740 };
2741 assert!(validate_bite_inputs(&[0.0], &[1.0], Some(&[0.5]), &defaults, -1).is_ok());
2742 }
2743
2744 #[test]
2745 fn helper_edge_paths_are_bounded_and_stable() {
2746 let mut rnd = BiteRnd::new(101);
2747 for value in [
2748 wrap_param(&mut rnd, -INT_MANT_MULT * 2),
2749 wrap_param(&mut rnd, INT_MANT_MULT * 3),
2750 ] {
2751 assert!((0..=INT_MANT_MULT).contains(&value));
2752 }
2753 for value in [wrap01(&mut rnd, -2.0), wrap01(&mut rnd, 3.0)] {
2754 assert!((0.0..=1.0).contains(&value));
2755 }
2756 for value in [
2757 wrap_param_real(&mut rnd, -30.0, -5.0, 10.0),
2758 wrap_param_real(&mut rnd, 30.0, -5.0, 10.0),
2759 ] {
2760 assert!((-5.0..=5.0).contains(&value));
2761 }
2762
2763 let mut population = BitePop::new(2, 4);
2764 population.update_pop(f64::NAN, &[1, 2], false);
2765 assert_eq!(population.costs[0], BAD_COST);
2766 population.need_cent = true;
2767 assert_eq!(population.get_centroid().len(), 2);
2768
2769 let mut selector = BiteSel::new(2);
2770 selector.reset(&mut rnd, 2);
2771 let fallback = SelUse {
2772 index: 0,
2773 value: selector.sels[0][0],
2774 position: 0,
2775 slot_id: selector.slot_ids[0],
2776 entry_id: u8::MAX,
2777 };
2778 selector.restore(fallback);
2779 assert_eq!(selector.sel, fallback.value);
2780 }
2781
2782 #[test]
2783 fn direct_driver_initial_guess_defaults_and_getters() {
2784 let params = BiteParams {
2785 max_evaluations: 20,
2786 stop_fitness: f64::INFINITY,
2787 seed: 102,
2788 ..Default::default()
2789 };
2790 let mut opt = BiteOpt::new(&[-1.0; 2], &[1.0; 2], Some(&[0.2, -0.2]), ¶ms);
2791 assert_eq!(opt.dim(), 2);
2792 assert_eq!(opt.population_size(), 15);
2793 assert_eq!(opt.stop_code(), 0);
2794 let result = opt.optimize(&(sphere as fn(&[f64]) -> f64));
2795 assert_eq!(result.evaluations, 1);
2796 assert_eq!(result.stop, 1);
2797
2798 let default_budget = BiteParams {
2799 max_evaluations: 0,
2800 ..Default::default()
2801 };
2802 let opt = BiteOpt::new(&[-1.0], &[1.0], None, &default_budget);
2803 assert_eq!(opt.max_evaluations, 50_000);
2804 let mut deep = DeepBiteOpt::new(&[-1.0], &[1.0], None, &default_budget, 1);
2805 assert_eq!(deep.max_evaluations, 50_000);
2806 assert_eq!(deep.dim(), 1);
2807 assert_eq!(deep.population_size(), 12);
2808 assert_eq!(deep.stop_code(), 0);
2809 let asked = deep.ask(2);
2810 assert_eq!(deep.ask(5), asked);
2811 }
2812
2813 #[test]
2814 fn ask_tell_enforces_batches_and_budget() {
2815 let params = BiteParams {
2816 popsize: 4,
2817 max_evaluations: 5,
2818 seed: 11,
2819 ..Default::default()
2820 };
2821 let mut opt = BiteOpt::new(&[-1.0; 2], &[1.0; 2], None, ¶ms);
2822
2823 assert!(opt.ask(0).is_empty());
2824 assert_eq!(opt.tell(&[0.0]), -1);
2825
2826 let first = opt.ask(3);
2827 assert_eq!(first.len(), 3);
2828 assert_eq!(opt.ask(99), first);
2829 assert_eq!(opt.tell(&[1.0, 2.0]), -1);
2830 assert_eq!(opt.current_batch_size(), 3);
2831 assert_eq!(opt.tell(&[1.0, 2.0, 3.0]), 0);
2832
2833 let second = opt.ask(8);
2835 assert_eq!(second.len(), 1);
2836 assert_eq!(opt.tell(&[4.0]), 0);
2837 let last = opt.ask(8);
2838 assert_eq!(last.len(), 1);
2839 opt.tell(&[5.0]);
2840
2841 assert!(opt.ask(8).is_empty());
2842 let result = opt.result_public();
2843 assert_eq!(result.evaluations, 5);
2844 assert_eq!(result.iterations, 5);
2845 }
2846
2847 #[test]
2848 fn ask_tell_records_the_resampled_compatible_method() {
2849 let params = BiteParams {
2850 popsize: 4,
2851 max_evaluations: 300,
2852 seed: 111,
2853 ..Default::default()
2854 };
2855 let mut opt = BiteOpt::new(&[-1.0; 2], &[1.0; 2], None, ¶ms);
2856 let initial = opt.ask(4);
2857 opt.tell(&vec![1.0; initial.len()]);
2858
2859 for cost in 2..250 {
2860 let cand = opt.gen_one(None);
2861 let method = cand
2862 .sels
2863 .iter()
2864 .find(|selection| selection.index == sel::METHOD)
2865 .unwrap();
2866 assert_ne!(method.value, 3);
2867 opt.apply_one(&cand, cost as f64, false);
2868 opt.recycle_candidate(cand);
2869 }
2870 }
2871
2872 #[test]
2873 fn deep_ask_tell_enforces_budget_and_sanitizes_costs() {
2874 let params = BiteParams {
2875 popsize: 4,
2876 max_evaluations: 7,
2877 seed: 12,
2878 ..Default::default()
2879 };
2880 let mut opt = DeepBiteOpt::new(&[-1.0; 2], &[1.0; 2], None, ¶ms, 3);
2881
2882 assert_eq!(opt.tell(&[0.0]), -1);
2883 let first = opt.ask(9);
2884 assert_eq!(first.len(), 4);
2885 assert_eq!(opt.tell(&[f64::NAN, f64::INFINITY, f64::NEG_INFINITY]), -1);
2886 assert_eq!(opt.current_batch_size(), 4);
2887 assert_eq!(
2888 opt.tell(&[f64::NAN, f64::INFINITY, f64::NEG_INFINITY, f64::NAN]),
2889 0
2890 );
2891
2892 let second = opt.ask(9);
2893 assert_eq!(second.len(), 3);
2894 opt.tell(&[3.0, 2.0, 1.0]);
2895 assert!(opt.ask(1).is_empty());
2896
2897 let result = opt.result_public();
2898 assert_eq!(result.evaluations, 7);
2899 assert_eq!(result.iterations, 7);
2900 assert!(result.y.is_finite());
2901 assert_eq!(result.y, 1.0);
2902 }
2903
2904 #[test]
2905 fn non_finite_objective_values_are_rejected() {
2906 let params = BiteParams {
2907 max_evaluations: 25,
2908 seed: 13,
2909 ..Default::default()
2910 };
2911 let result = optimize_bite(
2912 &(|_: &[f64]| f64::NAN),
2913 &[-1.0; 2],
2914 &[1.0; 2],
2915 None,
2916 ¶ms,
2917 2,
2918 );
2919 assert_eq!(result.y, BAD_COST);
2920 assert_eq!(result.evaluations, 25);
2921 assert!(result.x.iter().all(|value| value.is_finite()));
2922 }
2923
2924 #[test]
2925 fn stop_fitness_terminates_immediately() {
2926 let params = BiteParams {
2927 max_evaluations: 100,
2928 stop_fitness: f64::INFINITY,
2929 seed: 14,
2930 ..Default::default()
2931 };
2932 let result = optimize_bite(
2933 &(sphere as fn(&[f64]) -> f64),
2934 &[-1.0; 2],
2935 &[1.0; 2],
2936 None,
2937 ¶ms,
2938 1,
2939 );
2940 assert_eq!(result.evaluations, 1);
2941 assert_eq!(result.stop, 1);
2942 }
2943
2944 #[test]
2945 fn delayed_selector_feedback_restores_the_exact_slot() {
2946 let mut rnd = BiteRnd::new(15);
2947 let mut selector = BiteSel::new(3);
2948 selector.reset(&mut rnd, 4);
2949
2950 selector.slot = 2;
2951 selector.selp = 3;
2952 selector.sel = selector.sels[2][3];
2953 selector.sel_id = selector.entry_ids[2][3];
2954 let captured = selector.captured(7);
2955 let captured_id = captured.slot_id;
2956
2957 selector.slot = 4;
2959 selector.selp = 0;
2960 selector.sel = selector.sels[4][0];
2961 selector.sel_id = selector.entry_ids[4][0];
2962 let later_id = selector.slot_ids[4];
2963 selector.incr_captured(captured, 1.0);
2964
2965 let captured_slot = selector
2966 .slot_ids
2967 .iter()
2968 .position(|&id| id == captured_id)
2969 .unwrap();
2970 let later_slot = selector
2971 .slot_ids
2972 .iter()
2973 .position(|&id| id == later_id)
2974 .unwrap();
2975 assert_eq!(selector.slot_accums[captured_slot], 0.5);
2976 assert_eq!(selector.slot_accums[later_slot], 0.0);
2977 selector.decr_captured(captured);
2978 assert_eq!(selector.slot_accums[captured_slot], 0.0);
2979 assert_eq!(
2980 selector.entry_ids[captured_slot]
2981 .iter()
2982 .position(|&id| id == captured.entry_id),
2983 Some(1)
2984 );
2985 }
2986
2987 #[test]
2988 fn nelder_mead_copy_keeps_centroid_consistent() {
2989 let mut nm = NMSeqOpt::new(1, vec![-10.0], vec![20.0]);
2990 for i in 0..nm.m {
2991 nm.x[i][0] = i as f64;
2992 nm.y[i] = i as f64;
2993 }
2994 nm.calc_cent();
2995 assert_eq!(nm.xhi, nm.m - 1);
2996 nm.copy(&[-1.0], -1.0);
2997
2998 let expected =
2999 nm.x.iter()
3000 .enumerate()
3001 .filter(|(i, _)| *i != nm.xhi)
3002 .map(|(_, x)| x[0])
3003 .sum::<f64>()
3004 * nm.m1i;
3005 assert!((nm.x0[0] - expected).abs() < 1e-14);
3006 }
3007
3008 #[test]
3009 fn dynamic_population_size_stays_within_upstream_limits() {
3010 let params = BiteParams {
3011 popsize: 12,
3012 max_evaluations: 600,
3013 seed: 16,
3014 ..Default::default()
3015 };
3016 let mut opt = BiteOpt::new(&[-2.0; 3], &[2.0; 3], None, ¶ms);
3017 let mut next_good = -1.0;
3018 while opt.evaluations < params.max_evaluations {
3019 let xs = opt.ask(1);
3020 let cost = if opt.evaluations.is_multiple_of(2) {
3021 next_good -= 1.0;
3022 next_good
3023 } else {
3024 BAD_COST
3025 };
3026 opt.tell(&vec![cost; xs.len()]);
3027 assert!(opt.pop.cur_pop_size >= opt.pop_size / 2);
3028 assert!(opt.pop.cur_pop_size <= opt.pop_size);
3029 }
3030 }
3031
3032 #[test]
3033 fn secondary_optimizers_make_progress() {
3034 let objective = sphere as fn(&[f64]) -> f64;
3035 let mut rnd = BiteRnd::new(17);
3036 let mut spher = SpherOpt::new(3, vec![-5.0; 3], vec![10.0; 3], 17);
3037 spher.init(&mut rnd, None, 1.0);
3038 for _ in 0..2_000 {
3039 spher.optimize(&mut rnd, &objective);
3040 }
3041 assert!(spher.best_cost < 1e-5, "spher: {}", spher.best_cost);
3042
3043 let mut nm = NMSeqOpt::new(3, vec![-5.0; 3], vec![10.0; 3]);
3044 nm.init(&mut rnd, None, 1.0);
3045 for _ in 0..2_000 {
3046 nm.optimize(&mut rnd, &objective);
3047 }
3048 assert!(nm.best_cost < 1e-8, "nelder-mead: {}", nm.best_cost);
3049 }
3050}