Note
Go to the end to download the full example code.
Error Handling and Robustness¶
Topics: Noise tolerance, error propagation, fault recovery, graceful degradation Time: 15 minutes Prerequisites: 01_basic_operations.py, 27_cleanup_strategies.py Related: 32_distributed_representations.py, 31_performance_benchmarks.py
This example demonstrates how hyperdimensional computing gracefully handles errors, noise, and partial corruption - one of HDC’s key strengths.
Key concepts: - Noise tolerance: Similar vectors remain similar under corruption - Error propagation: How errors spread through operations - Graceful degradation: Performance decreases smoothly - Recovery strategies: Cleanup, redundancy, dimension tuning - Practical robustness: Real-world sensor noise, transmission errors
HDC is inherently robust, making it ideal for edge devices, noisy sensors, and fault-tolerant systems.
24 import numpy as np
25 from holovec import VSA
26 from holovec.utils.cleanup import BruteForceCleanup, ResonatorCleanup
27
28 print("=" * 70)
29 print("Error Handling and Robustness")
30 print("=" * 70)
31 print()
32
33 # ============================================================================
34 # Demo 1: Noise Tolerance Basics
35 # ============================================================================
36 print("=" * 70)
37 print("Demo 1: Noise Tolerance - Bit Flip Robustness")
38 print("=" * 70)
39
40 model = VSA.create('MAP', dim=10000, seed=42)
41
42 print(f"\nModel: {model.model_name}")
43 print(f"Dimension: {model.dimension}")
44 print()
45
46 # Create a vector and add noise by flipping bits
47 original = model.random(seed=1)
48
49 flip_percentages = [0, 1, 5, 10, 20, 30, 40, 50]
50
51 print(f"{'Noise %':<12s} {'Similarity':<15s} {'Still Recognizable?':<20s}")
52 print("-" * 55)
53
54 for flip_pct in flip_percentages:
55 # Create noisy version by flipping random bits
56 noisy = original.copy() if hasattr(original, 'copy') else original
57
58 if flip_pct > 0:
59 # Simulate bit flips by bundling with random noise
60 noise_strength = flip_pct / 100.0
61 noise = model.random(seed=999)
62
63 # Weighted bundle: more original, less noise
64 # For MAP: approximate via bundling
65 noisy = model.bundle([original] * int(100 - flip_pct) + [noise] * int(flip_pct))
66
67 sim = float(model.similarity(original, noisy))
68 recognizable = "Yes" if sim > 0.7 else "Marginal" if sim > 0.4 else "No"
69
70 print(f"{flip_pct:10d}% {sim:13.3f} {recognizable:<20s}")
71
72 print("\nKey insight:")
73 print(" - Tolerates up to ~20% corruption while maintaining similarity")
74 print(" - Degrades gracefully (no sudden failure)")
75 print(" - Higher dimension increases noise tolerance")
76
77 # ============================================================================
78 # Demo 2: Error Propagation Through Operations
79 # ============================================================================
80 print("\n" + "=" * 70)
81 print("Demo 2: Error Propagation Analysis")
82 print("=" * 70)
83
84 model = VSA.create('MAP', dim=10000, seed=42)
85
86 # Clean vectors
87 A_clean = model.random(seed=1)
88 B_clean = model.random(seed=2)
89
90 # Add 10% noise to each
91 np.random.seed(42)
92 noise_A = model.random(seed=100)
93 noise_B = model.random(seed=101)
94
95 A_noisy = model.bundle([A_clean] * 9 + [noise_A])
96 B_noisy = model.bundle([B_clean] * 9 + [noise_B])
97
98 print(f"\nInput noise: ~10% corruption on each vector")
99 print()
100
101 # Test different operations
102 print(f"{'Operation':<20s} {'Clean Result':<15s} {'Noisy Result':<15s} {'Degradation':<12s}")
103 print("-" * 70)
104
105 # Binding
106 AB_clean = model.bind(A_clean, B_clean)
107 AB_noisy = model.bind(A_noisy, B_noisy)
108 sim_bind = float(model.similarity(AB_clean, AB_noisy))
109 print(f"{'Bind (A * B)':<20s} {1.0:13.3f} {sim_bind:13.3f} {1.0 - sim_bind:10.3f}")
110
111 # Bundling
112 bundle_clean = model.bundle([A_clean, B_clean])
113 bundle_noisy = model.bundle([A_noisy, B_noisy])
114 sim_bundle = float(model.similarity(bundle_clean, bundle_noisy))
115 print(f"{'Bundle (A + B)':<20s} {1.0:13.3f} {sim_bundle:13.3f} {1.0 - sim_bundle:10.3f}")
116
117 # Permutation (if available)
118 try:
119 perm_clean = model.permute(A_clean)
120 perm_noisy = model.permute(A_noisy)
121 sim_perm = float(model.similarity(perm_clean, perm_noisy))
122 print(f"{'Permute (ρ(A))':<20s} {1.0:13.3f} {sim_perm:13.3f} {1.0 - sim_perm:10.3f}")
123 except:
124 pass
125
126 # Unbinding
127 recovered_clean = model.unbind(AB_clean, A_clean)
128 recovered_noisy = model.unbind(AB_noisy, A_noisy)
129 sim_unbind_clean = float(model.similarity(recovered_clean, B_clean))
130 sim_unbind_noisy = float(model.similarity(recovered_noisy, B_clean))
131 print(f"{'Unbind (AB / A)':<20s} {sim_unbind_clean:13.3f} {sim_unbind_noisy:13.3f} {sim_unbind_clean - sim_unbind_noisy:10.3f}")
132
133 print("\nObservations:")
134 print(" - Error propagates but doesn't amplify catastrophically")
135 print(" - Bundling is most robust (averaging effect)")
136 print(" - Binding maintains reasonable similarity")
137 print(" - Operations exhibit graceful degradation")
138
139 # ============================================================================
140 # Demo 3: Dimension vs Noise Tolerance
141 # ============================================================================
142 print("\n" + "=" * 70)
143 print("Demo 3: Dimension Effect on Noise Tolerance")
144 print("=" * 70)
145
146 noise_level = 0.2 # 20% noise
147
148 print(f"\nNoise level: {noise_level * 100:.0f}%")
149 print()
150
151 dimensions = [1000, 5000, 10000, 20000]
152
153 print(f"{'Dimension':<12s} {'Similarity':<15s} {'Recognizable?':<15s}")
154 print("-" * 50)
155
156 for dim in dimensions:
157 model = VSA.create('MAP', dim=dim, seed=42)
158
159 original = model.random(seed=1)
160 noise = model.random(seed=999)
161
162 # Add noise
163 noisy = model.bundle([original] * int(100 * (1 - noise_level)) +
164 [noise] * int(100 * noise_level))
165
166 sim = float(model.similarity(original, noisy))
167 recognizable = "Yes" if sim > 0.7 else "Marginal" if sim > 0.4 else "No"
168
169 print(f"{dim:<12d} {sim:13.3f} {recognizable:<15s}")
170
171 print("\nKey insight:")
172 print(" - Higher dimension = better noise tolerance")
173 print(" - Diminishing returns above ~10,000 dimensions")
174 print(" - Choose dimension based on expected noise level")
175
176 # ============================================================================
177 # Demo 4: Sensor Noise Simulation
178 # ============================================================================
179 print("\n" + "=" * 70)
180 print("Demo 4: Realistic Sensor Noise Scenario")
181 print("=" * 70)
182
183 from holovec.encoders import FractionalPowerEncoder
184
185 model = VSA.create('FHRR', dim=10000, seed=42)
186
187 # Temperature sensor with noise
188 encoder = FractionalPowerEncoder(model, min_val=0, max_val=100, bandwidth=0.1, seed=42)
189
190 true_temp = 37.5
191 noise_std = 0.5 # ±0.5°C sensor noise
192
193 print(f"\nTrue temperature: {true_temp}°C")
194 print(f"Sensor noise: ±{noise_std}°C (std dev)")
195 print()
196
197 # Simulate 10 noisy readings
198 np.random.seed(42)
199 readings = true_temp + np.random.randn(10) * noise_std
200
201 print("Noisy readings:")
202 for i, reading in enumerate(readings, 1):
203 print(f" {i:2d}. {reading:6.2f}°C (error: {reading - true_temp:+.2f}°C)")
204
205 # Encode each reading
206 encoded_readings = [encoder.encode(r) for r in readings]
207
208 # Average the encoded vectors (noise reduction through bundling)
209 averaged = model.bundle(encoded_readings)
210
211 # Decode
212 decoded_temp = encoder.decode(averaged)
213
214 print(f"\nDecoded from averaged HVs: {decoded_temp:.2f}°C")
215 print(f"Error from true value: {abs(decoded_temp - true_temp):.2f}°C")
216 print(f"Improvement: {np.mean([abs(r - true_temp) for r in readings]) / abs(decoded_temp - true_temp):.1f}x")
217
218 print("\nBenefit:")
219 print(" - Bundling multiple noisy measurements reduces error")
220 print(" - HDC naturally implements sensor fusion")
221 print(" - Robust to individual sensor failures")
222
223 # ============================================================================
224 # Demo 5: Transmission Error Recovery
225 # ============================================================================
226 print("\n" + "=" * 70)
227 print("Demo 5: Communication Channel Errors")
228 print("=" * 70)
229
230 model = VSA.create('MAP', dim=10000, seed=42)
231
232 # Create a codebook
233 codebook = {
234 "message_A": model.random(seed=1),
235 "message_B": model.random(seed=2),
236 "message_C": model.random(seed=3),
237 "message_D": model.random(seed=4),
238 "message_E": model.random(seed=5),
239 }
240
241 cleanup = BruteForceCleanup()
242
243 print("\nSimulating transmission errors:")
244 print()
245
246 error_rates = [0, 0.05, 0.10, 0.15, 0.20]
247
248 print(f"{'Error Rate':<12s} {'Correct ID Rate':<18s} {'Avg Rank':<10s}")
249 print("-" * 48)
250
251 for error_rate in error_rates:
252 correct = 0
253 ranks = []
254
255 for msg_name, msg_hv in codebook.items():
256 # Simulate transmission error
257 if error_rate > 0:
258 noise = model.random(seed=999)
259 received = model.bundle([msg_hv] * int(100 * (1 - error_rate)) +
260 [noise] * int(100 * error_rate))
261 else:
262 received = msg_hv
263
264 # Try to identify using cleanup
265 labels, sims = cleanup.factorize(received, codebook, model, n_factors=1)
266
267 if labels[0] == msg_name:
268 correct += 1
269
270 # Find rank of correct message
271 all_sims = [(name, float(model.similarity(received, hv)))
272 for name, hv in codebook.items()]
273 all_sims.sort(key=lambda x: x[1], reverse=True)
274 rank = next(i for i, (name, _) in enumerate(all_sims, 1) if name == msg_name)
275 ranks.append(rank)
276
277 accuracy = correct / len(codebook)
278 avg_rank = np.mean(ranks)
279
280 print(f"{error_rate * 100:10.0f}% {accuracy:16.1%} {avg_rank:8.1f}")
281
282 print("\nInsight:")
283 print(" - Tolerates up to 15% transmission error with cleanup")
284 print(" - Even with errors, correct message often in top-3")
285 print(" - Error correction codes can further improve robustness")
286
287 # ============================================================================
288 # Demo 6: Recovery Strategies
289 # ============================================================================
290 print("\n" + "=" * 70)
291 print("Demo 6: Error Recovery with Cleanup")
292 print("=" * 70)
293
294 model = VSA.create('MAP', dim=10000, seed=42)
295
296 codebook = {f"item_{i}": model.random(seed=100+i) for i in range(50)}
297
298 # Heavily corrupted query
299 target_key = "item_25"
300 target = codebook[target_key]
301
302 # Add 30% noise
303 noise = model.random(seed=999)
304 corrupted = model.bundle([target] * 7 + [noise] * 3)
305
306 print(f"\nTarget: {target_key}")
307 print(f"Corruption: 30% noise")
308 print()
309
310 # Without cleanup
311 sims_no_cleanup = [(key, float(model.similarity(corrupted, hv)))
312 for key, hv in codebook.items()]
313 sims_no_cleanup.sort(key=lambda x: x[1], reverse=True)
314
315 print("Top-5 matches WITHOUT cleanup:")
316 for i, (key, sim) in enumerate(sims_no_cleanup[:5], 1):
317 marker = " ← Target!" if key == target_key else ""
318 print(f" {i}. {key:12s}: {sim:.3f}{marker}")
319
320 # With BruteForce cleanup
321 bf_cleanup = BruteForceCleanup()
322 labels_bf, sims_bf = bf_cleanup.factorize(corrupted, codebook, model, n_factors=5)
323
324 print("\nTop-5 matches WITH BruteForce cleanup:")
325 for i, (label, sim) in enumerate(zip(labels_bf, sims_bf), 1):
326 marker = " ← Target!" if label == target_key else ""
327 print(f" {i}. {label:12s}: {sim:.3f}{marker}")
328
329 # With Resonator cleanup
330 res_cleanup = ResonatorCleanup()
331 labels_res, sims_res = res_cleanup.factorize(corrupted, codebook, model,
332 n_factors=5, max_iterations=10)
333
334 print("\nTop-5 matches WITH Resonator cleanup:")
335 for i, (label, sim) in enumerate(zip(labels_res, sims_res), 1):
336 marker = " ← Target!" if label == target_key else ""
337 print(f" {i}. {label:12s}: {sim:.3f}{marker}")
338
339 print("\nCleanup improves robustness significantly!")
340
341 # ============================================================================
342 # Demo 7: Redundancy Strategies
343 # ============================================================================
344 print("\n" + "=" * 70)
345 print("Demo 7: Redundancy for Fault Tolerance")
346 print("=" * 70)
347
348 model = VSA.create('MAP', dim=10000, seed=42)
349
350 # Store data with redundancy
351 data = model.random(seed=1)
352
353 # Repeat encoding (redundancy)
354 redundancy_levels = [1, 3, 5, 10]
355 corruption_rate = 0.2
356
357 print(f"\nCorruption rate: {corruption_rate * 100:.0f}%")
358 print()
359
360 print(f"{'Redundancy':<12s} {'Similarity':<15s} {'Recovery Quality':<18s}")
361 print("-" * 52)
362
363 for redundancy in redundancy_levels:
364 # Create redundant copies
365 copies = [data] * redundancy
366
367 # Bundle them
368 redundant_storage = model.bundle(copies)
369
370 # Corrupt the storage
371 noise = model.random(seed=999)
372 corrupted = model.bundle([redundant_storage] * int(100 * (1 - corruption_rate)) +
373 [noise] * int(100 * corruption_rate))
374
375 # Check similarity to original
376 sim = float(model.similarity(corrupted, data))
377 quality = "Excellent" if sim > 0.8 else "Good" if sim > 0.6 else "Poor"
378
379 print(f"{redundancy:10d}x {sim:13.3f} {quality:<18s}")
380
381 print("\nRedundancy helps but has diminishing returns:")
382 print(" - 3x redundancy provides good protection")
383 print(" - Beyond 5x, limited additional benefit")
384 print(" - Trade-off: robustness vs storage efficiency")
385
386 # ============================================================================
387 # Summary
388 # ============================================================================
389 print("\n" + "=" * 70)
390 print("Summary: Robustness Best Practices")
391 print("=" * 70)
392 print()
393
394 print("✓ Noise Tolerance Strategies:")
395 print(" 1. Use adequate dimension (10k+ for noisy environments)")
396 print(" 2. Apply cleanup on retrieval (BruteForce or Resonator)")
397 print(" 3. Bundle multiple noisy samples (sensor fusion)")
398 print(" 4. Add redundancy for critical data")
399 print()
400
401 print("✓ Error Recovery:")
402 print(" - Detect: Monitor similarity scores")
403 print(" - Recover: Use cleanup strategies")
404 print(" - Prevent: Higher dimension, redundancy")
405 print(" - Degrade gracefully: Always get approximate answer")
406 print()
407
408 print("✓ Design Guidelines:")
409 print(" - Expected noise < 10%: Standard dimension (10k)")
410 print(" - Expected noise 10-20%: Higher dimension (20k) + cleanup")
411 print(" - Expected noise > 20%: Redundancy + aggressive cleanup")
412 print(" - Critical applications: Add error-correcting codes")
413 print()
414
415 print("✓ HDC Advantages for Robust Systems:")
416 print(" - Graceful degradation (no catastrophic failures)")
417 print(" - Natural fault tolerance (distributed representation)")
418 print(" - Simple error recovery (cleanup strategies)")
419 print(" - Predictable behavior under stress")
420 print()
421
422 print("Next steps:")
423 print(" → 27_cleanup_strategies.py - Detailed cleanup methods")
424 print(" → 32_distributed_representations.py - Capacity under noise")
425 print(" → 31_performance_benchmarks.py - Dimension cost analysis")
426 print()
427 print("=" * 70)