From 6fe5046b090a830acab54acb31d174865fdf2d53 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:04:42 +0800 Subject: [PATCH 1/5] Add one-shot tensor-train overflow patch workflow --- .../one-shot-tensor-train-overflow-fix.yml | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 .github/workflows/one-shot-tensor-train-overflow-fix.yml diff --git a/.github/workflows/one-shot-tensor-train-overflow-fix.yml b/.github/workflows/one-shot-tensor-train-overflow-fix.yml new file mode 100644 index 0000000000..51808b1358 --- /dev/null +++ b/.github/workflows/one-shot-tensor-train-overflow-fix.yml @@ -0,0 +1,148 @@ +name: One-shot tensor-train overflow fix + +on: + push: + branches: + - agent/stabilize-tensor-train-relative-truncation + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: agent/stabilize-tensor-train-relative-truncation + + - name: Apply source and regression patches + shell: bash + run: | + python - <<'PY' + from pathlib import Path + + source_path = Path("src/pyrecest/distributions/hypertorus/_tensor_train.py") + source = source_path.read_text() + old_choose_rank = '''def _choose_rank(singular_values, max_rank, local_tolerance): + max_rank = _normalize_max_rank(max_rank) + full_rank = singular_values.size + if local_tolerance <= 0: + rank = full_rank + else: + squared_tail = np.cumsum(singular_values[::-1] ** 2)[::-1] + rank = full_rank + for candidate in range(1, full_rank + 1): + tail = ( + 0.0 if candidate == full_rank else sqrt(float(squared_tail[candidate])) + ) + if tail <= local_tolerance: + rank = candidate + break + if max_rank is not None: + rank = min(rank, max_rank) + return max(1, rank) + ''' + new_choose_rank = '''def _stable_vector_norm(values): + """Return a scale-safe Euclidean norm for finite array values.""" + magnitudes = np.abs(np.asarray(values)) + if magnitudes.size == 0: + return 0.0 + scale = float(np.max(magnitudes)) + if scale == 0.0 or not np.isfinite(scale): + return scale + return scale * sqrt(float(np.sum((magnitudes / scale) ** 2))) + + + def _choose_rank(singular_values, max_rank, local_tolerance): + max_rank = _normalize_max_rank(max_rank) + full_rank = singular_values.size + if local_tolerance <= 0: + rank = full_rank + else: + scale = float(np.max(np.abs(singular_values))) + if scale == 0.0: + rank = 1 + else: + scaled_values = singular_values / scale + squared_tail = np.cumsum(scaled_values[::-1] ** 2)[::-1] + scaled_tolerance = local_tolerance / scale + rank = full_rank + for candidate in range(1, full_rank + 1): + tail = ( + 0.0 + if candidate == full_rank + else sqrt(float(squared_tail[candidate])) + ) + if tail <= scaled_tolerance: + rank = candidate + break + if max_rank is not None: + rank = min(rank, max_rank) + return max(1, rank) + ''' + if old_choose_rank not in source: + raise SystemExit("expected _choose_rank implementation not found") + source = source.replace(old_choose_rank, new_choose_rank, 1) + + old_norm = " norm = float(np.linalg.norm(array.ravel()))\n" + new_norm = " norm = _stable_vector_norm(array.ravel())\n" + if old_norm not in source: + raise SystemExit("expected dense norm calculation not found") + source = source.replace(old_norm, new_norm, 1) + source_path.write_text(source) + + test_path = Path("tests/distributions/test_hypertoroidal_tensor_train.py") + tests = test_path.read_text() + anchor = ''' def test_frobenius_norm(self): + ''' + regression = ''' def test_relative_truncation_handles_large_finite_values(self): + tensor = np.diag([1e200, 1e200]) + with np.errstate(over="raise", invalid="raise"): + tt = TensorTrain.from_dense(tensor, rtol=1e-12) + + self.assertEqual(tt.ranks, (1, 2, 1)) + npt.assert_allclose(tt.to_dense() / 1e200, np.eye(2), atol=1e-12) + + ''' + if anchor not in tests: + raise SystemExit("expected tensor-train test anchor not found") + tests = tests.replace(anchor, regression + anchor, 1) + test_path.write_text(tests) + + Path(".github/workflows/one-shot-tensor-train-overflow-fix.yml").unlink() + PY + + - name: Validate focused regression + run: | + python -m py_compile \ + src/pyrecest/distributions/hypertorus/_tensor_train.py \ + tests/distributions/test_hypertoroidal_tensor_train.py + python - <<'PY' + import importlib.util + from pathlib import Path + + import numpy as np + + module_path = Path("src/pyrecest/distributions/hypertorus/_tensor_train.py") + spec = importlib.util.spec_from_file_location("tensor_train", module_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + tensor = np.diag([1e200, 1e200]) + with np.errstate(over="raise", invalid="raise"): + tt = module.TensorTrain.from_dense(tensor, rtol=1e-12) + np.testing.assert_allclose(tt.to_dense() / 1e200, np.eye(2), atol=1e-12) + assert tt.ranks == (1, 2, 1) + PY + + - name: Commit patch + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add \ + src/pyrecest/distributions/hypertorus/_tensor_train.py \ + tests/distributions/test_hypertoroidal_tensor_train.py \ + .github/workflows/one-shot-tensor-train-overflow-fix.yml + git commit -m "Stabilize tensor-train truncation norms" + git push origin HEAD:agent/stabilize-tensor-train-relative-truncation From 282dd37e5b7022900fe8fd773fc07b6d4305d5ee Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:06:00 +0800 Subject: [PATCH 2/5] Trigger one-shot tensor-train patch from pull request --- .github/workflows/one-shot-tensor-train-overflow-fix.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/one-shot-tensor-train-overflow-fix.yml b/.github/workflows/one-shot-tensor-train-overflow-fix.yml index 51808b1358..2573b29173 100644 --- a/.github/workflows/one-shot-tensor-train-overflow-fix.yml +++ b/.github/workflows/one-shot-tensor-train-overflow-fix.yml @@ -1,15 +1,16 @@ name: One-shot tensor-train overflow fix on: - push: + pull_request: branches: - - agent/stabilize-tensor-train-relative-truncation + - main permissions: contents: write jobs: patch: + if: github.head_ref == 'agent/stabilize-tensor-train-relative-truncation' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From afb49d604c45bbb24b502ecb89aedd8c4b70746c Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:07:15 +0800 Subject: [PATCH 3/5] Stabilize tensor-train truncation norms --- .../distributions/hypertorus/_tensor_train.py | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/pyrecest/distributions/hypertorus/_tensor_train.py b/src/pyrecest/distributions/hypertorus/_tensor_train.py index ca71eb5ccd..59afff0ea1 100644 --- a/src/pyrecest/distributions/hypertorus/_tensor_train.py +++ b/src/pyrecest/distributions/hypertorus/_tensor_train.py @@ -65,21 +65,40 @@ def _check_dense_validation_size(size, max_entries): ) +def _stable_vector_norm(values): + """Return a scale-safe Euclidean norm for finite array values.""" + magnitudes = np.abs(np.asarray(values)) + if magnitudes.size == 0: + return 0.0 + scale = float(np.max(magnitudes)) + if scale == 0.0 or not np.isfinite(scale): + return scale + return scale * sqrt(float(np.sum((magnitudes / scale) ** 2))) + + def _choose_rank(singular_values, max_rank, local_tolerance): max_rank = _normalize_max_rank(max_rank) full_rank = singular_values.size if local_tolerance <= 0: rank = full_rank else: - squared_tail = np.cumsum(singular_values[::-1] ** 2)[::-1] - rank = full_rank - for candidate in range(1, full_rank + 1): - tail = ( - 0.0 if candidate == full_rank else sqrt(float(squared_tail[candidate])) - ) - if tail <= local_tolerance: - rank = candidate - break + scale = float(np.max(np.abs(singular_values))) + if scale == 0.0: + rank = 1 + else: + scaled_values = singular_values / scale + squared_tail = np.cumsum(scaled_values[::-1] ** 2)[::-1] + scaled_tolerance = local_tolerance / scale + rank = full_rank + for candidate in range(1, full_rank + 1): + tail = ( + 0.0 + if candidate == full_rank + else sqrt(float(squared_tail[candidate])) + ) + if tail <= scaled_tolerance: + rank = candidate + break if max_rank is not None: rank = min(rank, max_rank) return max(1, rank) @@ -153,7 +172,7 @@ def from_dense(cls, tensor, *, max_rank=None, rtol=0.0, atol=0.0): if array.ndim == 1: return cls((array.reshape(1, array.shape[0], 1),)) - norm = float(np.linalg.norm(array.ravel())) + norm = _stable_vector_norm(array.ravel()) global_tolerance = max(atol, rtol * norm) local_tolerance = ( global_tolerance / sqrt(array.ndim - 1) if global_tolerance > 0 else 0.0 From 564fd7e082b4e1221401dad1e38c83463986dde5 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:07:39 +0800 Subject: [PATCH 4/5] Test large finite tensor-train truncation --- tests/distributions/test_hypertoroidal_tensor_train.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/distributions/test_hypertoroidal_tensor_train.py b/tests/distributions/test_hypertoroidal_tensor_train.py index 0a82bbe6e0..0631df4ce4 100644 --- a/tests/distributions/test_hypertoroidal_tensor_train.py +++ b/tests/distributions/test_hypertoroidal_tensor_train.py @@ -13,6 +13,14 @@ def test_dense_roundtrip_and_entry_access(self): self.assertEqual(tt.shape, (3, 3, 3)) npt.assert_allclose(tt.entry((1, 2, 0)), tensor[1, 2, 0], atol=1e-12) + def test_relative_truncation_handles_large_finite_values(self): + tensor = np.diag([1e200, 1e200]) + with np.errstate(over="raise", invalid="raise"): + tt = TensorTrain.from_dense(tensor, rtol=1e-12) + + self.assertEqual(tt.ranks, (1, 2, 1)) + npt.assert_allclose(tt.to_dense() / 1e200, np.eye(2), atol=1e-12) + def test_frobenius_norm(self): tensor = np.arange(9, dtype=float).reshape(3, 3) - 2.0 tt = TensorTrain.from_dense(tensor) From afd8bfe875ddf955f0477e9f6131e82f69df5169 Mon Sep 17 00:00:00 2001 From: Florian Pfaff <6773539+FlorianPfaff@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:07:45 +0800 Subject: [PATCH 5/5] Remove one-shot patch workflow --- .../one-shot-tensor-train-overflow-fix.yml | 149 ------------------ 1 file changed, 149 deletions(-) delete mode 100644 .github/workflows/one-shot-tensor-train-overflow-fix.yml diff --git a/.github/workflows/one-shot-tensor-train-overflow-fix.yml b/.github/workflows/one-shot-tensor-train-overflow-fix.yml deleted file mode 100644 index 2573b29173..0000000000 --- a/.github/workflows/one-shot-tensor-train-overflow-fix.yml +++ /dev/null @@ -1,149 +0,0 @@ -name: One-shot tensor-train overflow fix - -on: - pull_request: - branches: - - main - -permissions: - contents: write - -jobs: - patch: - if: github.head_ref == 'agent/stabilize-tensor-train-relative-truncation' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: agent/stabilize-tensor-train-relative-truncation - - - name: Apply source and regression patches - shell: bash - run: | - python - <<'PY' - from pathlib import Path - - source_path = Path("src/pyrecest/distributions/hypertorus/_tensor_train.py") - source = source_path.read_text() - old_choose_rank = '''def _choose_rank(singular_values, max_rank, local_tolerance): - max_rank = _normalize_max_rank(max_rank) - full_rank = singular_values.size - if local_tolerance <= 0: - rank = full_rank - else: - squared_tail = np.cumsum(singular_values[::-1] ** 2)[::-1] - rank = full_rank - for candidate in range(1, full_rank + 1): - tail = ( - 0.0 if candidate == full_rank else sqrt(float(squared_tail[candidate])) - ) - if tail <= local_tolerance: - rank = candidate - break - if max_rank is not None: - rank = min(rank, max_rank) - return max(1, rank) - ''' - new_choose_rank = '''def _stable_vector_norm(values): - """Return a scale-safe Euclidean norm for finite array values.""" - magnitudes = np.abs(np.asarray(values)) - if magnitudes.size == 0: - return 0.0 - scale = float(np.max(magnitudes)) - if scale == 0.0 or not np.isfinite(scale): - return scale - return scale * sqrt(float(np.sum((magnitudes / scale) ** 2))) - - - def _choose_rank(singular_values, max_rank, local_tolerance): - max_rank = _normalize_max_rank(max_rank) - full_rank = singular_values.size - if local_tolerance <= 0: - rank = full_rank - else: - scale = float(np.max(np.abs(singular_values))) - if scale == 0.0: - rank = 1 - else: - scaled_values = singular_values / scale - squared_tail = np.cumsum(scaled_values[::-1] ** 2)[::-1] - scaled_tolerance = local_tolerance / scale - rank = full_rank - for candidate in range(1, full_rank + 1): - tail = ( - 0.0 - if candidate == full_rank - else sqrt(float(squared_tail[candidate])) - ) - if tail <= scaled_tolerance: - rank = candidate - break - if max_rank is not None: - rank = min(rank, max_rank) - return max(1, rank) - ''' - if old_choose_rank not in source: - raise SystemExit("expected _choose_rank implementation not found") - source = source.replace(old_choose_rank, new_choose_rank, 1) - - old_norm = " norm = float(np.linalg.norm(array.ravel()))\n" - new_norm = " norm = _stable_vector_norm(array.ravel())\n" - if old_norm not in source: - raise SystemExit("expected dense norm calculation not found") - source = source.replace(old_norm, new_norm, 1) - source_path.write_text(source) - - test_path = Path("tests/distributions/test_hypertoroidal_tensor_train.py") - tests = test_path.read_text() - anchor = ''' def test_frobenius_norm(self): - ''' - regression = ''' def test_relative_truncation_handles_large_finite_values(self): - tensor = np.diag([1e200, 1e200]) - with np.errstate(over="raise", invalid="raise"): - tt = TensorTrain.from_dense(tensor, rtol=1e-12) - - self.assertEqual(tt.ranks, (1, 2, 1)) - npt.assert_allclose(tt.to_dense() / 1e200, np.eye(2), atol=1e-12) - - ''' - if anchor not in tests: - raise SystemExit("expected tensor-train test anchor not found") - tests = tests.replace(anchor, regression + anchor, 1) - test_path.write_text(tests) - - Path(".github/workflows/one-shot-tensor-train-overflow-fix.yml").unlink() - PY - - - name: Validate focused regression - run: | - python -m py_compile \ - src/pyrecest/distributions/hypertorus/_tensor_train.py \ - tests/distributions/test_hypertoroidal_tensor_train.py - python - <<'PY' - import importlib.util - from pathlib import Path - - import numpy as np - - module_path = Path("src/pyrecest/distributions/hypertorus/_tensor_train.py") - spec = importlib.util.spec_from_file_location("tensor_train", module_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - - tensor = np.diag([1e200, 1e200]) - with np.errstate(over="raise", invalid="raise"): - tt = module.TensorTrain.from_dense(tensor, rtol=1e-12) - np.testing.assert_allclose(tt.to_dense() / 1e200, np.eye(2), atol=1e-12) - assert tt.ranks == (1, 2, 1) - PY - - - name: Commit patch - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add \ - src/pyrecest/distributions/hypertorus/_tensor_train.py \ - tests/distributions/test_hypertoroidal_tensor_train.py \ - .github/workflows/one-shot-tensor-train-overflow-fix.yml - git commit -m "Stabilize tensor-train truncation norms" - git push origin HEAD:agent/stabilize-tensor-train-relative-truncation