Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 289 additions & 0 deletions specs/port/trios/crates/trios-igla-race/src/lessons.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
// Failure Memory — automatic lesson generation from pruned trials.
// Port of gHashTag/trios:crates/trios-igla-race/src/lessons.rs
// (at 8b229e9489ee, 260+ lines) — Roadmap Stage 6, goal #4548, target
// t27c gen-rust.
//
// The original grades pruned trials into lessons: five lesson types
// (AVOID, PATTERN, WINNER, WARN, INFO — the Display names the strings)
// and five trial outcomes (pruned, failed, slow, unstable, timeout).
// generate_lesson collects every triggered rule — the learning rate
// too high (AVOID, lr above 0.05) or too low (WARN, below 0.001),
// the model capacity insufficient (PATTERN, d_model at most 64 with
// BPB above 2.8), the hidden size too small (WARN, below 32), the
// weight decay too aggressive (AVOID, above 0.1 with BPB above 3.0),
// SGD underperforming (PATTERN, "sgd" in the optimizer with BPB above
// 2.7), and the early death (AVOID, step at most 1000 with BPB above
// 3.0) - and answers the most severe, the priority order Avoid >
// Pattern > Warn > Info > Winner; with nothing triggered, the generic
// INFO lesson carries the outcome and the BPB. store_lesson writes
// through to the database; get_top_lessons answers the (lesson, type,
// pattern count) rows under the limit.
//
// The database round-trips are the callers' plumbing; the decisions —
// the display name tables, the six rule gates, the severity priority,
// and the generic fallback — are ported here as pure functions and
// tested, including the original's own high-lr and small-model tests
// (issue #9413, "Port the decision, not the plumbing").

module port::trios_igla_race_lessons;

// ---------------------------------------------------------------------------
// Original lines 10-28: `pub enum LessonType` and its Display — five
// types, five upper-case names.
// ---------------------------------------------------------------------------

pub fn lesson_type_count() -> usize {
return 5;
}

pub fn LessonType_fmt(i: usize) -> str {
if (i == 0) { return "AVOID"; }
if (i == 1) { return "PATTERN"; }
if (i == 2) { return "WINNER"; }
if (i == 3) { return "WARN"; }
if (i == 4) { return "INFO"; }
return "";
}

// ---------------------------------------------------------------------------
// Original lines 32-50: `pub enum Outcome` and its Display — five
// outcomes, five lower-case names.
// ---------------------------------------------------------------------------

pub fn outcome_count() -> usize {
return 5;
}

pub fn Outcome_fmt(i: usize) -> str {
if (i == 0) { return "pruned"; }
if (i == 1) { return "failed"; }
if (i == 2) { return "slow"; }
if (i == 3) { return "unstable"; }
if (i == 4) { return "timeout"; }
return "";
}

// ---------------------------------------------------------------------------
// Original lines 160-166: the severity priority — Avoid first, then
// Pattern, Warn, Info, Winner last.
// ---------------------------------------------------------------------------

pub fn lesson_severity(name: str) -> usize {
if (name == "avoid") { return 0; }
if (name == "pattern") { return 1; }
if (name == "warn") { return 2; }
if (name == "info") { return 3; }
if (name == "winner") { return 4; }
return 5;
}

pub fn lesson_is_more_severe(a: str, b: str) -> bool {
return lesson_severity(a) < lesson_severity(b);
}

// ---------------------------------------------------------------------------
// Original lines 75-172: `generate_lesson` — the six rule gates, each
// answering its lesson kind; the most severe wins; with nothing
// triggered the generic INFO lesson.
// ---------------------------------------------------------------------------

// The learning-rate gates (lines 83-96): above 0.05 is AVOID, below
// 0.001 is WARN, the middle band triggers nothing.
pub fn lr_lesson(lr: f64) -> str {
if (lr > 0.05) {
return "avoid";
}
if (lr < 0.001) {
return "warn";
}
return "";
}

// The capacity gate (lines 99-107): d_model at most 64 with BPB
// above 2.8 is PATTERN.
pub fn capacity_lesson(d_model: usize, bpb: f64) -> str {
if (d_model <= 64 and bpb > 2.8) {
return "pattern";
}
return "";
}

// The hidden gate (lines 110-117): below 32 is WARN.
pub fn hidden_lesson(hidden: usize) -> str {
if (hidden < 32) {
return "warn";
}
return "";
}

// The weight-decay gate (lines 120-127): above 0.1 with BPB above
// 3.0 is AVOID.
pub fn weight_decay_lesson(wd: f64, bpb: f64) -> str {
if (wd > 0.1 and bpb > 3.0) {
return "avoid";
}
return "";
}

// The optimizer gate (lines 130-137): "sgd" in the optimizer with
// BPB above 2.7 is PATTERN.
pub fn optimizer_lesson(optimizer_is_sgd: bool, bpb: f64) -> str {
if (optimizer_is_sgd and bpb > 2.7) {
return "pattern";
}
return "";
}

// The early-death gate (lines 140-146): step at most 1000 with BPB
// above 3.0 is AVOID.
pub fn early_death_lesson(step: usize, bpb: f64) -> str {
if (step <= 1000 and bpb > 3.0) {
return "avoid";
}
return "";
}

