t27.aiРусский

Pick the largest

You will learn

How argmax names a class by comparing bits alone, and which rule settles a tie.

Recording pending: it waits on tri test and tri mutate plant from gHashTag/t27#7400, the two commands the recording runs, and until then the widget below is a placeholder that shows no run. A classifier ends by naming the class with the largest score. gft_argmax4.t27 does it with no arithmetic: gt ranks negatives below zero and zero below positives, then compares the low 16 bits, which grow with the size. Its header says the lowest index wins ties, strict >. The browser skips all 4 tests because its runner does not know assert_eq yet; the native t27c runs all 4, all pass, none vacuous. The recording turns > into >= for two positive scores, and exactly one test fails, tie_low: four scores of 1.0 now pick index 3, not 0. No test lets two negative scores decide the answer: reversing their comparison fails nothing. Every byte in the recording was printed by the command; only the typing is staged.

Try it

In the recording, find the changed line and the test that fails; then in the spec frame find the line that compares two negative scores and the one test that reaches it.

Open the interactive lesson →

gft_argmax4.t27: recording pending
gft_argmax4.t27: recording pending ↗

Recording pending: waits on tri test and tri mutate plant from gHashTag/t27#7400. Until then this page is a placeholder and shows no run.

specs/ternary/gft_argmax4.t27

module GftArgmax4;
// #1764 + GF-T: a classification head over four signed GF-T16 logits. Returns the
// INDEX {0,1,2,3} of the maximum real value (lowest index wins ties, strict >).
// This is the final stage of a GF-T classifier: a GF-T MLP (gft_mlp3) emits a
// vector of logits, argmax picks the predicted class. Comparison is exact on the
// real values -- no arithmetic, just a total order on the GF-T encoding.
//
// A signed GF-T16 value in u32 is [sign(bit16)][offset(7)][mant(9)]; the low 16
// bits (offset<<9 | mant) are MONOTONIC in the real magnitude, and raw 0 is the
// only zero. So the real order is: negatives < zero < positives; within positives
// larger magnitude bits = larger; within negatives larger magnitude bits = smaller.
//
// Inputs: x0..x3 signed GF-T16 (u32). Output: index u8 in {0,1,2,3}.

// sign category: zero->1, positive->2, negative->0 (higher category = greater).
fn category(a: u32) -> u8 {
    if (a == 0) { return 1; }
    if ((a >> 16) == 0) { return 2; }
    return 0;
}
// gt(a,b) = 1 iff real value(a) > value(b), else 0.
fn gt(a: u32, b: u32) -> u8 {
    var ca : u8 = category(a);
    var cb : u8 = category(b);
    if (ca != cb) { if (ca > cb) { return 1; } return 0; }
    var ma : u32 = a & 65535;
    var mb : u32 = b & 65535;
    if (ca == 2) { if (ma > mb) { return 1; } return 0; }   // both positive
    if (ca == 0) { if (ma < mb) { return 1; } return 0; }   // both negative
    return 0;                                               // both zero -> equal
}

// argmax over four logits, lowest index on ties.
fn on_comb(x0: u32, x1: u32, x2: u32, x3: u32) -> u8 {
    var idx : u8 = 0;
    var best : u32 = x0;
    if (gt(x1, best) == 1) { idx = 1; best = x1; }
    if (gt(x2, best) == 1) { idx = 2; best = x2; }
    if (gt(x3, best) == 1) { idx = 3; best = x3; }
    return idx;
}

// +1.0, -1.0, +2.0, 0 -> max is +2.0 at index 2.
test pick_pos { assert_eq(on_comb(20480, 86016, 20992, 0), 2); }
// -1.0, -2.0, 0, +1.0 -> max is +1.0 at index 3.
test pick_last { assert_eq(on_comb(86016, 86528, 0, 20480), 3); }
// -1.0, 0, -4.0, -2.0 -> max is 0 at index 1 (zero beats all negatives).
test zero_wins { assert_eq(on_comb(86016, 0, 87040, 86528), 1); }
// all equal (+1.0) -> lowest index 0.
test tie_low { assert_eq(on_comb(20480, 20480, 20480, 20480), 0); }
endmodule

Open the lesson's spec in the player ↗

All lessons