# How Semantica Handles Temporal Reasoning: Deterministic Allen Interval Algebra Implementation

> Discover how Semantica achieves deterministic temporal reasoning with Allen's interval algebra. Explore relation detection, merging, and gap analysis without LLMs.

- Repository: [Semantica /semantica](https://github.com/semantica-agi/semantica)
- Tags: deep-dive
- Published: 2026-08-22

---

**Semantica implements temporal reasoning as a deterministic, pure-Python engine based on Allen's interval algebra, using immutable `TemporalInterval` objects and the `TemporalReasoningEngine` class to perform relation detection, merging, gap analysis, and retroactive impact classification without any LLM dependencies.**

The semantica-agi/semantica repository provides a robust knowledge graph infrastructure where temporal reasoning operates through mathematical interval algebra rather than probabilistic inference. All temporal operations are confined to the `TemporalReasoningEngine` class, which guarantees reproducible behavior across downstream pipelines by eliminating non-deterministic components.

## Core Architecture and Design Philosophy

Semantica's temporal reasoning engine is architected as a stateless, deterministic processor located in [`semantica/kg/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_reasoning.py) and publicly re-exported through [`semantica/reasoning/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/temporal_reasoning.py). The engine operates exclusively on immutable `TemporalInterval` objects and the `IntervalRelation` enum, ensuring thread-safe operations and predictable results.

The implementation follows **Allen's interval algebra**, a mathematical framework that defines all possible temporal relationships between two intervals (BEFORE, OVERLAPS, CONTAINS, etc.). Because the engine contains **no LLM calls**, it produces fully deterministic outputs suitable for audit trails and reproducible knowledge graph construction.

## Data Structures and Allen's Interval Relations

The foundation of Semantica's temporal capabilities rests on three core data structures defined across [`semantica/kg/temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_model.py) and the reasoning modules:

- **`TemporalInterval`**: Immutable objects representing time spans with UTC-aware `datetime` start and end points.
- **`IntervalRelation`**: An enum encoding all thirteen possible Allen relations (BEFORE, MEETS, OVERLAPS, FINISHES, CONTAINS, etc.).
- **`TemporalBound.OPEN`**: A sentinel value representing open-ended intervals that extend indefinitely into the future or past.

The engine validates all intervals through the `_validate_interval` method, which guarantees that `start ≤ end` for any bounded interval. For granularity-based operations, the `_expand_end` method adjusts interval endpoints to the last instant of the chosen temporal granularity (second through year).

## Temporal Operations and Capabilities

The `TemporalReasoningEngine` exposes eight primary capability categories, each implemented as specific methods with deterministic algorithms.

### Relation Detection and Boolean Checks

The `relation(a, b)` method computes the precise Allen relationship between two intervals, returning an `IntervalRelation` enum value. For convenience, the engine provides optimized boolean accessors:

- **`overlaps(a, b)`**: Returns `True` if intervals share any common instant.
- **`contains(outer, inner)`**: Returns `True` if the outer interval fully encompasses the inner interval.

### Active-At Queries with Granularity Support

The `active_at(interval, timestamp, granularity=None)` method determines whether a specific timestamp falls within an interval. When provided, the `granularity` parameter (accepting values from `"second"` to `"year"`) normalizes both the interval and timestamp before comparison, enabling fuzzy temporal membership checks.

### Merging, Gap Analysis, and Coverage Metrics

For batch interval processing, the engine provides:

- **`merge_intervals(intervals)`**: Normalizes a collection of potentially overlapping intervals into a minimal set of non-overlapping, chronologically ordered intervals.
- **`gap_analysis(intervals, domain_start, domain_end)`**: Identifies temporal gaps within a specified domain that remain uncovered by the provided intervals.
- **`coverage_percentage(intervals, domain_start, domain_end)`**: Calculates the exact proportion of a temporal domain covered by the interval set, returning a float between 0.0 and 1.0.

### Timeline Construction and Retroactive Impact

For knowledge graph applications:

- **`timeline_of(entity_id, graph)`**: Extracts a chronologically ordered sequence of fact-level events (add, modify, remove) for a specific entity or relationship from the graph structure.
- **`retroactive_coverage`**: Classifies existing bi-temporal facts within the graph as *affected*, *partial*, or *unaffected* by a proposed revision, enabling impact analysis before applying changes.

### Normalization Utilities

The engine ensures temporal consistency through `normalize_timestamp` and `normalize_interval` methods, which align arbitrary datetime objects to specified granularities. All normalization operations preserve UTC awareness and handle the `TemporalBound.OPEN` sentinel appropriately.

## Working with Temporal Intervals: Code Examples

The following example demonstrates relation detection, active-at queries, interval merging, and coverage calculation using the public API:

```python
from datetime import datetime, timezone
from semantica.reasoning.temporal_reasoning import (
    TemporalInterval,
    TemporalReasoningEngine,
    IntervalRelation,
    TemporalBound,
)

engine = TemporalReasoningEngine()

# Define two intervals

a = TemporalInterval(
    start=datetime(2023, 1, 1, tzinfo=timezone.utc),
    end=datetime(2023, 1, 10, tzinfo=timezone.utc),
)
b = TemporalInterval(
    start=datetime(2023, 1, 5, tzinfo=timezone.utc),
    end=TemporalBound.OPEN,               # Open-ended interval

)

# Relation between a and b

rel = engine.relation(a, b)
print(rel)                     # → IntervalRelation.OVERLAPS

# Check if a timestamp is active within an interval (day granularity)

ts = "2023-01-05T12:34:00Z"
print(engine.active_at(a, ts, granularity="day"))  # → True

# Merge overlapping intervals

intervals = [a, b, TemporalInterval(
    start=datetime(2023, 1, 12, tzinfo=timezone.utc),
    end=datetime(2023, 1, 20, tzinfo=timezone.utc),
)]
merged = engine.merge_intervals(intervals)
print(merged)  # → two merged intervals (one open-ended)

# Compute coverage of a domain

domain_start = "2023-01-01"
domain_end = "2023-01-31"
coverage = engine.coverage_percentage(merged, domain_start, domain_end)
print(f"Coverage: {coverage:.2%}")  # → e.g., 71.4%

```

## Implementation Details and File Structure

The temporal reasoning system spans four key locations in the Semantica codebase:

- **[`semantica/kg/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_reasoning.py)**: Contains the core `TemporalReasoningEngine` implementation, including `_validate_interval` and `_expand_end` helper methods.
- **[`semantica/reasoning/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/temporal_reasoning.py)**: Public façade that re-exports the engine and data structures for consumer access.
- **[`semantica/kg/temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_model.py)**: Defines `BiTemporalFact`, `TemporalBound`, and parsing utilities used by the engine.
- **[`tests/test_395_temporal_semantics_comprehensive.py`](https://github.com/semantica-agi/semantica/blob/main/tests/test_395_temporal_semantics_comprehensive.py)**: Comprehensive test suite validating interval relations, merging algorithms, gap analysis, and coverage calculations.

All datetime handling strictly enforces UTC awareness to prevent timezone ambiguity, while the `TemporalBound.OPEN` sentinel enables representation of unbounded intervals without resorting to arbitrary maximum dates.

## Summary

- Semantica implements **deterministic temporal reasoning** through Allen's interval algebra, ensuring reproducible results without LLM dependencies.
- The `TemporalReasoningEngine` class in [`semantica/kg/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_reasoning.py) provides immutable, UTC-aware interval operations.
- Core capabilities include **relation detection**, **active-at queries**, **interval merging**, **gap analysis**, and **retroactive impact classification**.
- Open-ended intervals are handled via the `TemporalBound.OPEN` sentinel rather than arbitrary date limits.
- All temporal validation and granularity expansion occur through internal methods `_validate_interval` and `_expand_end`, guaranteeing mathematical consistency.

## Frequently Asked Questions

### How does Semantica ensure deterministic temporal reasoning?

Semantica eliminates non-determinism by implementing temporal reasoning as pure-Python mathematical operations without any LLM calls or stochastic components. The `TemporalReasoningEngine` processes immutable `TemporalInterval` objects using Allen's interval algebra, producing identical outputs for identical inputs across all executions, as verified in [`tests/test_395_temporal_semantics_comprehensive.py`](https://github.com/semantica-agi/semantica/blob/main/tests/test_395_temporal_semantics_comprehensive.py).

### What file contains the core temporal reasoning implementation?

The primary implementation resides in [`semantica/kg/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_reasoning.py), which defines the `TemporalReasoningEngine` class and all interval algebra logic. Consumers typically import from [`semantica/reasoning/temporal_reasoning.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/temporal_reasoning.py), which serves as the public façade re-exporting these components alongside definitions from [`semantica/kg/temporal_model.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/kg/temporal_model.py).

### How does Semantica handle open-ended or infinite time intervals?

Semantica represents unbounded intervals using the `TemporalBound.OPEN` sentinel value rather than arbitrary maximum datetime values. This sentinel can be assigned to either the start or end of a `TemporalInterval`, allowing the engine to properly handle queries against intervals that extend indefinitely into the past or future through the `relation` and `active_at` methods.

### Can Semantica normalize timestamps to different granularities?

Yes, the engine supports temporal normalization from second-level precision up to year-level granularity through the `normalize_timestamp` and `normalize_interval` methods. When using `active_at` with a `granularity` parameter (e.g., `"day"` or `"month"`), the engine internally calls `_expand_end` to adjust interval endpoints to the last instant of the specified granularity before performing comparisons.