Bifurcation-Calibrated Stale-Gradient Controller / bifurcation_controller.py

Failed on benchmark

Raw ⬇ ZIP
 1"""Small MVP of bifurcation-calibrated delayed two-mode control.
 2
 3The estimator fits the leading displacement model
 4 D(y) = V*y**(2*n) + kappa*mu/(abs(y)**(2*q)+epsilon)
 5from section-return observations.  The controller uses the resulting safety
 6bound mu_max = r**M*abs(V)/abs(kappa) to cap a requested delay.
 7"""
 8from dataclasses import dataclass
 9import numpy as np
10
11@dataclass
12class Calibration:
13    n: int
14    q: int
15    V: float
16    kappa: float
17    radius: float
18    @property
19    def M(self):
20        return 2 * (self.n + self.q)
21    @property
22    def mu_max(self):
23        if self.V >= 0 or self.kappa <= 0:
24            return 0.0
25        return self.radius ** self.M * abs(self.V) / self.kappa
26    def predicted_amplitude(self, mu):
27        if self.V >= 0 or self.kappa <= 0:
28            return np.nan
29        return (self.kappa * mu / abs(self.V)) ** (1.0 / self.M)
30
31def fit_return_map(y, y_next, mu, n=1, q=1, radius=1.0, epsilon=1e-12):
32    """Least-squares estimate of V and positive kappa from return pairs."""
33    y=np.asarray(y, dtype=float); y_next=np.asarray(y_next, dtype=float)
34    mu=np.broadcast_to(np.asarray(mu, dtype=float), y.shape)
35    D=y_next-y
36    A=np.column_stack((y**(2*n), mu/(np.abs(y)**(2*q)+epsilon)))
37    coef, *_ = np.linalg.lstsq(A, D, rcond=None)
38    return Calibration(n, q, float(coef[0]), float(abs(coef[1])), radius)
39
40class DelayedTwoModeController:
41    """Gate mode R from a delayed scalar section coordinate; otherwise mode L."""
42    def __init__(self, base_lr, requested_delay, calibration=None):
43        self.base_lr=float(base_lr); self.requested_delay=int(requested_delay)
44        self.calibration=calibration
45        self.delay=self.safe_delay()
46        self.buffer=[0.0]*(self.delay+1)
47    def safe_delay(self):
48        if self.calibration is None or self.calibration.mu_max <= 0:
49            return 0
50        # mu is approximated by delay times base learning rate.
51        return max(0, min(self.requested_delay,
52                          int(self.calibration.mu_max/self.base_lr)))
53    def mode(self, section_value):
54        self.buffer.append(float(section_value))
55        gate=self.buffer[-1-self.delay]
56        return 'R' if gate > 0 else 'L'