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/CausalAttentionHead4Context8.lvt
namespace Livt.ML.Attention
using Livt.IO
using Livt.ML.Activation
/**
* Reference causal scaled-dot-product attention for one GPT-Neo head.
*
* The fixed head width is four and context is eight. Dot products are divided
* by sqrt(4)=2 exactly. Q1.15 softmax weights are applied to stored integer
* values. Sixteen heads may reuse this component sequentially.
*/
component CausalAttentionHead4Context8
{
// Fixed attention geometry.
public const CONTEXT_LENGTH: int = 8
public const HEAD_WIDTH: int = 4
// Independent asynchronous vector stores and score normalization.
queries: AsynchronousDistributedRam32x32
keys: AsynchronousDistributedRam32x32
values: AsynchronousDistributedRam32x32
output: AsynchronousDistributedRam32x32
softmax: StableSoftmax8
// Live RAM boundary; scheduled methods own commands, reads stay combinational.
queriesAddress: logic[5]
queriesWriteEnable: bool
queriesWriteData: logic[32]
queriesReadData: logic[32]
keysAddress: logic[5]
keysWriteEnable: bool
keysWriteData: logic[32]
keysReadData: logic[32]
valuesAddress: logic[5]
valuesWriteEnable: bool
valuesWriteData: logic[32]
valuesReadData: logic[32]
outputAddress: logic[5]
outputWriteEnable: bool
outputWriteData: logic[32]
outputReadData: logic[32]
/**
* Creates independent stores; SetElement initializes cells before Compute.
*/
new()
{
this.queries = new AsynchronousDistributedRam32x32()
this.keys = new AsynchronousDistributedRam32x32()
this.values = new AsynchronousDistributedRam32x32()
this.output = new AsynchronousDistributedRam32x32()
this.softmax = new StableSoftmax8()
}
/**
* Stores one query, key, and value element.
*/
public fn SetElement(token: int, element: int, query: int, key: int, value: int)
{
if (token < 0 || token >= CONTEXT_LENGTH) {
return
}
if (element < 0 || element >= HEAD_WIDTH) {
return
}
var address = token * HEAD_WIDTH + element
state {
queriesAddress = address as logic[5]
queriesWriteData = query as logic[32]
queriesWriteEnable = true
keysAddress = address as logic[5]
keysWriteData = key as logic[32]
keysWriteEnable = true
valuesAddress = address as logic[5]
valuesWriteData = value as logic[32]
valuesWriteEnable = true
}
state {
queriesWriteEnable = false
keysWriteEnable = false
valuesWriteEnable = false
}
state {
}
}
/**
* Computes every causal output using scoreStep units per natural-log unit.
*/
public fn Compute(scoreStep: int)
{
for (var token = 0; token < CONTEXT_LENGTH; token++) {
var scores: int[8]
for (var source = 0; source <= token; source++) {
var dot = 0
for (var element = 0; element < HEAD_WIDTH; element++) {
state {
queriesAddress = (token * HEAD_WIDTH + element) as logic[5]
keysAddress = (source * HEAD_WIDTH + element) as logic[5]
}
state {
}
var queryValue = queriesReadData as int
var keyValue = keysReadData as int
var dotProduct = queryValue * keyValue
state {
}
dot = dot + dotProduct
}
scores[source] = dot / 2
}
softmax.ComputeCount(scores, token + 1, scoreStep)
for (var element = 0; element < HEAD_WIDTH; element++) {
var weighted = 0
for (var source = 0; source <= token; source++) {
state {
valuesAddress = (source * HEAD_WIDTH + element) as logic[5]
}
state {
}
var value = valuesReadData as int
var weightedProduct = softmax.GetOutput(source) * value
state {
}
weighted = weighted + weightedProduct
}
var result = weighted / StableSoftmax8.OUTPUT_SCALE
state {
outputAddress = (token * HEAD_WIDTH + element) as logic[5]
outputWriteData = result as logic[32]
outputWriteEnable = true
}
state {
outputWriteEnable = false
}
state {
}
}
}
}
/**
* Returns one output element, or zero for invalid coordinates.
*/
public fn GetOutput(token: int, element: int) int
{
if (token < 0 || token >= CONTEXT_LENGTH) {
return 0
}
if (element < 0 || element >= HEAD_WIDTH) {
return 0
}
state {
outputAddress = (token * HEAD_WIDTH + element) as logic[5]
}
state {
}
return outputReadData as int
}
/**
* Connects inherited RAM ports without implicit scheduled field observations.
*/
process MemoryPorts[]()
{
this.queries.address = queriesAddress
this.queries.writeEnable = queriesWriteEnable
this.queries.writeData = queriesWriteData
queriesReadData = this.queries.readData
this.keys.address = keysAddress
this.keys.writeEnable = keysWriteEnable
this.keys.writeData = keysWriteData
keysReadData = this.keys.readData
this.values.address = valuesAddress
this.values.writeEnable = valuesWriteEnable
this.values.writeData = valuesWriteData
valuesReadData = this.values.readData
this.output.address = outputAddress
this.output.writeEnable = outputWriteEnable
this.output.writeData = outputWriteData
outputReadData = this.output.readData
}
}