Livt packages

Livt.ML

Version 1.6.1

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

Published files (132)

src/linear/QLinearMatMul256x10.lvt

namespace Livt.ML.Linear

using Livt.ML.Numeric

/**
 * Applies an ONNX per-column QLinearMatMul from 256 uint8 inputs to ten outputs.
 */
component QLinearMatMul256x10
{
	// Owned scheduled requantizer; processing holds input acceptance until completion.
	requantize: ScheduledRequantizeUInt8

	// Compile-time dimensions, bounds, and numeric constants.
	public const INPUT_SIZE: int = 256
	public const OUTPUT_SIZE: int = 10

	// Input tensor storage; populate the required span before computation.
	inputValues: int[256]

	// Configured coefficients and numeric scaling parameters.
	weights: int[10, 256]
	multipliers: int[10]
	divisors: int[10]
	inputZeroPoints: int[10]
	weightZeroPoints: int[10]
	outputZeroPoints: int[10]

	// Wide output accumulators before final output conversion.
	accumulators: int[10]

	// Latest computed result, retained for the caller.
	output: int[10]

	/**
	 * Owns one scheduled requantizer for serialized processing.
	 */
	new()
	{
		requantize = new ScheduledRequantizeUInt8()
	}

	/**
	 * Stores one flattened uint8 input value and ignores an invalid index.
	 */
	public fn SetInput(index: int, value: int)
	{
		if (index < 0) {
			return
		}

		if (index >= INPUT_SIZE) {
			return
		}

		inputValues[index] = value
	}

	/**
	 * Clears all weights, quantization parameters, accumulators, and outputs.
	 */
	public fn Clear()
	{
		for (var column = 0; column < OUTPUT_SIZE; column++) {
			multipliers[column] = 0
			divisors[column] = 1
			inputZeroPoints[column] = 0
			weightZeroPoints[column] = 0
			outputZeroPoints[column] = 0
			accumulators[column] = 0
			output[column] = 0

			for (var row = 0; row < INPUT_SIZE; row++) {
				weights[column, row] = 0
			}
		}
	}

	/**
	 * Stores one signed-int8 column weight and ignores an invalid index.
	 */
	public fn SetWeight(column: int, row: int, value: int)
	{
		if (column < 0) {
			return
		}

		if (column >= OUTPUT_SIZE) {
			return
		}

		if (row < 0) {
			return
		}

		if (row >= INPUT_SIZE) {
			return
		}

		weights[column, row] = value
	}

	/**
	 * Stores the rational scale and tensor zero points for one output column.
	 */
	public fn SetColumnParameters(
		column: int, multiplier: int, divisor: int,
		inputZeroPoint: int, weightZeroPoint: int, outputZeroPoint: int)
	{
		if (column < 0) {
			return
		}

		if (column >= OUTPUT_SIZE) {
			return
		}

		multipliers[column] = multiplier
		divisors[column] = divisor
		inputZeroPoints[column] = inputZeroPoint
		weightZeroPoints[column] = weightZeroPoint
		outputZeroPoints[column] = outputZeroPoint
	}

	/**
	 * Computes ten uint8 outputs using ONNX-style centered multiplication.
	 */
	public fn Compute()
	{
		for (var column = 0; column < OUTPUT_SIZE; column++) {
			var accumulator = 0

			for (var row = 0; row < INPUT_SIZE; row++) {
				state {
					var centeredInput = inputValues[row] - inputZeroPoints[column]
					var centeredWeight = weights[column, row] - weightZeroPoints[column]
					var product = centeredInput * centeredWeight
					accumulator = accumulator + product
				}
			}

			state {
				accumulators[column] = accumulator
				output[column] = requantize.ApplyRational(
					accumulator, multipliers[column], divisors[column], outputZeroPoints[column])
			}
		}
	}

	/**
	 * Returns one int32 accumulator, or zero for an invalid index.
	 */
	public fn GetAccumulator(index: int) int
	{
		if (index < 0) {
			return 0
		}

		if (index >= OUTPUT_SIZE) {
			return 0
		}

		return accumulators[index]
	}

	/**
	 * Returns one quantized output, or zero for an invalid index.
	 */
	public fn GetOutput(index: int) int
	{
		if (index < 0) {
			return 0
		}

		if (index >= OUTPUT_SIZE) {
			return 0
		}

		return output[index]
	}
}