Mix.install([
{:jason, "~> 1.4"},
{:kino, "~> 0.12"},
{:kino_vega_lite, "~> 0.1"},
{:vega_lite, "~> 0.1"},
{:wolfram_model, path: __DIR__}
])Universe
steps = 30
initial =
Hypergraph.new()
|> Hypergraph.add_hyperedge([1, 2])
|> Hypergraph.add_hyperedge([2, 3])
|> Hypergraph.add_hyperedge([3, 4])
|> Hypergraph.add_hyperedge([4, 5])
|> Hypergraph.add_hyperedge([5, 6])
|> Hypergraph.add_hyperedge([6, 7])
|> Hypergraph.add_hyperedge([7, 8])
|> Hypergraph.add_hyperedge([8, 9])
|> WolframModel.new(WolframModel.RuleSet.rule_set(:growth))
evolved = WolframModel.evolve_steps(initial, steps)
Hypergraph.stats(evolved.hypergraph)WolframModel.HypergraphSVG.to_svg(evolved.hypergraph)
|> Kino.HTML.new()WolframModel.GeodesicPlotSVG.to_svg(evolved.hypergraph)
|> Kino.HTML.new()WolframModel.HypergraphSVG.evolution_to_svg(evolved)
|> Kino.HTML.new()Causal Network
This shows how evolution events are causally connected through time:
WolframModel.causal_network_data(evolved)
|> WolframModel.CausalGraphSVG.to_svg()
|> Kino.HTML.new()Foliations (Spacelike Slices)
Foliations partition causal events into layers where each layer contains only events whose parents all belong to earlier layers — the hypergraph analogue of constant-time slices in relativity.
layers = WolframModel.foliations(evolved)
foliation_data =
layers
|> Enum.with_index()
|> Enum.flat_map(fn {events, layer} ->
Enum.map(events, fn e ->
%{layer: layer, event_id: e.id, rule: e.rule.name, parents: length(e.parent_ids)}
end)
end)
VegaLite.new(width: 600, height: 300, title: "Causal Foliations (Spacelike Layers)")
|> VegaLite.data_from_values(foliation_data)
|> VegaLite.mark(:circle, size: 120)
|> VegaLite.encode_field(:x, "event_id", type: :quantitative, axis: [title: "Event ID"])
|> VegaLite.encode_field(:y, "layer", type: :quantitative, axis: [title: "Foliation Layer"])
|> VegaLite.encode_field(:color, "rule", type: :nominal, legend: [title: "Rule"])
|> VegaLite.encode(:tooltip, [
[field: "layer", type: :quantitative, title: "Layer"],
[field: "event_id", type: :quantitative, title: "Event ID"],
[field: "rule", type: :nominal, title: "Rule"],
[field: "parents", type: :quantitative, title: "# Parents"]
])Branchial Graph
The branchial graph connects rule matches that overlap (conflict) at the current state — these are the branching points in multiway evolution. Two nodes are adjacent when their matched hyperedges share a vertex.
WolframModel.branchial_graph(evolved)
|> WolframModel.BranchialGraphSVG.to_svg()
|> Kino.HTML.new()Causal Invariance
Check whether all pairs of non-overlapping rule applications at the initial state commute (a necessary condition for relativistic invariance).
WolframModel.causally_invariant?(initial)Event Graph Export
Export the full event DAG as nodes + edges for external tooling:
event_graph = WolframModel.export_event_graph(evolved)
IO.puts("Events: #{length(event_graph.nodes)}")
IO.puts("Causal edges: #{length(event_graph.edges)}")
event_graphEffective Spatial Dimension
Estimate the emergent spatial dimension of the evolved hypergraph using geodesic ball growth (fits $V(r) \sim r^d$ via log-log regression).
Track how dimension evolves across generations:
dim_data =
evolved.evolution_history
|> Enum.reverse()
|> Enum.with_index()
|> Enum.map(fn {hg, gen} ->
%{generation: gen, dimension: WolframModel.Analytics.estimate_dimension(hg)}
end)
VegaLite.new(width: 600, height: 300, title: "Emergent Spatial Dimension Over Time")
|> VegaLite.data_from_values(dim_data)
|> VegaLite.mark(:line, point: true, strokeWidth: 2)
|> VegaLite.encode_field(:x, "generation", type: :quantitative, axis: [title: "Generation"])
|> VegaLite.encode_field(:y, "dimension", type: :quantitative, axis: [title: "Est. Dimension"])
|> VegaLite.encode(:tooltip, [
[field: "generation", type: :quantitative],
[field: "dimension", type: :quantitative, format: ".3f"]
])Rule Analysis
Inspect the structural properties of the built-in rule sets:
alias WolframModel.RuleAnalysis
WolframModel.RuleSet.basic_rules()
|> Enum.map(fn rule ->
{pat, rep} = RuleAnalysis.arity(rule)
%{
name: rule.name,
reversible: RuleAnalysis.reversible?(rule),
self_complementary: RuleAnalysis.self_complementary?(rule),
introduces_new_vertices: RuleAnalysis.introduces_new_vertices?(rule),
hyperedge_delta: RuleAnalysis.hyperedge_delta(rule),
pattern_arities: inspect(pat),
replacement_arities: inspect(rep)
}
end)
|> Kino.DataTable.new()Multiway Evolution
WolframModel.multiway_explore_dag(evolved, 2)
|> WolframModel.MultiwayGraphSVG.to_svg()
|> Kino.HTML.new()