fixup(class-a-audit-batch-4b): invert MIN_HOLD_TEMPERATURE dir_acc → temp mapping
Post-commit kernel-formula audit of `compute_min_hold_penalty` at
trade_physics.cuh:567-577 showed the original `temp = TEMP_MIN +
(TEMP_MAX - TEMP_MIN) × skill` mapping was BACKWARDS for the
WR-plateau scenario this 8-commit chain targets.
Formula recap:
soft_factor = deficit / (deficit + T)
HIGH T → soft_factor → 0 → forgiving (no exit penalty)
LOW T → soft_factor → 1 → sharp (max exit penalty)
The deleted schedule's `START=50 → END=5` therefore meant
"permissive early, strict late" (classic explore-then-exploit), not
"permissive when committed, strict when uncertain" as the
substituted mapping assumed.
WR-plateau scenario: dir_acc ≈ 0.46-0.48 (model committed but
wrong) — the model needs a permissive (HIGH T) exit ramp to bail
out of bad committed directions, not maximum exit penalty (LOW T)
locking it into them.
Inverted the mapping using `confusion = 1 - skill`:
- dir_acc ≈ 0.5 (random / plateau) → confusion=1 → temp=50
(permissive, plateau exit ramp)
- dir_acc → 1.0 (saturated skill) → confusion=0 → temp=5
(strict, force informed commitment)
This preserves the deleted schedule's epoch-0 anchor (T=50
cold-start) while reacting to actual realized skill instead of an
epoch-time anchor.
Files touched (atomic per `feedback_no_partial_refactor`):
- min_hold_temperature_update_kernel.cu — formula + header doctring.
- sp14_isv_slots.rs — slot-doc comment expanded with new mapping
+ fixup-rationale paragraph.
- gpu_aux_trunk.rs — `MinHoldTemperatureUpdateOps` docstring.
- gpu_dqn_trainer.rs — cubin docstring + ISV_TOTAL_DIM doc-comment.
- tests/sp14_oracle_tests.rs — Tests 3 + 4 flipped (high dir_acc
now expects temp=TEMP_MIN; low dir_acc now expects TEMP_MAX);
Tests 1, 2, 5 numerically unchanged (sentinel guard fires before
the mapping; midpoint dir_acc=0.75 has skill=confusion=0.5 so
pre/post-fixup numerics match).
- docs/dqn-wire-up-audit.md — Item 4 entry rewritten with the
fixup rationale + mapping table updated.
Verification:
cargo check -p ml --tests --all-targets PASS
cargo test sp14_audit_4b (lib) 4/4 PASS (slot layout
locks unchanged)
GPU oracle re-run deferred to next L40S smoke (kernel was rebuilt
in-place so cubin contents change; the 5 oracles encode the new
mapping and will exercise the new cubin).
Per `pearl_first_observation_bootstrap.md` (sentinel→target
replace; sentinel anchors unchanged) +
`pearl_symmetric_clamp_audit.md` (bilateral clamp on target_temp
preserved) + `feedback_no_partial_refactor.md`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -973,16 +973,29 @@ impl DdSaturationFloorUpdateOps {
|
||||
/// reads `ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373]` (sp13 fast EMA of
|
||||
/// binary directional accuracy), maps it to a [5, 50] temperature via
|
||||
///
|
||||
/// skill = clamp((short_ema − 0.5) / 0.5, 0, 1)
|
||||
/// temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × skill
|
||||
/// skill = clamp((short_ema − 0.5) / 0.5, 0, 1)
|
||||
/// confusion = 1 − skill
|
||||
/// temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × confusion
|
||||
///
|
||||
/// and slow-EMA-blends into
|
||||
/// `ISV[MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX=460]` with α=0.05.
|
||||
///
|
||||
/// Per `compute_min_hold_penalty` at trade_physics.cuh:575
|
||||
/// (`soft_factor = deficit / (deficit + T)`), HIGH T = forgiving
|
||||
/// (saturates → 0, no exit penalty), LOW T = sharp (saturates → 1,
|
||||
/// max exit penalty). The `confusion = 1 − skill` mapping therefore
|
||||
/// gives the WR-plateau scenario its designed behavior:
|
||||
/// - dir_acc ≈ 0.5 (random / committed-but-wrong) → confusion=1 →
|
||||
/// temp=50 (permissive — exit ramp out of bad commitments)
|
||||
/// - dir_acc → 1.0 (saturated skill) → confusion=0 → temp=5
|
||||
/// (strict — force the model to stay in informed positions)
|
||||
///
|
||||
/// Replaces the deleted epoch-driven anneal schedule
|
||||
/// `T_end + (T_start − T_end) × exp(-epoch / decay)` (formerly
|
||||
/// state_layout.cuh::MIN_HOLD_TEMPERATURE_{START, END, DECAY} +
|
||||
/// training_loop.rs::min_hold_temperature_for_epoch). Decouples
|
||||
/// training_loop.rs::min_hold_temperature_for_epoch). The original
|
||||
/// schedule's `START=50 → END=5` was "permissive early, strict late"
|
||||
/// (classic explore-then-exploit, time-anchored). Decouples
|
||||
/// temperature from epoch number — when WR plateaus, the old
|
||||
/// schedule pinned LOW temp (sharp) at end of training (max
|
||||
/// punishment exactly when most needed forgiveness); the signal-driven
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,18 @@
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
* Class A audit-fix Batch 4-B — adaptive MIN_HOLD_TEMPERATURE producer
|
||||
* (2026-05-08, Item 4).
|
||||
* (2026-05-08, Item 4). Fixup 2026-05-08: inverted the dir_acc → temp
|
||||
* mapping after kernel-formula audit (`compute_min_hold_penalty` at
|
||||
* trade_physics.cuh:575): HIGH T → soft_factor → 0 → forgiving exits;
|
||||
* LOW T → soft_factor → 1 → sharp penalty. The original schedule
|
||||
* `START=50 → END=5` therefore means "permissive early, strict late"
|
||||
* (classic explore-then-exploit). The pre-fixup mapping
|
||||
* `temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × skill` was BACKWARDS for
|
||||
* the WR-plateau scenario this 8-commit chain targets: at plateau
|
||||
* (dir_acc ≈ 0.46-0.48, model committed but wrong) skill = 0 → temp = 5
|
||||
* (sharp) → maximum exit penalty exactly when the model most needs to
|
||||
* escape the bad committed direction. Inverted to `confusion = 1 - skill`
|
||||
* so plateau → temp HIGH (permissive, exit ramp) and skilled →
|
||||
* temp LOW (strict, force commitment).
|
||||
*
|
||||
* Replaces the deleted epoch-driven anneal schedule
|
||||
* `T_end + (T_start - T_end) × exp(-epoch / decay)`
|
||||
@@ -9,8 +21,9 @@
|
||||
* ISV-driven signal-driven temperature derived from the model's
|
||||
* directional-accuracy skill. The temperature controls the
|
||||
* `deficit / (deficit + T)` saturation factor in `compute_min_hold_penalty`
|
||||
* (trade_physics.cuh:575): HIGH T = forgiving (gradient is gentle near
|
||||
* deficit=0); LOW T = sharp (penalty rises steeply at any deficit).
|
||||
* (trade_physics.cuh:575): HIGH T = forgiving (penalty saturates near
|
||||
* 0 — easy to exit early); LOW T = sharp (penalty rises steeply at any
|
||||
* deficit — hold is forced).
|
||||
*
|
||||
* Why this matters (per Class A audit deferred-item batch 4-B):
|
||||
*
|
||||
@@ -20,19 +33,24 @@
|
||||
* plateaus. Maximum punishment is delivered exactly when the model
|
||||
* most needs forgiveness to escape the plateau. The signal-driven
|
||||
* replacement decouples temperature from epoch number: when the
|
||||
* model is committing skillfully (dir_acc > 0.5) → temp HIGH
|
||||
* (permissive); when at random baseline (dir_acc ≈ 0.5) → temp LOW
|
||||
* (sharp commitment pressure to escape the random-guess regime).
|
||||
* model is at random baseline / WR plateau (dir_acc ≈ 0.5,
|
||||
* confusion ≈ 1) → temp HIGH (permissive, exit ramp to escape bad
|
||||
* committed direction); when committing skillfully (dir_acc → 1.0,
|
||||
* confusion → 0) → temp LOW (strict, force commitment because the
|
||||
* commitment is informed).
|
||||
*
|
||||
* Driving signal: dir_acc skill from
|
||||
* `ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373]` — the SP13 fast EMA of
|
||||
* binary directional accuracy (sentinel 0.5 = random baseline).
|
||||
*
|
||||
* skill = clamp((short_ema - 0.5) / 0.5, 0, 1) ∈ [0, 1]
|
||||
* temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × skill
|
||||
* skill = clamp((short_ema - 0.5) / 0.5, 0, 1) ∈ [0, 1]
|
||||
* confusion = 1 - skill ∈ [0, 1]
|
||||
* temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × confusion
|
||||
*
|
||||
* - skill=0 (no directional skill, dir_acc ≤ 0.5) → temp=TEMP_MIN=5
|
||||
* - skill=1 (saturated dir_acc=1.0) → temp=TEMP_MAX=50
|
||||
* - confusion=1 (no directional skill, dir_acc ≤ 0.5) → temp=TEMP_MAX=50
|
||||
* (permissive — give the model an exit ramp out of the plateau)
|
||||
* - confusion=0 (saturated dir_acc=1.0) → temp=TEMP_MIN=5
|
||||
* (strict — force commitment because it's informed)
|
||||
*
|
||||
* Cold-start fallback: if `isv[AUX_DIR_ACC_SHORT_EMA_INDEX]` is at
|
||||
* sentinel (within EPS of 0.5) — i.e. fold reset just fired and no
|
||||
@@ -52,7 +70,8 @@
|
||||
* MIN_HOLD_TEMPERATURE_FALLBACK=50.0). Otherwise:
|
||||
*
|
||||
* skill = max(0, min(1, (short_ema - 0.5) / 0.5))
|
||||
* target_temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × skill
|
||||
* confusion = 1 - skill
|
||||
* target_temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × confusion
|
||||
* bilateral clamp to [TEMP_MIN, TEMP_MAX]
|
||||
*
|
||||
* Pearl-A bootstrap: if current is at sentinel (within EPS of 50.0),
|
||||
@@ -125,14 +144,34 @@ void min_hold_temperature_update(
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skill mapping: clamp((short_ema - 0.5) / 0.5, 0, 1).
|
||||
* - dir_acc=0.5 (random) → skill=0 → temp=temp_min=5 (sharp)
|
||||
* - dir_acc=1.0 (saturated) → skill=1 → temp=temp_max=50 (forgiving)
|
||||
* - dir_acc<0.5 (worse than random) → skill clamped to 0 → temp=5 */
|
||||
/* Confusion mapping: confusion = 1 - clamp((short_ema - 0.5)/0.5, 0, 1).
|
||||
* Inverted from the original "skill → temp" mapping after
|
||||
* trade_physics.cuh:575 audit revealed HIGH T = permissive
|
||||
* (deficit/(deficit+T) saturates to 0 → no exit penalty), LOW T =
|
||||
* strict (saturates to 1 → max exit penalty). The WR-plateau
|
||||
* scenario this 8-commit chain targets is "model committed but
|
||||
* wrong at dir_acc ≈ 0.46-0.48"; the model needs the permissive
|
||||
* branch (HIGH T) to escape the bad committed direction, which
|
||||
* means HIGH confusion → HIGH temp.
|
||||
*
|
||||
* - dir_acc=0.5 (random / plateau) → skill=0 → confusion=1 →
|
||||
* temp=temp_max=50 (permissive — exit ramp)
|
||||
* - dir_acc=1.0 (saturated skill) → skill=1 → confusion=0 →
|
||||
* temp=temp_min=5 (strict — force informed commitment)
|
||||
* - dir_acc<0.5 (worse than random) → skill clamped to 0 →
|
||||
* confusion=1 → temp=50 (max permissive)
|
||||
*
|
||||
* Note: the dir_acc==0.5 sentinel-guard fires before this branch
|
||||
* (returns without writing); the explicit `confusion=1 → temp=50`
|
||||
* arm above only fires for dir_acc < 0.5 - EPS (non-sentinel
|
||||
* worse-than-random) or when the upstream EMA has moved off
|
||||
* sentinel toward 0.5+ε. This matches the deleted schedule's
|
||||
* epoch-0 anchor (T=50). */
|
||||
float skill = (dir_acc_short - sentinel_dir_acc) / sentinel_dir_acc;
|
||||
skill = fmaxf(0.0f, fminf(1.0f, skill));
|
||||
const float confusion = 1.0f - skill;
|
||||
|
||||
float target_temp = temp_min + (temp_max - temp_min) * skill;
|
||||
float target_temp = temp_min + (temp_max - temp_min) * confusion;
|
||||
|
||||
/* Bilateral clamp per `pearl_symmetric_clamp_audit.md`. Defends
|
||||
* against arithmetic round-off pushing target outside the
|
||||
|
||||
@@ -344,29 +344,48 @@ pub const SP14_AUDIT_4B_PLAN_FLOOR_SLOT_END: usize = 460;
|
||||
// temperature derived from the model's directional accuracy skill. The
|
||||
// soft-saturation factor `deficit / (deficit + T)` in
|
||||
// `compute_min_hold_penalty` (trade_physics.cuh:575) controls how
|
||||
// sharply the min-hold penalty bites: HIGH T = forgiving (gradient is
|
||||
// gentle near deficit=0); LOW T = sharp (penalty rises steeply at any
|
||||
// deficit).
|
||||
// sharply the min-hold penalty bites: HIGH T → soft_factor → 0 →
|
||||
// forgiving (no exit penalty); LOW T → soft_factor → 1 → sharp
|
||||
// (max exit penalty). The deleted schedule's `START=50 → END=5`
|
||||
// therefore meant "permissive early, strict late" — classic
|
||||
// explore-then-exploit.
|
||||
//
|
||||
// Driving signal: dir_acc skill — `clamp((short_ema - 0.5) / 0.5, 0, 1)`
|
||||
// from ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373] (sp13 directional-accuracy
|
||||
// fast EMA, sentinel 0.5 = random-baseline). When the model is
|
||||
// committing skillfully (dir_acc > 0.5) → temp HIGH (permissive — the
|
||||
// model's exits are informed, no need to penalize sharply). When the
|
||||
// model is at random baseline (dir_acc ≈ 0.5) → temp LOW (sharp — the
|
||||
// model needs commitment pressure to escape the random-guess regime).
|
||||
// fast EMA, sentinel 0.5 = random-baseline). The mapping uses
|
||||
// `confusion = 1 - skill` so the model gets a permissive exit ramp
|
||||
// when at the WR-plateau (committed-but-wrong) regime and a strict
|
||||
// hold-enforcement when its commitment is informed:
|
||||
// - dir_acc ≈ 0.5 (random / WR-plateau) → confusion=1 → temp HIGH
|
||||
// (permissive — let the model bail out of the bad committed
|
||||
// direction; this is the WR-plateau exit ramp)
|
||||
// - dir_acc → 1.0 (saturated skill) → confusion=0 → temp LOW
|
||||
// (strict — force the model to stay in informed positions)
|
||||
//
|
||||
// Mapping: temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × skill, where:
|
||||
// - skill=0 (no directional skill, dir_acc ≤ 0.5) → temp=TEMP_MIN=5
|
||||
// - skill=1 (saturated dir_acc=1.0) → temp=TEMP_MAX=50
|
||||
// Mapping: temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × confusion, where:
|
||||
// - confusion=1 (dir_acc ≤ 0.5) → temp=TEMP_MAX=50 (permissive)
|
||||
// - confusion=0 (dir_acc=1.0) → temp=TEMP_MIN=5 (strict)
|
||||
//
|
||||
// Opposite of the deleted epoch-driven schedule which pinned LOW temp
|
||||
// at end of training (T_end=5). The epoch schedule assumes the model
|
||||
// gets MORE skilled with training; when WR plateaus this assumption
|
||||
// fails and the schedule becomes maximally punishing exactly when the
|
||||
// model needs forgiveness to escape the plateau. The signal-driven
|
||||
// version reacts to actual realized skill, decoupling temperature from
|
||||
// epoch number.
|
||||
// This preserves the deleted schedule's MAX-at-cold-start anchor
|
||||
// (T=50 at epoch 0) while decoupling the post-cold trajectory from
|
||||
// the epoch number. Under the old schedule, end-of-training pinned
|
||||
// LOW temp (T_end=5), assuming the model would be more skilled by
|
||||
// then; when WR plateaus this assumption fails and the schedule
|
||||
// becomes maximally punishing exactly when the model needs forgiveness
|
||||
// to escape the plateau. The signal-driven, confusion-mapped
|
||||
// replacement reacts to actual realized skill — the model only sees
|
||||
// strict enforcement when its commitments are informed, never as a
|
||||
// pre-set time anchor.
|
||||
//
|
||||
// FIXUP 2026-05-08: original mapping was the inverted form
|
||||
// (`temp = TEMP_MIN + (TEMP_MAX - TEMP_MIN) × skill`), which
|
||||
// punished plateau exits with max sharpness. After kernel-formula
|
||||
// audit (`compute_min_hold_penalty` — HIGH T = forgiving) and
|
||||
// matching the deleted schedule's "permissive early" intent, the
|
||||
// mapping was inverted to use confusion. Numerical fixed points:
|
||||
// dir_acc=0.5 (sentinel) → guard returns; dir_acc=0.75 → temp=27.5
|
||||
// (midpoint, identical pre/post fixup); dir_acc=1.0 → temp=5;
|
||||
// dir_acc<0.5 → temp=50.
|
||||
//
|
||||
// Producer kernel: `min_hold_temperature_update_kernel.cu` (per-epoch
|
||||
// boundary cadence). Consumer: `experience_kernels.cu` reads ISV[460]
|
||||
|
||||
@@ -1819,9 +1819,11 @@ mod sp14_audit_4b_min_hold_temperature_gpu {
|
||||
|
||||
/// Test 1 — Pearl-A first-observation bootstrap.
|
||||
///
|
||||
/// Seed dir_acc EMA at 0.75 (skill = (0.75 − 0.5)/0.5 = 0.5 → temp =
|
||||
/// 5 + 45 × 0.5 = 27.5). Pearl-A: temp at sentinel 50.0 → REPLACE
|
||||
/// directly with 27.5.
|
||||
/// Seed dir_acc EMA at 0.75 (skill = (0.75 − 0.5)/0.5 = 0.5;
|
||||
/// confusion = 1 − skill = 0.5 → temp = 5 + 45 × 0.5 = 27.5).
|
||||
/// Pearl-A: temp at sentinel 50.0 → REPLACE directly with 27.5.
|
||||
/// Midpoint: numeric result is identical to a pre-fixup
|
||||
/// "skill→temp" mapping; only the semantic interpretation flipped.
|
||||
#[test]
|
||||
#[ignore = "requires GPU"]
|
||||
fn min_hold_temp_pearl_a_bootstrap() {
|
||||
@@ -1850,7 +1852,9 @@ mod sp14_audit_4b_min_hold_temperature_gpu {
|
||||
let result = isv_buf.read_all();
|
||||
let temp = result[MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX];
|
||||
|
||||
// skill = (0.75 − 0.5) / 0.5 = 0.5; target = 5 + 45 × 0.5 = 27.5.
|
||||
// skill = (0.75 − 0.5) / 0.5 = 0.5; confusion = 1 − skill = 0.5;
|
||||
// target = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × confusion
|
||||
// = 5 + 45 × 0.5 = 27.5.
|
||||
let expected = 27.5_f32;
|
||||
assert!(
|
||||
(temp - expected).abs() < 1e-4,
|
||||
@@ -1895,20 +1899,24 @@ mod sp14_audit_4b_min_hold_temperature_gpu {
|
||||
);
|
||||
}
|
||||
|
||||
/// Test 3 — High dir_acc skill → temp HIGH (permissive).
|
||||
/// Test 3 — High dir_acc skill → temp LOW (strict, force informed
|
||||
/// commitment).
|
||||
///
|
||||
/// Seed dir_acc=1.0 (perfect skill → skill=1.0 → temp=5+45=50).
|
||||
/// Verifies the spec mapping "model commits skillfully → temp HIGH".
|
||||
/// Pearl-A REPLACE on sentinel.
|
||||
/// Seed dir_acc=1.0 (perfect skill → skill=1.0 → confusion=0 →
|
||||
/// temp=TEMP_MIN=5). Verifies the inverted (post-fixup) semantic
|
||||
/// "skilled commitment → temp LOW (strict)" — when the model is
|
||||
/// committing skillfully there's no plateau-escape need, so the
|
||||
/// kernel pins temp to the strict end (max exit penalty enforces
|
||||
/// the informed commitment). Pearl-A REPLACE on sentinel.
|
||||
#[test]
|
||||
#[ignore = "requires GPU"]
|
||||
fn min_hold_temp_high_dir_acc_yields_high_temp() {
|
||||
fn min_hold_temp_high_dir_acc_yields_low_temp() {
|
||||
let stream = make_stream();
|
||||
let kernel = load_kernel(&stream);
|
||||
|
||||
const ISV_DIM: usize = 1024;
|
||||
let mut isv = vec![0.0_f32; ISV_DIM];
|
||||
// Saturated dir_acc → maximum skill → temp at MAX (permissive).
|
||||
// Saturated dir_acc → maximum skill → confusion=0 → temp at MIN (strict).
|
||||
isv[AUX_DIR_ACC_SHORT_EMA_INDEX] = 1.0;
|
||||
isv[MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX] = SENTINEL_MIN_HOLD_TEMPERATURE;
|
||||
|
||||
@@ -1927,27 +1935,33 @@ mod sp14_audit_4b_min_hold_temperature_gpu {
|
||||
let result = isv_buf.read_all();
|
||||
let temp = result[MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX];
|
||||
|
||||
// skill=1.0 → temp = TEMP_MAX = 50.0.
|
||||
// skill=1.0 → confusion=0 → temp = TEMP_MIN = 5.0.
|
||||
assert!(
|
||||
(temp - MIN_HOLD_TEMPERATURE_MAX).abs() < 1e-4,
|
||||
"High dir_acc must drive temp HIGH (permissive); expected {}, got {temp}",
|
||||
MIN_HOLD_TEMPERATURE_MAX
|
||||
(temp - MIN_HOLD_TEMPERATURE_MIN).abs() < 1e-4,
|
||||
"High dir_acc must drive temp LOW (strict — informed commitment); \
|
||||
expected {}, got {temp}",
|
||||
MIN_HOLD_TEMPERATURE_MIN
|
||||
);
|
||||
}
|
||||
|
||||
/// Test 4 — Low dir_acc (worse than random) → temp LOW (sharp).
|
||||
/// Test 4 — Low dir_acc (worse than random / WR-plateau scenario)
|
||||
/// → temp HIGH (permissive — exit ramp).
|
||||
///
|
||||
/// Seed dir_acc=0.4 (worse than 0.5 random baseline). skill clamps
|
||||
/// to 0 → temp = TEMP_MIN = 5 (sharp commitment pressure).
|
||||
/// to 0 → confusion=1 → temp=TEMP_MAX=50 (permissive). This is the
|
||||
/// canonical WR-plateau scenario this 8-commit chain targets:
|
||||
/// the model is committed but wrong, and the kernel must give it
|
||||
/// an exit ramp (HIGH T = soft exit penalty) rather than
|
||||
/// punishing exits and locking it into the bad direction.
|
||||
#[test]
|
||||
#[ignore = "requires GPU"]
|
||||
fn min_hold_temp_low_dir_acc_yields_low_temp() {
|
||||
fn min_hold_temp_low_dir_acc_yields_high_temp() {
|
||||
let stream = make_stream();
|
||||
let kernel = load_kernel(&stream);
|
||||
|
||||
const ISV_DIM: usize = 1024;
|
||||
let mut isv = vec![0.0_f32; ISV_DIM];
|
||||
// dir_acc=0.4 (worse than random — skill clamps to 0).
|
||||
// dir_acc=0.4 (worse than random — skill clamps to 0, confusion=1).
|
||||
isv[AUX_DIR_ACC_SHORT_EMA_INDEX] = 0.4;
|
||||
isv[MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX] = SENTINEL_MIN_HOLD_TEMPERATURE;
|
||||
|
||||
@@ -1966,20 +1980,23 @@ mod sp14_audit_4b_min_hold_temperature_gpu {
|
||||
let result = isv_buf.read_all();
|
||||
let temp = result[MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX];
|
||||
|
||||
// skill clamped to 0 → target = TEMP_MIN. Pearl-A REPLACE on
|
||||
// sentinel → final temp = TEMP_MIN.
|
||||
// skill clamped to 0 → confusion=1 → target = TEMP_MAX. Pearl-A
|
||||
// REPLACE on sentinel → final temp = TEMP_MAX.
|
||||
assert!(
|
||||
(temp - MIN_HOLD_TEMPERATURE_MIN).abs() < 1e-4,
|
||||
"Low dir_acc must drive temp LOW (sharp); expected {}, got {temp}",
|
||||
MIN_HOLD_TEMPERATURE_MIN
|
||||
(temp - MIN_HOLD_TEMPERATURE_MAX).abs() < 1e-4,
|
||||
"Low dir_acc must drive temp HIGH (permissive — plateau exit ramp); \
|
||||
expected {}, got {temp}",
|
||||
MIN_HOLD_TEMPERATURE_MAX
|
||||
);
|
||||
}
|
||||
|
||||
/// Test 5 — Mid-cadence EMA blend after bootstrap.
|
||||
///
|
||||
/// Seed temp at 30.0 (NOT sentinel — past bootstrap), dir_acc=0.75
|
||||
/// (target_temp = 27.5). Blended = (1 − 0.05) × 30.0 + 0.05 × 27.5
|
||||
/// = 28.5 + 1.375 = 29.875.
|
||||
/// (skill=0.5, confusion=0.5, target_temp = 5+45×0.5 = 27.5).
|
||||
/// Blended = (1 − 0.05) × 30.0 + 0.05 × 27.5 = 28.5 + 1.375 = 29.875.
|
||||
/// Midpoint: numeric result is identical to a pre-fixup
|
||||
/// "skill→temp" mapping; only the semantic interpretation flipped.
|
||||
#[test]
|
||||
#[ignore = "requires GPU"]
|
||||
fn min_hold_temp_mid_cadence_ema_after_bootstrap() {
|
||||
|
||||
@@ -45,19 +45,20 @@ T(e) = T_end + (T_start - T_end) × exp(-e / decay)
|
||||
(formerly `state_layout.cuh::MIN_HOLD_TEMPERATURE_{START=50, END=5, DECAY=20}` + `training_loop.rs::min_hold_temperature_for_epoch`) with an ISV-driven signal-driven temperature derived from the model's directional accuracy skill. Producer kernel: `min_hold_temperature_update_kernel.cu` (NEW file, single-thread cold-path, per-epoch boundary launch).
|
||||
|
||||
**Driving signal — dir_acc skill (NOT dir_entropy_deficit):**
|
||||
The audit-spec called for `dir_entropy_deficit`. On verification, no `dir_entropy` slot exists in any ISV layout (only `val_dir_entropy` on CPU side via HEALTH_DIAG snapshots, and `ENTROPY_DIST_REF_INDEX=429` allocated but unused). The cleanest available signal is `ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373]` — the SP13 fast EMA of binary directional accuracy (sentinel 0.5 = random baseline). This serves as a parallel proxy for "model commits" via dir_acc skill:
|
||||
The audit-spec called for `dir_entropy_deficit`. On verification, no `dir_entropy` slot exists in any ISV layout (only `val_dir_entropy` on CPU side via HEALTH_DIAG snapshots, and `ENTROPY_DIST_REF_INDEX=429` allocated but unused). The cleanest available signal is `ISV[AUX_DIR_ACC_SHORT_EMA_INDEX=373]` — the SP13 fast EMA of binary directional accuracy (sentinel 0.5 = random baseline). This serves as a parallel proxy for "model is uncertain" via dir_acc confusion:
|
||||
```
|
||||
skill = clamp((short_ema − 0.5) / 0.5, 0, 1) ∈ [0, 1]
|
||||
temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × skill
|
||||
skill = clamp((short_ema − 0.5) / 0.5, 0, 1) ∈ [0, 1]
|
||||
confusion = 1 − skill ∈ [0, 1]
|
||||
temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × confusion
|
||||
```
|
||||
- skill=0 (no skill, dir_acc ≤ 0.5) → temp=TEMP_MIN=5 (sharp commitment pressure)
|
||||
- skill=1 (saturated dir_acc=1.0) → temp=TEMP_MAX=50 (permissive — informed exits, no need to penalize sharply)
|
||||
- confusion=1 (no skill, dir_acc ≤ 0.5) → temp=TEMP_MAX=50 (permissive — exit ramp out of plateau / random-baseline commitments)
|
||||
- confusion=0 (saturated dir_acc=1.0) → temp=TEMP_MIN=5 (strict — force the model to stay in informed positions)
|
||||
|
||||
This matches the audit-spec semantic ("model commits → temp HIGH"). Skilled directional commitment IS the signal — using `dir_entropy_deficit` would have required either a new producer (out-of-scope) OR using the unused `ENTROPY_DIST_REF` slot which has no documented producer plan.
|
||||
The mapping uses `confusion` (not raw `skill`) because the consumer formula `compute_min_hold_penalty` (`soft_factor = deficit / (deficit + T)` at trade_physics.cuh:575) makes HIGH T = forgiving (saturation factor → 0, no exit penalty) and LOW T = sharp (saturation factor → 1, max exit penalty). The deleted schedule's `START=50 → END=5` therefore meant "permissive early, strict late" (classic explore-then-exploit). Mapping `skill → temp` directly would be backwards: at the WR-plateau scenario this 8-commit chain targets (dir_acc ≈ 0.46-0.48, model committed but wrong), `skill=0` → `temp=5` would lock the model into bad committed directions exactly when an exit ramp is needed. The `confusion` inversion preserves the deleted schedule's epoch-0 anchor (T=50 cold-start) and gives the plateau scenario its designed exit ramp.
|
||||
|
||||
**Slot allocation:** ISV[460..461) — `MIN_HOLD_TEMPERATURE_ADAPTIVE_INDEX=460` (sentinel 50.0, bounds [5, 50], α=0.05). Locked by `sp14_audit_4b_min_hold_temperature_slot_layout_locked` test in `sp14_isv_slots.rs`.
|
||||
|
||||
**Why this matters (per Class A audit):** The epoch-driven schedule pinned LOW temp (T=5, sharp) at end of training. This works ONLY if the model gets more skilled with training — exactly the assumption that fails when WR plateaus. Maximum punishment is delivered exactly when the model most needs forgiveness to escape the plateau. The signal-driven replacement decouples temperature from epoch number: when the model is at random baseline (≈46-48% WR), dir_acc ≈ 0.5 → skill ≈ 0 → temp=5 (sharp), forcing commitment. When the model breaks through to dir_acc > 0.55, skill rises and temp climbs (permissive — let the model exit short trades on real signals).
|
||||
**Why this matters (per Class A audit):** The epoch-driven schedule pinned LOW temp (T=5, sharp) at end of training. This works ONLY if the model gets more skilled with training — exactly the assumption that fails when WR plateaus. Maximum punishment is delivered exactly when the model most needs forgiveness to escape the plateau. The signal-driven replacement decouples temperature from epoch number and inverts the dir_acc → temp coupling: when the model is at random baseline (≈46-48% WR), dir_acc ≈ 0.5 → confusion ≈ 1 → temp=50 (permissive, exit ramp out of bad committed direction). When the model breaks through to dir_acc → 1.0, confusion → 0 and temp drops toward 5 (strict — force commitment because it's now informed). This is "permissive when uncertain, strict when informed" — the opposite of the broken epoch-anchored schedule.
|
||||
|
||||
**DELETED atomically:** Per `feedback_no_legacy_aliases.md` + `feedback_no_partial_refactor.md`:
|
||||
1. `state_layout.cuh::MIN_HOLD_TEMPERATURE_{START, END, DECAY}` #defines.
|
||||
@@ -80,7 +81,7 @@ This matches the audit-spec semantic ("model commits → temp HIGH"). Skilled di
|
||||
- `crates/ml/build.rs` — kernel added to compile list.
|
||||
- `crates/ml/src/trainers/dqn/state_reset_registry.rs` — `sp14_audit_4b_min_hold_temperature_adaptive` registry entry.
|
||||
- `crates/ml/src/trainers/dqn/trainer/training_loop.rs` — DELETED `min_hold_temperature_for_epoch` body, DELETED both call sites, ADDED `read_min_hold_temperature_from_isv` helper, ADDED launcher invocation per-epoch, ADDED FoldReset dispatch arm.
|
||||
- `crates/ml/tests/sp14_oracle_tests.rs` — 5 GPU oracle tests (Pearl-A bootstrap; sentinel dir_acc preserves ISV; high dir_acc → temp HIGH; low dir_acc → temp LOW; mid-cadence EMA after bootstrap).
|
||||
- `crates/ml/tests/sp14_oracle_tests.rs` — 5 GPU oracle tests (Pearl-A bootstrap; sentinel dir_acc preserves ISV; high dir_acc → temp LOW [strict, informed commitment]; low dir_acc → temp HIGH [permissive, plateau exit ramp]; mid-cadence EMA after bootstrap).
|
||||
|
||||
### Verification
|
||||
|
||||
@@ -100,11 +101,13 @@ ISV_TOTAL_DIM: 459 → 461.
|
||||
The audit-spec was correct on Item 3 (plan_threshold floor) — line 44, formula `fmaxf(0.1f, 0.5f * ema)`, `0.1f` is the absolute floor and `0.5f * ema` is the relative one. Picked Design Y (inline) per the audit's recommendation.
|
||||
|
||||
The audit-spec was **partially wrong on Item 4** in two ways:
|
||||
1. **Driving signal:** `dir_entropy_deficit` does not exist as an ISV signal. No `dir_entropy` slot exists, only `val_dir_entropy` on CPU-side HEALTH_DIAG snapshots (post-validation, not consumable by GPU producers). `ENTROPY_DIST_REF_INDEX=429` is allocated but unused. Substituted `dir_acc skill` from `AUX_DIR_ACC_SHORT_EMA_INDEX=373` as the closest semantically-equivalent signal (parallel to entropy deficit: high skill = committed, low skill = uncertain). Mapping preserves the spec's intent ("model commits → temp HIGH").
|
||||
1. **Driving signal:** `dir_entropy_deficit` does not exist as an ISV signal. No `dir_entropy` slot exists, only `val_dir_entropy` on CPU-side HEALTH_DIAG snapshots (post-validation, not consumable by GPU producers). `ENTROPY_DIST_REF_INDEX=429` is allocated but unused. Substituted `dir_acc skill` from `AUX_DIR_ACC_SHORT_EMA_INDEX=373` as the closest semantically-equivalent signal (parallel to entropy deficit: high skill = committed, low skill = uncertain).
|
||||
2. **Slot allocation:** Audit-spec proposed slot 460 for MIN_HOLD_TEMPERATURE — confirmed and used.
|
||||
|
||||
This is the **7th time** the audit doc has been wrong (per the 6-prior-errors counter from the prompt). The verification heuristic in the prompt — "verify everything before editing" — caught this and prevented a wasted producer-kernel build (entropy slot would have errored at compile time).
|
||||
|
||||
**Mapping-direction fixup (2026-05-08):** The initial wiring of this fix used `temp = TEMP_MIN + (TEMP_MAX − TEMP_MIN) × skill` based on the audit-spec phrase "model commits → temp HIGH". On post-commit review the kernel formula `compute_min_hold_penalty` was re-read at trade_physics.cuh:567-577: `soft_factor = deficit / (deficit + T)`. HIGH T → soft_factor → 0 → forgiving (no exit penalty); LOW T → soft_factor → 1 → sharp (max exit penalty). The deleted schedule's `START=50 → END=5` therefore meant "permissive early, strict late" (classic explore-then-exploit), not "permissive when committed, strict when uncertain". The original `skill → temp` mapping was backwards for the WR-plateau scenario this 8-commit chain targets: at plateau (dir_acc ≈ 0.46-0.48, committed but wrong), `skill=0 → temp=5` would have locked the model into bad committed directions with maximum exit penalty — exactly when an exit ramp is needed. Inverted to `confusion = 1 − skill` so plateau → temp HIGH (permissive exit ramp) and saturated skill → temp LOW (strict, force informed commitment). Tests 3 and 4 flipped accordingly; tests 1, 2, 5 (sentinel guard, midpoint, midpoint EMA) are numerically unchanged because their dir_acc inputs sit at 0.5/0.75 fixed points where `skill ≈ confusion` or the guard fires before the mapping.
|
||||
|
||||
Reading directly from `compute_min_hold_penalty` in `trade_physics.cuh:567-577` confirmed the consumer signature takes `min_hold_temperature` as a scalar parameter. Migrating to direct ISV read inside the kernel was an option, but kept the kernel signature stable to preserve the existing test scaffold (`sp12_reward_math_test_kernel.cu` at line 36 also uses the scalar arg). The training loop seeds the scalar from ISV[460] per-epoch — same indirection pattern as `effective_min_hold_target` (Class A P0-C, slot 451) which also maintains scalar-passed cold-start fallback alongside ISV consumer logic.
|
||||
|
||||
## 2026-05-08 — Class A audit-fix Batch 4-A: DD saturation floor adaptive + legacy DD path Case A
|
||||
|
||||
Reference in New Issue
Block a user