Wiring Diagram

Wiring-diagram representation for dynamical systems.

This module defines the WiringDiagram class, which encodes the directed regulatory topology of a dynamical system independently of any update functions.

A wiring diagram specifies, for each node, the set of regulating nodes (predecessors). Nodes with no regulators are source nodes in the wiring diagram. Whether such nodes act as constants or identity nodes in dynamical systems can only be determined after dynamical update functions are assigned.

class boolforge.wiring_diagram.WiringDiagram(I: Sequence[Sequence[int]], variables: list[str] | ndarray | None = None, weights: Sequence[Sequence[float]] | None = None)[source]

Bases: WiringDiagramInteroperabilityMixin, WiringDiagramModularityMixin, WiringDiagramMotifMixin, WiringDiagramPlottingMixin

Directed wiring diagram for a dynamical system.

A wiring diagram specifies the regulatory topology of a dynamical system by listing, for each node, the indices of its regulators (incoming edges). It does not encode dynamical update functions or dynamical rules.

Nodes with zero indegree are source nodes. Whether a source node represents a constant, an identity node, or a dynamic variable is determined only after dynamical update functions are assigned.

Parameters

Isequence of sequences of int

Adjacency-list representation of the wiring diagram. Entry I[i] contains the indices of nodes regulating node i. Regulator lists may have variable length.

variableslist[str] or np.ndarray[str], optional

Names of the variables corresponding to each node. Must have length N if provided. If None, default names ['x0', 'x1', ..., 'x{N-1}'] are assigned.

weightssequence of sequences of float, optional

Interaction weights corresponding to I. Entry weights[i][j] gives the weight of the interaction from regulator I[i][j] to node i. Missing interactions may be encoded as np.nan.

Attributes

Ilist[np.ndarray]

Regulator index arrays, one per node.

variablesnp.ndarray[str]

Names of variables corresponding to each node.

Nint

Total number of nodes, including source nodes.

indegreesnp.ndarray[int]

Indegree of each node.

outdegreesnp.ndarray[int]

Outdegree of each node.

weightslist[np.ndarray] or None

Interaction weights associated with I if provided.

Notes

  • Node indices are zero-based.

  • Source nodes are identified solely by indegree == 0.

  • The wiring diagram encodes topology only and does not define dynamics.

Examples

Nodes with zero indegree are interpreted as source nodes.

>>> from boolforge import WiringDiagram
>>> I = [
        [],        # node 0 has no regulators -> source node
        [0],       # node 1 is regulated by node 0
        [0, 1],    # node 2 is regulated by nodes 0 and 1
    ]
>>> W = WiringDiagram(I)
>>> W.N
3
>>> W.get_source_nodes(as_dict=True)
{0: True, 1: False, 2: False}
>>> W.get_source_nodes(as_dict=False)
array([0])

See Also

boolforge.BooleanNetwork

__str__()[source]

Return str(self).

__repr__()[source]

Return repr(self).

get_outdegrees() ndarray[source]

Compute the outdegree of each node.

The outdegree of a node is the number of nodes it regulates, i.e., the number of outgoing edges from that node in the wiring diagram.

Returns

np.ndarray

One-dimensional array of length N containing the outdegree of each node.

__weakref__

list of weak references to the object

classmethod from_DiGraph(nx_DiGraph: nx.DiGraph) WiringDiagram

Construct a WiringDiagram from a NetworkX directed graph.

Each node in the directed graph represents a Boolean variable, and each directed edge u -> v indicates that variable u regulates variable v.

Parameters

nx_DiGraphnx.DiGraph

Directed graph whose nodes represent variables and whose edges represent regulatory interactions.

Node attributes (optional)
namestr

Name of the variable. If not provided, the node label is used when possible.

Edge attributes (optional)
weightfloat

Weight of the regulatory interaction from u to v. If present on all edges, weights are stored in the weights attribute of the resulting WiringDiagram.

Returns

WiringDiagram

Wiring diagram representing the topology of the directed graph.

Notes

  • Nodes are ordered according to the iteration order of nx_DiGraph.nodes.

  • Regulator lists are constructed from incoming edges (graph predecessors).

  • Edge weights are only stored if all edges define a 'weight' attribute.

Examples

>>> import networkx as nx
>>> from boolforge import WiringDiagram
>>> G = nx.DiGraph()
>>> G.add_edges_from([(0, 1), (1, 2)])
>>> W = WiringDiagram.from_DiGraph(G)
>>> W.I
[array([], dtype=int64), array([0]), array([1])]
>>> W.get_source_nodes()
{0: True, 1: False, 2: False}
get_fbls(max_length: int = 4, classify: bool = False) dict

Identify feedback loops (simple directed cycles).

A feedback loop is defined as a simple directed cycle in the underlying wiring diagram. This method enumerates elementary circuits using a variant of Johnson’s algorithm, restricted to cycles of length at most max_length.

Parameters

max_lengthint, optional

