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
2 changes: 1 addition & 1 deletion .github/fep-lean-pair.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"repository": "ActiveInferenceInstitute/fep_formal",
"revision": "b3e21f6690a22a98cc68b609a818817247fd6868"
"revision": "184ee53f55b526fdc858e2d962eba16e6066fb19"
}
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ Format follows [Keep a Changelog](https://keepachangelog.com/) and [Semantic Ver

## [Unreleased]

### Discrete filtering correctness (2026-10-18)

- PyTorch and NumPyro discrete simulations now predict the next observation's
prior through the previous action's transition matrix. The initial observation
uses the authored prior without an extra prediction. This prevents zero-mass
posterior traces in deterministic-observation models such as `simple_mdp` and
`markov_chain`.
- Generated programs reject zero/nonfinite predictive evidence and fail execution
if the persisted result validation is invalid. Native NumPy reference witnesses
cover asymmetric transitions, every posterior and per-action EFE, the initial
prediction boundary, and failure controls. Input models remain unchanged.

### E3-FA1 — explicit finite controls (2026-10-18)

- The optional cpomdp renderer admits versioned source-declared finite actions,
ordered IDs, units and asymmetric state-by-control maps. Invalid declarations,
incompatible modes and unsupported backends refuse explicitly.
- Results distinguish control coordinates from state increments and retain every
policy's pragmatic/epistemic scores and selected action identities. Duplicate
vectors retain their distinct IDs and source tie order.
- Admission bounds total policy-step evaluations across timesteps before
enumeration. Released-wheel NumPy Kalman/EFE witnesses cover asymmetric axes,
correlated covariances, policy scores and negative controls. This bounded
extension leaves the broader E3 and E4 roadmap contracts open.

### Fixed

- Lightweight processing reuses parsed structure during validation, preserves
Expand Down
7 changes: 5 additions & 2 deletions TO-DO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TO-DO — GNN next steps

Updated 2026-10-16.
Updated 2026-10-18.
Current software version is **4.1.0**. Its [GitHub release](https://github.com/ActiveInferenceInstitute/Generalized_Notation_Notation/releases/tag/v4.1.0) records exact tag, source, checks and artifacts; version metadata alone does not establish acceptance.
Every checkbox below is remaining work. Remove accepted bounded work while
retaining its evidence in [CHANGELOG.md](CHANGELOG.md), release receipts and Git
Expand Down Expand Up @@ -100,7 +100,10 @@ required.
evidence. Depends on S3 where structural zeros are required, plus M2/M3/E4.

- [ ] **E3 — extend cpomdp control/search capabilities safely (P2).**
Scope: specify new control/search behavior through the
Progress: E3-FA1 adds source-declared ordered finite actions, explicit control
matrices and units, complete policy/component receipts and a total evaluation
cap; its independent numerical witnesses retain a separate bounded scope.
Remaining scope: specify further control/search behavior through the
[cpomdp adapter](docs/gnn/implementations/cpomdp.md), preserving passive
no-search behavior, explicit EFE defaults and complete policy/source identity.
Accept: released constructor compatibility, finite/nonboolean options,
Expand Down
12 changes: 11 additions & 1 deletion docs/development/remaining_roadmap_contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ The maintained compass-rose action set and exhaustive search remain unchanged.
Direct options now share typed public admission; invalid options refuse without
replacing a caller's existing artifact.

A next extension may admit a source-declared finite action set with an explicit
E3-FA1 now admits a source-declared finite action set with an explicit
version, ordered action IDs, units and a control matrix mapping each action into
state space. Validate finite, nonboolean components and exact asymmetric control
dimensions. Preserve the authored action order for ties and policy identity.
Expand Down Expand Up @@ -204,3 +204,13 @@ A local package build or an unverified archive URL does not satisfy E5. Preserve
failed provider checks and leave the item open when public resolution or artifact
hash parity cannot be established. Archive publication and package-index delivery
remain distinct operations with their own destination and ownership contracts.

### E3-FA1 boundary

The source-declared finite-control implementation is specified in the
[cpomdp guide](../gnn/implementations/cpomdp.md#source-declared-finite-controls-e3-fa1).
It preserves authored action order, distinct IDs for equal vectors, units,
asymmetric control maps and complete per-policy EFE components. Admission also
bounds total evaluations across timesteps. This does not close E3's broader
control/search workstream, admit continuous action spaces, or establish E4's
independent source extraction or universal proof obligations.
50 changes: 50 additions & 0 deletions docs/gnn/implementations/cpomdp.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,53 @@ Execution needs no cpomdp-specific module. The execute step runs the rendered sc

- [NumPyro](numpyro.md), which renders the same continuous models with a Kalman filter and NUTS
- [GNN implementations index](README.md)

## Source-declared finite controls (E3-FA1)

A flat continuous source can add this single-line Python-literal mapping to
`InitialParameterization` (or the same mapping to the parsed Python spec).
`goal_mean` and `control_gain` remain required; the gain is not used by EFE mode.

```text
cpomdp_control = {"contract": "gnn-cpomdp-finite-actions-v1", "version": "thruster-v1", "action_ids": ["forward", "reverse", "coast"], "control_units": ["newton"], "state_units": ["m/s", "m/s", "m/s"], "actions": [[0.3], [-0.15], [0.0]], "control_matrix": [[0.2], [-0.4], [0.7]]}
```

For `n` states and `p` controls, `control_matrix` is exactly `n × p` and every
action has `p` components. The transition is
`x_t = F x_(t-1) + control_matrix @ a_(t-1) + noise`. Units label each authored
control and state axis; the matrix maps control units into state increments.
These labels do not certify dimensional analysis. The prior is still over the
first observed state, with no initial predict step.

All seven keys are mandatory. The contract version is fixed above; `version`
identifies the author's action set. IDs must be unique nonempty strings and
retain source order. Equal vectors with different IDs remain distinct choices;
ties select the first authored policy in Cartesian-product order. At least two
actions are required by released cpomdp 0.4.4. Values must be finite nonboolean
numbers, with no string conversion, normalization or deduplication. Invalid
declarations refuse before replacing an existing artifact. Unknown keys and
versions refuse. These controls require `efe`; `parity` and explicit
`action_scale` overrides refuse. Without this declaration, the existing compass
rose and passive behavior apply. Other backends refuse the declaration.

Results retain the complete `finite_control_contract`, `action_set_version`,
`control_matrix`, `control_inputs` (in control coordinates), and
`policy_action_ids` for each selected policy. Existing `controls` remain state
increments for downstream continuous comparisons. `policy_score_components`
contains pragmatic and epistemic values for **every** enumerated policy and
timestep; existing selected-component fields remain available. The validated
model digest includes the complete control declaration, including labels.

Search admission allows at most 4,096 policies, 32,768 policy-step evaluations
per cycle, 262,144 across all timesteps, and 128 MiB estimated allocation. Limits
are checked before enumeration, without computing an unbounded power. The
estimate includes declared arrays and retained score histories; interpreter/JIT
overhead is separate, and the process watchdog remains authoritative. Passive
models allocate no planner.

Independent NumPy witnesses use source-authored asymmetric state, observation
and control dimensions, nonidentity sensors, nonzero means and correlated
covariances. They compare every posterior and every policy's EFE components
against the released native backend, and check duplicate-vector tie identity.
Changed control maps and an extra initial prediction must disagree. This is a
bounded numerical witness, not a universal extraction or equivalence proof.
34 changes: 17 additions & 17 deletions output/data/manuscript_render_manifest.json
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
{
"counts_describe_commit": "690f8c5",
"counts_describe_commit": "4ed8b33",
"manifest_version": "gnn_render_manifest_v1",
"receipt_counts_describe_commit": "690f8c5",
"receipt_counts_describe_commit": "4ed8b33",
"recorded_by": "uv run python scripts/z_record_manuscript_render_manifest.py",
"render_artifacts_normalized_sha256": {
"output/pdf/_combined_manuscript.log": "1b46412330c82f3d1a22a5654fa24d3a8926c8b8f2906118c142360d7f6389f9",
"output/pdf/_combined_manuscript.md": "252cbe61cbec70e0d473c982c1b9b40a880b534bf8c04b6cf50d826441260dd0",
"output/pdf/_combined_manuscript.tex": "d46f4d7f1b25ccfad4475df5c653d6d5c272ebefcbd2e9ab1c30e3aab1c6985e"
"output/pdf/_combined_manuscript.md": "f8d7d333a127274c8068e4dd0640ecb2b88544053d69e3930d3dcc0fd1291e93",
"output/pdf/_combined_manuscript.tex": "bbc7b51b5606eb5558518d56e02ab55ddb5377d146993ee68ba810c246778beb"
},
"render_artifacts_sha256": {
"output/pdf/_combined_manuscript.log": "57e6fcb5d28a077d1775eff8848f1aa890b294143b3c7be22fe2b2f23aff73d9",
"output/pdf/_combined_manuscript.md": "f72213c0bf4b7050603b49b8ec8ed1179e74593b50c26a3150f761a3052da1f5",
"output/pdf/_combined_manuscript.tex": "7d9211ab16cf9a9caadc4728dbd51d7de9d5db7c165a37ef53cdf5c0a4f21d90"
"output/pdf/_combined_manuscript.log": "be663fe9a5fdb54b885ca1f2aba03bc8325a95c267377af82c2e3e7e49071803",
"output/pdf/_combined_manuscript.md": "27acaca71d11d3a887658df1ae0eebb1683e752b2fda1f72862c36d230c7b13b",
"output/pdf/_combined_manuscript.tex": "ee7f039e06d573825093dae08f580b3c0582401287c5378ddc2a8ca0f725bfdc"
},
"render_inputs_normalized_sha256": {
"output/manuscript/00_abstract.md": "a66f146435f8300a5bfa2b3fbbc60373dec0e2e3c3fa4de9856f71e8b7eef27e",
"output/manuscript/01_introduction.md": "5d0d444605725927d88c415bbd7b2b6d27dfe62d33130cddc3cdbc4669ccb771",
"output/manuscript/02_system_context.md": "8da04dfdff5ba163f4c9e944150cff92de19d9ab76e3bf00fda1ee4c279a2e5b",
"output/manuscript/03_methods.md": "b23b0a2717f6e4006c6016672ab8ff9f319494a29584e838970e3ffcaa3fe082",
"output/manuscript/04_artifacts_and_evidence.md": "ceb3fedfaca73cf4a0db9c3bbf75f5c8154f162531f0b731283ab8d7f9891f96",
"output/manuscript/05_reproducibility.md": "901bb1e18c872ee37decf5533ffea8b983f71a964b7b3bf764b10276a4265d4c",
"output/manuscript/04_artifacts_and_evidence.md": "53a557380bfe681f84830018b99f51ae4e36f2445e4a78993ace1a5bc53b7e45",
"output/manuscript/05_reproducibility.md": "ef7da6b1d821b4129053458c46bca80048a50a26cad59348aaa5aa21475a45bb",
"output/manuscript/06_limitations_and_next_steps.md": "86fdf003b8f486ce262699aca59d5b6bf2cea5b4ff9916acb87399d225a12d6e",
"output/manuscript/98_symbols_glossary.md": "4342b4bac17e063cd34f0e0805afe4769048efb22beced4887080ff198577c60",
"output/manuscript/99_references.md": "5e80feaa0ff39228501260f8e18f34debc071e1431de9c81a48016cb428aeef2",
"output/manuscript/S01_source_surface.md": "9dfa94d40dd6fa743bb1b2aec6303a2859bb037e407b5596fdbedb2885e8a712",
"output/manuscript/S01_source_surface.md": "5d707901f2045b7d39c925f51551558c1a44928257b7025411b80045b2fd2eea",
"output/manuscript/config.yaml": "94d56ef0b383c482d2807b49b9ed5df26705009203746aa4e06a2819313eebfa",
"output/manuscript/preamble.md": "ebdb657038ccf007dffefe5e3d5834145b956e6580628356975d741bcd6fb01e",
"output/manuscript/references.bib": "ed44f4594867a193123aaf03334e12c2f12e167584d42ac5bc6074c4bfec9f0d"
},
"render_inputs_sha256": {
"output/manuscript/00_abstract.md": "99a37dfdb6a65d936247d46e1e1888da18f94bcfe2110f3362da9451fb146467",
"output/manuscript/00_abstract.md": "9f41eac73a95505cccade998c67fc0a2245a1b079f9b6b378ff8307c6f9de50e",
"output/manuscript/01_introduction.md": "5d0d444605725927d88c415bbd7b2b6d27dfe62d33130cddc3cdbc4669ccb771",
"output/manuscript/02_system_context.md": "06e660631a2fdfe6755cc2c4724da56209e82f4558e8cdd56c6b389b23ad211e",
"output/manuscript/03_methods.md": "4f6380728f12b632b33ab6e41f9b7516d7d087e430d8e095ff98c0a00938a1ae",
"output/manuscript/04_artifacts_and_evidence.md": "b6a1a593b3abd3b60290551f9253d58c563f389e4bf2a017b41186e46859a744",
"output/manuscript/05_reproducibility.md": "e262cf07ff9736bed96e478cb2b806f7c6a16d68756cef4a54195d4f8cfd047e",
"output/manuscript/02_system_context.md": "3e6e912afca37b0c441b78f8c0059679b9f3698823ebc022cdb1014cda777249",
"output/manuscript/03_methods.md": "d8e8176ceaf484f5081ae251273f5e90ee46ea26cca7e5c1ab7357dd8545b6e6",
"output/manuscript/04_artifacts_and_evidence.md": "58a5d4ddb764086e2acd7cd46f701cda41290cffa8dabfdb9777fc0a4a1cd403",
"output/manuscript/05_reproducibility.md": "c454fc494d108f76b99b4963ce567d7d04b63825336079c8f0d1955d96dbec6b",
"output/manuscript/06_limitations_and_next_steps.md": "86fdf003b8f486ce262699aca59d5b6bf2cea5b4ff9916acb87399d225a12d6e",
"output/manuscript/98_symbols_glossary.md": "4342b4bac17e063cd34f0e0805afe4769048efb22beced4887080ff198577c60",
"output/manuscript/99_references.md": "5e80feaa0ff39228501260f8e18f34debc071e1431de9c81a48016cb428aeef2",
"output/manuscript/S01_source_surface.md": "9dfa94d40dd6fa743bb1b2aec6303a2859bb037e407b5596fdbedb2885e8a712",
"output/manuscript/S01_source_surface.md": "5d707901f2045b7d39c925f51551558c1a44928257b7025411b80045b2fd2eea",
"output/manuscript/config.yaml": "94d56ef0b383c482d2807b49b9ed5df26705009203746aa4e06a2819313eebfa",
"output/manuscript/preamble.md": "ebdb657038ccf007dffefe5e3d5834145b956e6580628356975d741bcd6fb01e",
"output/manuscript/references.bib": "ed44f4594867a193123aaf03334e12c2f12e167584d42ac5bc6074c4bfec9f0d"
},
"variables_sha256": "99256cf1ff620a37045b2f04ee1675b01f2e9778642f61b9a005ca805ec9ff8b"
"variables_sha256": "a8751fd723486c28d7a7975e8126013211ab177f3c2a6a48108936a72762799a"
}
12 changes: 6 additions & 6 deletions output/data/manuscript_variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"GNN_FAMILY_TARGET_DIR_COUNT": "9",
"GNN_FIRST_AUTHOR": "Daniel Ari Friedman",
"GNN_FRAMEWORK_TABLE": "| Framework | Discrete render | Continuous render | Executor status |\n|---|---|---|---|\n| PyMDP | yes | unsupported | executor |\n| RxInfer.jl | yes | yes | executor |\n| ActiveInference.jl | yes | unsupported | executor |\n| JAX | yes | yes | executor |\n| DisCoPy | yes | unsupported | executor |\n| PyTorch | yes | yes | executor |\n| NumPyro | yes | yes | executor |\n| Stan | yes | yes | executor |\n| bnlearn | yes | unsupported | executor |\n| cpomdp | no | yes | executor |\n| THRML | yes | unsupported | executor |\n| ngc-learn | no | yes | executor |\n: Model kinds each render framework supports, read entirely from the flags in `src/gnn/render/framework_registry.py` — the same source as [@tbl:backend_registry]. A continuous cell of `unsupported` is the renderer's own status for continuous-state models; continuous-only targets do not render categorical models. An executor entry declares dispatch support, not acceptance of every model or inference mode; a render-only entry has no Step-12 executor. Read with [@tbl:model_kinds], which groups the same flags by model kind. {#tbl:framework_capability}",
"GNN_GIT_COMMIT": "690f8c5",
"GNN_GIT_COMMIT": "4ed8b33",
"GNN_INPUT_FAMILY_DIR_COUNT": "12",
"GNN_KEYWORDS": "GNN, active inference, generative models, model notation, pipeline automation",
"GNN_LICENSE": "CC-BY-NC-SA-4.0",
Expand All @@ -45,9 +45,9 @@
"GNN_RELEASE_CODENAME": "Public contracts and native boundaries",
"GNN_RELEASE_DATE": "2026-10-08",
"GNN_REPO_URL": "https://github.com/ActiveInferenceInstitute/Generalized_Notation_Notation",
"GNN_SRC_LOC": "225574",
"GNN_SRC_LOC": "225800",
"GNN_SRC_PACKAGE_COUNT": "44",
"GNN_SRC_PY_FILE_COUNT": "765",
"GNN_SRC_PY_FILE_COUNT": "766",
"GNN_STEP_ADVANCED_VIZ": "9",
"GNN_STEP_ANALYSIS": "16",
"GNN_STEP_AUDIO": "15",
Expand Down Expand Up @@ -80,9 +80,9 @@
"GNN_STEP_WEBSITE": "20",
"GNN_SUBTITLE": "A text language and modular processing pipeline for Active Inference generative models",
"GNN_TARGET_DIR_COVERAGE_NOTE": "All 9 registered family target directories lie inside that tree, so a single invocation reaches every registered family.",
"GNN_TEST_COUNT": "6776",
"GNN_TEST_FILE_COUNT": "656",
"GNN_TEST_FUNCTION_COUNT": "6776",
"GNN_TEST_COUNT": "6790",
"GNN_TEST_FILE_COUNT": "658",
"GNN_TEST_FUNCTION_COUNT": "6790",
"GNN_TITLE": "GeneralizedNotationNotation: A Text Language for Active Inference Models",
"GNN_TOKEN_COUNT": "93",
"GNN_TOOL_COUNT": "164",
Expand Down
2 changes: 1 addition & 1 deletion output/data/manuscript_variables_receipt.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"counts_describe_commit": "690f8c5",
"counts_describe_commit": "4ed8b33",
"dirty_path_count": 11,
"dirty_paths": [
"output/.pipeline/",
Expand Down
8 changes: 4 additions & 4 deletions output/figures/figure_registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"png_sha256": "0d16072f46643702a2bc7fe393b9468da60682f89a9847eeb2a045d0240ef777",
"source_sha256": {
"input/model_family_manifest.json": "41adbb30ab861d094823b69669656081bbcb603c7203bd30335555e2f60b9e3c",
"src/gnn/render/framework_registry.py": "baab0f951725f7034af16b17d237513713a203cd222e271b1c912ffdb1dd6033"
"src/gnn/render/framework_registry.py": "b934336e544ca069da84223368fec6129f0abe30dede3144ba5bb7c4bfd7d9ec"
}
},
{
Expand All @@ -57,7 +57,7 @@
"input/model_family_manifest.json": "41adbb30ab861d094823b69669656081bbcb603c7203bd30335555e2f60b9e3c",
"src/gnn/manuscript/variables.py": "f0770f854a60ccabb86ac034b87646044200974ac0940426a3667851b3827a1d",
"src/gnn/pipeline/cross_framework_reliability.py": "f541016765f8c2b843226c9127f011cc77a0e66cbad694e89853db59c6cd8101",
"src/gnn/render/framework_registry.py": "baab0f951725f7034af16b17d237513713a203cd222e271b1c912ffdb1dd6033"
"src/gnn/render/framework_registry.py": "b934336e544ca069da84223368fec6129f0abe30dede3144ba5bb7c4bfd7d9ec"
}
},
{
Expand All @@ -71,13 +71,13 @@
"GNN_MCP_TOOL_COUNT": "164",
"GNN_SRC_PACKAGE_COUNT": "44",
"GNN_STEP_COUNT": "25",
"GNN_TEST_FILE_COUNT": "656",
"GNN_TEST_FILE_COUNT": "658",
"GNN_VERSION": "4.1.0"
},
"filename": "gnn_repo_metrics.png",
"generated_by": "scripts/manuscript_fig_repo_metrics.py",
"label": "fig:repo_metrics",
"png_sha256": "2da518030916e213272e776f4fd57638b9151c753a5ef8e2f96ccc9755e60519",
"png_sha256": "ed52c9dc28d588cef5fb1aead3fb526601bfe01d595f813d61e23732c16a33be",
"source_sha256": {}
},
{
Expand Down
Binary file modified output/figures/gnn_repo_metrics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading