{"slug": "formalizing-a-ring-theorem-with-lean-4-and-claude", "title": "Formalizing a ring theorem with Lean 4 and Claude", "summary": "A user tested Claude's ability to generate Lean 4 code to formalize a ring theorem, achieving a proof after 11 iterations but with five unproven 'sorry' sections. The experiment highlights challenges in AI-assisted formal verification, including hallucinations and dependency issues with Mathlib.", "body_md": "I’ve been testing Claude’s ability to generate Lean 4 code to prove theorems. I’ve written about a [couple](https://www.johndcook.com/blog/2026/06/10/claude-and-lean/) [experiments](https://www.johndcook.com/blog/2026/06/15/quaternions-claude-lean/) that verified calculations. I did not write about my failed attempt to get Claude to formalize a proof of the [pqr theorem](https://www.johndcook.com/pqr_theorem.pdf) for seminorms.\n\nThis time I asked Claude to formally prove the theorem from the previous post on [partial fraction decomposition](https://www.johndcook.com/blog/2026/06/16/partial-fraction-decomposition/) in an abstract setting. Unlike the previous experiments, I did not give it a proof or calculation to verify; I asked it to *generate* a proof.\n\nThis was the prompt:\n\nWrite a proof in Lean 4 for the following generalization of partial fraction decomposition.\n\nIf a ring R is a principle ideal domain (PID), then every element c of the field of fractions over R can be written in the form \\[c = \\sum_i \\frac{a_i}{p_i^{r_i}}\\]\n\nwhere the p_i are nonassociate irreducible elements of R, the r_i are non-negative integers, and the elements a_i and p_i are relatively prime.\n\nThis was Claude’s immediate response:\n\nI’ll work through this carefully, as it’s a substantial formalization task. Let me think through what’s available in Mathlib and what needs to be constructed.\n\nThis is a genuinely hard Lean 4 formalization problem. Let me think carefully about what’s in Mathlib, what the statement precisely means, and what proof strategy is realistic.\n\nIt also stated its overall proof strategy:\n\nProof strategy: Write`c = a/b`\n\nin lowest terms. Factor`b = ∏ pᵢ^rᵢ`\n\n. Use the Chinese Remainder Theorem / Bezout identity to decompose`a/b`\n\ninto partial fractions.\n\nIf I needed to prove the theorem by hand, this suggestion alone could be very helpful.\n\nThe first attempt generated code that would not build. As before, a lot of the problems seem to stem from things moving around in Mathlib due to refactorings. There are also occasional hallucinations.\n\nI did not offer any help other than pasting in error messages after each failure. When working on a real problem, I’d be more interactive. But as a test, I just passively pasted error messages and tried again. It took 11 iterations, but I finally got a proof. The proof had five sections marked “sorry,” which is essentially a TODO note indicating an unproven assumption.\n\nI asked if it could explain or remove the sorries, and it said it could remove three of them. But the generated code did not build and I hit a session limit before I could debug the code. Here is the code with five sorry sections.\n\n``` python\n/-\n  PartialFractions.lean  (v11 — final)\n-/\n\nimport Mathlib\n\nopen UniqueFactorizationMonoid\n\nvariable {R : Type*} [CommRing R] [IsDomain R] [IsPrincipalIdealRing R]\n\nstructure PFDSummand (R : Type*) [CommRing R] where\n  prime    : R\n  exponent : ℕ\n  numer    : R\n\nnoncomputable def PFDSummand.val [CommRing R] [IsDomain R] (s : PFDSummand R) :\n    FractionRing R :=\n  algebraMap R (FractionRing R) s.numer /\n  algebraMap R (FractionRing R) (s.prime ^ s.exponent)\n\nstructure IsPFD [CommRing R] [IsDomain R] [IsPrincipalIdealRing R]\n    (terms : List (PFDSummand R)) (c : FractionRing R) : Prop where\n  irred    : ∀ s ∈ terms, Irreducible s.prime\n  nonAssoc : ∀ s ∈ terms, ∀ t ∈ terms, s ≠ t → ¬Associated s.prime t.prime\n  coprime  : ∀ s ∈ terms, IsCoprime s.numer s.prime\n  sum_eq   : (terms.map PFDSummand.val).sum = c\n\nprivate lemma algMap_ne_zero {a : R} (h : a ≠ 0) :\n    (algebraMap R (FractionRing R)) a ≠ 0 :=\n  map_ne_zero_iff _ (IsFractionRing.injective R (FractionRing R)) |>.mpr h\n\nprivate lemma exists_num_denom (c : FractionRing R) :\n    ∃ a b : R, b ≠ 0 ∧\n      (algebraMap R (FractionRing R)) a / (algebraMap R (FractionRing R)) b = c := by\n  obtain ⟨a, b, hb, hab⟩ := IsFractionRing.div_surjective (A := R) c\n  exact ⟨a, b, nonZeroDivisors.ne_zero hb, hab⟩\n\n-- ======================================================================\n-- §6  Bézout splitting\n-- ======================================================================\n\nprivate lemma bezout_split (x y : R) (hcop : IsCoprime x y)\n    (hx : x ≠ 0) (hy : y ≠ 0) (e : R) :\n    ∃ s t : R, s * x + t * y = 1 ∧\n      (algebraMap R (FractionRing R) e) /\n        ((algebraMap R (FractionRing R) x) * (algebraMap R (FractionRing R) y)) =\n      (algebraMap R (FractionRing R) (e * t)) / (algebraMap R (FractionRing R) x) +\n      (algebraMap R (FractionRing R) (e * s)) / (algebraMap R (FractionRing R) y) := by\n  obtain ⟨s, t, hst⟩ := hcop\n  refine ⟨s, t, hst, ?_⟩\n  have hxF : (algebraMap R (FractionRing R)) x ≠ 0 := algMap_ne_zero hx\n  have hyF : (algebraMap R (FractionRing R)) y ≠ 0 := algMap_ne_zero hy\n  have hxyF : (algebraMap R (FractionRing R)) x *\n              (algebraMap R (FractionRing R)) y ≠ 0 := mul_ne_zero hxF hyF\n  have hst_F : (algebraMap R (FractionRing R)) s * (algebraMap R (FractionRing R)) x +\n               (algebraMap R (FractionRing R)) t * (algebraMap R (FractionRing R)) y = 1 := by\n    have h := congr_arg (algebraMap R (FractionRing R)) hst\n    simp only [map_add, map_mul, map_one] at h; exact h\n  -- Prove: ι e / (ι x * ι y) = ι(e*t)/ι x + ι(e*s)/ι y\n  -- Strategy: show RHS = LHS via eq_div_iff, simplify, linear_combination.\n  symm\n  rw [eq_div_iff hxyF, add_mul, div_mul_eq_mul_div, div_mul_eq_mul_div]\n  -- Goal: ι(e*t)*(ι x*ι y)/ι x + ι(e*s)*(ι x*ι y)/ι y = ι e\n  -- The mul_comm on the product inside:\n  rw [show (algebraMap R (FractionRing R)) x * (algebraMap R (FractionRing R)) y =\n        (algebraMap R (FractionRing R)) y * (algebraMap R (FractionRing R)) x from mul_comm _ _]\n  -- Now: ι(e*t)*(ι y*ι x)/ι x + ι(e*s)*(ι y*ι x)/ι y = ι e\n  -- Use field_simp to clear remaining denominators\n  field_simp [hxF, hyF]\n  -- Now a polynomial goal: close with linear_combination\n  simp only [map_mul]\n  linear_combination (algebraMap R (FractionRing R) e) * hst_F\n\n-- ======================================================================\n-- §7  Coprime numerator reduction\n-- ======================================================================\n\nprivate lemma coprime_numer_exists {p : R} (hp : Irreducible p) (a : R) (n : ℕ) :\n    ∃ a' q : R, a = a' + q * p ^ n ∧ IsCoprime a' p := by\n  rcases Classical.em (p ∣ a) with _h | h\n  · exact ⟨0, 0, by sorry, by sorry⟩\n  · exact ⟨a, 0, by ring,\n      isCoprime_comm.mpr ((irreducible_iff_prime.mp hp).coprime_iff_not_dvd.mpr h)⟩\n\n-- ======================================================================\n-- §8  Main theorem\n-- ======================================================================\n\nprivate def emptyPFD (c : FractionRing R) (hc : c = 0) :\n    ∃ terms : List (PFDSummand R), IsPFD terms c :=\n  ⟨[], { irred := by simp, nonAssoc := by simp, coprime := by simp,\n         sum_eq := by simp [hc] }⟩\n\nprivate lemma isPFD_of_denom : ∀ (d : R) (e : R),\n    ∃ terms : List (PFDSummand R),\n      IsPFD terms ((algebraMap R (FractionRing R) e) /\n                   (algebraMap R (FractionRing R) d)) := by\n  intro d\n  induction d using UniqueFactorizationMonoid.induction_on_coprime with\n  | h0 =>\n    intro e; simp only [map_zero, div_zero]\n    exact emptyPFD 0 rfl\n  | h1 =>\n    intro e\n    -- Unit denominator: sorry for now (§9C)\n    exact ⟨[], { irred := by simp, nonAssoc := by simp,\n                 coprime := by simp, sum_eq := by simp; sorry }⟩\n  | hpr =>\n    rename_i prime_elem exp hp_prime\n    intro e\n    have hp_irred : Irreducible prime_elem := irreducible_iff_prime.mpr hp_prime\n    obtain ⟨e', _q, _he, he'_cop⟩ := coprime_numer_exists hp_irred e exp\n    exact ⟨[⟨prime_elem, exp, e'⟩], {\n      irred    := fun s hs => by\n        simp only [List.mem_singleton] at hs; subst hs; exact hp_irred\n      nonAssoc := fun s hs t ht hne => by\n        simp only [List.mem_singleton] at hs ht; subst hs; subst ht; exact absurd rfl hne\n      coprime  := fun s hs => by\n        simp only [List.mem_singleton] at hs; subst hs; exact he'_cop\n      sum_eq   := by\n        simp only [List.map_singleton, List.sum_singleton, PFDSummand.val]; sorry }⟩\n  | hcp =>\n    rename_i x y hrel ih_x ih_y\n    intro e\n    -- Handle the case where x = 0 or y = 0 directly\n    rcases eq_or_ne x 0 with rfl | hx\n    · -- x = 0: denominator = 0*y = 0, so quotient = 0\n      simp only [zero_mul, map_zero, div_zero]\n      exact emptyPFD 0 rfl\n    rcases eq_or_ne y 0 with rfl | hy\n    · -- y = 0: denominator = x*0 = 0, so quotient = 0\n      simp only [mul_zero, map_zero, div_zero]\n      exact emptyPFD 0 rfl\n    -- Both nonzero: apply Bézout splitting\n    -- IsCoprime x y from IsRelPrime x y (using IsRelPrime.isCoprime)\n    have hcop : IsCoprime x y := hrel.isCoprime\n    obtain ⟨s, t, _hst, hsplit⟩ := bezout_split x y hcop hx hy e\n    obtain ⟨terms_x, hpfd_x⟩ := ih_x (e * t)\n    obtain ⟨terms_y, hpfd_y⟩ := ih_y (e * s)\n    exact ⟨terms_x ++ terms_y, {\n      irred    := fun s' hs' => by\n        rw [List.mem_append] at hs'\n        exact hs'.elim (hpfd_x.irred s') (hpfd_y.irred s')\n      nonAssoc := fun s' hs' t' ht' hne => by\n        rw [List.mem_append] at hs' ht'\n        rcases hs' with hs' | hs' <;> rcases ht' with ht' | ht'\n        · exact hpfd_x.nonAssoc s' hs' t' ht' hne\n        · intro; sorry\n        · intro; sorry\n        · exact hpfd_y.nonAssoc s' hs' t' ht' hne\n      coprime  := fun s' hs' => by\n        rw [List.mem_append] at hs'\n        exact hs'.elim (hpfd_x.coprime s') (hpfd_y.coprime s')\n      sum_eq   := by\n        rw [List.map_append, List.sum_append, hpfd_x.sum_eq, hpfd_y.sum_eq]\n        -- Goal: ι(e*t)/ι x + ι(e*s)/ι y = ι e / ι(x*y)\n        -- hsplit: ι e/(ι x*ι y) = ι(e*t)/ι x + ι(e*s)/ι y\n        -- So: ι(e*t)/ι x + ι(e*s)/ι y = ι e/(ι x*ι y) = ι e/ι(x*y)\n        rw [← hsplit, map_mul] }⟩\n\ntheorem pid_partial_fraction_decomposition (c : FractionRing R) :\n    ∃ terms : List (PFDSummand R), IsPFD terms c := by\n  obtain ⟨a, b, _hb, hc⟩ := exists_num_denom c\n  rw [← hc]; exact isPFD_of_denom b a\n\nsection IntExample\nexample (c : FractionRing ℤ) :\n    ∃ terms : List (PFDSummand ℤ), IsPFD terms c :=\n  pid_partial_fraction_decomposition c\nend IntExample\n```\n\n", "url": "https://wpnews.pro/news/formalizing-a-ring-theorem-with-lean-4-and-claude", "canonical_source": "https://www.johndcook.com/blog/2026/06/17/rings-with-lean-claude/", "published_at": "2026-06-17 14:23:51+00:00", "updated_at": "2026-06-17 14:34:57.939940+00:00", "lang": "en", "topics": ["ai-research", "ai-tools", "large-language-models"], "entities": ["Claude", "Lean 4", "Mathlib", "John D. Cook"], "alternates": {"html": "https://wpnews.pro/news/formalizing-a-ring-theorem-with-lean-4-and-claude", "markdown": "https://wpnews.pro/news/formalizing-a-ring-theorem-with-lean-4-and-claude.md", "text": "https://wpnews.pro/news/formalizing-a-ring-theorem-with-lean-4-and-claude.txt", "jsonld": "https://wpnews.pro/news/formalizing-a-ring-theorem-with-lean-4-and-claude.jsonld"}}