Skip to content

Graph

Primary graph objects from annnet.core.graph.

The main graph API centers on AnnNet/Graph, bulk node and edge construction with add_nodes and add_edges, graph-owned accessors (slices, layers, attrs, views, ops, idx, cache), annotation tables (obs, var, uns), and backend accessors (nx, ig, gt).

AnnNet

annnet.core.graph.AnnNet

Incidence-based graph with slices, multilayer coordinates, and rich edge types.

AnnNet stores topology in a sparse incidence matrix backed by canonical entity and edge registries. A row represents an entity, typically a node or an edge-entity, and a column represents an edge. The class supports:

  • binary directed and undirected edges
  • hyperedges, including directed head/tail hyperedges
  • edge-entities that can themselves participate as endpoints
  • slice membership and per-slice edge weights
  • optional multilayer coordinates on nodes and edges
  • dataframe-backed attribute storage

Parameters:

Name Type Description Default
directed bool | None

Default directedness for newly created binary edges. If None, methods fall back to directed semantics unless a per-edge flag is set.

None
annotations dict | None

Pre-built annotation tables to use instead of creating empty tables.

None
annotations_backend ('auto', 'polars', 'pandas', 'pyarrow')

Preferred backend for newly initialized annotation tables. "auto" prefers the first installed supported backend.

"auto"
aspects dict[str, list[str]] | None

Initial multilayer aspect declaration. If omitted, the graph starts flat with a single placeholder aspect "_".

None
**kwargs Any

Initial graph-level attributes stored in :attr:graph_attributes.

{}
Notes

Directed incidence columns use positive values for sources or heads and negative values for targets or tails. Undirected binary edges and undirected hyperedges use positive values for all incident entities.

See Also

add_node add_edge add_nodes add_edges view

Attributes

nv property
nv

Number of unique nodes (deduplicated across layers).

Returns:

Type Description
int

Distinct node IDs, ignoring layer multiplicity. Use :attr:nv_supra for the supra-incidence row count.

ne property
ne

Number of structural edges.

Returns:

Type Description
int

Count of incidence-matrix edge columns.

shape property
shape

Graph shape as (nv, ne).

Returns:

Type Description
tuple[int, int]

Node count and edge count. Use :attr:supra_shape for (nv_supra, ne).

E property
E

The edge sequence.

Returns:

Type Description
EdgeSequence

The edges in graph order, with the same keys as :attr:N. The direction, the weight, and the kind of an edge read like a column, so a filter over them needs no attribute.

Examples:

>>> list(G.E)
>>> G.E['weight']
>>> G.E.select(directed=True)
obs property
obs

The node attribute table, materialized on each read.

Returns:

Type Description
DataFrame - like

One row per node, with the id column first.

Notes

This is a table built for the caller and not the storage of the graph, so writing to it changes nothing. Write through :attr:N for a whole column, or through :attr:attrs for one value.

A whole table is the expensive way to read one column. G.N["kind"] is the cheap one.

Examples:

>>> G = AnnNet()
>>> G.add_nodes([{'node_id': 'A', 'kind': 'source'}])
>>> G.obs
var property
var

The edge attribute table, materialized on each read.

Returns:

Type Description
DataFrame - like

One row per edge, with the id column first.

Notes

This is a table built for the caller and not the storage of the graph, so writing to it changes nothing. Write through :attr:E for a whole column, or through :attr:attrs for one value.

Examples:

>>> G = AnnNet()
>>> G.add_nodes(['A', 'B'])
>>> G.add_edges([{'source': 'A', 'target': 'B', 'edge_id': 'e1'}])
>>> G.var
uns property
uns

Graph-level unstructured metadata.

Returns:

Type Description
dict

Mutable dictionary of graph-level attributes.

attrs property
attrs

Attribute operations namespace.

Returns:

Type Description
AttributesAccessor

Manager for graph-, node-, edge-, slice-, and edge-slice annotations.

Notes

Use this namespace for graph-, node-, edge-, and slice-level annotations.

Examples:

