Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions BENCHMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1255,3 +1255,10 @@ same-budget constant gain slower; s2 1.0000 to 1.0530 (was 1.0343 to 1.0719; 1.0
opening step no longer moves the stable phase); s3 1800/1800 lives within the caps and the opening step and (v, r, g) =
(0.15, 5, 1) within 0.0; s4 1800 gate strands, 0 verify failures, 0 third-copy and 0 split-copy differences, the planted
changes refused; the p0 part (n6, o5, s5, r5, l5) 5/5 and the P0 corpus 653 outputs, 0 differ.
0.10.1 development build, n10 and n11 (R126, the same command, `PROBBIT_THREADS=1`, load 12-30): with the forecast not
clipped at the cap and the k-th omission of a run cutting the success rate by 1 − k × rate (RFC 0002 A.3-A.4 as amended), n10
context at cap 0.8536 h a day at τ 0 and 0.0186 at τ 2 (−97.8 %; 0 overshoots; 100/100 seeds paid) and n11 report pursuits
1.22, 1.94, 1.59, 0, 0, 0, 0 (absent) and 0.19, 0.98, 1.45 (return days 8-10): both pass, neither bar changed; n1-n5, n7-n9,
o1-o4 14/14 with them; v1-v3, om1 4/4; p1a-p6 4/4; s1 0.5000-0.6667, s2 1.0343-1.0719, s3 1800/1800, s4 1800 strands 0 verify failures (as 0.10.0: the gate has no needs);
the p0 part 5/5 and the P0 corpus 653 outputs, 0 differ: 31/31. The nulls.py reference with the same rules:
n10 0.8493 → 0.0214, n11 0.92, 1.63, 1.61, 0, 0, 0, 0.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ All notable changes to this project are documented here. The format follows
opening step equals the fixed rate's (0.10.0 started at g = 2.5 and stepped 1.67× at the opening feedback, bench/body s3), and
`why` says `learning fast: feedback changed` only after a gate step whose volatility passed v0. `r_ref` above `r_max` is
refused (V-R2). Re-measured bars: PR body (bench/body s1-s4).
- Needs (docs/persona.md §2.12, RFC 0002 A.3-A.4): the pursue term's forecast is no longer clipped at the need's cap, and the
k-th omission in a run without a win multiplies the bound goal's success rate by max(1 − k × rate, 0) (state `needs.misses`,
only while a run is open). bench/body n10: context at cap 0.8536 → 0.0186 h a day at τ 2 (bar ≤ 0.5×); n11: 0 report pursuits
on away days 2-5, 1.58 a day before and 1.215 on return days 2-3 (bars 0, ≥ 0.5, ≥ 0.5); neither bar was changed.

