Skip to content

feat: extend tendsto_cont with dischargers, nhdsWithin, and trace mode - #384

Open
cameronfreer wants to merge 30 commits into
thefundamentaltheor3m:mainfrom
cameronfreer:cameronfreer/atom-engine
Open

feat: extend tendsto_cont with dischargers, nhdsWithin, and trace mode#384
cameronfreer wants to merge 30 commits into
thefundamentaltheor3m:mainfrom
cameronfreer:cameronfreer/atom-engine

Conversation

@cameronfreer

@cameronfreer cameronfreer commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • tendsto_cont (disch := tac) — pass a discharger to fun_prop for continuity side conditions (inv, div, log)
  • tendsto_cont (within_disch := tac) — discharge ∀ᶠ x in l, body x ∈ s obligations for nhdsWithin goals; auto-lifts pointwise proofs via Filter.univ_mem'
  • nhdsWithin support — accepts hypotheses (wrapped via mono_right), goals (split via tendsto_nhdsWithin_iff), and @[tendsto_cont] attributes
  • tendsto_cont? — trace mode reporting matched atoms, computed limit, nhdsWithin set info, and discharge method

Also adds compound-atom test cases for the compound-atom pattern reported as a gap in the fun_prop comparison (discussion).

Both hooks compose in either order.

Depends on #378 (cameronfreer/tendsto-cont-attr). Please merge that first.

Architecture

The reusable composite-atom discovery and product-abstraction logic is extracted into AtomEngine.lean, with TendstoCont.lean as the first client. This isolates reusable atom-matching logic for future tactic work.

New syntax

tendsto_cont (disch := positivity)
tendsto_cont (within_disch := exact hev)
tendsto_cont (disch := positivity) (within_disch := exact hev)
tendsto_cont?

Test coverage

Adds extensive tests covering compound atoms, nhdsWithin (hypotheses, goals, cross-bucket shadowing, ambiguity, @[tendsto_cont] with opaque nhdsWithin function), dischargers (disch/within_disch with pointwise lift, both hooks composed, reversed order), tendsto_cont? trace assertions via #guard_msgs (all five discharge outcomes), and negative tests for undischargeable goals and attribute validation.

Motivation

During discussion around mathlib #37056, Tomas Skrivan reported that fun_prop handled the existing tendsto_cont suite except the compound-atom cases — hypotheses about composed functions like Tendsto (fun x => f (g x)) l (nhds a). This branch adds compound-atom tests as a richer comparison suite and extracts the atom-matching engine so the advantage is reusable.

… arg syntax

Add two features to the `tendsto_cont` tactic:

- `@[tendsto_cont]` attribute to permanently register closed `Tendsto`
  lemmas for automatic use (global scope only; local/scoped rejected)
- `tendsto_cont [h₁, h₂]` syntax to pass lemmas inline without
  bringing them into the local context via `have`

Three-bucket candidate collection with cross-bucket shadowing:
inline args > local context > attribute registry. Candidates from a
lower-priority source are dropped if a higher-priority source provides
a candidate with the same `fn`, preserving within-source ambiguity
detection.

The environment extension is in a separate file
(TendstoContAttr.lean) so that `meta` tactic code can reference it
from a pre-compiled module.
The attribute handler now checks that the declaration's type is
`Tendsto f l (nhds a)` (after stripping forall binders). Non-Tendsto
declarations and wrong-target-filter declarations are rejected with
descriptive errors instead of being silently ignored at tactic time.

Also rename unused `h₁` to `_h₁` in the shadowing test to silence
the linter warning.
The attribute now rejects any declaration with binders (forall/Pi
types) up front, enforcing the "closed lemmas only" contract.
Previously, parameterized theorems were accepted at registration
but silently ignored at tactic time via catch/continue.
Use Bool with the indiscrete topology (⊤) where nhds = ⊤ and Tendsto
holds trivially for any limit. This lets us register two @[tendsto_cont]
lemmas for the same function with different limits (false vs true)
without sorry, triggering the same-level ambiguity error.
Add tests showing inline and local-context candidates correctly shadow
conflicting attribute-registered candidates, resolving the ambiguity
that would otherwise error with bare `tendsto_cont`.
Replace the mixed shadowing tests with a separate AttrShadowing section
that has a single attributed lemma (limit false) and tests that local
context and inline args correctly shadow it with limit true. The goal
uses nhds true, so if shadowing fails the proof genuinely breaks.

Also fix copyright year in TendstoContAttr.lean (2025 → 2026).
The shadowing tests verify that a higher-priority candidate prevents
the ambiguity error, but cannot verify that the correct limit is
actually used: in any topology where both Tendsto facts are provable,
nhds values coincide and reconcileLimits bridges the gap regardless.
Replace the indiscrete-topology shadowing tests with a Sierpinski-style
topology via mkOfNhds where nhds false = ⊤ ≠ pure true = nhds true.
Both Tendsto facts remain provable (good always returns true), but
using the wrong limit now genuinely fails reconcileLimits.

