Repository navigation
Formalize Leo Moser's finite distinct-subset-sums inequality - #285
Conversation
Greptile SummaryThis PR formalizes Leo Moser's finite distinct-subset-sums inequality (
Confidence Score: 5/5This PR adds a self-contained, sorry-free Lean 4 formalization with no infrastructure changes; it is safe to merge. All new files are purely additive content under LeanPool/ErdosMoser/. The proof strategy is mathematically correct — the discrete variance lower bound, subset-sum moment identities, and the final Moser inequality chain correctly. The author-reported axiom footprint is consistent with standard Mathlib usage. All four-line file headers are present, no sorry/admit/#eval appear, proof bodies are well within the 200-line limit, and the project card satisfies all required fields with a valid DOI source. Files Needing Attention: No files require special attention.
|
| Filename | Overview |
|---|---|
| LeanPool/ErdosMoser.lean | Top-level entry module with module docstring and single import; correctly structured. |
| LeanPool/ErdosMoser/Basic.lean | Defines HasDistinctSubsetSums and proves sumSquaresLeCardMulMaxSquare; straightforward and correct. |
| LeanPool/ErdosMoser/DiscreteVariance.lean | Proves the discrete variance lower bound for finite sets of naturals via orderEmbOfFin and pairwise-difference identity; mathematically correct, no Lean core imports. |
| LeanPool/ErdosMoser/SubsetSums.lean | Establishes first/second moment identities for subset sums and combines them with the variance bound to prove the main Moser inequality; logic is sound. |
| LeanPool/ErdosMoser/Bounds.lean | Derives erdosMoserMaxBound and erdosMoserMaxSqrt from the main inequality; correctly uses Real.sqrt_le_left for the square-root form. |
| LeanPool/projects.yml | Project card is well-formed with required fields, valid DOI source, provenance: AI, and github_repo now correctly pointing to an external formalization repository. |
| LeanPool.lean | Adds the five new ErdosMoser imports in alphabetical position; no issues. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["HasDistinctSubsetSums\n(Basic.lean)"]
B["sumSquaresLeCardMulMaxSquare\n(Basic.lean)"]
C["varianceLowerBoundFinsetNat\n(DiscreteVariance.lean)\nm²(m²-1)/12 ≤ m·Σt² - (Σt)²"]
D["sumSubsetSums / secondMomentSubsetSums\n(SubsetSums.lean)\n1st & 2nd moment identities"]
E["leoMoserVarianceBound\n(SubsetSums.lean)\n4^|A| - 1 ≤ 3·Σa²"]
F["erdosMoserMaxBound\n(Bounds.lean)\n4^|A| - 1 ≤ 3|A|·max(A)²"]
G["erdosMoserMaxSqrt\n(Bounds.lean)\n√((4^|A|-1)/(3|A|)) ≤ max(A)"]
A --> E
C --> E
D --> E
E --> F
B --> F
F --> G
Reviews (3): Last reviewed commit: "Link Erdős-Moser companion repository" | Re-trigger Greptile
Proof profile (new / modified Lean files)
This build covers the changed modules and their dependency cones on top of the restored cache. The serial per-file sums below are useful for ranking slow files, not as a build budget. Total heartbeats: 45 maxHeartbeats units across 5 files (649 added LOC). Sum of Count-heartbeats wall-clock total: 13.62 s. Repeated import cost inside Heartbeat values come from Mathlib's LOC counts added lines in the profiled Lean files from this PR diff.
Aggregate phase totals
Slowest changed modules (from
|
| Changed module | Lake time |
|---|---|
LeanPool.ErdosMoser.SubsetSums |
5.00 s |
LeanPool.ErdosMoser.Basic |
2.90 s |
LeanPool.ErdosMoser.DiscreteVariance |
2.50 s |
LeanPool.ErdosMoser.Bounds |
1.10 s |
LeanPool.ErdosMoser |
0.87 s |
Per-file `lean --profile` output
LeanPool/ErdosMoser.lean
import took 670ms
cumulative profiling times:
elaboration 0.155ms
import 670ms
initialization 37.6ms
interpretation 121ms
linting 0.39ms
module linting 0.0011ms
overlappingInstancesLinter 0.679ms
parsing 0.0415ms
tacticAnalysis 2.27ms
real 1.64
user 0.79
sys 0.48
LeanPool/ErdosMoser/Basic.lean
import took 563ms
cumulative profiling times:
attribute application 0.0324ms
congr simp thm 0.519ms
elaboration 9.22ms
fix level params 0.0458ms
import 563ms
initialization 34.7ms
instantiate metavars 0.101ms
interpretation 124ms
let-to-have transformation 0.0301ms
linting 2.11ms
module linting 0.00152ms
overlappingInstancesLinter 1.13ms
parsing 2.38ms
process pre-definitions 0.942ms
share common exprs 0.272ms
simp 12.3ms
tactic execution 15.5ms
tacticAnalysis 7.4ms
type checking 2.46ms
typeclass inference 26.8ms
real 1.15
user 0.80
sys 0.39
LeanPool/ErdosMoser/Bounds.lean
import took 669ms
cumulative profiling times:
attribute application 0.0216ms
congr simp thm 0.588ms
elaboration 9.75ms
fix level params 0.225ms
import 669ms
initialization 28.2ms
instantiate metavars 0.326ms
interpretation 186ms
let-to-have transformation 0.0368ms
linting 4.36ms
module linting 0.00142ms
norm_num 3.89ms
overlappingInstancesLinter 1.33ms
parsing 2.33ms
process pre-definitions 2.27ms
ring 16ms
share common exprs 2.74ms
simp 0.43ms
tactic execution 33.5ms
tacticAnalysis 9.62ms
type checking 15.2ms
typeclass inference 75.7ms
real 1.33
user 1.04
sys 0.41
LeanPool/ErdosMoser/DiscreteVariance.lean
import took 575ms
cumulative profiling times:
attribute application 0.0645ms
congr simp thm 3.1ms
elaboration 40.8ms
fix level params 2.02ms
import 575ms
initialization 28.7ms
instantiate metavars 8.23ms
interpretation 670ms
let-to-have transformation 0.0937ms
linting 18.1ms
module linting 0.00141ms
norm_num 135ms
overlappingInstancesLinter 3.24ms
parsing 17.4ms
process pre-definitions 16.5ms
ring 95ms
share common exprs 27.5ms
simp 214ms
tactic execution 307ms
tacticAnalysis 38.2ms
type checking 130ms
typeclass inference 682ms
real 2.45
user 2.96
sys 0.41
LeanPool/ErdosMoser/SubsetSums.lean
import took 565ms
interpretation of Mathlib.Tactic._aux_Mathlib_Tactic_Linarith_Frontend___elabRules_Mathlib_Tactic_nlinarith_1._boxed took 1.14s
interpretation of Mathlib.Tactic._aux_Mathlib_Tactic_Linarith_Frontend___elabRules_Mathlib_Tactic_nlinarith_1._boxed took 1.17s
interpretation of Mathlib.Tactic._aux_Mathlib_Tactic_Linarith_Frontend___elabRules_Mathlib_Tactic_nlinarith_1._boxed took 630ms
cumulative profiling times:
attribute application 0.0537ms
congr simp thm 1.29ms
elaboration 37.9ms
fix level params 1.24ms
import 565ms
initialization 28.2ms
instantiate metavars 4.85ms
interpretation 3.36s
let-to-have transformation 0.0928ms
linting 27.8ms
module linting 0.00153ms
norm_num 87.3ms
overlappingInstancesLinter 3.35ms
parsing 9.63ms
process pre-definitions 13.9ms
ring 109ms
share common exprs 26.6ms
simp 67.4ms
tactic execution 174ms
tacticAnalysis 30.2ms
type checking 70.3ms
typeclass inference 571ms
real 5.03
user 5.16
sys 0.39
Advisory only — never blocks merge. Full log uploaded as the proof-profile artifact.
🤖 LLM review (
|
| Rubric | Verdict | Bottom line |
|---|---|---|
| Faithfulness | ✅ pass |
Each listed declaration matches its informal statement, with all hypotheses disclosed. |
| Novelty | ✅ pass |
The Mathlib searches and pool inventory contain no declaration proving any of the three headline bounds. |
| Significance | ✅ pass |
The named finite distinct-subset-sums inequality, supported by a reusable variance and subset-moment development, is a good-fit graduate-level additive-combinatorics project. |
| Sources | ✅ pass |
The citation metadata is internally consistent, but the diff does not reproduce Guy’s Theorem 2, so the claimed theorem number and attribution are unverifiable here. |
| Code quality (advisory) | ✅ pass |
The proof is competently decomposed into reusable combinatorial, variance, and bound lemmas; the long algebraic blocks remain structured and create no material maintenance debt. |
| Aspect | Value |
|---|---|
| Proves the claim | ✅ proves_it |
| Assumed, not proved | The main inequality assumes HasDistinctSubsetSums A; the maximum bounds additionally assume A.Nonempty, and both conditions are stated in the card. |
| Matches cited source | 🟡 unverifiable |
| Fit | ✅ good_fit |
| Level | graduate |
| Branch | additive combinatorics |
| Mode | theory_building |
| Code quality | 3 / 5 |
Statement check: The Lean proves the real-valued finite sum-of-squares inequality and, for nonempty A, the stated direct and square-root bounds on A.max'.
The contribution derives (4 : ℝ) ^ A.card - 1 ≤ 3 * ∑ a ∈ A, (a : ℝ) ^ 2 from a sharp discrete-variance bound and exact subset-sum moment identities, then obtains largest-element lower bounds.
Tokens: 74,742 in / 6,512 out across 5 rubric calls · Tier: flex · Effort: xhigh · Cost: $0.2845
Each rubric is an independent review against .github/review-rubrics/ on top of .github/REVIEW_RULES.md. Disagree? Reply on the PR; rules can be updated in a PR of their own.
* Generate NOTICE from the registry; automate Mathlib bumps
NOTICE had drifted badly: 75 of 141 projects had no attribution entry at
all, which is an Apache-2.0 section 4(d) and MIT notice gap for every one
of them. Rather than lint a hand-maintained file, generate it.
- python/lean_pool/notice.py builds NOTICE from LeanPool/projects.yml
(which already carries `license` and `source.github_repo` for all 141)
plus NOTICE.extra.yml for the prose that cannot be derived: MIT
copyright lines, relicensing statements, upstream citation requests.
- notice.yml regenerates after merge, so drift cannot persist. It is not
a PR gate on purpose: a content PR may not touch NOTICE under
content-pr-guard, so gating there would be unsatisfiable.
- Regenerating also corrected four stale upstream URLs whose repositories
had been renamed (BrauerGroup_new, FLDutchmann/selberg-sieve4, RMT4,
AxiomMath/fel-polynomial).
mathlib-bump.yml migrates the pool to a new release in stages, only the
last of which needs a human: detect a newer tag, move the four pins and
plan shards, probe-build every project in parallel, triage the logs into a
per-project breakage map, fan out one Claude repair job per broken project,
then reassemble and open a draft PR. Pool projects never import each other,
so a bump decomposes into independent per-project repairs; the assemble
stage still rebuilds the whole pool, which is what catches the
cross-project effects per-project repair cannot see.
Repair jobs authenticate with a Claude subscription token and upload
patches rather than pushing, so parallel jobs cannot race the branch.
Probing is free and runs nightly regardless, so a release never lands as a
surprise; `repair: auto` spends quota on final releases only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Bump to the newest release; auto-rebase open PRs on merge
Bumps now target the newest available release, candidates included: mid
release-cycle that is what "latest Lean and Mathlib" means, and the pool
tracks the latest. `detect` therefore reports v4.33.0-rc1 rather than
v4.32.1, `--stable-only` opts out, and the repair fan-out no longer skips
rc targets (it would otherwise never have run).
auto-rebase.yml keeps the import queue mergeable without hand-holding.
When a content PR lands, every other open import PR conflicts in exactly
two files, and in both the resolution is mechanical:
- LeanPool.lean is a sorted list of imports regenerated from the file
tree. It reproduces the committed 2,885-line index byte-for-byte, so
the job needs no Lean toolchain and runs in seconds.
- LeanPool/projects.yml takes the merged base's registry plus the cards
the branch adds, moved as verbatim text blocks. Round-tripping 141
cards through a YAML dumper would reformat every one and bury the
real change.
Any other conflicted path is a genuine content overlap: the merge is
abandoned and the PR labelled needs-manual-rebase rather than guessed at.
Verified against real pull request data by simulating #285 landing, which
makes #287 conflict exactly as predicted. The regenerated index is the
exact sorted union of both branches' modules with no conflict markers, and
the merged registry is 143 cards with no duplicates, valid YAML, required
fields intact, and the 142 pre-existing cards byte-identical.
Pushing to a fork branch needs REBASE_TOKEN (a GitHub App installation
token or a PAT); GITHUB_TOKEN has no write access to forks even with
"Allow edits by maintainers" set. Without it, fork PRs are labelled
instead of rebased and same-repo PRs still work.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
/review — re-run to live-test the gpt-5.6-sol @ xhigh upgrade (752d7bf); comparing cost against the gpt-5.5 baseline on the same head. |
|
/review — first run of the audited rules (#293) now that they are on main. |
Follow-up to #293, from watching its first production run. The review of #285 came back `approve` while the assessment table showed `Already formalized | 🛑 unverifiable from the supplied diff`. The model answered the prior-art question in a field specified to hold a declaration name or nothing, and the renderer stamped 🛑 on whatever it found — so a PR the review approved carried a red row reading "this is a duplicate". Prior art is a request_changes ground, so the row is not cosmetic. _prior_art_cell now renders a multi-word value as a hedge and keeps the stop icon for an actual declaration. The challenge renderer had the same latent bug and shares the helper. Both rules docs now say the field is a bare declaration name, to leave it empty when nothing was found or it could not be checked, and never to use it to report unverifiability — source_match already has a value for that. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
/review — confirming the prior-art rendering fix (#303). |
`--jq ".content"` was paired with `Accept: application/vnd.github.raw+json`. The raw media type returns file bytes, so gh tried to parse YAML as JSON, printed "invalid character 'p' looking for beginning of value", and exited 1. That was caught and turned into "", which `new_claims` read as an empty registry — so every prior-art section would have said "this PR adds no new headline", and no Mathlib search would ever have run. My dry run missed it by fabricating the head registry instead of fetching one. Verified against PR #285's head now: 248k characters, three headlines. Two guards so the failure cannot be silent again. `gather_prior_art` distinguishes an unreadable registry from a PR that genuinely adds nothing — both give zero claims, but only one means prior art went unchecked — and a test pins the gh argument shape, since the broken and working forms differ only by a flag. Caught by Greptile on #306. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
REVIEW_RULES.md asks whether a headline is already proved; the reviewer had no way to find out, and its first production run answered `already_formalized` with "unverifiable from the supplied diff". That question has already cost two merges: #275 was approved with zero findings and closed by hand for duplicating `SimpleGraph.nonempty_hom_of_forall_finite_subgraph_hom`, and #225 was closed as subsumed by the pool's own `Incompleteness`. Two searches now run before the model is asked. Mathlib, via LeanExplore's hybrid name-and-meaning search over the (declaration, informal) pairs already in the card: #275's headline returns the exact declaration it duplicated, fifth of six, while #285's returns nothing relevant — so an empty result is usable as evidence of novelty rather than a shrug. And the pool, as all 141 projects in `slug: title [tags]` form, ~4k tokens; no similarity heuristic beats the complete list. Pre-computed rather than offered as a tool call: deterministic, testable, one round trip, and it sidesteps the reasoning_effort + function-tools incompatibility on gpt-5.6-sol. Scoped to project and challenge PRs and to headlines the PR actually adds. Results sit outside the contributor fence since they are ours, but the rules say a hit is a candidate and that shared vocabulary is not duplication. Never fatal, and never silently wrong: a missing key, a dead backend, an unreadable registry, and a single timed-out query each render as a stated "not searched" that the rules distinguish from a clean miss. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
/review — first run with the Mathlib prior-art search (#306). |
|
/review — first run of the five-rubric review (#313). |
This PR formalizes the finite inequality stated as Theorem 2 in Richard K. Guy’s 1982 account and credited there to Leo Moser:
4 ^ |A| - 1 ≤ 3 * ∑ a ∈ A, a ^ 2for a finite set of natural numbers with distinct subset sums. It also derives direct and square-root lower bounds for the largest element.
This is a known finite result. I am not claiming the stronger historical Erdős–Moser asymptotic bound, and Erdős Problem #1 remains open.
Source: Richard K. Guy, “Sets of Integers Whose Subsets Have Distinct Sums,” Theorem 2, Annals of Discrete Mathematics 12 (1982), 141–154. DOI: https://doi.org/10.1016/S0304-0208(08)73500-X
Local checks:
lake exe mk_all --checklake build LeanPool.ErdosMoserlake exe runLinter LeanPool.ErdosMoserlake exe lint-style LeanPool.ErdosMosergit diff --checkscanspropext,Classical.choice,Quot.soundAI disclosure: AI agents produced and refactored the Lean proof under my direction. I reviewed the theorem scope, source attribution, and verification results.