# gizai:tensor/lazy

fused elementwise chains (Lazy): a small tensor IR run 1024 elements at a time in L1, and products (Product) that a chain feeds or reads.

```gizlang
import * as Lazy from "gizai:tensor/lazy";
```

## Types

### `Step`

```gizlang
type Step = { op: i64; a: i64; b: i64; }
```

One operation of a chain: its op code and its operands (inputs or earlier steps).

- `op`: `i64`
- `a`: `i64`
- `b`: `i64`

### `Lazy`

```gizlang
type Lazy
```

An elementwise expression over tensors, evaluated by `eval`. With a `_product`, input 0 is that product, not yet
computed: on a GPU the expression runs as the product is stored (its epilogue), so C is written once.

- `shape`: `Vec<i64>`
- `inputs`: `Vec<_gizlang_lib_ai__tensor__storage.Tensor>`
- `steps`: `Vec<M49.Step>`

### `Product`

```gizlang
type Product = { a: Tensor; ta: bool; b: Tensor; tb: bool; m: i64; k: i64; n: i64; compute: i64; low: Vec<Tensor>; lowAt: i64; patch: Vec<i64>; }
```

A times B as matmulT takes them: m x k times k x n, on a GPU with the operands rounded to `compute` (-1: as they
are; lazyMatmulIn). `low` is [D, U] of a low-rank product D Uᵀ (D m x r, U n x r, both F16 or BF16 on a
GPU) the chain adds as its input `lowAt`: on NVIDIA's matrix cores the product's epilogue computes it from the
stage memory the main loop is done with, so D Uᵀ is never stored (a LoRA's up projection); elsewhere lowApart.

- `a`: `_gizlang_lib_ai__tensor__storage.Tensor`
- `ta`: `bool`
- `b`: `_gizlang_lib_ai__tensor__storage.Tensor`
- `tb`: `bool`
- `m`: `i64`
- `k`: `i64`
- `n`: `i64`
- `compute`: `i64`
- `low`: `Vec<_gizlang_lib_ai__tensor__storage.Tensor>`
- `lowAt`: `i64`
- `patch`: `Vec<i64>`

## Functions

### `withProduct`

```gizlang
function withProduct(x: Lazy, c: Tensor): Lazy
```

The chain with the product's tensor c as input 0.

Effects: fail, read, write, console, io.

### `lazy`

```gizlang
function lazy(t: Tensor): Lazy
```

T as a lazy expression with no steps yet.

Effects: fail.

### `lazyMatmul`

```gizlang
function lazyMatmul(x: Tensor, transposeA: bool, y: Tensor, transposeB: bool): Lazy
```

x times y as matmulT, not yet computed: the steps on it run as the product is stored.

Effects: fail, read, write, console, io.

### `lazyMatmulIn`

```gizlang
function lazyMatmulIn(x: Tensor, transposeA: bool, y: Tensor, transposeB: bool, compute: i64): Lazy
```

As lazyMatmul, with operands on a GPU rounded to `compute` (F16, BF16 or FP8) as Tensor.cast rounds them; in host
memory they stay f32. The matrix cores' kernel rounds an F32 operand of an F16 or BF16 product as it copies it, so
the cast takes no pass of its own (the bits are those of the cast and then the product). FP8 operands are scaled
to its range first (fp8Scaled), and the product's store multiplies by both scales.

Effects: fail, read, write, console, io.

### `lazyProduct`

```gizlang
function lazyProduct(shape: Vec<i64>, product: Product): Lazy
```

The expression of PRODUCT alone, of `shape`: its input 0 stands for the product and has no values.

Effects: fail.

### `lowApart`

```gizlang
function lowApart(x: Lazy): Lazy
```

x with its Product.low computed apart (F32), as an input like any other.

Effects: fail, read, write, console, io.

### `run`

```gizlang
function run(op: i64, a: i64, b: i64, out: i64, lanes: i64, regs: mut Vec<f32>): void
```

One step over a block of `lanes` elements (a multiple of 16): the operation
chosen once per block, then one vector loop for it.

Effects: fail.

### `fp8Scaled`

```gizlang
function fp8Scaled(t: Tensor): Vec<Tensor>
```

`t` scaled to the range of FP8 as tensorwise scaling (Transformer Engine) does it: [t / s as FP8, s], with
s = max |t| / 448 a tensor of shape [] where t is.

Effects: fail, read, write, console, io.

### `fp4Blocks`

```gizlang
function fp4Blocks(t: Tensor, across: bool): Vec<Tensor>
```

`t` (on a GPU) in NVFP4 blocks with its rows along k, as NVIDIA's Transformer Engine makes them: [q, s]. q is of
type FP4 and shape [rows, k]: rows x k / 2 bytes of E2M1 pairs (row-major, the first in the low half), then
k / 16 E4M3 block scales for each of `lines` (rows rounded up to 4; zeros past rows), in words of 4 (64 of k) by
columns: word w of row r at w lines + r, so the words of a warp's mma lie together and copy in 16 bytes. s (F32, shape []) is max |t| / (6 x 448). The value at row r, p is its E2M1
times the scale of its block times s. `across`: t is [k, rows], else its last axis is k.

Effects: fail, read, write, console, io.

### `fp4Lines`

```gizlang
function fp4Lines(rows: i64): i64
```

The rows of FP4 scales for `rows` rows (fp4Blocks).

Effects: fail.

### `fresh`

```gizlang
function fresh(): Option<Buffer>
```

No output memory given: an evaluation takes new memory.

### `evalAs`

```gizlang
function evalAs(self: Lazy, dtype: i64, into: Option<Buffer>): Tensor
```

The expression's tensor as `dtype`, or (-1) as eval gives it.

Effects: fail, read, write, console, io.

## Methods

### `Lazy.pending`

```gizlang
function Lazy.pending(self: Lazy): bool
```

Whether input 0 is a product not yet evaluated (`_product`): a step over it evaluates that product first.

### `Lazy.expand`

```gizlang
function Lazy.expand(self: Lazy, shape: Vec<i64>): Lazy
```

The expression broadcast to SHAPE.

Effects: fail, read, write, console, io.

### `Lazy.add`

```gizlang
function Lazy.add(self: Lazy, y: Lazy): Lazy
```

SELF + Y, element by element, with broadcasting.

Effects: fail, read, write, console, io.

### `Lazy.sub`

```gizlang
function Lazy.sub(self: Lazy, y: Lazy): Lazy
```

SELF - Y, element by element, with broadcasting.

Effects: fail, read, write, console, io.

### `Lazy.mul`

```gizlang
function Lazy.mul(self: Lazy, y: Lazy): Lazy
```

SELF * Y, element by element, with broadcasting.

Effects: fail, read, write, console, io.

### `Lazy.div`

```gizlang
function Lazy.div(self: Lazy, y: Lazy): Lazy
```

SELF / Y, element by element, with broadcasting.

Effects: fail, read, write, console, io.

### `Lazy.maximum`

```gizlang
function Lazy.maximum(self: Lazy, y: Lazy): Lazy
```

The larger of SELF and Y, element by element, with broadcasting.

Effects: fail, read, write, console, io.

### `Lazy.minimum`

```gizlang
function Lazy.minimum(self: Lazy, y: Lazy): Lazy
```

The smaller of SELF and Y, element by element, with broadcasting.

Effects: fail, read, write, console, io.

### `Lazy.greater`

```gizlang
function Lazy.greater(self: Lazy, y: Lazy): Lazy
```

1 where self > y, else 0.

Effects: fail, read, write, console, io.

### `Lazy.neg`

```gizlang
function Lazy.neg(self: Lazy): Lazy
```

-SELF, element by element.

Effects: fail.

### `Lazy.abs`

```gizlang
function Lazy.abs(self: Lazy): Lazy
```

The absolute value of SELF, element by element.

Effects: fail.

### `Lazy.sqrt`

```gizlang
function Lazy.sqrt(self: Lazy): Lazy
```

The square root of SELF, element by element.

Effects: fail.

### `Lazy.exp`

```gizlang
function Lazy.exp(self: Lazy): Lazy
```

e to the power SELF, element by element.

Effects: fail.

### `Lazy.log`

```gizlang
function Lazy.log(self: Lazy): Lazy
```

The natural logarithm of SELF, element by element.

Effects: fail.

### `Lazy.tanh`

```gizlang
function Lazy.tanh(self: Lazy): Lazy
```

The hyperbolic tangent of SELF, element by element.

Effects: fail.

### `Lazy.sigmoid`

```gizlang
function Lazy.sigmoid(self: Lazy): Lazy
```

The sigmoid 1 / (1 + e^-x) of SELF, element by element.

Effects: fail.

### `Lazy.relu`

```gizlang
function Lazy.relu(self: Lazy): Lazy
```

max(SELF, 0), element by element.

Effects: fail.

### `Lazy.square`

```gizlang
function Lazy.square(self: Lazy): Lazy
```

SELF squared, element by element.

Effects: fail.

### `Lazy.roundedBf16`

```gizlang
function Lazy.roundedBf16(self: Lazy): Lazy
```

Each value rounded to BF16 (nearest even) and kept as F32: where a pipeline stores BF16 between two steps that a
product's epilogue now runs together.

Effects: fail.

### `Lazy.gelu`

```gizlang
function Lazy.gelu(self: Lazy): Lazy
```

GELU, tanh form (as GPT-2 and torch's approximate="tanh").

Effects: fail.

### `Lazy.geluGrad`

```gizlang
function Lazy.geluGrad(self: Lazy, dy: Lazy): Lazy
```

The gradient through gelu: self is x, `dy` the output's gradient.

Effects: fail, read, write, console, io.

### `Lazy.geluErf`

```gizlang
function Lazy.geluErf(self: Lazy): Lazy
```

GELU, erf form (torch's default and BERT's "gelu").

Effects: fail.

### `Lazy.geluErfGrad`

```gizlang
function Lazy.geluErfGrad(self: Lazy, dy: Lazy): Lazy
```

The gradient through geluErf: self is x, `dy` the output's gradient.

Effects: fail, read, write, console, io.

### `Lazy.on`

```gizlang
function Lazy.on(self: Lazy, gpu: Gpu): Lazy
```

The expression with its inputs moved to `gpu`, so `eval` runs it there.

Effects: fail, read, write, console, io.

### `Lazy.like`

```gizlang
function Lazy.like(self: Lazy): Tensor
```

A tensor of the expression's shape where it is evaluated (a GPU or host memory), with no values: for its shape and
place (Tensor.filled, Tensor.gpu), never to read.

Effects: fail.

### `Lazy.eval`

```gizlang
function Lazy.eval(self: Lazy): Tensor
```

The expression's tensor: one pass, on every core when it is large, or one kernel on the GPU of an input. It is f32
but over a product, where it is of the product's type (productType) when every other input is of that type or has
one element, as torch's type promotion; else f32.

Effects: fail, read, write, console, io.

### `Lazy.evalIn`

```gizlang
function Lazy.evalIn(self: Lazy, dtype: i64): Tensor
```

The expression's tensor stored as `dtype` (F32, F16, BF16 or FP8) on a GPU, so a cast takes no pass of its own; in host
memory it is f32.

Effects: fail, read, write, console, io.

### `Lazy.evalInto`

```gizlang
function Lazy.evalInto(self: Lazy, target: Tensor): void
```

Stores the expression's values into GPU tensor `target` (of its shape) as `target`'s type: an update in place, so
the kernels of a recording (Gpu.record) that read `target` read them. Its kernel writes there where nothing it reads
can see the write (it reads `target` only at the element it writes); else it computes a new tensor and copies it.

Effects: fail, read, write, console, io.

### `Lazy.gluIn`

```gizlang
function Lazy.gluIn(self: Lazy, dtype: i64): Tensor
```

A product chain's rows [m, n] as SwiGLU pairs, `dtype` elements [m, n / 2]: column c is silu(v[2c]) times
v[2c + 1], each after the chain (a gate and an up projection with their rows interleaved). On a GPU the product's
store makes them, so C's n columns are never stored.

Effects: fail, read, write, console, io.

### `Lazy.gluInto`

```gizlang
function Lazy.gluInto(self: Lazy, target: Tensor): void
```

Lazy.gluIn into GPU tensor target ([m, n / 2]).

Effects: fail, read, write, console, io.

### `Lazy.reads`

```gizlang
function Lazy.reads(self: Lazy, t: Tensor): bool
```

Whether the expression reads GPU tensor t's memory (an input or a product's operand).

Effects: fail.

### `Lazy.total`

```gizlang
function Lazy.total(self: Lazy, policy: Reduction): Tensor
```

The sum of the expression's elements (Tensor.total) without its tensor: on a GPU, a strict sum of a small one is
one kernel that computes each element as it adds it.

Effects: fail, read, write, console, io.

### `Lazy.average`

```gizlang
function Lazy.average(self: Lazy, policy: Reduction): Tensor
```

The mean of the expression's elements (Tensor.average), as Lazy.total.

Effects: fail, read, write, console, io.

## Constants

### `OP_INPUT`

```gizlang
const OP_INPUT = 0
```

The step of a lazy expression that reads one of its inputs.

### `OP_GELU`

```gizlang
const OP_GELU = 1
```

The step: tanh-approximated GELU of a.

### `OP_ADD`

```gizlang
const OP_ADD = 2
```

The step: a + b.

### `OP_SUB`

```gizlang
const OP_SUB = 3
```

The step: a - b.

### `OP_MUL`

```gizlang
const OP_MUL = 4
```

The step: a * b.

### `OP_DIV`

```gizlang
const OP_DIV = 5
```

The step: a / b.

### `OP_MAX`

```gizlang
const OP_MAX = 6
```

The step: the larger of a and b.

### `OP_MIN`

```gizlang
const OP_MIN = 7
```

The step: the smaller of a and b.

### `OP_NEG`

```gizlang
const OP_NEG = 8
```

The step: -a.

### `OP_ABS`

```gizlang
const OP_ABS = 9
```

The step: |a|.

### `OP_SQRT`

```gizlang
const OP_SQRT = 10
```

The step: the square root of a.

### `OP_EXP`

```gizlang
const OP_EXP = 11
```

The step: e to the a.

### `OP_LOG`

```gizlang
const OP_LOG = 12
```

The step: the natural logarithm of a.

### `OP_TANH`

```gizlang
const OP_TANH = 13
```

The step: tanh a.

### `OP_SIGMOID`

```gizlang
const OP_SIGMOID = 14
```

The step: the logistic sigmoid of a.

### `OP_RELU`

```gizlang
const OP_RELU = 15
```

The step: max(a, 0).

### `OP_SQUARE`

```gizlang
const OP_SQUARE = 16
```

The step: a * a.

### `OP_GREATER`

```gizlang
const OP_GREATER = 17
```

The step: 1 when a > b, else 0.

### `OP_GELU_GRAD`

```gizlang
const OP_GELU_GRAD = 18
```

The step: the derivative of the tanh GELU at a.

### `OP_GELU_ERF`

```gizlang
const OP_GELU_ERF = 19
```

The step: the exact (erf) GELU of a.

### `OP_GELU_ERF_GRAD`

```gizlang
const OP_GELU_ERF_GRAD = 20
```

The step: the derivative of the exact GELU at a.

### `OP_BF16`

```gizlang
const OP_BF16 = 21
```

The step: a rounded to bfloat16.

