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