# `WolframModel`
[🔗](https://github.com/sragli/wolfram_model/blob/main/lib/wolfram_model.ex#L1)

A simplified implementation of the Wolfram Model using hypergraphs,capturing
key structural ideas of the Wolfram Model (hypergraph rewriting, event
causality, multiway branching).

# `rule`

```elixir
@type rule() :: %{
  pattern: [[Hypergraph.vertex()]],
  replacement: [[Hypergraph.vertex()]],
  name: String.t()
}
```

# `t`

```elixir
@type t() :: %WolframModel{
  causal_network: [WolframModel.Event.t()],
  event_index: term(),
  event_map: term(),
  evolution_history: [Hypergraph.t()],
  generation: non_neg_integer(),
  hypergraph: Hypergraph.t(),
  id_generator: (-&gt; integer()),
  next_event_id: term(),
  rules: [rule()]
}
```

# `branchial_graph`

```elixir
@spec branchial_graph(t()) :: %{nodes: [map()], edges: [map()]}
```

Builds the branchial graph for one step of multiway evolution.

Nodes are the possible rule matches at the current state. Two matches are
branchially connected when they overlap (touch at least one common hyperedge),
meaning they represent conflicting/branching rewrite choices.

Returns `%{nodes: [map()], edges: [map()]}`.

# `causal_network_data`

```elixir
@spec causal_network_data(t()) :: %{nodes: [map()], edges: [map()]}
```

Creates a visualization-friendly representation of the causal network.
Each event (i.e., a specific hypergraph update) becomes a vertex in the causal graph.
Implements the partial order of event dependencies (a DAG): if the application of
event B depends on the output of event A (e.g., B’s matching pattern overlaps with
hyperedges created by A), then the causal graph includes a directed edge A → B.

# `causally_invariant?`

```elixir
@spec causally_invariant?(t(), non_neg_integer()) :: boolean()
```

Checks causal invariance (confluence) for the current model state.

Tests all pairs of rule matches — both overlapping and non-overlapping:

- *Non-overlapping pairs*: applied in both orders; the results must be
  identical (immediate commutativity).
- *Overlapping pairs*: each match is applied independently; then evolution
  continues for up to `depth` additional steps (default 2) on each branch.
  Invariance holds if the two branches reach a common state within that
  depth (local Church-Rosser property).

Returns `true` if all tested pairs satisfy the check, `false` otherwise.
An empty match set is trivially invariant.

# `evolve_parallel`

```elixir
@spec evolve_parallel(t()) :: t()
```

Applies all non-conflicting rule matches in parallel during a single step.

Matches are processed greedily in rule/hyperedge order: once a hyperedge is
consumed by a chosen match it is unavailable to subsequent matches in the
same step. Returns the unchanged model when no matches exist.

# `evolve_step`

```elixir
@spec evolve_step(
  t(),
  keyword()
) :: t()
```

Evolves the universe by one step, applying one rule match selected by
`opts[:ordering]`.

Supported orderings:
- `:first` (default) — first match in rule/hyperedge order
- `:leftmost` — match whose hyperedges have the smallest vertex sort key
- `:random` — uniformly random match

Accepts `opts[:id_generator]` to override the model's id generator for
deterministic behaviour during tests.

Returns the unchanged model when no rules are applicable (fixpoint).

# `evolve_steps`

```elixir
@spec evolve_steps(t(), non_neg_integer(), keyword()) :: t()
```

Evolves the universe for a specified number of steps.
Accepts `opts` forwarded to `evolve_step/2` (e.g., `:id_generator`).

# `evolve_until_fixpoint`

```elixir
@spec evolve_until_fixpoint(t(), non_neg_integer(), keyword()) :: t()
```

Evolves the universe by repeatedly calling `evolve_step/2` until either
no rule is applicable or `max_steps` is reached.

Returns the final model. You can check `fixpoint?/1` on the result to
distinguish a natural fixpoint from a step-limit halt.

# `export_event_graph`

```elixir
@spec export_event_graph(t()) :: %{nodes: [map()], edges: [map()]}
```

Exports the causal event graph as a map of nodes and edges.

Nodes correspond to events in chronological order. Edges link parent events to
child events using the `parent_ids` recorded during evolution.

# `fixpoint?`

```elixir
@spec fixpoint?(t()) :: boolean()
```

Returns `true` when no rule can be applied to the current hypergraph
(the system has reached a fixpoint).

# `foliations`

```elixir
@spec foliations(t()) :: [[WolframModel.Event.t()]]
```

Computes foliations of the causal network: layers of events where each layer
contains only events whose parents all belong to earlier layers (spacelike
slices of the causal partial order).

Returns a list of lists of `Event.t()`, ordered from earliest to latest layer.
Layer 0 contains root events (no causal parents). Layer N contains events
whose deepest ancestor is in layer N-1.

# `multiway_explore`

```elixir
@spec multiway_explore(t(), non_neg_integer()) :: %{model: t(), children: [map()]}
```

Explores the multiway graph up to a specified depth.
Returns a tree of possible evolution paths.

# `multiway_explore_dag`

```elixir
@spec multiway_explore_dag(t(), non_neg_integer()) :: %{
  root: term(),
  nodes: map(),
  edges: MapSet.t()
}
```

Explores the multiway system as a directed acyclic graph (DAG) up to
`depth` steps. Unlike `multiway_explore/2`, states that are reachable via
multiple paths are represented once and converging branches share nodes.

Returns:
```
%{
  root: canonical_key,
  nodes: %{canonical_key => %WolframModel{}},
  edges: MapSet.t({from_key, to_key})
}
```
where `canonical_key` is a sorted list-of-lists encoding of the hypergraph.

# `multiway_step`

```elixir
@spec multiway_step(t()) :: [t()]
```

Finds all possible rule applications in the current state (multiway evolution).
Returns a deduplicated list of possible next states (unique hypergraphs).

# `new`

```elixir
@spec new(Hypergraph.t(), [rule()], keyword()) :: t()
```

Creates a new Wolfram Model universe with initial hypergraph and rules.

# `print_stats`

```elixir
@spec print_stats(t()) :: :ok
```

Helper function to print evolution statistics.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