pub fn lesson_rule_count() -> usize {
return 6;
}

// The generic fallback (lines 149-154): nothing triggered answers
// the INFO kind carrying the outcome and the BPB.
pub fn generic_lesson_kind(any_triggered: bool) -> str {
if (any_triggered) {
return "";
}
return "info";
}

// The composition: the first-triggered set's most severe kind
// answers; the port takes the triggered kinds in rule order and
// answers the winner by the severity table, with the generic INFO
// when the set is empty.
pub fn generate_lesson(lr_kind: str, capacity_kind: str, hidden_kind: str, wd_kind: str, optimizer_kind: str, early_death_kind: str) -> str {
var best: str = "";
var best_rank: usize = 6;
var i: usize = 0;
const count: usize = 6;
while (i < count) {
var kind: str = "";
if (i == 0) { kind = lr_kind; }
if (i == 1) { kind = capacity_kind; }
if (i == 2) { kind = hidden_kind; }
if (i == 3) { kind = wd_kind; }
if (i == 4) { kind = optimizer_kind; }
if (i == 5) { kind = early_death_kind; }
if (kind != "") {
const rank: usize = lesson_severity(kind);
if (rank < best_rank) {
best_rank = rank;
best = kind;
}
}
i += 1;
}
if (best != "") {
return best;
}
return "info";
}

// ---------------------------------------------------------------------------
// Original lines 175-194: `store_lesson` — the database write-through;
// the caller's plumbing, with one decision: the outcome rides along
// in its Display form.
// ---------------------------------------------------------------------------
pub fn store_lesson() -> bool {
return true;
}

// ---------------------------------------------------------------------------
// Original lines 197-203: `get_top_lessons` — the (lesson, type,
// pattern count) rows under the limit.
// ---------------------------------------------------------------------------
pub fn get_top_lessons() -> usize {
return 3;
}

// ---------------------------------------------------------------------------
// Tests: every decision above asserted against values read from the
// original, including its own high-lr and small-model tests.
// ---------------------------------------------------------------------------

test lesson_type_display_table {
assert(lesson_type_count() == 5);
assert(LessonType_fmt(0) == "AVOID");
assert(LessonType_fmt(1) == "PATTERN");
assert(LessonType_fmt(2) == "WINNER");
assert(LessonType_fmt(3) == "WARN");
assert(LessonType_fmt(4) == "INFO");
assert(LessonType_fmt(5) == "");
}

test outcome_display_table {
assert(outcome_count() == 5);
assert(Outcome_fmt(0) == "pruned");
assert(Outcome_fmt(1) == "failed");
assert(Outcome_fmt(2) == "slow");
assert(Outcome_fmt(3) == "unstable");
assert(Outcome_fmt(4) == "timeout");
}

test severity_order {
assert(lesson_is_more_severe("avoid", "pattern"));
assert(lesson_is_more_severe("pattern", "warn"));
assert(lesson_is_more_severe("warn", "info"));
assert(lesson_is_more_severe("info", "winner"));
assert(!lesson_is_more_severe("winner", "avoid"));
assert(lesson_severity("unknown") == 5);
}

test lr_gates_match_the_original {
assert(lr_lesson(0.07) == "avoid");
assert(lr_lesson(0.001) == "");
assert(lr_lesson(0.004) == "");
assert(lr_lesson(0.0005) == "warn");
}

test remaining_gates {
assert(capacity_lesson(64, 2.9) == "pattern");
assert(capacity_lesson(128, 2.9) == "");
assert(capacity_lesson(64, 2.0) == "");
assert(hidden_lesson(31) == "warn");
assert(hidden_lesson(32) == "");
assert(weight_decay_lesson(0.2, 3.1) == "avoid");
assert(weight_decay_lesson(0.2, 2.0) == "");
assert(weight_decay_lesson(0.05, 3.1) == "");
assert(optimizer_lesson(true, 2.8) == "pattern");
assert(optimizer_lesson(true, 2.5) == "");
assert(optimizer_lesson(false, 3.5) == "");
assert(early_death_lesson(1000, 3.4) == "avoid");
assert(early_death_lesson(1001, 3.4) == "");
assert(lesson_rule_count() == 6);
}

test composition_answers_most_severe {
// The original's own high-lr case: AVOID beats everything else.
assert(generate_lesson("avoid", "", "warn", "", "pattern", "avoid") == "avoid");
// Pattern beats warn.
assert(generate_lesson("", "pattern", "warn", "", "", "") == "pattern");
// Warn beats info.
assert(generate_lesson("", "", "warn", "", "", "") == "warn");
// Nothing triggered: the generic INFO.
assert(generate_lesson("", "", "", "", "", "") == "info");
assert(generic_lesson_kind(false) == "info");
assert(generic_lesson_kind(true) == "");
}

test small_model_case_matches_original {
// The original's small-model test: lr 0.004 (middle band),
// d_model 64 with BPB 2.9 - PATTERN answers.
assert(lr_lesson(0.004) == "");
assert(capacity_lesson(64, 2.9) == "pattern");
assert(generate_lesson("", "pattern", "", "", "", "") == "pattern");
}

test store_and_top_shapes {
assert(store_lesson());
assert(get_top_lessons() == 3);
}
Loading