### Other
- Development builds print `0.10.1-dev` (`probbit --version`, the strand header's `engine`, MCP `serverInfo`), so a
Expand Down
4 changes: 4 additions & 0 deletions bench/body/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,7 @@ choice is fixed here and frozen with the rest:
"constant gain 1 on a new learner" now equals the closed start by design (its opening step is the fixed rate's), so it is
replaced by "0.10.0's open start (r 1)", which s3 rejects. Pass values and bar text unchanged; MANIFEST regenerated for
`arith_0002.py`, `nulls.py` and `fixtures/bars.md`.
- **0.10.1, n10 and n11's rules** (R126, after the 0.10.0 engine measurement; engine rule changes, not bar repairs): the
pursue term's forecast is no longer clipped at the need's cap (x̂ = x + g_gr × forecast_per_hour × τ), and the k-th omission
in a run without a win multiplies the success rate by max(1 − k × rate, 0) (RFC 0002 A.3, A.4 as amended). `nulls.py`'s `RFCRule`
reference follows both rules. Pass values and bar text unchanged; MANIFEST regenerated for `nulls.py`.
2 changes: 1 addition & 1 deletion bench/body/fixtures/MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ b2b7dfac5be8699dfda4e9185f813eeead36265a27391b5a2849db37e90ba40e 622 fixtures/va
d87d6fc2c4a592e5e493a98ca13b6e58c001687feb955a0d09e55468cc8c37ee 5993 load.py
3706d65c888855dfaa2d0f7093c53de8368aa29d45da61b98728ec48154b710d 3998 manifest_check.py
b5a2f819f166993433c1f04799473371041807355a6cc0659e575c22a9637c4e 20981 needs.py
5f0ef18a5dce034cd2cc5cb3e0e9df9c8547617a57c5262b086df571891ee833 41077 nulls.py
1d207d56adc62f2e5061934fc4e1bf7f104a5937c7a9914ba312a95b819b3b54 41371 nulls.py
6f72609620c16db496256c958989618c9578690cf1591c6063d1ce582460a763 4389 p0.py
57d1e18fcb8528a8358272729958a5d2793d87d59eda6012edc409bc2c701363 10091 pace.py
372fd86d56fe6310c558b0b1254d35db1b7cca048fb0de2af630184a4af105b2 5705 replay.py
Expand Down
10 changes: 6 additions & 4 deletions bench/body/nulls.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,19 @@ class RFCRule(object):

def __init__(self, tau=0.0):
self.tau, self.s, self.win = tau, {"compact": 1.0, "report": 1.0}, {"compact": None, "report": None}
self.k = {"compact": 0, "report": 0} # 0.10.1: the run of omissions since the goal's last win

def __call__(self, t, q, x, sr, prev, answered, wins=None):
h = 0.0 if t == 0 else 1.0
for g, need in (("compact", "context"), ("report", "contact")):
if (wins or {}).get(need, 0) > 1e-12:
self.s[g], self.win[g] = R6(self.s[g] + 0.2 * (1 - self.s[g])), None
self.s[g], self.win[g], self.k[g] = R6(self.s[g] + 0.2 * (1 - self.s[g])), None, 0
elif self.win[g] is not None:
self.win[g] = max(self.win[g] - h, 0.0)
if self.win[g] <= 0:
self.s[g], self.win[g] = R6(self.s[g] * 0.8), None
xh = [min(x["context"] + 0.06 * self.tau, 1.0), x["contact"]]
if self.win[g] <= 0: # 0.10.1: the k-th omission in a run multiplies s by max(1 - k rate, 0)
self.k[g] += 1
self.s[g], self.win[g] = R6(self.s[g] * max(1 - 0.2 * self.k[g], 0.0)), None
xh = [x["context"] + 0.06 * self.tau, x["contact"]] # 0.10.1: the forecast is not clipped at the cap
term = {"compact": max(min(2 * self.s["compact"] * common.reduction(xh, 0, 0.6), 3.0), -3.0),
"report": max(min(2 * self.s["report"] * common.reduction(xh, 1, 0.8), 3.0), -3.0)}
f = needs.field(term)
Expand Down
11 changes: 9 additions & 2 deletions docs/persona.md
Original file line number Diff line number Diff line change
Expand Up @@ -628,11 +628,18 @@ only through D over the other needs, never in sign.
**The pursue term.** For each need, with s the bound goal's success rate and K its `amount`:

```
x^ = min(x + g_gr x forecast_per_hour x horizon_hours, cap) # the forecast: a declared growth rate
x^ = x + g_gr x forecast_per_hour x horizon_hours # the forecast: a declared growth rate, not clipped at cap
term = clip(want x s x (D(x^) - D(x^ with x^_j <- |x^_j - K|)), -cap, +cap) # cap = pursue.cap
s <- s + rate x (1 - s) on a win of the goal; s <- s x (1 - rate) when the goal's window fires (section 2.9)
s <- s + rate x (1 - s) on a win of the goal, and k <- 0
s <- s x max(1 - k x rate, 0) when the goal's window fires, k <- k + 1 first # k: omissions since the last win (section 2.9)
```

Since 0.10.1 the forecast is not clipped at the cap: the deficit stays in [0, cap], but a need sitting at its cap still forecasts
the growth its horizon brings, so the horizon also raises the term at the cap (before, x^ = x there and a horizon changed
nothing once the deficit had reached it; bench/body n10). And omissions in a run cut deeper: the k-th unanswered pursuit in a row
multiplies s by 1 - k x rate, so a run of 1/rate omissions (5 at rate 0.2) takes s to 0, while isolated omissions between wins
still cost 1 - rate each (n11); a win ends the run. The state keeps the run as `needs.misses: {<goal>: k}` for goals with k > 0 (absent when every run is 0).

It is lowered as the number input `need_<id>` (box [-pursue.cap, +pursue.cap]) with effect `{pursue: {<bind>: 1}}` (section 3,
step 2b). Pursuit is weighted by the goal's learned success, so unanswered pursuits extinguish themselves through the omission
let-down; at horizon 0 the term is positive exactly when x > K/2, the deficits at which the satisfaction pays a positive win.
Expand Down
18 changes: 15 additions & 3 deletions docs/rfcs/0002-neuromodulators-and-body.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,22 @@ r > 0 becomes a `win` of size r for the bound goal, r < 0 a `setback` of min(−
so the drive step treats them as any win or setback. **The pursue term** (section 3, step 2b) for the bound goal g:

```
x̂ = min(x + g_gr × forecast_per_hour × τ, cap) # the forecast: a declared growth rate
x̂ = x + g_gr × forecast_per_hour × τ # the forecast: a declared growth rate (0.10.1: not clipped at cap)
term = clip(want × s_g × (D(x̂) − D(x̂ with x̂_j ← |x̂_j − K|)), −cap, +cap)
s_g ← s_g + success.rate × (1 − s_g) on a win of g; s_g ← s_g × (1 − success.rate) when g's window fires (A.4)
s_g ← s_g + success.rate × (1 − s_g) on a win of g, and k_g ← 0
s_g ← s_g × max(1 − k_g × success.rate, 0) when g's window fires, k_g ← k_g + 1 first # 0.10.1: the k-th omission in a run (A.4)
```

Amended in 0.10.1 (R126; bench/body n10 and n11 failed on 0.10.0 as design findings, BENCHMARKS.md §12). The forecast was
`min(x + …, cap)`: at the cap x̂ = x, so a horizon added nothing once the deficit had reached it, and with two needs at their
caps the compact term (2 × (D(1, 1) − D(0.4, 1)) = 0.503) stayed under the interest gap ln(1/0.6) = 0.511 (the nulls.py
reference: every at-cap hour it kept on `work` had both deficits at 1 and s = 1). The deficit stays clipped; only the forecast
reads the growth the horizon brings. And an omission lowered s by a constant factor, so two omissions on the first away day
left s = 0.485, enough to pursue once more the next morning (2 of 100 lives, n11). Now the k-th omission of a run multiplies
s by 1 − k × rate: a run of 1/rate unanswered pursuits extinguishes pursuit, and the same life's third pursuit sees s = 0.364
(a geometric run, (1 − rate)^k, left 0.388 and a field margin of 0.004 on the engine: R126 cycle B-E1, not kept). The run k_g is state
(`needs.misses: {goal: k}`, only for k > 0; a state is refused when a run is not a whole number in [1, 10⁶]).

It is lowered as the number input `need_<id>` (box [−cap, +cap]) with effect `{pursue: {<bind>: 1}}`. Pursuit is
weighted by the goal's learned success, so unanswered pursuits extinguish themselves through the omission let-down (A.4).
The weight is a success rate s ∈ [0, 1], not §2.9's E: E is a win size and starts at 0, so weighting by it leaves a need
Expand All @@ -162,7 +173,8 @@ Omission of an expected reward is a negative prediction error (Schultz 2016) tha

The omitted d joins the turn's summed surprise (so `effects.surprise` and `drv_letdown` act on it) and the optional
`drives.effects.omission` map, lowered as the number input `drv_omit` = min(Σ omitted E, pe_cap) (default: no effects).
It does **not** join the learning sign (question 1), and it lowers the bound need's success rate (A.3).
It does **not** join the learning sign (question 1), and it lowers the bound need's success rate (A.3), by the factor 1 − k × rate
(at least 0) at the k-th omission in a run without a win (0.10.1).

### A.5 Refusals

Expand Down
Loading
Loading