Skip to content

Commit f3d4440

Browse files
wmaynerclaude
andcommitted
Saturate the over-size subset sum and the boundary case of the subset-minimum sum
sum_of_minimum_over_size_among_subsets raised OverflowError once a shared-atom group exceeded 1023 values: its Python-int coefficient 2**(a+1) exceeds float64 range at the division. The coefficient now saturates to +inf — the same uninformative ceiling as sum_of_minimum_among_subsets — with zero values contributing nothing. In sum_of_minimum_among_subsets, every count can fit float64 while their sum does not (exactly at 1024 values); the summation now runs under the same errstate so the total saturates to inf instead of escalating an overflow warning under warning-as-error configurations. Also adds the changelog fragments and roadmap record for the full set of condor smoke-test fixes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015f42ka7k2RhfsyKFLqyLdv
1 parent 5d05fe5 commit f3d4440

7 files changed

Lines changed: 87 additions & 4 deletions

ROADMAP.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,26 @@ item), mutation testing (N3/N17 ← T2), and the matching exact-oracle/standard-
744744
above a feasibility bound, and `collect` warns before computing a missing
745745
resolution state past ~12 units.
746746

747+
### 2026-07-23 CHTC smoke-test fixes
748+
749+
> The first real condor submission (13 shards on a production pool; the collected
750+
> structure matched the local run exactly) surfaced six bugs. Two were already fixed
751+
> (submit-file task-id padding via `$INT(task_id,%04d)`; the `request_memory` column,
752+
> landed with the 2026-07-21 follow-ups — the report was written against an older
753+
> checkout). **The other four landed 2026-07-23:** `preserve_relative_paths = true` in
754+
> the submit template (HTCondor flattens single transferred files to the scratch root,
755+
> so every job died on `FileNotFoundError`; invisible to the local runner, which
756+
> bypasses the submit file); int64 wraparound in
757+
> `combinatorics.sum_of_minimum_among_subsets` past 63 shared distinctions — silent
758+
> Σφ_r corruption in `AnalyticalRelations.sum_phi()` and the scope report — now exact
759+
> through int64's range and saturating to `inf` beyond float64's, with the same
760+
> saturation in `sum_of_minimum_over_size_among_subsets` (which raised `OverflowError`
761+
> past 1023 values); the measured bounds' `2.0**k` weight saturating to the documented
762+
> `inf` ceiling instead of raising past 1023 shared distinctions (crashed `collect`'s
763+
> scope report at production scale); and a clear `OverflowError` from
764+
> `AnalyticalRelations.__len__` pointing at `.num_relations()` when the closed-form
765+
> count exceeds `sys.maxsize` (the MCP result summary falls back accordingly).
766+
747767
## Context
748768

749769
PyPhi is a scientific library implementing Integrated Information Theory (IIT). It has
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
`len()` on `AnalyticalRelations` whose closed-form relation count
2+
exceeds `sys.maxsize` now raises an `OverflowError` that points at
3+
`.num_relations()`, instead of the bare protocol failure; the MCP
4+
result summary falls back accordingly.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
The generated condor submit file sets `preserve_relative_paths = true`.
2+
Without it, HTCondor flattens the transferred task file to the scratch
3+
root while `run_task.sh` expects it under `tasks/`, so every job on a
4+
real pool failed with `FileNotFoundError` (the local runner bypasses
5+
the submit file and never saw it).
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
`sum_phi_relations_measured_bound` (and `big_phi_measured_bound`) raised
2+
`OverflowError` when more than 1023 distinctions shared a purview atom —
3+
crashing `collect`'s scope report at production scale. The 2^k weight
4+
now saturates to the documented `inf` ceiling, and zero-density groups
5+
contribute nothing regardless of size.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
`combinatorics.sum_of_minimum_among_subsets` computed its subset counts
2+
in int64, silently wrapping once more than 63 values shared an atom —
3+
corrupting `AnalyticalRelations.sum_phi()` (and the campaign scope
4+
report's Σφ_r) on large structures. Counts now stay exact through
5+
int64's range and saturate to `inf` beyond float64's, with zero values
6+
contributing nothing at any size.
7+
`sum_of_minimum_over_size_among_subsets` likewise saturates instead of
8+
raising `OverflowError` past 1023 values.

pyphi/combinatorics.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ def sum_of_minimum_among_subsets(values: Sequence[float]) -> float:
123123
# shared by that many distinctions.
124124
counts = 2.0**exponents - 1.0
125125
terms = sorted_values * counts
126-
terms[sorted_values == 0.0] = 0.0 # a zero value contributes 0, not 0*inf=nan
127-
return float(np.sum(terms))
126+
terms[sorted_values == 0.0] = 0.0 # a zero value contributes 0, not 0*inf=nan
127+
# Each term can fit float64 while their sum does not; an overflowed
128+
# sum is exactly the saturated infinite total.
129+
return float(np.sum(terms))
128130

129131

130132
def sum_of_minimum_over_size_among_subsets(values: Sequence[float]) -> float:
@@ -149,8 +151,17 @@ def sum_of_minimum_over_size_among_subsets(values: Sequence[float]) -> float:
149151
for i in range(n):
150152
a = n - 1 - i
151153
if a > 0:
152-
coefficients[i] = (2 ** (a + 1) - 1 - (a + 1)) / (a + 1)
153-
return float(np.sum(sorted_values * coefficients))
154+
if a + 1 >= 1024:
155+
# 2**(a + 1) exceeds float64 range (the division would raise);
156+
# the coefficient saturates to +inf, the same uninformative
157+
# ceiling as in :func:`sum_of_minimum_among_subsets`.
158+
coefficients[i] = math.inf
159+
else:
160+
coefficients[i] = (2 ** (a + 1) - 1 - (a + 1)) / (a + 1)
161+
with np.errstate(over="ignore", invalid="ignore"):
162+
terms = sorted_values * coefficients
163+
terms[sorted_values == 0.0] = 0.0 # a zero value contributes 0, not 0*inf=nan
164+
return float(np.sum(terms))
154165

155166

156167
def sum_of_minimum_of_size_among_subsets(values: Sequence[float], size: int) -> float:

test/test_combinatorics.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,33 @@ def ref(values): # exact via Python big ints
293293
assert math.isclose(f(v70), ref(v70), rel_tol=1e-9) and f(v70) > 1e18
294294
assert f([0.1] * 1100) == math.inf # saturates, no raise
295295
assert f([0.0] * 1100 + [0.2]) == 0.0 # zeros give 0, not 0*inf=nan
296+
297+
298+
def test_sum_of_minimum_among_subsets_boundary_sum_saturates():
299+
"""At n=1024 every count fits float64 but their sum does not; the total
300+
saturates to inf instead of escalating an overflow warning."""
301+
import numpy as np
302+
303+
assert combinatorics.sum_of_minimum_among_subsets(np.ones(1024)) == math.inf
304+
305+
306+
def test_sum_of_minimum_over_size_among_subsets_saturates_to_inf():
307+
"""Raised OverflowError once a group exceeded 1023 values."""
308+
import numpy as np
309+
310+
values = np.ones(1100)
311+
assert combinatorics.sum_of_minimum_over_size_among_subsets(values) == math.inf
312+
313+
314+
def test_sum_of_minimum_over_size_among_subsets_zero_values_contribute_nothing():
315+
"""Zero values carry the overflowing coefficients but contribute 0, so
316+
the total reduces to the nonzero tail's."""
317+
import numpy as np
318+
319+
values = np.concatenate([np.zeros(300), np.ones(1000)])
320+
result = combinatorics.sum_of_minimum_over_size_among_subsets(values)
321+
assert math.isfinite(result)
322+
assert result == pytest.approx(
323+
combinatorics.sum_of_minimum_over_size_among_subsets(np.ones(1000)),
324+
rel=1e-12,
325+
)

0 commit comments

Comments
 (0)