Representation-Invariant Authority Margin / iad_experiment.py

Mechanism confirmed, baseline not beaten

Raw ⬇ ZIP
  1"""Representation-Invariant Authority Demand: small numerical verification.
  2
  3Toy system: xdot = alpha*x + u, x in R^2, ||u||_inf <= rho.
  4Safe set: h(x)=1-||x||^2 >= 0.  On the boundary, the exact IAD is alpha,
  5because r=2 alpha and a=2 rho (|x_1|+|x_2|), whose worst ratio is alpha/rho.
  6The exact boundary feasibility threshold is therefore rho=alpha.
  7"""
  8import json
  9import numpy as np
 10
 11SEED = 3140
 12rng = np.random.default_rng(SEED)
 13alpha = 0.73
 14N = 200_000
 15# Uniform angular boundary sample, plus axes so the supremum is represented.
 16theta = rng.uniform(0, 2*np.pi, N)
 17theta = np.concatenate([theta, [0, np.pi/2, np.pi, 3*np.pi/2]])
 18x = np.column_stack([np.cos(theta), np.sin(theta)])
 19
 20def base_h(z):
 21    return 1.0 - np.sum(z*z, axis=-1)
 22
 23def grad_base_h(z):
 24    return -2.0*z
 25
 26def transformed(z, name):
 27    h = base_h(z)
 28    if name == 'h':
 29        q, qp = h, np.ones_like(h)
 30    elif name == '2h':
 31        q, qp = 2*h, 2*np.ones_like(h)
 32    elif name == 'h+h^3':
 33        q, qp = h + h**3, 1 + 3*h*h
 34    elif name == 'log(1+h)':
 35        q, qp = np.log1p(h), 1/(1+h)
 36    elif name == 'h^3 (invalid)':
 37        q, qp = h**3, 3*h*h
 38    else:
 39        raise ValueError(name)
 40    # On/near the boundary grad(q(h)) = q'(h) grad(h).
 41    grad = qp[:, None] * grad_base_h(z)
 42    return q, grad
 43
 44def iad_for(rho, name):
 45    _, grad = transformed(x, name)
 46    f = alpha*x
 47    r = -np.sum(grad*f, axis=1)
 48    # support of [-rho,rho]^2: rho * L1 norm of grad
 49    a = rho*np.sum(np.abs(grad), axis=1)
 50    d = np.zeros_like(r)
 51    valid = a > 1e-14
 52    d[valid] = np.maximum(0, r[valid]) / a[valid]
 53    d[~valid & (r > 0)] = np.inf
 54    return float(np.max(d)), float(np.min(-r+a)), float(np.mean(r))
 55
 56def softsup(values, tau=0.02):
 57    m = np.max(values)
 58    return float(m + tau*np.log(np.mean(np.exp((values-m)/tau))))
 59
 60def run():
 61    names = ['h', '2h', 'h+h^3', 'log(1+h)', 'h^3 (invalid)']
 62    rows = {}
 63    for name in names:
 64        D, _, mean_r = iad_for(1.0, name)
 65        # raw boundary outward penalty changes with barrier representation;
 66        # IAD is a ratio and should not (for valid q'(0)>0).
 67        rows[name] = {'D_at_rho_1': D, 'raw_mean_r': mean_r}
 68
 69    # Transition: exact sampled min controlled derivative at each actuator scale.
 70    scales = np.array([0.50, 0.65, 0.72, 0.73, 0.74, 0.85, 1.00])
 71    transition = []
 72    for rho in scales:
 73        D, min_deriv, _ = iad_for(rho, 'h')
 74        transition.append({'rho': float(rho), 'D': D,
 75                           'min_boundary_derivative': min_deriv,
 76                           'feasible_sampled': bool(min_deriv >= -1e-10)})
 77
 78    # Empirical soft supremum converges to the exact supremum as sampling grows.
 79    # Compute pointwise demand for the base representation for a stable diagnostic.
 80    _, grad = transformed(x, 'h')
 81    r = -np.sum(grad*(alpha*x), axis=1)
 82    a = np.sum(np.abs(grad), axis=1)  # rho=1
 83    d = np.maximum(0, r)/a
 84    soft = softsup(d)
 85    # Finite-difference check of the analytic gradient on several boundary points.
 86    eps = 1e-6
 87    check = x[:32]
 88    numeric = np.empty_like(check)
 89    for j in range(2):
 90        step = np.zeros_like(check)
 91        step[:, j] = eps
 92        numeric[:, j] = (base_h(check + step) - base_h(check - step)) / (2 * eps)
 93    analytic = grad_base_h(check)
 94    grad_err = float(np.max(np.abs(numeric - analytic)))
 95
 96    result = {
 97        'seed': SEED, 'alpha_exact_threshold': alpha,
 98        'representation_checks': rows,
 99        'transition': transition,
100        'softsup_tau_0.02_rho_1': soft,
101        'max_boundary_gradient_finite_difference_error': grad_err,
102        'valid_invariance_relative_spread': float((max(rows[n]['D_at_rho_1'] for n in names[:4]) - min(rows[n]['D_at_rho_1'] for n in names[:4]))/alpha),
103        'invalid_h3_D': rows['h^3 (invalid)']['D_at_rho_1'],
104        'notes': 'For h^3, q_prime(0)=0, so the defining-function regularity assumption fails and the boundary gradient vanishes.'
105    }
106    with open('results.json', 'w') as f:
107        json.dump(result, f, indent=2)
108    print(json.dumps(result, indent=2))
109
110if __name__ == '__main__':
111    run()