Note
Go to the end to download the full example code.
Fractional Power Encoder Deep Dive¶
Topics: FPE theory, bandwidth tuning, similarity profiles, decoding accuracy Time: 15 minutes Prerequisites: 10_encoders_scalar.py, 01_basic_operations.py Related: 12_encoders_thermometer_level.py, 30_theory_fpe_validation.py
This example provides an in-depth exploration of the FractionalPowerEncoder (FPE), the most powerful continuous value encoder in HoloVec. Learn how to tune bandwidth for your specific application and understand the theoretical foundations.
Key concepts: - Complex exponential encoding: e^(2πi·φ(x)) - Bandwidth parameter: Controls similarity decay with distance - Smooth similarity: Nearby values have high similarity - Exact decoding: Reversible encoding for value recovery - Model compatibility: Works with FHRR, HRR (complex/real FFT models)
FPE is ideal for continuous measurements (temperature, pressure, time) where smooth similarity relationships are important.
25 import numpy as np
26 from holovec import VSA
27 from holovec.encoders import FractionalPowerEncoder
28
29 print("=" * 70)
30 print("Fractional Power Encoder Deep Dive")
31 print("=" * 70)
32 print()
33
34 # ============================================================================
35 # Demo 1: Understanding Bandwidth Parameter
36 # ============================================================================
37 print("=" * 70)
38 print("Demo 1: Bandwidth Parameter Effects")
39 print("=" * 70)
40
41 model = VSA.create('FHRR', dim=10000, seed=42)
42
43 # Create encoders with different bandwidths
44 bandwidths = [0.05, 0.1, 0.2, 0.5]
45 encoders = {bw: FractionalPowerEncoder(model, min_val=0, max_val=100,
46 bandwidth=bw, seed=42)
47 for bw in bandwidths}
48
49 print(f"\nModel: {model.model_name}, dimension={model.dimension}")
50 print(f"Range: 0-100")
51 print(f"\nTesting bandwidths: {bandwidths}")
52
53 # Test similarity decay with distance
54 reference_value = 50.0
55 test_values = [50.0, 51.0, 52.0, 55.0, 60.0, 70.0]
56
57 print(f"\n{'Distance':<10s} ", end="")
58 for bw in bandwidths:
59 print(f"BW={bw:<5.2f} ", end="")
60 print()
61 print("-" * 60)
62
63 for test_val in test_values:
64 distance = abs(test_val - reference_value)
65 print(f"{distance:<10.1f} ", end="")
66
67 for bw in bandwidths:
68 ref_hv = encoders[bw].encode(reference_value)
69 test_hv = encoders[bw].encode(test_val)
70 sim = float(model.similarity(ref_hv, test_hv))
71 print(f"{sim:7.3f} ", end="")
72 print()
73
74 print("\nObservations:")
75 print(" - Lower bandwidth = slower similarity decay (more tolerance)")
76 print(" - Higher bandwidth = faster decay (more discriminative)")
77 print(" - Choose based on your noise tolerance vs discrimination needs")
78
79 # ============================================================================
80 # Demo 2: Decoding Accuracy vs Bandwidth
81 # ============================================================================
82 print("\n" + "=" * 70)
83 print("Demo 2: Decoding Accuracy Analysis")
84 print("=" * 70)
85
86 test_values = [10.0, 25.5, 50.0, 75.3, 99.0]
87
88 print("\nDecoding accuracy for different bandwidths:\n")
89 print(f"{'Value':<10s} ", end="")
90 for bw in bandwidths:
91 print(f"BW={bw:<5.2f} ", end="")
92 print()
93 print("-" * 60)
94
95 for val in test_values:
96 print(f"{val:<10.1f} ", end="")
97
98 for bw in bandwidths:
99 hv = encoders[bw].encode(val)
100 decoded = encoders[bw].decode(hv)
101 error = abs(decoded - val)
102 print(f"{error:7.4f} ", end="")
103 print()
104
105 print("\nObservations:")
106 print(" - All bandwidths provide excellent decoding (errors < 0.001)")
107 print(" - Bandwidth affects similarity profiles, not decoding accuracy")
108
109 # ============================================================================
110 # Demo 3: Similarity Profile Visualization
111 # ============================================================================
112 print("\n" + "=" * 70)
113 print("Demo 3: Similarity Profile Shape")
114 print("=" * 70)
115
116 # Encode reference at 50
117 reference = 50.0
118 encoder = FractionalPowerEncoder(model, min_val=0, max_val=100,
119 bandwidth=0.1, seed=42)
120 ref_hv = encoder.encode(reference)
121
122 # Test entire range
123 test_range = np.linspace(0, 100, 21)
124 similarities = []
125
126 for val in test_range:
127 test_hv = encoder.encode(val)
128 sim = float(model.similarity(ref_hv, test_hv))
129 similarities.append(sim)
130
131 print(f"\nReference value: {reference}")
132 print(f"Bandwidth: 0.1")
133 print(f"\n{'Value':<8s} {'Similarity':<12s} Visualization")
134 print("-" * 50)
135
136 for val, sim in zip(test_range, similarities):
137 bar_length = int(sim * 40)
138 bar = "█" * bar_length
139 print(f"{val:6.1f} {sim:8.3f} {bar}")
140
141 print("\nObservations:")
142 print(" - Peak similarity at reference value (1.0)")
143 print(" - Smooth, gradual decay with distance")
144 print(" - Symmetric around reference point")
145
146 # ============================================================================
147 # Demo 4: Bandwidth Selection Guide
148 # ============================================================================
149 print("\n" + "=" * 70)
150 print("Demo 4: Bandwidth Selection Guide")
151 print("=" * 70)
152
153 # Simulate noisy measurements
154 np.random.seed(42)
155 true_value = 50.0
156 noise_levels = [0.5, 1.0, 2.0, 5.0]
157
158 print("\nScenario: Noisy sensor readings")
159 print(f"True value: {true_value}")
160 print()
161
162 for noise_std in noise_levels:
163 print(f"\nNoise level (std): {noise_std}")
164
165 # Generate noisy readings
166 noisy_readings = true_value + np.random.randn(10) * noise_std
167
168 # Test different bandwidths
169 for bw in [0.05, 0.1, 0.2]:
170 encoder = FractionalPowerEncoder(model, min_val=0, max_val=100,
171 bandwidth=bw, seed=42)
172
173 # Encode true value
174 true_hv = encoder.encode(true_value)
175
176 # Average similarity of noisy readings
177 sims = []
178 for reading in noisy_readings:
179 noisy_hv = encoder.encode(reading)
180 sim = float(model.similarity(true_hv, noisy_hv))
181 sims.append(sim)
182
183 avg_sim = np.mean(sims)
184 print(f" BW={bw:.2f}: avg similarity = {avg_sim:.3f}")
185
186 print("\nRecommendations:")
187 print(" - Low noise (< 1% of range): BW = 0.2-0.5 (high discrimination)")
188 print(" - Medium noise (1-5% of range): BW = 0.1-0.2 (balanced)")
189 print(" - High noise (> 5% of range): BW = 0.05-0.1 (noise tolerant)")
190
191 # ============================================================================
192 # Demo 5: Multi-Scale Encoding
193 # ============================================================================
194 print("\n" + "=" * 70)
195 print("Demo 5: Multi-Scale Hierarchical Encoding")
196 print("=" * 70)
197
198 # Encode at different scales (coarse to fine)
199 value = 42.567
200
201 # Coarse: tens place (0-100)
202 coarse_encoder = FractionalPowerEncoder(model, min_val=0, max_val=100,
203 bandwidth=0.1, seed=42)
204 # Fine: ones place (0-10)
205 fine_encoder = FractionalPowerEncoder(model, min_val=0, max_val=10,
206 bandwidth=0.1, seed=43)
207 # Very fine: decimals (0-1)
208 vfine_encoder = FractionalPowerEncoder(model, min_val=0, max_val=1,
209 bandwidth=0.1, seed=44)
210
211 # Encode at each scale
212 coarse_hv = coarse_encoder.encode(value) # 42.567 in [0,100]
213 fine_hv = fine_encoder.encode(value % 10) # 2.567 in [0,10]
214 vfine_hv = vfine_encoder.encode((value * 10) % 1) # 0.567 in [0,1]
215
216 # Bind scales together
217 COARSE = model.random(seed=100)
218 FINE = model.random(seed=101)
219 VFINE = model.random(seed=102)
220
221 multi_scale = model.bundle([
222 model.bind(COARSE, coarse_hv),
223 model.bind(FINE, fine_hv),
224 model.bind(VFINE, vfine_hv)
225 ])
226
227 print(f"\nOriginal value: {value}")
228 print("\nMulti-scale encoding:")
229 print(f" Coarse scale (tens): {value:.1f}")
230 print(f" Fine scale (ones): {value % 10:.2f}")
231 print(f" Very fine scale (decimals): {(value * 10) % 1:.3f}")
232
233 # Decode from each scale
234 coarse_decoded = coarse_encoder.decode(model.unbind(multi_scale, COARSE))
235 fine_decoded = fine_encoder.decode(model.unbind(multi_scale, FINE))
236 vfine_decoded = vfine_encoder.decode(model.unbind(multi_scale, VFINE))
237
238 print("\nDecoded values:")
239 print(f" Coarse: {coarse_decoded:.3f}")
240 print(f" Fine: {fine_decoded:.3f}")
241 print(f" Very fine: {vfine_decoded:.3f}")
242
243 print("\nBenefit:")
244 print(" - Different resolutions for different query types")
245 print(" - Coarse search → fine refinement")
246 print(" - Robust to scale-specific noise")
247
248 # ============================================================================
249 # Demo 6: Model Compatibility
250 # ============================================================================
251 print("\n" + "=" * 70)
252 print("Demo 6: FPE Model Compatibility")
253 print("=" * 70)
254
255 print("\nFPE works with complex/real FFT-based models:\n")
256
257 compatible_models = ['FHRR', 'HRR']
258 test_value = 37.5
259
260 for model_name in compatible_models:
261 m = VSA.create(model_name, dim=5000, seed=42)
262 encoder = FractionalPowerEncoder(m, min_val=0, max_val=100,
263 bandwidth=0.1, seed=42)
264
265 hv = encoder.encode(test_value)
266 decoded = encoder.decode(hv)
267 error = abs(decoded - test_value)
268
269 print(f"{model_name:10s}: encoded={test_value:.1f}°C, "
270 f"decoded={decoded:.3f}°C, error={error:.5f}")
271
272 print("\n⚠️ FPE does NOT work with:")
273 print(" - MAP (multiplication in {-1,+1} loses phase information)")
274 print(" - BSC (binary sparse, no complex representation)")
275 print(" - BSDC (segment binary, no complex representation)")
276
277 print("\nFor these models, use:")
278 print(" - ThermometerEncoder (ordinal encoding)")
279 print(" - LevelEncoder (discrete bins)")
280
281 # ============================================================================
282 # Summary
283 # ============================================================================
284 print("\n" + "=" * 70)
285 print("Summary: FPE Best Practices")
286 print("=" * 70)
287 print()
288
289 print("✓ Bandwidth Selection:")
290 print(" - Start with BW=0.1 (good default for most cases)")
291 print(" - Increase for high discrimination needs")
292 print(" - Decrease for noise-tolerant applications")
293 print()
294
295 print("✓ When to Use FPE:")
296 print(" - Continuous measurements (temp, pressure, time)")
297 print(" - Smooth similarity important")
298 print(" - Value recovery needed (reversible)")
299 print(" - Using FHRR or HRR models")
300 print()
301
302 print("✓ When NOT to Use FPE:")
303 print(" - Using MAP, BSC, BSDC models (incompatible)")
304 print(" - Only need ordinal relationships (use Thermometer)")
305 print(" - Discrete categories (use Level)")
306 print()
307
308 print("✓ Advanced Patterns:")
309 print(" - Multi-scale encoding for hierarchical queries")
310 print(" - Adaptive bandwidth for different value ranges")
311 print(" - Ensemble with other encoders for robustness")
312 print()
313
314 print("Next steps:")
315 print(" → 12_encoders_thermometer_level.py - Alternative scalar encoders")
316 print(" → 30_theory_fpe_validation.py - Mathematical foundations")
317 print(" → 25_app_integration_patterns.py - Combine FPE with other encoders")
318 print()
319 print("=" * 70)