>>> G.attrs.set_node_attrs('A', symbol='TP53')
>>> G.attrs.get_node_attrs('A')
>>> G.attrs.set_edge_slice_attrs('baseline', 'e1', weight=0.5)
views property
views

Materialized table namespace.

Returns:

Type Description
ViewsAccessor

Manager for dataframe-style materialized views.

Notes

This is the preferred namespace for notebook inspection and export of graph tables.

Examples:

>>> G.views.nodes()
>>> G.views.edges()
>>> G.views.slices()
>>> G.views.layers()
history property
history

Mutation history and snapshot namespace.

Returns:

Type Description
HistoryAccessor

Callable namespace: G.history() reads the log, and its methods enable it, clear it, export it, mark it, and snapshot the graph.

Examples:

>>> G.history()
>>> G.history.snapshot('before')
ops property
ops

Structural operations namespace.

Returns:

Type Description
OperationsAccessor

Manager for subgraphs, copies, reversals, incidence extraction, and memory inspection.

Examples:

>>> H = G.ops.subgraph(['A', 'B', 'C'])
>>> M = G.ops.node_incidence_matrix(sparse=True)
>>> usage = G.ops.memory_usage()
layers property
layers

Layer operations namespace.

Returns:

Type Description
LayerAccessor

Manager for multilayer aspects, layer coordinates, supra matrices, and layer set operations.

Notes

All multilayer configuration and layer-aware analysis lives here.

Examples:

>>> G.layers.set_aspects(['condition'], {'condition': ['ctrl', 'stim']})
>>> G.layers.list_layers()
>>> G.views.layers()
slices property
slices

Slice operations namespace.

Returns:

Type Description
SliceManager

Manager exposing slice creation, membership, set operations, and slice-level analysis.

Examples:

>>> G.slices.add('baseline')
>>> G.slices.active = 'baseline'
>>> G.slices.list()
idx property
idx

Index lookup namespace.

Returns:

Type Description
IndexManager

Manager for entity-to-row and edge-to-column index lookups.

cache property
cache

Sparse matrix cache namespace.

Returns:

Type Description
CacheManager

Manager for derived sparse matrix formats such as CSR and CSC.

nx property
nx

NetworkX interoperability namespace.

Returns:

Type Description
_NXBackendAccessor

Lazy proxy that converts to NetworkX only when an algorithm or backend graph is requested.

Examples:

>>> G.nx.backend()
>>> G.nx.shortest_path(G, 'A', 'B')
ig property
ig

Igraph interoperability namespace.

Returns:

Type Description
_IGBackendAccessor

Lazy proxy that converts to igraph only when requested.

gt property
gt

graph-tool interoperability namespace.

Returns:

Type Description
_GTBackendAccessor

Lazy proxy that converts to graph-tool only when requested.

is_multilayer property
is_multilayer

Whether the graph has declared multilayer aspects.

Returns:

Type Description
bool

True when the graph has user-declared aspects. Flat graphs use the internal sentinel aspect "_" and return False.

Functions

add_nodes
add_nodes(nodes, slice=None, layer=None, **attributes)

Add one node or many nodes.

This is the canonical public entry point for node creation. Use it for both single-node and batch insertion.

Parameters:

Name Type Description Default
nodes str | dict | tuple | Iterable

Node specification or iterable of specifications.

Accepted single-node forms are:

  • "A"
  • {"node_id": "A", "kind": "source"}
  • {"id": "A", ...}
  • {"name": "A", ...}
  • ("A", {"kind": "source"})

Accepted batch forms are iterables of the same specifications.

required
slice str

Slice receiving the inserted nodes. If omitted, the active slice is used.

None
layer str | tuple | dict

Layer coordinate for inserted nodes in multilayer graphs. A string is valid only for single-aspect graphs; a tuple must already be in aspect order; a dict maps aspect name to layer value.

None
**attributes Any

Attributes applied to a single node. These are merged with attributes in nodes when nodes is a single node.

{}

Returns:

Type Description
str | list[str]

The inserted node ID for a single node, or a list of node IDs for batch insertion.

Raises:

Type Description
ValueError

If a dictionary node specification does not contain "node_id", "id", or "name".

Notes

