How to Query Temporal Graphs Using Semantica's TemporalGraphQuery: A Complete Guide

Use TemporalGraphQuery from semantica/kg/temporal_query.py to execute time-point queries, time-range aggregations, pattern detection, and temporal path finding on knowledge graphs with validity periods.

Semantica stores knowledge as a temporal knowledge graph—entities and relationships annotated with valid_from and valid_until timestamps. The TemporalGraphQuery class provides the primary Python API for querying these time-aware structures in the semantica-agi/semantica repository. This guide walks through all query types with runnable code examples.

Core Architecture of Temporal Graph Queries

Understanding how to query temporal graphs using Semantica's TemporalGraphQuery requires familiarity with its five core components:

Component Role Location
TemporalGraphQuery Public API for all query operations semantica/kg/temporal_query.py (lines 41-61)
TemporalReasoningEngine Interval arithmetic (containment, overlap, ordering) semantica/kg/temporal_reasoning.py
TemporalPatternDetector Detects sequences and cycles semantica/kg/temporal_query.py (lines 111-125)
TemporalVersionManager Reproducible queries on historic graph states semantica/kg/temporal_query.py (lines 142-154)
Utility helpers Timestamp parsing, granularity handling Throughout temporal_query.py

The TemporalGraphQuery class orchestrates these components. When you invoke any query method, it delegates interval logic to TemporalReasoningEngine and pattern detection to TemporalPatternDetector.

Initializing TemporalGraphQuery

Create an instance with optional configuration for reasoning and granularity:

from semantica.kg.temporal_query import TemporalGraphQuery

engine = TemporalGraphQuery(
    enable_reasoning=True,      # Enable inference from interval relations

    granularity="day"           # Truncate comparisons to day precision

)

Key parameters:

  • enable_reasoning — Activates the TemporalReasoningEngine for advanced interval inference
  • granularity — One of "second", "minute", "hour", "day", "month", "year"; all timestamps are truncated before comparison

Sample Graph Setup

All following examples use this in-memory graph structure:

graph = {
    "entities": [
        {"id": "E1", "valid_from": "2020-01-01", "valid_until": "2025-12-31"},
        {"id": "E2", "valid_from": "2020-06-01", "valid_until": "2024-12-31"},
    ],
    "relationships": [
        {
            "id": "R1",
            "source": "E1",
            "target": "E2",
            "type": "connected_to",
            "valid_from": "2021-01-01",
            "valid_until": "2023-01-01",
        },
        {
            "id": "R2",
            "source": "E2",
            "target": "E1",
            "type": "connected_to",
            "valid_from": "2023-02-01",
            "valid_until": "2024-01-01",
        },
    ],
}

Time-Point Queries with query_at_time

The query_at_time method reconstructs the subgraph valid at a specific instant:

result = engine.query_at_time(graph, query="", at_time="2022-06-15")
print("Relationships valid on 2022-06-15:", result["relationships"])

Output: Only R1 appears because 2022-06-15 falls within R1's validity interval (2021-01-01 to 2023-01-01), but not R2's.

Internally, query_at_time calls _relationship_active_at_time and _entity_active_at_time to verify that:

  • The query timestamp falls within valid_from and valid_until
  • Both source and target entities are alive at that time (via _window_within_entity_lifetime)

Time-Range Queries with query_time_range

For intervals spanning multiple validity periods, use query_time_range:

range_result = engine.query_time_range(
    graph,
    query="",
    start_time="2022-01-01",
    end_time="2023-12-31",
    temporal_aggregation="union",
)
print("Relationships overlapping the range:", range_result["relationships"])

Aggregation modes:

  • "union" — Returns relationships active at any point in the range
  • "intersection" — Returns relationships active throughout the entire range
  • "evolution" — Returns relationships with their validity status changes

Both R1 and R2 appear in union mode because each overlaps the query window, even though they don't overlap each other.

Temporal Pattern Detection with query_temporal_pattern

Detect recurring structures using query_temporal_pattern, which delegates to TemporalPatternDetector:

patterns = engine.query_temporal_pattern(
    graph,
    pattern="sequence",
    min_support=1,
)
print("Detected sequences:", patterns["patterns"])

Supported patterns:

  • "sequence" — Ordered node-to-node transitions with configurable gap tolerance
  • "cycle" — Closed loops in temporal edge sequences

