AIG Export Guide
TorchLogix can export a trained model as a binary AIGER file (.aig), the
standard And-Inverter Graph format read by logic-synthesis and verification
tools such as ABC and
mockturtle. This guide walks through
the export pipeline end to end, without modifying any TorchLogix source code.
1. Build a Circuit
Circuit.from_model traces a model that has been put into export mode and
unrolls it into a flat gate list:
from torchlogix import Circuit
from torchlogix.utils import set_export_mode
set_export_mode(model) # required before tracing
circuit = Circuit.from_model(model, input_shape=(1, 28, 28))
Circuit requires binary inputs; binarize at the dataset level before
tracing, since binarization layers are not exported.
2. Simplify (optional)
circuit.simplify() constant-folds, dedups, and removes dead gates. It has
no effect on the circuit’s function, only its size, so it’s safe (and
recommended) to run before export — fewer gates means a smaller .aig file
and less work for downstream synthesis tools:
circuit.simplify()
3. Convert to an AIGGraph
circuit.to_and_inverter_graph() lowers every gate to AND/inverter form and
returns an AIGGraph:
aig = circuit.to_and_inverter_graph()
aig.n_inputs # number of primary inputs
aig.and_gates # list of (lhs, rhs0, rhs1) literal triples
aig.outputs # flat list of output literals — the AIGER-format payload
aig.output_specs # list[AIGOutputSpec] — how to regroup aig.outputs into
# circuit.outputs, see "Output bit ordering" below
aig.output_shape # copy of circuit.output_shape
Every non-AND gate in the original circuit (OR, XOR, NAND, WIRE, …)
is rewritten as one or more two-input ANDs plus literal negation — XOR/
XNOR, for example, expand to three AND gates each.
aig.outputs, aig.and_gates, and the header counts (M I L O A) are what
gets written to the .aig file — that’s the whole AIGER format, and it’s
what ABC/mockturtle read. aig.output_specs and aig.output_shape are
not written to the file; they only exist on the Python AIGGraph object.
See “Output bit ordering” below for what that means for a consumer that only
has the .aig file.
4. Write the .aig file
aig.write_to_aiger_file("circuit.aig")
Or skip step 3 and call the shortcut on Circuit directly, which does both
steps for you:
circuit.write_to_aiger_file("circuit.aig")
The file is written in the binary AIGER format (header line aig M I L O A
followed by delta-encoded AND gates). TorchLogix circuits are purely
combinational, so the latch count L is always 0.
5. Read the file with ABC or mockturtle
ABC:
abc -q "read_aiger circuit.aig; print_stats"
mockturtle (C++):
#include <mockturtle/mockturtle.hpp>
mockturtle::aig_network aig;
lorina::read_aiger("circuit.aig", mockturtle::aiger_reader(aig));
Both tools follow the AIGER literal convention: variable v’s positive
literal is 2*v and its negative (inverted) literal is 2*v + 1;
variable 0 is reserved so literal 0 means constant False and literal
1 means constant True. Primary input i (0-indexed, as passed to
Circuit.from_model) is AIGER variable i + 1.
6. Output bit ordering and decoding
aig.outputs is a flat list of literals with no embedded structure — a
10-class GroupSum model turns into 70+ anonymous output wires in there.
aig.output_specs is the output ABI: one AIGOutputSpec per entry in
circuit.outputs, in the same order, telling you exactly how to regroup
aig.outputs back into the original values:
@dataclass
class AIGOutputSpec:
start_bit: int # aig.outputs[start_bit : start_bit + width] is this output
width: int # 1 for a boolean output
kind: str # "bool" or "uint"
bit_order: str = "lsb_first"
tau: float = 1.0 # always 1.0 -- see Limitations
beta: float = 0.0 # always 0.0 -- see Limitations
kind="bool"(plain boolean output, noGroupSum):widthis always1— the value of that output bit directly.kind="uint"(aGroupSumscore /SumReduction):widthismax(1, len(inputs).bit_length())bits, ordered least-significant bit first, encoding the unsigned integersum(inputs).
Decoding with output_specs in Python:
def decode(aig, aig_output_bits):
values = []
for spec in aig.output_specs:
bits = aig_output_bits[spec.start_bit : spec.start_bit + spec.width]
if spec.kind == "bool":
values.append(bits[0])
else: # "uint", lsb_first
values.append(sum(b << k for k, b in enumerate(bits)))
return values
where aig_output_bits[k] is the resolved boolean value of aig.outputs[k]
(look up the AND-gate/input truth value for lit >> 1 and flip it if lit
is odd — per the AIGER literal convention above).
Important: output_specs/output_shape live only on the in-memory
AIGGraph — they are not written into the .aig file (which is plain
AIGER, readable by any AIGER-compliant tool, with no room for this
metadata). A consumer that reads the .aig file directly (e.g. from ABC or
mockturtle, without going through TorchLogix) does not get this layout for
free; it must be told out of band, e.g. by keeping the AIGGraph/Circuit
Python object around, or agreeing on the layout ahead of time from
circuit.output_shape and the GroupSum sizes in the model. Persisting
output_specs into the file itself (e.g. via AIGER output symbols, or a
JSON sidecar) is a natural extension but is not implemented yet.
Limitations
AIG export requires
tau == 1andbeta == 0exactly.to_and_inverter_graph()raisesValueErrorfor anyGroupSumoutput where that doesn’t hold. Earlier versions tolerated any whole-numberbetaand silently ignoredtau != 1(encoding a raw sum that didn’t match the model’s real output) — that was a trap, not a feature, so it’s now a hard error instead. This is a deliberate scope limit, not a bug: an And-Inverter Graph has no arithmetic for scaling or offsetting a value, so there is no way for AIG export to represent(sum + beta) / tauinside the graph itself, unlikewrite_c_code()/write_verilog_code(), whose output formats can express that arithmetic. If your model needs non-defaulttau/beta, use the C or Verilog export path instead of AIG.All outputs are unsigned integers or single bits — there is no floating-point output path in the AIG export, unlike
write_c_code()/write_verilog_code(), which fall back to afloatscore type whentau != 1orbetais fractional.