Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a68590c
Fix SPM construction contracts and refresh current repository guidance
docxology Oct 8, 2026
5f36773
Refresh generator-owned manuscript metadata for modernization
docxology Oct 8, 2026
74f188d
refactor(ci): share locked CPU setup and interpreter caches
docxology Oct 8, 2026
5b14314
fix(manuscript): stop verification admission after interruption
docxology Oct 8, 2026
d1c993e
Refresh producer metadata after CI and cancellation changes
docxology Oct 8, 2026
71e7e59
Clarify supported trend and version input contracts
docxology Oct 8, 2026
4167139
Refresh producer metadata after source documentation cleanup
docxology Oct 8, 2026
2ede3ec
Merge accepted GNN 4.1.0 pin into modernization candidate
docxology Oct 8, 2026
2d12114
Refresh producer metadata after accepted GNN main integration
docxology Oct 8, 2026
ec39494
Add finite circulation and graph Hodge contracts with issue reconcili…
docxology Oct 8, 2026
c4c9f19
Refresh generated manuscript date after circulation contracts
docxology Oct 8, 2026
c65e4e6
Skip unsaved cache pruning in CI reader jobs
docxology Oct 8, 2026
c78e067
Refresh generated metadata after cache writer cleanup contract
docxology Oct 8, 2026
555eb7b
Reject non-pytest exit five as an empty selection
docxology Oct 8, 2026
b3a5ed2
Refresh manuscript source metadata after exit classification fix
docxology Oct 8, 2026
ed6e4e5
Validate graph face directions independently of scale and refresh rea…
docxology Oct 8, 2026
abf1363
Validate and project the same numerical graph face space
docxology Oct 8, 2026
42d2964
Refresh canonical manuscript metadata after numerical review fixes
docxology Oct 8, 2026
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
14 changes: 14 additions & 0 deletions .github/actions/sync-cpu-workspace/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Sync CPU workspace
description: Install every locked workspace member, extra and dependency group on CPU runners.
runs:
using: composite
steps:
- name: Install locked CPU dependencies
shell: bash
run: |
# These optional native extras require CUDA or incompatible hosted
# VTK/C++ toolchains. All other extras and every dependency group stay
# installed. A cache hit never substitutes for the locked sync.
uv sync --locked --python "$(command -v python)" --all-packages --all-extras --all-groups \
--no-install-package cupy --no-install-package mayavi \
--no-install-package vaex --no-install-package vaex-core
78 changes: 23 additions & 55 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,15 @@ jobs:
with:
version: "0.10.7"
enable-cache: true
cache-suffix: validate
cache-suffix: cpu-workspace-py3.11
save-cache: false
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
.python-version

- name: Install locked dependencies
run: |
# CuPy requires a CUDA toolchain when no compatible wheel is
# available; the hosted CPU runners do not provide one. Keep the
# performance extra available to developers while excluding it from
# this CPU-only validation environment.
# Mayavi is an optional Python <3.12 3-D extra whose source build is
# incompatible with the hosted runner's VTK dependency; no CI test
# requires that optional backend.
# Vaex is an optional Python <3.12 performance extra whose C++
# extension does not build against the hosted runner toolchain.
uv sync --locked --python "$(command -v python)" --all-packages --all-extras --all-groups \
--no-install-package cupy --no-install-package mayavi \
--no-install-package vaex --no-install-package vaex-core
- name: Install locked workspace dependencies
uses: ./.github/actions/sync-cpu-workspace

- name: Provision compatible DuckDB Spatial runtime
run: |
Expand Down Expand Up @@ -229,10 +218,6 @@ jobs:
include-hidden-files: true
retention-days: 14

- name: Prune uv cache
if: always()
continue-on-error: true
run: uv cache prune --ci

fast-contracts:
# Early feedback is independent of the full, required category matrix.
Expand All @@ -256,26 +241,15 @@ jobs:
with:
version: "0.10.7"
enable-cache: true
cache-suffix: fast-contracts
cache-suffix: cpu-workspace-py3.11
save-cache: false
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
.python-version

- name: Install locked dependencies
run: |
# CuPy requires a CUDA toolchain when no compatible wheel is
# available; the hosted CPU runners do not provide one. Keep the
# performance extra available to developers while excluding it from
# this CPU-only validation environment.
# Mayavi is an optional Python <3.12 3-D extra whose source build is
# incompatible with the hosted runner's VTK dependency; no CI test
# requires that optional backend.
# Vaex is an optional Python <3.12 performance extra whose C++
# extension does not build against the hosted runner toolchain.
uv sync --locked --python "$(command -v python)" --all-packages --all-extras --all-groups \
--no-install-package cupy --no-install-package mayavi \
--no-install-package vaex --no-install-package vaex-core
- name: Install locked workspace dependencies
uses: ./.github/actions/sync-cpu-workspace

