Tagma Structural Indexing

Multi-axis coordinate index for the FIH storage engine

Author
Affiliation

SSCCS Foundation

Published

August 3, 2026

Abstract

The Tagma coordinate space replaces the multi-HashMap index layer (FihCoord) with a direct-address N-tuple identity, eliminating join explosion on multi-axis queries. The storage IO layer, record format, and external FihHash interface remain unchanged. The integration is a targeted 30-line addition to the index layer with zero changes to the surrounding architecture.

Other Formats

Motivation

The FIH storage engine’s index layer (FihCoord) stores six independent axis indexes as individual HashMaps:

  • by_origin, by_creator, by_status, by_semantic, by_time, by_fact

Each axis is queryable independently, but multi-axis queries (e.g., “all facts by creator X in time range Y with origin Z”) require manual Vec intersection at the call site:

let by_origin = coord.by_origin("document:nexus");
let by_creator = coord.by_creator("ingestion-agent");
let by_time = coord.by_time(t0..t1);
let result: Vec = by_origin.iter()
    .filter(|id| by_creator.contains(id))
    .filter(|id| by_time.contains(id))
    .collect();

As the number of axes grows, intersection cost explodes. RoaringBitmap, QueryPlanner, and selectivity-based ordering are mitigations for a fundamental problem: identity and index are separate data structures.

Tagma solves this by making the identity itself a coordinate in N-dimensional space. A multi-axis query becomes a single CoordPath construction followed by an O(N) array traversal (where N is path depth, unrelated to dataset size).

Design Principle

The integration follows a strict principle: external interfaces remain unchanged. The storage IO layer, record types, serialization format, and FihHash identity type are never modified. Tagma is introduced as a drop-in replacement for the multi-axis index only.

Layer Changed Rationale
Storage IO No IO is the architectural boundary; untouched
Record types (Fact, Intent, Hint) No Same fields, same serialization
FihHash ([u8; 32]) No External identity contract preserved
FihCoord multi-axis HashMap Yes Replaced with CoordSpaceN index
by_fact reverse index No Tagma cannot express reference graphs
Time index (BTreeMap) No Continuous time vs discrete coordinates

Architecture

BoardState (unchanged)
  │
  ├── Record Layer (unchanged)
  │   ├── facts:  Vec<Fact>
  │   ├── intents: Vec<Intent>
  │   └── hints:  Vec<Hint>
  │
  ├── Index Layer (Tagma replaces FihCoord)
  │   ├── axis_index: CoordSpaceN<3, FihHash>
  │   │     origin × creator × type → FihHash (single CoordPath lookup)
  │   ├── by_fact: HashMap<FihHash, Vec<FihHash>> (unchanged)
  │   └── time_index: BTreeMap<u64, FihHash> (unchanged)
  │
  └── FihCoord (deprecated, legacy compat shim)

The CoordSpaceN<3, FihHash> maps a 3-axis coordinate (origin, creator, type) directly to a FihHash. No HashMap lookup, no intersection, no allocation.

Recursive Coordinate Composition

A Tagma Coord is a 16-bit value defined by a 3-axis composition formula with ranges Axis 0 (19), Axis 1 (21), and Axis 2 (28), yielding 11,172 valid combinations total.1 Multiple Coord values compose into a CoordPath of arbitrary length for N-dimensional addressing.

The critical design property is that each dimension (origin, creator, type) occupies one Coord position in the path, NOT one axis within a Coord:

FIH identity = CoordPath (variable length)

Example 3D (origin × creator × type):
  CoordPath<3> = [origin_coord, creator_coord, type_coord]

Each coord.index() = 0..11172 (NOT 0..18)
Each dimension = 11,172 values, NOT 19

This avoids the flat mapping fallacy of assigning initial=origin(19), medial=creator(21), final=type(28). The 3-axis decomposition of an individual Coord is an internal encoding detail used only for Hamming distance and human readability. The storage and query hot path uses only coord.index() which is a flat 0..11172 value.

For tens of millions of entries, a 2-Coord CoordSpaceN<2, FihHash> with batch allocation (first coord = allocation batch, second coord = slot within batch) provides 124,813,584 addressable slots while keeping memory under control:

