Note
Go to the end to download the full example code.
Comprehensive demo of scalar encoders in holovec.¶
This script demonstrates: 1. FractionalPowerEncoder - Continuous scalar encoding with smooth similarity 2. ThermometerEncoder - Ordinal encoding with monotonic similarity 3. LevelEncoder - Discrete level encoding with exact recovery
Run with: python examples/demo_encoders.py
13 import numpy as np
14 from holovec import VSA
15 from holovec.encoders import (
16 FractionalPowerEncoder,
17 ThermometerEncoder,
18 LevelEncoder,
19 )
20
21
22 def demo_fpe_basic():
23 """Demonstrate basic FractionalPowerEncoder usage."""
24 print("=" * 70)
25 print("Demo 1: FractionalPowerEncoder - Basic Usage")
26 print("=" * 70)
27
28 # Create FHRR model (recommended for FPE)
29 model = VSA.create('FHRR', dim=10000, seed=42)
30
31 # Create encoder for temperature range 0-100°C
32 encoder = FractionalPowerEncoder(
33 model,
34 min_val=0,
35 max_val=100,
36 bandwidth=1.0, # Standard kernel width
37 seed=42
38 )
39
40 print(f"\nEncoder: {encoder}")
41 print(f"Reversible: {encoder.is_reversible}")
42 print(f"Compatible models: {encoder.compatible_models}")
43
44 # Encode some temperature values
45 temps = [20.0, 25.0, 30.0, 75.0]
46 print(f"\nEncoding temperatures: {temps}")
47
48 encoded = [encoder.encode(t) for t in temps]
49
50 # Check similarities
51 print("\nSimilarity Matrix:")
52 print(" ", " ".join(f"{t:5.1f}" for t in temps))
53 for i, t1 in enumerate(temps):
54 similarities = [
55 float(model.similarity(encoded[i], encoded[j]))
56 for j in range(len(temps))
57 ]
58 print(f"{t1:5.1f}", " ".join(f"{s:5.3f}" for s in similarities))
59
60 # Test decoding
61 print("\nDecoding test:")
62 for i, temp in enumerate(temps):
63 decoded = encoder.decode(encoded[i])
64 error = abs(decoded - temp)
65 print(f" Original: {temp:6.2f}°C → Decoded: {decoded:6.2f}°C "
66 f"(error: {error:5.3f}°C)")
67
68 print("\nKey observations:")
69 print(" - Close values (20, 25, 30) have high similarity (>0.9)")
70 print(" - Distant values (20, 75) have low similarity (<0.5)")
71 print(" - Decoding is approximate but accurate (error < 1°C)")
72
73
74 def demo_fpe_bandwidth():
75 """Demonstrate effect of bandwidth parameter."""
76 print("\n" + "=" * 70)
77 print("Demo 2: FractionalPowerEncoder - Bandwidth Effects")
78 print("=" * 70)
79
80 model = VSA.create('FHRR', dim=10000, seed=42)
81
82 # Test different bandwidths
83 bandwidths = [0.01, 0.1, 1.0, 10.0]
84 test_values = [25.0, 26.0, 30.0, 50.0]
85
86 print("\nEffect of bandwidth on similarity:")
87 print("Reference value: 25.0°C")
88 print("\nBandwidth | 26.0°C | 30.0°C | 50.0°C")
89 print("-" * 45)
90
91 for beta in bandwidths:
92 encoder = FractionalPowerEncoder(model, 0, 100, bandwidth=beta, seed=42)
93
94 # Encode all values
95 ref_hv = encoder.encode(25.0)
96 similarities = [
97 float(model.similarity(ref_hv, encoder.encode(v)))
98 for v in [26.0, 30.0, 50.0]
99 ]
100
101 print(f"{beta:8.2f} | {similarities[0]:6.3f} | "
102 f"{similarities[1]:6.3f} | {similarities[2]:6.3f}")
103
104 print("\nKey observations:")
105 print(" - Lower bandwidth → wider kernel → more similar values")
106 print(" - Higher bandwidth → narrower kernel → more distinct values")
107 print(" - β=0.01: Good for classification (generalization)")
108 print(" - β=10: Good for exact matching (discrimination)")
109
110
111 def demo_fpe_vs_hrr():
112 """Compare FPE performance on FHRR vs HRR."""
113 print("\n" + "=" * 70)
114 print("Demo 3: FractionalPowerEncoder - FHRR vs HRR")
115 print("=" * 70)
116
117 # Create both models
118 fhrr_model = VSA.create('FHRR', dim=10000, seed=42)
119 hrr_model = VSA.create('HRR', dim=10000, seed=42)
120
121 # Create encoders
122 fhrr_encoder = FractionalPowerEncoder(fhrr_model, 0, 100, bandwidth=1.0, seed=42)
123 hrr_encoder = FractionalPowerEncoder(hrr_model, 0, 100, bandwidth=1.0, seed=42)
124
125 # Test values
126 values = [20.0, 25.0, 30.0, 50.0]
127
128 print("\nSimilarity comparison (reference: 25.0°C):")
129 print("Value | FHRR | HRR")
130 print("-" * 25)
131
132 ref_fhrr = fhrr_encoder.encode(25.0)
133 ref_hrr = hrr_encoder.encode(25.0)
134
135 for v in values:
136 sim_fhrr = float(fhrr_model.similarity(ref_fhrr, fhrr_encoder.encode(v)))
137 sim_hrr = float(hrr_model.similarity(ref_hrr, hrr_encoder.encode(v)))
138 print(f"{v:5.1f} | {sim_fhrr:5.3f} | {sim_hrr:5.3f}")
139
140 print("\nKey observations:")
141 print(" - FHRR uses complex arithmetic (exact implementation)")
142 print(" - HRR uses FFT-based approximation (still accurate)")
143 print(" - Both preserve locality well")
144 print(" - FHRR is faster for encoding, HRR uses less memory")
145
146
147 def demo_thermometer():
148 """Demonstrate ThermometerEncoder."""
149 print("\n" + "=" * 70)
150 print("Demo 4: ThermometerEncoder - Ordinal Encoding")
151 print("=" * 70)
152
153 model = VSA.create('MAP', dim=10000, seed=42)
154
155 # Create thermometer encoder with 20 bins
156 encoder = ThermometerEncoder(
157 model,
158 min_val=0,
159 max_val=100,
160 n_bins=20,
161 seed=42
162 )
163
164 print(f"\nEncoder: {encoder}")
165 print(f"Number of bins: {encoder.n_bins}")
166 print(f"Bin width: {encoder.bin_width:.2f}°C")
167 print(f"Reversible: {encoder.is_reversible}")
168
169 # Encode values
170 values = [10.0, 20.0, 50.0, 90.0]
171 encoded = [encoder.encode(v) for v in values]
172
173 print("\nSimilarity Matrix:")
174 print(" ", " ".join(f"{v:5.1f}" for v in values))
175 for i, v1 in enumerate(values):
176 similarities = [
177 float(model.similarity(encoded[i], encoded[j]))
178 for j in range(len(values))
179 ]
180 print(f"{v1:5.1f}", " ".join(f"{s:5.3f}" for s in similarities))
181
182 print("\nKey observations:")
183 print(" - Monotonic similarity: higher value → more bins → higher similarity")
184 print(" - Simple and robust")
185 print(" - Works with all VSA models")
186 print(" - Not reversible (cannot decode)")
187
188
189 def demo_level():
190 """Demonstrate LevelEncoder."""
191 print("\n" + "=" * 70)
192 print("Demo 5: LevelEncoder - Discrete Level Encoding")
193 print("=" * 70)
194
195 model = VSA.create('MAP', dim=10000, seed=42)
196
197 # Create level encoder for weekdays (7 discrete levels)
198 encoder = LevelEncoder(
199 model,
200 min_val=0,
201 max_val=6,
202 n_levels=7,
203 seed=42
204 )
205
206 print(f"\nEncoder: {encoder}")
207 print(f"Number of levels: {encoder.n_levels}")
208 print(f"Reversible: {encoder.is_reversible}")
209
210 # Encode weekdays
211 days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
212 day_values = list(range(7))
213 encoded = [encoder.encode(float(v)) for v in day_values]
214
215 print("\nEncoding weekdays:")
216 for day, value, hv in zip(days, day_values, encoded):
217 decoded = encoder.decode(hv)
218 print(f" {day} ({value}) → HV → {decoded:.0f} ✓")
219
220 # Check similarity
221 print("\nSimilarity Matrix (first 4 days):")
222 print(" ", " ".join(f"{d:>3}" for d in days[:4]))
223 for i in range(4):
224 similarities = [
225 float(model.similarity(encoded[i], encoded[j]))
226 for j in range(4)
227 ]
228 print(f"{days[i]:>3} ", " ".join(f"{s:5.3f}" for s in similarities))
229
230 print("\nKey observations:")
231 print(" - Exact encoding/decoding (perfect recovery)")
232 print(" - Fast O(1) lookup")
233 print(" - Levels are nearly orthogonal (low similarity)")
234 print(" - Best for discrete categorical data")
235
236
237 def demo_encoder_comparison():
238 """Compare all three encoders on the same data."""
239 print("\n" + "=" * 70)
240 print("Demo 6: Encoder Comparison")
241 print("=" * 70)
242
243 # Create models
244 fhrr_model = VSA.create('FHRR', dim=10000, seed=42)
245 map_model = VSA.create('MAP', dim=10000, seed=42)
246
247 # Create encoders
248 fpe = FractionalPowerEncoder(fhrr_model, 0, 100, bandwidth=1.0, seed=42)
249 thermo = ThermometerEncoder(map_model, 0, 100, n_bins=20, seed=42)
250 level = LevelEncoder(map_model, 0, 100, n_levels=11, seed=42)
251
252 # Test data: 0, 10, 20, ..., 100 (11 values)
253 values = [float(i * 10) for i in range(11)]
254
255 print("\nEncoding 11 values: [0, 10, 20, ..., 100]")
256 print("\nSimilarity to reference value 50:")
257 print("\nValue | FPE | Thermo | Level")
258 print("-" * 38)
259
260 # Encode reference
261 ref_fpe = fpe.encode(50.0)
262 ref_thermo = thermo.encode(50.0)
263 ref_level = level.encode(50.0)
264
265 for v in values:
266 sim_fpe = float(fhrr_model.similarity(ref_fpe, fpe.encode(v)))
267 sim_thermo = float(map_model.similarity(ref_thermo, thermo.encode(v)))
268 sim_level = float(map_model.similarity(ref_level, level.encode(v)))
269
270 print(f"{v:5.0f} | {sim_fpe:5.3f} | {sim_thermo:6.3f} | {sim_level:5.3f}")
271
272 print("\nKey differences:")
273 print(" - FPE: Smooth Gaussian-like profile (continuous)")
274 print(" - Thermometer: Monotonic increasing (ordinal)")
275 print(" - Level: Binary (1.0 for exact match, ~0 otherwise)")
276
277 print("\nWhen to use each:")
278 print(" - FPE: Continuous values, need smooth similarity")
279 print(" - Thermometer: Ordinal data, monotonicity important")
280 print(" - Level: Discrete categories, exact matching")
281
282
283 def demo_visualization():
284 """Visualize similarity profiles of different encoders."""
285 print("\n" + "=" * 70)
286 print("Demo 7: Similarity Profile Visualization")
287 print("=" * 70)
288
289 try:
290 import matplotlib
291 matplotlib.use('Agg') # Non-interactive backend
292 import matplotlib.pyplot as plt
293 except ImportError:
294 print("\nMatplotlib not available. Skipping visualization.")
295 return
296
297 # Create models
298 fhrr_model = VSA.create('FHRR', dim=10000, seed=42)
299 map_model = VSA.create('MAP', dim=10000, seed=42)
300
301 # Create encoders
302 fpe = FractionalPowerEncoder(fhrr_model, 0, 100, bandwidth=1.0, seed=42)
303 thermo = ThermometerEncoder(map_model, 0, 100, n_bins=20, seed=42)
304 level = LevelEncoder(map_model, 0, 100, n_levels=11, seed=42)
305
306 # Reference value
307 ref_value = 50.0
308
309 # Test range
310 test_values = np.linspace(0, 100, 101)
311
312 # Compute similarities
313 ref_fpe = fpe.encode(ref_value)
314 ref_thermo = thermo.encode(ref_value)
315 ref_level = level.encode(ref_value)
316
317 sim_fpe = [
318 float(fhrr_model.similarity(ref_fpe, fpe.encode(v)))
319 for v in test_values
320 ]
321 sim_thermo = [
322 float(map_model.similarity(ref_thermo, thermo.encode(v)))
323 for v in test_values
324 ]
325 sim_level = [
326 float(map_model.similarity(ref_level, level.encode(v)))
327 for v in test_values
328 ]
329
330 # Create plot
331 plt.figure(figsize=(12, 6))
332
333 plt.subplot(1, 3, 1)
334 plt.plot(test_values, sim_fpe, 'b-', linewidth=2)
335 plt.axvline(ref_value, color='r', linestyle='--', alpha=0.5)
336 plt.xlabel('Value')
337 plt.ylabel('Similarity')
338 plt.title('FractionalPowerEncoder\n(Smooth, Gaussian-like)')
339 plt.grid(True, alpha=0.3)
340 plt.ylim(-0.1, 1.1)
341
342 plt.subplot(1, 3, 2)
343 plt.plot(test_values, sim_thermo, 'g-', linewidth=2)
344 plt.axvline(ref_value, color='r', linestyle='--', alpha=0.5)
345 plt.xlabel('Value')
346 plt.ylabel('Similarity')
347 plt.title('ThermometerEncoder\n(Monotonic, Triangular)')
348 plt.grid(True, alpha=0.3)
349 plt.ylim(-0.1, 1.1)
350
351 plt.subplot(1, 3, 3)
352 plt.plot(test_values, sim_level, 'orange', linewidth=2)
353 plt.axvline(ref_value, color='r', linestyle='--', alpha=0.5)
354 plt.xlabel('Value')
355 plt.ylabel('Similarity')
356 plt.title('LevelEncoder\n(Discrete, Binary)')
357 plt.grid(True, alpha=0.3)
358 plt.ylim(-0.1, 1.1)
359
360 plt.tight_layout()
361 plt.savefig('encoder_similarity_profiles.png', dpi=150)
362 print("\nSimilarity profiles saved to: encoder_similarity_profiles.png")
363
364 print("\nVisualization notes:")
365 print(" - Red dashed line: reference value (50)")
366 print(" - FPE: Smooth bell curve centered at reference")
367 print(" - Thermometer: Triangular peak at reference")
368 print(" - Level: Sharp spike at exact match only")
369
370
371 def main():
372 """Run all demos."""
373 print("\n" + "=" * 70)
374 print("HDVEC Scalar Encoders - Comprehensive Demo")
375 print("=" * 70)
376
377 try:
378 # Run all demos
379 demo_fpe_basic()
380 demo_fpe_bandwidth()
381 demo_fpe_vs_hrr()
382 demo_thermometer()
383 demo_level()
384 demo_encoder_comparison()
385 demo_visualization()
386
387 print("\n" + "=" * 70)
388 print("All demos completed successfully!")
389 print("=" * 70)
390 print("\nFor more information:")
391 print(" - Theory: docs/theory/encoders.md")
392 print(" - Tests: tests/test_encoders_scalar.py")
393 print(" - API docs: help(FractionalPowerEncoder)")
394
395 except Exception as e:
396 print(f"\nError running demos: {e}")
397 import traceback
398 traceback.print_exc()
399
400
401 if __name__ == '__main__':
402 main()