Source code for kqcircuits.util.netlist_graph

# This code is part of KQCircuits
# Copyright (C) 2021 IQM Finland Oy
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# https://www.gnu.org/licenses/gpl-3.0.html.
#
# The software distribution should follow IQM trademark policy for open-source software
# (meetiqm.com/iqm-open-source-trademark-policy). IQM welcomes contributions to the code.
# Please see our contribution agreements for individuals (meetiqm.com/iqm-individual-contributor-license-agreement)
# and organizations (meetiqm.com/iqm-organization-contributor-license-agreement).

import importlib.util
import networkx as nx
from kqcircuits.defaults import default_netlist_ignore_connections

spec = importlib.util.find_spec("matplotlib")
matplotlib_exists = spec is not None
if matplotlib_exists:
    from matplotlib import pyplot as plt


[docs] def network_as_graph(network): """ Import KQC netlist as networkx graph. Each element is added as a node, identified by the subcircuit id used in the netlist. For each node, the data dictionary contains the following items: * cell_name: identifies the PCell, for example ``Waveguide Coplanar$1``. Multiple nodes can point to the same PCell, if it has identical parameters. * cell_type: The PCell full name, for example ``Waveguide Coplanar`` * location: A list [x, y] specifying the element coordinates in um. * instance_name: The instance name of the PCell instance, may be an empty string * name: A unique name for this instance. Equal to instance_name if that exists, made unique by appending a numbered suffix if needed. If the instance name is empty, a string containing the subcircuit_id. * properties: A dictionary containing the PCell properties associated with this instance Args: network: dictionary of netlist data obtained by loading the netlist json file Returns. Networkx Graph """ edges = [] used_subcircuit_ids = set() used_names = set() # Add all edges from the netlist for net in network["nets"].values(): if len(net) >= 2: for i, net_i in enumerate(net): for net_j in net[i + 1 :]: reasons_to_ignore_connections = [ (a, b) for a, b in default_netlist_ignore_connections if (net_i["pin"] == a and net_j["pin"] == b) or (net_i["pin"] == b and net_j["pin"] == a) ] if len(reasons_to_ignore_connections) > 0: continue edges.append([net_i["subcircuit_id"], net_j["subcircuit_id"]]) used_subcircuit_ids.add(net_i["subcircuit_id"]) used_subcircuit_ids.add(net_j["subcircuit_id"]) graph = nx.Graph() graph.add_edges_from(edges) # Add data to the nodes for subcircuit_id in used_subcircuit_ids: subcircuit = network["subcircuits"][str(subcircuit_id)] graph.nodes[subcircuit_id]["cell_name"] = subcircuit["cell_name"] graph.nodes[subcircuit_id]["cell_type"] = subcircuit["cell_name"].split("$")[0].replace("*", " ") graph.nodes[subcircuit_id]["location"] = [ subcircuit["subcircuit_location"]["x"], subcircuit["subcircuit_location"]["y"], ] if "instance_name" in subcircuit and subcircuit["instance_name"] is not None: instance_name = subcircuit["instance_name"] else: instance_name = "" base_name = instance_name if instance_name != "" else str(subcircuit_id) # Define a unique name by suffixing with a number if needed name = base_name i = 0 while name in used_names: i += 1 name = f"{base_name}_{i}" used_names.add(base_name) graph.nodes[subcircuit_id]["instance_name"] = instance_name graph.nodes[subcircuit_id]["name"] = name graph.nodes[subcircuit_id]["properties"] = subcircuit.get("properties", {}) return graph
[docs] def draw_graph(graph, with_labels=True, with_position=True, figsize=(8, 8), export_path=None): """ Draw a netlist graph Args: graph: Networkx Graph with data structures as loaded by ``network_as_graph`` with_labels: if True, the unique ``name`` of each node will be shown as label with_position: if True, the nodes will be positioned as they are physically located on the chip. If False, a spring layout will be used to position the nodes. figsize: Figure size to pass to matplotlib, default (8, 8) export_path: Path to export image to, or None to show interactive plot """ if not matplotlib_exists: return plt.figure(figsize=figsize) if with_position: pos = {node: data["location"] for node, data in graph.nodes(data=True)} else: pos = nx.spring_layout(graph) labels = {node: data["name"] for node, data in graph.nodes(data=True)} nx.draw(graph, pos=pos, labels=labels, with_labels=with_labels) if export_path is not None: plt.savefig(export_path) else: plt.show()