// rl_q_distill_lambda_controller.cu — adaptive λ_distill controller. // // Adapts the Q→π distillation strength (λ, slot 486) from the KL // divergence between Q's Boltzmann policy and π's softmax (slot 488). // // Controller logic (asymmetric bounded step): // // if KL > target × TOLERANCE → λ *= RAMP_RATE (Q signal not // landing, pull harder) // if KL < target / TOLERANCE → λ *= DECAY_RATE (aligned, ease off) // else → λ unchanged (dead-zone) // // CRITICAL DESIGN: decay is 100× slower than ramp. This prevents the // self-defeating oscillation where λ ramps up → Q and π align → λ // collapses → they diverge again. The asymmetry means coupling // establishes quickly but persists for thousands of steps, giving // Q time to teach π meaningful action preferences. // // Per `feedback_no_atomicadd`: single-thread kernel, no atomics. // Per `feedback_cpu_is_read_only`: all state in ISV. #define RL_Q_DISTILL_LAMBDA_INDEX 486 #define RL_Q_DISTILL_KL_EMA_INDEX 488 #define RL_Q_DISTILL_KL_TARGET_INDEX 491 // Adaptive controller floors (spec 2026-05-30 Special case Q): // hardcoded `MIN_LAMBDA = 0.05f` floor replaced with adaptive bound // derived from Welford variance on q_distill_kl_ema. Phase 4.5 reduces // KL signal magnitudes which this controller consumes; the "below target" // path fires continuously, decaying λ to MIN. Adaptive bound // `max(0.001, sqrt(var) × 0.05)` lets λ decay to a level proportional to // the actual signal noise rather than the hardcoded 0.05 floor. #define RL_Q_DISTILL_KL_VAR_COUNT_INDEX 618 #define RL_Q_DISTILL_KL_VAR_M2_INDEX 620 #define RL_Q_DISTILL_LAMBDA_MIN_ADAPTIVE_INDEX 639 // MAX_LAMBDA, KL_TOLERANCE, LAMBDA_RAMP_RATE, LAMBDA_DECAY_RATE are now // ISV-driven per the 2026-05-30 clamp-bound extension. ADAPTIVE_MIN_* // remain hardcoded — they're new constants from the noise-floor design // itself per spec exemption ("not clamp bounds, new pattern constants"). #define RL_Q_DISTILL_LAMBDA_MAX_INDEX 650 #define RL_Q_DISTILL_KL_TOLERANCE_INDEX 651 #define RL_Q_DISTILL_LAMBDA_RAMP_RATE_INDEX 652 #define RL_Q_DISTILL_LAMBDA_DECAY_RATE_INDEX 653 #define ADAPTIVE_MIN_ABSOLUTE 0.001f #define ADAPTIVE_MIN_STD_SCALE 0.05f extern "C" __global__ void rl_q_distill_lambda_controller( float* __restrict__ isv ) { if (threadIdx.x != 0 || blockIdx.x != 0) return; const float kl_observed = isv[RL_Q_DISTILL_KL_EMA_INDEX]; const float kl_target = isv[RL_Q_DISTILL_KL_TARGET_INDEX]; float lambda = isv[RL_Q_DISTILL_LAMBDA_INDEX]; if (kl_observed <= 0.0f || kl_target <= 0.0f || lambda <= 0.0f) return; // Adaptive MIN bound (spec 2026-05-30 Special case Q): replaces // hardcoded `MIN_LAMBDA = 0.05f`. Welford sample variance = // M² / (count − 1) when count > 1. Adaptive min scales with signal // std so λ can decay proportional to actual KL noise rather than // pegging at an arbitrary 0.05 floor. const float kl_var_count = isv[RL_Q_DISTILL_KL_VAR_COUNT_INDEX]; const float kl_var = (kl_var_count > 1.0f) ? isv[RL_Q_DISTILL_KL_VAR_M2_INDEX] / (kl_var_count - 1.0f) : 0.0f; const float kl_std = sqrtf(kl_var); const float adaptive_min = fmaxf(ADAPTIVE_MIN_ABSOLUTE, kl_std * ADAPTIVE_MIN_STD_SCALE); isv[RL_Q_DISTILL_LAMBDA_MIN_ADAPTIVE_INDEX] = adaptive_min; const float max_lambda = isv[RL_Q_DISTILL_LAMBDA_MAX_INDEX]; const float kl_tolerance = isv[RL_Q_DISTILL_KL_TOLERANCE_INDEX]; const float lambda_ramp_rate = isv[RL_Q_DISTILL_LAMBDA_RAMP_RATE_INDEX]; const float lambda_decay_rate = isv[RL_Q_DISTILL_LAMBDA_DECAY_RATE_INDEX]; const float upper = kl_target * kl_tolerance; const float lower = kl_target / kl_tolerance; if (kl_observed > upper) { lambda = fminf(max_lambda, lambda * lambda_ramp_rate); } else if (kl_observed < lower) { lambda = fmaxf(adaptive_min, lambda * lambda_decay_rate); } isv[RL_Q_DISTILL_LAMBDA_INDEX] = lambda; }