Skip to content

feat(LipschitzSmooth): algebraic preservation lemmas#33

Draft
FordUniver wants to merge 5 commits into
feat/lipschitzSmooth-basicfrom
feat/lipschitzSmooth-algebra
Draft

feat(LipschitzSmooth): algebraic preservation lemmas#33
FordUniver wants to merge 5 commits into
feat/lipschitzSmooth-basicfrom
feat/lipschitzSmooth-algebra

Conversation

@FordUniver

@FordUniver FordUniver commented May 19, 2026

Copy link
Copy Markdown
Owner

Algebraic preservation lemmas for LipschitzSmoothWith: closure under +, nonneg c •, and composition with continuous affine maps, plus the K=0 base cases for constants and affine functions.


Sketch / WIP — proof status:

  • lipschitzSmoothWith_const ✅ proved (Basic.lean, 5 lines)
  • lipschitzSmoothWith_affine ✅ proved (Basic.lean, 5 lines)
  • lipschitzSmoothWith_continuousAffineMap ✅ proved (Algebra.lean, 3 lines, derived from _affine)
  • LipschitzSmoothWith.add ✅ proved (Algebra.lean, 9 lines, requires Differentiable ℝ f₁/f₂ — necessary)
  • LipschitzSmoothWith.const_smul ✅ proved (Algebra.lean, 9 lines, requires Differentiable ℝ f — necessary)
  • LipschitzSmoothWith.comp_continuousAffineMapsorry placeholder

On the Differentiable hypothesis

A critical reading of Cannarsa-Sinestrari 2004 (Chapter 3) initially suggested these hypotheses might be removable via an implicit line-differentiability extractor LipschitzSmoothWith.hasLineDerivAt. They cannot. Concrete counterexamples:

  • f(x) = -|x| on ℝ satisfies LipschitzSmoothWith 0 (the descent inequality holds because lineDeriv happens to be junk = 0 at the kink and f ≤ 0 globally) but HasLineDerivAt ℝ f 0 0 1 fails — the one-sided limits -sign(t) disagree.
  • f₁(x, y) = -|x| and f₂(x, y) = -2y are each LipschitzSmoothWith 0, but the sum f₁ + f₂ is not (predicate fails at (0,0) against (0.01, -10): 19.99 ≤ 0 is false).

Root cause: our predicate is strictly stronger than the semiconcavity class Cannarsa-Sinestrari work with — it requires the descent inequality with mathlib's specific junk-valued lineDeriv, whereas semiconcavity only requires some super-gradient.

So the Differentiable hypothesis on add / const_smul is necessary, not a stopgap.

File split

K=0 cases (lipschitzSmoothWith_const, lipschitzSmoothWith_affine) live in Basic.lean since they need no new imports. The rest live in a new Algebra.lean which pulls in Mathlib.Analysis.Normed.Affine.ContinuousAffineMap — keeping that import out of Basic.lean so it doesn't cascade through the rest of the LipschitzSmooth API.

Diff for the changes just in this PR over its predecessor: link

Change `LipschitzSmoothWith K f` from the one-sided descent inequality

  f y ≤ f x + lineDeriv ℝ f x (y - x) + (K / 2) (dist x y)²

to the two-sided absolute-value form

  |f y - f x - lineDeriv ℝ f x (y - x)| ≤ (K / 2) (dist x y)²

matching the textbook definition of L-smoothness (Lipschitz gradient,
class C^{1,1}) — orientation-agnostic, closed under f ↦ -f, and used in
Nesterov (Lemma 1.2.3), Beck (Ch. 5), Bauschke-Combettes (Lemma 2.64),
etc. The one-sided form admitted pathological cases like `-|x|` being
0-smooth, which the two-sided form excludes.

API changes:
- New primary extractor `lineDeriv_abs_le` (matching the def body).
- New lower-bound `lineDeriv_descent_ge` and two-sided variance
  `lineDeriv_apply_sub_abs_le` extractors.
- New theoretical results unlocked: `LipschitzSmoothWith.hasLineDerivAt`
  (line-differentiability everywhere — provable from the predicate's
  bound `|f(x + tv) - f x - t·L| ≤ K/2 t²‖v‖² = o(t)`) and
  `LipschitzSmoothWith.continuous`. Both currently `sorry`; full proofs
  follow in subsequent commits.
