From c2769e6b18e299b2267662b7c48364e8c220f631 Mon Sep 17 00:00:00 2001 From: YurenHao0426 Date: Tue, 21 Jul 2026 07:01:08 -0500 Subject: fix: calibrate feedback on the pre-update state --- sdil/core.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sdil/core.py b/sdil/core.py index 048408e..3115d46 100644 --- a/sdil/core.py +++ b/sdil/core.py @@ -338,6 +338,16 @@ def sdil_step(net, x, y, y_onehot, cfg, step, prev_error=None): r_list.append(r) a_list.append(a) + # Calibrate A against the same network state that produced r and c. + # Measuring q after mutating W would pair a post-update causal target + # with a pre-update prediction, introducing an avoidable stale-target + # error (especially at large learning rates). + did_pert = cfg.learn_A and (step % cfg.pert_every == 0) + qs = None + if did_pert: + qs = node_perturbation_targets( + net, x, y, sigma=cfg.pert_sigma, n_dirs=cfg.pert_ndirs) + # ================= forward weight updates ================= # hidden layers: three-factor local rule for l in range(net.L - 1): @@ -362,10 +372,7 @@ def sdil_step(net, x, y, y_onehot, cfg, step, prev_error=None): _apply(net, net.L - 1, dWL, dbL, cfg) # ================= apical vectorizer A via node perturbation ======= - did_pert = False - if cfg.learn_A and (step % cfg.pert_every == 0): - did_pert = True - qs = node_perturbation_targets(net, x, y, sigma=cfg.pert_sigma, n_dirs=cfg.pert_ndirs) + if did_pert: for l in range(net.L - 1): dA = (qs[l] - r_list[l]).t() @ c / B # (n_l, n_classes) net.A[l] += cfg.eta_A * dA -- cgit v1.2.3