1#![allow(clippy::needless_range_loop, clippy::too_many_arguments)]
29
30use crate::errors::EdgeErrors;
31
32#[derive(Clone, Copy, Debug)]
38pub struct BobyqaParams {
39 pub xtol_rel: f64,
41 pub maxeval: usize,
43}
44
45impl Default for BobyqaParams {
46 fn default() -> Self {
48 Self {
49 xtol_rel: 1e-6,
50 maxeval: 1000,
51 }
52 }
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub enum BobyqaStatus {
58 Running,
60 XtolReached,
62 MaxevalReached,
64 RoundoffLimited,
66 Success,
69}
70
71#[derive(Clone, Debug)]
73pub struct BobyqaResult {
74 pub x: Vec<f64>,
76 pub f: f64,
78 pub evaluations: usize,
80 pub status: BobyqaStatus,
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90enum Label {
91 PrelimNext,
93 PrelimPost,
95 Start,
97 L20,
99 L60,
101 L90,
103 L190,
105 L190Post,
107 L210,
109 L230,
111 L360,
113 L360Post,
115 L650,
117 L680,
119 L720,
121 RescueStart,
123 RescueNext,
125 RescuePost,
127 Wait,
129 Done,
131}
132
133pub struct BobyqaStepper {
142 n: usize,
144 npt: usize,
146 ndim: usize,
148 s: Vec<f64>,
150 xl: Vec<f64>,
152 xu: Vec<f64>,
154 rhoend: f64,
156 rhobeg: f64,
158 maxeval: usize,
160 nevals: usize,
162
163 x: Vec<f64>,
165 pending: Vec<f64>,
167 resume: Label,
169 label: Label,
171 f: f64,
173 rc: BobyqaStatus,
175 minf: f64,
177 xbest: Vec<f64>,
179
180 xbase: Vec<f64>,
183 xpt: Vec<f64>,
185 fval: Vec<f64>,
187 xopt: Vec<f64>,
189 gopt: Vec<f64>,
191 hq: Vec<f64>,
193 pq: Vec<f64>,
195 bmat: Vec<f64>,
197 zmat: Vec<f64>,
199 sl: Vec<f64>,
201 su: Vec<f64>,
203 xnew: Vec<f64>,
205 xalt: Vec<f64>,
207 d: Vec<f64>,
209 vlag: Vec<f64>,
211 w: Vec<f64>,
213
214 kopt: usize,
217 knew: usize,
219 kbase: usize,
221 ntrits: i64,
223 nresc: usize,
225 nfsav: usize,
227 itest: i64,
229 diffa: f64,
231 diffb: f64,
233 diffc: f64,
235 rho: f64,
237 delta: f64,
239 xoptsq: f64,
241 dsq: f64,
243 crvmin: f64,
245 dnorm: f64,
247 adelt: f64,
249 distsq: f64,
251 alpha: f64,
253 cauchy: f64,
255 beta: f64,
257 denom: f64,
259 vquad: f64,
261 ratio: f64,
263 fopt: f64,
265 fsave: f64,
267
268 nf: usize,
271 nfm: usize,
273 nfx: i64,
275 stepa: f64,
277 stepb: f64,
279 fbeg: f64,
281 ipt: usize,
283 jpt: usize,
285
286 kpt: usize,
289 rvquad: f64,
291 fbase: f64,
293 rip: usize,
295 riq: usize,
297 xp: f64,
299 xq: f64,
301}
302
303impl BobyqaStepper {
304 pub fn new(
319 x0: &[f64],
320 lower: &[f64],
321 upper: &[f64],
322 params: Option<BobyqaParams>,
323 ) -> Result<Self, EdgeErrors> {
324 let params = params.unwrap_or_default();
325 let n = x0.len();
326 if n == 0 || lower.len() != n || upper.len() != n {
327 return Err(EdgeErrors::InvalidArgument(
328 "BOBYQA needs matching, non-empty x0 and bounds.".to_string(),
329 ));
330 }
331
332 let mut dx = vec![0.0; n];
334 for i in 0..n {
335 let (lb, ub, x) = (lower[i], upper[i], x0[i]);
336 let mut step = f64::INFINITY;
337 if ub.is_finite() && lb.is_finite() && (ub - lb) * 0.25 < step && ub > lb {
338 step = (ub - lb) * 0.25;
339 }
340 if ub.is_finite() && ub - x < step && ub > x {
341 step = (ub - x) * 0.75;
342 }
343 if lb.is_finite() && x - lb < step && x > lb {
344 step = (x - lb) * 0.75;
345 }
346 if step.is_infinite() {
347 if ub.is_finite() && (ub - x).abs() < step.abs() {
348 step = (ub - x) * 1.1;
349 }
350 if lb.is_finite() && (x - lb).abs() < step.abs() {
351 step = (x - lb) * 1.1;
352 }
353 }
354 if step.is_infinite() || step == 0.0 || step.is_subnormal() {
355 step = x;
356 }
357 if step.is_infinite() || step == 0.0 {
358 step = 1.0;
359 }
360 dx[i] = step;
361 }
362
363 let mut s = vec![1.0; n];
365 if n > 1 && (1..n).any(|i| dx[i] != dx[i - 1]) {
366 for i in 1..n {
367 s[i] = dx[i] / dx[0];
368 }
369 }
370 if s.iter().any(|&v| v == 0.0 || !v.is_finite()) {
371 return Err(EdgeErrors::InvalidArgument(
372 "BOBYQA rescaling overflowed.".to_string(),
373 ));
374 }
375
376 let one_based = |v: &[f64], scale: &[f64]| -> Vec<f64> {
377 std::iter::once(0.0)
378 .chain(v.iter().zip(scale).map(|(a, b)| a / b))
379 .collect()
380 };
381 let mut x = one_based(x0, &s);
382 let mut xl = one_based(lower, &s);
383 let mut xu = one_based(upper, &s);
384 for j in 1..=n {
385 if xl[j] > xu[j] {
386 std::mem::swap(&mut xl[j], &mut xu[j]);
387 }
388 }
389 let rhobeg = (dx[0] / s[0]).abs();
390 let rhoend = params.xtol_rel * rhobeg;
391
392 let npt = 2 * n + 1;
393 let np = n + 1;
394 let ndim = npt + n;
395 let mut sl = vec![0.0; n + 1];
396 let mut su = vec![0.0; n + 1];
397 for j in 1..=n {
398 let temp = xu[j] - xl[j];
399 if temp < rhobeg + rhobeg {
400 return Err(EdgeErrors::InvalidArgument(format!(
401 "insufficient space between the bounds: {} - {} < {}",
402 xu[j],
403 xl[j],
404 rhobeg + rhobeg
405 )));
406 }
407 sl[j] = xl[j] - x[j];
408 su[j] = xu[j] - x[j];
409 if sl[j] >= -rhobeg {
410 if sl[j] >= 0.0 {
411 x[j] = xl[j];
412 sl[j] = 0.0;
413 su[j] = temp;
414 } else {
415 x[j] = xl[j] + rhobeg;
416 sl[j] = -rhobeg;
417 su[j] = (xu[j] - x[j]).max(rhobeg);
418 }
419 } else if su[j] <= rhobeg {
420 if su[j] <= 0.0 {
421 x[j] = xu[j];
422 sl[j] = -temp;
423 su[j] = 0.0;
424 } else {
425 x[j] = xu[j] - rhobeg;
426 sl[j] = (xl[j] - x[j]).min(-rhobeg);
427 su[j] = rhobeg;
428 }
429 }
430 }
431
432 let mut stepper = Self {
433 n,
434 npt,
435 ndim,
436 s,
437 xl,
438 xu,
439 rhoend,
440 rhobeg,
441 maxeval: params.maxeval,
442 nevals: 0,
443 x,
444 pending: vec![0.0; n],
445 resume: Label::Done,
446 label: Label::PrelimNext,
447 f: 0.0,
448 rc: BobyqaStatus::Running,
449 minf: f64::INFINITY,
450 xbest: vec![0.0; n + 1],
451 xbase: vec![0.0; n + 1],
452 xpt: vec![0.0; npt * n],
453 fval: vec![0.0; npt + 1],
454 xopt: vec![0.0; n + 1],
455 gopt: vec![0.0; n + 1],
456 hq: vec![0.0; n * np / 2 + 1],
457 pq: vec![0.0; npt + 1],
458 bmat: vec![0.0; ndim * n],
459 zmat: vec![0.0; npt * (npt - np)],
460 sl,
461 su,
462 xnew: vec![0.0; n + 1],
463 xalt: vec![0.0; n + 1],
464 d: vec![0.0; n + 1],
465 vlag: vec![0.0; ndim + 1],
466 w: vec![0.0; 3 * ndim + n + npt + 1],
467 kopt: 1,
468 knew: 0,
469 kbase: 1,
470 ntrits: 0,
471 nresc: 0,
472 nfsav: 0,
473 itest: 0,
474 diffa: 0.0,
475 diffb: 0.0,
476 diffc: 0.0,
477 rho: rhobeg,
478 delta: rhobeg,
479 xoptsq: 0.0,
480 dsq: 0.0,
481 crvmin: 0.0,
482 dnorm: 0.0,
483 adelt: 0.0,
484 distsq: 0.0,
485 alpha: 0.0,
486 cauchy: 0.0,
487 beta: 0.0,
488 denom: 0.0,
489 vquad: 0.0,
490 ratio: 0.0,
491 fopt: 0.0,
492 fsave: 0.0,
493 nf: 0,
494 nfm: 0,
495 nfx: 0,
496 stepa: 0.0,
497 stepb: 0.0,
498 fbeg: 0.0,
499 ipt: 0,
500 jpt: 0,
501 kpt: 0,
502 rvquad: 0.0,
503 fbase: 0.0,
504 rip: 0,
505 riq: 0,
506 xp: 0.0,
507 xq: 0.0,
508 };
509 stepper.prelim_init();
510 stepper.advance();
511 Ok(stepper)
512 }
513
514 pub fn ask(&self) -> Option<&[f64]> {
520 if self.label == Label::Wait {
521 Some(&self.pending)
522 } else {
523 None
524 }
525 }
526
527 pub fn tell(&mut self, value: f64) {
533 debug_assert_eq!(self.label, Label::Wait);
534 self.f = value;
535 self.label = self.resume;
536 self.advance();
537 }
538
539 pub fn result(&self) -> BobyqaResult {
546 BobyqaResult {
547 x: (1..=self.n)
548 .map(|j| self.xbest[j] * self.s[j - 1])
549 .collect(),
550 f: self.minf,
551 evaluations: self.nevals,
552 status: self.rc,
553 }
554 }
555
556 fn request(&mut self, next: Label) {
562 for j in 1..=self.n {
563 self.pending[j - 1] = self.x[j] * self.s[j - 1];
564 }
565 self.nevals += 1;
566 self.resume = next;
567 self.label = Label::Wait;
568 }
569
570 fn stop_evals(&self) -> bool {
576 self.maxeval > 0 && self.nevals >= self.maxeval
577 }
578
579 #[inline]
581 fn ix(&self, k: usize, j: usize) -> usize {
582 (k - 1) + (j - 1) * self.npt
583 }
584
585 #[inline]
587 fn ib(&self, k: usize, j: usize) -> usize {
588 (k - 1) + (j - 1) * self.ndim
589 }
590
591 #[inline]
593 fn iz(&self, k: usize, j: usize) -> usize {
594 (k - 1) + (j - 1) * self.npt
595 }
596
597 fn prelim_init(&mut self) {
599 for j in 1..=self.n {
600 self.xbase[j] = self.x[j];
601 }
602 self.xpt.fill(0.0);
603 self.bmat.fill(0.0);
604 self.hq.fill(0.0);
605 self.pq.fill(0.0);
606 self.zmat.fill(0.0);
607 self.nf = 0;
608 }
609
610 fn advance(&mut self) {
612 loop {
613 self.label = match self.label {
614 Label::PrelimNext => self.prelim_next(),
615 Label::PrelimPost => self.prelim_post(),
616 Label::Start => self.start(),
617 Label::L20 => self.l20(),
618 Label::L60 => self.l60(),
619 Label::L90 => self.l90(),
620 Label::L190 => {
621 self.nfsav = self.nevals;
622 self.kbase = self.kopt;
623 Label::RescueStart
624 }
625 Label::L190Post => self.l190_post(),
626 Label::L210 => self.l210(),
627 Label::L230 => self.l230(),
628 Label::L360 => self.l360(),
629 Label::L360Post => self.l360_post(),
630 Label::L650 => self.l650(),
631 Label::L680 => self.l680(),
632 Label::L720 => {
633 self.l720();
634 Label::Done
635 }
636 Label::RescueStart => self.rescue_start(),
637 Label::RescueNext => self.rescue_next(),
638 Label::RescuePost => self.rescue_post(),
639 Label::Wait | Label::Done => return,
640 };
641 }
642 }
643
644 fn prelim_next(&mut self) -> Label {
650 let n = self.n;
651 let np = n + 1;
652 self.nfm = self.nf;
653 self.nfx = self.nf as i64 - n as i64;
654 self.nf += 1;
655 let nf = self.nf;
656 let nfm = self.nfm;
657 let rhobeg = self.rhobeg;
658 if nfm <= 2 * n {
659 if nfm >= 1 && nfm <= n {
660 self.stepa = rhobeg;
661 if self.su[nfm] == 0.0 {
662 self.stepa = -self.stepa;
663 }
664 let i = self.ix(nf, nfm);
665 self.xpt[i] = self.stepa;
666 } else if nfm > n {
667 let nfx = self.nfx as usize;
668 self.stepa = self.xpt[self.ix(nf - n, nfx)];
669 self.stepb = -rhobeg;
670 if self.sl[nfx] == 0.0 {
671 self.stepb = (2.0 * rhobeg).min(self.su[nfx]);
672 }
673 if self.su[nfx] == 0.0 {
674 self.stepb = (-2.0 * rhobeg).max(self.sl[nfx]);
675 }
676 let i = self.ix(nf, nfx);
677 self.xpt[i] = self.stepb;
678 }
679 } else {
680 let mut itemp = (nfm - np) / n;
681 let mut jpt = nfm - itemp * n - n;
682 let mut ipt = jpt + itemp;
683 if ipt > n {
684 itemp = jpt;
685 jpt = ipt - n;
686 ipt = itemp;
687 }
688 self.ipt = ipt;
689 self.jpt = jpt;
690 let a = self.xpt[self.ix(ipt + 1, ipt)];
691 let i = self.ix(nf, ipt);
692 self.xpt[i] = a;
693 let b = self.xpt[self.ix(jpt + 1, jpt)];
694 let i = self.ix(nf, jpt);
695 self.xpt[i] = b;
696 }
697
698 for j in 1..=n {
699 let p = self.xpt[self.ix(nf, j)];
700 self.x[j] = self.xl[j].max(self.xbase[j] + p).min(self.xu[j]);
701 if p == self.sl[j] {
702 self.x[j] = self.xl[j];
703 }
704 if p == self.su[j] {
705 self.x[j] = self.xu[j];
706 }
707 }
708 self.request(Label::PrelimPost);
709 Label::Wait
710 }
711
712 fn prelim_post(&mut self) -> Label {
714 let n = self.n;
715 let npt = self.npt;
716 let nf = self.nf;
717 let nfm = self.nfm;
718 let f = self.f;
719 let rhosq = self.rhobeg * self.rhobeg;
720 let recip = 1.0 / rhosq;
721 self.fval[nf] = f;
722 if nf == 1 {
723 self.fbeg = f;
724 self.kopt = 1;
725 } else if f < self.fval[self.kopt] {
726 self.kopt = nf;
727 }
728
729 if nf <= 2 * n + 1 {
730 if nf >= 2 && nf <= n + 1 {
731 self.gopt[nfm] = (f - self.fbeg) / self.stepa;
732 if npt < nf + n {
733 let (a, b, c) = (self.ib(1, nfm), self.ib(nf, nfm), self.ib(npt + nfm, nfm));
734 self.bmat[a] = -1.0 / self.stepa;
735 self.bmat[b] = 1.0 / self.stepa;
736 self.bmat[c] = -0.5 * rhosq;
737 }
738 } else if nf >= n + 2 {
739 let nfx = self.nfx as usize;
740 let ih = nfx * (nfx + 1) / 2;
741 let temp = (f - self.fbeg) / self.stepb;
742 let diff = self.stepb - self.stepa;
743 self.hq[ih] = 2.0 * (temp - self.gopt[nfx]) / diff;
744 self.gopt[nfx] = (self.gopt[nfx] * self.stepb - temp * self.stepa) / diff;
745 if self.stepa * self.stepb < 0.0 && f < self.fval[nf - n] {
746 self.fval[nf] = self.fval[nf - n];
747 self.fval[nf - n] = f;
748 if self.kopt == nf {
749 self.kopt = nf - n;
750 }
751 let (a, b) = (self.ix(nf - n, nfx), self.ix(nf, nfx));
752 self.xpt[a] = self.stepb;
753 self.xpt[b] = self.stepa;
754 }
755 let b1 = self.ib(1, nfx);
756 let bnf = self.ib(nf, nfx);
757 let bnn = self.ib(nf - n, nfx);
758 self.bmat[b1] = -(self.stepa + self.stepb) / (self.stepa * self.stepb);
759 self.bmat[bnf] = -0.5 / self.xpt[self.ix(nf - n, nfx)];
760 self.bmat[bnn] = -self.bmat[b1] - self.bmat[bnf];
761 let z1 = self.iz(1, nfx);
762 let znf = self.iz(nf, nfx);
763 let znn = self.iz(nf - n, nfx);
764 self.zmat[z1] = 2.0f64.sqrt() / (self.stepa * self.stepb);
765 self.zmat[znf] = 0.5f64.sqrt() / rhosq;
766 self.zmat[znn] = -self.zmat[z1] - self.zmat[znf];
767 }
768 } else {
769 let nfx = self.nfx as usize;
770 let (ipt, jpt) = (self.ipt, self.jpt);
771 let ih = ipt * (ipt - 1) / 2 + jpt;
772 let idx = [
773 self.iz(1, nfx),
774 self.iz(nf, nfx),
775 self.iz(ipt + 1, nfx),
776 self.iz(jpt + 1, nfx),
777 ];
778 self.zmat[idx[0]] = recip;
779 self.zmat[idx[1]] = recip;
780 self.zmat[idx[2]] = -recip;
781 self.zmat[idx[3]] = -recip;
782 let temp = self.xpt[self.ix(nf, ipt)] * self.xpt[self.ix(nf, jpt)];
783 self.hq[ih] = (self.fbeg - self.fval[ipt + 1] - self.fval[jpt + 1] + f) / temp;
784 }
785 if self.stop_evals() {
786 self.rc = BobyqaStatus::MaxevalReached;
787 return self.after_prelim();
788 }
789 if nf < npt {
790 return Label::PrelimNext;
791 }
792 self.after_prelim()
793 }
794
795 fn after_prelim(&mut self) -> Label {
798 self.xoptsq = 0.0;
799 for i in 1..=self.n {
800 self.xopt[i] = self.xpt[self.ix(self.kopt, i)];
801 self.xoptsq += self.xopt[i] * self.xopt[i];
802 }
803 self.fsave = self.fval[1];
804 if self.rc != BobyqaStatus::Running {
805 return Label::L720;
806 }
807 Label::Start
808 }
809
810 fn start(&mut self) -> Label {
816 self.kbase = 1;
817 self.rho = self.rhobeg;
818 self.delta = self.rho;
819 self.nresc = self.nevals;
820 self.ntrits = 0;
821 self.diffa = 0.0;
822 self.diffb = 0.0;
823 self.itest = 0;
824 self.nfsav = self.nevals;
825 Label::L20
826 }
827
828 fn l20(&mut self) -> Label {
830 let n = self.n;
831 let npt = self.npt;
832 if self.kopt != self.kbase {
833 let mut ih = 0;
834 for j in 1..=n {
835 for i in 1..=j {
836 ih += 1;
837 if i < j {
838 self.gopt[j] += self.hq[ih] * self.xopt[i];
839 }
840 self.gopt[i] += self.hq[ih] * self.xopt[j];
841 }
842 }
843 if self.nevals > npt {
844 for k in 1..=npt {
845 let mut temp = 0.0;
846 for j in 1..=n {
847 temp += self.xpt[self.ix(k, j)] * self.xopt[j];
848 }
849 temp *= self.pq[k];
850 for i in 1..=n {
851 self.gopt[i] += temp * self.xpt[self.ix(k, i)];
852 }
853 }
854 }
855 }
856 Label::L60
857 }
858
859 fn l60(&mut self) -> Label {
861 let n = self.n;
862 let npt = self.npt;
863 {
864 let (gnew, rest) = self.w[1..].split_at_mut(n);
865 let (xbdi, rest) = rest.split_at_mut(n);
866 let (s, rest) = rest.split_at_mut(n);
867 let (hs, rest) = rest.split_at_mut(n);
868 let hred = &mut rest[..n];
869 trsbox(
870 n,
871 npt,
872 &self.xpt,
873 &self.xopt[1..],
874 &self.gopt[1..],
875 &self.hq[1..],
876 &self.pq[1..],
877 &self.sl[1..],
878 &self.su[1..],
879 self.delta,
880 &mut self.xnew[1..],
881 &mut self.d[1..],
882 gnew,
883 xbdi,
884 s,
885 hs,
886 hred,
887 &mut self.dsq,
888 &mut self.crvmin,
889 );
890 }
891 self.dnorm = self.delta.min(self.dsq.sqrt());
892 if self.dnorm < 0.5 * self.rho {
893 self.ntrits = -1;
894 self.distsq = (10.0 * self.rho) * (10.0 * self.rho);
895 if self.nevals <= self.nfsav + 2 {
896 return Label::L650;
897 }
898 let errbig = self.diffa.max(self.diffb).max(self.diffc);
899 let frhosq = self.rho * 0.125 * self.rho;
900 if self.crvmin > 0.0 && errbig > frhosq * self.crvmin {
901 return Label::L650;
902 }
903 let bdtol = errbig / self.rho;
904 for j in 1..=n {
905 let mut bdtest = bdtol;
906 if self.xnew[j] == self.sl[j] {
907 bdtest = self.w[j];
908 }
909 if self.xnew[j] == self.su[j] {
910 bdtest = -self.w[j];
911 }
912 if bdtest < bdtol {
913 let mut curv = self.hq[(j + j * j) / 2];
914 for k in 1..=npt {
915 let v = self.xpt[self.ix(k, j)];
916 curv += self.pq[k] * (v * v);
917 }
918 bdtest += 0.5 * curv * self.rho;
919 if bdtest < bdtol {
920 return Label::L650;
921 }
922 }
923 }
924 return Label::L680;
925 }
926 self.ntrits += 1;
927 Label::L90
928 }
929
930 fn l90(&mut self) -> Label {
932 let n = self.n;
933 let npt = self.npt;
934 let nptm = npt - n - 1;
935 if self.dsq <= self.xoptsq * 0.001 {
936 let fracsq = self.xoptsq * 0.25;
937 let mut sumpq = 0.0;
938 for k in 1..=npt {
939 sumpq += self.pq[k];
940 let mut sum = -0.5 * self.xoptsq;
941 for i in 1..=n {
942 sum += self.xpt[self.ix(k, i)] * self.xopt[i];
943 }
944 self.w[npt + k] = sum;
945 let temp = fracsq - 0.5 * sum;
946 for i in 1..=n {
947 self.w[i] = self.bmat[self.ib(k, i)];
948 self.vlag[i] = sum * self.xpt[self.ix(k, i)] + temp * self.xopt[i];
949 let ip = npt + i;
950 for j in 1..=i {
951 let b = self.ib(ip, j);
952 self.bmat[b] =
953 self.bmat[b] + self.w[i] * self.vlag[j] + self.vlag[i] * self.w[j];
954 }
955 }
956 }
957
958 for jj in 1..=nptm {
959 let mut sumz = 0.0;
960 let mut sumw = 0.0;
961 for k in 1..=npt {
962 let z = self.zmat[self.iz(k, jj)];
963 sumz += z;
964 self.vlag[k] = self.w[npt + k] * z;
965 sumw += self.vlag[k];
966 }
967 for j in 1..=n {
968 let mut sum = (fracsq * sumz - 0.5 * sumw) * self.xopt[j];
969 for k in 1..=npt {
970 sum += self.vlag[k] * self.xpt[self.ix(k, j)];
971 }
972 self.w[j] = sum;
973 for k in 1..=npt {
974 let b = self.ib(k, j);
975 self.bmat[b] += sum * self.zmat[self.iz(k, jj)];
976 }
977 }
978 for i in 1..=n {
979 let ip = i + npt;
980 let temp = self.w[i];
981 for j in 1..=i {
982 let b = self.ib(ip, j);
983 self.bmat[b] += temp * self.w[j];
984 }
985 }
986 }
987
988 let mut ih = 0;
989 for j in 1..=n {
990 self.w[j] = -0.5 * sumpq * self.xopt[j];
991 for k in 1..=npt {
992 self.w[j] += self.pq[k] * self.xpt[self.ix(k, j)];
993 let x = self.ix(k, j);
994 self.xpt[x] -= self.xopt[j];
995 }
996 for i in 1..=j {
997 ih += 1;
998 self.hq[ih] = self.hq[ih] + self.w[i] * self.xopt[j] + self.xopt[i] * self.w[j];
999 let (dst, src) = (self.ib(npt + i, j), self.ib(npt + j, i));
1000 self.bmat[dst] = self.bmat[src];
1001 }
1002 }
1003 for i in 1..=n {
1004 self.xbase[i] += self.xopt[i];
1005 self.xnew[i] -= self.xopt[i];
1006 self.sl[i] -= self.xopt[i];
1007 self.su[i] -= self.xopt[i];
1008 self.xopt[i] = 0.0;
1009 }
1010 self.xoptsq = 0.0;
1011 }
1012 if self.ntrits == 0 {
1013 Label::L210
1014 } else {
1015 Label::L230
1016 }
1017 }
1018
1019 fn l190_post(&mut self) -> Label {
1021 let n = self.n;
1022 self.xoptsq = 0.0;
1023 if self.kopt != self.kbase {
1024 for i in 1..=n {
1025 self.xopt[i] = self.xpt[self.ix(self.kopt, i)];
1026 self.xoptsq += self.xopt[i] * self.xopt[i];
1027 }
1028 }
1029 if self.rc != BobyqaStatus::Running {
1030 return Label::L720;
1031 }
1032 self.nresc = self.nevals;
1033 if self.nfsav < self.nevals {
1034 self.nfsav = self.nevals;
1035 return Label::L20;
1036 }
1037 if self.ntrits > 0 {
1038 return Label::L60;
1039 }
1040 Label::L210
1041 }
1042
1043 fn l210(&mut self) -> Label {
1045 let n = self.n;
1046 let npt = self.npt;
1047 let ndim = self.ndim;
1048 {
1049 let (head, tail) = self.w.split_at_mut(ndim + 1);
1050 let (glag, hcol) = head[1..].split_at_mut(n);
1051 let hcol = &mut hcol[..npt];
1052 let wk = &mut tail[..2 * n];
1053 altmov(
1054 n,
1055 npt,
1056 &self.xpt,
1057 &self.xopt[1..],
1058 &self.bmat,
1059 &self.zmat,
1060 ndim,
1061 &self.sl[1..],
1062 &self.su[1..],
1063 self.kopt,
1064 self.knew,
1065 self.adelt,
1066 &mut self.xnew[1..],
1067 &mut self.xalt[1..],
1068 &mut self.alpha,
1069 &mut self.cauchy,
1070 glag,
1071 hcol,
1072 wk,
1073 );
1074 }
1075 for i in 1..=n {
1076 self.d[i] = self.xnew[i] - self.xopt[i];
1077 }
1078 Label::L230
1079 }
1080
1081 fn l230(&mut self) -> Label {
1083 let n = self.n;
1084 let npt = self.npt;
1085 let nptm = npt - n - 1;
1086 for k in 1..=npt {
1087 let mut suma = 0.0;
1088 let mut sumb = 0.0;
1089 let mut sum = 0.0;
1090 for j in 1..=n {
1091 suma += self.xpt[self.ix(k, j)] * self.d[j];
1092 sumb += self.xpt[self.ix(k, j)] * self.xopt[j];
1093 sum += self.bmat[self.ib(k, j)] * self.d[j];
1094 }
1095 self.w[k] = suma * (0.5 * suma + sumb);
1096 self.vlag[k] = sum;
1097 self.w[npt + k] = suma;
1098 }
1099 self.beta = 0.0;
1100 for jj in 1..=nptm {
1101 let mut sum = 0.0;
1102 for k in 1..=npt {
1103 sum += self.zmat[self.iz(k, jj)] * self.w[k];
1104 }
1105 self.beta -= sum * sum;
1106 for k in 1..=npt {
1107 self.vlag[k] += sum * self.zmat[self.iz(k, jj)];
1108 }
1109 }
1110 self.dsq = 0.0;
1111 let mut bsum = 0.0;
1112 let mut dx = 0.0;
1113 for j in 1..=n {
1114 self.dsq += self.d[j] * self.d[j];
1115 let mut sum = 0.0;
1116 for k in 1..=npt {
1117 sum += self.w[k] * self.bmat[self.ib(k, j)];
1118 }
1119 bsum += sum * self.d[j];
1120 let jp = npt + j;
1121 for i in 1..=n {
1122 sum += self.bmat[self.ib(jp, i)] * self.d[i];
1123 }
1124 self.vlag[jp] = sum;
1125 bsum += sum * self.d[j];
1126 dx += self.d[j] * self.xopt[j];
1127 }
1128 self.beta =
1129 dx * dx + self.dsq * (self.xoptsq + dx + dx + 0.5 * self.dsq) + self.beta - bsum;
1130 self.vlag[self.kopt] += 1.0;
1131
1132 if self.ntrits == 0 {
1133 let vk = self.vlag[self.knew];
1134 self.denom = vk * vk + self.alpha * self.beta;
1135 if self.denom < self.cauchy && self.cauchy > 0.0 {
1136 for i in 1..=n {
1137 self.xnew[i] = self.xalt[i];
1138 self.d[i] = self.xnew[i] - self.xopt[i];
1139 }
1140 self.cauchy = 0.0;
1141 return Label::L230;
1142 }
1143 if self.denom <= 0.5 * (vk * vk) {
1144 if self.nevals > self.nresc {
1145 return Label::L190;
1146 }
1147 self.rc = BobyqaStatus::RoundoffLimited;
1148 return Label::L720;
1149 }
1150 } else {
1151 let delsq = self.delta * self.delta;
1152 let mut scaden = 0.0;
1153 let mut biglsq = 0.0f64;
1154 self.knew = 0;
1155 for k in 1..=npt {
1156 if k == self.kopt {
1157 continue;
1158 }
1159 let mut hdiag = 0.0;
1160 for jj in 1..=nptm {
1161 let z = self.zmat[self.iz(k, jj)];
1162 hdiag += z * z;
1163 }
1164 let den = self.beta * hdiag + self.vlag[k] * self.vlag[k];
1165 let mut distsq = 0.0;
1166 for j in 1..=n {
1167 let v = self.xpt[self.ix(k, j)] - self.xopt[j];
1168 distsq += v * v;
1169 }
1170 let r = distsq / delsq;
1171 let temp = 1.0f64.max(r * r);
1172 if temp * den > scaden {
1173 scaden = temp * den;
1174 self.knew = k;
1175 self.denom = den;
1176 }
1177 biglsq = biglsq.max(temp * (self.vlag[k] * self.vlag[k]));
1178 }
1179 if scaden <= 0.5 * biglsq {
1180 if self.nevals > self.nresc {
1181 return Label::L190;
1182 }
1183 self.rc = BobyqaStatus::RoundoffLimited;
1184 return Label::L720;
1185 }
1186 }
1187 Label::L360
1188 }
1189
1190 fn l360(&mut self) -> Label {
1192 for i in 1..=self.n {
1193 self.x[i] = self.xl[i].max(self.xbase[i] + self.xnew[i]).min(self.xu[i]);
1194 if self.xnew[i] == self.sl[i] {
1195 self.x[i] = self.xl[i];
1196 }
1197 if self.xnew[i] == self.su[i] {
1198 self.x[i] = self.xu[i];
1199 }
1200 }
1201 if self.stop_evals() {
1202 self.rc = BobyqaStatus::MaxevalReached;
1203 return Label::L720;
1204 }
1205 self.request(Label::L360Post);
1206 Label::Wait
1207 }
1208
1209 fn l360_post(&mut self) -> Label {
1211 let n = self.n;
1212 let npt = self.npt;
1213 let nptm = npt - n - 1;
1214 let nh = n * (n + 1) / 2;
1215 let f = self.f;
1216 if self.ntrits == -1 {
1217 self.fsave = f;
1218 self.rc = BobyqaStatus::XtolReached;
1219 if self.fsave < self.fval[self.kopt] {
1220 self.minf = f;
1221 self.xbest.copy_from_slice(&self.x);
1222 return Label::Done;
1223 }
1224 return Label::L720;
1225 }
1226
1227 self.fopt = self.fval[self.kopt];
1228 let fopt = self.fopt;
1229 let mut vquad = 0.0;
1230 let mut ih = 0;
1231 for j in 1..=n {
1232 vquad += self.d[j] * self.gopt[j];
1233 for i in 1..=j {
1234 ih += 1;
1235 let mut temp = self.d[i] * self.d[j];
1236 if i == j {
1237 temp *= 0.5;
1238 }
1239 vquad += self.hq[ih] * temp;
1240 }
1241 }
1242 for k in 1..=npt {
1243 let v = self.w[npt + k];
1244 vquad += 0.5 * self.pq[k] * (v * v);
1245 }
1246 self.vquad = vquad;
1247 let diff = f - fopt - vquad;
1248 self.diffc = self.diffb;
1249 self.diffb = self.diffa;
1250 self.diffa = diff.abs();
1251 if self.dnorm > self.rho {
1252 self.nfsav = self.nevals;
1253 }
1254
1255 if self.ntrits > 0 {
1256 if vquad >= 0.0 {
1257 self.rc = BobyqaStatus::RoundoffLimited;
1258 return Label::L720;
1259 }
1260 self.ratio = (f - fopt) / vquad;
1261 if self.ratio <= 0.1 {
1262 self.delta = (0.5 * self.delta).min(self.dnorm);
1263 } else if self.ratio <= 0.7 {
1264 self.delta = (0.5 * self.delta).max(self.dnorm);
1265 } else {
1266 self.delta = (0.5 * self.delta).max(self.dnorm + self.dnorm);
1267 }
1268 if self.delta <= self.rho * 1.5 {
1269 self.delta = self.rho;
1270 }
1271
1272 if f < fopt {
1273 let ksav = self.knew;
1274 let densav = self.denom;
1275 let delsq = self.delta * self.delta;
1276 let mut scaden = 0.0;
1277 let mut biglsq = 0.0f64;
1278 self.knew = 0;
1279 for k in 1..=npt {
1280 let mut hdiag = 0.0;
1281 for jj in 1..=nptm {
1282 let z = self.zmat[self.iz(k, jj)];
1283 hdiag += z * z;
1284 }
1285 let den = self.beta * hdiag + self.vlag[k] * self.vlag[k];
1286 let mut distsq = 0.0;
1287 for j in 1..=n {
1288 let v = self.xpt[self.ix(k, j)] - self.xnew[j];
1289 distsq += v * v;
1290 }
1291 let r = distsq / delsq;
1292 let temp = 1.0f64.max(r * r);
1293 if temp * den > scaden {
1294 scaden = temp * den;
1295 self.knew = k;
1296 self.denom = den;
1297 }
1298 biglsq = biglsq.max(temp * (self.vlag[k] * self.vlag[k]));
1299 }
1300 if scaden <= 0.5 * biglsq {
1301 self.knew = ksav;
1302 self.denom = densav;
1303 }
1304 }
1305 }
1306
1307 let knew = self.knew;
1308 update(
1309 n,
1310 npt,
1311 &mut self.bmat,
1312 &mut self.zmat,
1313 self.ndim,
1314 &mut self.vlag[1..],
1315 self.beta,
1316 self.denom,
1317 knew,
1318 &mut self.w[1..],
1319 );
1320 let mut ih = 0;
1321 let pqold = self.pq[knew];
1322 self.pq[knew] = 0.0;
1323 for i in 1..=n {
1324 let temp = pqold * self.xpt[self.ix(knew, i)];
1325 for j in 1..=i {
1326 ih += 1;
1327 self.hq[ih] += temp * self.xpt[self.ix(knew, j)];
1328 }
1329 }
1330 for jj in 1..=nptm {
1331 let temp = diff * self.zmat[self.iz(knew, jj)];
1332 for k in 1..=npt {
1333 self.pq[k] += temp * self.zmat[self.iz(k, jj)];
1334 }
1335 }
1336
1337 self.fval[knew] = f;
1338 for i in 1..=n {
1339 let x = self.ix(knew, i);
1340 self.xpt[x] = self.xnew[i];
1341 self.w[i] = self.bmat[self.ib(knew, i)];
1342 }
1343 for k in 1..=npt {
1344 let mut suma = 0.0;
1345 for jj in 1..=nptm {
1346 suma += self.zmat[self.iz(knew, jj)] * self.zmat[self.iz(k, jj)];
1347 }
1348 if suma.is_infinite() {
1349 self.rc = BobyqaStatus::RoundoffLimited;
1350 return Label::L720;
1351 }
1352 let mut sumb = 0.0;
1353 for j in 1..=n {
1354 sumb += self.xpt[self.ix(k, j)] * self.xopt[j];
1355 }
1356 let temp = suma * sumb;
1357 for i in 1..=n {
1358 self.w[i] += temp * self.xpt[self.ix(k, i)];
1359 }
1360 }
1361 for i in 1..=n {
1362 self.gopt[i] += diff * self.w[i];
1363 }
1364
1365 if f < fopt {
1366 self.kopt = knew;
1367 self.xoptsq = 0.0;
1368 let mut ih = 0;
1369 for j in 1..=n {
1370 self.xopt[j] = self.xnew[j];
1371 self.xoptsq += self.xopt[j] * self.xopt[j];
1372 for i in 1..=j {
1373 ih += 1;
1374 if i < j {
1375 self.gopt[j] += self.hq[ih] * self.d[i];
1376 }
1377 self.gopt[i] += self.hq[ih] * self.d[j];
1378 }
1379 }
1380 for k in 1..=npt {
1381 let mut temp = 0.0;
1382 for j in 1..=n {
1383 temp += self.xpt[self.ix(k, j)] * self.d[j];
1384 }
1385 temp *= self.pq[k];
1386 for i in 1..=n {
1387 self.gopt[i] += temp * self.xpt[self.ix(k, i)];
1388 }
1389 }
1390 }
1392
1393 if self.ntrits > 0 {
1394 for k in 1..=npt {
1395 self.vlag[k] = self.fval[k] - self.fval[self.kopt];
1396 self.w[k] = 0.0;
1397 }
1398 for j in 1..=nptm {
1399 let mut sum = 0.0;
1400 for k in 1..=npt {
1401 sum += self.zmat[self.iz(k, j)] * self.vlag[k];
1402 }
1403 for k in 1..=npt {
1404 self.w[k] += sum * self.zmat[self.iz(k, j)];
1405 }
1406 }
1407 for k in 1..=npt {
1408 let mut sum = 0.0;
1409 for j in 1..=n {
1410 sum += self.xpt[self.ix(k, j)] * self.xopt[j];
1411 }
1412 self.w[k + npt] = self.w[k];
1413 self.w[k] *= sum;
1414 }
1415 let mut gqsq = 0.0;
1416 let mut gisq = 0.0;
1417 for i in 1..=n {
1418 let mut sum = 0.0;
1419 for k in 1..=npt {
1420 sum = sum
1421 + self.bmat[self.ib(k, i)] * self.vlag[k]
1422 + self.xpt[self.ix(k, i)] * self.w[k];
1423 }
1424 if self.xopt[i] == self.sl[i] {
1425 let a = 0.0f64.min(self.gopt[i]);
1426 gqsq += a * a;
1427 let b = 0.0f64.min(sum);
1428 gisq += b * b;
1429 } else if self.xopt[i] == self.su[i] {
1430 let a = 0.0f64.max(self.gopt[i]);
1431 gqsq += a * a;
1432 let b = 0.0f64.max(sum);
1433 gisq += b * b;
1434 } else {
1435 gqsq += self.gopt[i] * self.gopt[i];
1436 gisq += sum * sum;
1437 }
1438 self.vlag[npt + i] = sum;
1439 }
1440
1441 self.itest += 1;
1442 if gqsq < 10.0 * gisq {
1443 self.itest = 0;
1444 }
1445 if self.itest >= 3 {
1446 for i in 1..=npt.max(nh) {
1447 if i <= n {
1448 self.gopt[i] = self.vlag[npt + i];
1449 }
1450 if i <= npt {
1451 self.pq[i] = self.w[npt + i];
1452 }
1453 if i <= nh {
1454 self.hq[i] = 0.0;
1455 }
1456 self.itest = 0;
1457 }
1458 }
1459 }
1460
1461 if self.ntrits == 0 {
1462 return Label::L60;
1463 }
1464 if f <= fopt + 0.1 * vquad {
1465 return Label::L60;
1466 }
1467 let a = 2.0 * self.delta;
1468 let b = 10.0 * self.rho;
1469 self.distsq = (a * a).max(b * b);
1470 Label::L650
1471 }
1472
1473 fn l650(&mut self) -> Label {
1476 let n = self.n;
1477 let npt = self.npt;
1478 self.knew = 0;
1479 for k in 1..=npt {
1480 let mut sum = 0.0;
1481 for j in 1..=n {
1482 let v = self.xpt[self.ix(k, j)] - self.xopt[j];
1483 sum += v * v;
1484 }
1485 if sum > self.distsq {
1486 self.knew = k;
1487 self.distsq = sum;
1488 }
1489 }
1490
1491 if self.knew > 0 {
1492 let dist = self.distsq.sqrt();
1493 if self.ntrits == -1 {
1494 self.delta = (0.1 * self.delta).min(0.5 * dist);
1495 if self.delta <= self.rho * 1.5 {
1496 self.delta = self.rho;
1497 }
1498 }
1499 self.ntrits = 0;
1500 self.adelt = (0.1 * dist).min(self.delta).max(self.rho);
1501 self.dsq = self.adelt * self.adelt;
1502 return Label::L90;
1503 }
1504 if self.ntrits == -1 {
1505 return Label::L680;
1506 }
1507 if self.ratio > 0.0 {
1508 return Label::L60;
1509 }
1510 if self.delta.max(self.dnorm) > self.rho {
1511 return Label::L60;
1512 }
1513 Label::L680
1514 }
1515
1516 fn l680(&mut self) -> Label {
1518 if self.rho > self.rhoend {
1519 self.delta = 0.5 * self.rho;
1520 self.ratio = self.rho / self.rhoend;
1521 if self.ratio <= 16.0 {
1522 self.rho = self.rhoend;
1523 } else if self.ratio <= 250.0 {
1524 self.rho = self.ratio.sqrt() * self.rhoend;
1525 } else {
1526 self.rho *= 0.1;
1527 }
1528 self.delta = self.delta.max(self.rho);
1529 self.ntrits = 0;
1530 self.nfsav = self.nevals;
1531 return Label::L60;
1532 }
1533 if self.ntrits == -1 {
1534 return Label::L360;
1535 }
1536 Label::L720
1537 }
1538
1539 fn l720(&mut self) {
1541 for i in 1..=self.n {
1542 self.x[i] = self.xl[i].max(self.xbase[i] + self.xopt[i]).min(self.xu[i]);
1543 if self.xopt[i] == self.sl[i] {
1544 self.x[i] = self.xl[i];
1545 }
1546 if self.xopt[i] == self.su[i] {
1547 self.x[i] = self.xu[i];
1548 }
1549 }
1550 self.minf = self.fval[self.kopt];
1551 self.xbest.copy_from_slice(&self.x);
1552 if self.rc == BobyqaStatus::Running {
1553 self.rc = BobyqaStatus::Success;
1554 }
1555 }
1556
1557 #[inline]
1563 fn pa(&self, r: usize, j: usize) -> usize {
1564 1 + (r - 1) + 2 * (j - 1)
1565 }
1566
1567 #[inline]
1569 fn pid(&self, k: usize) -> usize {
1570 2 * self.n + k
1571 }
1572
1573 #[inline]
1575 fn rw(&self, i: usize) -> usize {
1576 self.ndim + self.n + i
1577 }
1578
1579 fn pid_parts(&self, k: usize) -> (usize, usize) {
1589 let np = (self.n + 1) as f64;
1590 let v = self.w[self.pid(k)];
1591 let ip = v as i64;
1592 let iq = (np * v - (ip as f64) * np) as i64;
1593 (ip.max(0) as usize, iq.max(0) as usize)
1594 }
1595
1596 fn rescue_start(&mut self) -> Label {
1598 let n = self.n;
1599 let npt = self.npt;
1600 let ndim = self.ndim;
1601 let np = n + 1;
1602 let nptm = npt - np;
1603 let sfrac = 0.5 / np as f64;
1604
1605 let mut sumpq = 0.0;
1606 let mut winc = 0.0f64;
1607 for k in 1..=npt {
1608 let mut distsq = 0.0;
1609 for j in 1..=n {
1610 let x = self.ix(k, j);
1611 self.xpt[x] -= self.xopt[j];
1612 distsq += self.xpt[x] * self.xpt[x];
1613 }
1614 sumpq += self.pq[k];
1615 let r = self.rw(ndim + k);
1616 self.w[r] = distsq;
1617 winc = winc.max(distsq);
1618 for j in 1..=nptm {
1619 let z = self.iz(k, j);
1620 self.zmat[z] = 0.0;
1621 }
1622 }
1623
1624 let mut ih = 0;
1625 for j in 1..=n {
1626 let rj = self.rw(j);
1627 self.w[rj] = 0.5 * sumpq * self.xopt[j];
1628 for k in 1..=npt {
1629 self.w[rj] += self.pq[k] * self.xpt[self.ix(k, j)];
1630 }
1631 for i in 1..=j {
1632 ih += 1;
1633 self.hq[ih] =
1634 self.hq[ih] + self.w[self.rw(i)] * self.xopt[j] + self.w[rj] * self.xopt[i];
1635 }
1636 }
1637
1638 for j in 1..=n {
1639 self.xbase[j] += self.xopt[j];
1640 self.sl[j] -= self.xopt[j];
1641 self.su[j] -= self.xopt[j];
1642 self.xopt[j] = 0.0;
1643 let (p1, p2) = (self.pa(1, j), self.pa(2, j));
1644 self.w[p1] = self.delta.min(self.su[j]);
1645 self.w[p2] = (-self.delta).max(self.sl[j]);
1646 if self.w[p1] + self.w[p2] < 0.0 {
1647 self.w.swap(p1, p2);
1648 }
1649 if self.w[p2].abs() < 0.5 * self.w[p1].abs() {
1650 self.w[p2] = 0.5 * self.w[p1];
1651 }
1652 for i in 1..=ndim {
1653 let b = self.ib(i, j);
1654 self.bmat[b] = 0.0;
1655 }
1656 }
1657 self.fbase = self.fval[self.kopt];
1658
1659 let p1 = self.pid(1);
1660 self.w[p1] = sfrac;
1661 for j in 1..=n {
1662 let jp = j + 1;
1663 let jpn = jp + n;
1664 let pj = self.pid(jp);
1665 self.w[pj] = j as f64 + sfrac;
1666 let a1 = self.w[self.pa(1, j)];
1667 let a2 = self.w[self.pa(2, j)];
1668 if jpn <= npt {
1669 let pj = self.pid(jpn);
1670 self.w[pj] = j as f64 / np as f64 + sfrac;
1671 let temp = 1.0 / (a1 - a2);
1672 let (bjp, bjpn, b1) = (self.ib(jp, j), self.ib(jpn, j), self.ib(1, j));
1673 self.bmat[bjp] = -temp + 1.0 / a1;
1674 self.bmat[bjpn] = temp + 1.0 / a2;
1675 self.bmat[b1] = -self.bmat[bjp] - self.bmat[bjpn];
1676 let (z1, zjp, zjpn) = (self.iz(1, j), self.iz(jp, j), self.iz(jpn, j));
1677 self.zmat[z1] = 2.0f64.sqrt() / (a1 * a2).abs();
1678 self.zmat[zjp] = self.zmat[z1] * a2 * temp;
1679 self.zmat[zjpn] = -self.zmat[z1] * a1 * temp;
1680 } else {
1681 let (b1, bjp, bjn) = (self.ib(1, j), self.ib(jp, j), self.ib(j + npt, j));
1682 self.bmat[b1] = -1.0 / a1;
1683 self.bmat[bjp] = 1.0 / a1;
1684 self.bmat[bjn] = -0.5 * (a1 * a1);
1685 }
1686 }
1687
1688 if npt >= n + np {
1689 for k in (2 * np)..=npt {
1690 let iw = (((k - np) as f64 - 0.5) / n as f64) as usize;
1691 let ip = k - np - iw * n;
1692 let mut iq = ip + iw;
1693 if iq > n {
1694 iq -= n;
1695 }
1696 let pk = self.pid(k);
1697 self.w[pk] = ip as f64 + iq as f64 / np as f64 + sfrac;
1698 let temp = 1.0 / (self.w[self.pa(1, ip)] * self.w[self.pa(1, iq)]);
1699 let idx = [
1700 self.iz(1, k - np),
1701 self.iz(ip + 1, k - np),
1702 self.iz(iq + 1, k - np),
1703 self.iz(k, k - np),
1704 ];
1705 self.zmat[idx[0]] = temp;
1706 self.zmat[idx[1]] = -temp;
1707 self.zmat[idx[2]] = -temp;
1708 self.zmat[idx[3]] = temp;
1709 }
1710 }
1711 let mut nrem = npt;
1712 let mut kold = 1;
1713 let mut knew = self.kopt;
1714 let mut beta = 0.0;
1715 let mut denom = 0.0;
1716
1717 'l80: loop {
1719 for j in 1..=n {
1720 let (a, b) = (self.ib(kold, j), self.ib(knew, j));
1721 self.bmat.swap(a, b);
1722 }
1723 for j in 1..=nptm {
1724 let (a, b) = (self.iz(kold, j), self.iz(knew, j));
1725 self.zmat.swap(a, b);
1726 }
1727 let (pk, pn) = (self.pid(kold), self.pid(knew));
1728 self.w[pk] = self.w[pn];
1729 self.w[pn] = 0.0;
1730 let rn = self.rw(ndim + knew);
1731 self.w[rn] = 0.0;
1732 nrem -= 1;
1733 if knew != self.kopt {
1734 self.vlag.swap(kold, knew);
1735 let rbase = self.rw(1);
1736 update(
1737 n,
1738 npt,
1739 &mut self.bmat,
1740 &mut self.zmat,
1741 ndim,
1742 &mut self.vlag[1..],
1743 beta,
1744 denom,
1745 knew,
1746 &mut self.w[rbase..],
1747 );
1748 if nrem == 0 {
1749 return Label::L190Post;
1750 }
1751 for k in 1..=npt {
1752 let r = self.rw(ndim + k);
1753 self.w[r] = self.w[r].abs();
1754 }
1755 }
1756
1757 'l120: loop {
1758 let mut dsqmin = 0.0;
1759 for k in 1..=npt {
1760 let v = self.w[self.rw(ndim + k)];
1761 if v > 0.0 && (dsqmin == 0.0 || v < dsqmin) {
1762 knew = k;
1763 dsqmin = v;
1764 }
1765 }
1766 if dsqmin == 0.0 {
1767 break 'l80;
1768 }
1769
1770 for j in 1..=n {
1771 let r = self.rw(npt + j);
1772 self.w[r] = self.xpt[self.ix(knew, j)];
1773 }
1774 for k in 1..=npt {
1775 let mut sum = 0.0;
1776 if k == self.kopt {
1777 } else if self.w[self.pid(k)] == 0.0 {
1778 for j in 1..=n {
1779 sum += self.w[self.rw(npt + j)] * self.xpt[self.ix(k, j)];
1780 }
1781 } else {
1782 let (ip, iq) = self.pid_parts(k);
1783 if ip > 0 {
1784 sum = self.w[self.rw(npt + ip)] * self.w[self.pa(1, ip)];
1785 }
1786 if iq > 0 {
1787 let iw = if ip == 0 { 2 } else { 1 };
1788 sum += self.w[self.rw(npt + iq)] * self.w[self.pa(iw, iq)];
1789 }
1790 }
1791 let r = self.rw(k);
1792 self.w[r] = 0.5 * sum * sum;
1793 }
1794
1795 for k in 1..=npt {
1796 let mut sum = 0.0;
1797 for j in 1..=n {
1798 sum += self.bmat[self.ib(k, j)] * self.w[self.rw(npt + j)];
1799 }
1800 self.vlag[k] = sum;
1801 }
1802 beta = 0.0;
1803 for j in 1..=nptm {
1804 let mut sum = 0.0;
1805 for k in 1..=npt {
1806 sum += self.zmat[self.iz(k, j)] * self.w[self.rw(k)];
1807 }
1808 beta -= sum * sum;
1809 for k in 1..=npt {
1810 self.vlag[k] += sum * self.zmat[self.iz(k, j)];
1811 }
1812 }
1813 let mut bsum = 0.0;
1814 let mut distsq = 0.0;
1815 for j in 1..=n {
1816 let mut sum = 0.0;
1817 for k in 1..=npt {
1818 sum += self.bmat[self.ib(k, j)] * self.w[self.rw(k)];
1819 }
1820 let jp = j + npt;
1821 bsum += sum * self.w[self.rw(jp)];
1822 for ip in (npt + 1)..=ndim {
1823 sum += self.bmat[self.ib(ip, j)] * self.w[self.rw(ip)];
1824 }
1825 bsum += sum * self.w[self.rw(jp)];
1826 self.vlag[jp] = sum;
1827 let v = self.xpt[self.ix(knew, j)];
1828 distsq += v * v;
1829 }
1830 beta = 0.5 * distsq * distsq + beta - bsum;
1831 self.vlag[self.kopt] += 1.0;
1832
1833 denom = 0.0;
1834 let mut vlmxsq = 0.0f64;
1835 for k in 1..=npt {
1836 if self.w[self.pid(k)] != 0.0 {
1837 let mut hdiag = 0.0;
1838 for j in 1..=nptm {
1839 let z = self.zmat[self.iz(k, j)];
1840 hdiag += z * z;
1841 }
1842 let den = beta * hdiag + self.vlag[k] * self.vlag[k];
1843 if den > denom {
1844 kold = k;
1845 denom = den;
1846 }
1847 }
1848 vlmxsq = vlmxsq.max(self.vlag[k] * self.vlag[k]);
1849 }
1850 if denom <= vlmxsq * 0.01 {
1851 let r = self.rw(ndim + knew);
1852 self.w[r] = -self.w[r] - winc;
1853 continue 'l120;
1854 }
1855 continue 'l80;
1856 }
1857 }
1858
1859 self.kpt = 0;
1860 Label::RescueNext
1861 }
1862
1863 fn rescue_next(&mut self) -> Label {
1865 let n = self.n;
1866 let npt = self.npt;
1867 loop {
1868 self.kpt += 1;
1869 if self.kpt > npt {
1870 return Label::L190Post;
1871 }
1872 let kpt = self.kpt;
1873 if self.w[self.pid(kpt)] == 0.0 {
1874 continue;
1875 }
1876 if self.stop_evals() {
1877 self.rc = BobyqaStatus::MaxevalReached;
1878 return Label::L190Post;
1879 }
1880
1881 let mut ih = 0;
1882 for j in 1..=n {
1883 let rj = self.rw(j);
1884 let x = self.ix(kpt, j);
1885 self.w[rj] = self.xpt[x];
1886 self.xpt[x] = 0.0;
1887 let temp = self.pq[kpt] * self.w[rj];
1888 for i in 1..=j {
1889 ih += 1;
1890 self.hq[ih] += temp * self.w[self.rw(i)];
1891 }
1892 }
1893 self.pq[kpt] = 0.0;
1894 let (ip, iq) = self.pid_parts(kpt);
1895 self.rip = ip;
1896 self.riq = iq;
1897 if ip > 0 {
1898 self.xp = self.w[self.pa(1, ip)];
1899 let x = self.ix(kpt, ip);
1900 self.xpt[x] = self.xp;
1901 }
1902 if iq > 0 {
1903 self.xq = self.w[self.pa(1, iq)];
1904 if ip == 0 {
1905 self.xq = self.w[self.pa(2, iq)];
1906 }
1907 let x = self.ix(kpt, iq);
1908 self.xpt[x] = self.xq;
1909 }
1910
1911 let mut vquad = self.fbase;
1912 let mut ihp = 0;
1913 if ip > 0 {
1914 ihp = (ip + ip * ip) / 2;
1915 vquad += self.xp * (self.gopt[ip] + 0.5 * self.xp * self.hq[ihp]);
1916 }
1917 if iq > 0 {
1918 let ihq = (iq + iq * iq) / 2;
1919 vquad += self.xq * (self.gopt[iq] + 0.5 * self.xq * self.hq[ihq]);
1920 if ip > 0 {
1921 let iw = ihp.max(ihq) - ip.abs_diff(iq);
1922 vquad += self.xp * self.xq * self.hq[iw];
1923 }
1924 }
1925 for k in 1..=npt {
1926 let mut temp = 0.0;
1927 if ip > 0 {
1928 temp += self.xp * self.xpt[self.ix(k, ip)];
1929 }
1930 if iq > 0 {
1931 temp += self.xq * self.xpt[self.ix(k, iq)];
1932 }
1933 vquad += 0.5 * self.pq[k] * temp * temp;
1934 }
1935 self.rvquad = vquad;
1936
1937 for i in 1..=n {
1938 let p = self.xpt[self.ix(kpt, i)];
1939 self.x[i] = self.xl[i].max(self.xbase[i] + p).min(self.xu[i]);
1940 if p == self.sl[i] {
1941 self.x[i] = self.xl[i];
1942 }
1943 if p == self.su[i] {
1944 self.x[i] = self.xu[i];
1945 }
1946 }
1947 self.request(Label::RescuePost);
1948 return Label::Wait;
1949 }
1950 }
1951
1952 fn rescue_post(&mut self) -> Label {
1954 let n = self.n;
1955 let npt = self.npt;
1956 let nptm = npt - n - 1;
1957 let kpt = self.kpt;
1958 let f = self.f;
1959 self.fval[kpt] = f;
1960 if f < self.fval[self.kopt] {
1961 self.kopt = kpt;
1962 }
1963 if self.stop_evals() {
1964 self.rc = BobyqaStatus::MaxevalReached;
1965 return Label::L190Post;
1966 }
1967
1968 let diff = f - self.rvquad;
1969 for i in 1..=n {
1970 self.gopt[i] += diff * self.bmat[self.ib(kpt, i)];
1971 }
1972 for k in 1..=npt {
1973 let mut sum = 0.0;
1974 for j in 1..=nptm {
1975 sum += self.zmat[self.iz(k, j)] * self.zmat[self.iz(kpt, j)];
1976 }
1977 let temp = diff * sum;
1978 if self.w[self.pid(k)] == 0.0 {
1979 self.pq[k] += temp;
1980 } else {
1981 let (ip, iq) = self.pid_parts(k);
1982 let ihq = (iq * iq + iq) / 2;
1983 if ip == 0 {
1984 let a = self.w[self.pa(2, iq)];
1985 self.hq[ihq] += temp * (a * a);
1986 } else {
1987 let ihp = (ip * ip + ip) / 2;
1988 let a = self.w[self.pa(1, ip)];
1989 self.hq[ihp] += temp * (a * a);
1990 if iq > 0 {
1991 let b = self.w[self.pa(1, iq)];
1992 self.hq[ihq] += temp * (b * b);
1993 let iw = ihp.max(ihq) - iq.abs_diff(ip);
1994 self.hq[iw] += temp * a * b;
1995 }
1996 }
1997 }
1998 }
1999 let p = self.pid(kpt);
2000 self.w[p] = 0.0;
2001 Label::RescueNext
2002 }
2003}
2004
2005fn trsbox(
2024 n: usize,
2025 npt: usize,
2026 xpt: &[f64],
2027 xopt: &[f64],
2028 gopt: &[f64],
2029 hq: &[f64],
2030 pq: &[f64],
2031 sl: &[f64],
2032 su: &[f64],
2033 delta: f64,
2034 xnew: &mut [f64],
2035 d: &mut [f64],
2036 gnew: &mut [f64],
2037 xbdi: &mut [f64],
2038 s: &mut [f64],
2039 hs: &mut [f64],
2040 hred: &mut [f64],
2041 dsq: &mut f64,
2042 crvmin: &mut f64,
2043) {
2044 #[derive(Clone, Copy)]
2045 enum At {
2046 L20,
2047 L30,
2048 L50,
2049 L90,
2050 L100,
2051 L120,
2052 L150,
2053 L190,
2054 L210,
2055 }
2056 let ix = |k: usize, j: usize| (k - 1) + (j - 1) * npt;
2057
2058 let mut iterc: i64 = 0;
2059 let mut nact: usize = 0;
2060 for i in 1..=n {
2061 xbdi[i - 1] = 0.0;
2062 if xopt[i - 1] <= sl[i - 1] {
2063 if gopt[i - 1] >= 0.0 {
2064 xbdi[i - 1] = -1.0;
2065 }
2066 } else if xopt[i - 1] >= su[i - 1] && gopt[i - 1] <= 0.0 {
2067 xbdi[i - 1] = 1.0;
2068 }
2069 if xbdi[i - 1] != 0.0 {
2070 nact += 1;
2071 }
2072 d[i - 1] = 0.0;
2073 gnew[i - 1] = gopt[i - 1];
2074 }
2075 let mut delsq = delta * delta;
2076 let mut qred = 0.0;
2077 *crvmin = -1.0;
2078
2079 let mut beta = 0.0;
2080 let mut stepsq = 0.0;
2081 let mut gredsq = 0.0;
2082 let mut itermax: i64 = 0;
2083 let mut ggsav = 0.0;
2084 let mut iact: usize = 0;
2085 let mut dredsq = 0.0;
2086 let mut dredg = 0.0;
2087 let mut sredg = 0.0;
2088 let mut itcsav: i64 = 0;
2089 let mut angbd = 0.0;
2090 let mut xsav = 0.0;
2091 let mut shs;
2092 let mut sdec;
2093
2094 let mut at = At::L20;
2095 loop {
2096 match at {
2097 At::L20 => {
2098 beta = 0.0;
2099 at = At::L30;
2100 }
2101 At::L30 => {
2102 stepsq = 0.0;
2103 for i in 1..=n {
2104 if xbdi[i - 1] != 0.0 {
2105 s[i - 1] = 0.0;
2106 } else if beta == 0.0 {
2107 s[i - 1] = -gnew[i - 1];
2108 } else {
2109 s[i - 1] = beta * s[i - 1] - gnew[i - 1];
2110 }
2111 stepsq += s[i - 1] * s[i - 1];
2112 }
2113 if stepsq == 0.0 {
2114 at = At::L190;
2115 continue;
2116 }
2117 if beta == 0.0 {
2118 gredsq = stepsq;
2119 itermax = iterc + n as i64 - nact as i64;
2120 }
2121 if gredsq * delsq <= qred * 1e-4 * qred {
2122 at = At::L190;
2123 continue;
2124 }
2125 at = At::L210;
2126 }
2127 At::L50 => {
2128 let mut resid = delsq;
2129 let mut ds = 0.0;
2130 shs = 0.0;
2131 for i in 1..=n {
2132 if xbdi[i - 1] == 0.0 {
2133 resid -= d[i - 1] * d[i - 1];
2134 ds += s[i - 1] * d[i - 1];
2135 shs += s[i - 1] * hs[i - 1];
2136 }
2137 }
2138 if resid <= 0.0 {
2139 at = At::L90;
2140 continue;
2141 }
2142 let temp = (stepsq * resid + ds * ds).sqrt();
2143 let blen = if ds < 0.0 {
2144 (temp - ds) / stepsq
2145 } else {
2146 resid / (temp + ds)
2147 };
2148 let mut stplen = blen;
2149 if shs > 0.0 {
2150 stplen = blen.min(gredsq / shs);
2151 }
2152
2153 iact = 0;
2154 for i in 1..=n {
2155 if s[i - 1] != 0.0 {
2156 let xsum = xopt[i - 1] + d[i - 1];
2157 let temp = if s[i - 1] > 0.0 {
2158 (su[i - 1] - xsum) / s[i - 1]
2159 } else {
2160 (sl[i - 1] - xsum) / s[i - 1]
2161 };
2162 if temp < stplen {
2163 stplen = temp;
2164 iact = i;
2165 }
2166 }
2167 }
2168
2169 sdec = 0.0;
2170 if stplen > 0.0 {
2171 iterc += 1;
2172 let temp = shs / stepsq;
2173 if iact == 0 && temp > 0.0 {
2174 *crvmin = crvmin.min(temp);
2175 if *crvmin == -1.0 {
2176 *crvmin = temp;
2177 }
2178 }
2179 ggsav = gredsq;
2180 gredsq = 0.0;
2181 for i in 1..=n {
2182 gnew[i - 1] += stplen * hs[i - 1];
2183 if xbdi[i - 1] == 0.0 {
2184 gredsq += gnew[i - 1] * gnew[i - 1];
2185 }
2186 d[i - 1] += stplen * s[i - 1];
2187 }
2188 sdec = (stplen * (ggsav - 0.5 * stplen * shs)).max(0.0);
2189 qred += sdec;
2190 }
2191
2192 if iact > 0 {
2193 nact += 1;
2194 xbdi[iact - 1] = 1.0;
2195 if s[iact - 1] < 0.0 {
2196 xbdi[iact - 1] = -1.0;
2197 }
2198 delsq -= d[iact - 1] * d[iact - 1];
2199 if delsq <= 0.0 {
2200 at = At::L90;
2201 continue;
2202 }
2203 at = At::L20;
2204 continue;
2205 }
2206
2207 if stplen < blen {
2208 if iterc == itermax {
2209 at = At::L190;
2210 continue;
2211 }
2212 if sdec <= qred * 0.01 {
2213 at = At::L190;
2214 continue;
2215 }
2216 beta = gredsq / ggsav;
2217 at = At::L30;
2218 continue;
2219 }
2220 at = At::L90;
2221 }
2222 At::L90 => {
2223 *crvmin = 0.0;
2224 at = At::L100;
2225 }
2226 At::L100 => {
2227 if nact + 1 >= n {
2228 at = At::L190;
2229 continue;
2230 }
2231 dredsq = 0.0;
2232 dredg = 0.0;
2233 gredsq = 0.0;
2234 for i in 1..=n {
2235 if xbdi[i - 1] == 0.0 {
2236 dredsq += d[i - 1] * d[i - 1];
2237 dredg += d[i - 1] * gnew[i - 1];
2238 gredsq += gnew[i - 1] * gnew[i - 1];
2239 s[i - 1] = d[i - 1];
2240 } else {
2241 s[i - 1] = 0.0;
2242 }
2243 }
2244 itcsav = iterc;
2245 at = At::L210;
2246 }
2247 At::L120 => {
2248 iterc += 1;
2249 let temp = gredsq * dredsq - dredg * dredg;
2250 if temp <= qred * 1e-4 * qred {
2251 at = At::L190;
2252 continue;
2253 }
2254 let temp = temp.sqrt();
2255 for i in 1..=n {
2256 if xbdi[i - 1] == 0.0 {
2257 s[i - 1] = (dredg * d[i - 1] - dredsq * gnew[i - 1]) / temp;
2258 } else {
2259 s[i - 1] = 0.0;
2260 }
2261 }
2262 sredg = -temp;
2263
2264 angbd = 1.0;
2265 iact = 0;
2266 let mut back_to_100 = false;
2267 for i in 1..=n {
2268 if xbdi[i - 1] == 0.0 {
2269 let tempa = xopt[i - 1] + d[i - 1] - sl[i - 1];
2270 let tempb = su[i - 1] - xopt[i - 1] - d[i - 1];
2271 if tempa <= 0.0 {
2272 nact += 1;
2273 xbdi[i - 1] = -1.0;
2274 back_to_100 = true;
2275 break;
2276 } else if tempb <= 0.0 {
2277 nact += 1;
2278 xbdi[i - 1] = 1.0;
2279 back_to_100 = true;
2280 break;
2281 }
2282 let ssq = d[i - 1] * d[i - 1] + s[i - 1] * s[i - 1];
2283 let a = xopt[i - 1] - sl[i - 1];
2284 let temp = ssq - a * a;
2285 if temp > 0.0 {
2286 let temp = temp.sqrt() - s[i - 1];
2287 if angbd * temp > tempa {
2288 angbd = tempa / temp;
2289 iact = i;
2290 xsav = -1.0;
2291 }
2292 }
2293 let b = su[i - 1] - xopt[i - 1];
2294 let temp = ssq - b * b;
2295 if temp > 0.0 {
2296 let temp = temp.sqrt() + s[i - 1];
2297 if angbd * temp > tempb {
2298 angbd = tempb / temp;
2299 iact = i;
2300 xsav = 1.0;
2301 }
2302 }
2303 }
2304 }
2305 if back_to_100 {
2306 at = At::L100;
2307 continue;
2308 }
2309 at = At::L210;
2310 }
2311 At::L150 => {
2312 shs = 0.0;
2313 let mut dhs = 0.0;
2314 let mut dhd = 0.0;
2315 for i in 1..=n {
2316 if xbdi[i - 1] == 0.0 {
2317 shs += s[i - 1] * hs[i - 1];
2318 dhs += d[i - 1] * hs[i - 1];
2319 dhd += d[i - 1] * hred[i - 1];
2320 }
2321 }
2322
2323 let mut redmax = 0.0;
2324 let mut isav: i64 = 0;
2325 let mut redsav = 0.0;
2326 let mut rdprev = 0.0;
2327 let mut rdnext = 0.0;
2328 let iu = (angbd * 17.0 + 3.1) as i64;
2329 for i in 1..=iu {
2330 let angt = angbd * i as f64 / iu as f64;
2331 let sth = (angt + angt) / (1.0 + angt * angt);
2332 let temp = shs + angt * (angt * dhd - dhs - dhs);
2333 let rednew = sth * (angt * dredg - sredg - 0.5 * sth * temp);
2334 if rednew > redmax {
2335 redmax = rednew;
2336 isav = i;
2337 rdprev = redsav;
2338 } else if i == isav + 1 {
2339 rdnext = rednew;
2340 }
2341 redsav = rednew;
2342 }
2343
2344 if isav == 0 {
2345 at = At::L190;
2346 continue;
2347 }
2348 let mut angt = 0.0;
2349 if isav < iu {
2350 let temp = (rdnext - rdprev) / (redmax + redmax - rdprev - rdnext);
2351 angt = angbd * (isav as f64 + 0.5 * temp) / iu as f64;
2352 }
2353 let cth = (1.0 - angt * angt) / (1.0 + angt * angt);
2354 let sth = (angt + angt) / (1.0 + angt * angt);
2355 let temp = shs + angt * (angt * dhd - dhs - dhs);
2356 sdec = sth * (angt * dredg - sredg - 0.5 * sth * temp);
2357 if sdec <= 0.0 {
2358 at = At::L190;
2359 continue;
2360 }
2361
2362 dredg = 0.0;
2363 gredsq = 0.0;
2364 for i in 1..=n {
2365 gnew[i - 1] = gnew[i - 1] + (cth - 1.0) * hred[i - 1] + sth * hs[i - 1];
2366 if xbdi[i - 1] == 0.0 {
2367 d[i - 1] = cth * d[i - 1] + sth * s[i - 1];
2368 dredg += d[i - 1] * gnew[i - 1];
2369 gredsq += gnew[i - 1] * gnew[i - 1];
2370 }
2371 hred[i - 1] = cth * hred[i - 1] + sth * hs[i - 1];
2372 }
2373 qred += sdec;
2374 if iact > 0 && isav == iu {
2375 nact += 1;
2376 xbdi[iact - 1] = xsav;
2377 at = At::L100;
2378 continue;
2379 }
2380
2381 if sdec > qred * 0.01 {
2382 at = At::L120;
2383 continue;
2384 }
2385 at = At::L190;
2386 }
2387 At::L190 => {
2388 *dsq = 0.0;
2389 for i in 1..=n {
2390 xnew[i - 1] = (xopt[i - 1] + d[i - 1]).min(su[i - 1]).max(sl[i - 1]);
2391 if xbdi[i - 1] == -1.0 {
2392 xnew[i - 1] = sl[i - 1];
2393 }
2394 if xbdi[i - 1] == 1.0 {
2395 xnew[i - 1] = su[i - 1];
2396 }
2397 d[i - 1] = xnew[i - 1] - xopt[i - 1];
2398 *dsq += d[i - 1] * d[i - 1];
2399 }
2400 return;
2401 }
2402 At::L210 => {
2403 let mut ih = 0;
2404 for j in 1..=n {
2405 hs[j - 1] = 0.0;
2406 for i in 1..=j {
2407 ih += 1;
2408 if i < j {
2409 hs[j - 1] += hq[ih - 1] * s[i - 1];
2410 }
2411 hs[i - 1] += hq[ih - 1] * s[j - 1];
2412 }
2413 }
2414 for k in 1..=npt {
2415 if pq[k - 1] != 0.0 {
2416 let mut temp = 0.0;
2417 for j in 1..=n {
2418 temp += xpt[ix(k, j)] * s[j - 1];
2419 }
2420 temp *= pq[k - 1];
2421 for i in 1..=n {
2422 hs[i - 1] += temp * xpt[ix(k, i)];
2423 }
2424 }
2425 }
2426 if *crvmin != 0.0 {
2427 at = At::L50;
2428 continue;
2429 }
2430 if iterc > itcsav {
2431 at = At::L150;
2432 continue;
2433 }
2434 hred[..n].copy_from_slice(&hs[..n]);
2435 at = At::L120;
2436 }
2437 }
2438 }
2439}
2440
2441fn altmov(
2455 n: usize,
2456 npt: usize,
2457 xpt: &[f64],
2458 xopt: &[f64],
2459 bmat: &[f64],
2460 zmat: &[f64],
2461 ndim: usize,
2462 sl: &[f64],
2463 su: &[f64],
2464 kopt: usize,
2465 knew: usize,
2466 adelt: f64,
2467 xnew: &mut [f64],
2468 xalt: &mut [f64],
2469 alpha: &mut f64,
2470 cauchy: &mut f64,
2471 glag: &mut [f64],
2472 hcol: &mut [f64],
2473 w: &mut [f64],
2474) {
2475 let ix = |k: usize, j: usize| (k - 1) + (j - 1) * npt;
2476 let ib = |k: usize, j: usize| (k - 1) + (j - 1) * ndim;
2477 let iz = |k: usize, j: usize| (k - 1) + (j - 1) * npt;
2478 let const_ = 1.0 + 2.0f64.sqrt();
2479
2480 for k in 1..=npt {
2481 hcol[k - 1] = 0.0;
2482 }
2483 for j in 1..=(npt - n - 1) {
2484 let temp = zmat[iz(knew, j)];
2485 for k in 1..=npt {
2486 hcol[k - 1] += temp * zmat[iz(k, j)];
2487 }
2488 }
2489 *alpha = hcol[knew - 1];
2490 let ha = 0.5 * *alpha;
2491
2492 for i in 1..=n {
2493 glag[i - 1] = bmat[ib(knew, i)];
2494 }
2495 for k in 1..=npt {
2496 let mut temp = 0.0;
2497 for j in 1..=n {
2498 temp += xpt[ix(k, j)] * xopt[j - 1];
2499 }
2500 temp *= hcol[k - 1];
2501 for i in 1..=n {
2502 glag[i - 1] += temp * xpt[ix(k, i)];
2503 }
2504 }
2505
2506 let mut presav = 0.0;
2507 let mut ksav = 0;
2508 let mut stpsav = 0.0;
2509 let mut ibdsav: i64 = 0;
2510 for k in 1..=npt {
2511 if k == kopt {
2512 continue;
2513 }
2514 let mut dderiv = 0.0;
2515 let mut distsq = 0.0;
2516 for i in 1..=n {
2517 let temp = xpt[ix(k, i)] - xopt[i - 1];
2518 dderiv += glag[i - 1] * temp;
2519 distsq += temp * temp;
2520 }
2521 let mut subd = adelt / distsq.sqrt();
2522 let mut slbd = -subd;
2523 let mut ilbd: i64 = 0;
2524 let mut iubd: i64 = 0;
2525 let sumin = 1.0f64.min(subd);
2526
2527 for i in 1..=n {
2528 let temp = xpt[ix(k, i)] - xopt[i - 1];
2529 if temp > 0.0 {
2530 if slbd * temp < sl[i - 1] - xopt[i - 1] {
2531 slbd = (sl[i - 1] - xopt[i - 1]) / temp;
2532 ilbd = -(i as i64);
2533 }
2534 if subd * temp > su[i - 1] - xopt[i - 1] {
2535 subd = sumin.max((su[i - 1] - xopt[i - 1]) / temp);
2536 iubd = i as i64;
2537 }
2538 } else if temp < 0.0 {
2539 if slbd * temp > su[i - 1] - xopt[i - 1] {
2540 slbd = (su[i - 1] - xopt[i - 1]) / temp;
2541 ilbd = i as i64;
2542 }
2543 if subd * temp < sl[i - 1] - xopt[i - 1] {
2544 subd = sumin.max((sl[i - 1] - xopt[i - 1]) / temp);
2545 iubd = -(i as i64);
2546 }
2547 }
2548 }
2549
2550 let mut step;
2551 let mut vlag;
2552 let mut isbd;
2553 if k == knew {
2554 let diff = dderiv - 1.0;
2555 step = slbd;
2556 vlag = slbd * (dderiv - slbd * diff);
2557 isbd = ilbd;
2558 let temp = subd * (dderiv - subd * diff);
2559 if temp.abs() > vlag.abs() {
2560 step = subd;
2561 vlag = temp;
2562 isbd = iubd;
2563 }
2564 let tempd = 0.5 * dderiv;
2565 let tempa = tempd - diff * slbd;
2566 let tempb = tempd - diff * subd;
2567 if tempa * tempb < 0.0 {
2568 let temp = tempd * tempd / diff;
2569 if temp.abs() > vlag.abs() {
2570 step = tempd / diff;
2571 vlag = temp;
2572 isbd = 0;
2573 }
2574 }
2575 } else {
2576 step = slbd;
2577 vlag = slbd * (1.0 - slbd);
2578 isbd = ilbd;
2579 let temp = subd * (1.0 - subd);
2580 if temp.abs() > vlag.abs() {
2581 step = subd;
2582 vlag = temp;
2583 isbd = iubd;
2584 }
2585 if subd > 0.5 && vlag.abs() < 0.25 {
2586 step = 0.5;
2587 vlag = 0.25;
2588 isbd = 0;
2589 }
2590 vlag *= dderiv;
2591 }
2592
2593 let temp = step * (1.0 - step) * distsq;
2594 let predsq = vlag * vlag * (vlag * vlag + ha * temp * temp);
2595 if predsq > presav {
2596 presav = predsq;
2597 ksav = k;
2598 stpsav = step;
2599 ibdsav = isbd;
2600 }
2601 }
2602
2603 let ksav = ksav.max(1);
2607 for i in 1..=n {
2608 let temp = xopt[i - 1] + stpsav * (xpt[ix(ksav, i)] - xopt[i - 1]);
2609 xnew[i - 1] = sl[i - 1].max(su[i - 1].min(temp));
2610 }
2611 if ibdsav < 0 {
2612 let i = (-ibdsav) as usize;
2613 xnew[i - 1] = sl[i - 1];
2614 }
2615 if ibdsav > 0 {
2616 let i = ibdsav as usize;
2617 xnew[i - 1] = su[i - 1];
2618 }
2619
2620 let bigstp = adelt + adelt;
2621 let mut iflag = 0;
2622 let mut csave = 0.0;
2623 loop {
2624 let mut wfixsq = 0.0;
2626 let mut ggfree = 0.0;
2627 for i in 1..=n {
2628 w[i - 1] = 0.0;
2629 let tempa = (xopt[i - 1] - sl[i - 1]).min(glag[i - 1]);
2630 let tempb = (xopt[i - 1] - su[i - 1]).max(glag[i - 1]);
2631 if tempa > 0.0 || tempb < 0.0 {
2632 w[i - 1] = bigstp;
2633 ggfree += glag[i - 1] * glag[i - 1];
2634 }
2635 }
2636 if ggfree == 0.0 {
2637 *cauchy = 0.0;
2638 return;
2639 }
2640
2641 let mut step = 0.0;
2643 loop {
2644 let temp = adelt * adelt - wfixsq;
2645 if temp > 0.0 {
2646 let wsqsav = wfixsq;
2647 step = (temp / ggfree).sqrt();
2648 ggfree = 0.0;
2649 for i in 1..=n {
2650 if w[i - 1] == bigstp {
2651 let temp = xopt[i - 1] - step * glag[i - 1];
2652 if temp <= sl[i - 1] {
2653 w[i - 1] = sl[i - 1] - xopt[i - 1];
2654 wfixsq += w[i - 1] * w[i - 1];
2655 } else if temp >= su[i - 1] {
2656 w[i - 1] = su[i - 1] - xopt[i - 1];
2657 wfixsq += w[i - 1] * w[i - 1];
2658 } else {
2659 ggfree += glag[i - 1] * glag[i - 1];
2660 }
2661 }
2662 }
2663 if wfixsq > wsqsav && ggfree > 0.0 {
2664 continue;
2665 }
2666 }
2667 break;
2668 }
2669
2670 let mut gw = 0.0;
2671 for i in 1..=n {
2672 if w[i - 1] == bigstp {
2673 w[i - 1] = -step * glag[i - 1];
2674 xalt[i - 1] = sl[i - 1].max(su[i - 1].min(xopt[i - 1] + w[i - 1]));
2675 } else if w[i - 1] == 0.0 {
2676 xalt[i - 1] = xopt[i - 1];
2677 } else if glag[i - 1] > 0.0 {
2678 xalt[i - 1] = sl[i - 1];
2679 } else {
2680 xalt[i - 1] = su[i - 1];
2681 }
2682 gw += glag[i - 1] * w[i - 1];
2683 }
2684
2685 let mut curv = 0.0;
2686 for k in 1..=npt {
2687 let mut temp = 0.0;
2688 for j in 1..=n {
2689 temp += xpt[ix(k, j)] * w[j - 1];
2690 }
2691 curv += hcol[k - 1] * temp * temp;
2692 }
2693 if iflag == 1 {
2694 curv = -curv;
2695 }
2696 if curv > -gw && curv < -const_ * gw {
2697 let scale = -gw / curv;
2698 for i in 1..=n {
2699 let temp = xopt[i - 1] + scale * w[i - 1];
2700 xalt[i - 1] = sl[i - 1].max(su[i - 1].min(temp));
2701 }
2702 let a = 0.5 * gw * scale;
2703 *cauchy = a * a;
2704 } else {
2705 let a = gw + 0.5 * curv;
2706 *cauchy = a * a;
2707 }
2708
2709 if iflag == 0 {
2710 for i in 1..=n {
2711 glag[i - 1] = -glag[i - 1];
2712 w[n + i - 1] = xalt[i - 1];
2713 }
2714 csave = *cauchy;
2715 iflag = 1;
2716 continue;
2717 }
2718 if csave > *cauchy {
2719 for i in 1..=n {
2720 xalt[i - 1] = w[n + i - 1];
2721 }
2722 *cauchy = csave;
2723 }
2724 return;
2725 }
2726}
2727
2728fn update(
2739 n: usize,
2740 npt: usize,
2741 bmat: &mut [f64],
2742 zmat: &mut [f64],
2743 ndim: usize,
2744 vlag: &mut [f64],
2745 beta: f64,
2746 denom: f64,
2747 knew: usize,
2748 w: &mut [f64],
2749) {
2750 let ib = |k: usize, j: usize| (k - 1) + (j - 1) * ndim;
2751 let iz = |k: usize, j: usize| (k - 1) + (j - 1) * npt;
2752 let nptm = npt - n - 1;
2753
2754 let mut ztest = 0.0f64;
2755 for k in 1..=npt {
2756 for j in 1..=nptm {
2757 ztest = ztest.max(zmat[iz(k, j)].abs());
2758 }
2759 }
2760 ztest *= 1e-20;
2761
2762 for j in 2..=nptm {
2763 if zmat[iz(knew, j)].abs() > ztest {
2764 let a = zmat[iz(knew, 1)];
2765 let b = zmat[iz(knew, j)];
2766 let temp = (a * a + b * b).sqrt();
2767 let tempa = a / temp;
2768 let tempb = b / temp;
2769 for i in 1..=npt {
2770 let temp = tempa * zmat[iz(i, 1)] + tempb * zmat[iz(i, j)];
2771 zmat[iz(i, j)] = tempa * zmat[iz(i, j)] - tempb * zmat[iz(i, 1)];
2772 zmat[iz(i, 1)] = temp;
2773 }
2774 }
2775 zmat[iz(knew, j)] = 0.0;
2776 }
2777
2778 for i in 1..=npt {
2779 w[i - 1] = zmat[iz(knew, 1)] * zmat[iz(i, 1)];
2780 }
2781 let alpha = w[knew - 1];
2782 let tau = vlag[knew - 1];
2783 vlag[knew - 1] -= 1.0;
2784
2785 let temp = denom.sqrt();
2786 let tempb = zmat[iz(knew, 1)] / temp;
2787 let tempa = tau / temp;
2788 for i in 1..=npt {
2789 zmat[iz(i, 1)] = tempa * zmat[iz(i, 1)] - tempb * vlag[i - 1];
2790 }
2791
2792 for j in 1..=n {
2793 let jp = npt + j;
2794 w[jp - 1] = bmat[ib(knew, j)];
2795 let tempa = (alpha * vlag[jp - 1] - tau * w[jp - 1]) / denom;
2796 let tempb = (-beta * w[jp - 1] - tau * vlag[jp - 1]) / denom;
2797 for i in 1..=jp {
2798 bmat[ib(i, j)] = bmat[ib(i, j)] + tempa * vlag[i - 1] + tempb * w[i - 1];
2799 if i > npt {
2800 bmat[ib(jp, i - npt)] = bmat[ib(i, j)];
2801 }
2802 }
2803 }
2804}
2805
2806pub fn bobyqa<F>(
2820 mut f: F,
2821 x0: &[f64],
2822 lower: &[f64],
2823 upper: &[f64],
2824 params: Option<BobyqaParams>,
2825) -> Result<BobyqaResult, EdgeErrors>
2826where
2827 F: FnMut(&[f64]) -> f64,
2828{
2829 let mut stepper = BobyqaStepper::new(x0, lower, upper, params)?;
2830 while let Some(x) = stepper.ask() {
2831 let value = f(x);
2832 stepper.tell(value);
2833 }
2834 Ok(stepper.result())
2835}
2836
2837#[cfg(test)]
2842mod tests {
2843 use super::*;
2844 use std::path::PathBuf;
2845
2846 struct Case {
2849 tag: &'static str,
2851 f: fn(&[f64]) -> f64,
2853 x0: &'static [f64],
2855 lo: &'static [f64],
2857 hi: &'static [f64],
2859 status: BobyqaStatus,
2861 }
2862
2863 fn rosen(x: &[f64]) -> f64 {
2864 100.0 * (x[1] - x[0] * x[0]).powi(2) + (1.0 - x[0]).powi(2)
2865 }
2866
2867 fn corner(x: &[f64]) -> f64 {
2868 (x[0] - 3.0).powi(2) + 10.0 * (x[1] + 1.0).powi(2) + x[0] * x[1]
2869 }
2870
2871 fn ripple(x: &[f64]) -> f64 {
2872 let u = 10000.0 * x[0] + 30000.0 * x[1];
2873 let t = (u - 2.0 * ((u + 1.0) / 2.0).floor()).abs();
2874 (x[0] - 0.25).powi(2) + 2.0 * (x[1] + 0.125).powi(2) + 0.5 * x[0] * x[1] + 0.5 * (t * t)
2875 }
2876
2877 fn valley(x: &[f64]) -> f64 {
2878 (x[0] + x[1] - 1.0).powi(2)
2879 }
2880
2881 fn chain3(x: &[f64]) -> f64 {
2882 (x[0] - 1.0).powi(2)
2883 + 100.0 * (x[1] - x[0] * x[0]).powi(2)
2884 + (x[2] - x[1]).powi(2)
2885 + 0.5 * x[2].powi(2).powi(2)
2886 }
2887
2888 const CASES: [Case; 6] = [
2889 Case {
2890 tag: "rosen",
2891 f: rosen,
2892 x0: &[-1.2, 1.0],
2893 lo: &[-2.0, -1.0],
2894 hi: &[2.0, 3.0],
2895 status: BobyqaStatus::XtolReached,
2896 },
2897 Case {
2898 tag: "rosen_edge",
2899 f: rosen,
2900 x0: &[0.4001, 0.2],
2901 lo: &[0.4, 0.2],
2902 hi: &[2.0, 2.0],
2903 status: BobyqaStatus::XtolReached,
2904 },
2905 Case {
2906 tag: "corner",
2907 f: corner,
2908 x0: &[1.0, 1.0],
2909 lo: &[0.0, 0.0],
2910 hi: &[2.0, 2.0],
2911 status: BobyqaStatus::XtolReached,
2912 },
2913 Case {
2914 tag: "chain3",
2915 f: chain3,
2916 x0: &[0.5, 2.0, -1.0],
2917 lo: &[-3.0, -3.0, -3.0],
2918 hi: &[3.0, 3.0, 3.0],
2919 status: BobyqaStatus::XtolReached,
2920 },
2921 Case {
2923 tag: "rescue",
2924 f: ripple,
2925 x0: &[-1.0, 0.25],
2926 lo: &[-2.0, -2.0],
2927 hi: &[2.0, 2.0],
2928 status: BobyqaStatus::XtolReached,
2929 },
2930 Case {
2931 tag: "roundoff",
2932 f: valley,
2933 x0: &[0.0, 0.0],
2934 lo: &[-2.0, -2.0],
2935 hi: &[2.0, 2.0],
2936 status: BobyqaStatus::RoundoffLimited,
2937 },
2938 ];
2939
2940 fn trace(tag: &str) -> Vec<Vec<f64>> {
2942 let path: PathBuf = [
2943 env!("CARGO_MANIFEST_DIR"),
2944 "tests",
2945 "data",
2946 "e2e",
2947 &format!("bobyqa_{tag}.csv"),
2948 ]
2949 .iter()
2950 .collect();
2951 std::fs::read_to_string(&path)
2952 .unwrap_or_else(|e| panic!("cannot read {}: {e}", path.display()))
2953 .lines()
2954 .map(|l| {
2955 l.split(',')
2956 .map(|v| v.trim().parse().expect("number"))
2957 .collect()
2958 })
2959 .collect()
2960 }
2961
2962 #[test]
2963 fn test_bobyqa_retraces_nloptr_point_for_point() {
2964 for case in &CASES {
2965 let want = trace(case.tag);
2966 let mut got: Vec<Vec<f64>> = Vec::new();
2967 let res = bobyqa(
2968 |x| {
2969 let v = (case.f)(x);
2970 got.push(x.iter().copied().chain([v]).collect());
2971 v
2972 },
2973 case.x0,
2974 case.lo,
2975 case.hi,
2976 None,
2977 )
2978 .expect("valid box");
2979 assert_eq!(got.len(), want.len(), "{}: evaluation count", case.tag);
2980 for (i, (g, w)) in got.iter().zip(&want).enumerate() {
2981 assert_eq!(g, w, "{}: evaluation {i}", case.tag);
2982 }
2983 assert_eq!(res.evaluations, want.len());
2984 assert_eq!(res.status, case.status, "{}: status", case.tag);
2985 }
2986 }
2987
2988 #[test]
2989 fn test_bobyqa_lands_on_an_active_bound() {
2990 let res = bobyqa(corner, &[1.0, 1.0], &[0.0, 0.0], &[2.0, 2.0], None).expect("valid box");
2991 assert_eq!(res.x, vec![2.0, 0.0]);
2992 assert_eq!(res.f, 11.0);
2993 }
2994}