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/numeric/FixedPointMath.lvt

namespace Livt.ML.Numeric

using Livt.Math.Arithmetic

/**
 * Signed symmetric fixed point, with explicit ties-even operator boundaries.
 */
component FixedPointMath<BITS: int = 24, FRACTION: int = 12, SCALE_FRACTION: int = 20>
{
	static assert BITS >= 2 && BITS <= 30, "activation bits must be 2..30"

	static assert FRACTION >= 0 && FRACTION < BITS, "fraction must fit activation"

	static assert SCALE_FRACTION >= FRACTION && SCALE_FRACTION - FRACTION <= 30, "invalid scale shift"

	// Symmetric raw activation magnitude allowed by the configured bit width.
	public const LIMIT: int = (1 << (BITS - 1)) - 1
	public const ONE: int = 1 << FRACTION

	// Multiply/add/divide/clamp worker; square-root hardware is excluded.
	math: WideArithmetic<BITS, 23>

	// Validity of the latest checked numeric conversion.
	failed: bool

	/**
	 * Owns one wide arithmetic worker for reconstruction and rounding; calls must be
	 * serialized in the caller clock/reset context.
	 */
	new()
	{
		math = new WideArithmetic<BITS, 23>()
	}

	/**
	 * All 16 two's-complement nibble codes are decoded, including -8.
	 */
	public fn SignedNibble(packed: byte, high: bool) int
	{
		var code = (packed as int) & 15

		if (high) {
			code = ((packed as int) >> 4) & 15
		}

		if (code >= 8) {
			code = code - 16
		}

		return code
	}

	/**
	 * Reconstruct each weight before the MAC. Out-of-range weights are errors.
	 */
	public fn Weight(code: int, scale: logic[32]) int
	{
		failed = code < -8 || code > 7

		if (failed) {
			return 0
		}

		var product = math.Multiply(code as logic[64], scale as logic[64])
		var reconstructed = math.DivideEven(product, (1 << (SCALE_FRACTION - FRACTION)) as logic[64])
		var result = math.Clamp(reconstructed)
		failed = (result as logic[64]) != reconstructed

		if (failed) {
			return 0
		}

		return result
	}

	/**
	 * Saturating residual or bias sum. Inputs must already fit the format.
	 */
	public fn Add(a: int, b: int) int
	{
		failed = a < -LIMIT || a > LIMIT || b < -LIMIT || b > LIMIT

		if (failed) {
			return 0
		}

		return math.Clamp(math.Add(a as logic[64], b as logic[64]))
	}

	/**
	 * Rounded, saturated product for gates and affine normalization.
	 */
	public fn Multiply(a: int, b: int) int
	{
		failed = a < -LIMIT || a > LIMIT || b < -LIMIT || b > LIMIT

		if (failed) {
			return 0
		}

		var product = math.Multiply(a as logic[64], b as logic[64])
		return math.Clamp(math.DivideEven(product, ONE as logic[64]))
	}

	/**
	 * True when the last request violated an input or weight-range contract.
	 */
	public fn Failed() bool
	{
		return failed
	}
}