Visualization

Logic synthesis is inherently structural, and visualizing an AIG is one of the fastest ways to debug a network, understand what an optimization pass actually changed, or explain a circuit to someone else. aigverse does not ship its own plotting library, but it exposes the network structure through standard formats and adapters so that mature Python visualization tooling can be used directly. The examples below use structured benchmark networks (see Generators) rather than arbitrary toy circuits, so the resulting structures are non-trivial and reproducible: the Graphviz, NetworkX, and highlighting examples share a single ripple-carry adder, while the optimization comparison at the end uses a separate carry-lookahead adder.

Graphviz (DOT) Export

The write_dot() function writes a network to a Graphviz DOT file. Once written, the file can be rendered directly inside a script or notebook using the graphviz Python package.

 1import graphviz
 2
 3from aigverse.generators import ripple_carry_adder
 4from aigverse.io import write_dot
 5
 6# A 4-bit ripple-carry adder, reused throughout this page
 7aig = ripple_carry_adder(bitwidth=4)
 8
 9# Write to DOT format
10write_dot(aig, "example.dot")
11
12# Render the DOT file inline
13graphviz.Source.from_file("example.dot")
_images/ff5b4195b7619383fa53e631e2a04831378b31e80da55374b7c8b216d74fec49.svg

Note

Rendering DOT files requires a local Graphviz installation (the dot executable) in addition to the graphviz Python package.

NetworkX and Matplotlib

The to_networkx() adapter converts an AIG into a DiGraph, which can be laid out and drawn with NetworkX and Matplotlib. A full worked example that labels nodes with their level, fanout, type, and function is available in the NetworkX section of the Machine Learning Integration guide. A minimal version of the same workflow, using networkx.multipartite_layout to place every node on the row that matches its logic level (so all primary inputs line up on a single row) and coloring nodes by type:

 1import matplotlib.pyplot as plt
 2import networkx as nx
 3
 4import aigverse.adapters
 5
 6# Node type one-hot order is [constant, pi, gate, po]
 7type_colors = ["black", "#4C72B0", "#DDDDDD", "#55A868"]
 8
 9
10def draw_layered(graph, node_colors, node_sizes, *, title):
11    """Draws a NetworkX AIG graph with nodes arranged into rows by logic level."""
12    pos = nx.multipartite_layout(graph, subset_key="level", align="horizontal")
13    plt.figure(figsize=(8, 5))
14    nx.draw(
15        graph, pos, node_color=node_colors, node_size=node_sizes, edgecolors="black", linewidths=0.8,
16        arrows=True, arrowsize=10, width=0.8,
17    )
18    plt.title(title)
19    plt.show()
20
21
22# Convert the AIG to a NetworkX graph, including each node's logic level
23G = aig.to_networkx(levels=True)
24
25node_colors = [type_colors[data["type"].argmax()] for _, data in G.nodes(data=True)]
26draw_layered(G, node_colors, node_sizes=180, title="Ripple-carry adder structure")
_images/9df4b8c9607d218e4f44c3fb590f4f771b940e675808b25d8e7db9e363753963.svg

Highlighting Critical Paths and Fanout

Wrapping an AIG in DepthAig or FanoutAig exposes per-node critical-path and fanout information, which can be used to color-code a plot, making bottlenecks and high-congestion nodes immediately visible.

 1from aigverse.networks import DepthAig, FanoutAig
 2
 3depth_aig = DepthAig(aig)
 4fanout_aig = FanoutAig(aig)
 5
 6# Synthetic PO nodes (index >= aig.size) represent outputs, not real AIG nodes, so they are excluded here.
 7node_colors = ["#C44E52" if node < aig.size and depth_aig.is_on_critical_path(node) else "#DDDDDD" for node in G.nodes()]
 8node_sizes = [100 + 300 * fanout_aig.fanout_size(node) if node < aig.size else 100 for node in G.nodes()]
 9
10draw_layered(G, node_colors, node_sizes, title="Critical path (red) and fanout-scaled node size")
_images/8add18a3087a3d44458ff3feb441999a4ad6f3a5bae929016e5e664def5fcc22.svg

Interactive Exploration

For larger AIGs, a static plot quickly becomes hard to read. Interactive graph-drawing libraries such as pyvis or ipycytoscape can render the same DiGraph produced by to_networkx() as a zoomable, draggable graph with hover tooltips for node attributes. These are not dependencies of aigverse and must be installed separately.

Before vs. After: Visualizing Optimization

Comparing the DOT rendering of a network before and after an optimization pipeline visually confirms the effect of the transformation on structure, depth, and gate count. A 4-bit carry-lookahead adder makes for a good demonstration here, since (unlike the ripple-carry adder above) its structure still leaves room for the resubstitution, refactoring, and rewriting passes from the Algorithms guide to find and remove redundant logic.

 1from aigverse.algorithms import aig_cut_rewriting, aig_resubstitution, balancing, cleanup_dangling, sop_refactoring
 2from aigverse.generators import carry_lookahead_adder
 3from aigverse.networks import DepthAig
 4
 5# Generators can leave behind a handful of dead gates; clean those up first for a fair baseline
 6aig_cla = cleanup_dangling(carry_lookahead_adder(bitwidth=4))
 7
 8aig_optimized = aig_cla.clone()
 9aig_optimized = aig_resubstitution(aig_optimized)
10aig_optimized = sop_refactoring(aig_optimized)
11aig_optimized = aig_cut_rewriting(aig_optimized)
12aig_optimized = balancing(aig_optimized, rebalance_function="sop")
13
14write_dot(aig_cla, "before.dot")
15write_dot(aig_optimized, "after.dot")
16
17print(f"Before: {aig_cla.num_gates} gates, {DepthAig(aig_cla).num_levels} levels")
18print(f"After:  {aig_optimized.num_gates} gates, {DepthAig(aig_optimized).num_levels} levels")
Before: 34 gates, 8 levels
After:  28 gates, 6 levels
1graphviz.Source.from_file("before.dot")
_images/00629bc7f57db38991b49fc0149b67f5b2430f69edf1a19de9ded27e982345a6.svg
1graphviz.Source.from_file("after.dot")
_images/8bab07d70473af1b792149c0a84efd887197b77818edc03d67d242b50f631a09.svg