IMM Stale-Feedback Detector / mini_control.py

Failed on benchmark

Raw ⬇ ZIP
 1import json, math
 2import numpy as np
 3
 4# Scalar quadratic: f(x)=x^2/2. A worker applies a gradient from d steps ago.
 5# The IMM observes x with Gaussian noise and controls the learning rate after alarm.
 6def detect(obs, q=.002, r=.01, D=4, persistence=.985, threshold=.01):
 7    m=D+1; T=np.full((m,m),(1-persistence)/(m-1)); np.fill_diagonal(T,persistence)
 8    pi=np.ones(m)/m; hist=[0.]*(D+1); alarms=0
 9    out=[]
10    for y in obs:
11        hist.append(hist[-1]); hist=hist[-D-1:]
12        c=T.T@pi; means=np.asarray(hist[::-1])
13        ll=-.5*((y-means)**2/r+math.log(2*math.pi*r))
14        u=c*np.exp(ll-ll.max()); pi=u/u.sum(); alarms=alarms+1 if pi[0]<=threshold else 0
15        out.append((pi.copy(), alarms>=3))
16    return out
17
18def run(lr, delay, controlled, seed):
19    rng=np.random.default_rng(seed); n=250; x=[2.0]*(delay+1); ys=[]
20    # First 30 clean observations calibrate the delay monitor; attack thereafter.
21    for t in range(n):
22        d=0 if t<30 else delay
23        g=x[-1-d]
24        x.append(x[-1]-lr*g if not controlled else x[-1])
25        ys.append(x[-1]+rng.normal(0,.1))
26    decisions=detect(ys)
27    # Re-run dynamics with detector decisions causally.
28    x=[2.0]*(delay+1); lr_now=lr; alarm_step=None
29    for t in range(n):
30        if controlled and decisions[t][1] and alarm_step is None:
31            alarm_step=t+1; lr_now=lr/4
32        d=0 if t<30 else delay
33        x.append(x[-1]-lr_now*x[-1-d])
34    return float(.5*x[-1]**2), alarm_step, max(abs(v) for v in x)
35
36def main():
37    rows=[]
38    for lr in (.8,1.0,1.2):
39        b=[run(lr,2,False,s) for s in range(20)]
40        i=[run(lr,2,True,s) for s in range(20)]
41        rows.append({'lr':lr,'baseline_final_loss':float(np.median([z[0] for z in b])),
42                     'imm_final_loss':float(np.median([z[0] for z in i])),
43                     'baseline_max_abs_x':float(np.median([z[2] for z in b])),
44                     'imm_max_abs_x':float(np.median([z[2] for z in i])),
45                     'imm_alarm_median':float(np.median([z[1] for z in i if z[1] is not None]))})
46    print(json.dumps({'quadratic_delayed_feedback':rows,'steps':250,'attack_starts':30,'replicates':20},indent=2))
47if __name__=='__main__': main()