# API and semantics ## Modules Subclass `nn.Module`, call `super().__init__()`, define children in the constructor, and implement `forward`. Call modules as `model(x)` so composition stays consistent. A child can return arrays, tuples, or dictionaries of arrays. `nn.Parameter(array)` registers a differentiable variable; `nn.Buffer(array)` registers a non-parameter state variable. Access arrays through `.value`. Assigning the same module to two attributes shares weights. Parameter traversal deduplicates by identity. `ModuleList` and `ModuleDict` express named collections; the underlying NNX backend also discovers ordinary lists and dictionaries. `named_parameters()`, `named_buffers()`, and `named_modules()` produce canonical paths. `parameters()` returns a live selection for an optimizer, not copies. `state_dict()` returns independent NumPy snapshots. A strict `load_state_dict` checks paths, shape, and dtype before copying values. Structural edits require a new optimizer and compiled step. Do not move/cast parameters after creating an optimizer; optimizer moments would otherwise retain the old device/dtype. `train()`/`eval()` recursively toggle stochastic/normalization behavior and return self. They do not turn autodiff on/off. Switch mode outside a compiled step. ## Layers and shapes | Layer | Signature and convention | | --- | --- | | `Linear(I, O, bias=True, rngs=...)` | `[..., I] -> [..., O]`, weight `[O,I]` | | `Conv1d(I, O, K, stride=1, padding=0, groups=1, rngs=...)` | `[B,C,L]`; integer padding | | `Conv2d(I, O, K, stride=1, padding=0, groups=1, rngs=...)` | NCHW; integer/pair or SAME/VALID padding | | `ConvTranspose2d(I, O, K, stride=1, padding=0, output_padding=0, rngs=...)` | NCHW; grouping and dilation unsupported; weights `[O,I,KH,KW]` | | `MaxPool2d(K, stride=None)`, `AvgPool2d(...)` | NCHW, valid windows | | `AdaptiveAvgPool2d((1,1))` | Global spatial mean; other sizes rejected | | `BatchNorm1d(C)`, `BatchNorm2d(C)` | NC/NCL or NCHW; affine parameters and running buffers | | `LayerNorm(shape)` | Normalize trailing dimensions | | `Dropout(p)` | Explicit `rngs` in training; identity in evaluation | | `Embedding(V,D,rngs=...)` | Integer `[...] -> [...,D]` | | `RNN(I,H,num_layers=1, rngs=...)` | `[B,T,I] -> (sequence, h)` | | `LSTM(I,H,num_layers=1, rngs=...)` | `[B,T,I] -> (sequence, (h,c))` | | `MultiheadAttention(D, heads, rngs=...)` | Query `[B,Tq,D]`, key/value `[B,Tk,D]` | ReLU, GELU, Tanh, Sigmoid, Flatten, and Sequential provide simple composition. `Sequential` forwards one value without RNG keywords; write a custom `forward` for stochastic layers or multiple inputs. Linear/conv use fan-in-scaled uniform weights and zero biases. LSTM forget bias is one. BatchNorm uses population batch variance for both normalization and running-statistic updates, not PyTorch's unbiased running-variance update. Results can therefore differ from PyTorch. Transposed convolution output length on each spatial axis is `(input - 1) * stride - 2 * padding + kernel_size + output_padding`. ## Recurrent state Hidden states use `[layers,B,H]`. `state=None` initializes zeros. LSTM state is a pair `(h,c)`. `lengths[B]` freezes the carry after valid timesteps and zeros padded outputs. Zero-length examples keep their initial carry. Lengths must be integers between zero and T; validate data before entering compiled code. Bidirectionality is not implemented. For streaming, pass the returned carry to the next chunk; for truncated backpropagation, call `js.stop_gradient(carry)` between chunks. ## Attention Masks are boolean and broadcast to `[B,heads,Tq,Tk]`; **True means allowed**. `nn.attention.causal_mask(T)` has shape `[1,1,T,T]` and `padding_mask(valid[B,T])` has shape `[B,1,1,T]`. Padded query positions still need a loss mask. Fully masked rows return zero weights and output, including projection bias, with finite gradients. `need_weights=True` returns `(output, weights)` and must be static under compilation. Width must be divisible by heads. Embedding indices and class targets must be in range; these are input-data preconditions, not runtime checks inserted into compiled kernels. ## Randomness and compilation Constructors consume `js.Rngs(params=seed)`. Runtime dropout consumes a separate `js.Rngs(dropout=seed)`. `rngs.next(name)` advances a named checkpointable stream; missing names are errors. Sampling can use `js.random` (JAX's random namespace). `js.jit`, `js.value_and_grad`, and `js.grad` are graph-aware NNX transforms. Pass stateful objects explicitly. Use ordinary JAX transforms only on pure array functions or the `split/merge/apply/update` boundary. Static shapes/modes can recompile. Avoid arbitrary Python side effects inside a transformed function. `value_and_grad` differentiates parameter leaves of the selected module and returns an NNX gradient state; `has_aux=True` returns `((loss, aux), grads)`. NNX semantics govern exceptions and advanced transform nesting. Eager mutations are not automatically rolled back after an exception. Keep shapes/configuration valid before calling transformed code. ## Losses and optimization `nn.functional` exposes `cross_entropy`, `binary_cross_entropy_with_logits`, `mse_loss`, `l1_loss`, and `smooth_l1_loss`. Prediction/target shapes must match except cross-entropy's class axis. Use integer labels with raw class logits; class axis defaults to last. Reductions are `none`, `sum`, `mean`. Masked cross-entropy divides by valid count, with zero loss/gradient for an all-masked batch. BCE uses logits for numerical stability. `optim.SGD`, `Adam`, and `AdamW` accept `model.parameters()` and `lr`. `optimizer.step(grads)` updates once; `optimizer.step_count` reports the counter. `clip_grad_norm(grads, max_norm)` returns `(clipped_grads, original_norm)`. There are no `.grad` buffers, implicit zeroing, parameter groups, or freezing API in this release. Use disjoint modules/optimizers for GANs and differentiate only the intended module. Gradients must originate from the same model structure; matching-shaped trees from an unrelated model are not identity-validated. ## Training utilities `training` includes compiled steps for classification, CNN dropout, forecasting, autoencoding, and the two GAN phases. They are readable reference implementations, not a mandatory trainer abstraction. Use your own step for custom objectives. `Task(model, optimizer, rngs=None)` may override `training_step`, `validation_step`, `set_phase`, and `checkpoint_objects`. Steps own updates; Trainer never differentiates again. `Trainer(max_epochs, callbacks=(), run=None)` aggregates `loss`, `count`, and optional `correct`; custom metrics/GAN orchestration should use the explicit loop. `ModelCheckpoint(path, monitor='val/loss', mode='min')` saves the selected checkpoint. Automatic trainer resume and callback-state resume are not implemented; use the explicit checkpoint/loader APIs.