The detector walks outgoing edges while respecting the gap_tolerance parameter (default: none), returning patterns that meet min_support frequency thresholds.

Temporal Path Finding with find_temporal_paths

Find chronologically valid routes between entities:

paths = engine.find_temporal_paths(
    graph,
    source="E1",
    target="E2",
    start_time="2020-01-01",
    end_time="2025-01-01",
    max_path_length=3,
    enforce_causal_ordering=True,
    ordering_strategy="strict",
)
print("Temporal paths:", paths["paths"])

Causal ordering strategies:

  • "strict" — Each edge must start after the previous edge ends
  • "overlap" — Adjacent edges must overlap in time
  • "any" — No temporal constraints between consecutive edges

The BFS algorithm in find_temporal_paths builds a time-aware adjacency map and validates each step against _path_respects_causal_order when enforce_causal_ordering is enabled.

Evolution Analysis with analyze_evolution

Aggregate structural metrics over time windows:

evolution = engine.analyze_evolution(
    graph,
    start_time="2020-01-01",
    end_time="2025-01-01",
    metrics=["count", "diversity", "stability"],
)
print(evolution)

Available metrics:

  • "count" — Total relationship instances
  • "diversity" — Number of unique relationship types
  • "stability" — Fraction of persistent relationships across sub-intervals

How Temporal Consistency Is Enforced

Three mechanisms ensure valid results when querying temporal graphs using Semantica's TemporalGraphQuery:

Entity lifetime checks The _window_within_entity_lifetime helper verifies that relationship validity intervals fall within both source and target entity lifetimes. A relationship is discarded if either endpoint didn't exist.

Interval ordering When enforce_causal_ordering=True, _path_respects_causal_order validates that path edges follow the specified ordering_strategy. This prevents causality violations in temporal path results.

Granularity truncation All comparisons pass through _truncate_to_granularity using the engine's configured precision. This ensures consistent behavior when timestamps have varying precision.

Key Source Files

File Responsibility
semantica/kg/temporal_query.py TemporalGraphQuery, TemporalPatternDetector, TemporalVersionManager
semantica/kg/temporal_reasoning.py TemporalReasoningEngine, interval relation logic
semantica/kg/temporal_model.py BiTemporalFact, TemporalBound data models
semantica/utils/logging.py Logging configuration for query operations
semantica/utils/progress_tracker.py Optional progress tracking for long-running queries

Summary

  • Import from semantica.kg.temporal_query to access the complete temporal query API
  • Query single instants with query_at_time for point-in-time graph reconstruction
  • Query intervals with query_time_range using union, intersection, or evolution aggregation
  • Detect patterns via query_temporal_pattern supporting sequences and cycles
  • Find paths with find_temporal_paths enforcing strict or relaxed causal ordering
  • Analyze trends using analyze_evolution with count, diversity, and stability metrics

All temporal operations respect entity lifetimes, configurable granularity, and explicit causal constraints as implemented in the Semantica source code.

Frequently Asked Questions

What is the minimum Python version for Semantica's temporal query features?

Semantica requires Python 3.9 or higher. The TemporalGraphQuery class uses modern type hints and dataclass features introduced in later Python versions. Install with pip install semantica to resolve all dependencies including the temporal reasoning engine.

How does TemporalGraphQuery handle timezone-aware timestamps?

The engine accepts ISO 8601 formatted strings and passes them through _parse_time in temporal_query.py. For consistent behavior, normalize timestamps to UTC before querying. The granularity parameter truncates after parsing, so timezone information must be resolved upstream.

Can I query historical graph versions with TemporalVersionManager?

Yes. TemporalVersionManager (lines 142-154) enables reproducible queries on past states. Create snapshots via version_manager.create_version(graph, label) and later query with engine.query_at_time(versioned_graph, at_time=..., version_label="..."). This integrates with query_at_time and query_time_range for audit and rollback scenarios.

What is the performance impact of enforce_causal_ordering in path finding?

Strict causal ordering adds O(k) validation per edge expansion in the BFS, where k is path depth. For large graphs, set max_path_length conservatively or disable ordering with enforce_causal_ordering=False. The TemporalReasoningEngine caches interval relations to mitigate repeated comparisons.

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 →