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/attention/AttentionAccumulator32Port.lvt

namespace Livt.ML.Attention

using Livt.Math.Arithmetic

/**
 * Checked 32-element attention with a live sixteen-lane input port.
 * Owns the same Math arithmetic primitive as AttentionAccumulator32, without
 * copying 512-bit rows through scheduled argument frames. One caller must hold
 * values stable until each method returns. Blocks 0 then 1 form each vector.
 * Reset cancels work; the producer must stop driving transactions during reset.
 * No scaling, softmax, division, saturation or rounding is performed here.
 */
component AttentionAccumulator32Port<PRODUCT_STAGES: int = 1>
{
	static assert PRODUCT_STAGES == 1 || PRODUCT_STAGES == 2, "product stages must be 1 or 2"

	// Live input: sixteen little-endian signed int32 lanes; lane zero is low.
	public values: in logic[512]

	// Owned checked arithmetic and completion phase; status is retained by the core.
	primitive: CheckedBlockMacPrimitive
	request: logic
	queryFirst: bool
	queryValid: bool
	scorePending: bool
	scoreSum: logic[64]
	valuePending: bool
	valueFailed: bool
	valueProbability: int

	/**
	 * Owns one 32x32/64-bit primitive in the caller's clock/reset context.
	 */
	new()
	{
		primitive = new CheckedBlockMacPrimitive()
	}

	/**
	 * Waits for one completed block. Caller controls and input remain stable.
	 */
	fn Submit(operation: int, index: int)
	{
		state {
			primitive.operation = operation
			primitive.index = index
		}

		request = !request
		primitive.request = request

		while (primitive.acknowledge != request) {
			state {}
		}

	}

	/**
	 * Loads query blocks in order 0,1. Invalid/partial loads prevent scoring.
	 */
	public fn LoadQueryBlock(block: int) bool
	{
		scorePending = false
		queryValid = false

		if (block == 0) {
			Submit(0, 0)
			queryFirst = (primitive.result >> 64) == 0
			return queryFirst
		}

		if (block != 1 || !queryFirst) {
			queryFirst = false
			return false
		}

		queryFirst = false
		Submit(0, 1)
		queryValid = (primitive.result >> 64) == 0
		return queryValid
	}

	/**
	 * Scores K blocks in order 0,1. Block 0 returns the checked partial sum;
	 * block 1 returns the full dot product. Bit 64 is failure; failed low bits
	 * are zero. An invalid sequence cannot expose an old accumulated score.
	 */
	public fn ScoreBlock(block: int) logic[65]
	{
		if (!queryValid || block < 0 || block > 1 || (block == 1 && !scorePending)) {
			scorePending = false
			return (1 as logic[65]) << 64
		}

		primitive.seed = 0

		if (block == 1) {
			primitive.seed = scoreSum
		}

		scorePending = false
		Submit(1, block)
		var result = primitive.result

		if ((result >> 64) == 0 && block == 0) {
			scoreSum = result as logic[64]
			scorePending = true
		}

		return result
	}

	/**
	 * Clears weighted sums, partial-pair state and failure; preserves the query.
	 */
	public fn ClearValues() bool
	{
		valuePending = false
		Submit(2, 0)
		valueFailed = (primitive.result >> 64) != 0
		return !valueFailed
	}

	/**
	 * Updates V blocks 0 then 1 using the same probability for both halves.
	 * Invalid order, changed probability or arithmetic failure poisons reads
	 * and updates until ClearValues/reset. No partial vector is readable.
	 */
	public fn AccumulateBlock(block: int, probability: int) bool
	{
		if (valueFailed || block < 0 || block > 1 || (block == 0 && valuePending)
			|| (block == 1 && (!valuePending || probability != valueProbability))) {
			valueFailed = true
			return false
		}

		primitive.scalar = probability
		valueProbability = probability
		Submit(3, block)
		valueFailed = (primitive.result >> 64) != 0
		valuePending = block == 0
		return !valueFailed
	}

	/**
	 * Reads an int64 numerator at 0..31 with failure in bit 64. Scaling and
	 * division remain the caller's responsibility. Failed low bits are zero.
	 */
	public fn Read(index: int) logic[65]
	{
		if (valuePending || valueFailed) {
			return (1 as logic[65]) << 64
		}

		Submit(4, index)
		return primitive.result
	}

	/**
	 * Shares the stable input row directly with the checked arithmetic core.
	 */
	process ConnectValues[]()
	{
		primitive.values = values
		primitive.aBits = 32
		primitive.bBits = 32
		primitive.accumulatorBits = 64
		primitive.productStages = PRODUCT_STAGES
	}
}