holo-four-axis

A single memory store parameterized on four orthogonal axes.

Every previous tool in this series fixed one behavior. This tool exposes the substrate's design space. Four parameters, four behaviors, one class:

Axis Parameter What it controls
1 d (dimension) Capacity β€” how many items fit
2 kind (per region) Algebra β€” item, anti, or chain
3 threshold Readout confidence gate
4 consolidation_rate Forgetting behavior

Regions of different kinds can share one store. The same primitives, the same dimension, three different address shapes.

What it does

from holo_four_axis import FourAxisMemory

mem = FourAxisMemory(d=2048, threshold=0.05, consolidation_rate=0.3)

# Three regions, three algebras
mem.add_region("items",  kind="item")
mem.add_region("claims", kind="anti")
mem.add_region("events", kind="chain")

# Item region: presence / absence
mem.store("items", "apple")
mem.query("items", "apple")   # YES
mem.query("items", "pear")    # UNKNOWN

# Anti region: presence / denial / contradiction
mem.store("claims", "earth_round")
mem.deny("claims", "earth_flat")
mem.store("claims", "water_is_wet")
mem.deny("claims", "water_is_wet")
mem.query("claims", "earth_round")   # YES
mem.query("claims", "earth_flat")    # NO
mem.query("claims", "water_is_wet")  # AMBIGUOUS

# Chain region: ordered traversal
mem.chain("events", ["wake", "coffee", "work", "lunch", "sleep"])
mem.walk("events", "wake")
# ['wake', 'coffee', 'work', 'lunch', 'sleep']

Why four axes

Prior work in this series measured each axis separately. This tool demonstrates that they are orthogonal. Changing d changes capacity but not address shape. Changing kind changes address shape but not capacity. Changing threshold changes the confidence gate but not what is stored. Changing consolidation_rate changes retention but not the other three.

The design intent is a memory whose behavior is fully specified by its profile. A working memory is d=512, threshold=0.10, consolidation_rate=0.8. A long-term store is d=2048, threshold=0.04, consolidation_rate=0.0. A sequence memory is d=2048, threshold=0.08, consolidation_rate=0.3 with a chain region. Same code, different profile, different behavior.

Installation

pip install numpy        # required
pip install matplotlib   # optional, for the region plot

Usage

Python

from holo_four_axis import FourAxisMemory

mem = FourAxisMemory(
    d=2048,
    kind="item",
    threshold=0.05,
    consolidation_rate=0.3,
    anchor_quantile=0.8,
)

mem.add_region("items",  kind="item")
mem.add_region("claims", kind="anti")
mem.add_region("events", kind="chain")

# Store with weights
mem.store("items", "fact_a", weight=1.0)
mem.store("items", "fact_b", weight=0.5)

# Consolidate β€” non-anchors decay, anchors preserved
mem.consolidate(cycles=5)

# Save / load
mem.save_json("state.json")
mem.plot("regions.png")

CLI

python holo_four_axis.py
python holo_four_axis.py --output results/

Runs five demonstrations (one per axis plus a combined profile test) and writes the region plot.

Results

All results at D=2048 unless noted. Self-test verifies the three primitives (bind/unbind, directed bind/unbind, raw projection).

Axis 1 β€” Dimension

Store K items, query each, count correct.

D K=20 K=50 K=100 K=200
512 20/20 50/50 95/100 149/200
1024 20/20 50/50 99/100 167/200
2048 20/20 50/50 100/100 174/200

The capacity ceiling scales sublinearly with D, matching the 0.907 exponent measured in earlier work.

Axis 2 β€” Algebra

Three regions, same D, three different address shapes.

Item region:

Query Verdict Similarity
apple YES +0.573
orange YES +0.565
banana YES +0.582
pear UNKNOWN +0.000

Anti region:

Query Verdict raw_yes raw_no
earth_round YES +0.999 +0.006
earth_flat NO +0.012 +1.005
water_is_wet AMBIGUOUS +0.999 +1.005
sun_orbits_earth UNKNOWN +0.000 +0.000

Chain region:

wake -> coffee -> work -> lunch -> afternoon -> dinner -> sleep

Same primitives. Same code path. Different algebra. Three behaviors.

Axis 3 β€” Threshold

Store 30 items. Query stored keys and unseen keys. Sweep the readout threshold to trade acceptance of known items against rejection of unseen ones.

Threshold Known accepted Unseen rejected
0.05 30/30 30/30
0.10 30/30 30/30
0.20 30/30 30/30
0.40 30/30 30/30
0.60 0/30 30/30

With raw-projection readout, stored items are read back at their stored weight (~1.0) and unseen items read at near zero. The threshold has a wide plateau where both quantities are perfect. Above the plateau, stored items are rejected. Below, unseen items are accepted. The user picks the operating point.

Axis 4 β€” Consolidation

Store 50 items with weights drawn uniformly from [0.3, 1.0]. Run 5 consolidation cycles. Anchors (top 20% by weight) are preserved; non-anchors decay by consolidation_rate per cycle.

