// SPDX-License-Identifier: Apache-2.0
; specs/widgets/nn-embedding.t27 -- the embedding table: a word becomes a row of numbers
; Source of truth for public/widgets/nn-embedding/ (gHashTag/trinity): scripts/widget-pages-from-spec.mjs
; compiles this file, checks its constants, evaluates the tests below and writes the page; the page
; hands every constant to tool.js as window.T27_WIDGET. tool.js holds no English and NO FORMULA of
; its own: every value it draws comes from public/widgets/nn-embedding/logic.js -- the fn bodies of
; gHashTag/t27 specs/ml/layers/embedding_layer.t27 compiled to wasm, beside its copy logic.t27.
; Lesson 8 of the course neural-networks. ASCII only (L3), English only (LANG-EN).
; Numbers are integers (the schema reads no float): a K_ name ending in _C is in hundredths.

; WHAT IT DOES: the reader picks two tokens from four. The table is lookup_at(v, v, j) for every row
; v, the output lookup_at(a, b, i), and the gradient table grad_at(a, b, i) for the spec's gradient
; rows (1, 2) and (3, 4).

; WHAT IT DOES NOT CLAIM: the table is the spec's 4 x 2 fixture (row v = 10 v, 10 v + 1); a real one
; has a row per word and hundreds of columns.
; phi^2 + 1/phi^2 = 3 | TRINITY

module widget_nn_embedding;

pub const KIND : str = "widget-tool";
pub const ID : str = "nn-embedding";
pub const TITLE : str = "Embeddings: how a word becomes a row of numbers";
pub const DESCRIPTION : str = "Pick two tokens: each one copies its row out of the table. Then the gradient flows back only into the rows that were read, and a token read twice collects both.";
pub const IMAGE_ALT : str = "Embedding card: token 2 read twice from a 4 x 2 table whose rows are (0, 1), (10, 11), (20, 21), (30, 31). Row 2 is outlined and the others dimmed; the output is (20, 21) twice, and the gradient table is all zero except row 2, which holds 4 and 6.";
pub const HOOK : str = "A token is an index into a table, nothing more. Read token 2 twice and its row's gradient is (1 + 3, 2 + 4) = (4, 6); the other rows get 0.";
pub const CATEGORY : str = "compiler";
pub const DATA_SOURCES : [2]str = ["gHashTag/t27 specs/ml/layers/embedding_layer.t27 (t27 master 239b4b059): every value on the page is its fn bodies, compiled to wasm (logic.js)", "Course 10 of t27.ai, neural-networks (gHashTag/trinity#1695): lesson 8 opens this widget"];
pub const READS_LOCAL_FILES : bool = false;
pub const SENDS_NOTHING : bool = true;

pub const K_CARD_WIDTH : u16 = 1200;
pub const K_CARD_HEIGHT : u16 = 630;

; --- Numbers: ranges, presets, and fixtures the spec holds (held equal by the tests below)
pub const K_VOCAB : u8 = 4;
pub const K_WIDTH : u8 = 2;
pub const K_PRESET_A : [3]u8 = [2, 1, 0];
pub const K_PRESET_B : [3]u8 = [2, 3, 2];
pub const K_START_PRESET : u8 = 0;

; --- Words on screen ------------------------------------------------------------------------
pub const SAY_PRESETS_LABEL : str = "Start from";
pub const SAY_PRESET_NAMES : [3]str = ["The same token twice", "Tokens 1 and 3", "Tokens 0 and 2"];
pub const SAY_ASK : str = "How does a word become numbers a network can use?";
pub const SAY_TOKEN_NAMES : [4]str = ["token 0", "token 1", "token 2", "token 3"];
pub const SAY_FIRST : str = "first token";
pub const SAY_SECOND : str = "second token";
pub const SAY_DIM : str = "dim";
pub const SAY_TABLE_LABEL : str = "The embedding table: one row per token";
pub const SAY_OUT_LABEL : str = "What the layer outputs";
pub const SAY_GRAD_LABEL : str = "Where the gradient lands";
pub const SAY_ROWS_TOUCHED : str = "rows read";
pub const SAY_ONE_ROW : str = "one row, twice";
pub const SAY_TWO_ROWS : str = "two rows";
pub const SAY_VECTORS : str = "output";
pub const SAY_GRAD_ROW : str = "gradient of {0}";
pub const SAY_NOTE : str = "An embedding layer has no multiply at all: it copies rows. Learning changes only the rows it copied, which is why a rare word learns slowly.";
pub const SAY_SOURCE : str = "Logic: t27 spec ml/layers/embedding_layer.t27, compiled";

test the_card_is_the_share_size {
    assert K_CARD_WIDTH == 1200;
    assert K_CARD_HEIGHT == 630;
    assert K_CARD_WIDTH * 21 == K_CARD_HEIGHT * 40;
}

test the_table_is_the_spec_fixture {
    assert K_VOCAB == 4;
    assert K_WIDTH == 2;
    assert K_VOCAB * K_WIDTH == 8;
}

test the_presets_name_real_tokens {
    assert K_PRESET_A[0] == K_PRESET_B[0];
    assert K_PRESET_B[1] < K_VOCAB;
    assert K_PRESET_A[1] != K_PRESET_B[1];
}
