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 true
  • valid_until: The instant it ceases to be true (or TemporalBound.OPEN for 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 instant
  • loose: 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_from and valid_until fields normalized by TemporalModel
  • Reason about intervals with TemporalReasoningEngine implementing all thirteen Allen relations
  • Query at instants with query_at_time using active_at() filtering
  • Query over ranges with query_time_range and overlap/coverage predicates
  • Find valid paths with find_temporal_paths and configurable ordering strategies
  • Analyze coverage using gap_analysis and merge_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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →