BalancedTernaryEngine. Packer
(balanced_ternary_engine v0.1.0-dev)
Copy Markdown
Packs ternary {-1, 0, +1} tensors into compact binary format.
Supports:
- Dense packing: 10 trits per 16-bit word (base-3 encoding)
- Sparse encoding: index + sign for high-sparsity layers
- Round-trip verification
- Compression ratio calculation
Dense word layout
Each group of 10 trits is shifted to base-3 digits via trit + 1
({-1,0,+1} → {0,1,2}) and read most-significant digit first, so a
group's first weight occupies the highest-value position:
word = d₀·3⁹ + d₁·3⁸ + … + d₉·3⁰ where dᵢ = tritᵢ + 1Since 3¹⁰ = 59_049 < 2¹⁶ = 65_536 every word fits in 16 bits (~90% efficiency). Trailing weights of the final group are zero-padded.
Sparse layout
<<count::32>> followed by count entries of <<index::15, sign::1>>
(sign: 0 → +1, 1 → −1). Only wins above ~88–90% sparsity because an
entry costs 16 bits versus 1.6 bits per densely packed weight.
Summary
Functions
Compute compression ratio for a given tensor.
Pack a ternary tensor into a binary (10 trits per 16-bit word).
Pack with sparse encoding for high-sparsity layers.
Unpack a binary back into a flat list of trits.
Unpack a sparse-encoded binary.
Verify round-trip: pack then unpack should return the original.
Functions
@spec compression_ratio(Nx.Tensor.t()) :: {non_neg_integer(), non_neg_integer(), float()}
Compute compression ratio for a given tensor.
Returns {original_bytes, packed_bytes, ratio}.
@spec pack(Nx.Tensor.t() | [integer()]) :: binary()
Pack a ternary tensor into a binary (10 trits per 16-bit word).
Examples
iex> t = Nx.tensor([1, 0, -1, 1, 1, 0, -1, 0, 1, -1])
iex> BalancedTernaryEngine.Packer.pack(t)
<<14285::16-native>>
@spec pack_sparse(Nx.Tensor.t()) :: binary()
Pack with sparse encoding for high-sparsity layers.
Stores only non-zero entries as {index, sign} pairs. More efficient than dense packing when sparsity > ~88%.
Format per entry (16 bits):
- Index: 15 bits (position in flattened tensor, max 32767)
- Sign: 1 bit (0 for +1, 1 for -1)
A u32 count prefixes the entry list so unpack_sparse/2 knows how
many entries to read without relying on binary length alone.
@spec unpack(binary(), non_neg_integer()) :: [integer()]
Unpack a binary back into a flat list of trits.
Examples
iex> BalancedTernaryEngine.Packer.unpack(<<14285::16-native>>, 10)
[1, 0, -1, 1, 1, 0, -1, 0, 1, -1]
@spec unpack_sparse(binary(), non_neg_integer()) :: [integer()]
Unpack a sparse-encoded binary.
@spec verify_roundtrip(Nx.Tensor.t()) :: boolean()
Verify round-trip: pack then unpack should return the original.