Node attributes are stored in :attr:obs and can be edited through :attr:attrs. In multilayer graphs, omitting layer places the node on the placeholder layer coordinate.

Examples:

>>> G = AnnNet()
>>> G.add_nodes('A', kind='source')
'A'
>>> G.add_nodes(
...     [
...         {'node_id': 'B', 'kind': 'relay'},
...         ('C', {'kind': 'sink'}),
...     ]
... )
['B', 'C']
add_edges
add_edges(*args, **kwargs)

Add one edge, many edges, or hyperedges.

This is the canonical public entry point for all edge creation. It handles binary edges, directed and undirected edges, edge-entities, and hyperedges through the shape of the input specification.

Parameters:

Name Type Description Default
*args Any

Edge specification. Common forms are:

  • G.add_edges("A", "B")
  • G.add_edges("A", "B", weight=2.0, edge_id="e1")
  • G.add_edges({"source": "A", "target": "B"})
  • G.add_edges([{"source": "A", "target": "B"}, ...])
  • G.add_edges([{"members": ["A", "B", "C"]}, ...])
  • G.add_edges([{"tail": ["A"], "head": ["B", "C"]}, ...])
  • G.add_edges([{"edge_id": "EE1", ...}, ...], as_entity=True)
()
**kwargs Any

Options for single-edge or batch insertion.

{}

Other Parameters:

Name Type Description
source str

Source endpoint for a binary edge.

src str

Source endpoint for a binary edge.

target str

Target endpoint for a binary edge.

tgt str

Target endpoint for a binary edge.

weight float

Incidence weight for the edge.

edge_id str

Explicit edge identifier. If omitted, an edge_N ID is assigned.

directed bool

Directedness for a single edge.

edge_directed bool

Directedness for edge specs in batch input.

slice str

Slice receiving the inserted edges. If omitted, the active slice is used.

as_entity bool

If True, each created edge is also registered as an entity so it can be used as the endpoint of later edges. In batch mode, items that carry no source/target are treated as null-endpoint edge-entity placeholders and require this flag to be set.

parallel ('update', 'error', 'parallel')

Policy for single-edge insertion when edge_id is not supplied and the same endpoints already have an edge. "update" reuses the existing edge; "parallel" creates an additional edge; "error" raises ValueError. Ignored in batch mode.

propagate ('none', 'shared', 'all')

Slice propagation policy. "shared" adds the edge to every slice containing both endpoints; "all" adds it to every slice containing either endpoint.

flexible dict

Data-driven direction policy. Requires keys "var" and "threshold". Single-edge path only.

default_weight float

Batch default for edge specs without an explicit weight.

default_edge_directed bool

Batch default directedness.

default_propagate ('none', 'shared', 'all')

Batch default propagation policy.

default_edge_type str

Batch default edge type stored in the edge record.

default_slice_weight float

Batch per-slice weight override.

Returns:

Type Description
str | list[str]

Edge ID for single-edge insertion, or a list of edge IDs for batch insertion.

Raises:

Type Description
TypeError

If unsupported keyword arguments are supplied for batch insertion.

ValueError

If the edge specification is structurally invalid.

Notes

Hyperedges are detected from dictionaries containing "members" for undirected hyperedges or "head"/"tail" for directed hyperedges. Binary edges use "source"/"target" or "src"/"tgt".

For a full guide covering all input forms, dispatch logic, parallel policy, propagation, flexible direction, and batch formats, see the Adding edges explanation page.

Examples:

>>> G = AnnNet(directed=True)
>>> G.add_nodes(['A', 'B', 'C'])
['A', 'B', 'C']
>>> G.add_edges('A', 'B', edge_id='e1', weight=0.5)
'e1'
>>> G.add_edges(
...     [
...         {'source': 'B', 'target': 'C'},
...         {'members': ['A', 'B', 'C'], 'edge_id': 'h1'},
...     ]
... )
['edge_0', 'h1']
remove_nodes
remove_nodes(node_ids, *, errors='raise')

Remove one node or many nodes.

Parameters:

Name Type Description Default
node_ids str | tuple | Iterable[str | tuple]

