Repository navigation
Release 0.94.0: maturity, an opt-in for a brain that keeps learning - #255
Merged
Merged
Conversation
…es (#253, opt-in) maturity=True gives each brain one slow variable, the use of its plasticity: each actor update adds its applied magnitude in the brain's own step units per synapse, and every moment of life (live, step, or learn called on its own) keeps 1 - 1/maturity_recovery of it. The step factor m = 1 / (1 + (use / maturity_scale) ** 4) multiplies the actor's synapse and bias steps and engages the store's surprise-gated writes with strength 1 - m: a write's change of a record, the shared matrix and the stream's residual alike, is multiplied by 1 - (1 - m)(1 - g), with g = max(0, 1 - 2 sqrt(R) / |e|) and R the record's running noise scale (rate 0.05, from zero at birth). No count is kept; an idle brain's use relaxes. Founders: maturity_scale 7.5, maturity_recovery 200,000. The variable and the store's noise scales are saved and loaded with the brain, kept by grow and graft and counted by parameters(); the scale and the recovery are retune settings, maturity a construction one. On a nursery life the library form reads as the evidence lane's prototype to rounding (the actor's synapses within 2e-15, the use and the noise scales equal). Off, the founder is unchanged: founder parity 0 of 24 against a4fcea0, and the identity, options, refusals and slots batteries read 0 differences. Shared suite 3991 passed, 98 skipped, 1 xfailed; MLX suite 4021 passed, 68 skipped, 1 xfailed; ruff and mypy clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every panel life keeps the step factor at 0.98 or more at this scale (SCREEN7: 7.5 held one key-door life at 0.9606, 9.0 at 0.9790, 10.0 all at 0.9887 or more). Maturity tests 10 passed; founder parity 0 of 24 against a4fcea0; the four batteries 0 differences. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A refused live, step or learn of a mature brain relaxed its use (the moment wrapper relaxed in a finally), and a refused write left the store's noise scales moved (the gate updated them in place, and Brain.learn's restore did not keep them). The gate now computes the moved noise scales as a new array that the write installs with the records it accepts, Brain.learn keeps and restores the reference with the store's other arrays, and the use relaxes only when the outermost moment returns. Tests: a refused live, a refused step and a learn the actor refuses after the store's write leave every array and the use as they were; a refused overflowing store write keeps the noise scales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.93.1 loaded a maturity checkpoint, saved as cadence-brain/1, as a founder brain, dropping the maturity and the store's noise scales without a word. Following the library's convention for a new component, a brain with maturity now saves under the new format name cadence-brain/2, which 0.93.1 and earlier refuse; every other brain saves cadence-brain/1 byte for byte as before. Brain.load reads both and refuses a cadence-brain/1 file, or a file in the layout of 0.84.3, that carries a maturity, and a cadence-brain/2 file without one. Test: the founder's and the mature brain's format names, and each mismatch refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#253) With two or more writing streams the gate moved the shared records by the mean of each row's gained change while a stream that did not write moved by the mean gain times the ungated change, so its residual around the shared records drifted (0.095 in the review's probe), and memory.md's formula held for one writing stream only. The gate now takes one gain per action for the write, the mean over the writing rows that observe the action, and applies it to the ungated change of the shared records and of every writing stream's residual alike: every other stream keeps its residual exactly as without the gate. With one writing stream the gain is that stream's own and the arithmetic is the same to the bit. The docstring and memory.md give the exact formula. Test: two writing streams, one confirmed and one surprised, move the shared records and their residuals by the mean gain times the ungated change, and the third stream's residual stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A finite outcome near 1.3e154 squared beyond the floating-point range: the noise scale became inf, and NaN on the coordinates the cue never touched (zero weight times inf), and the brain saved a checkpoint its own load refused. The store now checks the new noise scales with the records and refuses such a write with the overflow's ValueError before anything is installed, so no NaN is written and a saved brain always loads. Test: a live and a step paid 1.3e155 are refused, every array and the use stay, and the brain saves and loads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mature brain's actor update built its scaled config with dataclasses.replace, which reran ActorCriticConfig's validation, so the eta_bias-above-eta RuntimeWarning fired on every update (27 times in 30 moments in the review's probe). The update now copies the validated config and sets the two scaled steps on the copy without validating again; the arithmetic is unchanged. Test: a brain with eta_bias above eta and maturity warns once, at construction, through 30 moments of life at a factor below one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maturity_scale and maturity_recovery were accepted and ignored without maturity=True, even at -5 or NaN. Construction now validates both always and refuses either at a value other than its founder without maturity, as memory_context is refused without memory_choice; api.md's construction refusals list both rules. Test: each setting without maturity, at an invalid or a changed value, is refused; invalid values with maturity are refused; the founders named alone build the founder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eads any use (#253) A saved maturity with a missing field raised KeyError, one of the wrong type TypeError, and an extra field, a non-boolean surprise_gate, negative noise scales and use=1e300 loaded (the last then overflowed in factor(), a Python float power); a mature brain whose store had lost its noise scales loaded ungated, and integer noise scales loaded and then failed the first write. Load now validates the maturity field by field (exactly scale, recovery and use, each a finite real in its range, use at most scale * Maturity.LIMIT, 1e76, where the factor is 1e-304), refuses a non-boolean surprise_gate, negative noise scales and a maturity without the store's noise scales, each with a ValueError naming the field, and reads the noise scales as floating point. factor() returns 0 beyond the limit instead of overflowing; below it the arithmetic is unchanged. api.md lists the rules. Tests: each malformed field refused by name, integer noise scales load and live, the factor at 1e70 per scale is 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…253) On the torch backend a mature brain's actor update read the efficacy back to the host to measure the step it applied (one full read per update, where the founder reads none). The update now keeps the kernel's scale tensor before the step (an update replaces it) and sums the step's magnitude on the device, so one number comes to the host; the CPU and MLX backends measure on the host arrays as before. Test: a torch brain with maturity reads no efficacy through 60 moments of life and its use matches the CPU brain's within 1e-9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
api.md's parameters() row now counts the maturity's use and the place records' noise scales, its SynapticMemory section gives gate_noise and maturity with the gate's constants, and the agent section documents Maturity (fields, factor() with ORDER and LIMIT, relax(), to_dict()) and ActorCritic.maturity. Maturity is exported from cadence as the package exports the other classes a brain exposes (ActorCritic, SynapticMemory, Arousal), and listed in the API index. memory.md states that the gate is the place records' alone: the context records and the direct payments write as without maturity. Test: cadence.Maturity is exported, and a brain with memory_choice, memory_extinction and maturity gates its place records only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) The previous commit bounded a saved use by scale * 1e76, so a brain retuned to a smaller scale could save a use its own load refused. The bound is now absolute, Maturity.LIMIT = 1e64, at which the factor's power stays finite at the smallest scale (1e-12); the actor's update keeps the use at most the limit (no life comes near it, so every reading is unchanged), load refuses a use above it, and factor() returns 0 instead of overflowing for a use set beyond it by hand. Test: a brain at the limit, at the smallest scale, stays there through five moments and saves a checkpoint that loads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oment sentence (#253) Brain.load accepted surprise_gate and gate_noise on the episodic context records and the direct payments, which a brain never saves; _load_memory now takes gate=True for the place records alone and refuses the gate elsewhere, with a test on both stores. docs/reward.md: a live refused after its outcome was taken keeps that outcome learned, so the sentence on refused moments is narrowed to moments refused before they learn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ActorCritic.learn swapped in a copy of its frozen config with the steps scaled (copy.copy and object.__setattr__), restored it in a finally, kept a second try block for the founder and restored a use that nothing in the try writes. The steps now read the factor where they are taken (_rates, in _learn and _learn_device), so learn has one try block and measures the step applied after it. The same bits: the founder's step is (1.0 * eta) * step, the mature one's (m * eta) * step as before; founder parity 0 of 24 and the mature battery 0 of 926 values against 2c26af0. The docstring names what maturity scales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SynapticMemory._gate divided the noise spread by the error outside its errstate, so a subnormal error against a nonzero noise scale (an error of 1e-310 against a noise of 1) raised "RuntimeWarning: overflow encountered in divide", an error under warnings-as-errors. The ratio's overflow is the right reading (a surprise of zero), so the division now runs with overflow ignored. The local named strength, which read as the store's own strength array, is engaged. Same bits: the mature battery reads 0 of 926 values against 2c26af0, founder parity 0 of 24. The new test fails without the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SynapticMemory.observe wrote the shared change, the prediction and the fast correction twice, once for the founder's write and once inside the surprise gate's branch. They are computed once; the founder installs them as they are and the gate multiplies them per action by its gain. A change to the write law is now made in one place. Same bits: founder parity 0 of 24, the mature battery 0 of 926 values against 2c26af0 (several streams, masks, salience, partial writes, m from 1 to near 0), and the memory and episodic test files pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brain._gate_store had one caller, and the isinstance check before it was always true there (the store function makes a SynapticMemory). The two assignments now stand where the place records are made, and the method is gone. Same bits: founder parity 0 of 24, the mature battery 0 of 926 values against 2c26af0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…253) The decorator that relaxes a mature brain's use once per moment of life was named _moment, the name of the brain's record of the action awaiting its outcome (self._moment, with _moment_code and _coded_moment beside it), and its counter _moment_depth read as part of that record. They are _moment_of_life and _life_depth. A rename only: founder parity 0 of 24, the mature battery 0 of 926. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#253) reward.md's section now says what maturity is for (a brain that goes on learning after it has acquired a skill, #169), what it leaves at its own steps (the critic, lessons, rehearsal, a learned code, the recall memory's maps), that maturity is a construction setting retune refuses and grow and graft keep, that the scale and the recovery other than their founders need it, what it costs (the noise scales, a pass over the synapses per actor update, the gate's arithmetic per write) and that a mature brain saves cadence-brain/2, which 0.93.1 refuses. A step refused after its outcome was learned keeps the step in the use without the moment's relaxation, as a live does (read on 99d1fbb). The founder-scale readings name the preservation panel with its link. tune.md's defaults table gains the three settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
memory.md gave the gain but described the noise scale as a running mean at 0.05; the code moves each record's noise by 0.05 times the key's squared weight on it relative to the key's largest, per writing stream and observed action. The text block now has the gain, g and the noise update with their symbols (k, e = v - k @ (C + F), sqrt(k^2 @ R)). A young brain writes as without the gate within rounding, and every write into the place records is gated: outcomes and, with memory_choice, replayed credit and demonstrations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thing (#253) "cadence-brain/1, one format whatever the brain holds, except" and "a file of either format carrying the other's maturity" now read: a brain with maturity writes cadence-brain/2, and Brain.load refuses a /1 file with a maturity and a /2 file without one. The members table lists gate_noise under memories/hippocampus/. Maturity(scale, recovery, use) validates nothing itself (a scale of 0 divides by zero in factor()); compose, retune and load do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) The entry ran one paragraph with a 130-column line and placed the settings' founders inside the description of the use. It now gives what maturity is for (#169), the use and its relaxation per accepted moment, the step factor and the gate, what keeps its steps, then the checkpoint, retune and export facts. "An idle brain" is "a brain that lives without learning", here and in reward.md: the use relaxes per moment of life, not with time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…253) SynapticMemory's docstring ran the gate's law in one sentence and said the noise moves "toward the squared error at GATE_RATE"; it now names m, g, e and R in order, says that Brain.compose(maturity=True) sets the gate on the place records, and gives the noise update with its weight (the cue's squared weight on the record relative to its largest). Maturity's docstring says "one that lives without learning" for "an idle one". Brain.retune's docstring lists maturity_scale and maturity_recovery among the names it takes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The field was Any, so mypy read nothing of what the gate calls on it and a maintainer had to find its type in generic.py. It is Maturity | None, imported for type checking only (memory.py imports nothing of plasticity at run time), and _gate asserts both halves of the gate. ruff and mypy clean; founder parity 0 of 24, the mature battery 0 of 926. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ales (#253) parameters() adds the maturity's use and the place records' noise scales, as docs/api.md says; the docstring listed neither. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new user found maturity only through the CHANGELOG or the reward page's body. The "Read in this order" entry for the reward page names it, and the "By situation" table has a row for it: a brain whose actor's steps fall as it uses its plasticity and whose store moves on surprises. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
outer(k^2 / max(k^2), e^2 - R) took a matrix as its second argument, where the store section's outer takes two vectors. The update reads R[i, a] += 0.05 * k_i^2 / max(k^2) * (e_a^2 - R[i, a]), as _gate computes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
grow(maturity=True) answered "maturity is not a part; retune changes a living brain's settings", and retune refuses maturity as a construction setting, so each refusal pointed at the other. maturity_scale or maturity_recovery on a brain without maturity did the same. grow now refuses them as it refuses arousal: "maturity is a construction choice grow does not add; compose the brain with maturity=True". A mature brain's maturity_scale still reads "not a part; retune changes ...", which retune does. grow.md and api.md list maturity with arousal and the episodic memory. The two new cases of test_grow_refuses_with_the_reason fail without the change. Founder parity 0 of 24, the mature battery 0 of 926; test_grow and test_documentation pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…253, #169) Version 0.94.0 in pyproject.toml, cadence.__version__ and the install pins of the README and the docs; CHANGELOG's Unreleased section becomes 0.94.0 with the readings of the confirmation declared before its run: seven of #169's eight core gates held, the fight statistics failed by one calm run, so #169 stays open and maturity ships as an opt-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 0.94.0 (milestone v0.94.0, roadmap #109): one opt-in, maturity (#253), for a brain that goes on learning after it has acquired a skill (#169). Off, every brain is 0.93.1's to the bit.
What it adds
Brain.compose(..., maturity=True)gives the brain one slow variable, the use of its plasticity: a leaky integral of the actor's applied synapse steps, with no count of updates or age. The step factorm = 1 / (1 + (use / maturity_scale) ** 4)multiplies the actor's synapse and bias steps and engages surprise-gated writes on the place records with strength1 - m, so a mature store keeps what an outcome confirms and moves on a surprise.maturity_scale(10) andmaturity_recovery(200,000 moments) areretunesettings;maturityis a construction setting. A mature brain saves checkpoint formatcadence-brain/2; every other brain savescadence-brain/1as before. Documented in reward, memory, api and tune.The readings (#169)
Read on fresh brains under a protocol declared before the run, in a continuing robot arena (144 brain-runs per arm), against the founder at its own actor step and at step 0.003, the bar #169 sets. Seven of the eight core gates held:
#169 stays open with the full table; whether maturity becomes the default is #252's panel; savings after displacement are #254.
No regressions
growandretuneeach pointing to the other formaturity. The last round also simplified the actor's scaled steps and the gated write, bit-identical for the founder (0 of 24 lives) and the mature brain (0 of 926 hashed values over NumPy, torch and mlx).twine check --strict; the wheel installed into a fresh venv runs the founder, every 0.93 option and maturity (living and stepping) through train, save, load, continue and grow, with a mature brain savingcadence-brain/2and every othercadence-brain/1.🤖 Generated with Claude Code