Skip to main content

Compositional HRR morpheme tokenizer/embeddings: circular-convolution prefix (x) root (x) suffix, for Apple MLX.

Project description


license: mit library_name: mlx tags:

  • hrr
  • vsa
  • holographic-reduced-representations
  • tokenizer
  • morphemes
  • mlx
  • compositional language: en pipeline_tag: feature-extraction

morph-hrr

A compositional morpheme tokenizer built on Holographic Reduced Representations (HRR), for Apple MLX.

Each word becomes one fixed-width dense vector by binding its morphemes (prefix ⊗ root ⊗ suffix) into a single "holistic" superposition. Because the binding is circular convolution with unitary role vectors, you can algebraically pull a morpheme back out: unbind(word_vec, prefix_role) recovers the prefix. Words that share morphology land near each other in vector space, and an out-of-vocabulary word built from its pieces still neighbors its root family.

What this is — and isn't

It is: an input representation / embedding layer for HRR-native or experimental models. It emits dense vectors, one per word, and exposes the HRR algebra (bind, unbind, bundle, make_unitary) behind them.

It is not: a HuggingFace text↔ID tokenizer. It does not produce token IDs, has no vocabulary, and will not drop into transformers. Think of it as an alternative to a learned embedding table — one whose vectors are compositional by construction rather than arbitrary.

It is not: a way to make a pretrained Qwen/Gemma smaller or faster. HRR input is a different representation than what pretrained weights expect; you cannot swap it into an existing model without retraining from scratch. (See Scope & honesty below.)

Install

pip install morph-hrr        # Apple Silicon (mlx is the only dependency)

Development:

git clone <repo> && cd morph-hrr
pip install -e ".[test]"
pytest -q

Quickstart

from morph_hrr import MorphemeTokenizer, unbind, cosine_similarity

tok = MorphemeTokenizer(dim=2048)        # deterministic given seed

# A word is one fixed-width vector, composed from its morphemes by role.
v = tok.word_vector("unhappy")           # mx.array, shape (2048,)
print(tok.segment("unhappy"))            # ('un', 'happy', '')

# Composition is algebraic: recover the prefix filler by unbinding its role.
recovered = unbind(v, tok.prefix_role)
print(cosine_similarity(recovered, tok.bytes_vector("un")))   # ~0.6 (vs ~0 control)

# Shared morphology => shared neighborhood.
print(cosine_similarity(tok.word_vector("unhappy"),
                        tok.word_vector("happy")))            # > 0.2 (vs ~0 unrelated)

# Encode a sentence: one float16 vector per word.
mat = tok.encode("the quick brown fox")   # mx.array, shape (4, 2048), float16

The math (one paragraph)

bind(a, b) is circular convolution, computed in the Fourier domain as real(IFFT(FFT(a) * FFT(b))). unbind(bind(a,b), b) is the convolution inverse, real(IFFT(FFT(bound) * conj(FFT(b)))). A unitary vector has all FFT magnitudes equal to 1, so binding with it is a perfect, lossless rotation — unbinding recovers the original to >0.99 cosine. A word vector bundles three role⊗filler pairs — prefix_role ⊗ bytes(prefix), root_role ⊗ bytes(root), suffix_role ⊗ bytes(suffix) — into one normalized superposition; unbinding a role pulls its filler back out of the superposition (the other two act as noise, which is why longer dims recover more cleanly).

Compositionality demo (regression-tested, dim=2048)

Property HRR cosine Random control Test
Prefix recovery (unbind(unhappy, prefix_role)bytes("un")) > 0.50 ≈ 0 test_prefix_recovery_beats_control
Shared-root clustering (unhappy ~ happy) > 0.20 ≈ 0 test_shared_root_clusters
Shared-suffix clustering (running ~ walking) > 0.15 ≈ 0 test_shared_suffix_clusters
OOV root family (unbind(unkind, root_role)bytes("kind")) > 0.50 ≈ 0 test_oov_root_family
Role vectors near-orthogonal < 0.10 test_roles_are_near_orthogonal

Each HRR measurement is paired against a random-vector control of the same dimension, asserting the structured signal is meaningfully stronger than noise.

Public API

Name What
MorphemeTokenizer(dim=2048, seed=0) Map text → HRR morpheme vectors
.segment(word) (prefix, root, suffix)
.word_vector(word) / .bytes_vector(text) Holistic / byte-fold word vector
.encode(text) / .iter_vectors(text) Stacked (n, dim) float16 / lazy yield
.prefix_role / .root_role / .suffix_role Exposed unitary role vectors (for unbind)
bind, unbind, bundle, normalize, make_unitary, cosine_similarity, update_context HRR primitives
segment, PREFIXES, SUFFIXES Standalone morphological segmentation

HolographicMorphemeTokenizer is kept as a backwards-compatible alias for MorphemeTokenizer.

Segmentation: deliberately simple

morphemes.segment is dependency-free, longest-match affix stripping over curated prefix/suffix lists, with a minimum-root guard. It is imperfect by design: it does not undo consonant doubling (running → runn + ing, not run + ing) and cannot tell a real root from a suffixable tail (preorder → pre + ord + er). No dictionary, no learned model, no nltk. This keeps the package lightweight; better segmentation is future work and is orthogonal to the HRR representation itself.

Scope & honesty

  • mlx-only. Apple Silicon target audience; a numpy/JAX backend is a documented future option, not this release.
  • Representation, not a drop-in tokenizer. No token IDs, no HF integration.
  • Can't retrofit pretrained models. Swapping HRR input into a real model throws away its pretrained weights — you must train from scratch. On consumer hardware (e.g. 16 GB) that means small research-scale models, not deployable ones. The value of this package is the compositional input representation and the HRR algebra, demonstrated on small models.
  • v0.1 ships the tokenizer + primitives + tests. A demo Space, a trained reference model, and a portable backend are future work.

License

MIT. See LICENSE.

Cite / priority

If you build on this representation in published work, please cite this repository. The compositional HRR morpheme representation (role⊗filler binding of prefix/root/suffix via circular convolution, with unitary roles enabling algebraic morpheme manipulation) is, to our knowledge, novel as a tokenization scheme; we'd appreciate attribution.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

morph_hrr-0.1.1.tar.gz (9.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

morph_hrr-0.1.1-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

Details for the file morph_hrr-0.1.1.tar.gz.

File metadata

  • Download URL: morph_hrr-0.1.1.tar.gz
  • Upload date:
  • Size: 9.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for morph_hrr-0.1.1.tar.gz
Algorithm Hash digest
SHA256 09402bd39a000c8ea49395cdb1dd035517c0fa63b1e766ffa603d9b6eeac38a6
MD5 9bb692a2d03cdac79b008e7d2f11aa0b
BLAKE2b-256 a574c83304735336c1280a9b2671132ce7528235f2d09ef8755609b75803f6f4

See more details on using hashes here.

File details

Details for the file morph_hrr-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: morph_hrr-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 9.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for morph_hrr-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4928b8ab3dd522a55a764b975513834207363cc8de21ca80f9af2bccb0d19bd4
MD5 4f0fd41efa29935d6f4deb7689b5c411
BLAKE2b-256 99a0ef7093acb5583315a0be34821bd63b35dd41f7a47bcc637868ff4b4d7fc1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page