Livt packages

Livt.ML

Version 1.6.1

Fixed-size quantized inference, tensor storage, attention, and transformer building blocks for Livt.

Livt.ML

Fixed-size machine-learning building blocks for Livt hardware-oriented designs.

Livt.ML provides small, explicit components for integer vectors, quantized linear inference, activation functions, embeddings, attention, normalization, transformer-style composition, and simple fitted models. The package is intentionally concrete rather than generic: APIs expose fixed sizes, stored outputs, and visible scale factors so generated hardware remains easy to inspect.

๐Ÿ“ฆ Package

[dependencies]
"Livt.ML" = "1.6.1"

Version 1.6.1 publishes package discovery metadata, this README, the changelog, and the MIT license. Its component API is unchanged from 1.6.0.

Livt.ML 1.6.1 depends on Livt.Math 1.7.2 for reusable arithmetic and Livt.IO 1.8.1 for RAM-backed tensor storage. See CHANGELOG.md for release notes and compatibility guidance. Memory sources use the generic Livt.IO API: byte tensors use scheduled BlockRam<byte, 2048> banks, and causal attention uses AsynchronousDistributedRam32x32. Run livt sync after updating dependencies. Livt.Math 1.7.2 and Livt.IO 1.8.1 must be available in your registry or local workspace before resolving this version of Livt.ML.

๐Ÿ“š API Overview

