Parametrizations API¶
Parametrizations are the "what is the LUT" half of a
LogicDense layer. Each one owns (1) the shape of
the per-neuron weight tensor, (2) a forward basis that maps inputs and
weights to LUT outputs, and (3) a get_luts method that discretizes
weights into {0, 1} truth tables for hardware export.
Pick a parametrization either by string through LogicDense:
from bitlogic.layers import LogicDense
layer = LogicDense(
in_dim=784, out_dim=4000,
parametrization="warp", lut_rank=4, temperature=1.0,
)
...or instantiate the concrete class directly:
Factory¶
setup_parametrization ¶
setup_parametrization(name: str, lut_rank: int, **kwargs: Any) -> LUTParametrization
Build a parametrization by name (lowercase string).
Extra kwargs are forwarded to the constructor, so e.g.::
setup_parametrization("light", lut_rank=4, forward_sampling="hard",
weight_init="random")
Raises KeyError for unknown names.
Base class¶
LUTParametrization ¶
LUTParametrization(lut_rank: int, forward_sampling: str = 'soft', temperature: float = 1.0, weight_init: str = 'random', residual_probability: float = 0.951, anchor_init: bool = True)
Bases: Module, ABC
Abstract base class for LUT parametrizations.
A parametrization owns (1) the shape of the per-neuron weight tensor,
(2) the training-only forward basis that maps inputs and weights to LUT
outputs (_forward_train), and (3) a get_lut method that
discretizes weights into {0, 1} truth tables for hardware export and
for the shared eager-eval lookup. The public forward is concrete on
the base and dispatches between the two.
LUT indexing convention: every parametrization returns
get_lut(weight) in MSB ordering — LUT entry k corresponds to the
input pattern where input j occupies bit rank-1-j of k
(i.e. input 0 is the MSB of the address). This matches the Kronecker
indicator basis used by :meth:forward in eval mode and by
:class:~bitlogic.parametrizations.LightLUT during training.
Class attributes
LUT_INFERENCE_SUPPORTED: Whether :func:get_lut returns a
well-defined binary truth table (output at input pattern k
matches LUT entry k). False for Walsh-Hadamard / linear
bases where get_lut is a thresholded coefficient, not a
round-tripping truth table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lut_rank
|
int
|
Number of inputs per neuron. Must be one of
|
required |
forward_sampling
|
str
|
How logits are sampled during training. Supported
values depend on the concrete parametrization (commonly
|
'soft'
|
temperature
|
float
|
Sigmoid / softmax temperature — lower values give sharper, more discrete outputs. |
1.0
|
weight_init
|
str
|
Either |
'random'
|
residual_probability
|
float
|
Probability that a residual-initialized LUT entry evaluates to its identity target. Sets the magnitude of the initial logits. |
0.951
|
anchor_init
|
bool
|
If |
True
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in bitlogic/parametrizations/base.py
init_weights
abstractmethod
¶
Return the initial weight tensor for a layer of num_neurons.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_neurons
|
int
|
Number of output neurons in the owning layer. |
required |
device
|
device | str | None
|
Target device for the returned tensor. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Tensor of shape |
Tensor
|
is parametrization-specific ( |
Tensor
|
DWN, |
Source code in bitlogic/parametrizations/base.py
forward ¶
Evaluate LUT outputs on gathered inputs.
Dispatches to :meth:_forward_train when training is True.
Otherwise contracts the discrete truth table from :meth:get_lut with
the Kronecker indicator basis — for binary x, this is the same as
indexing get_lut(weight)[neuron, addr(x)]. Subclasses override
:meth:_forward_train, not :meth:forward.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
Gathered input tensor of shape
|
required |
weight
|
Tensor
|
Per-neuron weight tensor returned by
:meth: |
required |
training
|
bool
|
Whether to use the stochastic / soft forward
( |
required |
contraction
|
str
|
Einsum pattern for the per-neuron reduction
(typically |
'n,bn->bn'
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Tensor of shape |
Source code in bitlogic/parametrizations/base.py
effective_weight ¶
Add the residual anchor (if any) to the trainable weight.
update_temperature ¶
Concrete parametrizations¶
LightLUT ¶
LightLUT(lut_rank: int, forward_sampling: str = 'soft', temperature: float = 1.0, weight_init: str = 'random', residual_probability: float = 0.951, anchor_init: bool = True)
Bases: LUTParametrization
LUT parametrization with Kronecker indicator basis.
forward_sampling controls how logits are mapped to LUT values:
* "soft" — plain sigmoid. Legacy ProbabilisticParam.
* "hard" — sigmoid + STE hard. Legacy HybridParam (approx).
* "gumbel_soft" / "gumbel_hard" — Gumbel-sigmoid variants.
Source code in bitlogic/parametrizations/light.py
WarpLUT ¶
WarpLUT(lut_rank: int, forward_sampling: str = 'soft', temperature: float = 1.0, weight_init: str = 'random', residual_probability: float = 0.951, anchor_init: bool = True)
Bases: LUTParametrization
Walsh–Hadamard-basis LUT parametrization.
Inputs are mapped to {-1, +1} and contracted with Walsh basis vectors
to obtain logits z. forward_sampling controls how the logits are
squashed during training:
* "soft" — plain σ(z / τ) (deterministic; default).
* "hard" — σ(z / τ) with a Bernoulli-sampled straight-
through round to {0, 1}. Matches torchlogix-extended's
sigmoid(..., hard=True).
* "gumbel_soft" — Gumbel-sigmoid σ((z + logistic_noise)/τ),
matching Gerlach et al. (2025) Sec 3's Gumbel reparameterization.
* "gumbel_hard" — same as "gumbel_soft" with a hard
straight-through round to {0, 1}.
Eval mode (training=False) collapses to the discrete truth-table
lookup (get_lut(weight)[neuron, addr(x)]) so eager model.eval()
matches :class:PackedLogicNet and the emitted HDL bit-for-bit.
Supports lut_rank ∈ {2, 4, 6}. Weight shape:
(num_neurons, 2**lut_rank). See
:class:~bitlogic.parametrizations.LUTParametrization for the inherited
constructor arguments.
Source code in bitlogic/parametrizations/warp.py
LinearLUT ¶
LinearLUT(lut_rank: int, forward_sampling: str = 'soft', temperature: float = 1.0, weight_init: str = 'random', residual_probability: float = 0.951, anchor_init: bool = True)
Bases: LUTParametrization
Affine + sigmoid parametrization: y_n = σ(W_n @ x_n + b_n).
The simplest parametrization — mostly a sanity-check primitive. Weight
shape is (num_neurons, lut_rank + 1) packing the lut_rank affine
weights and a bias in the trailing column. See
:class:~bitlogic.parametrizations.LUTParametrization for constructor
arguments.
Source code in bitlogic/parametrizations/base.py
PolyLUT ¶
Bases: LUTParametrization
Multivariate-monomial LUT parametrization up to total degree D.
y = σ(Σ_a w_a · Π_j x_j^{a_j}). Number of monomials is
C(lut_rank + D, D) (including the constant term); weight shape is
(num_neurons, num_monomials).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lut_rank
|
int
|
Number of inputs per neuron ( |
required |
degree
|
int
|
Maximum total degree of the monomial basis. |
_DEFAULT_DEGREE
|
**kwargs
|
Any
|
Forwarded to
:class: |
{}
|
Source code in bitlogic/parametrizations/polylut.py
NeuralLUT ¶
NeuralLUT(lut_rank: int, hidden_width: int = _DEFAULT_HIDDEN_WIDTH, depth: int = _DEFAULT_DEPTH, activation: str = _DEFAULT_ACTIVATION, **kwargs: Any)
Bases: LUTParametrization
Tiny-MLP-per-neuron LUT parametrization.
Each neuron is a small MLP of shape
lut_rank → hidden_width → ... → 1. All neuron MLPs share the same
architecture and are evaluated in parallel via bmm. Residual init
is not supported — weight_init is effectively always random here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lut_rank
|
int
|
Number of inputs per neuron. |
required |
hidden_width
|
int
|
Width of the hidden layers. |
_DEFAULT_HIDDEN_WIDTH
|
depth
|
int
|
Number of linear layers ( |
_DEFAULT_DEPTH
|
activation
|
str
|
Hidden-activation name — one of |
_DEFAULT_ACTIVATION
|
**kwargs
|
Any
|
Forwarded to
:class: |
{}
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in bitlogic/parametrizations/neurallut.py
DwnLUT ¶
Bases: LUTParametrization
Binarized-input LUT with Extended Finite Difference (EFD) gradient.
Forward: binarize inputs at 0.5, then do a direct LUT lookup.
Backward: EFD per Bacellar et al. 2025 Sec 3.1 — Hamming-weighted sum
over all 2^n LUT entries (default). Setting efd=False falls
back to the single-bit-flip finite-difference approximation (legacy
code path; closer to what WNN-style papers did before DWN).
Weight shape: (num_neurons, 2**lut_rank); sigmoid(weight) is the
LUT value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lut_rank
|
int
|
Number of inputs per neuron. |
required |
alpha
|
float | None
|
(E)FD backward scale. Defaults to
|
None
|
efd
|
bool
|
If |
True
|
**kwargs
|
Any
|
Forwarded to
:class: |
{}
|
Source code in bitlogic/parametrizations/dwn.py
DiffLogicLUT ¶
Bases: LUTParametrization
DiffLogic-style softmax over the 16 two-input Boolean functions.
Rank 2 only. Each neuron has a (16,) logit vector; the forward
computes Σ_f softmax(weights)_f · f(a, b). The 16-way sum collapses
algebraically into four coefficient terms (constant, linear-a,
linear-b, a·b) — implemented as 4 einsums rather than materializing
all 16 op values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lut_rank
|
int
|
Fixed at |
2
|
**kwargs
|
Any
|
Forwarded to
:class: |
{}
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |