Interactive Circuit Graphs¶
Often, a static plot is incomprehensible or unhandy. Fortunately, graphs can be made interactive using Cytoscape. Netlist Carpentry provides a modern way to create interactive circuit visualizations using ipycytoscape, which renders directly in Jupyter notebooks. Execute the cell below to read the design file as usual and set up the graph so that it can be displayed interactively.
import netlist_carpentry
circuit = netlist_carpentry.read("../files/decentral_mux.v", top="decentral_mux")
circuit.top.optimize()
GRAPH = circuit.top.graph()
The show() Convenience Function¶
For quick visualization using a predefined format, the show() function can be used directly. It handles graph creation, formatting, and display in a single call.
This creates a CytoscapeGraph with pre-configured IMMS styles, applies input/output node coloring, and displays the interactive widget automatically.
from netlist_carpentry import show
show(GRAPH)
# Alternatively provide the module: show(circuit["decentral_mux"])
Interacting with the Graph¶
The graph supports node dragging, zooming, and panning natively within the notebook.
- Click a node to show its type (i.e.,
"input"/"output"for ports, or the instance type) instead of the name. Click again to hide the type and show the name again. - Hover over an edge to show the wire name that this edge is representing. In addition, the bit width of the wire is also shown. Click the edge to lock the name, and click it again to release it, so the name is hidden as soon as the cursor leaves the edge.
If a node is clicked once, it is selected (highlighted in a different color) and additional information (type, parameters, etc.) is shown in the info box in the top left corner.
Customizing the Visualization¶
You can fully customize the appearance using FormatDefinition objects.
from netlist_carpentry.vis.dynamic import CytoscapeGraph
from netlist_carpentry.vis.styling import FormatDefinition
cyto = CytoscapeGraph(module_graph=GRAPH)
# Custom node style: dark blue and rounded rectangle
port_format = FormatDefinition(background_color="#093D74", shape='round-rectangle')
and_format = FormatDefinition(background_color="#FBD100", shape='star', color="brown")
cyto.formats.add_format('.port', port_format)
cyto.formats.add_format('.and', and_format)
# Now all nodes representing ports will be formatted with the ".port" format
cyto.format_nodes(lambda node_id, node_data: node_data["ntype"]=="PORT", ".port")
# Now all nodes with "and" in their name will be formatted with the ".and" format
cyto.format_nodes(lambda node_id, node_data: "and" in node_id.lower(), ".and")
cyto.show()
In the cell above, two formats are created:
.portdefines a format, where the node is a dark-blue rounded rectangle.anddefines a format, where the node is a goldish star-shaped node, and the text is brown
With the format_nodes() method, nodes can be formatted based on a given function.
The function takes two parameters, one being the node identifier, and the other being the node data.
The node data is a dictionary with keys 'ntype' ('PORT' or 'INSTANCE'), 'nsubtype' ('input'/'output' for ports, or the instance type if it is an instance), and 'ndata', which is the actual circuit object (a Port or Instance instance).
In the cell below, a small example function is shown checking the type of a node via the circuit object and formatting it if it contains an 'e'.
from netlist_carpentry import Instance
from netlist_carpentry.vis.dynamic.ipycytoscape import GraphDataDict
def has_e_in_type(node_id: str, node_data: GraphDataDict) -> bool:
circuit_object = node_data["ndata"]
if isinstance(circuit_object, Instance):
if "e" in circuit_object.instance_type.lower():
return True
return False
e_format = FormatDefinition(background_color="#FB1100", shape='rhomboid')
cyto.formats.add_format('.e', e_format)
cyto.format_nodes(has_e_in_type, ".e")
cyto.show()
There is also a method specifically for formatting input and output nodes. In the cell below the usage is shown. Two formats are created that change a node to a pink or green diamond shape, depending on the direction. Execute the cell below to make the inputs pink diamonds, and the outputs green diamonds.
in_format = FormatDefinition(shape="diamond", background_color="#FD48C0")
out_format = FormatDefinition(shape="diamond", background_color="#00A405")
cyto.formats.add_format(".in", in_format)
cyto.formats.add_format(".out", out_format)
cyto.format_in_out(in_format=".in", out_format=".out")
cyto.show()