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 theTemporalReasoningEnginefor advanced interval inferencegranularity— 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_fromandvalid_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_queryto access the complete temporal query API - Query single instants with
query_at_timefor point-in-time graph reconstruction - Query intervals with
query_time_rangeusing union, intersection, or evolution aggregation - Detect patterns via
query_temporal_patternsupporting sequences and cycles - Find paths with
find_temporal_pathsenforcing strict or relaxed causal ordering - Analyze trends using
analyze_evolutionwith 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →