Note
Go to the end to download the full example code.
Working Memory with Cleanup Strategies¶
Topics: Working memory, cleanup, resonator, factorization, noisy retrieval Time: 20 minutes Prerequisites: 23_app_symbolic_reasoning.py, 26_retrieval_basics.py Related: 27_cleanup_strategies.py, 28_factorization_methods.py
This example demonstrates working memory systems using hyperdimensional computing, with a focus on cleanup strategies for retrieving information from noisy bundled representations.
Key concepts: - Working memory: Bundled representation of active information - Cleanup: Recovering clean symbols from noisy hypervectors - BruteForce cleanup: Exhaustive nearest-neighbor search - Resonator cleanup: Iterative refinement using codebook - Multi-factor unbinding: Decompose bundled representations - Noise tolerance: Graceful degradation with increasing bundle size
Working memory in HDC mimics human working memory: limited capacity, distributed representation, and content-addressable retrieval.
26 from holovec import VSA
27 from holovec.retrieval import ItemStore, Codebook
28 from holovec.utils.cleanup import BruteForceCleanup, ResonatorCleanup
29
30 print("=" * 70)
31 print("Working Memory with Cleanup Strategies")
32 print("=" * 70)
33 print()
34
35 # Create model
36 model = VSA.create('FHRR', dim=10000, seed=42)
37
38 # ============================================================================
39 # Demo 1: Basic Working Memory - Bundled Active Items
40 # ============================================================================
41 print("=" * 70)
42 print("Demo 1: Basic Working Memory")
43 print("=" * 70)
44
45 # Simulate working memory with 5 active items
46 print("\nEncoding 5 active items in working memory:")
47
48 items = {}
49 items["book"] = model.random(seed=100)
50 items["pen"] = model.random(seed=101)
51 items["coffee"] = model.random(seed=102)
52 items["phone"] = model.random(seed=103)
53 items["keys"] = model.random(seed=104)
54
55 print(" Items: book, pen, coffee, phone, keys")
56
57 # Bundle into working memory
58 working_memory = model.bundle([items["book"], items["pen"], items["coffee"],
59 items["phone"], items["keys"]])
60
61 print("\nWorking memory: bundled all 5 items")
62
63 # Query: Is "book" in working memory?
64 print("\n" + "=" * 70)
65 print("Query: Check if items are in working memory")
66 print("=" * 70)
67
68 print("\nSimilarity to active items:")
69 for name, vec in items.items():
70 sim = float(model.similarity(working_memory, vec))
71 print(f" {name:10s}: {sim:.3f}")
72
73 # Check items NOT in working memory
74 print("\nSimilarity to inactive items:")
75 laptop = model.random(seed=200)
76 mouse = model.random(seed=201)
77 print(f" laptop: {float(model.similarity(working_memory, laptop)):.3f}")
78 print(f" mouse: {float(model.similarity(working_memory, mouse)):.3f}")
79
80 print("\nKey observation:")
81 print(" - Active items have high similarity (~0.45)")
82 print(" - Inactive items have low similarity (~0)")
83 print(" - Working memory acts as distributed content-addressable store")
84
85 # ============================================================================
86 # Demo 2: Cleanup with BruteForce Strategy
87 # ============================================================================
88 print("\n" + "=" * 70)
89 print("Demo 2: BruteForce Cleanup Strategy")
90 print("=" * 70)
91
92 # Create noisy query by adding noise
93 print("\nSimulating noisy query:")
94 print(" Original: book")
95
96 # Add noise to book vector
97 noise = model.random(seed=999)
98 noisy_book = model.bundle([items["book"], noise]) # 50% book, 50% noise
99
100 print(f" Similarity noisy_book → book: {float(model.similarity(noisy_book, items['book'])):.3f}")
101 print(f" (Pure book would be 1.000, random would be ~0)")
102
103 # Create codebook for cleanup
104 codebook = Codebook(items, backend=model.backend)
105
106 # BruteForce cleanup: find nearest neighbor
107 print("\n" + "=" * 70)
108 print("Cleanup with BruteForce (nearest neighbor)")
109 print("=" * 70)
110
111 cleanup_bf = BruteForceCleanup()
112 labels, sims = cleanup_bf.factorize(noisy_book, items, model, n_factors=3)
113
114 print("\nTop 3 matches:")
115 for i, (label, sim) in enumerate(zip(labels, sims), 1):
116 marker = " ← Correct!" if label == "book" else ""
117 print(f" {i}. {label:10s}: {sim:.3f}{marker}")
118
119 print("\nKey observation:")
120 print(" - BruteForce finds nearest neighbor via exhaustive search")
121 print(" - Correct even with significant noise")
122 print(" - Fast for small codebooks, O(N) complexity")
123
124 # ============================================================================
125 # Demo 3: Resonator Cleanup Strategy
126 # ============================================================================
127 print("\n" + "=" * 70)
128 print("Demo 3: Resonator Cleanup Strategy")
129 print("=" * 70)
130
131 # Resonator uses iterative refinement
132 print("\nResonator cleanup (iterative refinement):")
133
134 cleanup_res = ResonatorCleanup()
135
136 # Create moderately noisy query
137 noise_moderate = model.random(seed=888)
138 noisy_pen = model.bundle([items["pen"], items["pen"], items["pen"], noise_moderate]) # 75% pen
139
140 print(f" Starting similarity to pen: {float(model.similarity(noisy_pen, items['pen'])):.3f}")
141
142 labels_res, sims_res = cleanup_res.factorize(noisy_pen, items, model, n_factors=1,
143 max_iterations=10, threshold=0.99)
144
145 print(f"\nResonator result:")
146 print(f" Best match: {labels_res[0]} (similarity={sims_res[0]:.3f})")
147
148 print("\nKey observation:")
149 print(" - Resonator iteratively refines noisy input")
150 print(" - Uses codebook to project onto valid subspace")
151 print(" - More robust to noise than single nearest-neighbor")
152
153 # ============================================================================
154 # Demo 4: Multi-Factor Unbinding - Decomposing Bundled Representations
155 # ============================================================================
156 print("\n" + "=" * 70)
157 print("Demo 4: Multi-Factor Unbinding")
158 print("=" * 70)
159
160 # Create bundle of multiple items
161 print("\nBundling 3 items:")
162 item1 = items["book"]
163 item2 = items["coffee"]
164 item3 = items["phone"]
165
166 bundle = model.bundle([item1, item2, item3])
167
168 print(" Bundle: book ⊕ coffee ⊕ phone")
169
170 # Factorize to recover all items
171 print("\n" + "=" * 70)
172 print("Factorizing bundle to recover all items:")
173 print("=" * 70)
174
175 labels_fact, sims_fact = cleanup_bf.factorize(bundle, items, model, n_factors=5)
176
177 print("\nRecovered factors:")
178 for i, (label, sim) in enumerate(zip(labels_fact, sims_fact), 1):
179 in_bundle = "✓" if label in ["book", "coffee", "phone"] else "✗"
180 print(f" {i}. {label:10s}: {sim:.3f} [{in_bundle}]")
181
182 print("\nKey observation:")
183 print(" - Factorization recovers multiple bundled items")
184 print(" - Top factors are the original bundled items")
185 print(" - Similarity degrades but items still identifiable")
186
187 # ============================================================================
188 # Demo 5: Capacity Limits - How Many Items Can Working Memory Hold?
189 # ============================================================================
190 print("\n" + "=" * 70)
191 print("Demo 5: Working Memory Capacity Limits")
192 print("=" * 70)
193
194 # Test with increasing bundle sizes
195 print("\nTesting working memory capacity:")
196
197 # Create larger item set
198 large_items = {}
199 for i in range(20):
200 large_items[f"item_{i}"] = model.random(seed=300 + i)
201
202 # Test different bundle sizes
203 bundle_sizes = [3, 5, 7, 10, 15]
204
205 print("\nRecovery accuracy vs. bundle size:")
206 print("Size | Top-1 | Top-3 | Top-5")
207 print("-" * 40)
208
209 for size in bundle_sizes:
210 # Bundle first 'size' items
211 bundled_items = [large_items[f"item_{i}"] for i in range(size)]
212 bundle_test = model.bundle(bundled_items)
213
214 # Factorize to recover
215 recovered, _ = cleanup_bf.factorize(bundle_test, large_items, model, n_factors=5)
216
217 # Count correct in top-k
218 correct_labels = {f"item_{i}" for i in range(size)}
219 top1_correct = 1 if recovered[0] in correct_labels else 0
220 top3_correct = sum(1 for r in recovered[:3] if r in correct_labels) / min(3, size)
221 top5_correct = sum(1 for r in recovered[:5] if r in correct_labels) / min(5, size)
222
223 print(f" {size:2d} | {top1_correct:5.2f} | {top3_correct:5.2f} | {top5_correct:5.2f}")
224
225 print("\nKey observation:")
226 print(" - Accuracy degrades with bundle size")
227 print(" - Working memory capacity: ~5-7 items (like human WM!)")
228 print(" - Distributed representation naturally limits capacity")
229
230 # ============================================================================
231 # Demo 6: Structured Working Memory - Role-Filler Binding
232 # ============================================================================
233 print("\n" + "=" * 70)
234 print("Demo 6: Structured Working Memory")
235 print("=" * 70)
236
237 # Working memory with structure: current task context
238 print("\nEncoding task context:")
239 print(" Task: 'Write email to Bob about meeting'")
240
241 # Define roles
242 task_type = model.random(seed=400)
243 recipient = model.random(seed=401)
244 topic = model.random(seed=402)
245
246 # Define fillers
247 write_email = model.random(seed=500)
248 bob_entity = model.random(seed=501)
249 meeting_topic = model.random(seed=502)
250
251 # Create structured working memory
252 wm_structure = model.bundle([
253 model.bind(task_type, write_email),
254 model.bind(recipient, bob_entity),
255 model.bind(topic, meeting_topic)
256 ])
257
258 print("\nRole-filler bindings in working memory:")
259 print(" task_type ⊗ write_email")
260 print(" recipient ⊗ bob")
261 print(" topic ⊗ meeting")
262
263 # Query roles
264 print("\n" + "=" * 70)
265 print("Querying working memory by role:")
266 print("=" * 70)
267
268 # What is the task type?
269 task_query = model.unbind(wm_structure, task_type)
270 print(f"\nWhat is the task?")
271 print(f" Similarity to write_email: {float(model.similarity(task_query, write_email)):.3f} ← Match")
272
273 # Who is the recipient?
274 recip_query = model.unbind(wm_structure, recipient)
275 print(f"\nWho is the recipient?")
276 print(f" Similarity to bob: {float(model.similarity(recip_query, bob_entity)):.3f} ← Match")
277
278 # What is the topic?
279 topic_query = model.unbind(wm_structure, topic)
280 print(f"\nWhat is the topic?")
281 print(f" Similarity to meeting: {float(model.similarity(topic_query, meeting_topic)):.3f} ← Match")
282
283 print("\nKey observation:")
284 print(" - Structured working memory supports role-based queries")
285 print(" - Combines bundling (multiple bindings) with binding (role-filler)")
286 print(" - Enables cognitive architectures with WM component")
287
288 # ============================================================================
289 # Demo 7: Working Memory with ItemStore
290 # ============================================================================
291 print("\n" + "=" * 70)
292 print("Demo 7: Working Memory with ItemStore")
293 print("=" * 70)
294
295 # Create item store for semantic memory (long-term)
296 print("\nBuilding semantic memory (long-term store):")
297
298 semantic_memory = ItemStore(model)
299 semantic_memory.add("alice", model.random(seed=600))
300 semantic_memory.add("bob", model.random(seed=601))
301 semantic_memory.add("charlie", model.random(seed=602))
302 semantic_memory.add("email", model.random(seed=603))
303 semantic_memory.add("meeting", model.random(seed=604))
304 semantic_memory.add("document", model.random(seed=605))
305
306 print(" Stored: alice, bob, charlie, email, meeting, document")
307
308 # Working memory holds current focus
309 print("\nWorking memory (current focus):")
310 wm_current = model.bundle([
311 semantic_memory.codebook._items["bob"],
312 semantic_memory.codebook._items["meeting"]
313 ])
314
315 print(" Active: bob, meeting")
316
317 # Query: What's in working memory?
318 print("\n" + "=" * 70)
319 print("Retrieving from working memory:")
320 print("=" * 70)
321
322 # Use ItemStore to query working memory
323 results = semantic_memory.query(wm_current, k=6)
324
325 print("\nTop matches in semantic memory:")
326 for i, (label, sim) in enumerate(results, 1):
327 in_wm = "✓" if label in ["bob", "meeting"] else " "
328 print(f" {i}. {label:10s}: {sim:.3f} [{in_wm}]")
329
330 print("\nKey observation:")
331 print(" - ItemStore enables fast retrieval from semantic memory")
332 print(" - Working memory acts as query to semantic memory")
333 print(" - Models interaction between WM and long-term memory")
334
335 # ============================================================================
336 # Summary
337 # ============================================================================
338 print("\n" + "=" * 70)
339 print("Summary: Working Memory Key Takeaways")
340 print("=" * 70)
341 print()
342 print("✓ Working memory: Bundled representation of active items")
343 print("✓ Content-addressable: Query by similarity, not address")
344 print("✓ Cleanup strategies: Recover clean symbols from noise")
345 print("✓ BruteForce: Fast O(N) nearest-neighbor search")
346 print("✓ Resonator: Iterative refinement for robustness")
347 print("✓ Multi-factor unbinding: Decompose bundled items")
348 print("✓ Capacity limits: ~5-7 items (mirrors human WM!)")
349 print()
350 print("Cleanup strategy comparison:")
351 print(" BruteForce:")
352 print(" - Fast for small codebooks (O(N))")
353 print(" - Single nearest-neighbor lookup")
354 print(" - Good for clean or moderately noisy queries")
355 print()
356 print(" Resonator:")
357 print(" - Iterative refinement (O(k*N), k iterations)")
358 print(" - Projects onto valid subspace using codebook")
359 print(" - Robust to higher noise levels")
360 print(" - Can recover from very corrupted inputs")
361 print()
362 print("Working memory applications:")
363 print(" - Cognitive architectures: Active information maintenance")
364 print(" - Attention mechanisms: Focus on relevant items")
365 print(" - Task context: Maintain current goals and parameters")
366 print(" - Short-term buffers: Temporary storage before consolidation")
367 print()
368 print("Next steps:")
369 print(" → 27_cleanup_strategies.py - Detailed cleanup comparison")
370 print(" → 28_factorization_methods.py - Advanced unbinding techniques")
371 print(" → 25_app_integration_patterns.py - Integrate WM in larger systems")
372 print()
373 print("=" * 70)