1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Integration tests for the box-constrained MADS variant
//! (`Mads::new().bounded()`), which enforces bounds by the extreme barrier
//! (infeasible poll points get `f = +∞`).
//!
//! Mirrors `bounded_nelder_mead.rs`: slack-bounds / tight-bounds / infeasible-
//! start coverage on `BoothBoxed`. Backend coverage piggybacks on the
//! unbounded `mads_public.rs` tests (the bounded path differs only in the
//! barrier eval closure, which is backend-agnostic).
use basin::problems::BoothBoxed;
use basin::{Executor, Mads, MadsState, MaxCostEvals, State, TerminationReason};
/// Slack bounds: the unconstrained Booth minimum `(1, 3)` lies inside `[-5, 5]²`,
/// so the barrier never fires and MADS recovers the unconstrained optimum.
#[test]
fn slack_bounds_recover_unconstrained_minimum() {
let problem = BoothBoxed::<Vec<f64>>::new(vec![-5.0, -5.0], vec![5.0, 5.0]);
let result = Executor::new(
problem,
Mads::new()
.bounded()
.with_initial_poll_size(1.0)
.with_min_poll_size(1e-6),
MadsState::new(vec![0.0, 0.0]),
)
.max_iter(50_000)
.terminate_on(MaxCostEvals(20_000))
.run()
.unwrap();
assert!(result.best_cost() < 1e-6, "cost = {}", result.best_cost());
let x = result.best_param();
assert!(
(x[0] - 1.0).abs() < 1e-3,
"x[0] = {} (expected near 1)",
x[0]
);
assert!(
(x[1] - 3.0).abs() < 1e-3,
"x[1] = {} (expected near 3)",
x[1]
);
}
/// Tight bounds: the unconstrained minimum `(1, 3)` lies outside `[-1, 1]²`; the
/// constrained optimum is the box corner `(1, 1)`. The extreme barrier rejects
/// every poll point that steps out of the box, so MADS pins the incumbent to the
/// active upper face and converges to the corner.
#[test]
fn tight_bounds_converge_to_box_corner() {
let problem = BoothBoxed::<Vec<f64>>::new(vec![-1.0, -1.0], vec![1.0, 1.0]);
let result = Executor::new(
problem,
Mads::new()
.bounded()
.with_initial_poll_size(1.0)
.with_min_poll_size(1e-8),
MadsState::new(vec![0.0, 0.0]),
)
.max_iter(50_000)
.terminate_on(MaxCostEvals(20_000))
.run()
.unwrap();
let x = result.best_param();
assert!(
(x[0] - 1.0).abs() < 1e-4,
"x[0] = {} (expected pinned at upper bound 1)",
x[0]
);
assert!(
(x[1] - 1.0).abs() < 1e-4,
"x[1] = {} (expected pinned at upper bound 1)",
x[1]
);
}
/// An infeasible start is clamped into the box at `init`, so with `max_iter = 0`
/// the reported iterate is already feasible (the `(10, 10)` start clamps to the
/// `(1, 1)` corner).
#[test]
fn infeasible_start_clamped_at_init() {
let problem = BoothBoxed::<Vec<f64>>::new(vec![-1.0, -1.0], vec![1.0, 1.0]);
let result = Executor::new(
problem,
Mads::new().bounded(),
MadsState::new(vec![10.0, 10.0]),
)
.max_iter(0)
.run()
.unwrap();
assert_eq!(result.reason, TerminationReason::MaxIter);
let x = result.state.param();
assert!(
(x[0] - 1.0).abs() < 1e-12 && (x[1] - 1.0).abs() < 1e-12,
"start (10, 10) should clamp to the corner (1, 1); got {x:?}"
);
}