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

> Query temporal graphs efficiently using Semantica's TemporalGraphQuery. Explore time-point queries, aggregations, pattern detection, and path finding on knowledge graphs with validity periods. Get the complete guide now.

- Repository: [Semantica /semantica](https://github.com/semantica-agi/semantica)
- Tags: how-to-guide
- Published: 2026-08-15

---

**Use `TemporalGraphQuery` from [`semantica/kg/temporal_query.py`](https://github.com/semantica-agi/semantica/blob/main/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](https://github.com/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`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_query.py) (lines 41-61) |
| **TemporalReasoningEngine** | Interval arithmetic (containment, overlap, ordering) | [`semantica/kg/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_reasoning.py) |
| **TemporalPatternDetector** | Detects sequences and cycles | [`semantica/kg/temporal_query.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_query.py) (lines 111-125) |
| **TemporalVersionManager** | Reproducible queries on historic graph states | [`semantica/kg/temporal_query.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_query.py) (lines 142-154) |
| **Utility helpers** | Timestamp parsing, granularity handling | Throughout [`temporal_query.py`](https://github.com/semantica-agi/semantica/blob/main/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:

```python
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:

```python
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:

```python
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`:

```python
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`:

```python
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:

```python
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:

```python
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`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_query.py) | `TemporalGraphQuery`, `TemporalPatternDetector`, `TemporalVersionManager` |
| [`semantica/kg/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_reasoning.py) | `TemporalReasoningEngine`, interval relation logic |
| [`semantica/kg/temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_model.py) | `BiTemporalFact`, `TemporalBound` data models |
| [`semantica/utils/logging.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/utils/logging.py) | Logging configuration for query operations |
| [`semantica/utils/progress_tracker.py`](https://github.com/semantica-agi/semantica/blob/main/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`](https://github.com/semantica-agi/semantica/blob/main/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.