Node ID, explicit multilayer node key, or iterable of IDs/keys.

required
errors ('raise', 'ignore')

"raise" (NetworkX convention) raises KeyError listing the unknown IDs. "ignore" silently skips them.

"raise"

Returns:

Type Description
None
Notes

Incident edges are removed with each node.

Examples:

>>> G.remove_nodes('A')
>>> G.remove_nodes(['B', 'C'])
>>> G.remove_nodes('nope', errors='ignore')
remove_edges
remove_edges(edge_ids, *, errors='raise')

Remove one edge or many edges.

Parameters:

Name Type Description Default
edge_ids str | Iterable[str]

Edge ID or iterable of edge IDs to remove.

required
errors ('raise', 'ignore')

"raise" (NetworkX convention) raises KeyError listing the unknown IDs. "ignore" silently skips them.

"raise"

Returns:

Type Description
None

Examples:

>>> G.remove_edges('e1')
>>> G.remove_edges(['e2', 'e3'])
>>> G.remove_edges('nope', errors='ignore')
has_node
has_node(node_id)

Check whether a node exists.

Parameters:

Name Type Description Default
node_id str | tuple

Bare node ID, or explicit (node_id, layer_coord) tuple for multilayer graphs.

required

Returns:

Type Description
bool

True if the graph contains a node entity matching node_id.

Notes

In multilayer graphs, a bare node ID returns True if that node is present on at least one layer coordinate.

has_edge
has_edge(source=None, target=None, edge_id=None)

Check whether an edge exists.

Parameters:

Name Type Description Default
source str

Source endpoint.

None
target str

Target endpoint.

None
edge_id str

Edge identifier.

None

Returns:

Type Description
bool | tuple[bool, list[str]]

If only edge_id is provided, returns a boolean. If source and target are provided, returns (exists, edge_ids). If all three arguments are provided, returns whether that exact edge ID connects the given endpoints.

Raises:

Type Description
ValueError

If the argument combination is invalid.

Examples:

>>> G.has_edge(edge_id='e1')
True
>>> G.has_edge('A', 'B')
(True, ['e1'])
nodes
nodes()

Return unique node IDs (one per node, deduplicated across layers).

Returns:

Type Description
list[str]

Distinct node identifiers, excluding edge-entities. In a multilayer graph each node appears exactly once regardless of how many elementary layers it inhabits.

See Also

supra_nodes : (node_id, layer_coord) pairs (one per row of the supra-incidence matrix).

edges
edges()

Return all structural edge IDs.

Returns:

Type Description
list[str]

Edge identifiers for edges with an incidence-matrix column.

degree
degree(entity_id)

Return the incidence degree of a node or edge-entity.

Parameters:

Name Type Description Default
entity_id str | tuple

Node ID, edge-entity ID, or explicit multilayer entity key.

required

Returns:

Type Description
int

Number of non-zero incidence entries in the entity row. Missing entities have degree 0.

incident_edges
incident_edges(nodes, direction='both')

Return edges incident to one or more nodes.

Parameters:

Name Type Description Default
nodes str | Iterable[str]

One node identifier or an iterable of identifiers.

required
direction ('in', 'out', 'both')

Directional filter applied to binary edges. Undirected edges are included for both "in" and "out".

"in"

Returns:

Type Description
list[tuple[int, EdgeView]]

Pairs of (column_index, edge_view) as returned by :meth:get_edge, materialized for consistency with the sibling nodes / edges / edge_list APIs which all return lists.

Raises:

Type Description
ValueError

If direction is not "in", "out", or "both".

Examples:

>>> G.incident_edges('A', direction='out')
[(0, EdgeView(edge_id='e0', kind='binary', ...))]
read classmethod
read(path, **kwargs)

Read a graph from the native .annnet format.

Parameters:

Name Type Description Default
path str | Path

Input file path.

required
**kwargs

Passed to annnet.io.annnet_format.read.

{}

Returns:

Type Description
AnnNet

Deserialized graph.

Examples:

>>> G = AnnNet.read('graph.annnet')
write
write(path, *, matrix=False, **kwargs)

Write the graph to the native .annnet format.