- Existing extractors `lineDeriv_descent_le`, `lineDeriv_apply_sub_le`,
  `lineDeriv_sub_apply_le` retained (one-sided, derived from the abs).
- Parallel additions in `FDeriv.lean`, `Deriv.lean`, `Gradient.lean`.
- `CocoerciveWith` and `CocoerciveWith.lipschitzWith_gradient` unchanged.
Fill in `LipschitzSmoothWith.hasLineDerivAt`: a `K`-smooth function is
line-differentiable everywhere, and the `lineDeriv` value at every
direction is the actual line derivative. The predicate bound

  |f(x + t·v) - f x - t · lineDeriv f x v| ≤ K/2 · t² · ‖v‖²

(obtained by applying the abs-bound at `(x, x + t • v)` and factoring
`t` via `lineDeriv_smul`) is `o(t)`, which is the `IsLittleO` form of
`HasDerivAt` at `0` for the 1D restriction.

The `continuous` field remains `sorry` pending a separate argument.
Basic.lean (K = 0 cases, no extra hypotheses):
- `lipschitzSmoothWith_const`: constants are 0-smooth
- `lipschitzSmoothWith_affine`: affine functions `y ↦ ℓ y + c` are 0-smooth

Algebra.lean (new, imports `ContinuousAffineMap`):
- `LipschitzSmoothWith.add`: closed under `+` with `K₁ + K₂`
- `LipschitzSmoothWith.const_smul`: closed under nonneg `c •` with `c · K`
- `LipschitzSmoothWith.comp_continuousAffineMap`: composition with continuous affine
  map `A` gives `‖A.contLinear‖² · K`-smooth (currently `sorry`)

The `add` and `const_smul` lemmas require `Differentiable ℝ f` hypotheses because
mathlib's `lineDeriv` doesn't have an unconditional additivity/scaling lemma (junk
values when a summand is non-differentiable); the proofs route through the
`fderiv`-form characterisation to sidestep this.
…ffineMap companion

- `lipschitzSmoothWith_const` now derived as a one-liner from
  `lipschitzSmoothWith_affine` with `ℓ = 0` (was duplicated proof).
- Add `lipschitzSmoothWith_continuousAffineMap` in `Algebra.lean` as the
  bundled `ContinuousAffineMap` companion to the linear+const form in
  `Basic.lean`. Mathematically cleanest statement, but stays in
  `Algebra.lean` since it pulls in `ContinuousAffineMap`.
…predicate

The two-sided abs form of `LipschitzSmoothWith` (predicated on the
weakest form `lineDeriv`) implies line-differentiability everywhere via
`LipschitzSmoothWith.hasLineDerivAt`, so the algebraic preservation
lemmas no longer require any `Differentiable ℝ f` hypothesis:

- `add` works in pure `lineDeriv` form by computing
  `HasLineDerivAt (f₁ + f₂)` from the two pointwise `hasLineDerivAt`
  witnesses (via `HasDerivAt.add`), then chaining the triangle
  inequality with the two abs bounds.
- `const_smul` analogously via `HasDerivAt.const_smul` and `abs_mul`
  plus `abs_of_nonneg` (since `c : NNReal ≥ 0`).
- `neg` becomes trivial under the abs predicate (it was not provable
  under the old one-sided descent form), with the *same* constant `K`.

Also fix the `lipschitzSmoothWith_affine` proof in `Basic.lean` (the
post-`map_sub` goal is now `|0| ≤ 0` rather than the old descent
`linarith`-trivial goal, so the closing tactic switches to `simp`).

The module docstring is updated to drop the (now-false) "not closed
under negation" caveat — that was a consequence of the one-sided form
and disappears in the two-sided form. The deferred-follow-up sorry on
`comp_continuousAffineMap` is unchanged.
@FordUniver FordUniver force-pushed the feat/lipschitzSmooth-algebra branch from c47d7c2 to 3ed2362 Compare May 19, 2026 22:49
@FordUniver FordUniver force-pushed the feat/lipschitzSmooth-basic branch from 9437e2a to 6b44bc9 Compare May 20, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant