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)

Gallery generated by Sphinx-Gallery