- name: Provision compatible DuckDB Spatial runtime
run: |
Expand Down Expand Up @@ -485,26 +459,17 @@ jobs:
with:
version: "0.10.7"
enable-cache: true
cache-suffix: py${{ matrix.python-version }}-${{ matrix.category }}
cache-suffix: cpu-workspace-py${{ matrix.python-version }}
# One complete workspace writer per interpreter; other categories
# restore the same lock-bound download cache without save races.
save-cache: ${{ matrix.category == 'unit' }}
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
.python-version

- name: Install locked dependencies
run: |
# CuPy requires a CUDA toolchain when no compatible wheel is
# available; the hosted CPU runners do not provide one. Keep the
# performance extra available to developers while excluding it from
# this CPU-only validation environment.
# Mayavi is an optional Python <3.12 3-D extra whose source build is
# incompatible with the hosted runner's VTK dependency; no CI test
# requires that optional backend.
# Vaex is an optional Python <3.12 performance extra whose C++
# extension does not build against the hosted runner toolchain.
uv sync --locked --python "$(command -v python)" --all-packages --all-extras --all-groups \
--no-install-package cupy --no-install-package mayavi \
--no-install-package vaex --no-install-package vaex-core
- name: Install locked workspace dependencies
uses: ./.github/actions/sync-cpu-workspace

- name: Provision compatible DuckDB Spatial runtime
run: |
Expand Down Expand Up @@ -549,7 +514,8 @@ jobs:
retention-days: 14

- name: Prune uv cache
if: always()
# Readers do not save a cache; prune only the selected cache writer.
if: always() && matrix.category == 'unit'
continue-on-error: true
run: uv cache prune --ci

Expand Down Expand Up @@ -582,13 +548,15 @@ jobs:
with:
version: "0.10.7"
enable-cache: true
cache-suffix: manuscript
cache-suffix: cpu-workspace-py3.11
save-cache: false
cache-dependency-glob: |
**/pyproject.toml
**/uv.lock
.python-version

- name: Install locked workspace dependencies
run: |
uv sync --locked --python "$(command -v python)" --all-packages --all-extras --all-groups \
--no-install-package cupy --no-install-package mayavi \
--no-install-package vaex --no-install-package vaex-core
uses: ./.github/actions/sync-cpu-workspace

- name: Generate research artifacts
run: |
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### 0.4.0 candidate preparation

- Validate an orthonormal basis of the graph face-boundary numerical span;
tiny invalid faces cannot pass the chain check and produce divergent curl
projections. Analytical regressions cover tiny and large valid/invalid faces
zero and dependent valid columns, and near-dependent invalid columns. Refresh readiness records to distinguish verified
candidate evidence from historical failures and pending main publication.

- Make the non-pytest exit-code regression launch a real child and assert its
exit code, output, failure classification and complete receipt custody;
a metadata failure can no longer satisfy the test. Limit allowed-empty
exit-code handling and pytest diagnostics to actual pytest entry points;
ordinary commands returning code 5 remain failures even with allow_empty.

- Add MATH finite-real skew decomposition, transition/rate probability currents,
detailed-balance checks and discrete graph Hodge projections with an explicit
harmonic remainder. Analytical tests cover nonuniform stationary measures,
irreversible cycles, face/no-face topology, disconnected gauges and invalid
inputs; these numerical anchors do not claim external formal proof acceptance.
- Repair SPM formula rejection, degenerate radial/Gaussian bases and the default
polynomial basis expansion; document supported grammar and polynomial order.
- Share locked CPU workspace installation and interpreter-specific uv package
caches across CI jobs, with one cache writer per interpreter. Prune only
those cache writers; readers skip cleanup of caches they never save. Cache parsed
workflow fixtures while returning independent copies to each contract test.
- Stop manuscript verification admission after interruption, retaining the
interrupted attempt and explicit not-run groups.
- Replace obsolete H3 installation/API examples and unsupported RNG docstrings
with current uv, H3 v4 and generator contracts.

