Hyperedges and traversal¶
Hyperedges model complexes or reactions. Traversal helpers make local neighborhoods available without converting to another graph library.
In [1]:
Copied!
import annnet as an
import annnet as an
Undirected complexes and directed reactions¶
In [2]:
Copied!
H = an.AnnNet(directed=True)
H.add_vertices(['Glc', 'ATP', 'G6P', 'ADP', 'HK1', 'PFK'])
H.add_edges(['Glc', 'ATP', 'HK1'], edge_id='enzyme_complex', directed=False)
H.add_edges(
src=['Glc', 'ATP'],
tgt=['G6P', 'ADP'],
edge_id='hexokinase',
directed=True,
weight=2.0,
)
H.add_edges('G6P', 'PFK', edge_id='activates_pfk')
H.views.edges().select(['edge_id', 'kind', 'members', 'head', 'tail'])
H = an.AnnNet(directed=True)
H.add_vertices(['Glc', 'ATP', 'G6P', 'ADP', 'HK1', 'PFK'])
H.add_edges(['Glc', 'ATP', 'HK1'], edge_id='enzyme_complex', directed=False)
H.add_edges(
src=['Glc', 'ATP'],
tgt=['G6P', 'ADP'],
edge_id='hexokinase',
directed=True,
weight=2.0,
)
H.add_edges('G6P', 'PFK', edge_id='activates_pfk')
H.views.edges().select(['edge_id', 'kind', 'members', 'head', 'tail'])
Out[2]:
shape: (3, 5)
| edge_id | kind | members | head | tail |
|---|---|---|---|---|
| str | str | list[str] | list[str] | list[str] |
| "enzyme_complex" | "hyper" | ["ATP", "Glc", "HK1"] | null | null |
| "hexokinase" | "hyper" | null | ["ATP", "Glc"] | ["ADP", "G6P"] |
| "activates_pfk" | "binary" | null | null | null |
Draw the hypergraph¶
Graphviz represents each true hyperedge through a small square connector node. Binary edges remain ordinary arrows.
In [3]:
Copied!
from annnet.utils import plotting
plotting.plot(H, backend='graphviz', show_edge_labels=True)
from annnet.utils import plotting
plotting.plot(H, backend='graphviz', show_edge_labels=True)
Out[3]:
Endpoint coefficients¶
In [4]:
Copied!
H.set_edge_coeffs(
'hexokinase',
{'Glc': -1.0, 'ATP': -1.0, 'G6P': 1.0, 'ADP': 1.0},
)
col = H._edges['hexokinase'].col_idx
for vertex_id in sorted(H.vertices()):
row = H._entities[H._resolve_entity_key(vertex_id)].row_idx
value = H._matrix[row, col]
if value != 0:
print(f'{vertex_id:>4}: {value:+.1f}')
H.set_edge_coeffs(
'hexokinase',
{'Glc': -1.0, 'ATP': -1.0, 'G6P': 1.0, 'ADP': 1.0},
)
col = H._edges['hexokinase'].col_idx
for vertex_id in sorted(H.vertices()):
row = H._entities[H._resolve_entity_key(vertex_id)].row_idx
value = H._matrix[row, col]
if value != 0:
print(f'{vertex_id:>4}: {value:+.1f}')
ADP: +1.0 ATP: -1.0 G6P: +1.0 Glc: -1.0
Traverse local neighborhoods¶
In [5]:
Copied!
print('neighbors(Glc):', sorted(H.neighbors('Glc')))
print('successors(Glc):', sorted(H.successors('Glc')))
print('predecessors(G6P):', sorted(H.predecessors('G6P')))
print('neighbors(Glc):', sorted(H.neighbors('Glc')))
print('successors(Glc):', sorted(H.successors('Glc')))
print('predecessors(G6P):', sorted(H.predecessors('G6P')))
neighbors(Glc): ['ADP', 'ATP', 'G6P', 'HK1'] successors(Glc): ['ADP', 'ATP', 'G6P', 'HK1'] predecessors(G6P): ['ATP', 'Glc']
Incidence matrix¶
In [6]:
Copied!
import polars as pl
incidence = H.ops.vertex_incidence_matrix(values=True, sparse=True)
rows = []
for edge_id in H.edges():
col = H._edges[edge_id].col_idx
row = {'edge': edge_id}
for vertex_id in sorted(H.vertices()):
vertex_row = H._entities[H._resolve_entity_key(vertex_id)].row_idx
row[vertex_id] = float(H._matrix[vertex_row, col])
rows.append(row)
print('incidence shape:', incidence.shape)
print('non-zero entries:', incidence.nnz)
pl.DataFrame(rows)
import polars as pl
incidence = H.ops.vertex_incidence_matrix(values=True, sparse=True)
rows = []
for edge_id in H.edges():
col = H._edges[edge_id].col_idx
row = {'edge': edge_id}
for vertex_id in sorted(H.vertices()):
vertex_row = H._entities[H._resolve_entity_key(vertex_id)].row_idx
row[vertex_id] = float(H._matrix[vertex_row, col])
rows.append(row)
print('incidence shape:', incidence.shape)
print('non-zero entries:', incidence.nnz)
pl.DataFrame(rows)
incidence shape: (6, 3) non-zero entries: 9
Out[6]:
shape: (3, 7)
| edge | ADP | ATP | G6P | Glc | HK1 | PFK |
|---|---|---|---|---|---|---|
| str | f64 | f64 | f64 | f64 | f64 | f64 |
| "enzyme_complex" | 0.0 | 1.0 | 0.0 | 1.0 | 1.0 | 0.0 |
| "hexokinase" | 1.0 | -1.0 | 1.0 | -1.0 | 0.0 | 0.0 |
| "activates_pfk" | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | -1.0 |
The same object supports readable graph views, local traversal, and incidence-level inspection. Use the level that matches the question you are asking.