FractionalPowerEncoder
The Fractional Power Encoder (FPE) encodes continuous scalar values while preserving metric structure. Similar values produce similar hypervectors, enabling interpolation and generalization.
Overview
| Property | Value |
|---|---|
| Input | Scalar ∈ [min_val, max_val] |
| Output | Hypervector |
| Preserves | Metric/locality |
| Best Model | FHRR (native support) |
| Complexity | O(d) |
When to Use
- Continuous sensor readings (temperature, pressure)
- Numeric features for ML
- Values where interpolation matters
- When similar values should match
Theory
Fractional Power Encoding
For a base hypervector b and scalar value x:
\[\text{encode}(x) = b^x\]
For FHRR (complex phasors):
\[b^x_i = e^{i \cdot x \cdot \theta_i}\]
where θ_i is the base phase at dimension i.
Similarity Behavior
For values x and y:
\[\text{sim}(\text{encode}(x), \text{encode}(y)) \approx \text{sinc}(\pi \sigma |x - y|)\]
where σ is the bandwidth parameter.
Key properties: - sim(x, x) = 1.0 - Similarity decreases smoothly with distance - Rate of decrease controlled by bandwidth
Bandwidth Parameter
The bandwidth σ controls the encoding resolution:
| Bandwidth | Effect |
|---|---|
| Low (0.1) | Wide similarity kernel, smooth |
| Medium (1.0) | Balanced resolution |
| High (10.0) | Narrow kernel, precise |
Code Examples
Basic Usage
from holovec import VSA
from holovec.encoders import FractionalPowerEncoder
# Create model and encoder
model = VSA.create('FHRR', dim=2048)
encoder = FractionalPowerEncoder(
model,
min_val=0,
max_val=100,
bandwidth=1.0
)
# Encode values
v50 = encoder.encode(50)
v51 = encoder.encode(51)
v60 = encoder.encode(60)
v100 = encoder.encode(100)
# Check similarity
print(model.similarity(v50, v51)) # ~0.99 (very similar)
print(model.similarity(v50, v60)) # ~0.90 (similar)
print(model.similarity(v50, v100)) # ~0.50 (different)
Tuning Bandwidth
# Compare bandwidths
for bw in [0.5, 1.0, 2.0, 5.0]:
enc = FractionalPowerEncoder(model, min_val=0, max_val=100, bandwidth=bw)
v0 = enc.encode(0)
v10 = enc.encode(10)
sim = model.similarity(v0, v10)
print(f"bandwidth={bw}: sim(0, 10)={sim:.3f}")
Interpolation
# FPE naturally supports interpolation
v25 = encoder.encode(25)
v75 = encoder.encode(75)
# Encode 50 directly
v50_direct = encoder.encode(50)
# "Interpolate" via bundling (approximate midpoint)
v50_interp = model.bundle([v25, v75])
# Should be similar (not identical due to bundling)
print(model.similarity(v50_direct, v50_interp)) # ~0.8
Multi-dimensional Values
# Encode (x, y) coordinates
x_base = model.random(seed=1)
y_base = model.random(seed=2)
x_enc = FractionalPowerEncoder(model, min_val=0, max_val=100, base=x_base)
y_enc = FractionalPowerEncoder(model, min_val=0, max_val=100, base=y_base)
def encode_point(x, y):
return model.bundle([x_enc.encode(x), y_enc.encode(y)])
# Nearby points are similar
p1 = encode_point(50, 50)
p2 = encode_point(51, 49)
p3 = encode_point(90, 10)
print(model.similarity(p1, p2)) # High (nearby)
print(model.similarity(p1, p3)) # Low (far)
Decoding
# Create decoder with resolution
values = list(range(0, 101, 5)) # [0, 5, 10, ..., 100]
codebook = {v: encoder.encode(v) for v in values}
def decode(vec):
best_val = None
best_sim = -1
for v, enc in codebook.items():
sim = model.similarity(vec, enc)
if sim > best_sim:
best_sim = sim
best_val = v
return best_val
# Test decoding
v47 = encoder.encode(47)
decoded = decode(v47)
print(f"Encoded 47, decoded to {decoded}") # 45 or 50 (nearest)
Use Cases
Sensor Fusion
# Temperature and humidity sensors
temp_enc = FractionalPowerEncoder(model, min_val=-20, max_val=50)
hum_enc = FractionalPowerEncoder(model, min_val=0, max_val=100)
TEMP_ROLE = model.random(seed=1)
HUM_ROLE = model.random(seed=2)
def encode_reading(temp, humidity):
return model.bundle([
model.bind(TEMP_ROLE, temp_enc.encode(temp)),
model.bind(HUM_ROLE, hum_enc.encode(humidity))
])
# Similar conditions → similar vectors
hot_humid = encode_reading(35, 80)
hot_dry = encode_reading(35, 20)
cold_dry = encode_reading(5, 20)
print(model.similarity(hot_humid, hot_dry)) # Moderate
print(model.similarity(hot_dry, cold_dry)) # Moderate
print(model.similarity(hot_humid, cold_dry)) # Low
Feature Scaling
# Automatic normalization via encoder range
features = [
(FractionalPowerEncoder(model, min_val=0, max_val=1e6), "price"),
(FractionalPowerEncoder(model, min_val=0, max_val=5), "rating"),
(FractionalPowerEncoder(model, min_val=1900, max_val=2024), "year"),
]
def encode_item(price, rating, year):
parts = []
for enc, name in features:
role = model.random(seed=hash(name) % 1000)
if name == "price":
parts.append(model.bind(role, enc.encode(price)))
elif name == "rating":
parts.append(model.bind(role, enc.encode(rating)))
else:
parts.append(model.bind(role, enc.encode(year)))
return model.bundle(parts)
Comparison with Other Encoders
| vs Encoder | FPE Advantage | FPE Disadvantage |
|---|---|---|
| Thermometer | Smoother similarity | Not for ordinals |
| Level | Continuous interpolation | Overkill for discrete |
| Random | Preserves locality | Requires FHRR for best results |
References
- Frady, E. P., et al. (2021). Computing on Functions Using Randomized Vector Representations
- Plate, T. A. (2003). Holographic Reduced Representations
See Also
- Encoders Overview — All encoders
- Model-FHRR — Best model for FPE
- Encoder-Thermometer-Level — For ordinal data