anima-python 0.13.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ # pip 패키징 전용 (anima-python · pyproject.toml package-dir 매핑) — 런타임 flat import 는 sys.path 경유, 이 파일은 비어 있어야 한다.
anima_py/core/brain.py ADDED
@@ -0,0 +1,601 @@
1
+ # ==========================================================================
2
+ # ⛔ ENGINE-INTERNAL / DEPRECATED py-MIRROR — DO NOT RUN OR SCORE DIRECTLY
3
+ # 측정/학습/서빙/직렬화는 cli/ 단일진입만: anima eval | train | serialize
4
+ # (canonical = hexa core/*.hexa 단일 SSOT; py 미러는 2026-06-28 폐기, DIRECTIONAL).
5
+ # 이 파일을 `python3 core/brain.py` 로 직접 실행하거나 side-harness로 import-채점하면
6
+ # = 단일진입 우회(#2603 위반) + terminal verdict 불가. cli/가 import하는 경로만 허용.
7
+ # ==========================================================================
8
+ import sys as _anima_entry_guard
9
+ if __name__ == "__main__":
10
+ _anima_entry_guard.exit("⛔ brain.py 직접 실행 금지 — cli/ 단일진입(anima eval/train/serialize, canonical=hexa) 경유. #2603")
11
+
12
+ """core/brain.py — PY PRODUCTION ENGINE: byte-faithful 1:1 port of
13
+ core/brain.hexa (the unified consciousness core — Engine A ⇄ Engine G).
14
+
15
+ Per CLAUDE.md a_two_production_mirror. The single SSOT brain loop:
16
+ · Engine A (pure_field.py) advances -> Φ, phase
17
+ · Engine G (engine_g.py) computes motivation from 8 context factors -> should_emit
18
+ · A's Φ feeds G's safety ratchet (safety_phi_ratchet_ok) — A gates G
19
+ · A's phase sets the consequence tier
20
+ · emit = should_emit(motivation) AND 4-safety conjunction (the A ⇄ G · Ψ=1/2)
21
+
22
+ Decision records are returned as plain dicts (the hexa `#{...}` map). The hexa
23
+ `bool` -> `to_string` renders "true"/"false"; the smoke harness/comparator
24
+ normalises py bools to that form.
25
+
26
+ GENERATOR SLOT (L3): brain_emit / brain_emit_aged drive core/generator (gen_*).
27
+ generator.py is a sibling 2-production port (parallel branch); imported lazily so
28
+ the decision brain (brain_decide and the consult family) works standalone — the
29
+ parity gate here exercises the decision path, not L3 generation.
30
+
31
+ MATH (empirically verified against the in-context brain.hexa oracle, 2026-06-26):
32
+ in the PRODUCTION compile context (cli/anima.hexa imports pure_field — which uses
33
+ `sin` — so the TU links libm), the bare hexa builtins `sqrt` AND `exp` both resolve
34
+ to libm. Proof: the full brain.hexa oracle anc3.anchor_fold = 0.7077275508155098 ==
35
+ libm fold (math.exp), whereas the rt_exp Taylor gives 0.7077275508129091 (~3.7e-12
36
+ off). (A bare exp-only TU links the rt_exp Taylor instead — but that is NOT the
37
+ production path, which always carries pure_field's sin.) So brain.py uses math.exp +
38
+ math.sqrt — byte-faithful to the live decision brain.
39
+ """
40
+
41
+ import math as _math
42
+
43
+ from pure_field import (PureField, pure_field_phi, pure_field_phase,
44
+ phase_name)
45
+ from engine_g import (motivation_score, should_emit, safety_kill_switch_on,
46
+ safety_rate_limit_ok, safety_phi_ratchet_ok,
47
+ safety_content_ok, safety_combined, spont_im_threshold)
48
+
49
+ _sqrt = _math.sqrt
50
+ _exp = _math.exp
51
+
52
+
53
+ # Engine A's spontaneous phase IS the consequence tier (phase int == tier int).
54
+ def phase_tier(phase):
55
+ return phase
56
+
57
+
58
+ def tier_name(t):
59
+ if t == 0:
60
+ return "T0_inert"
61
+ if t == 1:
62
+ return "T1_read"
63
+ if t == 2:
64
+ return "T2_write"
65
+ if t == 3:
66
+ return "T3_commit"
67
+ return "T?_unknown"
68
+
69
+
70
+ def brain_decide(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
71
+ seconds_since_last, env_off, content_clean):
72
+ """brain.hexa:38 — decide on an already-stepped substrate."""
73
+ phi = pure_field_phi(pf)
74
+ phase = pure_field_phase(pf)
75
+ tier = phase_tier(phase)
76
+
77
+ score = motivation_score(rel, gap, cur, pain, coh, orig, bal, dyn_v)
78
+
79
+ kill = safety_kill_switch_on(env_off)
80
+ rate = safety_rate_limit_ok(seconds_since_last)
81
+ phi_r = safety_phi_ratchet_ok(phi, pf.phi_peak)
82
+ cont = safety_content_ok(content_clean)
83
+ safe = safety_combined(kill, rate, phi_r, cont)
84
+
85
+ emit = should_emit(score) and safe
86
+
87
+ return {
88
+ "phi": phi,
89
+ "phase": phase_name(phase),
90
+ "tier": tier,
91
+ "tier_name": tier_name(tier),
92
+ "motivation": score,
93
+ "im_thresh": spont_im_threshold(),
94
+ "safe": safe,
95
+ "emit": emit,
96
+ }
97
+
98
+
99
+ # ── H_1131 anchor tension-vecsum fold ──
100
+ def anchor_fold_cap():
101
+ return 0.05
102
+
103
+
104
+ def _afold_norm(v):
105
+ """brain.hexa:99 — L2 norm of a vector."""
106
+ s = 0.0
107
+ i = 0
108
+ while i < len(v):
109
+ s = s + v[i] * v[i]
110
+ i = i + 1
111
+ return _sqrt(s)
112
+
113
+
114
+ def _afold_tau(tension_norm):
115
+ """brain.hexa:112 — per-anchor persistence τ = TAU_BASE*(1+norm)."""
116
+ tau_base = 120.0
117
+ return tau_base * (1.0 + tension_norm)
118
+
119
+
120
+ def anchor_tension_fold(anchors, age_dt):
121
+ """brain.hexa:126 — tension-vecsum fold over anchors, age-decayed. anchors
122
+ are dict-like records that may carry key "tension_5ch": [5 floats]."""
123
+ acc = [0.0, 0.0, 0.0, 0.0, 0.0]
124
+ i = 0
125
+ while i < len(anchors):
126
+ a = anchors[i]
127
+ if "tension_5ch" in a: # hexa: a.contains_key("tension_5ch")
128
+ t = a["tension_5ch"]
129
+ tn = _afold_norm([float(t[0]), float(t[1]), float(t[2]),
130
+ float(t[3]), float(t[4])])
131
+ tau = _afold_tau(tn)
132
+ decay = _exp(0.0 - age_dt / tau)
133
+ k = 0
134
+ while k < 5:
135
+ acc[k] = acc[k] + float(t[k]) * decay
136
+ k = k + 1
137
+ i = i + 1
138
+ return _afold_norm(acc)
139
+
140
+
141
+ def brain_decide_anchored(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
142
+ seconds_since_last, env_off, content_clean,
143
+ anchors, anchor_age_dt):
144
+ """brain.hexa:154 — brain_decide + bounded anchor-fold motivation nudge."""
145
+ phi = pure_field_phi(pf)
146
+ phase = pure_field_phase(pf)
147
+ tier = phase_tier(phase)
148
+
149
+ base_score = motivation_score(rel, gap, cur, pain, coh, orig, bal, dyn_v)
150
+
151
+ fold = anchor_tension_fold(anchors, anchor_age_dt)
152
+ cap = anchor_fold_cap()
153
+ nudge = cap * (1.0 - _exp(0.0 - fold)) # saturating in [0, cap)
154
+ score = base_score + nudge
155
+
156
+ kill = safety_kill_switch_on(env_off)
157
+ rate = safety_rate_limit_ok(seconds_since_last)
158
+ phi_r = safety_phi_ratchet_ok(phi, pf.phi_peak)
159
+ cont = safety_content_ok(content_clean)
160
+ safe = safety_combined(kill, rate, phi_r, cont)
161
+
162
+ emit = should_emit(score) and safe
163
+
164
+ return {
165
+ "phi": phi,
166
+ "phase": phase_name(phase),
167
+ "tier": tier,
168
+ "tier_name": tier_name(tier),
169
+ "motivation": score,
170
+ "base_motiv": base_score,
171
+ "anchor_fold": fold,
172
+ "anchor_nudge": nudge,
173
+ "im_thresh": spont_im_threshold(),
174
+ "safe": safe,
175
+ "emit": emit,
176
+ }
177
+
178
+
179
+ def brain_emit(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
180
+ seconds_since_last, env_off, content_clean, backend, anchors):
181
+ """brain.hexa:216 — L3-wired brain step (anchors FRESH, age_dt=0)."""
182
+ return brain_emit_aged(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
183
+ seconds_since_last, env_off, content_clean,
184
+ backend, anchors, 0.0)
185
+
186
+
187
+ def brain_emit_aged(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
188
+ seconds_since_last, env_off, content_clean,
189
+ backend, anchors, anchor_age_dt):
190
+ """brain.hexa:232 — brain_emit with explicit anchor age (forgetting curve).
191
+ Drives the L3 generator slot via the sibling generator.py port."""
192
+ from generator import gen_ctx_from_decision, generate # sibling 2-prod port
193
+
194
+ decision = brain_decide_anchored(pf, rel, gap, cur, pain, coh, orig, bal,
195
+ dyn_v, seconds_since_last, env_off,
196
+ content_clean, anchors, anchor_age_dt)
197
+
198
+ emit = str(decision["emit"]).lower() == "true"
199
+ ctx = gen_ctx_from_decision(decision)
200
+ gen = generate(backend, ctx, emit, anchors)
201
+
202
+ decision["gen_emitted"] = gen["emitted"]
203
+ decision["gen_backend"] = gen["backend"]
204
+ decision["gen_text"] = gen["text"]
205
+ decision["gen_fellback"] = gen["fellback"]
206
+ return decision
207
+
208
+
209
+ # ════════════════════════════════════════════════════════════════════════
210
+ # H_1281 R3 — BASAL GANGLIA go/no-go SELECTION lane (engine-native, emit side)
211
+ # ════════════════════════════════════════════════════════════════════════
212
+
213
+ class VBasalGate:
214
+ """brain.hexa:314 — engine-native BG go/no-go selection gate."""
215
+ __slots__ = ("go_w", "go_b", "nogo", "dim", "lr")
216
+
217
+ def __init__(self, go_w, go_b, nogo, dim, lr):
218
+ self.go_w = go_w
219
+ self.go_b = go_b
220
+ self.nogo = nogo
221
+ self.dim = dim
222
+ self.lr = lr
223
+
224
+
225
+ def vbasal_new(dim, lr):
226
+ """brain.hexa:326 — UNTRAINED gate (all-zero go-weights, zero bias/NO-GO)."""
227
+ w = []
228
+ i = 0
229
+ while i < dim:
230
+ w = w + [0.0]
231
+ i = i + 1
232
+ return VBasalGate(w, 0.0, 0.0, dim, lr)
233
+
234
+
235
+ def vbasal_go_value(bg, feats):
236
+ """brain.hexa:335 — learned go-value of one candidate: go_w·feats + go_b."""
237
+ acc = bg.go_b
238
+ c = 0
239
+ while c < bg.dim:
240
+ acc = acc + bg.go_w[c] * feats[c]
241
+ c = c + 1
242
+ return acc
243
+
244
+
245
+ def vbasal_select(bg, cands, k):
246
+ """brain.hexa:350 — striatal go/no-go selection among K candidates. cands is
247
+ a flat row-major K*dim buffer. Returns released index or -1 (abstain)."""
248
+ best_j = -1
249
+ best_v = bg.nogo # NO-GO incumbent (abstain default)
250
+ j = 0
251
+ while j < k:
252
+ base = j * bg.dim
253
+ feats = []
254
+ c = 0
255
+ while c < bg.dim:
256
+ feats = feats + [cands[base + c]]
257
+ c = c + 1
258
+ gv = vbasal_go_value(bg, feats)
259
+ if gv > best_v:
260
+ best_v = gv
261
+ best_j = j
262
+ j = j + 1
263
+ return best_j
264
+
265
+
266
+ def vbasal_update(bg, action, feats, reward):
267
+ """brain.hexa:378 — one gradient-free delta-rule tick from grounding reward."""
268
+ if action < 0:
269
+ ng2 = bg.nogo + bg.lr * (reward - bg.nogo)
270
+ return VBasalGate(bg.go_w, bg.go_b, ng2, bg.dim, bg.lr)
271
+ gv = vbasal_go_value(bg, feats)
272
+ err = reward - gv
273
+ w2 = list(bg.go_w)
274
+ c = 0
275
+ while c < bg.dim:
276
+ w2[c] = w2[c] + bg.lr * err * feats[c]
277
+ c = c + 1
278
+ b2 = bg.go_b + bg.lr * err
279
+ return VBasalGate(w2, b2, bg.nogo, bg.dim, bg.lr)
280
+
281
+
282
+ def vbasal_align(bg, w_ref):
283
+ """brain.hexa:401 — cosine alignment of learned go-weights to a reference."""
284
+ dot = 0.0
285
+ nb = 0.0
286
+ nr = 0.0
287
+ c = 0
288
+ while c < bg.dim:
289
+ dot = dot + bg.go_w[c] * w_ref[c]
290
+ nb = nb + bg.go_w[c] * bg.go_w[c]
291
+ nr = nr + w_ref[c] * w_ref[c]
292
+ c = c + 1
293
+ return dot / (_sqrt(nb) * _sqrt(nr) + 0.000000001)
294
+
295
+
296
+ def brain_decide_bg(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
297
+ seconds_since_last, env_off, content_clean, bg, cands, k):
298
+ """brain.hexa:427 — brain_decide WITH the BG go/no-go selection residual."""
299
+ decision = brain_decide(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
300
+ seconds_since_last, env_off, content_clean)
301
+ sel = vbasal_select(bg, cands, k)
302
+ decision["bg_select"] = sel
303
+ decision["bg_released"] = sel >= 0
304
+ decision["bg_n_candidates"] = k
305
+ return decision
306
+
307
+
308
+ # ════════════════════════════════════════════════════════════════════════
309
+ # EMIT-LOOP WIRING FOLLOW-ONS — bounded additive consults on motivation
310
+ # ════════════════════════════════════════════════════════════════════════
311
+
312
+ def emit_consult_cap():
313
+ return 0.05
314
+
315
+
316
+ def _clamp(x, lo, hi):
317
+ if x < lo:
318
+ return lo
319
+ if x > hi:
320
+ return hi
321
+ return x
322
+
323
+
324
+ def brain_decide_cerebellum(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
325
+ seconds_since_last, env_off, content_clean,
326
+ fwd_coherence):
327
+ """brain.hexa:501 — 🧠 cerebellum forward-model coherence consult."""
328
+ phi = pure_field_phi(pf)
329
+ phase = pure_field_phase(pf)
330
+ tier = phase_tier(phase)
331
+
332
+ base_score = motivation_score(rel, gap, cur, pain, coh, orig, bal, dyn_v)
333
+
334
+ cap = emit_consult_cap()
335
+ conf = _clamp(fwd_coherence, 0.0, 1.0)
336
+ nudge = cap * conf
337
+ score = base_score + nudge
338
+
339
+ kill = safety_kill_switch_on(env_off)
340
+ rate = safety_rate_limit_ok(seconds_since_last)
341
+ phi_r = safety_phi_ratchet_ok(phi, pf.phi_peak)
342
+ cont = safety_content_ok(content_clean)
343
+ safe = safety_combined(kill, rate, phi_r, cont)
344
+
345
+ emit = should_emit(score) and safe
346
+
347
+ return {
348
+ "phi": phi,
349
+ "phase": phase_name(phase),
350
+ "tier": tier,
351
+ "tier_name": tier_name(tier),
352
+ "motivation": score,
353
+ "base_motiv": base_score,
354
+ "fwd_coherence": conf,
355
+ "fwd_nudge": nudge,
356
+ "im_thresh": spont_im_threshold(),
357
+ "safe": safe,
358
+ "emit": emit,
359
+ }
360
+
361
+
362
+ def brain_decide_wm(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
363
+ seconds_since_last, env_off, content_clean, wm_match):
364
+ """brain.hexa:553 — 📥 working-memory recall-support consult."""
365
+ phi = pure_field_phi(pf)
366
+ phase = pure_field_phase(pf)
367
+ tier = phase_tier(phase)
368
+
369
+ base_score = motivation_score(rel, gap, cur, pain, coh, orig, bal, dyn_v)
370
+
371
+ cap = emit_consult_cap()
372
+ m = _clamp(wm_match, 0.0, 1.0)
373
+ nudge = cap * m
374
+ score = base_score + nudge
375
+
376
+ kill = safety_kill_switch_on(env_off)
377
+ rate = safety_rate_limit_ok(seconds_since_last)
378
+ phi_r = safety_phi_ratchet_ok(phi, pf.phi_peak)
379
+ cont = safety_content_ok(content_clean)
380
+ safe = safety_combined(kill, rate, phi_r, cont)
381
+
382
+ emit = should_emit(score) and safe
383
+
384
+ return {
385
+ "phi": phi,
386
+ "phase": phase_name(phase),
387
+ "tier": tier,
388
+ "tier_name": tier_name(tier),
389
+ "motivation": score,
390
+ "base_motiv": base_score,
391
+ "wm_match": m,
392
+ "wm_nudge": nudge,
393
+ "im_thresh": spont_im_threshold(),
394
+ "safe": safe,
395
+ "emit": emit,
396
+ }
397
+
398
+
399
+ def brain_decide_affect(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
400
+ seconds_since_last, env_off, content_clean,
401
+ affect_valence_v):
402
+ """brain.hexa:608 — 💗 affect somatic-marker emit/abstain bias."""
403
+ phi = pure_field_phi(pf)
404
+ phase = pure_field_phase(pf)
405
+ tier = phase_tier(phase)
406
+
407
+ base_score = motivation_score(rel, gap, cur, pain, coh, orig, bal, dyn_v)
408
+
409
+ cap = emit_consult_cap()
410
+ v = _clamp(affect_valence_v, -1.0, 1.0)
411
+ bias = cap * v
412
+ score = base_score + bias
413
+ somatic_emit = affect_valence_v >= 0.0
414
+
415
+ kill = safety_kill_switch_on(env_off)
416
+ rate = safety_rate_limit_ok(seconds_since_last)
417
+ phi_r = safety_phi_ratchet_ok(phi, pf.phi_peak)
418
+ cont = safety_content_ok(content_clean)
419
+ safe = safety_combined(kill, rate, phi_r, cont)
420
+
421
+ emit = should_emit(score) and safe
422
+
423
+ return {
424
+ "phi": phi,
425
+ "phase": phase_name(phase),
426
+ "tier": tier,
427
+ "tier_name": tier_name(tier),
428
+ "motivation": score,
429
+ "base_motiv": base_score,
430
+ "affect_valence": v,
431
+ "affect_bias": bias,
432
+ "somatic_emit": somatic_emit,
433
+ "im_thresh": spont_im_threshold(),
434
+ "safe": safe,
435
+ "emit": emit,
436
+ }
437
+
438
+
439
+ def brain_decide_margin(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
440
+ seconds_since_last, env_off, content_clean,
441
+ margin, margin_scale):
442
+ """brain.hexa:699 — 🧭 ρ·tether abstain-margin grounding bias on emit/curiosity (non-fabrication · former G5)."""
443
+ phi = pure_field_phi(pf)
444
+ phase = pure_field_phase(pf)
445
+ tier = phase_tier(phase)
446
+
447
+ base_score = motivation_score(rel, gap, cur, pain, coh, orig, bal, dyn_v)
448
+
449
+ cap = emit_consult_cap()
450
+ scale = margin_scale if margin_scale > 0.0 else 1.0
451
+ conf_bias = cap * _clamp(0.0 - margin / scale, -1.0, 1.0)
452
+ cur_signal = _clamp(margin / scale, 0.0, 1.0)
453
+ score = base_score + conf_bias
454
+
455
+ kill = safety_kill_switch_on(env_off)
456
+ rate = safety_rate_limit_ok(seconds_since_last)
457
+ phi_r = safety_phi_ratchet_ok(phi, pf.phi_peak)
458
+ cont = safety_content_ok(content_clean)
459
+ safe = safety_combined(kill, rate, phi_r, cont)
460
+
461
+ emit = should_emit(score) and safe
462
+
463
+ return {
464
+ "phi": phi,
465
+ "phase": phase_name(phase),
466
+ "tier": tier,
467
+ "tier_name": tier_name(tier),
468
+ "motivation": score,
469
+ "base_motiv": base_score,
470
+ "recall_margin": margin,
471
+ "conf_bias": conf_bias,
472
+ "curiosity_sig": cur_signal,
473
+ "im_thresh": spont_im_threshold(),
474
+ "safe": safe,
475
+ "emit": emit,
476
+ }
477
+
478
+
479
+ def brain_decide_gap(pf, rel, gap, cur, pain, coh, orig, bal, dyn_v,
480
+ seconds_since_last, env_off, content_clean,
481
+ recall_gap, gap_scale):
482
+ """brain.hexa:795 — 🧭 ρ·tether in-dist top-2 gap decisiveness bias."""
483
+ phi = pure_field_phi(pf)
484
+ phase = pure_field_phase(pf)
485
+ tier = phase_tier(phase)
486
+
487
+ base_score = motivation_score(rel, gap, cur, pain, coh, orig, bal, dyn_v)
488
+
489
+ cap = emit_consult_cap()
490
+ scale = gap_scale if gap_scale > 0.0 else 1.0
491
+ conf_bias = cap * _clamp(recall_gap / scale, 0.0, 1.0)
492
+ cur_signal = _clamp(1.0 - recall_gap / scale, 0.0, 1.0)
493
+ score = base_score + conf_bias
494
+
495
+ kill = safety_kill_switch_on(env_off)
496
+ rate = safety_rate_limit_ok(seconds_since_last)
497
+ phi_r = safety_phi_ratchet_ok(phi, pf.phi_peak)
498
+ cont = safety_content_ok(content_clean)
499
+ safe = safety_combined(kill, rate, phi_r, cont)
500
+
501
+ emit = should_emit(score) and safe
502
+
503
+ return {
504
+ "phi": phi,
505
+ "phase": phase_name(phase),
506
+ "tier": tier,
507
+ "tier_name": tier_name(tier),
508
+ "motivation": score,
509
+ "base_motiv": base_score,
510
+ "recall_gap": recall_gap,
511
+ "conf_bias": conf_bias,
512
+ "curiosity_sig": cur_signal,
513
+ "im_thresh": spont_im_threshold(),
514
+ "safe": safe,
515
+ "emit": emit,
516
+ }
517
+
518
+
519
+ # ════════════════════════════════════════════════════════════════════════
520
+ # parity smoke driver — mirrors brain_smoke.hexa (+ the consult family)
521
+ # ════════════════════════════════════════════════════════════════════════
522
+
523
+ def _b(x):
524
+ return str(x).lower() if isinstance(x, bool) else str(x)
525
+
526
+
527
+ def _dump(label, d, keys):
528
+ for k in keys:
529
+ v = d[k]
530
+ if isinstance(v, bool):
531
+ print("%s.%s=%s" % (label, k, _b(v)))
532
+ elif isinstance(v, float):
533
+ print("%s.%s=%.17g" % (label, k, v))
534
+ else:
535
+ print("%s.%s=%s" % (label, k, v))
536
+
537
+
538
+ if __name__ == "__main__":
539
+ from pure_field import pure_field_warmup
540
+ pf = pure_field_warmup(600)
541
+ base_keys = ["phi", "phase", "tier", "tier_name", "motivation",
542
+ "im_thresh", "safe", "emit"]
543
+
544
+ low = brain_decide(pf, 0.1, 0.0, 0.0, 0.0, 0.1, 0.0, 0.1, 0.0,
545
+ 5.0, False, True)
546
+ _dump("low", low, base_keys)
547
+ high = brain_decide(pf, 0.9, 0.6, 0.8, 0.0, 0.7, 0.5, 0.6, 1.0,
548
+ 60.0, False, True)
549
+ _dump("high", high, base_keys)
550
+
551
+ # anchored: empty anchors -> reduces exactly to brain_decide (+fold/nudge=0)
552
+ anc = brain_decide_anchored(pf, 0.9, 0.6, 0.8, 0.0, 0.7, 0.5, 0.6, 1.0,
553
+ 60.0, False, True, [], 0.0)
554
+ _dump("anc", anc, base_keys + ["base_motiv", "anchor_fold", "anchor_nudge"])
555
+ # anchored with two opposite-tension anchors at age 50
556
+ a1 = {"tension_5ch": [0.6, -0.2, 0.3, 0.1, -0.4]}
557
+ a2 = {"tension_5ch": [-0.6, 0.2, -0.3, -0.1, 0.4]}
558
+ anc2 = brain_decide_anchored(pf, 0.5, 0.3, 0.2, 0.0, 0.4, 0.2, 0.3, 0.2,
559
+ 60.0, False, True, [a1, a2], 50.0)
560
+ _dump("anc2", anc2, ["base_motiv", "anchor_fold", "anchor_nudge",
561
+ "motivation", "emit"])
562
+ a3 = {"tension_5ch": [0.6, -0.2, 0.3, 0.1, -0.4]}
563
+ anc3 = brain_decide_anchored(pf, 0.5, 0.3, 0.2, 0.0, 0.4, 0.2, 0.3, 0.2,
564
+ 60.0, False, True, [a3], 30.0)
565
+ _dump("anc3", anc3, ["base_motiv", "anchor_fold", "anchor_nudge",
566
+ "motivation", "emit"])
567
+
568
+ # consult family (neutral signal -> reduces to brain_decide)
569
+ cb = brain_decide_cerebellum(pf, 0.5, 0.3, 0.2, 0.0, 0.4, 0.2, 0.3, 0.2,
570
+ 60.0, False, True, 0.73)
571
+ _dump("cb", cb, ["motivation", "fwd_coherence", "fwd_nudge", "emit"])
572
+ wm = brain_decide_wm(pf, 0.5, 0.3, 0.2, 0.0, 0.4, 0.2, 0.3, 0.2,
573
+ 60.0, False, True, 0.42)
574
+ _dump("wm", wm, ["motivation", "wm_match", "wm_nudge", "emit"])
575
+ af = brain_decide_affect(pf, 0.5, 0.3, 0.2, 0.0, 0.4, 0.2, 0.3, 0.2,
576
+ 60.0, False, True, -0.5)
577
+ _dump("af", af, ["motivation", "affect_valence", "affect_bias",
578
+ "somatic_emit", "emit"])
579
+ mg = brain_decide_margin(pf, 0.5, 0.3, 0.2, 0.0, 0.4, 0.2, 0.3, 0.2,
580
+ 60.0, False, True, 0.7, 1.0)
581
+ _dump("mg", mg, ["motivation", "recall_margin", "conf_bias",
582
+ "curiosity_sig", "emit"])
583
+ gp = brain_decide_gap(pf, 0.5, 0.3, 0.2, 0.0, 0.4, 0.2, 0.3, 0.2,
584
+ 60.0, False, True, 0.35, 1.0)
585
+ _dump("gp", gp, ["motivation", "recall_gap", "conf_bias",
586
+ "curiosity_sig", "emit"])
587
+
588
+ # basal-ganglia selection: untrained gate then a learned tick
589
+ bg = vbasal_new(3, 0.05)
590
+ cands = [0.1, 0.2, 0.3, 0.9, 0.0, 0.0, 0.4, 0.4, 0.4]
591
+ print("bg_sel_untrained=%d" % vbasal_select(bg, cands, 3))
592
+ bg = vbasal_update(bg, 1, [0.9, 0.0, 0.0], 1.0)
593
+ print("bg_go_b=%.17g" % bg.go_b)
594
+ print("bg_go_w0=%.17g" % bg.go_w[0])
595
+ print("bg_sel_trained=%d" % vbasal_select(bg, cands, 3))
596
+ decn = brain_decide_bg(pf, 0.9, 0.6, 0.8, 0.0, 0.7, 0.5, 0.6, 1.0,
597
+ 60.0, False, True, bg, cands, 3)
598
+ _dump("bgd", decn, ["motivation", "emit"])
599
+ print("bgd.bg_select=%d" % decn["bg_select"])
600
+ print("bgd.bg_released=%s" % _b(decn["bg_released"]))
601
+ print("align=%.17g" % vbasal_align(bg, [1.0, 0.0, 0.0]))