Livt packages
Livt.ML
Version 1.6.1
Fixed-size quantized inference, tensor storage, attention, and transformer building blocks for Livt.
Published files (132)
- DotProduct.lvt
- FixedPointAccumulator.lvt
- Int8BroadcastMac64x8.lvt
- Int8BroadcastMac64x8Primitive.lvt
- Int8BroadcastMac64x8Primitive.vhd
- Int8Dot64x8.lvt
- Int8Dot64x8Primitive.lvt
- Int8Dot64x8Primitive.vhd
- Int8Linear4x8.lvt
- Int8Linear8x3.lvt
- Int8LinearAccumulator.lvt
- Int8Mac8.lvt
- Int8MatrixAccumulator32.lvt
- Int8MatrixAccumulator32Primitive.lvt
- Int8MatrixAccumulator32Primitive.vhd
- Int8MatrixAccumulator32Stream.lvt
- Linear8x8.lvt
- MatrixVectorMultiplication.lvt
- QLinearMatMul256x10.lvt
- QLinearMatMul256x10Ram.lvt
- QLinearMatMul256x10Stream.lvt
- QuantizedMatrix.lvt
- VecAdd.lvt
src/linear/QuantizedMatrix.lvt
namespace Livt.ML.Linear
using Livt.Math.Arithmetic
using Livt.ML.Numeric
using Livt.ML.Storage
using Livt.IO
/**
* Row-major packed signed INT4 projection with flattened scale groups and injected storage.
*/
component QuantizedMatrix<ROWS: int, COLUMNS: int, GROUP_SIZE: int,
BITS: int, FRACTION: int, SCALE_FRACTION: int, WEIGHTS: IQuantizedWeights, INPUT: IRam<int>>
{
// Shared worker for non-power-of-two scale groups.
divider: UnsignedDivision<31>
static assert ROWS > 0 && ROWS <= 65536, "invalid row count"
static assert COLUMNS > 0 && COLUMNS <= 65536, "invalid column count"
static assert ROWS <= 2147483647 / COLUMNS, "matrix index overflows int"
static assert GROUP_SIZE > 0 && GROUP_SIZE <= 65536, "invalid group size"
// Borrowed packed coefficients and activation memory; keep stable during Compute.
weights: WEIGHTS
input: INPUT
// Owned arithmetic worker used by serialized operations.
arithmetic: FixedPointMath<BITS, FRACTION, SCALE_FRACTION>
// Owned bounded dot-product reducer.
accumulator: FixedPointAccumulator<COLUMNS, BITS, FRACTION>
// Validity of the latest output-row computation.
failed: bool
/**
* Borrows packed weights and owns fixed-point reconstruction and accumulation helpers.
* Keep weights and scales stable throughout each computation.
*/
new(weights: WEIGHTS, input: INPUT)
{
divider = new UnsignedDivision<31>()
this.weights = weights
this.input = input
arithmetic = new FixedPointMath<BITS, FRACTION, SCALE_FRACTION>()
accumulator = new FixedPointAccumulator<COLUMNS, BITS, FRACTION>()
}
/**
* Compute one output row; storage remains external and no weight ROM is elaborated.
*/
public fn Compute(row: int, inputBase: int) int
{
failed = row < 0 || row >= ROWS || inputBase < 0 || inputBase > 2147483647 - COLUMNS
if (failed) {
return 0
}
if (weights.WeightCount() < ROWS * COLUMNS ||
weights.ScaleCount() < ((ROWS * COLUMNS - 1) / GROUP_SIZE) + 1 ||
!input.IsValidAddress(inputBase) ||
!input.IsValidAddress(inputBase + COLUMNS - 1)) {
failed = true
return 0
}
accumulator.Begin()
var previousGroup = -1
var scale: logic[32] = 0
for (var column = 0; column < COLUMNS; column++) {
var index = row * COLUMNS + column
var group = this.ScaleGroup(index)
if (group != previousGroup) {
scale = weights.ReadScale(group)
previousGroup = group
}
var packed = weights.ReadPacked(index / 2)
var code = arithmetic.SignedNibble(packed, index % 2 != 0)
var weight = arithmetic.Weight(code, scale)
if (arithmetic.Failed()) {
failed = true
return 0
}
accumulator.Accumulate(input.Read(inputBase + column), weight)
}
var result = accumulator.Finish()
failed = accumulator.Failed()
return result
}
/**
* Reports whether the latest computation rejected its shape, storage, weights, or
* arithmetic inputs; a failed result must be discarded.
*/
public fn Failed() bool
{
return failed
}
/**
* Maps a nonnegative element index to its scale group. Power-of-two
* specialization retains cheap constant arithmetic; other sizes are scheduled.
*/
fn ScaleGroup(index: int) int
{
static if ((GROUP_SIZE & (GROUP_SIZE - 1)) == 0) {
return index / GROUP_SIZE
} else {
return divider.Divide(index as uint, GROUP_SIZE as uint) as int
}
}
}