Entries Batches Leaf arrays Index memory
100,000 9 9 ~3.2 MB
1,000,000 90 90 ~32 MB
10,000,000 895 895 ~320 MB

The leaf array allocation is the dominant cost: each populated batch allocates a fixed 11,172-slot [Option<FihHash>; 11172] array (approximately 357 KB). For practical FIH workloads with 1-10 million entries, this is acceptable.

Performance

Index-level comparison

The following table compares the current FihCoord (multi-HashMap) approach against Tagma CoordSpaceN for the same query patterns. Results are from the syntagma Criterion benchmark suite on ARMv8.4-A Firestorm.

Query pattern Example Current (FihCoord) Tagma (CoordSpaceN) Ratio
Single-axis “creator=Y” O(1) HashMap lookup O(domain) 11K scan HashMap faster, kept
2-axis selective “origin=X & creator=Y” Vec intersection O(KxN) O(2) path lookup ~1000x
3-axis selective “origin=X & creator=Y & type=Z” 3 Vec ops + 2 intersections O(3) path lookup ~1000-3000x
Existence check “does this 3-axis point exist?” HashMap + Vec fetch O(3) path lookup, None possible ~1Mx (nonexist)
Nonexistent check “does NOT exist” Vec scan until found O(3) None return 14.0Mx
Single CoordSpace get baseline N/A 0.82 ns baseline
CoordSpaceN<2> get 2-Coord N/A ~1.5 ns (est) baseline

The nonexist column requires explanation and is detailed below.

Identity generation

Tagma CoordPath generation replaces SHA256-based FihHash creation in new code paths. The identity size matches the address space requirements rather than being fixed at 32 bytes.

Method N-Coord Address space Latency (ns/op) Size (bytes) Speedup vs SHA256
SHA256 N/A 2^256 227 32 1x
Tagma 1-Coord 1 1.12e4 0.38 2 597x
Tagma 2-Coord 2 1.25e8 1.1 4 206x
Tagma 6-Coord 6 1.94e24 3.7 12 61x
Tagma 19-Coord 19 ~2^256 54.9 38 4.1x

For FIH identity generation, a 6-Coord Tagma CoordPath (12 bytes, 1.94e24 address space, ~3.7 ns) provides UUID-scale collision-free identity at 61x the speed of SHA256.

The nonexist asymmetry

The decisive advantage of Tagma indexing is not in existing-entity queries but in nonexistent-entity queries. This is the benchmark result that requires careful explanation because it is counterintuitive.

Current FihCoord approach:

A by_origin.get("X") returns a HashMap lookup of Vec<u32> in O(1). That is fast. But a query of “origin=X AND creator=Y AND type=Z” requires:

let by_o = coord.by_origin("X");    // O(1), returns Vec
let by_c = coord.by_creator("Y");   // O(1), returns Vec
let by_t = coord.by_type("Z");      // O(1), returns Vec

// Intersection: at minimum, iterate smallest Vec and check HashSet
let smallest = [&by_o, &by_c, &by_t].iter().min_by_key(|v| v.len());
let others: HashSet<u32> = ...;

let result: Vec = smallest.iter()
    .filter(|id| others.contains(id))
    .collect();                     // O(K) where K = smallest Vec size

If the smallest Vec has 1000 elements and the intersection is empty, the query still iterates 1000 elements and performs 1000 HashSet lookups before returning None.

Tagma CoordSpaceN approach:

let path = CoordPath::new([origin_idx, creator_idx, type_idx]);
let result = axis_index.at_path(&path);  // O(3), returns Option<&FihHash>

If the 3-axis point does not exist, the third array access returns None and the query terminates immediately. No Vec iteration, no allocation, no HashSet.

This asymmetry is structural. The HashMap-based index can answer “what exists at this single axis” in O(1), but answering “what exists at this combination of N axes” requires materializing each axis Vec and computing their intersection. The cost grows with axis cardinality and is paid regardless of whether the result is empty.