- Measure owning TEST source files named `test_*` instead of excluding them by
filename, with regression coverage for each declared coverage configuration.
- Propagate ownership census timeouts separately from target pipe polling and
Expand Down
2 changes: 1 addition & 1 deletion GEO-INFER-AI/src/geo_infer_ai/utils/rng.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def resolve_rng(seed: SeedLike = None) -> np.random.Generator:
Raises
------
TypeError
If ``seed`` cannot produce a generator (including the legacy
If ``seed`` cannot produce a generator (including unsupported
``numpy.random.RandomState`` and the ``numpy.random`` module).

Examples
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def load_algorithms_from_file(self, filepath: str) -> None:
"""Load schema version 1 using only the trusted callable registry.

Validation is atomic: malformed metadata, duplicate names, unsupported
versions, source-bearing legacy files and unknown keys raise
versions, source-bearing files and unknown keys raise
``ValueError`` without registering any entries. No persisted value is
passed to import, exec or eval.
"""
Expand Down
2 changes: 1 addition & 1 deletion GEO-INFER-BAYES/src/geo_infer_bayes/utils/rng.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def resolve_rng(seed: SeedLike = None) -> np.random.Generator:
Raises
------
TypeError
If ``seed`` cannot produce a generator (including the legacy
If ``seed`` cannot produce a generator (including unsupported
``numpy.random.RandomState`` and the ``numpy.random`` module).

Examples
Expand Down
2 changes: 1 addition & 1 deletion GEO-INFER-COG/src/geo_infer_cog/utils/rng.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def resolve_rng(seed: SeedLike = None) -> np.random.Generator:
Raises
------
TypeError
If ``seed`` cannot produce a generator (including the legacy
If ``seed`` cannot produce a generator (including unsupported
``numpy.random.RandomState`` and the ``numpy.random`` module).

Examples
Expand Down
2 changes: 1 addition & 1 deletion GEO-INFER-ECON/src/geo_infer_econ/utils/rng.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def resolve_rng(seed: SeedLike = None) -> np.random.Generator:
Raises
------
TypeError
If ``seed`` cannot produce a generator (including the legacy
If ``seed`` cannot produce a generator (including unsupported
``numpy.random.RandomState`` and the ``numpy.random`` module).

Examples
Expand Down
1 change: 1 addition & 0 deletions GEO-INFER-INTRA/docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- `integration/`
- `knowledge_base/`
- `materiality/`
- `math/`
- `modules/`
- `ontology/`
- `realms/`
Expand Down
1 change: 1 addition & 0 deletions GEO-INFER-INTRA/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Docs workspace within `GEO-INFER-INTRA`.
- `integration/`
- `knowledge_base/`
- `materiality/`
- `math/`
- `modules/`
- `ontology/`
- `realms/`
Expand Down
9 changes: 1 addition & 8 deletions GEO-INFER-INTRA/docs/geospatial/data_formats/h3/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,7 @@ use, and output schemas.
construction.
- Treat pentagons and antimeridian-crossing geometries as explicit edge cases.

## Common migration mistakes

The following v3 names are not supported in GEO-INFER documentation or runtime
paths:

```python
# Removed v3 names: geo_to_h3, h3_to_geo, h3_to_geo_boundary, k_ring
```
## Verify the supported runtime

Use the v4 table above. The repository H3 contract gate is:

Expand Down
10 changes: 4 additions & 6 deletions GEO-INFER-INTRA/docs/getting_started/installation_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,11 @@ For a complete pre-merge check, use the command list in the root
- If an import resolves from an unexpected interpreter, use `uv run python` and
inspect `python -c "import sys; print(sys.executable)"`.

## Do not use these legacy setup paths
## Environment ownership

- Do not install an unrelated PyPI package named `geo-infer` and assume it is
this checkout.
- Do not reintroduce `setup.py` or `requirements.txt` mirrors; modules build
from `pyproject.toml` and the root `uv.lock` pins resolution.
- Do not run examples with a system interpreter that bypasses the uv lockfile.
Install workspace packages from their `pyproject.toml` metadata and retain the
root `uv.lock`. Run examples through `uv run` from the checkout. A similarly named
package from a registry does not establish that this workspace is installed.

## Next step

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ print(f"Cell + neighbors: {len(neighbors)} cells")
```

**H3 v4 API note**: GEO-INFER uses the H3 v4 Python bindings. The function
names are `latlng_to_cell` and `cell_to_latlng` (not the legacy v3 names
`geo_to_h3` / `h3_to_geo`). All GEO-INFER documentation and code follows the
names are `latlng_to_cell` and `cell_to_latlng`. All GEO-INFER documentation and code follows the
v4 convention.

## Core Spatial Operations
Expand Down
64 changes: 64 additions & 0 deletions GEO-INFER-INTRA/docs/math/circulation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Finite probability currents and graph decomposition

The owning API is `geo_infer_math.core.circulation`. These are finite real
numerical operations, with input validation and no implicit normalization.
They provide implemented anchors for [issue #58](https://github.com/ActiveInferenceInstitute/GEO_INFER/issues/58);
external notation-bridge integration and formal proof acceptance are separate.

`skew_part(A)` returns `(A - A.T)/2`. Its trace and real quadratic form vanish;
adding the symmetric part reconstructs the input. Scaling before subtraction
preserves finite opposite-sign entries near the floating-point limit.

`probability_current(K, pi, kind="transition")` accepts a nonnegative
row-stochastic transition matrix and a normalized nonnegative probability vector.
`kind="rate"` instead requires nonnegative off-diagonals, nonpositive diagonals
and zero row sums. State order is preserved. The vector need not be stationary.
The returned current is `J[i,j] = pi[i] K[i,j] - pi[j] K[j,i]`.
`circulation_part` returns half that current; `satisfies_detailed_balance`
checks each current entry against an explicit absolute tolerance.

`current_divergence(J)[j] = sum_i J[i,j]` uses the incoming convention.
For transition kernels this equals `pi @ K - pi`; for rate kernels it equals
`pi @ K`. Thus stationarity gives zero divergence but does not imply detailed
balance: a directed three-state cycle has a nonzero divergence-free current.
Dropping the circulation loses that current.

```python
import numpy as np
from geo_infer_math.core.circulation import (
probability_current, current_divergence, satisfies_detailed_balance,
graph_hodge_decomposition,
)

P = np.array([[0., 1., 0.], [0., 0., 1.], [1., 0., 0.]])
pi = np.full(3, 1/3)
J = probability_current(P, pi)
assert np.allclose(current_divergence(J), 0)
assert not satisfies_detailed_balance(P, pi)

B = np.array([[-1., 0., 1.], [1., -1., 0.], [0., 1., -1.]])
parts = graph_hodge_decomposition(B, [2., -1., 5.], np.ones((3, 1)))
assert np.allclose(parts.gradient, [0., -3., 3.])
assert np.allclose(parts.solenoidal, [2., 2., 2.])
assert np.allclose(parts.harmonic, 0)
```

The unweighted discrete graph contract uses incidence `B` (nodes by edges), with
one -1 source and +1 target per edge. Optional face boundary `C` (edges by faces)
must satisfy `B @ C = 0`. Validation applies `atol` to an orthonormal basis of the numerical face span,
using the default least-squares rank cutoff. Uniform scaling preserves directions;
nearly dependent columns cannot hide a non-cycle direction. Zero columns remain
valid. The same basis computes projections and minimum-norm face potentials. Least-squares projections return gradient in
`range(B.T)`, face-curl (`solenoidal`) in `range(C)` and the explicit harmonic
remainder. The remainder lies in `ker(B)` and `ker(C.T)` up to numerical error.
Node and face potentials use the minimum-norm gauge. Disconnected and edgeless
graphs are supported. Without faces, the face-curl is zero and cycle flow is
reported as harmonic. The total divergence-free component is the sum of
`solenoidal` and `harmonic`; the names depend on the supplied finite complex.
This API does not solve continuum PDE boundary conditions or infer faces.

Verify with:

```bash
uv run python GEO-INFER-TEST/run_unified_tests.py --paths GEO-INFER-MATH/tests/unit/test_circulation.py
```
22 changes: 22 additions & 0 deletions GEO-INFER-INTRA/docs/releases/0.4.0_migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,25 @@ documented approximation. macOS 27 users affected by PyTensor's obsolete
`-ld64` linker argument can select the explicit, guarded compiler profile in
[native sampling profiles](../../../../GEO-INFER-SPM/docs/native_sampling_profiles.md).
This profile runs the native compiler and preserves compiled sampling.

## SPM construction helpers

`generate_coordinates` requires a positive integer point count, four finite
ordered bounds, and a positive integer cluster count with a finite nonnegative
spread. Random grids use an explicit `Generator`; `None` draws fresh entropy.
Invalid cluster options fail before consuming a caller-owned random stream.

`create_spatial_basis_functions` requires finite nonempty two-column coordinates
and a positive integer basis count. Polynomial columns follow increasing total
degree: intercept, latitude, longitude, latitude squared, latitude × longitude,
longitude squared, then cubic terms. The default ten-column polynomial basis now
terminates; the former per-axis exponent cap could never produce its tenth
column. Constant coordinate axes produce zero normalized terms; coincident
Gaussian centers produce finite unit responses. Zero-radius synthetic radial
signals also remain finite.

The formula helper supports `~`, `+`, `*` as an explicit product, and `0` to
suppress the intercept. It rejects unknown product operands and empty designs.
It does not interpret `*` as automatic main-effect expansion or implement a
Patsy grammar. Explicit factor levels must be unique, nonempty, and include every
observed value; undeclared categories no longer silently become the reference.
Loading
Loading