Lean 4 自动化内幕

17. gcongr:广义合同性🔗

本章目标:理解 gcongr 如何将不等式目标分解为逐分量的子目标, 掌握 @[gcongr] 引理库的覆盖范围与模式匹配机制, 识别常见失败模式并学会修复,在 calc 链和 positivity 配合中高效使用 gcongr

数学推理中最常见的一步:

已知 a ≤ b,而 f 单调递增,则 f(a) ≤ f(b)。

这看起来简单,但在 Lean 中手动完成时需要精确引用单调性引理并提供侧条件—— 当表达式变复杂时极其繁琐。gcongrgeneralized congruence)自动完成这一过程: 分析目标两侧的公共结构,找出不同的子表达式,搜索匹配的单调性引理将目标拆解。

17.1. 16.1 gcongr 解决什么问题🔗

gcongr 证明形如 f(a₁, ..., aₙ) ≤ f(b₁, ..., bₙ) 的目标, 其中 f 是某种关于每个参数单调的运算:

example (a b c : ) (h : a b) (hc : 0 c) : a * c b * c := a:b:c:h:a bhc:0 ca * c b * c All goals completed! 🐙 example (a b : ) (h : a b) : a + 1 b + 1 := a:b:h:a ba + 1 b + 1 All goals completed! 🐙 example (a b : ) (ha : 0 a) (hab : a b) : a ^ 3 b ^ 3 := a:b:ha:0 ahab:a ba ^ 3 b ^ 3 All goals completed! 🐙

核心思想:不手动引理,让 gcongr 搜索和分解。它把目标两侧 当作"带洞的模板"对齐,找出不同的位置,然后用注册的单调性引理逐个填充。

17.2. 16.2 分解机制详解🔗

17.2.1. 基本流程🔗

gcongr 的工作分为三步:

目标: a₁ * c ≤ b₁ * c          (假设 h : a₁ ≤ b₁, hc : 0 ≤ c)

第一步 —— 对齐: (·) * c ≤ (·) * c
  不同位置: 左侧 a₁  vs  右侧 b₁            -- ❶
  相同位置: c = c

第二步 —— 搜索引理:
  在 @[gcongr] 引理中找到:                    -- ❷
  mul_le_mul_of_nonneg_right : a ≤ b → 0 ≤ c → a * c ≤ b * c

第三步 —— 生成子目标:
  · a₁ ≤ b₁   (由 h 关闭)                  -- ❸
  · 0 ≤ c      (由 hc 关闭)
  • 对齐两侧结构,定位差异的子表达式。

  • @[gcongr] 标记的引理库中搜索匹配的单调性定理。

  • 将原目标拆成更简单的子目标,尝试自动关闭或留给用户。

17.2.2. 多位置分解🔗

当两侧有多个不同位置时,gcongr 逐一分解:

example (a b c d : ) (h1 : a b) (h2 : c d) (ha : 0 a) (hc : 0 c) : a * c b * d := a:b:c:d:h1:a bh2:c dha:0 ahc:0 ca * c b * d a:b:c:d:h1:a bh2:c dha:0 ahc:0 chb:0 ba * c b * d All goals completed! 🐙

分解树:

目标: a * c ≤ b * d

对齐: (·) * (·) ≤ (·) * (·)
├── 位置 1: a vs b → 子目标 a ≤ b             -- ❶
├── 位置 2: c vs d → 子目标 c ≤ d             -- ❷
├── 侧条件: 0 ≤ a (由 ha 自动关闭)          -- ❸
└── 侧条件: 0 ≤ c (由 hc 自动关闭)          -- ❹
  • ❶❷ 主要不等式子目标。❸❹ 单调性引理所需的侧条件。

注意gcongr 不只是"照着原假设机械拆分"——它搜索的单调性引理 可能引入你意想不到的额外 side goals。例如,目标 a * c ≤ b * d 拆分时,引理可能要求 0 ≤ a0 ≤ c,即使你的假设中并没有 直接提供这些条件。遇到 gcongr 留下未关闭的子目标时, 用 gcongr? 查看它实际调用了哪条引理,以理解额外条件的来源。

17.3. 16.3 @[gcongr] 引理库🔗

gcongr 的能力完全取决于注册的引理。Mathlib 注册了数百条,覆盖常见运算:

17.3.1. 算术运算🔗

example (a b c d : ) (h1 : a b) (h2 : c d) : a + c b + d := a:b:c:d:h1:a bh2:c da + c b + d All goals completed! 🐙 example (a b c : ) (h : a b) (hc : 0 c) : a * c b * c := a:b:c:h:a bhc:0 ca * c b * c All goals completed! 🐙 example (a b c : ) (h : a b) (hc : 0 c) : c * a c * b := a:b:c:h:a bhc:0 cc * a c * b All goals completed! 🐙 example (a b : ) (ha : 0 a) (h : a b) : a ^ 5 b ^ 5 := a:b:ha:0 ah:a ba ^ 5 b ^ 5 All goals completed! 🐙

17.3.2. 其他运算🔗

gcongr 还覆盖集合()、求和(Finset.sum)、积分等:

example (s t : Set ) (h : s t) (f : ) : f '' s f '' t := s:Set t:Set h:s tf: f '' s f '' t All goals completed! 🐙 example (s : Finset ) (f g : ) (h : i s, f i g i) : s.sum f s.sum g := s:Finset f: g: h: i s, f i g is.sum f s.sum g s:Finset f: g: h: i s, f i g ii✝:a✝:i✝ sf i✝ g i✝; All goals completed! 🐙

17.3.3. 注册自定义引理🔗

-- [示意] 为自定义函数注册单调性
@[gcongr]
theorem myFunc_le_myFunc (h : a ≤ b) (ha : 0 ≤ a) : myFunc a ≤ myFunc b := ...

@[gcongr] 的要求:结论必须是 _ ≤ __ < __ ⊆ _ 形式, 且参数中变化的部分对应相同的关系。

17.4. 16.4 模式模板:用 gcongr 指定对齐🔗

有时 gcongr 无法自动对齐两侧结构。可以用模式模板显式指定, ?_ 标记变化位置,固定部分写具体表达式:

example (a b c d : ) (h1 : a b) (h2 : c d) (ha : 0 a) (hc : 0 c) : a * c b * d := a:b:c:d:h1:a bh2:c dha:0 ahc:0 ca * c b * d a:b:c:d:h1:a bh2:c dha:0 ahc:0 chb:0 ba * c b * d All goals completed! 🐙 example (a b c : ) (h : a b) (hc : 0 c) : a * c b * c := a:b:c:h:a bhc:0 ca * c b * c All goals completed! 🐙 example (a b : ) (ha : 0 a) (hab : a b) : (a + 1) ^ 2 (b + 1) ^ 2 := a:b:ha:0 ahab:a b(a + 1) ^ 2 (b + 1) ^ 2 All goals completed! 🐙
  • 两个占位符,gcongr 对两个位置分别生成子目标。

  • 固定 c,只分解乘法左侧。

  • 嵌套模式——整个 (· + 1) ^ 2 结构固定,只有底数变化。

提醒:模式模板只帮你指定"哪些位置变化",并不保证 side goals 会变简单。 引理本身的侧条件仍然会出现,你仍需自行关闭它们。

17.5. 16.5 gcongr 与 calc 链🔗

gcongrcalc 是天然搭档——calc 建立不等式链, gcongr 证明链中每一步的单调性推理:

example (a b c : ) (h1 : a b) (h2 : b c) (ha : 0 a) (hb : 0 b) : a ^ 2 c ^ 2 := a:b:c:h1:a bh2:b cha:0 ahb:0 ba ^ 2 c ^ 2 calc a ^ 2 b ^ 2 := a:b:c:h1:a bh2:b cha:0 ahb:0 ba ^ 2 b ^ 2 All goals completed! 🐙 _ c ^ 2 := a:b:c:h1:a bh2:b cha:0 ahb:0 bb ^ 2 c ^ 2 All goals completed! 🐙
  • gcongr 使用 h1ha 使用 h2hb

17.5.1. 混合关系链🔗

example (a b c : ) (h1 : a < b) (h2 : b c) (ha : 0 a) (hb : 0 b) : a ^ 2 < c ^ 2 := a:b:c:h1:a < bh2:b cha:0 ahb:0 ba ^ 2 < c ^ 2 calc a ^ 2 < b ^ 2 := a:b:c:h1:a < bh2:b cha:0 ahb:0 ba ^ 2 < b ^ 2 All goals completed! 🐙 _ c ^ 2 := a:b:c:h1:a < bh2:b cha:0 ahb:0 bb ^ 2 c ^ 2 All goals completed! 🐙

gcongr 同时支持 <,自动搜索严格/非严格版本的单调性引理。

17.6. 16.6 gcongr 与 positivity 的协作🔗

gcongr 的单调性引理常常需要侧条件如 0 ≤ a, 这正是 positivity 的专长——两者形成高效组合:

example (x y : ) (hx : 0 x) (hxy : x y) : x ^ 3 y ^ 3 := x:y:hx:0 xhxy:x yx ^ 3 y ^ 3 All goals completed! 🐙

当侧条件不是简单的假设时:

example (x y : ) (hx : 0 < x) (hxy : x y) : x ^ 2 * (x + 1) y ^ 2 * (y + 1) := x:y:hx:0 < xhxy:x yx ^ 2 * (x + 1) y ^ 2 * (y + 1) x:y:hx:0 < xhxy:x yhy:0 < yx ^ 2 * (x + 1) y ^ 2 * (y + 1) All goals completed! 🐙 <;> this tactic is never executed Note: This linter can be disabled with `set_option linter.unreachableTactic false`'linarith' tactic does nothing Note: This linter can be disabled with `set_option linter.unusedTactic false`linarith

侧条件关闭策略:上下文中有直接假设 → 自动关闭;结构化表达式 → positivity; 需要推理 → linarith

17.7. 16.7 gcongr 的五种失败模式🔗

17.7.1. 失败 1:两侧结构不匹配🔗

-- [会报错] 两侧的函数头不同
example (a b : ℝ) (h : a ≤ b) : a ^ 2 ≤ b ^ 3 := by gcongr
  -- ▸ gcongr failed: 左侧 (·)^2 vs 右侧 (·)^3,结构不一致
  -- ▸ 修复:用 calc 拆成两步,或直接用 nlinarith

gcongr 要求两侧有相同的外层结构——只有"洞"里的值不同。 a ^ 2b ^ 3 的指数不同,无法对齐。

17.7.2. 失败 2:缺少侧条件🔗

-- [会报错] 乘法单调性需要非负条件
example (a b c : ℝ) (h : a ≤ b) : a * c ≤ b * c := by gcongr
  -- ▸ gcongr failed: 缺少 0 ≤ c
  -- ▸ 修复:添加假设 (hc : 0 ≤ c) 或在上下文中提供

这是最常见的失败——单调性引理有侧条件(如底数非负、乘数非负), 但上下文中找不到对应的假设。

-- [会报错] 幂需要底数非负
example (a b : ℝ) (h : a ≤ b) : a ^ 3 ≤ b ^ 3 := by gcongr
  -- ▸ 修复:添加 (ha : 0 ≤ a)

17.7.3. 失败 3:找不到注册的引理🔗

-- [会报错] 目标关系没有对应的 @[gcongr] 引理
example (a b : ℝ) (h : a ≤ b) : Real.log a ≤ Real.log b := by gcongr
  -- ▸ 修复:log 的单调性需要 0 < a 的条件且可能没有注册 @[gcongr]
  -- ▸ 改用 Real.log_le_log 手动引用

不是所有函数都注册了 @[gcongr] 引理。可在 Mathlib 中搜索确认。

17.7.4. 失败 4:目标不是不等式🔗

-- [会报错] gcongr 只处理 ≤、<、⊆ 等序关系
example (a b : ℝ) (h : a = b) : a + 1 = b + 1 := by gcongr
  -- ▸ 修复:等式用 congr 或 rw,不用 gcongr

gcongr不等式的合同性工具。等式有专门的 congr tactic。

17.7.5. 失败 5:表达式方向反了🔗

-- [会报错] 假设方向和目标不一致
example (a b c : ℝ) (h : b ≤ a) (hc : 0 ≤ c) : a * c ≤ b * c := by gcongr
  -- ▸ gcongr 需要 a ≤ b,但假设是 b ≤ a
  -- ▸ 修复:目标方向可能有误,或用 gcongr; exact h.le / linarith

gcongr 不会翻转假设方向。如果目标需要 a ≤ b 但只有 b ≤ a, 需要手动检查目标是否正确。

17.8. 16.8 调试技巧🔗

17.8.1. 技巧 1:用 gcongr? 查看分解🔗

example (a b : ) (h : a b) (ha : 0 a) : a ^ 2 b ^ 2 := a:b:h:a bha:0 aa ^ 2 b ^ 2 All goals completed! 🐙

gcongr? 输出具体的引理调用,可用于理解分解过程或替换为显式证明。

17.8.2. 技巧 2:用 calc 拆步定位失败🔗

-- [示意] 一次性 gcongr 失败时,拆成两步定位问题
example (a b c d : ℝ) (h1 : a ≤ b) (h2 : c ≤ d)
    (ha : 0 ≤ a) (hc : 0 ≤ c) :
    a ^ 2 * c ≤ b ^ 2 * d := by
  calc a ^ 2 * c
      ≤ b ^ 2 * c := by gcongr    -- ✓ 先处理底数
    _ ≤ b ^ 2 * d := by gcongr    -- ✓ 再处理乘数