Maximum length of feedback loops to consider. Cycles longer than max_length are not returned. Default is 4.

classifybool, optional

If True and interaction weights are available, the type of each feedback loop is determined.

Returns

dict

list of list of int List of feedback loops, where each loop is represented as a list of node indices forming a directed cycle. Self-loops are returned as single-element lists.

get_ffls() tuple[list[list[int]], list[list[float]] | None]

Identify feed-forward loops (FFLs) in the wiring diagram.

A feed-forward loop is a triplet of nodes (i, j, k) such that i -> j, j -> k, and i -> k are all regulatory interactions.

Returns

tuple

A tuple (ffls, types) where:

  • ffls is a list of feed-forward loops, each represented as [i, j, k].

  • types is a list of corresponding edge-weight triplets [i -> k, i -> j, j -> k] if interaction weights are defined, or None otherwise.

get_modular_structure() set[tuple[int, int]]

Compute the modular (condensation) structure of the wiring diagram.

The modular structure is represented as a directed acyclic graph (DAG) whose nodes correspond to strongly connected components (SCCs) of the wiring diagram. A directed edge (i, j) indicates that there exists at least one regulatory interaction from SCC i to SCC j.

Returns

set of tuple of int

Set of directed edges representing the condensation DAG, where each edge (i, j) denotes a regulation from SCC i to SCC j.

Notes

SCC indices correspond to the ordering returned by get_strongly_connected_components.

get_source_nodes(as_dict: bool = True) dict[int, bool] | ndarray[source]

Identify source nodes in the wiring diagram.

A source node is a node with zero indegree. Source nodes represent inputs to the wiring diagram; whether they act as constants or identity nodes in dynamical systems depends on the associated dynamical update functions and is not determined at the wiring-diagram level.

Parameters

as_dictbool, optional

If True (default), return a dictionary mapping node indices to Boolean values indicating whether each node is a source node. If False, return an array of indices corresponding to source nodes.

Returns

dict[int, bool] or np.ndarray

If as_dict is True, a dictionary where keys are node indices and values indicate whether the node is a source node. If as_dict is False, a one-dimensional array containing the indices of source nodes.

get_strongly_connected_components() list

Compute the strongly connected components of the wiring diagram.

A strongly connected component (SCC) is a maximal set of nodes such that every node in the set is reachable from every other node via directed paths.

Returns

list of set of int

List of strongly connected components, where each component is represented as a set of node indices.

plot(max_expanded_sccs: int = 4, min_scc_size: int = 2, show: bool = True, curviness: float = 0.25)

Plot an integrated overview of the wiring diagram.

The plot consists of:
  1. A top panel showing the modular structure of the network as a DAG of strongly connected components (SCCs).

  2. Bottom panels showing the internal wiring of selected SCCs using a circular layout.

By default, the largest SCCs of size >= min_scc_size are expanded, up to max_expanded_sccs.

Parameters

max_expanded_sccsint, default=4

Maximum number of SCCs to expand and show in detail.

min_scc_sizeint, default=2

Minimum SCC size to be eligible for expansion.

showbool, default=True

Whether to call plt.show() at the end.

curvinessfloat, default=0.25

Curvature of edges spanning multiple layers in the modular graph (0 = straight).

Returns

figmatplotlib.figure.Figure

The created figure.

plot_modular_structure(ax=None, show=True, node_labels: bool = True, max_nodes: int = 50, curviness: float = 0.25)

Plot the wiring diagram as a directed acyclic graph of strongly connected components.

The wiring diagram is first condensed into its strongly connected components (SCCs), yielding a directed acyclic graph (DAG). Each node in the plot represents one SCC.

The layout is hierarchical (top-to-bottom) using topological generations, making feed-forward structure visually apparent, while condensing feedback loops.

Parameters

axmatplotlib.axes.Axes, optional

Axis to draw on. If None, a new figure and axis are created.

showbool, default=True

Whether to call plt.show() after plotting.

node_labelsbool, default=True

Whether to label SCC nodes by their size (only shown for SCCs of size > 1).

max_nodesint, default=50

If the number of SCCs exceeds this value, edges are sparsified to reduce clutter.

curvinessfloat, default=0.25

Curvature of edges spanning multiple layers (0 = straight).

Returns

axmatplotlib.axes.Axes

The axis containing the plot.

to_DiGraph(use_variable_names: bool = True) DiGraph

Convert the wiring diagram into a NetworkX directed graph.

A directed edge u -> v indicates that node u regulates node v.

Parameters

use_variable_namesbool, optional

If True (default), nodes are labeled using the variable names stored in self.variables. If False, nodes are labeled by integer indices 0, 1, ..., N-1.

Returns

nx.DiGraph

Directed graph representing the wiring diagram. If interaction weights are present, they are stored as edge attributes under the key 'weight'.

Notes

  • The node ordering follows the internal ordering of the wiring diagram.

  • Incoming edges of node i correspond to the regulators listed in self.I[i].

  • Edge weights are included only if the wiring diagram defines weights.