Also fix copyright year in TendstoContAttr.lean (2025 → 2026).
Replace trivial positive tests for `tendsto_cont [...]` and
`@[tendsto_cont]` with failure-before-success patterns using
private defs/theorems and #guard_msgs, so CI enforces that the
features are genuinely needed.
Emit a warning when `tendsto_cont [h]` is passed a local hypothesis
that would have been picked up automatically, unless the inline arg
serves a disambiguation purpose (conflicting limits for the same atom).
The redundancy warning for inline FVars now also checks other inline
arguments for same-fn/different-limit conflicts, not just the local
context. Prevents false warnings when an inline FVar disambiguates
against a non-local inline arg.
Add failure-before-success test using an opaque axiomatized function
where fun_prop can't help, making the @[tendsto_cont] attribute
genuinely load-bearing.
Extract the composite-atom discovery and product-abstraction logic from
tendsto_cont into a reusable AtomEngine module, keeping tendsto_cont as
the first client.

New features:
- `tendsto_cont (disch := tac)`: pass a discharger to fun_prop for side
  conditions (inv, div, log, sqrt, etc.)
- `tendsto_cont?`: report matched atoms and computed limit before solving
- Accept `nhdsWithin` hypotheses: extracts limit and wraps via
  Tendsto.mono_right nhdsWithin_le_nhds

Also adds compound-atom test cases (Group A: pure gap isolations,
Group B: integration coverage with continuous wrappers and limit
reconciliation) motivated by Tomas Skrivan's fun_prop PR #37056.
…provenance

Two fixes from code review:

1. tendsto_cont? now reports the actual computed limit (body evaluated at
   atom values) instead of the raw product value point. Uses new
   AtomEngine.substituteAtomValues to replace atoms with their values
   directly, avoiding product projection reduction issues.

2. nhdsWithin hypothesis wrapping preserves original hypothesis via
   Atom.origHyp field, so redundancy warnings still fire correctly
   when nhdsWithin hypotheses are passed as inline args.

Adds tests for nhdsWithin redundancy warnings and tendsto_cont? with
disch.
- nhdsWithin inline shadowing (no warning when inline overrides local)
- nhdsWithin ambiguity detection (two nhdsWithin hyps, different limits)
- disch := positivity as non-norm_num discharger example
Accept goals of the form `Tendsto body l (nhdsWithin c s)`, not just
`nhds c`. Strategy: split via `tendsto_nhdsWithin_iff` into an nhds
subgoal (proved by existing machinery) and an `∀ᶠ x in l, body x ∈ s`
subgoal (closed by univ_mem/assumption/simp/trivial, or left for the
user).

Tests cover Set.univ (trivially closed), explicit ∀ᶠ hypothesis in
context, and nhdsWithin + limit reconciliation.
Fixes:
- Constant-body nhdsWithin goals now work (was hitting
  tendsto_const_nhds which only targets nhds)
- ∀ᶠ discharge uses safe tryEvGoal: tries assumption first, then
  univ_mem' only after checking the predicate reduces to True (avoids
  logged errors from failed exact elaboration)
- Updated module docs and error hints for nhdsWithin goal support

Tests:
- Constant body + Set.univ: auto-closed
- Constant body + Set.Ioi: user provides membership proof
- Atom body + undischargeable ∀ᶠ: tactic leaves subgoal (sorry test)
- Atom body + assumption finds ∀ᶠ: auto-closed
- Replace sorry-based negative test with proper #guard_msgs(error)
  asserting the exact remaining ∀ᶠ goal from nhdsWithin splitting
