Circuitropy is a Python library for inspecting combinational digital circuits from Verilog netlists, transforming them as editable DAGs, and estimating their information loss through Shannon entropy.
In practice, it lets you:
- Parse a Verilog netlist into a simple internal representation (
pi,po,node) that you can inspect and edit directly. - Measure size, depth and Landauer-related entropy, using a bundled Rust
simulator for the entropy runs — on the CPU, or on the video card
(
update_entropy(gpu=True)) for the same counts about 9–16× faster on large circuits. - Transform circuits with energy-oriented (EO) and depth-oriented (DO) passes, external ABC scripts, or your own passes chained in a pipeline.
- Compare the original and optimized versions of a circuit, side by side or in batch over a directory of netlists (CSV report).
- Export results as versioned JSON, reconstructed Verilog, or DAG images.
Everything is available both as a Python API and as an installed circuitropy
command-line tool.
You need python3 (3.10+) and cargo (Rust) installed.
1. Install. From the repository root, run the setup script — it installs the Python package with its dependencies and builds the two bundled Rust binaries:
./fast_start.sh2. Check the environment.
circuitropy doctor3. Analyze your first circuit.
circuitropy analyze tests/fixtures/half_adder.vOr from Python:
fromcircuitropyimportENERGY_ORIENTED, Circuitropytb=Circuitropy("tests/fixtures/half_adder.v") # parse the netlisttb.update_entropy() # run the entropy simulationoptimized=tb.copy().apply(ENERGY_ORIENTED) # apply an EO pass on a copytb.to_json("half_adder.json") # export as versioned JSONOther useful CLI commands:
circuitropy compare original.v optimized.v # side-by-side metrics
circuitropy optimize circuit.v --method eo -o out.json
circuitropy optimize circuit.aig --backend abc --script "balance; rewrite"
circuitropy batch benchmarks/ -o results.csv # CSV report for a directory
circuitropy visualize circuit.v --metric energy # render the DAGSee the CLI documentation for all options.
The repository only ships the minimal fixtures used by the tests; the full benchmark collections (EDGE, EPFL, BCGEN adders) are kept in a dedicated repository, verilog-benchmarks:
git clone https://github.com/Carlos-Jr/verilog-benchmarks.git
circuitropy batch verilog-benchmarks/EDGE -o results.csvIf you prefer not to use fast_start.sh, install the package with pip.
Dependencies are declared only in pyproject.toml, as extras:
python -m pip install -e ".[viz]"# runtime + DAG rendering
python -m pip install -e ".[dev]"# development (viz + test + docs + tooling)| Extra | Contents |
|---|---|
viz | matplotlib, networkx |
test | pytest, pytest-cov, hypothesis |
docs | mkdocs-material |
dev | viz + test + docs + ruff, mypy, build, twine |
Then build the two Rust binaries once:
# Entropy simulator, used by update_entropy()# (add --no-default-features to skip the wgpu GPU backend)cd circuitropy/iron_circuit_sim && RUSTFLAGS="-C target-cpu=native" cargo build --release &&cd ../..
# EO/DO transformer, used by apply()cd circuitropy/eo_do_rs && RUSTFLAGS="-C target-cpu=native" cargo build --release &&cd ../..RUSTFLAGS="-C target-cpu=native" enables CPU-specific optimizations and
produces a noticeably faster binary on the machine that compiled it; omit it if
you need a binary portable across different CPUs. fast_start.sh accepts the
same trade-off via flags: --dev, --docs, --no-native, --help.
If the EO/DO binary is missing, apply() falls back to the Python reference
implementation and emits a RuntimeWarning. Environment variables:
| Variable | Effect |
|---|---|
CIRCUITROPY_EODO_BACKEND=rust | Turn a missing binary into an error. |
CIRCUITROPY_EODO_BACKEND=python | Force the Python implementation. |
CIRCUITROPY_EODO_BIN=/path/to/eo_do_rs | Override the binary location. |
To inspect the local entropic contribution of a gate, keep chunk partials during the entropy run and query them afterward:
tb.update_entropy(chunks=8, save_entropy=True)
gate_energy=tb.energy_in(42)Here "energy" means the local entropy difference
H(input_joint) - H(output_joint) in bits. energy_in() requires saved
partials from update_entropy(..., save_entropy=True) and does not run a new
simulation.
The complete documentation is in documentation/docs.
Start with:
To serve the documentation locally:
python -m pip install -e ".[docs]"
mkdocs serve -f documentation/mkdocs.yml- CONTRIBUTING.md — development setup, quality checks and the release process.
- CHANGELOG.md — notable changes per release.
- CITATION.cff — citation metadata (GitHub renders a "Cite this repository" box from it).
- SECURITY.md — how to report vulnerabilities.