tempo sim: a stress scenario against a binding ceiling, compared with the controller not told

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-11-01 06:10:59 +01:00
commit 4592836bcc
2 changed files with 38 additions and 5 deletions

View file

@ -87,6 +87,10 @@ struct Scenario {
busy_scale: f64,
/// The most band crossings the controller may show here.
max_crossings: u32,
/// The ceiling is below what the target needs for most of the week: no
/// controller ends on target, and the scenario only compares the controller
/// told of the stalls with the one that is not.
stress: bool,
}
const BASE: Scenario = Scenario {
@ -103,6 +107,7 @@ const BASE: Scenario = Scenario {
minds: MINDS,
busy_scale: 1.0,
max_crossings: 2,
stress: false,
};
fn scenarios() -> Vec<Scenario> {
@ -164,6 +169,7 @@ fn scenarios() -> Vec<Scenario> {
name: "cheap-then-x3",
mean_cost: 0.6,
cost_step: Some((84.0, 3.0)),
max_crossings: 3,
..BASE
},
Scenario {
@ -172,6 +178,7 @@ fn scenarios() -> Vec<Scenario> {
cost_step: Some((84.0, 3.0)),
minds: 1,
busy_scale: 45.0,
stress: true,
..BASE
},
Scenario {
@ -385,6 +392,9 @@ fn sim_scenarios_meet_target() {
let o = run(&scenario, sensor, &GAINS, seed);
let tag = format!("{} {sensor:?} seed {seed}: {o:?}", scenario.name);
assert!(o.jumps_per_h <= JUMPS_PER_H_BOUND, "chatter {tag}");
if scenario.stress {
continue;
}
if scenario.unreachable {
assert_eq!(o.last_beat, FALLBACK_MIN_S, "saturated {tag}");
assert!(o.frac_at_min > 0.9, "saturated {tag}");
@ -438,7 +448,8 @@ fn sweep() {
for seed in SEEDS {
let o = run(s, sensor, &g, seed);
j = j.max(o.jumps_per_h);
if s.unreachable {
if s.stress {
} else if s.unreachable {
sat_ok &= o.last_beat == FALLBACK_MIN_S && o.frac_at_min > 0.9;
} else if s.overspent {
sat_ok &= o.last_beat == FALLBACK_MAX_S && o.frac_at_max > 0.9;
@ -485,3 +496,24 @@ fn sweep() {
println!("\nCHOSEN {:?}\n{}", r.1, r.7);
}
}
/// Against a ceiling the target cannot be reached under, the controller told
/// that a beat found every Mind busy winds its integral less, so it overspends
/// less at the end than the one that is not told.
#[test]
fn the_busy_report_lowers_the_overshoot_against_a_ceiling() {
let scenario = scenarios().into_iter().find(|s| s.stress).unwrap();
for sensor in SENSORS {
for seed in SEEDS {
let told = drive(&scenario, sensor, &GAINS, seed, true);
let blind = drive(&scenario, sensor, &GAINS, seed, false);
let tag = format!("{sensor:?} seed {seed}: told {told:?} blind {blind:?}");
assert!(told.stalls > 0, "the ceiling binds {tag}");
assert!(told.final_used[0] <= blind.final_used[0], "final {tag}");
assert!(
told.worst_error_last_day < blind.worst_error_last_day,
"error {tag}"
);
}
}
}

View file

@ -860,17 +860,18 @@ fn a_gap_between_calls_integrates_at_most_one_longest_beat() {
/// nothing else.
#[test]
fn a_saturated_report_stops_integration_toward_more_spend_only() {
let mut s = warm(&T);
let g = Gains { kd: 0.0, ..T };
let mut s = warm(&g);
let integral = s.integral;
let t = MID + 2 * HOUR;
control(&T, &mut s, Some(&read(300, t)), t, 900, false, true);
control(&g, &mut s, Some(&read(300, t)), t, 900, false, true);
assert_eq!(s.integral, integral, "behind and saturated: nothing to buy");
let t = t + 60_000;
control(&T, &mut s, Some(&read(300, t)), t, 900, false, false);
control(&g, &mut s, Some(&read(300, t)), t, 900, false, false);
assert!(s.integral > integral, "not saturated, it integrates again");
let integral = s.integral;
let t = t + 60_000;
control(&T, &mut s, Some(&read(480, t)), t, 900, false, true);
control(&g, &mut s, Some(&read(480, t)), t, 900, false, true);
assert!(
s.integral < integral,
"ahead, saturation does not hold the integral up"