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/activation/SoftmaxApprox.lvt

namespace Livt.ML.Activation

using Livt.Math.Arithmetic

/**
 * Runtime division is scheduled; completion latency increases with work.
 * Serialize operations and keep inputs stable until the call or handshake completes.
 *
 * Computes an 8-element integer softmax approximation with scaled outputs.
 */
component SoftmaxApprox
{
	// Owned divider; calls complete before this component advances.
	divider: IntegerDivision<32>

	// Number of logits processed by Compute.
	public const VECTOR_SIZE: int = 8

	// Number of entries in the clamped exponential lookup table.
	public const LUT_SIZE: int = 16

	// Fixed-point probability scale used for stored outputs.
	public const SCALE_FACTOR: int = 256

	// Owned approximation table in the component numeric scale.
	expLut: int[16]

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

	/**
	 * Initializes the 16-entry exponential approximation table.
	 */
	new()
	{
		divider = new IntegerDivision<32>()
		expLut[0] = 1
		expLut[1] = 2
		expLut[2] = 7
		expLut[3] = 20
		expLut[4] = 54
		expLut[5] = 148
		expLut[6] = 403
		expLut[7] = 1096
		expLut[8] = 2980
		expLut[9] = 8103
		expLut[10] = 22026
		expLut[11] = 59874
		expLut[12] = 162754
		expLut[13] = 442413
		expLut[14] = 1202604
		expLut[15] = 3269446
	}

	/**
	 * Computes scaled softmax probabilities after clamping logits to [0, 15].
	 */
	public fn Compute(input: int[8])
	{
		this.ComputeCount(input, VECTOR_SIZE)
	}

	/**
	 * Computes softmax over the first count values and clears inactive outputs.
	 */
	public fn ComputeCount(input: int[8], count: int)
	{
		var activeCount = count

		if (activeCount < 0) {
			activeCount = 0
		}

		if (activeCount > VECTOR_SIZE) {
			activeCount = VECTOR_SIZE
		}

		for (var i = 0; i < VECTOR_SIZE; i++) {
			output[i] = 0
		}

		if (activeCount == 0) {
			return
		}

		var sumExp = 0

		for (var i = 0; i < activeCount; i++) {
			var clampedA = input[i]

			if (clampedA < 0) {
				clampedA = 0
			}

			if (clampedA > 15) {
				clampedA = 15
			}

			sumExp = sumExp + expLut[clampedA]
		}

		if (sumExp == 0) {
			sumExp = 1
		}

		for (var i = 0; i < activeCount; i++) {
			var clampedB = input[i]

			if (clampedB < 0) {
				clampedB = 0
			}

			if (clampedB > 15) {
				clampedB = 15
			}

			var ev = expLut[clampedB]
			output[i] = divider.Divide(ev * SCALE_FACTOR, sumExp)
		}
	}

	/**
	 * Returns one scaled probability, or zero when index is out of range.
	 */
	public fn GetOutput(index: int) int
	{
		if (index < 0) {
			return 0
		}

		if (index >= VECTOR_SIZE) {
			return 0
		}

		return output[index]
	}
}