PositionBindingEncoder, NGramEncoder, TrajectoryEncoder
These encoders handle ordered sequences of items, preserving positional or contextual information.
PositionBindingEncoder
Overview
| Property | Value |
|---|---|
| Input | List of hypervectors |
| Output | Single hypervector |
| Preserves | Absolute position |
| Best For | Fixed-length sequences |
How It Works
Binds each item with its position vector:
\[\text{seq} = \sum_i \text{bind}(\text{item}_i, \rho^i(\text{pos}))\]
where ρⁱ is the i-th permutation of a base position vector.
Code Example
from holovec import VSA
from holovec.encoders import PositionBindingEncoder
model = VSA.create('FHRR', dim=2048)
encoder = PositionBindingEncoder(model, max_length=10)
# Create item vectors
items = {
"cat": model.random(seed=1),
"sat": model.random(seed=2),
"mat": model.random(seed=3)
}
# Encode sequences
seq1 = encoder.encode([items["cat"], items["sat"], items["mat"]])
seq2 = encoder.encode([items["mat"], items["sat"], items["cat"]])
# Different order → different encoding
print(model.similarity(seq1, seq2)) # ~0.3 (some overlap from "sat" in middle)
Querying by Position
# Query: What's at position 0?
pos_0 = encoder.get_position_vector(0)
query_result = model.unbind(seq1, pos_0)
# Should be similar to "cat"
print(model.similarity(query_result, items["cat"])) # High
print(model.similarity(query_result, items["mat"])) # Low
NGramEncoder
Overview
| Property | Value |
|---|---|
| Input | Sequence of items |
| Output | Single hypervector |
| Preserves | Local context |
| Best For | Text, variable-length |
How It Works
Creates and bundles all n-grams:
For sequence [A, B, C, D] with n=2: - bigrams: [A,B], [B,C], [C,D] - Each bigram: bind(A, permute(B)) - Result: bundle all bigrams
Code Example
from holovec import VSA
from holovec.encoders import NGramEncoder
model = VSA.create('FHRR', dim=2048)
encoder = NGramEncoder(model, n=3) # trigrams
# Character-level encoding
def text_to_vectors(text, model):
return [model.random(seed=ord(c)) for c in text]
# Encode words
cat_vecs = text_to_vectors("cat", model)
car_vecs = text_to_vectors("car", model)
dog_vecs = text_to_vectors("dog", model)
v_cat = encoder.encode(cat_vecs)
v_car = encoder.encode(car_vecs)
v_dog = encoder.encode(dog_vecs)
# Similar prefixes → some similarity
print(model.similarity(v_cat, v_car)) # Moderate (share "ca")
print(model.similarity(v_cat, v_dog)) # Low (different)
Configurable N
# Compare different n values
for n in [2, 3, 4]:
enc = NGramEncoder(model, n=n)
v1 = enc.encode(text_to_vectors("hello", model))
v2 = enc.encode(text_to_vectors("hella", model))
sim = model.similarity(v1, v2)
print(f"n={n}: sim('hello', 'hella') = {sim:.3f}")
TrajectoryEncoder
Overview
| Property | Value |
|---|---|
| Input | Sequence of encoded points |
| Output | Single hypervector |
| Preserves | Temporal order |
| Best For | Paths, time series |
How It Works
Uses sequential binding to encode trajectory:
\[\text{traj} = ((...((p_0 \otimes p_1) \otimes p_2)...) \otimes p_n)\]
or permutation-based:
\[\text{traj} = \sum_i \text{bind}(p_i, \rho^i(\text{time}))\]
Code Example
from holovec import VSA
from holovec.encoders import TrajectoryEncoder, FractionalPowerEncoder
model = VSA.create('FHRR', dim=2048)
# Position encoder
pos_enc = FractionalPowerEncoder(model, min_val=0, max_val=100)
# Trajectory encoder
traj_enc = TrajectoryEncoder(model)
# Encode a path: [10, 20, 30, 40, 50]
path1 = [pos_enc.encode(x) for x in [10, 20, 30, 40, 50]]
path2 = [pos_enc.encode(x) for x in [10, 20, 30, 40, 55]] # Similar end
path3 = [pos_enc.encode(x) for x in [50, 40, 30, 20, 10]] # Reversed
t1 = traj_enc.encode(path1)
t2 = traj_enc.encode(path2)
t3 = traj_enc.encode(path3)
# Similar paths → similar encodings
print(model.similarity(t1, t2)) # High
print(model.similarity(t1, t3)) # Low (reversed)
Time Series Encoding
# Encode sensor readings over time
def encode_timeseries(readings, model, traj_enc, value_enc):
points = [value_enc.encode(r) for r in readings]
return traj_enc.encode(points)
# Similar patterns → similar encodings
pattern1 = [1, 2, 3, 4, 5]
pattern2 = [1, 2, 3, 4, 6] # Slight difference at end
pattern3 = [5, 4, 3, 2, 1] # Reversed
value_enc = FractionalPowerEncoder(model, min_val=0, max_val=10)
t1 = encode_timeseries(pattern1, model, traj_enc, value_enc)
t2 = encode_timeseries(pattern2, model, traj_enc, value_enc)
t3 = encode_timeseries(pattern3, model, traj_enc, value_enc)
Choosing a Sequence Encoder
| Scenario | Use |
|---|---|
| Fixed-length, query by position | PositionBindingEncoder |
| Variable-length text | NGramEncoder |
| Temporal order matters | TrajectoryEncoder |
| Need subsequence matching | NGramEncoder |
Comparison
| Property | Position | NGram | Trajectory |
|---|---|---|---|
| Query by position | Yes | No | Partial |
| Variable length | Yes | Yes | Yes |
| Order sensitivity | Strong | Local | Sequential |
| Reversal detection | Yes | Partial | Yes |
References
- Plate, T. A. (2003). Holographic Reduced Representations
- Kanerva, P. (1996). Binary Spatter-Coding of Ordered K-Tuples
See Also
- Encoders Overview — All encoders
- Encoder-FractionalPower — For encoding sequence elements
- Patterns — Sequence encoding patterns