// SPDX-License-Identifier: Apache-2.0
; specs/widgets/nn-batchnorm.t27 -- batch normalisation: features on different scales, made comparable
; Source of truth for public/widgets/nn-batchnorm/ (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-batchnorm/logic.js -- the fn bodies of
; gHashTag/t27 specs/ml/layers/batchnorm_layer.t27 compiled to wasm, beside its copy logic.t27.
; Lesson 12 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 reads the batch before and after and sets gamma and the running
; statistics. The normalised batch is train_at(b, f) and the inference bars forward_at(gamma,
; shifted, i) on inputs (1, 2, 3), beta 0.25.

; WHAT IT DOES NOT CLAIM: the batch on the left is the spec's fixture, repeated as K_BATCH; the
; normalised values come from the compiled fn.
; phi^2 + 1/phi^2 = 3 | TRINITY

module widget_nn_batchnorm;

pub const KIND : str = "widget-tool";
pub const ID : str = "nn-batchnorm";
pub const TITLE : str = "Batch norm: features on very different scales, made comparable";
pub const DESCRIPTION : str = "A batch of 4 samples whose 3 features live on scales 1, 10 and 1: batch norm gives each feature mean 0 and variance 1. Then set gamma and the running statistics for inference.";
pub const IMAGE_ALT : str = "Batch-norm card: on the left the spec's batch, 4 samples by 3 features (1, 2, 3, 4 / 10, 20, 30, 40 / -1, -1, 1, 1); on the right the normalised batch, the first two columns both -1.34, -0.45, 0.45, 1.34 and the third -1.00, -1.00, 1.00, 1.00. Below, gamma 1 at inference gives 1.25, 2.25, 3.25.";
pub const HOOK : str = "Features 1..4 and 10..40 come out identical, -1.34, -0.45, 0.45, 1.34: batch norm keeps the shape of each feature and throws away its scale.";
pub const CATEGORY : str = "compiler";
pub const DATA_SOURCES : [2]str = ["gHashTag/t27 specs/ml/layers/batchnorm_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 12 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_BATCH_ROWS : u8 = 4;
pub const K_FEATURES : u8 = 3;
pub const K_BATCH : [12]i8 = [1, 10, -1, 2, 20, -1, 3, 30, 1, 4, 40, 1];
pub const K_GAMMA_HI_C : u16 = 300;
pub const K_INFER_INPUT : [3]u8 = [1, 2, 3];
pub const K_BAR_LO_C : i16 = -200;
pub const K_BAR_HI_C : u16 = 1000;
pub const K_BAR_TICKS : [5]i8 = [-2, 0, 2, 5, 10];
pub const K_PRESET_GAMMA_C : [4]u16 = [100, 200, 100, 50];
pub const K_PRESET_SHIFTED : [4]u8 = [0, 0, 1, 1];
pub const K_START_PRESET : u8 = 0;

; --- Words on screen ------------------------------------------------------------------------
pub const SAY_PRESETS_LABEL : str = "Start from";
pub const SAY_PRESET_NAMES : [4]str = ["gamma 1, mean 0 / var 1", "gamma 2", "gamma 1, mean 2 / var 4", "gamma 0.5, mean 2 / var 4"];
pub const SAY_ASK : str = "What happens when one feature is ten times larger than another?";
pub const SAY_GAMMA : str = "gamma";
pub const SAY_SHIFTED : str = "running mean 2, variance 4";
pub const SAY_BEFORE_LABEL : str = "The batch: 4 samples x 3 features";
pub const SAY_AFTER_LABEL : str = "After batch norm";
pub const SAY_INFER_LABEL : str = "At inference: inputs 1, 2, 3 through the running statistics";
pub const SAY_FEATURE : str = "feature";
pub const SAY_SAMPLE : str = "sample";
pub const SAY_INPUT : str = "input";
pub const SAY_COL : str = "feature {0} after";
pub const SAY_INFER : str = "inference out";
pub const SAY_NOTE : str = "Training normalises with the batch's own mean and variance; inference cannot, so it uses running averages kept during training. gamma and beta are learned, so the layer can undo the normalisation where that helps.";
pub const SAY_SOURCE : str = "Logic: t27 spec ml/layers/batchnorm_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_batch_is_the_spec_fixture {
    assert K_BATCH_ROWS * K_FEATURES == 12;
    assert K_BATCH[1] == 10;
    assert K_BATCH[10] == 40;
    assert K_BATCH[2] + 1 == 0;
}

test the_presets_stay_in_range {
    assert K_PRESET_GAMMA_C[1] <= K_GAMMA_HI_C;
    assert K_PRESET_SHIFTED[2] == 1;
    assert K_INFER_INPUT[2] == 3;
}
