Skip to content

Randomised level-set shield: what pure actions give away

The paper (thm:fixed_floor_permissive, following Court et al. 2026, Robust Shielding) proves soundness and permissiveness for a randomised shield. It samples a pair (action, successor budget) from a distribution nu with

max_{l in Lambda} E_{(a, W) ~ nu} [ sum_{s'} P_l(s' | s, a) W(s') ] <= q,   W >= beta.

The opponents' level is fixed before the focal action is sampled, so the worst case sits outside the mixture. The deployed TransitionShield restricts nu to point masses: it is sound, but not complete.

Values

  • Pure (deployed): beta_rob(s; f) = min_a max_{l >= f} P_l(s, a) . beta_rob.
  • Mixed: beta_mix(s; f) = min_{sigma in Delta(A)} max_{l >= f} sum_a sigma_a P_l(s, a) . beta_mix.

With one retained level the two coincide, because a linear objective is minimised at a vertex. Under patience 64 the floor reaches the top level early in five of the six environments. From then on, mixing can only add budget lotteries, not hedging across levels.

How src/shield/ambiguity/mixed.py solves it

  • minimax_mixed(costs): the per-state minimax min_sigma max_l sigma . C_l is a linear programme. An optimal vertex mixes at most |Lambda| actions, with their level costs tied. So it enumerates pure actions, pairs tied on two levels, and triples tied on all three, and is exact for three or fewer levels.
  • It is vectorised over states, and every candidate is scored by its own worst level, so each value is attained.
  • It is cross-checked against scipy.optimize.linprog in tests/test_mixed_values.py.
  • floor_values(compiled, floor): two-sided iteration of both operators, using the same conventions as MarginSolver:
  • unsafe states are fixed at 1;
  • almost-surely-safe states are fixed at 0 (a mixture is almost surely safe only if each of its actions is);
  • upper iterates are rounded outward, so every upper iterate is an inductive certificate.
  • permissiveness_loss: bounds d = ||beta_pure - beta_mix||_inf by max(pure_upper - mixed_lower), so the bound is sound.

Report

scripts/compute_mixed_values.py writes exports/mixed_values/<env>.json with, per agent and floor:

  • reset brackets under both operators;
  • d over all states and over reachable states;
  • the theorem's eps = d / (d + p - beta(s0)) for the deployed pure certificate;
  • the count of reachable states where mixing certainly lowers the value.

Earlier evidence: the two-action mixed mode

ParameterizedTransitionShield in egalitarian_mixed mode (see parameterized-actions.md) is already a K-compliant controller with two atoms and the pure certificate. On the campaign seeds (paper Table replacement_ablation) it:

  • fails the gate in Bertrand (U200 0.335) and Chicken (U200 0.400);
  • loses reward in Gathering and Markov Stag Hunt;
  • ties in Pursuit.

Randomising lets the shield spend its whole budget. The pure shield's unspent slack is what absorbs opponent-model error in practice. So a randomised mode should not become the default without new evidence.

d for the deployed certificate (6 Oct 2026)

The theorem's d = ||beta - beta_mix||_inf concerns the certificate the shield actually uses, bundle.floor_budget_lower_bounds. That is robust value iteration at floors with several levels, and the single level's sound value iteration at the top floor. The runtime reads a missing state as 1.

compute_mixed_values.py now reports deployed: the bound d <= max(beta_deployed - mixed_lower), the theorem's epsilon, and the gap from this solver's pure upper iterate (svi_gap_*). Results for Pursuit (exports/mixed_values_deployed/pursuit.json, CX3 job 4282640):

Floor Reset (deployed) d eps SVI gap
0 0.1514877 0.014467 0.6296 <= 2.7e-8
1 0.1397650 0.011279 0.3579 <= 1.1e-8
2 0.0938741 6.6e-9 1.0e-7 <= 4.0e-9

No state has the deployed certificate below the mixed lower iterate. Floor 2's eps is about 1e-7, not exactly 0, because of value-iteration slack.

The other five environments (CX3 job 4282650, same export directory) give d = 0 exactly at every floor, Congestion for both roles, with no SVI gap: every state's deployed value equals the mixed lower iterate. Unsafe states are 1, and every other state is 0 or matches exactly.