Temporal Inference Engine
Added in version 1.5.0.
The s3dgraphy.temporal package implements Layer B of DP-32 —
the TPQ / TAQ closure that converts topological stratigraphic
relations (cuts, overlies, fills) into temporal direction
(is_after / is_before) and computes the earliest / latest
plausible date of every stratigraphic unit by BFS over the resulting
DAG.
Two cooperating components
s3dgraphy.temporal.inference_engine.TemporalInferenceEngineTopology-to-temporal mapping plus transitive reduction (NetworkX). Given a graph with
cuts/overlies/fillsedges, the engine returns the minimal set ofis_afteredges that captures the same temporal order, dropping the redundant ones.Graph.calculate_chronology()The high-level entry point used by chronology consumers. Internally:
Seeds node-level TPQ/TAQ from PropertyNodes named
absolute_time_start/absolute_time_endvia the resolver registered in Property Propagation.Seeds swimlane-level TPQ/TAQ from EpochNode
start_time/end_time(header) or from a PropertyNode attached to the epoch (SL_PD override — the resolver prefers this over the header).Runs BFS over the inferred temporal DAG, propagating
CALCUL_START_T(max of upstream TPQs) andCALCUL_END_T(min of downstream TAQs) until fixpoint.Surfaces
[chronology paradox]warnings with attribution (see Diagnostics API) when propagation forces a node’s window to be empty or inverted.
Topological-to-temporal mapping
The current canonical table lives in
TemporalInferenceEngine.TOPOLOGICAL_TO_TEMPORAL:
Edge type |
Temporal direction |
|---|---|
|
source is more recent |
|
target is more recent |
|
source is more recent |
|
target is more recent |
|
source is more recent |
|
target is more recent |
|
ambiguous (no temporal direction) |
|
symmetric (no temporal direction) |
|
symmetric (no temporal direction) |
|
Edges that are already is_after (or is_before) are absorbed
into the DAG without re-derivation. Symmetric and ambiguous relations
are intentionally dropped — they encode physical contact, not
sequence.
attribute_temporal_claim
The temporal layer relies on attribute_temporal_claim() (in
s3dgraphy.diagnostics) to answer the question “who asserted
this temporal seed?” whenever a paradox is detected. Resolution order:
Direct
has_authoron the temporal PropertyNode.A sibling
ExtractorNodein the containingParadataNodeGroupand itshas_author.The PropertyNode’s host swimlane (epoch).
The returned tuple (display, kind, author_uuid) is appended to the
[chronology paradox] warning as
[attributed to <name> (<kind>)] so the user can tell whether to
audit the original document author (kind = "author") or the AI
extractor that produced the bad inference
(kind = "extractor").
Stratigraphic cycle detection
Graph.calculate_chronology() runs
s3dgraphy.diagnostics.detect_stratigraphic_cycles() before the
TPQ/TAQ propagation pass. The detector runs Tarjan SCC over
is_after / cuts / overlies / fills / is_before
edges and appends one [stratigraphic cycle] warning per loop, with
per-node attribution. The propagation BFS already survived cycles via
a visited set, but the user must be notified — AI extractors
occasionally close these loops.
Worked example — temporal closure
US005 --overlies--> US003 # construction order
US005 --has_property--> PN("absolute_time_start" = -80)
US001 --has_property--> PN("absolute_time_end" = -120)
US005 --cuts--> US001 # foundation trench cuts US001
US001 --has_first_epoch--> EP_HELLENISTIC (end_time = -100)
For graph.calculate_chronology():
Engine maps
overlies→ US005 more recent than US003;cuts→ US005 more recent than US001.Seeds: US005 TPQ = −80; US001 TAQ = −120 (node-level); US001 TAQ also constrained by swimlane to −100.
BFS: US005 inherits
CALCUL_END_Tno later than −80 (own TPQ becomes the floor); US003 inherits no later than US005’s end; US001 inherits no earlier than US005’s start (TPQ propagates backward viais_after).Paradox check: US001 has TPQ ≤ −80 (from cuts ▸ US005) but TAQ = −120 — the unit’s window is empty. A
[chronology paradox]warning is emitted with attribution.
Why a Layer B at all?
Topological edges are what archaeologists see in the trench;
temporal direction is what we infer about time. Keeping them as
separate edge types in s3dgraphy (cuts vs is_after) lets the
graph faithfully record both layers — the engine derives one from
the other on demand, with transitive reduction so the DAG stays
readable.
API Reference
Temporal Inference Engine for s3dgraphy
Converts topological stratigraphic relations (cuts, overlies, fills) into temporal relations (is_after, is_before) and performs transitive reduction to obtain the minimal set of temporal edges.
- class s3dgraphy.temporal.inference_engine.TemporalInferenceEngine(connections_datamodel=None)[source]
Bases:
objectInfers temporal relationships from topological stratigraphic relations.
The engine: 1. Maps topological relations to temporal directions 2. Builds a directed acyclic graph (DAG) of temporal precedence 3. Performs transitive reduction to eliminate redundant edges 4. Detects cycles (data inconsistencies)
- __init__(connections_datamodel=None)[source]
Initialize the temporal inference engine.
- Parameters:
connections_datamodel – Optional connections datamodel dict. If None, loads from default location.
- extract_temporal_from_graph(graph) List[Tuple[str, str]][source]
Extract temporal relations from a graph’s topological edges.
- transitive_reduction(temporal_edges: List[Tuple[str, str]]) List[Tuple[str, str]][source]
Perform transitive reduction on temporal edges.
Removes redundant edges from the temporal DAG. For example: - If A→B and B→C and A→C, then A→C is redundant (transitive) - Output: A→B, B→C
- Parameters:
temporal_edges – List of (source, target) tuples where source is more recent than target
- Returns:
Minimal set of temporal edges
- Return type:
- Raises:
ValueError – If the graph contains cycles (data inconsistency)
- get_temporal_order(temporal_edges: List[Tuple[str, str]]) List[str][source]
Get topological sort of nodes by temporal order.
- Parameters:
temporal_edges – List of (source, target) temporal edges
- Returns:
Node IDs sorted from most recent to most ancient
- Return type:
List[str]
- validate_consistency(temporal_edges: List[Tuple[str, str]]) Tuple[bool, List[List[str]]][source]
Validate temporal consistency (check for cycles).