Skip to main content

acsets-algebraic-databases

ACSets (Attributed C-Sets): Algebraic databases with Specter-style bidirectional

Zur Installation springen

Quellinformationen

Repository
plurigrid/asi
Letzte Quellaktivität
10. Juni 2026 um 11:55
Erkannte Sprache von SKILL.md
Englisch
Sterne
64
Forks
12

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
acsets-algebraic-databases
description
ACSets (Attributed C-Sets): Algebraic databases with Specter-style bidirectional
version
1.0.0
# ACSets: Algebraic Databases Skill > *"The category of simple graphs does not even have a terminal object!"* > — AlgebraicJulia Blog, with characteristic ironic detachment ## bmorphism Contributions > *"Parametrised optics model cybernetic systems, namely dynamical systems steered by one or more agents. Then ⊛ represents agency being exerted on systems"* > — [@bmorphism](https://github.com/bmorphism), GitHub bio > *"universal topos construction for social cognition and democratization of mathematical approach to problem-solving to all"* > — [Plurigrid: the story thus far](https://gist.github.com/bmorphism/a400e174b9f93db299558a6986be0310) **Related repos**: - [plurigrid/act](https://github.com/plurigrid/act) - "building blocks for cognitive category theory" (active inference + ACT + enacted cognition) - [bmorphism/awesome-applied-category-theory](https://github.com/bmorphism/awesome-applied-category-theory) - ACT community resources ## What Are ACSets? ACSets ("attributed C-sets") are a family of data structures generalizing both **graphs** and **data frames**. They are an efficient in-memory implementation of a category-theoretic formalism for relational databases. **C-set** = Functor `X: C → Set` where C is a small category (schema) ``` ┌─────────────────────────────────────────────────────────────┐ │ Schema (Small Category C) │ │ ┌─────┐ src ┌─────┐ │ │ │ E │───────▶│ V │ │ │ │ │ tgt │ │ │ │ └──┬──┘───────▶└─────┘ │ │ │ │ │ │ A C-set X assigns: │ │ │ X(V) = set of vertices │ │ │ X(E) = set of edges │ │ │ X(src): X(E) → X(V) │ │ │ X(tgt): X(E) → X(V) │ └─────────────────────────────────────────────────────────────┘ ``` ## Core Concepts ### 1. Schema Definition ```julia using Catlab.CategoricalAlgebra @present SchGraph(FreeSchema) begin V::Ob E::Ob src::Hom(E,V) tgt::Hom(E,V) end @acset_type Graph(SchGraph, index=[:src,:tgt]) ``` ### 2. Symmetric Graphs (Undirected) ```julia @present SchSymmetricGraph <: SchGraph begin inv::Hom(E,E) compose(inv,src) == tgt compose(inv,tgt) == src compose(inv,inv) == id(E) end @acset_type SymmetricGraph(SchSymmetricGraph, index=[:src]) ``` ### 3. Attributed ACSets (with Data) ```julia @present SchWeightedGraph <: SchGraph begin Weight::AttrType weight::Attr(E, Weight) end @acset_type WeightedGraph(SchWeightedGraph, index=[:src,:tgt]){Float64} ``` ## GF(3) Conservation for ACSets Integrate with Music Topos 3-coloring: ```julia # Map ACSet parts to trits for GF(3) conservation function acset_to_trits(g::Graph, seed::UInt64) rng = SplitMix64(seed) trits = Int[] for e in parts(g, :E) h = next_u64!(rng) hue = (h >> 16 & 0xffff) / 65535.0 * 360 trit = hue < 60 || hue >= 300 ? 1 : hue < 180 ? 0 : -1 push!(trits, trit) end trits end # Verify conservation: sum(trits) ≡ 0 (mod 3) function gf3_conserved(trits) sum(trits) % 3 == 0 end ``` ## Gay.jl Color Bindings for @acset_colim (PR #990) Since Catlab PR #990, `@acset_colim` exposes name→part bindings. Combine with Gay.jl for: ### Named Part Coloring ```julia using Gay # Build ACSet with named parts result, bindings = @acset_colim SchGraph begin e::E v1::V; v2::V src(e) == v1 tgt(e) == v2 end # Color each named part deterministically seed = 0x114514 colors = Dict{Symbol, String}() for (name, (ob, idx)) in bindings colors[name] = Gay.color_at(seed, idx) # deterministic hex end # => Dict(:e => "#A855F7", :v1 => "#3B82F6", :v2 => "#10B981") ``` ### Two Modalities for XOR Validation **Modality 1: Different seeds (parallel verification)** ```julia seeds = [0x1, 0x2, 0x3] # Three independent streams colors_per_seed = [Gay.palette(s, length(bindings)) for s in seeds] # XOR guarantee: if all three agree on structure, computation is stable # Divergence → indicates floating-point or algorithmic instability ``` **Modality 2: Same seed, staggered indices (convergence test)** ```julia seed = 0x114514 # Run same computation 3 times, color at indices 1, 2, 3 c1 = compute_and_color(data, seed, index=1) c2 = compute_and_color(data, seed, index=2) c3 = compute_and_color(data, seed, index=3) # Convergence: c1 == c2 == c3 within bounded iterations → stable # Divergence: colors differ → numerical instability detected automatically ``` ### Empty Block = Initial Object = Neutral Color ```julia # Empty block produces initial object (fixed in PR #990) init, _ = @acset_colim SchGraph begin end # ∅ # Initial object gets neutral/zero color (the "0" in GF(3)) neutral_color = Gay.color_at(seed, 0) # or special "initial" marker ``` ### Bidirectional Index with Color Tags ```julia struct ColoredACSet{T} acset::T bindings::Dict{Symbol, Tuple{Symbol, Int}} colors::Dict{Symbol, String} seed::UInt64 end function colored_acset_colim(schema, seed, block) acset, bindings = @acset_colim schema block colors = Dict(name => Gay.color_at(seed, idx) for (name, (_, idx)) in bindings) ColoredACSet(acset, bindings, colors, seed) end # Lookup by name → part index → color (all directions) # name → color: ca.colors[:v1] # color → name: findfirst(==(hex), ca.colors) # name → part: ca.bindings[:v1][2] ``` ### Instability Detection Pattern ```julia function detect_instability(f, input, seed; tolerance=3) """ Run f three times with same seed at staggered indices. If colors diverge beyond tolerance, flag instability. """ results = [f(input) for _ in 1:3] colors = [Gay.color_at(seed, i) for i in 1:3] # Compare results - if they should be identical but aren't, # the divergent colors make the instability visually obvious for i in 1:3, j in i+1:3 if results[i] ≠ results[j] @warn "Instability detected" color_i=colors[i] color_j=colors[j] return false end end true end ``` This integrates the semantic naming from PR #990 with Gay.jl's deterministic coloring to create self-validating, visually debuggable ACSet constructions. ## Specter-Style Bidirectional Navigation Inspired by Nathan Marz's Specter library, navigate ACSets with paths that work for both **select** AND **transform**. ### The Key Insight: comp-navs = alloc + field sets From Marz: **"comp-navs is fast because it's just object allocation + field sets"** ```julia # What comp_navs actually does: comp_navs(a, b, c) = ComposedNav([a, b, c]) # That's it! # No compilation, no interpretation, no optimization # Just: allocate struct, set field, done # All work happens at traversal via CPS: nav_select(nav1, data, r1 -> nav_select(nav2, r1, r2 -> nav_select(nav3, r2, identity))) ``` This means: - **O(1) composition** - constant time path building - **Inline caching** - paths compiled once per callsite - **Near-hand-written speed** - CPS eliminates intermediate allocations ### ACSet Navigators ```julia using SpecterACSet # Navigate morphism values acset_field(:E, :src) # All source vertex IDs acset_field(:E, :tgt) # All target vertex IDs # Filter parts by predicate acset_where(:E, :src, ==(1)) # Edges where src == 1 # Navigate all parts of an object acset_parts(:V) # All vertex IDs acset_parts(:E) # All edge IDs ``` ### Bidirectional Example ```julia g = @acset Graph begin V=4; E=3; src=[1,2,3]; tgt=[2,3,4] end # Select: get all source vertices select([acset_field(:E, :src)], g) # → [1, 2, 3] # Transform: shift all targets (same path!) g2 = transform([acset_field(:E, :tgt)], t -> mod1(t+1, 4), g) select([acset_field(:E, :tgt)], g2) # → [3, 4, 1] ``` ### Cross-Domain Bridge (Sexp ↔ ACSet) ```julia # ACSet → Sexp → Navigate → Transform → Sexp → ACSet sexp = sexp_of_acset(g) # Navigate sexp to find all morphism names morphism_names = select([SEXP_CHILDREN, sexp_nth(1), ATOM_VALUE], sexp) # Roundtrip back to ACSet g2 = acset_of_sexp(Graph, sexp) ``` ## Higher-Order Functions on ACSets From Issue #7, implement functional patterns: | Function | Description | Example | |----------|-------------|---------| | `map` | Transform parts | `map(g, :E) do e; ... end` | | `filter` | Select parts by predicate | `filter(g, :V) { |v| degree(g,v) > 2 }` | | `fold` | Aggregate over parts | `fold(+, g, :E, :weight)` | ## Open ACSets (Composable Interfaces) ```julia # From Issue #89: Open versions of InterType ACSets using ACSets.OpenACSetTypes # Create open ACSet with exposed ports @open_acset_type OpenGraph(SchGraph, [:V]) # Compose via pushout g1 = OpenGraph(...) # ports: v1, v2 g2 = OpenGraph(...) # ports: v3, v4 g_composed = compose(g1, g2, [:v2 => :v3]) ``` ## Why Simple Graphs Are Badly Behaved The category of simple graphs does not even have a terminal object. Under the standard definition (symmetric, irreflexive edge relation), there's no "universal" graph that every other graph maps to uniquely. This reveals hidden assumptions in the simple graph model. **Simple graph**: G = (V, E) where E is a binary relation on V that is: - Symmetric: E(v,u) whenever E(u,v) - Irreflexive: E(v,v) for *no* vertex v **Category theorist's graph**: G consists of: - Vertex set G(V) - Edge set G(E) - Functions G(src), G(tgt): G(E) → G(V) This allows: - Multiple edges between vertices (multigraph) - Self-loops - Edges as first-class citizens with identity ## C-Sets: The Mathematical Foundation A **C-set** is a functor X: C → Set where C is a small category (schema). ``` Schema C (small category) C-set X (functor C → Set) ────────────────────────── ───────────────────────── Objects c ∈ C ────▶ Sets X(c) Morphisms f: c → d ────▶ Functions X(f): X(c) → X(d) ``` ### Terminology | Term | Definition | |------|------------| | **C-set** | Functor C → Set (copresheaf) | | **Presheaf** | Functor C^op → Set (contravariant) | | **Category action** | C-set generalizes G-set (group action) | ### The Schema for Graphs The schema Sch(Graph) is the category with: - Two objects: E, V - Two non-identity morphisms: src: E → V, tgt: E → V ``` ┌───┐ src ┌───┐ │ E │───────▶│ V │ │ │ tgt │ │ └───┘───────▶└───┘ ``` A graph G is a Sch(Graph)-set, meaning: - G(V) = set of vertices - G(E) = set of edges - G(src): G(E) → G(V) assigns source vertex to each edge - G(tgt): G(E) → G(V) assigns target vertex to each edge ## Creating Graphs in Catlab ```julia using Catlab.CategoricalAlgebra using Catlab.Graphs, Catlab.Graphics # Create empty graph and add parts g = Graph() add_parts!(g, :V, 3) add_parts!(g, :E, 4, src=[1,2,2,3], tgt=[2,3,3,3]) # Query incident edges (uses index) incident(g, 3, :tgt) # => [2, 3, 4] # Graphs.jl-style convenience interface g2 = Graph() add_vertices!(g2, 3) add_edges!(g2, [1,2,2,3], [2,3,3,3]) # Visualization to_graphviz(g, node_labels=true, edge_labels=true) ``` ### Indexing for Efficient Queries The `index=[:src,:tgt]` parameter creates inverse lookups: ```julia @acset_type Graph(SchGraph, index=[:src,:tgt]) # Without index: O(|E|) to find edges incident to vertex # With index: O(k) where k = number of incident edges ``` ## Symmetric Graphs (Undirected) The schema for symmetric graphs extends the graph schema with an involution: ``` ┌───┐ src ┌───┐ │ E │───────▶│ V │ │ │ tgt │ │ │ │───────▶│ │ │ │ inv │ │ │ │◀──────▶│ │ └───┘ └───┘ ``` Subject to equations: ``` inv ⨟ src = tgt inv ⨟ tgt = src inv² = id_E ``` ### Meaning of Equations For every edge e ∈ G(E): - `e.inv.src = e.tgt` (inverted edge starts where original ends) - `e.inv.tgt = e.src` (inverted edge ends where original starts) - `e.inv.inv = e` (involution is self-inverse) ```julia
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen