Module circuitgraph.circuit
Class for circuit graphs
The Circuit class can be constructed from a generic verilog file or existing graph. Each node in the graph represents a logic gate and has an associated name and gate type. The supported types are:
- Standard input-order-independent gates: ['and','nand','or','nor','not','buf','xor','xnor']
- Inputs and Constant values: ['input','1','0']
- Sequential elements: ['ff','lat']
Additionally, nodes have the following attributes:
- Labeling node as an output ['output']
- Tracking sequential element connections ['clk','r','s']
Expand source code
"""Class for circuit graphs
The Circuit class can be constructed from a generic verilog file or
existing graph. Each node in the graph represents a logic gate and has
an associated name and gate type. The supported types are:
- Standard input-order-independent gates:
['and','nand','or','nor','not','buf','xor','xnor']
- Inputs and Constant values:
['input','1','0']
- Sequential elements:
['ff','lat']
Additionally, nodes have the following attributes:
- Labeling node as an output
['output']
- Tracking sequential element connections
['clk','r','s']
"""
import networkx as nx
from networkx.exception import NetworkXNoCycle
class Circuit:
"""Class for representing circuits"""
def __init__(self, graph=None, name=None):
"""
Parameters
----------
name : str
Name of circuit.
graph : networkx.DiGraph
Graph data structure to be used in new instance.
Examples
--------
Create an empty circuit.
>>> import circuitgraph as cg
>>> c = cg.Circuit()
Add an AND gate named 'x'.
>>> c.add('x','and')
Add an additional nodes and connect them.
>>> c.add('y','or',fanout='x')
>>> c.add('z','xor',fanin=['x','y'])
Another way to create the circuit is through a file.
>>> c = cg.from_file('path/circuit.v')
"""
if name:
self.name = name
else:
self.name = "circuit"
if graph:
self.graph = graph
else:
self.graph = nx.DiGraph()
def __contains__(self, n):
return self.graph.__contains__(n)
def __len__(self):
return self.graph.__len__()
def __iter__(self):
return self.graph.__iter__()
def set_type(self, ns, t):
"""
Returns node(s) type(s).
Parameters
----------
ns : str or iterable of str
Node.
t : str
Type.
"""
if isinstance(ns, str):
ns = [ns]
for n in ns:
self.graph.nodes[n]["type"] = t
def type(self, ns):
"""
Returns node(s) type(s).
Parameters
----------
ns : str or iterable of str
Node.
Returns
-------
str or list of str
Type of node or a list of node types.
Raises
------
KeyError
If type of queried node is not defined.
Examples
--------
Create a with several gate types.
>>> c = cg.Circuit()
>>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}', g)
Calling `type` for a single gate returns a single type
>>> c.type('g0')
{'xor'}
Calling `type` on an iterable returns a set of types
>>> c.type(c.nodes())
['xor', 'or', 'xor', 'ff']
"""
if isinstance(ns, str):
try:
return self.graph.nodes[ns]["type"]
except KeyError:
raise KeyError(f"Node {ns} does not have a type defined.")
return [self.type(n) for n in ns]
def set_output(self, ns, value=True):
"""
Sets node(s) as output.
Parameters
----------
ns : str or iterable of str
Node.
value : bool
Output value.
"""
if isinstance(ns, str):
ns = [ns]
for n in ns:
self.graph.nodes[n]["output"] = value
def output(self, ns):
"""
Returns node(s) output value.
Parameters
----------
ns : str or iterable of str
Node.
Returns
-------
str or list of str
Type of node or a list of node output values.
Raises
------
KeyError
If type of queried node is not defined.
"""
if isinstance(ns, str):
try:
return self.graph.nodes[ns]["output"]
except KeyError:
raise KeyError(f"Node {ns} does not have a output defined.")
return [self.output(n) for n in ns]
def nodes(self, types=None, output=None):
"""
Returns circuit nodes, optionally filtering by type
Parameters
----------
types : str or iterable of str
Type(s) to filter in.
output : str or iterable of str
Attributes(s) to filter in.
Returns
-------
set of str
Nodes
Examples
--------
Create a circuit with several gate types.
>>> c = cg.Circuit()
>>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}',g)
Calling `nodes` with no argument returns all nodes in the circuit
>>> c.nodes()
{'g0', 'g1', 'g2', 'g3'}
Passing a node type, we can selectively return nodes.
>>> c.nodes('xor')
{'g2', 'g0'}
"""
if isinstance(types, str):
types = [types]
if types is None and output is None:
return set(self.graph.nodes)
# return set(n for n in self.graph.nodes)
elif types is None:
return set(n for n in self.nodes() if self.output(n) == output)
elif output is None:
return set(n for n in self.nodes() if self.type(n) in types)
else:
return set(
n
for n in self.nodes()
if self.type(n) in types and self.output(n) == output
)
def edges(self):
"""
Returns circuit edges
Returns
-------
networkx.EdgeView
Edges in circuit
"""
return set(self.graph.edges)
def add(
self, n, type, fanin=None, fanout=None, clk=None, r=None, s=None, output=False
):
"""
Adds a new node to the circuit, optionally connecting it
Parameters
----------
n : str
New node name
type : str
New node type
fanin : iterable of str
Nodes to add to new node's fanin
fanout : iterable of str
Nodes to add to new node's fanout
clk : str
Clock connection of sequential element
r : str
Reset connection of sequential element
s : str
Set connection of sequential element
Returns
-------
str
New node name.
Example
-------
Add a single node
>>> import circuitgraph as cg
>>> c = cg.Circuit()
>>> c.add('a','or')
'a'
In the above example the function returns the name of the new node.
This allows us to quickly generate an AND tree with the following
syntax.
>>> c.add('g','and',fanin=[c.add(f'in_{i}','input') for i in range(4)])
'g'
>>> c.fanin('g')
{'in_1', 'in_0', 'in_3', 'in_2'}
"""
# clean arguments
if fanin is None:
fanin = []
elif isinstance(fanin, str):
fanin = [fanin]
if fanout is None:
fanout = []
elif isinstance(fanout, str):
fanout = [fanout]
# raise error for invalid inputs
if len(fanin) > 1 and type in ["ff", "lat", "buf", "not"]:
raise ValueError(f"{type} cannot have more than one fanin")
if fanin and type in ["0", "1", "input"]:
raise ValueError(f"{type} cannot have fanin")
if n[0] in "0123456789":
raise ValueError(f"cannot add node starting with int: {n}")
# add node
self.graph.add_node(n, type=type, r=r, s=s, clk=clk, output=output)
# connect
self.graph.add_edges_from((n, f) for f in fanout)
self.graph.add_edges_from((f, n) for f in fanin)
return n
def remove(self, ns):
"""
Removes node(s)
Parameters
----------
ns : str or iterable of str
Node(s) to remove.
"""
if isinstance(ns, str):
ns = [ns]
self.graph.remove_nodes_from(ns)
def extend(self, c, mapping=None):
"""
Adds nodes from another circuit
Parameters
----------
c : Circuit
Other circuit
"""
if mapping is None:
self.graph.update(c.graph)
else:
import circuitgraph.transform as tr
cr = tr.relabel(c, mapping)
self.graph.update(cr.graph)
def connect(self, us, vs):
"""
Adds connections to the graph
Parameters
----------
us : str or iterable of str
Head node(s)
vs : str or iterable of str
Tail node(s)
"""
if isinstance(us, str):
us = [us]
if isinstance(vs, str):
vs = [vs]
self.graph.add_edges_from((u, v) for u in us for v in vs)
def disconnect(self, us, vs):
"""
Removes connections to the graph
Parameters
----------
us : str or iterable of str
Head node(s)
vs : str or iterable of str
Tail node(s)
"""
if isinstance(us, str):
us = [us]
if isinstance(vs, str):
vs = [vs]
self.graph.remove_edges_from((u, v) for u in us for v in vs)
def fanin(self, ns):
"""
Computes the fanin of a node.
Parameters
----------
ns : str or iterable of str
Node(s) to compute fanin for.
Returns
-------
set of str
Nodes in fanin.
Example
-------
>>> c.fanout('n_20')
{'G17'}
>>> c.fanout('n_11')
{'n_12'}
>>> c.fanout(['n_11','n_20'])
{'n_12', 'G17'}
"""
gates = set()
if isinstance(ns, str):
ns = [ns]
for n in ns:
gates |= set(self.graph.predecessors(n))
return gates
def fanout(self, ns):
"""
Computes the fanout of a node.
Parameters
----------
ns : str or iterable of str
Node(s) to compute fanout for.
Returns
-------
set of str
Nodes in fanout.
"""
gates = set()
if isinstance(ns, str):
ns = [ns]
for n in ns:
gates |= set(self.graph.successors(n))
return gates
def transitive_fanin(
self, ns, stopat_types=["ff", "lat"], stopat_nodes=[], gates=None
):
"""
Computes the transitive fanin of a node.
Parameters
----------
ns : str or iterable of str
Node(s) to compute transitive fanin for.
stopat_types : iterable of str
Node types to stop recursion at.
stopat_nodes : iterable of str
Nodes to stop recursion at.
gates : set of str
Visited nodes.
Returns
-------
set of str
Nodes in transitive fanin.
"""
if gates is None:
gates = set()
if isinstance(ns, str):
ns = [ns]
for n in ns:
for p in self.graph.predecessors(n):
if p not in gates:
gates.add(p)
if self.type(p) not in stopat_types and p not in stopat_nodes:
self.transitive_fanin(p, stopat_types, stopat_nodes, gates)
return gates
def transitive_fanout(
self, ns, stopat_types=["ff", "lat"], stopat_nodes=[], gates=None
):
"""
Computes the transitive fanout of a node.
Parameters
----------
ns : str or iterable of str
Node(s) to compute transitive fanout for.
stopat_types : iterable of str
Node types to stop recursion at.
stopat_nodes : iterable of str
Nodes to stop recursion at.
gates : set of str
Visited nodes.
Returns
-------
set of str
Nodes in transitive fanout.
"""
if gates is None:
gates = set()
if isinstance(ns, str):
ns = [ns]
for n in ns:
for s in self.graph.successors(n):
if s not in gates:
gates.add(s)
if self.type(s) not in stopat_types and s not in stopat_nodes:
self.transitive_fanout(s, stopat_types, stopat_nodes, gates)
return gates
def fanin_comb_depth(self, ns, shortest=False, visited=None, depth=0):
"""
Computes the combinational fanin depth of a node(s).
Parameters
----------
ns : str or iterable of str
Node(s) to compute depth for.
shortest : bool
Selects between finding the shortest and longest paths.
Returns
-------
int
Depth.
"""
# select comparison function
comp = min if shortest else max
# find depth of a group
if not isinstance(ns, str):
return comp(self.fanin_comb_depth(n, shortest) for n in ns)
else:
n = ns
if visited is None:
visited = set()
depths = set()
depth += 1
visited.add(n)
for f in self.fanin(n):
if self.type(f) in ["ff", "lat", "input"] or f in visited:
depths.add(depth)
else:
depths.add(self.fanin_comb_depth(f, shortest, visited.copy(), depth))
return comp(depths)
def fanout_comb_depth(self, ns, shortest=False, visited=None, depth=0):
"""
Computes the combinational fanout depth of a node(s).
Parameters
----------
ns : str or iterable of str
Node(s) to compute depth for.
shortest : bool
Selects between finding the shortest and longest paths.
Returns
-------
int
Depth.
"""
# select comparison function
comp = min if shortest else max
# find depth of a group
if not isinstance(ns, str):
return comp(self.fanout_comb_depth(n, shortest) for n in ns)
else:
n = ns
if visited is None:
visited = set()
depths = set()
if self.output(n) or not self.fanout(n):
depths.add(depth)
visited.add(n)
for f in self.fanout(n):
if self.type(f) in ["ff", "lat"] or f in visited:
depths.add(depth)
else:
depths.add(
self.fanout_comb_depth(f, shortest, visited.copy(), depth + 1)
)
return comp(depths)
def lats(self):
"""
Returns the circuit's latches
Returns
-------
set of str
Latch nodes in circuit.
"""
return self.nodes("lat")
def ffs(self):
"""
Returns the circuit's flip-flops
Returns
-------
set of str
Flip-flop nodes in circuit.
"""
return self.nodes("ff")
def seq(self):
"""
Returns the circuit's sequential nodes
Returns
-------
set of str
Sequential nodes in circuit.
"""
return self.nodes(["ff", "lat"])
def r(self, ns):
"""
Returns sequential element's reset connection
Parameters
----------
ns : str or iterable of str
Node(s) to return reset for.
Returns
-------
set of str
Reset nodes.
"""
if isinstance(ns, str):
try:
return self.graph.nodes[ns]["r"]
except KeyError:
raise KeyError(f"Node {ns} does not have a reset defined.")
return [self.r(n) for n in ns]
def set_r(self, ns, r):
"""
Sets sequential element's reset connection
Parameters
----------
ns : str or iterable of str
Node(s) to set reset for.
r : str
Node(s) to use as reset.
"""
if isinstance(ns, str):
self.graph.nodes[ns]["r"] = r
else:
for n in ns:
self.set_r(n, r)
def s(self, ns):
"""
Returns sequential element's set connection
Parameters
----------
ns : str or iterable of str
Node(s) to return set for.
Returns
-------
set of str
Set nodes.
"""
if isinstance(ns, str):
try:
return self.graph.nodes[ns]["s"]
except KeyError:
raise KeyError(f"Node {ns} does not have a set defined.")
return [self.s(n) for n in ns]
def set_s(self, ns, s):
"""
Sets sequential element's set connection
Parameters
----------
ns : str or iterable of str
Node(s) to set set for.
s : str
Node(s) to use as set.
"""
if isinstance(ns, str):
self.graph.nodes[ns]["s"] = s
else:
for n in ns:
self.set_s(n, s)
def clk(self, ns):
"""
Returns sequential element's clk connection
Parameters
----------
ns : str or iterable of str
Node(s) to return clk for.
Returns
-------
set of str
Clk nodes.
"""
if isinstance(ns, str):
try:
return self.graph.nodes[ns]["clk"]
except KeyError:
raise KeyError(f"Node {ns} does not have a clk defined.")
return [self.clk(n) for n in ns]
def set_clk(self, ns, clk):
"""
Sets sequential element's clk connection
Parameters
----------
ns : str or iterable of str
Node(s) to set clk for.
clk : str
Node(s) to use as clk.
"""
if isinstance(ns, str):
self.graph.nodes[ns]["clk"] = clk
else:
for n in ns:
self.set_clk(n, clk)
def d(self, ns):
"""
Returns sequential element's d connection
Parameters
----------
ns : str or iterable of str
Node(s) to return d for.
Returns
-------
set of str
D nodes.
"""
if isinstance(ns, str):
try:
return self.fanin(ns).pop()
except KeyError:
raise KeyError(f"Node {ns} does not have a d defined.")
return [self.d(n) for n in ns]
def inputs(self):
"""
Returns the circuit's inputs
Returns
-------
set of str
Input nodes in circuit.
"""
return self.nodes("input")
def outputs(self):
"""
Returns the circuit's outputs
Returns
-------
set of str
Output nodes in circuit.
"""
return self.nodes(output=True)
def io(self):
"""
Returns the circuit's io
Returns
-------
set of str
Output and input nodes in circuit.
"""
return self.nodes("input") | self.nodes(output=True)
def startpoints(self, ns=None):
"""
Computes the startpoints of a node, nodes, or circuit.
Parameters
----------
ns : str or iterable of str
Node(s) to compute startpoints for.
Returns
-------
set of str
Startpoints of ns.
"""
if isinstance(ns, str):
ns = [ns]
circuit_startpoints = self.inputs() | self.seq()
if ns:
non_start = set(ns) - circuit_startpoints
return (set(ns) | self.transitive_fanin(non_start)) & circuit_startpoints
else:
return circuit_startpoints
def endpoints(self, ns=None):
"""
Computes the endpoints of a node, nodes, or circuit.
Parameters
----------
ns : str or iterable of str
Node(s) to compute endpoints for.
Returns
-------
set of str
Endpoints of ns.
"""
if isinstance(ns, str):
ns = [ns]
circuit_endpoints = self.outputs() | set(self.d(self.seq()))
if ns:
return (set(ns) | self.transitive_fanout(ns)) & circuit_endpoints
else:
return circuit_endpoints
def is_cyclic(self):
"""
Checks for combinational loops in circuit
Returns
-------
Bool
Existence of cycle
"""
g = self.graph.copy()
g.remove_edges_from(
(e, s) for s in self.startpoints() for e in self.endpoints()
)
try:
nx.find_cycle(g)
return True
except NetworkXNoCycle:
pass
return False
Classes
class Circuit (graph=None, name=None)
-
Class for representing circuits
Parameters
name
:str
- Name of circuit.
graph
:networkx.DiGraph
- Graph data structure to be used in new instance.
Examples
Create an empty circuit.
>>> import circuitgraph as cg >>> c = cg.Circuit()
Add an AND gate named 'x'.
>>> c.add('x','and')
Add an additional nodes and connect them.
>>> c.add('y','or',fanout='x') >>> c.add('z','xor',fanin=['x','y'])
Another way to create the circuit is through a file.
>>> c = cg.from_file('path/circuit.v')
Expand source code
class Circuit: """Class for representing circuits""" def __init__(self, graph=None, name=None): """ Parameters ---------- name : str Name of circuit. graph : networkx.DiGraph Graph data structure to be used in new instance. Examples -------- Create an empty circuit. >>> import circuitgraph as cg >>> c = cg.Circuit() Add an AND gate named 'x'. >>> c.add('x','and') Add an additional nodes and connect them. >>> c.add('y','or',fanout='x') >>> c.add('z','xor',fanin=['x','y']) Another way to create the circuit is through a file. >>> c = cg.from_file('path/circuit.v') """ if name: self.name = name else: self.name = "circuit" if graph: self.graph = graph else: self.graph = nx.DiGraph() def __contains__(self, n): return self.graph.__contains__(n) def __len__(self): return self.graph.__len__() def __iter__(self): return self.graph.__iter__() def set_type(self, ns, t): """ Returns node(s) type(s). Parameters ---------- ns : str or iterable of str Node. t : str Type. """ if isinstance(ns, str): ns = [ns] for n in ns: self.graph.nodes[n]["type"] = t def type(self, ns): """ Returns node(s) type(s). Parameters ---------- ns : str or iterable of str Node. Returns ------- str or list of str Type of node or a list of node types. Raises ------ KeyError If type of queried node is not defined. Examples -------- Create a with several gate types. >>> c = cg.Circuit() >>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}', g) Calling `type` for a single gate returns a single type >>> c.type('g0') {'xor'} Calling `type` on an iterable returns a set of types >>> c.type(c.nodes()) ['xor', 'or', 'xor', 'ff'] """ if isinstance(ns, str): try: return self.graph.nodes[ns]["type"] except KeyError: raise KeyError(f"Node {ns} does not have a type defined.") return [self.type(n) for n in ns] def set_output(self, ns, value=True): """ Sets node(s) as output. Parameters ---------- ns : str or iterable of str Node. value : bool Output value. """ if isinstance(ns, str): ns = [ns] for n in ns: self.graph.nodes[n]["output"] = value def output(self, ns): """ Returns node(s) output value. Parameters ---------- ns : str or iterable of str Node. Returns ------- str or list of str Type of node or a list of node output values. Raises ------ KeyError If type of queried node is not defined. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["output"] except KeyError: raise KeyError(f"Node {ns} does not have a output defined.") return [self.output(n) for n in ns] def nodes(self, types=None, output=None): """ Returns circuit nodes, optionally filtering by type Parameters ---------- types : str or iterable of str Type(s) to filter in. output : str or iterable of str Attributes(s) to filter in. Returns ------- set of str Nodes Examples -------- Create a circuit with several gate types. >>> c = cg.Circuit() >>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}',g) Calling `nodes` with no argument returns all nodes in the circuit >>> c.nodes() {'g0', 'g1', 'g2', 'g3'} Passing a node type, we can selectively return nodes. >>> c.nodes('xor') {'g2', 'g0'} """ if isinstance(types, str): types = [types] if types is None and output is None: return set(self.graph.nodes) # return set(n for n in self.graph.nodes) elif types is None: return set(n for n in self.nodes() if self.output(n) == output) elif output is None: return set(n for n in self.nodes() if self.type(n) in types) else: return set( n for n in self.nodes() if self.type(n) in types and self.output(n) == output ) def edges(self): """ Returns circuit edges Returns ------- networkx.EdgeView Edges in circuit """ return set(self.graph.edges) def add( self, n, type, fanin=None, fanout=None, clk=None, r=None, s=None, output=False ): """ Adds a new node to the circuit, optionally connecting it Parameters ---------- n : str New node name type : str New node type fanin : iterable of str Nodes to add to new node's fanin fanout : iterable of str Nodes to add to new node's fanout clk : str Clock connection of sequential element r : str Reset connection of sequential element s : str Set connection of sequential element Returns ------- str New node name. Example ------- Add a single node >>> import circuitgraph as cg >>> c = cg.Circuit() >>> c.add('a','or') 'a' In the above example the function returns the name of the new node. This allows us to quickly generate an AND tree with the following syntax. >>> c.add('g','and',fanin=[c.add(f'in_{i}','input') for i in range(4)]) 'g' >>> c.fanin('g') {'in_1', 'in_0', 'in_3', 'in_2'} """ # clean arguments if fanin is None: fanin = [] elif isinstance(fanin, str): fanin = [fanin] if fanout is None: fanout = [] elif isinstance(fanout, str): fanout = [fanout] # raise error for invalid inputs if len(fanin) > 1 and type in ["ff", "lat", "buf", "not"]: raise ValueError(f"{type} cannot have more than one fanin") if fanin and type in ["0", "1", "input"]: raise ValueError(f"{type} cannot have fanin") if n[0] in "0123456789": raise ValueError(f"cannot add node starting with int: {n}") # add node self.graph.add_node(n, type=type, r=r, s=s, clk=clk, output=output) # connect self.graph.add_edges_from((n, f) for f in fanout) self.graph.add_edges_from((f, n) for f in fanin) return n def remove(self, ns): """ Removes node(s) Parameters ---------- ns : str or iterable of str Node(s) to remove. """ if isinstance(ns, str): ns = [ns] self.graph.remove_nodes_from(ns) def extend(self, c, mapping=None): """ Adds nodes from another circuit Parameters ---------- c : Circuit Other circuit """ if mapping is None: self.graph.update(c.graph) else: import circuitgraph.transform as tr cr = tr.relabel(c, mapping) self.graph.update(cr.graph) def connect(self, us, vs): """ Adds connections to the graph Parameters ---------- us : str or iterable of str Head node(s) vs : str or iterable of str Tail node(s) """ if isinstance(us, str): us = [us] if isinstance(vs, str): vs = [vs] self.graph.add_edges_from((u, v) for u in us for v in vs) def disconnect(self, us, vs): """ Removes connections to the graph Parameters ---------- us : str or iterable of str Head node(s) vs : str or iterable of str Tail node(s) """ if isinstance(us, str): us = [us] if isinstance(vs, str): vs = [vs] self.graph.remove_edges_from((u, v) for u in us for v in vs) def fanin(self, ns): """ Computes the fanin of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute fanin for. Returns ------- set of str Nodes in fanin. Example ------- >>> c.fanout('n_20') {'G17'} >>> c.fanout('n_11') {'n_12'} >>> c.fanout(['n_11','n_20']) {'n_12', 'G17'} """ gates = set() if isinstance(ns, str): ns = [ns] for n in ns: gates |= set(self.graph.predecessors(n)) return gates def fanout(self, ns): """ Computes the fanout of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute fanout for. Returns ------- set of str Nodes in fanout. """ gates = set() if isinstance(ns, str): ns = [ns] for n in ns: gates |= set(self.graph.successors(n)) return gates def transitive_fanin( self, ns, stopat_types=["ff", "lat"], stopat_nodes=[], gates=None ): """ Computes the transitive fanin of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute transitive fanin for. stopat_types : iterable of str Node types to stop recursion at. stopat_nodes : iterable of str Nodes to stop recursion at. gates : set of str Visited nodes. Returns ------- set of str Nodes in transitive fanin. """ if gates is None: gates = set() if isinstance(ns, str): ns = [ns] for n in ns: for p in self.graph.predecessors(n): if p not in gates: gates.add(p) if self.type(p) not in stopat_types and p not in stopat_nodes: self.transitive_fanin(p, stopat_types, stopat_nodes, gates) return gates def transitive_fanout( self, ns, stopat_types=["ff", "lat"], stopat_nodes=[], gates=None ): """ Computes the transitive fanout of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute transitive fanout for. stopat_types : iterable of str Node types to stop recursion at. stopat_nodes : iterable of str Nodes to stop recursion at. gates : set of str Visited nodes. Returns ------- set of str Nodes in transitive fanout. """ if gates is None: gates = set() if isinstance(ns, str): ns = [ns] for n in ns: for s in self.graph.successors(n): if s not in gates: gates.add(s) if self.type(s) not in stopat_types and s not in stopat_nodes: self.transitive_fanout(s, stopat_types, stopat_nodes, gates) return gates def fanin_comb_depth(self, ns, shortest=False, visited=None, depth=0): """ Computes the combinational fanin depth of a node(s). Parameters ---------- ns : str or iterable of str Node(s) to compute depth for. shortest : bool Selects between finding the shortest and longest paths. Returns ------- int Depth. """ # select comparison function comp = min if shortest else max # find depth of a group if not isinstance(ns, str): return comp(self.fanin_comb_depth(n, shortest) for n in ns) else: n = ns if visited is None: visited = set() depths = set() depth += 1 visited.add(n) for f in self.fanin(n): if self.type(f) in ["ff", "lat", "input"] or f in visited: depths.add(depth) else: depths.add(self.fanin_comb_depth(f, shortest, visited.copy(), depth)) return comp(depths) def fanout_comb_depth(self, ns, shortest=False, visited=None, depth=0): """ Computes the combinational fanout depth of a node(s). Parameters ---------- ns : str or iterable of str Node(s) to compute depth for. shortest : bool Selects between finding the shortest and longest paths. Returns ------- int Depth. """ # select comparison function comp = min if shortest else max # find depth of a group if not isinstance(ns, str): return comp(self.fanout_comb_depth(n, shortest) for n in ns) else: n = ns if visited is None: visited = set() depths = set() if self.output(n) or not self.fanout(n): depths.add(depth) visited.add(n) for f in self.fanout(n): if self.type(f) in ["ff", "lat"] or f in visited: depths.add(depth) else: depths.add( self.fanout_comb_depth(f, shortest, visited.copy(), depth + 1) ) return comp(depths) def lats(self): """ Returns the circuit's latches Returns ------- set of str Latch nodes in circuit. """ return self.nodes("lat") def ffs(self): """ Returns the circuit's flip-flops Returns ------- set of str Flip-flop nodes in circuit. """ return self.nodes("ff") def seq(self): """ Returns the circuit's sequential nodes Returns ------- set of str Sequential nodes in circuit. """ return self.nodes(["ff", "lat"]) def r(self, ns): """ Returns sequential element's reset connection Parameters ---------- ns : str or iterable of str Node(s) to return reset for. Returns ------- set of str Reset nodes. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["r"] except KeyError: raise KeyError(f"Node {ns} does not have a reset defined.") return [self.r(n) for n in ns] def set_r(self, ns, r): """ Sets sequential element's reset connection Parameters ---------- ns : str or iterable of str Node(s) to set reset for. r : str Node(s) to use as reset. """ if isinstance(ns, str): self.graph.nodes[ns]["r"] = r else: for n in ns: self.set_r(n, r) def s(self, ns): """ Returns sequential element's set connection Parameters ---------- ns : str or iterable of str Node(s) to return set for. Returns ------- set of str Set nodes. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["s"] except KeyError: raise KeyError(f"Node {ns} does not have a set defined.") return [self.s(n) for n in ns] def set_s(self, ns, s): """ Sets sequential element's set connection Parameters ---------- ns : str or iterable of str Node(s) to set set for. s : str Node(s) to use as set. """ if isinstance(ns, str): self.graph.nodes[ns]["s"] = s else: for n in ns: self.set_s(n, s) def clk(self, ns): """ Returns sequential element's clk connection Parameters ---------- ns : str or iterable of str Node(s) to return clk for. Returns ------- set of str Clk nodes. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["clk"] except KeyError: raise KeyError(f"Node {ns} does not have a clk defined.") return [self.clk(n) for n in ns] def set_clk(self, ns, clk): """ Sets sequential element's clk connection Parameters ---------- ns : str or iterable of str Node(s) to set clk for. clk : str Node(s) to use as clk. """ if isinstance(ns, str): self.graph.nodes[ns]["clk"] = clk else: for n in ns: self.set_clk(n, clk) def d(self, ns): """ Returns sequential element's d connection Parameters ---------- ns : str or iterable of str Node(s) to return d for. Returns ------- set of str D nodes. """ if isinstance(ns, str): try: return self.fanin(ns).pop() except KeyError: raise KeyError(f"Node {ns} does not have a d defined.") return [self.d(n) for n in ns] def inputs(self): """ Returns the circuit's inputs Returns ------- set of str Input nodes in circuit. """ return self.nodes("input") def outputs(self): """ Returns the circuit's outputs Returns ------- set of str Output nodes in circuit. """ return self.nodes(output=True) def io(self): """ Returns the circuit's io Returns ------- set of str Output and input nodes in circuit. """ return self.nodes("input") | self.nodes(output=True) def startpoints(self, ns=None): """ Computes the startpoints of a node, nodes, or circuit. Parameters ---------- ns : str or iterable of str Node(s) to compute startpoints for. Returns ------- set of str Startpoints of ns. """ if isinstance(ns, str): ns = [ns] circuit_startpoints = self.inputs() | self.seq() if ns: non_start = set(ns) - circuit_startpoints return (set(ns) | self.transitive_fanin(non_start)) & circuit_startpoints else: return circuit_startpoints def endpoints(self, ns=None): """ Computes the endpoints of a node, nodes, or circuit. Parameters ---------- ns : str or iterable of str Node(s) to compute endpoints for. Returns ------- set of str Endpoints of ns. """ if isinstance(ns, str): ns = [ns] circuit_endpoints = self.outputs() | set(self.d(self.seq())) if ns: return (set(ns) | self.transitive_fanout(ns)) & circuit_endpoints else: return circuit_endpoints def is_cyclic(self): """ Checks for combinational loops in circuit Returns ------- Bool Existence of cycle """ g = self.graph.copy() g.remove_edges_from( (e, s) for s in self.startpoints() for e in self.endpoints() ) try: nx.find_cycle(g) return True except NetworkXNoCycle: pass return False
Methods
def add(self, n, type, fanin=None, fanout=None, clk=None, r=None, s=None, output=False)
-
Adds a new node to the circuit, optionally connecting it
Parameters
n
:str
- New node name
type
:str
- New node type
fanin
:iterable
ofstr
- Nodes to add to new node's fanin
fanout
:iterable
ofstr
- Nodes to add to new node's fanout
clk
:str
- Clock connection of sequential element
r
:str
- Reset connection of sequential element
s
:str
- Set connection of sequential element
Returns
str
- New node name.
Example
Add a single node
>>> import circuitgraph as cg >>> c = cg.Circuit() >>> c.add('a','or') 'a'
In the above example the function returns the name of the new node. This allows us to quickly generate an AND tree with the following syntax.
>>> c.add('g','and',fanin=[c.add(f'in_{i}','input') for i in range(4)]) 'g' >>> c.fanin('g') {'in_1', 'in_0', 'in_3', 'in_2'}
Expand source code
def add( self, n, type, fanin=None, fanout=None, clk=None, r=None, s=None, output=False ): """ Adds a new node to the circuit, optionally connecting it Parameters ---------- n : str New node name type : str New node type fanin : iterable of str Nodes to add to new node's fanin fanout : iterable of str Nodes to add to new node's fanout clk : str Clock connection of sequential element r : str Reset connection of sequential element s : str Set connection of sequential element Returns ------- str New node name. Example ------- Add a single node >>> import circuitgraph as cg >>> c = cg.Circuit() >>> c.add('a','or') 'a' In the above example the function returns the name of the new node. This allows us to quickly generate an AND tree with the following syntax. >>> c.add('g','and',fanin=[c.add(f'in_{i}','input') for i in range(4)]) 'g' >>> c.fanin('g') {'in_1', 'in_0', 'in_3', 'in_2'} """ # clean arguments if fanin is None: fanin = [] elif isinstance(fanin, str): fanin = [fanin] if fanout is None: fanout = [] elif isinstance(fanout, str): fanout = [fanout] # raise error for invalid inputs if len(fanin) > 1 and type in ["ff", "lat", "buf", "not"]: raise ValueError(f"{type} cannot have more than one fanin") if fanin and type in ["0", "1", "input"]: raise ValueError(f"{type} cannot have fanin") if n[0] in "0123456789": raise ValueError(f"cannot add node starting with int: {n}") # add node self.graph.add_node(n, type=type, r=r, s=s, clk=clk, output=output) # connect self.graph.add_edges_from((n, f) for f in fanout) self.graph.add_edges_from((f, n) for f in fanin) return n
def clk(self, ns)
-
Returns sequential element's clk connection
Parameters
ns
:str
oriterable
ofstr
- Node(s) to return clk for.
Returns
set
ofstr
- Clk nodes.
Expand source code
def clk(self, ns): """ Returns sequential element's clk connection Parameters ---------- ns : str or iterable of str Node(s) to return clk for. Returns ------- set of str Clk nodes. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["clk"] except KeyError: raise KeyError(f"Node {ns} does not have a clk defined.") return [self.clk(n) for n in ns]
def connect(self, us, vs)
-
Adds connections to the graph
Parameters
us
:str
oriterable
ofstr
- Head node(s)
vs
:str
oriterable
ofstr
- Tail node(s)
Expand source code
def connect(self, us, vs): """ Adds connections to the graph Parameters ---------- us : str or iterable of str Head node(s) vs : str or iterable of str Tail node(s) """ if isinstance(us, str): us = [us] if isinstance(vs, str): vs = [vs] self.graph.add_edges_from((u, v) for u in us for v in vs)
def d(self, ns)
-
Returns sequential element's d connection
Parameters
ns
:str
oriterable
ofstr
- Node(s) to return d for.
Returns
set
ofstr
- D nodes.
Expand source code
def d(self, ns): """ Returns sequential element's d connection Parameters ---------- ns : str or iterable of str Node(s) to return d for. Returns ------- set of str D nodes. """ if isinstance(ns, str): try: return self.fanin(ns).pop() except KeyError: raise KeyError(f"Node {ns} does not have a d defined.") return [self.d(n) for n in ns]
def disconnect(self, us, vs)
-
Removes connections to the graph
Parameters
us
:str
oriterable
ofstr
- Head node(s)
vs
:str
oriterable
ofstr
- Tail node(s)
Expand source code
def disconnect(self, us, vs): """ Removes connections to the graph Parameters ---------- us : str or iterable of str Head node(s) vs : str or iterable of str Tail node(s) """ if isinstance(us, str): us = [us] if isinstance(vs, str): vs = [vs] self.graph.remove_edges_from((u, v) for u in us for v in vs)
def edges(self)
-
Returns circuit edges
Returns
networkx.EdgeView
- Edges in circuit
Expand source code
def edges(self): """ Returns circuit edges Returns ------- networkx.EdgeView Edges in circuit """ return set(self.graph.edges)
def endpoints(self, ns=None)
-
Computes the endpoints of a node, nodes, or circuit.
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute endpoints for.
Returns
set
ofstr
- Endpoints of ns.
Expand source code
def endpoints(self, ns=None): """ Computes the endpoints of a node, nodes, or circuit. Parameters ---------- ns : str or iterable of str Node(s) to compute endpoints for. Returns ------- set of str Endpoints of ns. """ if isinstance(ns, str): ns = [ns] circuit_endpoints = self.outputs() | set(self.d(self.seq())) if ns: return (set(ns) | self.transitive_fanout(ns)) & circuit_endpoints else: return circuit_endpoints
def extend(self, c, mapping=None)
-
Expand source code
def extend(self, c, mapping=None): """ Adds nodes from another circuit Parameters ---------- c : Circuit Other circuit """ if mapping is None: self.graph.update(c.graph) else: import circuitgraph.transform as tr cr = tr.relabel(c, mapping) self.graph.update(cr.graph)
def fanin(self, ns)
-
Computes the fanin of a node.
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute fanin for.
Returns
set
ofstr
- Nodes in fanin.
Example
>>> c.fanout('n_20') {'G17'} >>> c.fanout('n_11') {'n_12'} >>> c.fanout(['n_11','n_20']) {'n_12', 'G17'}
Expand source code
def fanin(self, ns): """ Computes the fanin of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute fanin for. Returns ------- set of str Nodes in fanin. Example ------- >>> c.fanout('n_20') {'G17'} >>> c.fanout('n_11') {'n_12'} >>> c.fanout(['n_11','n_20']) {'n_12', 'G17'} """ gates = set() if isinstance(ns, str): ns = [ns] for n in ns: gates |= set(self.graph.predecessors(n)) return gates
def fanin_comb_depth(self, ns, shortest=False, visited=None, depth=0)
-
Computes the combinational fanin depth of a node(s).
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute depth for.
shortest
:bool
- Selects between finding the shortest and longest paths.
Returns
int
- Depth.
Expand source code
def fanin_comb_depth(self, ns, shortest=False, visited=None, depth=0): """ Computes the combinational fanin depth of a node(s). Parameters ---------- ns : str or iterable of str Node(s) to compute depth for. shortest : bool Selects between finding the shortest and longest paths. Returns ------- int Depth. """ # select comparison function comp = min if shortest else max # find depth of a group if not isinstance(ns, str): return comp(self.fanin_comb_depth(n, shortest) for n in ns) else: n = ns if visited is None: visited = set() depths = set() depth += 1 visited.add(n) for f in self.fanin(n): if self.type(f) in ["ff", "lat", "input"] or f in visited: depths.add(depth) else: depths.add(self.fanin_comb_depth(f, shortest, visited.copy(), depth)) return comp(depths)
def fanout(self, ns)
-
Computes the fanout of a node.
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute fanout for.
Returns
set
ofstr
- Nodes in fanout.
Expand source code
def fanout(self, ns): """ Computes the fanout of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute fanout for. Returns ------- set of str Nodes in fanout. """ gates = set() if isinstance(ns, str): ns = [ns] for n in ns: gates |= set(self.graph.successors(n)) return gates
def fanout_comb_depth(self, ns, shortest=False, visited=None, depth=0)
-
Computes the combinational fanout depth of a node(s).
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute depth for.
shortest
:bool
- Selects between finding the shortest and longest paths.
Returns
int
- Depth.
Expand source code
def fanout_comb_depth(self, ns, shortest=False, visited=None, depth=0): """ Computes the combinational fanout depth of a node(s). Parameters ---------- ns : str or iterable of str Node(s) to compute depth for. shortest : bool Selects between finding the shortest and longest paths. Returns ------- int Depth. """ # select comparison function comp = min if shortest else max # find depth of a group if not isinstance(ns, str): return comp(self.fanout_comb_depth(n, shortest) for n in ns) else: n = ns if visited is None: visited = set() depths = set() if self.output(n) or not self.fanout(n): depths.add(depth) visited.add(n) for f in self.fanout(n): if self.type(f) in ["ff", "lat"] or f in visited: depths.add(depth) else: depths.add( self.fanout_comb_depth(f, shortest, visited.copy(), depth + 1) ) return comp(depths)
def ffs(self)
-
Returns the circuit's flip-flops
Returns
set
ofstr
- Flip-flop nodes in circuit.
Expand source code
def ffs(self): """ Returns the circuit's flip-flops Returns ------- set of str Flip-flop nodes in circuit. """ return self.nodes("ff")
def inputs(self)
-
Returns the circuit's inputs
Returns
set
ofstr
- Input nodes in circuit.
Expand source code
def inputs(self): """ Returns the circuit's inputs Returns ------- set of str Input nodes in circuit. """ return self.nodes("input")
def io(self)
-
Returns the circuit's io
Returns
set
ofstr
- Output and input nodes in circuit.
Expand source code
def io(self): """ Returns the circuit's io Returns ------- set of str Output and input nodes in circuit. """ return self.nodes("input") | self.nodes(output=True)
def is_cyclic(self)
-
Checks for combinational loops in circuit
Returns
Bool
- Existence of cycle
Expand source code
def is_cyclic(self): """ Checks for combinational loops in circuit Returns ------- Bool Existence of cycle """ g = self.graph.copy() g.remove_edges_from( (e, s) for s in self.startpoints() for e in self.endpoints() ) try: nx.find_cycle(g) return True except NetworkXNoCycle: pass return False
def lats(self)
-
Returns the circuit's latches
Returns
set
ofstr
- Latch nodes in circuit.
Expand source code
def lats(self): """ Returns the circuit's latches Returns ------- set of str Latch nodes in circuit. """ return self.nodes("lat")
def nodes(self, types=None, output=None)
-
Returns circuit nodes, optionally filtering by type
Parameters
types
:str
oriterable
ofstr
- Type(s) to filter in.
output
:str
oriterable
ofstr
- Attributes(s) to filter in.
Returns
set
ofstr
- Nodes
Examples
Create a circuit with several gate types.
>>> c = cg.Circuit() >>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}',g)
Calling
nodes
with no argument returns all nodes in the circuit>>> c.nodes() {'g0', 'g1', 'g2', 'g3'}
Passing a node type, we can selectively return nodes.
>>> c.nodes('xor') {'g2', 'g0'}
Expand source code
def nodes(self, types=None, output=None): """ Returns circuit nodes, optionally filtering by type Parameters ---------- types : str or iterable of str Type(s) to filter in. output : str or iterable of str Attributes(s) to filter in. Returns ------- set of str Nodes Examples -------- Create a circuit with several gate types. >>> c = cg.Circuit() >>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}',g) Calling `nodes` with no argument returns all nodes in the circuit >>> c.nodes() {'g0', 'g1', 'g2', 'g3'} Passing a node type, we can selectively return nodes. >>> c.nodes('xor') {'g2', 'g0'} """ if isinstance(types, str): types = [types] if types is None and output is None: return set(self.graph.nodes) # return set(n for n in self.graph.nodes) elif types is None: return set(n for n in self.nodes() if self.output(n) == output) elif output is None: return set(n for n in self.nodes() if self.type(n) in types) else: return set( n for n in self.nodes() if self.type(n) in types and self.output(n) == output )
def output(self, ns)
-
Returns node(s) output value.
Parameters
ns
:str
oriterable
ofstr
- Node.
Returns
str
orlist
ofstr
- Type of node or a list of node output values.
Raises
KeyError
- If type of queried node is not defined.
Expand source code
def output(self, ns): """ Returns node(s) output value. Parameters ---------- ns : str or iterable of str Node. Returns ------- str or list of str Type of node or a list of node output values. Raises ------ KeyError If type of queried node is not defined. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["output"] except KeyError: raise KeyError(f"Node {ns} does not have a output defined.") return [self.output(n) for n in ns]
def outputs(self)
-
Returns the circuit's outputs
Returns
set
ofstr
- Output nodes in circuit.
Expand source code
def outputs(self): """ Returns the circuit's outputs Returns ------- set of str Output nodes in circuit. """ return self.nodes(output=True)
def r(self, ns)
-
Returns sequential element's reset connection
Parameters
ns
:str
oriterable
ofstr
- Node(s) to return reset for.
Returns
set
ofstr
- Reset nodes.
Expand source code
def r(self, ns): """ Returns sequential element's reset connection Parameters ---------- ns : str or iterable of str Node(s) to return reset for. Returns ------- set of str Reset nodes. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["r"] except KeyError: raise KeyError(f"Node {ns} does not have a reset defined.") return [self.r(n) for n in ns]
def remove(self, ns)
-
Removes node(s)
Parameters
ns
:str
oriterable
ofstr
- Node(s) to remove.
Expand source code
def remove(self, ns): """ Removes node(s) Parameters ---------- ns : str or iterable of str Node(s) to remove. """ if isinstance(ns, str): ns = [ns] self.graph.remove_nodes_from(ns)
def s(self, ns)
-
Returns sequential element's set connection
Parameters
ns
:str
oriterable
ofstr
- Node(s) to return set for.
Returns
set
ofstr
- Set nodes.
Expand source code
def s(self, ns): """ Returns sequential element's set connection Parameters ---------- ns : str or iterable of str Node(s) to return set for. Returns ------- set of str Set nodes. """ if isinstance(ns, str): try: return self.graph.nodes[ns]["s"] except KeyError: raise KeyError(f"Node {ns} does not have a set defined.") return [self.s(n) for n in ns]
def seq(self)
-
Returns the circuit's sequential nodes
Returns
set
ofstr
- Sequential nodes in circuit.
Expand source code
def seq(self): """ Returns the circuit's sequential nodes Returns ------- set of str Sequential nodes in circuit. """ return self.nodes(["ff", "lat"])
def set_clk(self, ns, clk)
-
Sets sequential element's clk connection
Parameters
ns
:str
oriterable
ofstr
- Node(s) to set clk for.
clk
:str
- Node(s) to use as clk.
Expand source code
def set_clk(self, ns, clk): """ Sets sequential element's clk connection Parameters ---------- ns : str or iterable of str Node(s) to set clk for. clk : str Node(s) to use as clk. """ if isinstance(ns, str): self.graph.nodes[ns]["clk"] = clk else: for n in ns: self.set_clk(n, clk)
def set_output(self, ns, value=True)
-
Sets node(s) as output.
Parameters
ns
:str
oriterable
ofstr
- Node.
value
:bool
- Output value.
Expand source code
def set_output(self, ns, value=True): """ Sets node(s) as output. Parameters ---------- ns : str or iterable of str Node. value : bool Output value. """ if isinstance(ns, str): ns = [ns] for n in ns: self.graph.nodes[n]["output"] = value
def set_r(self, ns, r)
-
Sets sequential element's reset connection
Parameters
ns
:str
oriterable
ofstr
- Node(s) to set reset for.
r
:str
- Node(s) to use as reset.
Expand source code
def set_r(self, ns, r): """ Sets sequential element's reset connection Parameters ---------- ns : str or iterable of str Node(s) to set reset for. r : str Node(s) to use as reset. """ if isinstance(ns, str): self.graph.nodes[ns]["r"] = r else: for n in ns: self.set_r(n, r)
def set_s(self, ns, s)
-
Sets sequential element's set connection
Parameters
ns
:str
oriterable
ofstr
- Node(s) to set set for.
s
:str
- Node(s) to use as set.
Expand source code
def set_s(self, ns, s): """ Sets sequential element's set connection Parameters ---------- ns : str or iterable of str Node(s) to set set for. s : str Node(s) to use as set. """ if isinstance(ns, str): self.graph.nodes[ns]["s"] = s else: for n in ns: self.set_s(n, s)
def set_type(self, ns, t)
-
Returns node(s) type(s).
Parameters
ns
:str
oriterable
ofstr
- Node.
t
:str
- Type.
Expand source code
def set_type(self, ns, t): """ Returns node(s) type(s). Parameters ---------- ns : str or iterable of str Node. t : str Type. """ if isinstance(ns, str): ns = [ns] for n in ns: self.graph.nodes[n]["type"] = t
def startpoints(self, ns=None)
-
Computes the startpoints of a node, nodes, or circuit.
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute startpoints for.
Returns
set
ofstr
- Startpoints of ns.
Expand source code
def startpoints(self, ns=None): """ Computes the startpoints of a node, nodes, or circuit. Parameters ---------- ns : str or iterable of str Node(s) to compute startpoints for. Returns ------- set of str Startpoints of ns. """ if isinstance(ns, str): ns = [ns] circuit_startpoints = self.inputs() | self.seq() if ns: non_start = set(ns) - circuit_startpoints return (set(ns) | self.transitive_fanin(non_start)) & circuit_startpoints else: return circuit_startpoints
def transitive_fanin(self, ns, stopat_types=['ff', 'lat'], stopat_nodes=[], gates=None)
-
Computes the transitive fanin of a node.
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute transitive fanin for.
stopat_types
:iterable
ofstr
- Node types to stop recursion at.
stopat_nodes
:iterable
ofstr
- Nodes to stop recursion at.
gates
:set
ofstr
- Visited nodes.
Returns
set
ofstr
- Nodes in transitive fanin.
Expand source code
def transitive_fanin( self, ns, stopat_types=["ff", "lat"], stopat_nodes=[], gates=None ): """ Computes the transitive fanin of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute transitive fanin for. stopat_types : iterable of str Node types to stop recursion at. stopat_nodes : iterable of str Nodes to stop recursion at. gates : set of str Visited nodes. Returns ------- set of str Nodes in transitive fanin. """ if gates is None: gates = set() if isinstance(ns, str): ns = [ns] for n in ns: for p in self.graph.predecessors(n): if p not in gates: gates.add(p) if self.type(p) not in stopat_types and p not in stopat_nodes: self.transitive_fanin(p, stopat_types, stopat_nodes, gates) return gates
def transitive_fanout(self, ns, stopat_types=['ff', 'lat'], stopat_nodes=[], gates=None)
-
Computes the transitive fanout of a node.
Parameters
ns
:str
oriterable
ofstr
- Node(s) to compute transitive fanout for.
stopat_types
:iterable
ofstr
- Node types to stop recursion at.
stopat_nodes
:iterable
ofstr
- Nodes to stop recursion at.
gates
:set
ofstr
- Visited nodes.
Returns
set
ofstr
- Nodes in transitive fanout.
Expand source code
def transitive_fanout( self, ns, stopat_types=["ff", "lat"], stopat_nodes=[], gates=None ): """ Computes the transitive fanout of a node. Parameters ---------- ns : str or iterable of str Node(s) to compute transitive fanout for. stopat_types : iterable of str Node types to stop recursion at. stopat_nodes : iterable of str Nodes to stop recursion at. gates : set of str Visited nodes. Returns ------- set of str Nodes in transitive fanout. """ if gates is None: gates = set() if isinstance(ns, str): ns = [ns] for n in ns: for s in self.graph.successors(n): if s not in gates: gates.add(s) if self.type(s) not in stopat_types and s not in stopat_nodes: self.transitive_fanout(s, stopat_types, stopat_nodes, gates) return gates
def type(self, ns)
-
Returns node(s) type(s).
Parameters
ns
:str
oriterable
ofstr
- Node.
Returns
str
orlist
ofstr
- Type of node or a list of node types.
Raises
KeyError
- If type of queried node is not defined.
Examples
Create a with several gate types.
>>> c = cg.Circuit() >>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}', g)
Calling
type
for a single gate returns a single type>>> c.type('g0') {'xor'}
Calling
type
on an iterable returns a set of types>>> c.type(c.nodes()) ['xor', 'or', 'xor', 'ff']
Expand source code
def type(self, ns): """ Returns node(s) type(s). Parameters ---------- ns : str or iterable of str Node. Returns ------- str or list of str Type of node or a list of node types. Raises ------ KeyError If type of queried node is not defined. Examples -------- Create a with several gate types. >>> c = cg.Circuit() >>> for i,g in enumerate(['xor','or','xor','ff']): c.add(f'g{i}', g) Calling `type` for a single gate returns a single type >>> c.type('g0') {'xor'} Calling `type` on an iterable returns a set of types >>> c.type(c.nodes()) ['xor', 'or', 'xor', 'ff'] """ if isinstance(ns, str): try: return self.graph.nodes[ns]["type"] except KeyError: raise KeyError(f"Node {ns} does not have a type defined.") return [self.type(n) for n in ns]