Skip to main content

Probabilistic temporal belief graph for uncertain narrative timelines.

Project description

temporal-belief-graph

PyPI version Python Tests License: MIT

A lightweight Python package for modeling uncertain event orderings in narrative timelines, lore databases, historical reconstructions, and worldbuilding systems.

Instead of assuming every temporal relation is fixed, temporal-belief-graph represents event ordering as probabilistic belief edges and updates them as new evidence accumulates — using log-odds Bayesian updates that never reverse direction due to weak sources.


Why this exists

Most graph libraries treat edges as binary: A comes before B, or it does not.
Real-world narrative and lore data is messier. Multiple sources disagree. Evidence is partial. Some orderings are guesses.

temporal-belief-graph was built to handle exactly this:

  • A story timeline where fan sources and official sources conflict
  • A historical reconstruction where some events have uncertain sequencing
  • A worldbuilding database where causality and temporal order must be separated
  • Any system where "we think A happened before B, but we are not sure" needs to be expressed and updated over time

Installation

pip install temporal-belief-graph

Requires Python 3.10 or later. No external dependencies.


Quick start

from tbg import BeliefGraph, EventNode, PriorConfig
from tbg import BayesianUpdater, Evidence, Validator
from tbg import graph_to_json, graph_from_json
from tbg.report import graph_to_markdown, graph_to_mermaid

# 1. Configure the graph
config = PriorConfig(
    default_p=0.5,
    source_weights={"official_lore": 1.5, "fan_wiki": 0.7},
)
graph = BeliefGraph(prior_config=config)

# 2. Add events
graph.add_node(EventNode(id="kain_incident", label="Kain Incident", era="First Rift War"))
graph.add_node(EventNode(id="rift_opening",  label="Rift Opening",  era="First Rift War"))

# 3. Add an uncertain edge (p=0.5 means order is unknown)
graph.init_uniform("kain_incident", "rift_opening")

# 4. Update belief with evidence
updater = BayesianUpdater(graph)
updater.update_edge(
    "kain_incident", "rift_opening",
    Evidence(key="official_001", supports_forward=True, strength=0.9, source="official_lore"),
)
updater.update_edge(
    "kain_incident", "rift_opening",
    Evidence(key="fan_002", supports_forward=False, strength=0.6, source="fan_wiki"),
)

edge = graph.get_edge("kain_incident", "rift_opening")
print(edge.p_forward)   # updated belief: P(kain_incident → rift_opening)

# 5. Explain the current belief state
print(updater.explain_edge("kain_incident", "rift_opening"))

# 6. Validate the graph
result = Validator().validate(graph)
print(result.is_valid)
print(result.warnings)

# 7. Export
graph_to_json(graph, "graph.json")
print(graph_to_mermaid(graph))

A full working example is available in examples/basic_usage.py.


Core concepts

EventNode

A single event in the graph. Every node carries an era — a concrete arc, chapter, or period name. Vague era labels like "present" or "past" are flagged by the validator.

EventNode(id="my_event", label="The Great Collapse", era="Third Age")

BeliefEdge

A directed edge from source_id to target_id carrying p_forward: the probability that the source event occurs before the target event.

p_forward Meaning
0.5 No information — uniform prior
0.9 Strong belief that source comes first
0.1 Strong belief that target comes first

BayesianUpdater

Updates edge probabilities using log-odds steps:

new_logit = old_logit + direction × strength × source_weight × learning_rate
p_new     = sigmoid(new_logit)

Key property: a source with source_weight < 1.0 produces a smaller update in the same direction — it never flips the direction of evidence.

Ensemble update

When multiple sources each propose a p_forward value, ensemble_update combines them via weighted average before applying a single Bayesian step:

updater.ensemble_update(
    "event_a", "event_b",
    claims=[
        (0.9, 1.5),   # official source claims 0.9, weight 1.5
        (0.6, 0.8),   # secondary source claims 0.6, weight 0.8
        (0.3, 0.5),   # weak source claims 0.3, weight 0.5
    ],
)

Validator

Runs seven checks and returns a ValidationResult with errors and warnings:

Check Level
pseudo_era — vague era label used error
cycle — confirmed edges form a directed cycle error
contradiction — two opposing edges both highly confident error
missing_era — node has no era value warning
missing_evidence — edge probability changed without evidence warning
overconfident_edge — high probability with very few updates warning
isolated_node — node has no incident edges warning
result = Validator().validate(graph)
result.raise_if_errors()   # raises ValueError if any errors exist

JSON export / import

from tbg import graph_to_json, graph_from_json

graph_to_json(graph, "graph.json")
restored = graph_from_json("graph.json")

Markdown and Mermaid report

from tbg.report import graph_to_markdown, graph_to_mermaid, save_markdown, save_mermaid

print(graph_to_markdown(graph))   # full Markdown report
print(graph_to_mermaid(graph))    # Mermaid flowchart diagram
save_markdown(graph, "report.md")
save_mermaid(graph, "diagram.mmd")

Package structure

tbg/
├── schema.py      EventNode, BeliefEdge, EvidenceRecord, PriorConfig
├── graph.py       BeliefGraph
├── bayesian.py    BayesianUpdater, Evidence
├── validator.py   Validator, ValidationResult
├── io.py          graph_to_json, graph_from_json
└── report.py      graph_to_markdown, graph_to_mermaid

examples/
└── basic_usage.py End-to-end example

docs/
└── model.md       Mathematical model documentation

Design principles

Probabilistic by default. Every edge starts uncertain. Certainty is earned through accumulated evidence, not assumed.

Direction-safe updates. A weak source supporting forward always moves p_forward up — never down. Source weight controls magnitude, not direction.

Explainable history. Every probability update is recorded as an EvidenceRecord on the edge. You can always reconstruct why the current value is what it is.

No hard deletes. The graph is append-friendly. Contradictions are surfaced by the validator, not silently overwritten.

No external dependencies. Pure Python standard library only.


Development

Install the package in editable mode with development dependencies:

pip install -e ".[dev]"

Run the test suite:

pytest

Run tests across all supported Python versions:

tox

Build the package:

python -m build

This package is currently in alpha. APIs may change before version 1.0.0.


Contributing

Contributions, issues, and feature requests are welcome.
Please open an issue before submitting a pull request.


Contributors

Handle GitHub Role
lajjadred @lajjadred Project lead
이채문 @CHML-real Mathematical algorithm development
CUBE @90cube Idea proposal and data collection

License

MIT License. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

temporal_belief_graph-0.1.3.tar.gz (23.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

temporal_belief_graph-0.1.3-py3-none-any.whl (17.3 kB view details)

Uploaded Python 3

File details

Details for the file temporal_belief_graph-0.1.3.tar.gz.

File metadata

  • Download URL: temporal_belief_graph-0.1.3.tar.gz
  • Upload date:
  • Size: 23.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for temporal_belief_graph-0.1.3.tar.gz
Algorithm Hash digest
SHA256 b400c6a9d10f35587e74a650a1dae18709127942e384e5d4305ddd1236fda40e
MD5 0d7ff5ccb208f5a95e0384bf5941dbbd
BLAKE2b-256 82e9467e32ea1602447d1a45855e5931204ee3ef4b47892a2fb10107338d5936

See more details on using hashes here.

File details

Details for the file temporal_belief_graph-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for temporal_belief_graph-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 8abd1a9d4290041debb3b379281102ce5601905ee69f786fa80a7cff79d7499d
MD5 1ca9c07c1e43dd21017d5cb473a4a227
BLAKE2b-256 4b958340a15ba68cefb11902bca6029846b88af7b061838500f92f9209146f5f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page