Parameters:

Name Type Description Default
path str | Path

Output file path.

required
matrix bool

Also persist the incidence matrix. The records are the source of truth and fully reconstruct it — including explicit coefficients, which are written as records data either way — so this is a size/load-time trade, never a correctness one. Left off, read defers the rebuild until the matrix is first touched, which is usually cheaper than loading it. Turn it on for graphs large enough that the rebuild dominates.

False
**kwargs

Passed to annnet.io.annnet_format.write.

{}

Returns:

Type Description
None

Examples:

>>> G.write('graph.annnet')  # matrix rebuilt on demand
>>> G.write('graph.annnet', matrix=True)  # cache it alongside
view
view(nodes=None, edges=None, slices=None, predicate=None)

Create a lazy graph view.

Parameters:

Name Type Description Default
nodes Iterable[str]

Node IDs to include.

None
edges Iterable[str]

Edge IDs to include.

None
slices Iterable[str]

Slice IDs to include.

None
predicate callable

Predicate used for additional filtering.

None

Returns:

Type Description
GraphView

View object backed by this graph.

Notes

Views are lightweight filters over an existing graph. Use :attr:views for materialized dataframe views.

global_count
global_count(kind)

Count unique members present across slices.

Parameters:

Name Type Description Default
kind ('nodes', 'edges', 'entities')

Membership domain. "nodes" counts slice node members, "edges" counts slice edge members, and "entities" counts the union of both domains.

"nodes"

Returns:

Type Description
int

Number of unique members observed in slice membership.

Raises:

Type Description
ValueError

If kind is not one of "nodes", "edges", or "entities".

Notes

This is a slice-membership count, not a storage count. For graph storage counts, use :meth:ncount and :meth:ecount.

get_node
get_node(node_id)

Return a :class:NodeView for one node.

Parameters:

Name Type Description Default
node_id str

Node identifier. A lookup takes an id and nothing else. A caller holding a row of the incidence matrix asks G.idx.row_to_entity(row) for the identity on it, and a caller who wants the n-th node of a sequence writes G.N[n].

required

Returns:

Type Description
NodeView

A string-shaped record equal to the id. kind, layers and attrs are exposed as attributes.

Raises:

Type Description
TypeError

If the argument is not an id.

KeyError

If the id is unknown.

get_edge
get_edge(edge_id)

Return an :class:EdgeView for one edge.

Parameters:

Name Type Description Default
edge_id str

Edge identifier. A lookup takes an id and nothing else. A caller holding a column of the incidence matrix asks G.idx.col_to_edge(column) for the id on it.

required

Returns:

Type Description
EdgeView

A tuple-shaped record. (source, target) tuple unpacking still works; edge_id, kind, members, weight and directed are also exposed as attributes.

Raises:

Type Description
TypeError

If the argument is not an id.

KeyError

If the id is unknown.

edge_list
edge_list()

Materialize binary edges as endpoint tuples.

Returns:

Type Description
list[tuple[str, str, str, float]]

Tuples of (source, target, edge_id, weight) for binary and node-edge records. Hyperedges and endpoint-less placeholders are omitted. The weight reflects the active slice's per-edge override when one is set; otherwise the edge's stored weight.

make_undirected
make_undirected(*, drop_flexible=True, update_default=True)

Convert all existing edges to undirected form in place.

Parameters:

Name Type Description Default
drop_flexible bool

If True, clear flexible-direction policies after rewriting edge incidence signs.

True
update_default bool

If True, set G.directed = False so future edges are undirected unless explicitly overridden.

True

Returns:

Type Description
AnnNet

The modified graph, returned for chaining.

Notes

Directed binary edges are rewritten from signed incidence (+w, -w) to unsigned incidence (+w, +w). Directed hyperedges are converted to undirected hyperedges over the union of their head and tail members.

Examples:

>>> G = AnnNet(directed=True)
>>> G.add_nodes(['A', 'B'])
['A', 'B']
>>> G.add_edges('A', 'B')
'edge_0'
>>> G.make_undirected()
AnnNet(...)

EdgeType

annnet.core._records.EdgeType