- Fix docs: discharge list now matches implementation (assumption +
  guarded univ_mem', not simp/trivial)
New feature:
- `tendsto_cont (within_disch := tac)`: discharge the `∀ᶠ x in l,
  body x ∈ s` obligation for nhdsWithin goals. Runs after built-in
  `assumption` and `univ_mem'` fail. Composes with `disch :=`:
  `tendsto_cont (disch := positivity) (within_disch := exact hev)`.
- `tendsto_cont?` now reports the nhdsWithin set and membership
  obligation status for nhdsWithin goals.

Tests:
- within_disch on constant body + nontrivial set
- within_disch on atom body with pointwise hypothesis
- Both disch + within_disch together
- Negative test: without within_disch, same goal left open
- tendsto_cont? trace for nhdsWithin goals
- Support both option orders: (disch := ...) (within_disch := ...)
  and (within_disch := ...) (disch := ...)
- Fix misleading doc example (positivity doesn't close ∀ᶠ goals)
- tendsto_cont? now reports when ∀ᶠ membership obligation is left
  for the user after nhdsWithin discharge attempt
- Add reversed-order option test
Pointwise lift:
- within_disch tactics are now auto-lifted via Filter.univ_mem' when
  they fail on the ∀ᶠ goal directly. This means users can write
  `within_disch := norm_num` instead of
  `within_disch := exact Filter.univ_mem' (fun _ => by norm_num)`.
- Tried first directly, then with pointwise intro.

Syntax consolidation:
- 10 syntax rules (down from 14) + 10 elab_rules (down from 20)
  via shared `tc` handler with named optional parameters.
- Cleaner, easier to maintain when adding future options.

Tests updated to use simpler within_disch invocations.
Attribute extension:
- @[tendsto_cont] now accepts nhdsWithin lemmas (not just nhds).
  The tactic already handled nhdsWithin hypotheses via mono_right;
  the attribute registrar now matches.

Trace consistency:
- tendsto_cont? on constant bodies now reports "constant body" with
  computed limit and nhdsWithin set info (was silently returning).
- EvDischargeMethod enum tracks which strategy closed the ∀ᶠ goal:
  assumption, univ_mem', within_disch (direct), pointwise lift, or
  undischarged. tendsto_cont? reports this in its trace output.

Tests:
- tendsto_cont? constant body (nhds and nhdsWithin)
- tendsto_cont? nhdsWithin with discharge method reporting
- tendsto_cont? with within_disch (pointwise lift reported)
- @[tendsto_cont] nhdsWithin positive test
- Updated existing #guard_msgs for widened error messages
Replace constant-body attribute test (which was solved by the
constant fast path regardless of attribute support) with an opaque
function that exercises the actual attribute → nhdsWithin → nhds
wrapping pipeline. Uses before/after registration pattern.
- Add tendsto_cont? test exercising the direct within_disch discharge
  path (not pointwise lift). Uses ∀ x hypothesis (not ∀ᶠ) so
  assumption can't match, forcing through within_disch.
- Note brittleness of candidate list in before-registration test
  (depends on earlier atTop attribute registrations).

All four EvDischargeMethod arms are now covered by #guard_msgs tests:
assumption, univMem, withinDisch, pointwiseLift, undischarged.
Adds #guard_msgs test for the undischarged EvDischargeMethod arm,
completing trace coverage for all five discharge outcomes:
assumption, univMem, withinDisch, pointwiseLift, undischarged.
cameronfreer added a commit to cameronfreer/Sphere-Packing-Lean that referenced this pull request Mar 30, 2026
Extract each candidate-collection bucket into its own function per
b-mehta's review feedback:

- collectInlineCands: inline argument candidates
- warnRedundantInlineArgs: redundancy warnings for inline FVars
- collectContextCands: local context candidates (shadowed by inline)
- collectAttrCands: attribute registry candidates (shadowed by both)
- checkAmbiguity: same-fn-different-limit detection

collectAtoms now composes these, improving readability and preparing
for the AtomEngine extraction in the follow-up PR thefundamentaltheor3m#384.
Conflict resolutions:
- SpherePacking.lean: main's import list + AtomEngine import
- TendstoCont.lean: branch version kept; main's collectAtoms bucket-split
  refactor is superseded by the AtomEngine extraction (same logic)
- TendstoContAttr.lean: branch version (main's + nhdsWithin validation)
- Test/TendstoCont.lean: branch tests + copyright header and #guard_msgs
  docstring from thefundamentaltheor3m#414 (header is load-bearing for linter.style.header)
- replaceAtoms: single generic traversal for abstractBody and
  substituteAtomValues (callback decides projection vs value)
- add forallE arm so replacement traverses the same constructors as
  exprChildren discovery (previously atoms under a forall binder were
  discovered but never abstracted)
- foldrAtoms: generic right-associated fold subsuming buildProdType,
  buildValuePoint, and TendstoCont's buildProdMkNhds
- fix AtomEngine copyright year (file created 2026)
tendstoContOption groups (disch := tac) / (within_disch := tac) as a
repeated element, collapsing 10 syntax rules x 10 elab_rules into 2 + 2.
Order flexibility now falls out of the grammar; duplicate options are
rejected at elaboration with #guard_msgs tests locking in the errors.
@cameronfreer

Copy link
Copy Markdown
Contributor Author

awaiting-review

seewoo5 pushed a commit that referenced this pull request Jun 23, 2026
## Summary

One-line style cleanup matching PR #422's `tendsto_cont` convention.

In `RamanujanIdentities.lean`, replace the older throwaway-`have` form
with the inline-argument form:

```lean
-- before
(by have := E₄_tendsto_one_atImInfty; tendsto_cont)
-- after
(by tendsto_cont [E₄_tendsto_one_atImInfty])
```

`E₄_tendsto_one_atImInfty` is a *global* theorem fact, so passing it
inline is the correct spelling — not the redundant-local-argument case
that #384's tests warn about (those are for named local hypotheses
already in context, where bare `tendsto_cont` stays right).

## Test plan
- [x] `lake build` passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant