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/convolution/QLinearConv1x8Same5x5.lvt

namespace Livt.ML.Convolution

using Livt.ML.Numeric

/**
 * Applies an ONNX QLinearConv from one 28x28 plane to eight 28x28 planes.
 *
 * The kernel is 5x5 with stride one and SAME_UPPER padding. Inputs and stored
 * outputs represent uint8 values in Livt ints; weights represent signed int8;
 * biases and accumulators use signed int32 semantics.
 */
component QLinearConv1x8Same5x5
{
	// Owned scheduled requantizer; processing holds input acceptance until completion.
	requantize: ScheduledRequantizeUInt8

	// Compile-time dimensions, bounds, and numeric constants.
	public const INPUT_SIZE: int = 784
	public const OUTPUT_SIZE: int = 6272
	public const CHANNELS: int = 8
	public const KERNEL_SIZE: int = 25

	// Configured coefficients and numeric scaling parameters.
	weights: int[8, 25]
	biases: int[8]
	multipliers: int[8]
	divisors: int[8]
	inputZeroPoints: int[8]
	weightZeroPoints: int[8]
	outputZeroPoints: int[8]

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

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

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

	/**
	 * Clears all learned parameters and output activations.
	 */
	public fn Clear()
	{
		for (var channel = 0; channel < CHANNELS; channel++) {
			biases[channel] = 0
			multipliers[channel] = 0
			divisors[channel] = 1
			inputZeroPoints[channel] = 0
			weightZeroPoints[channel] = 0
			outputZeroPoints[channel] = 0

			for (var index = 0; index < KERNEL_SIZE; index++) {
				weights[channel, index] = 0
			}
		}

		for (var index = 0; index < OUTPUT_SIZE; index++) {
			output[index] = 0
		}
	}

	/**
	 * 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
	}

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

		if (channel >= CHANNELS) {
			return
		}

		if (index < 0) {
			return
		}

		if (index >= KERNEL_SIZE) {
			return
		}

		weights[channel, index] = value
	}

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

		if (channel >= CHANNELS) {
			return
		}

		biases[channel] = bias
		multipliers[channel] = multiplier
		divisors[channel] = divisor
		inputZeroPoints[channel] = inputZeroPoint
		weightZeroPoints[channel] = weightZeroPoint
		outputZeroPoints[channel] = outputZeroPoint
	}

	/**
	 * Computes eight quantized output planes from one row-major input plane.
	 */
	public fn Compute()
	{
		for (var channel = 0; channel < CHANNELS; channel++) {
			for (var row = 0; row < 28; row++) {
				for (var column = 0; column < 28; column++) {
					var accumulator = biases[channel]

					for (var kernelRow = 0; kernelRow < 5; kernelRow++) {
						state {
							var inputRow = row + kernelRow - 2

							for (var kernelColumn = 0; kernelColumn < 5; kernelColumn++) {
								var inputColumn = column + kernelColumn - 2

								if (inputRow >= 0 && inputRow < 28 && inputColumn >= 0 && inputColumn < 28) {
									var inputIndex = inputRow * 28 + inputColumn
									var inputValue = inputValues[inputIndex]

									if (inputValue != inputZeroPoints[channel]) {
										var weightIndex = kernelRow * 5 + kernelColumn
										var centeredInput = inputValue - inputZeroPoints[channel]
										var centeredWeight = weights[channel, weightIndex] - weightZeroPoints[channel]
										var product = centeredInput * centeredWeight
										accumulator = accumulator + product
									}
								}
							}
						}
					}

					state {
						var outputIndex = channel * 784 + row * 28 + column
						output[outputIndex] = requantize.ApplyRational(
							accumulator, multipliers[channel], divisors[channel], outputZeroPoints[channel])
					}
				}
			}
		}
	}

	/**
	 * Returns one flattened NCHW output value, 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]
	}
}