Multilayer¶
Multilayer graphs attach vertices and edges to elementary layer coordinates such as condition or time.
In [1]:
Copied!
import annnet as an
import annnet as an
Define layers and supra-nodes¶
In [2]:
Copied!
M = an.AnnNet(directed=False, aspects={'condition': ['ctrl', 'stim']})
M.add_vertices(['A', 'B'], layer=('ctrl',))
M.add_vertices(['A', 'B'], layer=('stim',))
print('aspects:', M.layers.list_aspects())
print('layers:', M.layers.list_layers())
print('global shape:', M.shape)
M = an.AnnNet(directed=False, aspects={'condition': ['ctrl', 'stim']})
M.add_vertices(['A', 'B'], layer=('ctrl',))
M.add_vertices(['A', 'B'], layer=('stim',))
print('aspects:', M.layers.list_aspects())
print('layers:', M.layers.list_layers())
print('global shape:', M.shape)
aspects: ('condition',)
layers: {'condition': ['ctrl', 'stim']}
global shape: (2, 0)
Intra-layer and coupling edges¶
In [3]:
Copied!
M.add_edges(('A', ('ctrl',)), ('B', ('ctrl',)), edge_id='e_ctrl', weight=1.0)
M.add_edges(('A', ('stim',)), ('B', ('stim',)), edge_id='e_stim', weight=1.4)
n_couplings = M.layers.add_layer_coupling_pairs([(('ctrl',), ('stim',))])
print('coupling edges added:', n_couplings)
M.views.edges().select(['edge_id', 'kind', 'source', 'target', 'effective_weight'])
M.add_edges(('A', ('ctrl',)), ('B', ('ctrl',)), edge_id='e_ctrl', weight=1.0)
M.add_edges(('A', ('stim',)), ('B', ('stim',)), edge_id='e_stim', weight=1.4)
n_couplings = M.layers.add_layer_coupling_pairs([(('ctrl',), ('stim',))])
print('coupling edges added:', n_couplings)
M.views.edges().select(['edge_id', 'kind', 'source', 'target', 'effective_weight'])
coupling edges added: 2
Out[3]:
shape: (4, 5)
| edge_id | kind | source | target | effective_weight |
|---|---|---|---|---|
| str | str | str | str | f64 |
| "e_ctrl" | "binary" | "('A', ('ctrl',))" | "('B', ('ctrl',))" | 1.0 |
| "e_stim" | "binary" | "('A', ('stim',))" | "('B', ('stim',))" | 1.4 |
| "A>A@ctrl~stim" | "binary" | "('A', ('ctrl',))" | "('A', ('stim',))" | 1.0 |
| "B>B@ctrl~stim" | "binary" | "('B', ('ctrl',))" | "('B', ('stim',))" | 1.0 |
Draw the supra graph¶
Supra-nodes include both the base vertex and its layer coordinate, so the drawing makes coupling edges visible.
In [4]:
Copied!
from annnet.utils import plotting
plotting.plot(M, backend='graphviz', show_edge_labels=True)
from annnet.utils import plotting
plotting.plot(M, backend='graphviz', show_edge_labels=True)
Out[4]:
Layer selections and derived slices¶
In [5]:
Copied!
stim_graph = M.layers.subgraph_from_layer_tuple(('stim',))
M.layers.create_slice_from_layer('stim_only', ('stim',))
print('stim graph shape:', stim_graph.shape)
print('stim slice edges:', sorted(M.slices.edges('stim_only')))
stim_graph = M.layers.subgraph_from_layer_tuple(('stim',))
M.layers.create_slice_from_layer('stim_only', ('stim',))
print('stim graph shape:', stim_graph.shape)
print('stim slice edges:', sorted(M.slices.edges('stim_only')))
stim graph shape: (2, 1) stim slice edges: ['e_stim']
Draw one layer as its own graph¶
In [6]:
Copied!
plotting.plot(stim_graph, backend='graphviz', show_edge_labels=True)
plotting.plot(stim_graph, backend='graphviz', show_edge_labels=True)
Out[6]:
Supra matrices and a diffusion step¶
In [7]:
Copied!
import numpy as np
adjacency = M.layers.supra_adjacency()
start = np.zeros(M.nv_supra)
start[0] = 1.0
diffused = M.layers.diffusion_step(start, tau=0.25)
print('supra-adjacency shape:', adjacency.shape)
print('supra-adjacency nnz:', adjacency.nnz)
print('diffused mass:', round(float(diffused.sum()), 3))
import numpy as np
adjacency = M.layers.supra_adjacency()
start = np.zeros(M.nv_supra)
start[0] = 1.0
diffused = M.layers.diffusion_step(start, tau=0.25)
print('supra-adjacency shape:', adjacency.shape)
print('supra-adjacency nnz:', adjacency.nnz)
print('diffused mass:', round(float(diffused.sum()), 3))
supra-adjacency shape: (4, 4) supra-adjacency nnz: 8 diffused mass: 1.0
Multilayer AnnNet objects keep condition-specific topology, coupling edges, derived slices, and supra-level matrices in the same container.