Rate Magnitude before Magnitude after Retrievable
0.0 4.88 4.88 48/50
0.2 4.88 3.25 34/50
0.5 4.88 2.99 10/50
0.8 4.88 2.99 10/50

At rate=0.0 nothing decays. At rate=0.2 the store loses ~30% of retrievable items over 5 cycles. At rate=0.5 and 0.8 the store converges to the anchor set (10 items) β€” aggressive decay flushes everything below the anchor quantile, and further decay does not remove anchors. The floor at 10 is a feature, not a bug.

Combined β€” Four profiles

Same code, four parameter profiles:

Profile d threshold rate Behavior
working 512 0.10 0.8 fast-shallow, most items decay
longterm 2048 0.04 0.0 wide-persistent, nothing decays
seqmem 2048 0.08 0.3 medium persistence, chain walk works
contra 2048 0.05 0.1 contradiction-aware, AMBIGUOUS verdict

Each profile produces measurably different behavior. The four-axis structure is what makes this possible with one class.

API reference

FourAxisMemory

FourAxisMemory(
    d=2048,
    kind="item",
    threshold=0.05,
    consolidation_rate=0.0,
    anchor_quantile=0.8,
    seed=0,
)

Region management

  • add_region(name, kind) β€” kind is "item", "anti", or "chain".
  • _region(name) -> Region β€” direct access (private).

Storage

  • store(region, label, weight=1.0) β€” add an item (or a chain node).
  • deny(region, label, weight=1.0) β€” add a denial (anti regions only).
  • store_many(region, labels, weight=1.0)
  • chain(region, labels, weight=1.0) β€” add a chain of linked nodes.

Query

  • query(region, label) -> dict β€” verdict, similarity, raw_yes, raw_no.
  • query_next(region, label) -> label | None β€” successor in a chain.
  • query_prev(region, label) -> label | None β€” predecessor.
  • walk(region, start, max_steps=20) -> [label] β€” full walk.

Maintenance

  • consolidate(cycles=1) -> dict β€” decay non-anchors.
  • save_json(path) β€” full state.
  • plot(path) β€” region plot.
  • stats() -> dict β€” per-region diagnostics.

Query result

{
    "region": str,
    "label": str,
    "verdict": "YES" | "NO" | "UNKNOWN" | "AMBIGUOUS",
    "similarity": float,     # max similarity in the region's readout
    "confidence": float,     # magnitude of the winning signal
    "raw_yes": float,        # positive-trace projection
    "raw_no": float,         # negative-trace projection (anti only)
}

Design notes

Region-based algebra

Each region declares its kind at creation. The store routes operations accordingly. An item region accepts store and answers query. An anti region accepts store, deny, and answers with four verdicts. A chain region accepts chain and answers query_next, query_prev, and walk. A single FourAxisMemory object can hold all three kinds simultaneously, sharing the same D.

Raw projection vs cosine

Item and anti regions use project for the readout, not cos. The difference: cos(w * v, v) = 1.0 for any positive w, while project(w * v, v) = w. The item region needs to distinguish an item stored with weight 0.3 from one stored with weight 1.0, so it needs raw projection. The same is true for the anti region's four-way verdict.

Anchor preservation

Consolidation decays everything below the anchor_quantile (default 0.8 β€” top 20% by weight). The anchors do not decay. This produces the floor at 10 items in the Axis 4 sweep: aggressive decay flushes the non-anchors, but the top 10 items remain retrievable regardless of decay rate. The floor is the anchor set.

Limitations

No cross-region queries. A query goes to one region. Asking "is this item in any region" requires calling query per region.

No provenance. Weights accumulate. The store does not track which call produced which weight.

No automatic re-anchoring. The anchor set is fixed at the first consolidation call. If a non-anchor becomes important later, it will be decayed unless the user re-stores it or adjusts the quantile.

Consolidation is destructive. Decay subtracts from the trace and multiplies the stored weight by (1 - rate). There is no undo. Save state before consolidating if you might want to reverse it.

Chain regions have no weights. Chain nodes are stored with weight_yes[label] = 1.0 regardless of the weight argument to chain. Weighted chains are not implemented.

No deletion of individual chain edges. Removing one edge requires subtracting it from the trace manually. No helper is provided.

Citation

@misc{holo-four-axis2026,
  title  = {holo-four-axis: A memory store parameterized on four
            orthogonal axes},
  author = {zeechimp},
  year   = {2026},
  note   = {Item, anti, and chain regions share one substrate.
            Capacity, algebra, threshold, and consolidation are
            independent parameters.}
}

References

  • Plate, T. A. "Holographic Reduced Representations." IEEE Transactions on Neural Networks 6:3 (1995), 623–641.
  • Kanerva, P. "Hyperdimensional Computing." Cognitive Computation 1:2 (2009), 139–159.
  • Gayler, R. W. "Vector Symbolic Architectures Answer Jackendoff's Challenges." ICCS/ASCS (2003).

License

Apache 2.0

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support