How to Use Temporal Knowledge Graphs with `valid_from`/`valid_until` and Allen Interval Algebra in Semantica
Use the TemporalModel class in semantica/kg/temporal_model.py to store temporal facts with valid_from and valid_until fields, then query them with the TemporalReasoningEngine in semantica/kg/temporal_reasoning.py which implements full Allen interval algebra for point-in-time, range, and path-based temporal reasoning.
Temporal knowledge graphs extend traditional KGs by tracking when facts are true rather than treating them as eternal. In the Semantica open-source framework, this is achieved through explicit temporal bounds and a complete implementation of Allen's thirteen interval relations. This guide covers how to model, store, and query time-varying knowledge using the framework's core temporal components.
Modeling Temporal Facts with valid_from and valid_until
Semantica represents temporal information through two mandatory fields on entities and relationships:
valid_from: The instant a fact becomes truevalid_until: The instant it ceases to be true (orTemporalBound.OPENfor ongoing facts)
The Temporal Model (semantica/kg/temporal_model.py) normalizes these to UTC datetime objects. Key classes include:
| Class | Purpose |
|---|---|
BiTemporalFact |
Stores both assertion time and valid time (bitemporal modeling) |
TemporalBound.OPEN |
Sentinel value representing unbounded future validity |
TemporalInterval |
Internal representation of [start, end) periods |
When constructing facts, always provide ISO 8601 strings or datetime objects. The normalizer handles timezone conversion internally.
Understanding Allen Interval Algebra in TemporalReasoningEngine
The Allen interval algebra implementation resides in TemporalReasoningEngine (semantica/kg/temporal_reasoning.py). Each temporal fact becomes a TemporalInterval with start, end, and optional label. The engine exposes these core methods:
| Method | Returns | Use Case |
|---|---|---|
relation(a, b) |
IntervalRelation enum |
Determine which of 13 Allen relations holds |
overlaps(a, b) |
bool |
Check for any temporal intersection |
contains(outer, inner) |
bool |
Verify interval containment |
active_at(interval, ts, granularity=None) |
bool |
Point-in-time membership test |
merge_intervals(intervals) |
List[TemporalInterval] |
Build non-overlapping union |
gap_analysis(intervals, domain_start, domain_end) |
List[TemporalInterval] |
Find uncovered periods |
coverage_percentage(intervals, domain) |
float |
Calculate temporal completeness |
The IntervalRelation enum implements all thirteen Allen relations: BEFORE, MEETS, OVERLAPS, STARTS, DURING, FINISHES, EQUALS, and their inverses (AFTER, MET_BY, OVERLAPPED_BY, STARTED_BY, CONTAINS, FINISHED_BY).
Querying Temporal Knowledge Graphs
Higher-level operations are provided by TemporalGraphQuery in semantica/kg/temporal_query.py (lines 44-66). All queries delegate to the reasoning engine.
Point-in-Time Queries
Use query_at_time to reconstruct the graph as it existed at a specific instant:
from semantica.kg.temporal_query import TemporalGraphQuery
engine = TemporalGraphQuery()
result = engine.query_at_time(
graph=my_kg, # dict with "entities" and "relationships"
query="*",
at_time="2023-07-15T00:00:00Z"
)
print("Valid relationships at that date:", len(result["relationships"]))
Internally, this calls _relationship_active_at_time → active_at() to filter relationships whose valid interval contains the timestamp.
Time-Range Queries
For interval-based retrieval, use query_time_range with normalize_interval for granularity alignment:
# Relationships overlapping a range
overlapping = engine._relationship_overlaps_range(graph, start, end)
# Relationships fully covering a range
covering = engine._relationship_covers_range(graph, start, end)
Determining Allen Relations Between Facts
Compare two temporal intervals explicitly:
from semantica.kg.temporal_reasoning import TemporalReasoningEngine, TemporalInterval
engine = TemporalReasoningEngine()
a = TemporalInterval(
start=engine._parse_time("2022-01-01"),
end=engine._parse_time("2022-06-30")
)
b = TemporalInterval(
start=engine._parse_time("2022-04-01"),
end=engine._parse_time("2022-12-31")
)
relation = engine.relation(a, b)
print("Allen relation:", relation.value) # → "overlaps"
Advanced Temporal Path Finding and Pattern Detection
Temporal Path Queries
The find_temporal_paths method respects validity intervals during graph traversal:
paths = engine.find_temporal_paths(
graph=my_kg,
source="A",
target="B",
start_time="2021-01-01",
end_time="2023-01-01",
ordering_strategy="strict" # "strict", "overlap", or "loose"
)
print("Found", paths["num_paths"], "temporal paths")
Ordering strategies:
strict: Edges must follow chronological order (end time ≤ next start time)overlap: Adjacent edges must share at least one common instantloose: Only requires edges to exist within the query window
Gap Analysis and Coverage
Analyze temporal completeness of your data:
intervals = [
TemporalInterval(start=engine._parse_time("2020-01-01"), end=engine._parse_time("2020-03-01")),
TemporalInterval(start=engine._parse_time("2020-04-15"), end=engine._parse_time("2020-06-01")),
]
gaps = engine.gap_analysis(intervals, domain_start="2020-01-01", domain_end="2020-12-31")
for gap in gaps:
print("Gap from", gap.start, "to", gap.end)
The TemporalPatternDetector (semantica/kg/temporal_pattern_detector.py) extends these primitives to discover sequential and cyclic patterns automatically.
Key Source Files Reference
| File | Role |
|---|---|
semantica/kg/temporal_model.py |
Normalizes valid_from/valid_until, defines BiTemporalFact, TemporalBound |
semantica/kg/temporal_reasoning.py |
TemporalReasoningEngine with full Allen algebra implementation |
semantica/kg/temporal_query.py |
High-level TemporalGraphQuery API for point-in-time, range, and path queries |
semantica/kg/temporal_pattern_detector.py |
Pattern detection using interval relations |
semantica/visualization/temporal_visualizer.py |
Timeline and dashboard visualization |
Summary
- Store temporal facts using
valid_fromandvalid_untilfields normalized byTemporalModel - Reason about intervals with
TemporalReasoningEngineimplementing all thirteen Allen relations - Query at instants with
query_at_timeusingactive_at()filtering - Query over ranges with
query_time_rangeand overlap/coverage predicates - Find valid paths with
find_temporal_pathsand configurable ordering strategies - Analyze coverage using
gap_analysisandmerge_intervals
According to the Semantica source code, these components operate on UTC-normalized datetime objects with optional granularity parameters for flexible temporal matching.
Frequently Asked Questions
What is the difference between valid_from/valid_until and bitemporal modeling?
valid_from and valid_until represent valid time (when a fact is true in reality). Bitemporal modeling adds assertion time (when the database recorded the fact). Use BiTemporalFact in semantica/kg/temporal_model.py when you need to track both—for example, to correct historical records while preserving the original ingestion timestamp.
How does Semantica handle open-ended intervals?
The TemporalBound.OPEN sentinel in temporal_model.py represents unbounded future validity. In the reasoning engine, this compares as greater than any concrete timestamp, enabling queries against facts that remain current.
Which Allen relation should I use for "happened during the same time"?
Use relation() and check for IntervalRelation.OVERLAPS, OVERLAPPED_BY, STARTS, STARTED_BY, FINISHES, FINISHED_BY, DURING, CONTAINS, or EQUALS. For a simple boolean check, call overlaps(a, b) which returns true for any non-disjoint relation (excluding only BEFORE, AFTER, MEETS, MET_BY).
Can I query with day-level granularity instead of exact timestamps?
Yes. Pass the granularity parameter to active_at() or use normalize_interval() in temporal_reasoning.py before comparisons. This rounds timestamps to the specified unit (day, hour, etc.) for fuzzy temporal matching.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →