History and reproducibility¶
The history accessor records mutating calls, named snapshots, diffs, and exportable provenance.
In [1]:
Copied!
import annnet as an
import annnet as an
Record construction history¶
In [2]:
Copied!
G = an.AnnNet(directed=True)
G.history.clear()
G.add_vertices(['A', 'B'])
G.add_edges('A', 'B', edge_id='e1', weight=1.0)
G.history.mark('baseline graph')
G.history.snapshot('baseline')
import polars as pl
print('events:', len(G.history()))
print('last op:', G.history()[-1]['op'])
pl.DataFrame(
[
{
'version': event['version'],
'operation': event['op'],
'result': str(event.get('result', '')),
}
for event in G.history()
]
)
G = an.AnnNet(directed=True)
G.history.clear()
G.add_vertices(['A', 'B'])
G.add_edges('A', 'B', edge_id='e1', weight=1.0)
G.history.mark('baseline graph')
G.history.snapshot('baseline')
import polars as pl
print('events:', len(G.history()))
print('last op:', G.history()[-1]['op'])
pl.DataFrame(
[
{
'version': event['version'],
'operation': event['op'],
'result': str(event.get('result', '')),
}
for event in G.history()
]
)
events: 4 last op: snapshot
Out[2]:
shape: (4, 3)
| version | operation | result |
|---|---|---|
| i64 | str | str |
| 1 | "add_vertices" | "['A', 'B']" |
| 2 | "add_edges" | "e1" |
| 3 | "mark" | "" |
| 3 | "snapshot" | "" |
Compare snapshots¶
In [3]:
Copied!
G.add_vertices('C')
G.add_edges('B', 'C', edge_id='e2', weight=0.5)
G.slices.add('candidate')
G.slices.add_edge_to_slice('candidate', 'e2')
G.history.snapshot('candidate')
diff = G.history.diff('baseline', 'candidate')
import json
print(diff.summary())
print(json.dumps(diff.to_dict(), indent=2))
G.add_vertices('C')
G.add_edges('B', 'C', edge_id='e2', weight=0.5)
G.slices.add('candidate')
G.slices.add_edge_to_slice('candidate', 'e2')
G.history.snapshot('candidate')
diff = G.history.diff('baseline', 'candidate')
import json
print(diff.summary())
print(json.dumps(diff.to_dict(), indent=2))
Diff: baseline - candidate
Vertices: +1 added, 0 removed
Edges: +1 added, 0 removed
slices: +1 added, 0 removed
{
"snapshot_a": "baseline",
"snapshot_b": "candidate",
"vertices_added": [
"C"
],
"vertices_removed": [],
"edges_added": [
"e2"
],
"edges_removed": [],
"slices_added": [
"candidate"
],
"slices_removed": []
}
Export history¶
In [4]:
Copied!
import os
import tempfile
import json
tmp = tempfile.mkdtemp(prefix='annnet_history_')
json_path = os.path.join(tmp, 'history.json')
n_events = G.history.export(json_path)
with open(json_path, encoding='utf-8') as handle:
exported = json.load(handle)
print('events written:', n_events)
print('file exists:', os.path.exists(json_path))
print(json.dumps(exported[:4], indent=2))
import os
import tempfile
import json
tmp = tempfile.mkdtemp(prefix='annnet_history_')
json_path = os.path.join(tmp, 'history.json')
n_events = G.history.export(json_path)
with open(json_path, encoding='utf-8') as handle:
exported = json.load(handle)
print('events written:', n_events)
print('file exists:', os.path.exists(json_path))
print(json.dumps(exported[:4], indent=2))
events written: 7
file exists: True
[
{
"version": 1,
"ts_utc": "2026-07-30T18:17:40.649380Z",
"mono_ns": 1469216,
"op": "add_vertices",
"vertices": [
"A",
"B"
],
"slice": null,
"layer": null,
"attributes": {},
"result": [
"A",
"B"
]
},
{
"version": 2,
"ts_utc": "2026-07-30T18:17:40.649659Z",
"mono_ns": 1741116,
"op": "add_edges",
"args": [
"A",
"B"
],
"kwargs": {
"edge_id": "e1",
"weight": 1.0
},
"result": "e1"
},
{
"version": 3,
"ts_utc": "2026-07-30T18:17:40.649723Z",
"mono_ns": 1803053,
"op": "mark",
"label": "baseline graph"
},
{
"version": 3,
"ts_utc": "2026-07-30T18:17:40.649781Z",
"mono_ns": 1860861,
"op": "snapshot",
"label": "baseline"
}
]
History is not a workflow engine; it is a compact provenance layer. It helps notebooks explain how a graph changed and makes named graph states easy to compare.