Tagma’s direct-address structure answers the N-axis question at the same cost as a single-axis question: N array accesses. The result is None at the same cost as Some. This is the source of the 14.0Mx benchmark result for nonexistent prefix queries.

ev (ExaVerif) synergy

An incidental but structurally significant finding: the ev verification tool already imports tagma_core::{Coord, CoordPath} and uses coords_to_path to bridge instruction field combinations into Tagma coordinate space. The RISC-V instruction verification state space is structurally identical to the FIH coordinate space. Both are N-dimensional state spaces indexed by coordinate composition, and both benefit from O(1) direct addressing versus enumeration-based approaches.

Implementation

Phase 1a: Multi-axis index (30 lines)

Add CoordSpaceN<3, FihHash> as a field in FihCoord. Route multi-axis queries through the Tagma path. Single-axis queries continue through the existing HashMap indexes for backward compatibility.

// In FihCoord:
pub struct FihCoord {
    // Legacy single-axis indexes (kept for backward compat)
    by_origin: HashMap<String, Vec<u32>>,
    by_creator: HashMap<String, Vec<u32>>,
    by_status: HashMap<String, Vec<u32>>,
    by_semantic: BTreeMap<String, Vec<u32>>,

    // Tagma multi-axis index (added)
    axis_index: CoordSpaceN<3, FihHash>,

    // Unchanged
    by_fact: HashMap<u32, Vec<u32>>,
    time_index: BTreeMap<u64, u32>,
    // ...
}

Phase 1b: TagmaId identity (20 lines)

Add an optional TagmaId field alongside FihHash in new record creation. New scopes can opt into Tagma-based identity without affecting existing scopes.

pub struct Fact {
    pub id: FihHash,                    // unchanged, external
    pub tagma_id: Option<CoordPath<6>>, // added, internal
    // ...
}

Phase 2: New scope migration

New nex-tagma scopes use Tagma CoordPath as the primary identity. Existing scopes continue with FihHash. The storage layer is trait-parameterized.

Phase 3: FihHash deprecation for query-dominated workloads

Scopes with heavy multi-axis queries migrate fully to Tagma-based addressing. FihHash remains for workloads requiring cryptographic preimage resistance.

Non-goals

  • Replacing cryptographic hashing. SHA256 remains for signatures, Merkle proofs, and verifiable provenance chains.
  • Removing FihHash entirely. Both identity systems coexist.
  • Modifying the storage IO layer. Tagma is an index-only addition.
  • Hardware verification of Tagma Coord. Requires a separate silicon track.
  • Time index replacement. BTreeMap serves continuous time values well.

Impact summary

Metric Before (FihCoord) After (Tagma index) Change
Multi-axis query Vec intersection O(KxN) CoordPath O(N) 1000x+
Nonexist detection Vec scan until empty O(N) None return 14.0Mx
Identity generation SHA256 227 ns CoordPath ~3.7 ns (6-Coord) 61x
Index memory (10M entries) ~128 MB (6 HashMap) ~320 MB (CS3 arrays) 2.5x more
Code change baseline +30 lines negligible
Storage IO baseline unchanged 0

The memory tradeoff (2.5x more for the index) is acceptable because the index is a fixed overhead per populated batch slot, not per entry. At 10M entries using batch allocation, approximately 895 leaf arrays consume ~320 MB. This is dwarfed by the record storage itself (~1.28 GB for 10M Fact records).

References

  • syntagma (github.com/ssccsorg/syntagma): Core Coord, CoordSpaceN, DynCoordSpace implementation. Criterion benchmark suite (51+ functions, 12 groups). White paper at tagma/docs/wp.qmd.
  • syntagma PR #23: CoordMap to CoordSpace rename, merged to main 2026-07-18.
  • nexus issue #151: Structural addressing epic.
  • nexus issue #150: nex-tagma PoC.
  • ev (github.com/ssccsorg/ev): ExaVerif verification tool, imports tagma_core for coordinate-based state space.

Footnotes

  1. The ranges 19, 21, and 28 derive from the Unicode block U+AC00–U+D7AF, which encodes the compositional writing system.↩︎