From a47ec0bf0e2f0a88b694ceec5a9b026bc80818a9 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:18:38 +0800 Subject: [PATCH 1/6] Add temporary workflow for linear-update symmetrization fix --- ...ent_patch_linear_update_symmetrization.yml | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/agent_patch_linear_update_symmetrization.yml diff --git a/.github/workflows/agent_patch_linear_update_symmetrization.yml b/.github/workflows/agent_patch_linear_update_symmetrization.yml new file mode 100644 index 0000000000..c82ba3aab1 --- /dev/null +++ b/.github/workflows/agent_patch_linear_update_symmetrization.yml @@ -0,0 +1,76 @@ +name: Agent patch linear update symmetrization + +on: + push: + branches: + - agent/stabilize-linear-update-symmetrization + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Apply focused fix and regression + shell: bash + run: | + python - <<'PY' + from pathlib import Path + + implementation = Path("src/pyrecest/filters/linear_update_planning.py") + source = implementation.read_text() + old = "def _symmetrized(matrix: np.ndarray) -> np.ndarray:\n return 0.5 * (matrix + matrix.T)\n" + new = "def _symmetrized(matrix: np.ndarray) -> np.ndarray:\n return 0.5 * matrix + 0.5 * matrix.T\n" + if old not in source: + raise SystemExit("Expected linear-update symmetrizer not found") + implementation.write_text(source.replace(old, new, 1)) + + tests = Path("tests/filters/test_linear_update_planning.py") + test_source = tests.read_text() + test_name = "test_plan_linear_measurement_update_avoids_symmetrization_overflow" + if test_name not in test_source: + test_source += '''\n\ndef test_plan_linear_measurement_update_avoids_symmetrization_overflow() -> None:\n large_covariance = 0.75 * np.finfo(float).max\n\n with np.errstate(over="raise", invalid="raise"):\n plan = plan_linear_measurement_update(\n mean=np.zeros(1),\n covariance_matrix=np.array([[large_covariance]]),\n measurement_vector=np.zeros(1),\n measurement_covariance=np.zeros((1, 1)),\n observation_matrix=np.ones((1, 1)),\n )\n\n assert plan.accepted is True\n assert np.isfinite(plan.nominal_innovation_covariance).all()\n assert plan.nominal_innovation_covariance[0, 0] == large_covariance\n''' + tests.write_text(test_source) + + Path(".github/workflows/agent_patch_linear_update_symmetrization.yml").unlink() + PY + + - name: Validate patch shape and numerical regression + shell: bash + run: | + python -m py_compile \ + src/pyrecest/filters/linear_update_planning.py \ + tests/filters/test_linear_update_planning.py + python - <<'PY' + import numpy as np + + large = 0.75 * np.finfo(float).max + matrix = np.array([[large]]) + try: + with np.errstate(over="raise", invalid="raise"): + 0.5 * (matrix + matrix.T) + except FloatingPointError: + pass + else: + raise SystemExit("Pre-fix expression unexpectedly stayed finite") + + with np.errstate(over="raise", invalid="raise"): + repaired = 0.5 * matrix + 0.5 * matrix.T + assert repaired[0, 0] == large + assert np.isfinite(repaired).all() + PY + git diff --check + + - name: Commit patch + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "Stabilize linear-update covariance symmetrization" + git push origin HEAD From a7aa29f28cf0078308f4c4f11e2e2fd9ee3570a4 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:19:17 +0800 Subject: [PATCH 2/6] Run linear-update patch workflow for pull requests --- .../workflows/agent_patch_linear_update_symmetrization.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/agent_patch_linear_update_symmetrization.yml b/.github/workflows/agent_patch_linear_update_symmetrization.yml index c82ba3aab1..0d8339f012 100644 --- a/.github/workflows/agent_patch_linear_update_symmetrization.yml +++ b/.github/workflows/agent_patch_linear_update_symmetrization.yml @@ -4,8 +4,10 @@ on: push: branches: - agent/stabilize-linear-update-symmetrization - -permissions: + pull_request: + branches: + - main +\ npermissions: contents: write jobs: @@ -15,6 +17,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + ref: ${{ github.head_ref || github.ref_name }} - name: Apply focused fix and regression shell: bash From 0cb85fefd37fddc7aba3966570f54006b42b5262 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:19:33 +0800 Subject: [PATCH 3/6] Fix temporary linear-update patch workflow --- .github/workflows/agent_patch_linear_update_symmetrization.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/agent_patch_linear_update_symmetrization.yml b/.github/workflows/agent_patch_linear_update_symmetrization.yml index 0d8339f012..6a48b12e0f 100644 --- a/.github/workflows/agent_patch_linear_update_symmetrization.yml +++ b/.github/workflows/agent_patch_linear_update_symmetrization.yml @@ -7,7 +7,8 @@ on: pull_request: branches: - main -\ npermissions: + +permissions: contents: write jobs: From f690f565e964afec07072bcbe92280d99f583a88 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:22:54 +0800 Subject: [PATCH 4/6] Stabilize linear-update covariance symmetrization --- src/pyrecest/filters/linear_update_planning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyrecest/filters/linear_update_planning.py b/src/pyrecest/filters/linear_update_planning.py index 281ee69f05..a9d38ce110 100644 --- a/src/pyrecest/filters/linear_update_planning.py +++ b/src/pyrecest/filters/linear_update_planning.py @@ -424,7 +424,7 @@ def _resolve_threshold( def _symmetrized(matrix: np.ndarray) -> np.ndarray: - return 0.5 * (matrix + matrix.T) + return 0.5 * matrix + 0.5 * matrix.T def _validate_covariance_matrix( From 7a396d18844b4378bb75923b7b8fde12eba42e48 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:23:25 +0800 Subject: [PATCH 5/6] Test extreme linear-update covariance symmetrization --- tests/filters/test_linear_update_planning.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/filters/test_linear_update_planning.py b/tests/filters/test_linear_update_planning.py index f7e2570125..eafc18821c 100644 --- a/tests/filters/test_linear_update_planning.py +++ b/tests/filters/test_linear_update_planning.py @@ -269,3 +269,20 @@ def test_plan_linear_measurement_update_uses_scale_stable_residual_norm() -> Non assert plan.accepted is True assert plan.action == "updated" assert np.isclose(plan.residual_norm, np.sqrt(2.0) * 1.0e200) + + +def test_plan_linear_measurement_update_avoids_symmetrization_overflow() -> None: + large_covariance = 0.75 * np.finfo(float).max + + with np.errstate(over="raise", invalid="raise"): + plan = plan_linear_measurement_update( + mean=np.zeros(1), + covariance_matrix=np.array([[large_covariance]]), + measurement_vector=np.zeros(1), + measurement_covariance=np.zeros((1, 1)), + observation_matrix=np.ones((1, 1)), + ) + + assert plan.accepted is True + assert np.isfinite(plan.nominal_innovation_covariance).all() + assert plan.nominal_innovation_covariance[0, 0] == large_covariance From 1a42ffb69b14172c04bc5d63a1a5df3068e8c3ba Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:23:35 +0800 Subject: [PATCH 6/6] Remove temporary patch workflow --- ...ent_patch_linear_update_symmetrization.yml | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 .github/workflows/agent_patch_linear_update_symmetrization.yml diff --git a/.github/workflows/agent_patch_linear_update_symmetrization.yml b/.github/workflows/agent_patch_linear_update_symmetrization.yml deleted file mode 100644 index 6a48b12e0f..0000000000 --- a/.github/workflows/agent_patch_linear_update_symmetrization.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Agent patch linear update symmetrization - -on: - push: - branches: - - agent/stabilize-linear-update-symmetrization - pull_request: - branches: - - main - -permissions: - contents: write - -jobs: - patch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.head_ref || github.ref_name }} - - - name: Apply focused fix and regression - shell: bash - run: | - python - <<'PY' - from pathlib import Path - - implementation = Path("src/pyrecest/filters/linear_update_planning.py") - source = implementation.read_text() - old = "def _symmetrized(matrix: np.ndarray) -> np.ndarray:\n return 0.5 * (matrix + matrix.T)\n" - new = "def _symmetrized(matrix: np.ndarray) -> np.ndarray:\n return 0.5 * matrix + 0.5 * matrix.T\n" - if old not in source: - raise SystemExit("Expected linear-update symmetrizer not found") - implementation.write_text(source.replace(old, new, 1)) - - tests = Path("tests/filters/test_linear_update_planning.py") - test_source = tests.read_text() - test_name = "test_plan_linear_measurement_update_avoids_symmetrization_overflow" - if test_name not in test_source: - test_source += '''\n\ndef test_plan_linear_measurement_update_avoids_symmetrization_overflow() -> None:\n large_covariance = 0.75 * np.finfo(float).max\n\n with np.errstate(over="raise", invalid="raise"):\n plan = plan_linear_measurement_update(\n mean=np.zeros(1),\n covariance_matrix=np.array([[large_covariance]]),\n measurement_vector=np.zeros(1),\n measurement_covariance=np.zeros((1, 1)),\n observation_matrix=np.ones((1, 1)),\n )\n\n assert plan.accepted is True\n assert np.isfinite(plan.nominal_innovation_covariance).all()\n assert plan.nominal_innovation_covariance[0, 0] == large_covariance\n''' - tests.write_text(test_source) - - Path(".github/workflows/agent_patch_linear_update_symmetrization.yml").unlink() - PY - - - name: Validate patch shape and numerical regression - shell: bash - run: | - python -m py_compile \ - src/pyrecest/filters/linear_update_planning.py \ - tests/filters/test_linear_update_planning.py - python - <<'PY' - import numpy as np - - large = 0.75 * np.finfo(float).max - matrix = np.array([[large]]) - try: - with np.errstate(over="raise", invalid="raise"): - 0.5 * (matrix + matrix.T) - except FloatingPointError: - pass - else: - raise SystemExit("Pre-fix expression unexpectedly stayed finite") - - with np.errstate(over="raise", invalid="raise"): - repaired = 0.5 * matrix + 0.5 * matrix.T - assert repaired[0, 0] == large - assert np.isfinite(repaired).all() - PY - git diff --check - - - name: Commit patch - shell: bash - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git commit -m "Stabilize linear-update covariance symmetrization" - git push origin HEAD