Skip to content

chc.barrier

Stability tier: stable.

Safety certificates when the control effect is only PARTIALLY IDENTIFIED (§40).

The pessimism line (chc.support, chc.uncertainty) spends an identification radius on performance: a worse effect estimate buys a wider penalty and a costlier plan. This module spends the same radius on safety, and the accounting is different in a way that matters operationally.

For a control-affine plant xdot = f(x) + B u and a safe set {h >= 0}, the barrier condition is grad h . (f + B u) >= -alpha * h. Write a = grad h . f for the drift term, g for the control channel and U for the actuation limit. If the effect matrix is known only up to a radius Delta -- here an identification radius from §32's bounded-density-ratio model at level Gamma, not a disturbance bound -- the guaranteed barrier derivative is a + g*u - d*|u| with d = Delta * ||grad h||, and:

  • the best guaranteed margin is a + (|g| - d)_+ * U, so the margin lost to an identification radius d is U * min(d, |g|) -- first order in d while control authority lasts, then flat: past d = |g| there is nothing left to lose;
  • at d >= |g| the robust-optimal action is exactly zero: with the sign of the channel unidentified, acting cannot improve the guarantee (the safety analogue of §11's sign threshold);
  • certification is sharp at d* = |g| - D/U, where D = -alpha*h - a is the deficit the drift leaves for the controller, which inverts to a largest tolerable sensitivity Gamma*. Three regimes, and only the third is the interesting one: D <= 0 (the drift alone is safe, no radius can break it), D > U*|g| (nominally infeasible -- not certified even at d = 0), and 0 < D <= U*|g| (d* in [0, |g|), the case the threshold formula describes).

The tightening itself is the standard robust-CBF one (Jankovic 2018; Kolathaya-Ames 2019). What is new here is the source of the radius and the resulting order dichotomy: the same effect error costs performance regret only at second order (§33, L_reg * e**2): an interior optimum has a vanishing gradient, while a binding constraint has no envelope protection and pays at first order. Objectives are protected; constraints are not. That comparison lives in d < |g|, which is also where any usable controller lives -- once the radius exceeds the channel the safety loss is capped at U*|g| simply because the guarantee is already gone. Within that band, and below e = U / L_reg, safety is the larger loss by a ratio that diverges as the error shrinks.

Uncertainty-set assumption. a + <w, u> - d*||u|| is the worst case for a symmetric, isotropic set: ||u||_2 <= U for the action and a dual-norm ball of radius d for the channel error. It is exact there (Cauchy-Schwarz is attained). For box actuation limits or anisotropic channel uncertainty the worst case is the corresponding support function and the closed forms below are only an outer bound. For an asymmetric scalar interval g_true in [g - d_lo, g + d_hi] the two branch slopes differ (g - d_lo right, g + d_hi left) and the zero-action rule reads in its general form: acting is pointless exactly when the identified interval contains zero.

NumPy, like the rest of the §32-§40 sensitivity line, so it is independent of the JAX x64 flag. Maxima validation/barrier_feasibility.mac, Rocq proofs/barrier_feasibility.v.

BarrierConfoundingCurve dataclass

Measured evidence for §40 over a sensitivity grid on one confounded safety problem.

SafetyFilterBenchmark dataclass

Closed-loop evidence for the §40 claim that a regret-sized budget is not a safety budget.

control_channel(grad_h, b_hat)

||B^T grad h|| -- how strongly the control can move the barrier, in the best direction.

The multivariate stand-in for the scalar |g|. Cauchy-Schwarz is tight at u = U * w / ||w||, so every bound below is attained rather than conservative.

robust_barrier_margin(drift, channel, radius, u_max)

Best barrier derivative guaranteed against every effect inside the identified set.

drift + (channel - radius)_+ * u_max. The clip at zero is the whole content: past radius = channel extra actuation buys nothing, because the adversary can cancel it. So the margin lost relative to a perfectly identified channel is u_max * min(radius, channel) -- first order in the radius until the authority runs out, then constant. Exact for a Euclidean action ball and an isotropic channel radius (see the module docstring).

robust_safe_action(grad_h, b_hat, radius, u_max)

The action attaining robust_barrier_margin: full authority, or nothing at all.

Returns exactly 0 when radius >= ||B^T grad h||. That is not a numerical artefact and not conservatism -- it is the optimum: with the channel's sign unidentified, every nonzero action has a worst case at least as bad as standing still.

identification_radius_threshold(drift, channel, u_max, alpha_h)

Largest radius d* whose safety certificate still holds: channel - D / u_max.

D = -alpha_h - drift is the deficit the drift leaves for the controller to cover, and the answer is piecewise in it -- the formula alone is a trap, because it happily returns a negative "largest radius" for a problem no radius can save:

  • D <= 0 -- the uncontrolled system already satisfies the barrier. No radius can break it, so the threshold is vacuous and inf says so rather than inviting a comparison.
  • D > u_max * channel -- nominally infeasible: even a perfectly identified channel at full authority cannot cover the deficit, so nothing is certified, not even at radius = 0. nan (the module's empty-set convention, as in admissible_action_interval).
  • 0 < D <= u_max * channel -- the case worth having a threshold for: d* lands in [0, channel) and certification holds iff radius <= d*.

barrier_gamma_star(threshold_radius, cvar_gap, grad_norm)

Largest MSM Gamma a safety certificate survives: (gap + c) / (gap - c).

Inverts §32's Delta(Gamma) = (Gamma-1)/(Gamma+1) * cvar_gap at threshold_radius, with c = threshold_radius / grad_norm the effect-space radius the barrier can absorb. The closed form is only valid on 0 < c < cvar_gap; outside it the four cases are genuinely different answers rather than edge-case noise, and the denominator would silently change sign:

  • c < 0 (or nan) -- nothing is certified, not even at Gamma = 1 where the radius is zero. nan, because no Gamma >= 1 works and reporting 1.0 would claim the opposite.
  • c == 0 -- exact identification and nothing more: 1.0.
  • 0 < c < cvar_gap -- the finite ceiling (gap + c) / (gap - c).
  • c >= cvar_gap -- Delta saturates at cvar_gap as Gamma -> inf, so the threshold is beyond anything the sensitivity model can produce: inf.

grad_norm == 0 is a state where the barrier is flat, such as the centre of a ball, and there the radius Delta * grad_norm is zero at every Gamma: the step holds at every level or at none, inf when threshold_radius >= 0 and nan otherwise.

admissible_action_interval(channel, radius, u_max, drift, alpha_h)

The scalar actions whose guaranteed barrier derivative clears -alpha_h.

channel is the signed grad h . B, unlike the norm taken by robust_barrier_margin -- which way to push is the whole question here. The guaranteed derivative drift + channel*u - radius*|u| is concave in u, so the admissible set is an interval; combined with |u| <= u_max it stays one. Returns an empty interval as (nan, nan) when no admissible action exists, which the caller must handle rather than silently clip into.

A non-positive deficit means u = 0 is admissible -- it does not mean every action is. The control that serves the task is exactly the one that can spend the drift's slack and cross the boundary, so both ends are computed from the branch slopes in every case.

robust_safety_filter(u_nominal, channel, radius, u_max, drift, alpha_h)

Least-restrictive admissible action: the nominal, clipped into the certified interval.

Minimal intervention in the exact sense -- among all actions whose guaranteed margin clears, the one closest to what the task controller asked for. Scalar control only: for m > 1 inputs the same set is convex but its projection is no longer a clip, and a filter that quietly over-brakes would misreport the price of safety. Use robust_safe_action there, which maximises the margin rather than tracking a nominal.

When the interval is empty -- the deficit exceeds what the identified channel can deliver -- the margin-maximising action sign(channel)*u_max is returned: it does not certify, and the caller should already know that from identification_radius_threshold.

barrier_confounding_certificate(channel=0.6, drift=-0.9, u_max=2.0, alpha_h=0.5, cvar_gap=1.0, grad_norm=1.0, l_reg=3.0, gammas=(1.0, 1.5, 2.0, 3.0, 5.0, 9.0))

Sweep the sensitivity level and check the threshold, the zero-action rule and the orders.

The defaults are chosen so the sweep crosses every regime rather than sitting inside one: the drift is in deficit (D = 0.4 > 0) so the controller is load-bearing, Gamma* = 7/3 falls inside the grid so certification genuinely fails partway, and the radius overtakes the channel at Gamma = 4 so the tail of the grid exercises the zero-action rule. A configuration with D <= 0 would certify everything and prove nothing.

safety_filter_benchmark(b_true=1.0, kappa=-0.5, confounding=1.0, gamma=3.0, decay=0.3, x0=0.0, x_ref=3.0, x_limit=2.0, alpha=1.0, dt=0.1, n_steps=60, u_max=6.0, n_data=4000, seed=0)

Drive a confounded plant at a reference beyond a safety limit, four ways.

The scalar plant x' = decay*x + b_true*u must track x_ref while respecting x <= x_limit, and the two are in conflict by construction -- the reference sits past the limit, so nothing but the constraint stops the controller. The drift is unstable (decay > 0), which is what makes the threshold bite: near the limit the plant pushes outward on its own, so the controller has a real deficit to cover and Gamma* is finite. The actuator gain is calibrated from an observational log whose action responded to a latent driver (kappa < 0 attenuates it), so the controller believes a weaker actuator than it has and over-commands.

The four rows are the point:

  • oracle filters with the true gain and a zero radius -- the best achievable safe tracking;
  • safety_calibrated filters with the biased gain and the §32 radius at gamma;
  • regret_calibrated shrinks its action by the §33 accounting instead -- the radius spent on the objective, which is what this library did everywhere before §40;
  • greedy believes the biased gain and does nothing about it.

Returns violation rate, worst excursion and tracking cost for each. Read the tracking column with its floor in mind: a controller that respects the limit cannot beat (x_limit - x_ref)**2 on this plant, so the violators' lower cost is bought with the excursions in the other two columns and is not evidence that they control better. This is a demonstration on one synthetic plant, not a theorem: it grounds the order argument rather than proving it.