Circuit
- class torchlogix.Circuit(n_inputs, input_shape, gates=<factory>, outputs=<factory>, output_shape=<factory>, sum_nodes=<factory>)[source]
Bases:
object- classmethod from_model(model, input_shape)[source]
Build a Circuit from a PyTorch model by tracing and folding it.
The model should be in export mode (if applicable) and should have been traced and folded with the appropriate utilities to ensure the FX graph is in the expected form.
- Return type:
- classmethod from_fx_graph(gm, input_shape)[source]
Build a Circuit directly from a folded FX graph.
This is the core logic for walking the FX graph and constructing the flat gate list. Assumptions (satisfied after constant_fold_views): - Exactly one placeholder node (‘input’) - Wiring is done via aten.index.Tensor with folded constant index tensors - LUT dispatch is a cascade of aten.eq + aten.where nodes - Layers are connected by further aten.index.Tensor nodes
- Return type:
- fuse_not_inputs()[source]
Absorb NOT gates into their single downstream consumer.
- Return type:
Recognises patterns that arise when native-torch boolean ops are used instead of all 16 gates, and folds them into the equivalent single-gate form:
AND(x, NOT_1use(y)) -> AND_NOT_B(x, y) AND(NOT_1use(x), y) -> AND_NOT_A(x, y) OR(x, NOT_1use(y)) -> OR_NOT_B(x, y) OR(NOT_1use(x), y) -> OR_NOT_A(x, y) NOT(AND(x, y))_1use -> NAND(x, y) NOT(OR(x, y))_1use -> NOR(x, y) NOT(XOR(x, y))_1use -> XNOR(x, y)
After fusion the absorbed NOT gates become dead and are removed by the next eliminate_dead_gates() call (which simplify() already calls).
- eliminate_dead_gates()[source]
Remove gates that do not contribute to the output (i.e. not on a path from any output ID back to an input ID).
- Return type:
- constant_fold_sum_reductions()[source]
For each SumReduction, fold CONST_TRUE / CONST_FALSE inputs directly into beta, leaving only genuinely variable inputs in input_ids.
After this pass, a fully-folded reduction has input_ids == [] and beta encodes the entire sum; codegen emits a constant rather than a loop. output_ids is rebuilt from the remaining live inputs so that eliminate_dead_gates can remove the constant gates that were folded away.
- Return type:
- constant_fold_gates()[source]
Evaluate gates that have constant inputs and replace them with CONST_TRUE or CONST_FALSE gates as appropriate. This can simplify the circuit and reduce the number of gates.
- Return type:
- bypass_wires()[source]
- Return type:
- Eliminate trivial aliases:
WIRE(x) -> x NOT(NOT(x)) -> x
Rewrites all fanins/output IDs transitively and removes dead alias gates.
This pass is intentionally conservative and cheap.
- dedup()[source]
Structural hashing / common-subexpression elimination.
- Return type:
- Deduplicates gates with identical:
(op, in0, in1)
- Commutative ops are canonicalized so:
AND(a,b) == AND(b,a)
After deduplication, all fanins/output IDs are rewritten to the canonical representative and duplicate gates are removed.
- compile(opt_level=1, pack_bits=None)[source]
Write C code to a temp file, compile to a shared library, and load it. After calling this, circuit(x) will use the compiled implementation.
- Return type:
- __call__(input, use_compiled=False)[source]
Evaluate the circuit on a given input tensor (shape: batch x n_inputs).
Uses the compiled C library when available (after compile()), otherwise evaluates the gate list in Python.
Returns a tensor of shape (batch, len(outputs)). The dtype is determined by _c_output_dtype over the output sum nodes: uint{N}_t when all tau=1, float when tau≠1, bool when there are no sum nodes in outputs.
Attention: For a fair performance comparison, the compiled code does not do type conversions or looping over batches. Instead, it expects numpy inputs of the correct shape (batch dim must match number of packed bits)
- Return type:
- get_c_code(inline_single_use=False, pack_bits=None)[source]
Generate a self-contained C function that evaluates the circuit.
inline_single_use=True (default): gates used by only one other gate are inlined into their parent’s expression rather than emitted as variables. This eliminates most temporaries and makes each output a single expression tree rooted at the inputs.
- Return type:
- classmethod from_dict(data)[source]
Create a Circuit instance from a (JSON-deserialized) dictionary.
- Return type:
- classmethod from_json_file(file_path)[source]
Load a Circuit instance from a JSON file.
- Return type:
- get_verilog_code(inline_single_use=False)[source]
Generate a Verilog module that implements the circuit.
- Return type:
- Each gate becomes a continuous assignment:
wire g<id> = <expr>;
- Outputs are assigned to an output bus:
assign out[k] = <expr>;
inline_single_use=True: single-use gates are folded into their parent expression rather than named wires (same semantics as in emit_c).
- __init__(n_inputs, input_shape, gates=<factory>, outputs=<factory>, output_shape=<factory>, sum_nodes=<factory>)