Namespace Component Purpose
Livt.ML.Numeric RequantizeInt8 Converts int32 accumulators to signed int8 with ties-to-even rounding and saturation
Livt.ML.Numeric ScheduledRequantizeUInt8 Scheduled rational uint8 requantization with ties-to-even rounding and saturation
Livt.ML.Convolution QLinearConv1x8Same5x5 Quantized convolution from one 28x28 plane to eight planes
Livt.ML.Convolution QLinearConv8x16Same5x5 Quantized convolution from eight 14x14 planes to sixteen planes
Livt.ML.Convolution QLinearConv1x8Same5x5Ram RAM-backed non-streaming reference for the first MNIST convolution
Livt.ML.Convolution QLinearConv8x16Same5x5Ram RAM-backed non-streaming reference for the second MNIST convolution
Livt.ML.Convolution QConv2DStreamBase Reusable zero-point-aware uint8-by-int8 convolution MAC building block
Livt.ML.Convolution QConv2D1x8Kernel5Image28Stream Specialized high-throughput 1x8, 5x5, 28x28 streaming convolution
Livt.ML.Convolution QConv2D8x16Kernel5Image14Stream Specialized high-throughput 8x16, 5x5, 14x14 streaming convolution
Livt.ML.Pooling MaxPool8x28To14 2x2 stride-two max pooling for eight planes
Livt.ML.Pooling MaxPool16x14To4 3x3 stride-three max pooling for sixteen planes
Livt.ML.Pooling MaxPool8x28To14Ram RAM-backed reference preserving the tensor lifecycle
Livt.ML.Pooling MaxPool16x14To4Ram RAM-backed reference preserving the tensor lifecycle
Livt.ML.Pooling MaxPool8x28To14Stream Line-buffered streaming alternative with a toggle handshake
Livt.ML.Pooling MaxPool16x14To4Stream Four-window streaming alternative with ONNX floor sizing
Livt.ML.Linear QLinearMatMul256x10 Zero-point-aware per-column quantized projection from 256 activations to ten outputs
Livt.ML.Linear QLinearMatMul256x10Ram Compact RAM-backed non-streaming projection reference
Livt.ML.Linear QLinearMatMul256x10Stream Compact-weight streaming projection with ten parallel accumulators
Livt.ML.Linear Int8Linear4x8 Applies eight signed-int8 rows to four inputs with int32 biases and accumulators
Livt.ML.Linear Int8Linear8x3 Applies three signed-int8 rows to eight inputs with int32 biases and accumulators
Livt.ML.Linear VecAdd Adds two 8-element integer vectors
Livt.ML.Linear DotProduct Computes an 8-element dot product with positive saturation
Livt.ML.Linear MatrixVectorMultiplication Applies a 4 by 8 integer matrix to a vector
Livt.ML.Linear Linear8x8 Applies an 8 by 8 integer matrix to a vector
Livt.ML.Linear Int8LinearAccumulator Storage-free centered int8 projection lane with int32 accumulation
Livt.ML.Linear Int8Mac8 Context-free eight-lane signed-int8 MAC for memory-fed datapaths
Livt.ML.Linear Int8BroadcastMac64x8 Pipelined 64-output matrix-row accumulator using eight signed 16x8 lanes
Livt.ML.Linear Int8Dot64x8 Pipelined 64-element dot product using eight signed 16x8 lanes
Livt.ML.Linear Int8MatrixAccumulator32 Signed int32 ร— INT8, 64-column chunks and up to 1,024 BRAM sums
Livt.ML.Linear Int8MatrixAccumulator32Stream Ordered retained-row batches over the same checked matrix core
Livt.ML.Storage ByteTensorRam2048 Byte tensor storage backed by one Livt.IO RAM bank
Livt.ML.Storage ByteTensorRam4096 Byte tensor storage backed by two Livt.IO RAM banks
Livt.ML.Storage ByteTensorRam8192 Byte tensor storage backed by four Livt.IO RAM banks
Livt.ML.Storage Int16TensorRam2048 Signed 16-bit activation tensor backed by Livt.IO RAM
Livt.ML.Storage Int32TensorRam2048 Signed 32-bit accumulator tensor backed by Livt.IO RAM
Livt.ML.Activation ReLU Stateless integer ReLU
Livt.ML.Activation SigmoidLUT 256-entry sigmoid lookup table
Livt.ML.Activation SiLUApprox 8-element SiLU approximation
Livt.ML.Activation SoftmaxApprox 8-element scaled softmax approximation
Livt.ML.Activation StableSoftmax8 Max-subtracted signed-score softmax with Q1.15 outputs
Livt.ML.Activation GeluNewApproxQ8 GPT-Neo GELU-new approximation for Q8.8 values
Livt.ML.Norm RMSNorm 8-element integer RMS normalization
Livt.ML.Norm LayerNorm64 Readable learned 64-value LayerNorm reference
Livt.ML.Norm LayerNorm64Stream Toggle-stream implementation without tensor-valued public calls
Livt.ML.Embedding FixedTokenEmbedding Mutable 16 by 8 integer embedding table
Livt.ML.Embedding Embedding64Stream External-memory base for streaming a 64-value row
Livt.ML.Embedding TokenEmbedding50257x64Stream GPT-2-vocabulary specialization without an internal table
Livt.ML.Embedding PositionEmbedding8x64Stream Context-eight position specialization
Livt.ML.Attention AttentionAccumulator32 Checked 32-element Q.K scores and retained weighted-V numerators using paired input rows
Livt.ML.Attention AttentionAccumulator32Port The same checked arithmetic with live input rows and ordered block-pair control
Livt.ML.Attention KVCache Four-slot key/value cache
Livt.ML.Attention ScaledDotProductAttention Attention over the fixed key/value cache
Livt.ML.Attention CausalAttentionHead4Context8 One reusable GPT-Neo causal head for context 8 and width 4
Livt.ML.Classifier ArgMax3 Deterministic argmax for three logits
Livt.ML.Classifier ArgMax10 Deterministic first-maximum argmax for ten logits
Livt.ML.Classifier StreamingArgMax Arbitrary-length ArgMax without materializing logits
Livt.ML.Classifier ClassProjection Experimental fixed projection from 8 features to 3 logits
Livt.ML.Transformer FeedForwardBlock Two-layer 8-element feed-forward block
Livt.ML.Transformer TransformerBlock Experimental transformer-style composition block
Livt.ML.Model LinearRegression Fits and evaluates a small integer linear model

Most stateful components follow the same call shape: configure any stored weights or tables, call Compute(...), then read results with GetOutput(...). For the simple fixed-size components, out-of-range getters return 0 and setters ignore the write. Checked matrix and attention APIs instead report failure explicitly and may invalidate retained results; follow their contracts. Optimized components ending in Stream use their documented toggle or ready/valid handshake and bounded internal storage; see docs/streaming.md.

ClassProjection, ScaledDotProductAttention, and TransformerBlock remain experimental compositions. Their APIs may evolve when exercised by an imported language model; the numeric, tensor, RAM, and streaming operator APIs form the current stable inference foundation.

using Livt.ML.Linear

component ExampleLayer
{
	layer: Linear8x8

	new()
	{
		this.layer = new Linear8x8()
	}

	public fn Run(input: int[8])
	{
		this.layer.SetRowValues(0, 1, 0, 0, 0, 0, 0, 0, 0)
		this.layer.Compute(input)
		var first: int = this.layer.GetOutput(0)
	}
}

๐Ÿ”ข Data Model

The API uses fixed-size int vectors, mostly int[8]. Signed-int8 tensor elements are represented by int values in [-128, 127]; quantized linear layers retain signed int32 accumulator results until callers explicitly invoke RequantizeInt8. Several components expose integer scale constants:

  • SigmoidLUT, SiLUApprox, and SoftmaxApprox use scaled integer approximations.
  • ScaledDotProductAttention uses explicit score and weight scale constants.
  • LinearRegression stores the fitted slope as aFixed, scaled by LinearRegression.SCALE.
  • DotProduct.ACC_MAX defines the positive saturation limit used by the linear projection components.
  • LayerNorm64, GeluNewApproxQ8, and their learned affine values use Q8.8 scale 256; StableSoftmax8 emits Q1.15 weights at scale 32768.
  • Int8LinearAccumulator retains signed int32 sums and deliberately does not choose output scales; model importers supply those after calibration.
  • Int8BroadcastMac64x8 and Int8Dot64x8 accept signed 16-bit activations, packed signed-int8 weights, and retain signed int32 accumulator results. Callers must prove that activations fit [-32768, 32767] before the boundary.

Approximation components are useful for simulation, reference designs, and early hardware exploration. Timing-critical designs should review docs/hardware-notes.md.

๐Ÿงช Build and Test

Build the package:

livt build

Run the configured test components:

livt test

The test list is defined in livt.toml. Short call-order examples live in docs/usage.md.

๐Ÿ”ง Layout

src/numeric/      ML-specific quantization and requantization
src/linear/       vector addition, dot products, and dense projections
src/pooling/      tensor-reference and line-buffered streaming pooling
src/storage/      banked byte tensors backed by Livt.IO RAM components
src/activation/   ReLU, sigmoid/SiLU/GELU approximations, and stable softmax
src/norm/         RMSNorm and reference/streaming LayerNorm
src/embedding/    fixed tables and external-memory embedding streams
src/attention/    KV cache plus reference and bounded causal attention
src/classifier/   small classifier helpers
src/transformer/  fixed-size transformer-style blocks
src/model/        small fitted model components
tests/<domain>/   tests mirroring each source domain
docs/             usage examples and package notes

Source namespaces mirror their folders, for example Livt.ML.Attention. Test namespaces mirror the same structure below Livt.ML.Tests.

๐Ÿ“– Notes

Compiler bugs should be recorded only in COMPILER.md. Hardware tradeoffs and implementation notes belong in docs/.

๐Ÿš€ Outlook

Livt.ML provides a broad set of fixed-size components. Future work should add more hardware-optimized variants, shared fixed-point helpers through Livt.Math, and stronger model-weight loading patterns.

๐Ÿ“„ License

This project is licensed under the MIT License. See LICENSE.

Generic wide fixed-point operators

Version 1.3.0 adds streamed INT4 projection, fixed-point RMSNorm, masked softmax and lookup activation with explicit formats and injected storage. See contracts. Existing operator semantics are unchanged.

Generic fixed-point transformer operations (1.4.0)

ITransformerKernels and FixedTransformerKernels provide serialized embedding, affine RMS normalization, matrix projection, unscaled masked attention with relative position bias, table-driven activation/gating and residual addition. Token/vector bounds, fixed-point formats, epsilon, external IRam<int> storage, weights and activation tables are generic parameters. One worker can serve every layer. This is a serial correctness implementation, not a throughput or FPGA resource guarantee.

Attention scratch is an explicit generic IRam<int> dependency with at least 2 * MAX_TOKENS words. See the source contracts in ITransformerKernels and FixedTransformerKernels for ownership, reset, capacity, scheduling and completion requirements.

QuantizedTensorWeights selects immutable ITensorCatalog records and reads low-nibble-first packed INT4 codes and unsigned scale words from external memory. Addresses count int32 words except catalog packed-code addresses, which count bytes. It handles unaligned byte offsets and flattened groups crossing matrix rows. Invalid selection/reads latch a failure until a successful selection.

A kernel returns false on invalid dimensions, ranges, values or provider failure; its destination may then contain partial writes and must be discarded. Input and model spans must remain stable for the call. The memory provider must expose contiguous valid ranges. Only residual addition permits exact input/output aliasing; partial overlap is rejected. The caller owns serialization and result validity. FixedTransformerKernelsTest covers a different Q8 format and shape from FLAN-T5, including the deliberate lack of probability-sum correction.

Incremental transformer operations (1.4.1)

IIncrementalTransformerKernels extends the existing encoder interface with single-query CachedAttention and ProjectArgMax; existing implementations of ITransformerKernels need no changes. FixedTransformerKernels implements both through interface inheritance and reuses its arithmetic and provider instances.

Cached attention takes an independent K/V length, query position, causal flag, key mask and optional bias tensor. A bias ID of -1 disables bias. Bucket entries are int32 words covering relative distances -(MAX_TOKENS-1)..MAX_TOKENS-1. Masked/future K/V entries are not read. Scratch scores for excluded keys are unspecified; their probabilities and contribution to context are zero.

Streaming projection supports up to 65,536 output rows independently of the maximum vector width. It rounds and clips every complete dot product before feeding StreamingArgMax, preserves lowest-index ties, and returns -1 on error. It allocates no output-logit tensor. Tests cover a full 32,128-row scan, clipping that creates a tie, poisoned masked/future cache entries, Q8 and existing kernel contracts. Model cache addresses and lifecycle policy stay in application code.

Scheduled integer division

Division policy and migration status describe the shared Livt.Math quotient/remainder API and its use in transformer shape checks. Runtime division is explicitly scheduled; compile-time arithmetic and existing fixed-point rounding retain their own contracts.

Checked INT8 matrix kernel (1.6.0)

Linear.Int8MatrixAccumulator32<ACTIVATION_BITS, PRODUCT_STAGES> adds signed int32 activations, INT8 weights and checked int64 output sums retained inside the engine. Four packed activation loads configure a 64-column chunk; live weight rows then update up to 1,024 output sums without returning partial dots to the caller. Product stages 1 and 2 preserve identical arithmetic.

The caller owns memory transport, row layout, final scaling and rounding. Existing 16-bit kernels remain unchanged. See the matrix contract for packing, bias, overflow, ownership, reset and validation details.

livt test --run Int8MatrixAccumulator32Test

Checked block attention

Attention.AttentionAccumulator32<PRODUCT_STAGES> retains a 32-element query and computes exact Q.K scores and weighted-V numerators through Livt.Math.Arithmetic.CheckedBlockMac32. Each vector is supplied as two packed 512-bit blocks of sixteen signed int32 lanes. Results contain a checked int64 sum and a failure flag. Scaling, softmax and final division stay with the caller.

AttentionAccumulator32Port<PRODUCT_STAGES> accepts a live 512-bit values input and schedules control operations, avoiding full-row argument copies. Hold each row stable until the method returns and submit blocks in order 0, 1. Invalid query/score order fails; invalid or overflowing weighted updates poison reads until clear/reset. Both variants require a single owner and serialized calls. PRODUCT_STAGES selects 1 (default) or 2 without changing arithmetic.

These additions preserve existing INT8/16-bit kernel APIs and defaults. See the attention contract for row lifetime, block ordering, overflow and reset behavior. Run the paired-row generated-wrapper regression with the Livt compiler/simulator:

livt test --run AttentionAccumulator32Test

Batched INT8 matrix interface (1.6.0)

Linear.Int8MatrixAccumulator32Stream<ACTIVATION_BITS, PRODUCT_STAGES> shares its checked core with the existing blocking interface and adds AccumulateRows(first, count). A ready/valid producer retains each packed 64-byte weight row until acceptance. Eight lanes feed continuously within a batch while completed rows update BRAM; model layout, memory transport and final scaling remain caller responsibilities. No DDR or board dependency is introduced. See the stream contract.

livt test --run Int8MatrixAccumulator32StreamTest

The producer must retain valid/data until the transfer edge; ready releases its row storage after eight feeding cycles. Batch completion includes all BRAM writes. Source failures and overflow invalidate the bank until Begin or reset. The native core uses synchronous active-high reset; reset both ends together across a rising edge and call Begin before reuse.