17.9. 16.9 gcongr vs 其他不等式 tactic🔗

  • 场景:f(a) ≤ f(b),单调函数 —— 推荐 tactic:gcongr —— 原因:自动搜索单调性引理

  • 场景:0 ≤ e0 < e —— 推荐 tactic:positivity —— 原因:符号分析,非单调性

  • 场景:线性不等式组合 —— 推荐 tactic:linarith —— 原因:线性算术

  • 场景:非线性不等式 —— 推荐 tactic:nlinarith —— 原因:非线性算术,可能需要提示

17.10. 16.10 练习🔗

17.10.1. 练习 1(基础):判断 gcongr 能否成功🔗

判断以下哪些能被 gcongr 证明,不能的说明原因:

example (a b : ℝ) (h : a ≤ b) : a + 3 ≤ b + 3 := by sorry          -- (a)
example (a b : ℝ) (h : a ≤ b) : a * b ≤ b * b := by sorry          -- (b)
example (a b : ℝ) (h : a ≤ b) : a ^ 2 ≤ b ^ 3 := by sorry          -- (c)
example (a b c : ℝ) (h : a ≤ b) : a * c ≤ b * c := by sorry        -- (d)
example (a b : ℝ) (h : a ≤ b) (ha : 0 ≤ a) : a ^ 4 ≤ b ^ 4 := by sorry -- (e)

提示:(a) ✓ 加法无条件单调;(b) 需要 0 ≤ b 作为侧条件; (c) ✗ 指数不同,结构不匹配;(d) 需要 0 ≤ c 侧条件; (e) ✓ 有底数非负假设。

17.10.2. 练习 2(calc 链):分步不等式🔗

example (a b c : ℝ) (h1 : a ≤ b) (h2 : b ≤ c)
    (ha : 0 ≤ a) (hb : 0 ≤ b) (hc : 0 ≤ c) :
    a ^ 2 + a ≤ c ^ 2 + c := by
  sorry

提示:用 gcongr 直接尝试,或用 calc 拆成 a^2+a ≤ b^2+b ≤ c^2+c, 每步用 gcongr + linarith

17.10.3. 练习 3(侧条件修复):补充假设🔗

-- 添加最少的假设使 gcongr 成功
example (a b c d : ℝ) (h1 : a ≤ b) (h2 : c ≤ d)
    /- 补充假设 -/ : a * c ≤ b * d := by
  gcongr

提示:需要 (ha : 0 ≤ a) (hc : 0 ≤ c)——乘法两侧都需非负。 注意是较小的那侧(ac)需要非负,不是较大侧。

17.10.4. 练习 4(positivity 配合):复合不等式🔗

example (x y : ℝ) (hx : 0 < x) (hxy : x ≤ y) :
    x ^ 2 * Real.exp x ≤ y ^ 2 * Real.exp y := by
  sorry

提示gcongr 可以分解,侧条件由 positivitylinarith 关闭。 注意 exp 的单调性可能需要 Real.exp_le_exp.mpr

17.10.5. 练习 5(实战):从 calc 链构建证明🔗

example (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) (hab : a ≤ b) :
    a ^ 3 + a ^ 2 ≤ b ^ 3 + b ^ 2 := by
  sorry

提示gcongr 直接在 + 上分解:a^3 ≤ b^3a^2 ≤ b^2, 每个子目标再由 gcongrgcongr; assumption 处理。

17.10.6. 练习 6(进阶):模式模板🔗

example (a b c : ℝ) (h : a ≤ b) (ha : 0 ≤ a) (hc : 0 ≤ c) :
    (a + c) ^ 2 ≤ (b + c) ^ 2 := by
  sorry

提示:使用 gcongr (?_ + c) ^ 2 指定只有 ?_ 位置变化, 然后关闭子目标 a ≤ b 和侧条件 0 ≤ a + c(用 linarithpositivity)。

17.11. 16.11 小结🔗

  • 核心功能:将 f(a) ≤ f(b) 分解为逐分量的 aᵢ ≤ bᵢ 子目标

  • 分解机制:对齐两侧结构,定位不同的子表达式,搜索 @[gcongr] 引理

  • 引理要求:结论为 < 形式的单调性定理

  • 模式模板gcongr ?_ * c 显式指定哪些位置变化

  • 侧条件:乘法/幂需要非负条件,通常由 positivity 或假设关闭

  • 主要陷阱:结构不匹配、缺侧条件、无注册引理、方向反了

  • calc 协作:建立不等式链,每步由 gcongr 证明

  • positivity 协作positivity 提供 0 ≤ a 等侧条件

  • 调试gcongr? 查看引理,calc 拆步定位,exact? 搜索引理

gcongr 是不等式工具链中的结构分解专家——不做算术推理, 只做"两侧结构相同时,逐位置传递不等式"。理解其本质,才能与 linarithpositivitycalc 有效组合。