‹ BackHN Continuity

Thread

Anatomy of a Lean proof for software engineers

128 points · 76 comments · abiro

  1. thom · · focus · HN ↗
    So this is all great, but given that we’re being asked to accept 500k+ line Lean proofs that check but could easily have major semantic errors hidden in them, what’s the plan? These seem like uniquely fragile software artifacts, despite the excellent and robust promises made by the runtime.
    1. 6gvONxR4sf7o · · focus · HN ↗
      Much of the point is that you have to understand the lean statement, not its proof. If the proof checker says it's good and reports that it just uses the usual axioms, then you can trust that they imply the statement. And the statement is never the 500k line part.
      1. thom · · focus · HN ↗
        How many new lines of Lean do these recent Millennium Prize proofs introduce on top of known good axioms? I suppose I’m asking what the actual workflow is to inspect that code and come to the conclusion that it is faithfully reproducing the exact chain of proofs we think it is, because I know of no other substantial source code produced by LLMs that has literally zero bugs, however strong the type system of the language it’s writing in.
        1. SkiFire13 · · focus · HN ↗
          > inspect that code and come to the conclusion that it is faithfully reproducing the exact chain of proofs we think it is

          The point is that you don't need to do that. It shouldn't matter whether the proof is the one you think it is or not, only that it is a proof.

          > that has literally zero bugs, however strong the type system of the language it’s writing in.

          If by "bug" you mean a logic error that makes the proof invalid then Lean will catch that. Some code successfully type checking in Lean is a guarantee there are no such "bugs" (modulo bugs in Lean itself, but Lean is human written, the core is rather small, and most of it was manually proven to be sound).

          1. thom · · focus · HN ↗
            Surely “the proof is the one you think it is” is the whole ballgame? Why is it impossible for a condition to be subtly reversed somewhere in the code, rendering the entire proof useless In the real world? I’m begging for an explanation of why this is impossible because I have installed a Lean environment and it seems trivial to misname something, to mistake the order of arguments, to compare to the wrong value. There are infinitely many Lean programs that check but don’t do what they say they do, like every other language.
            1. ndriscoll · · focus · HN ↗
              Because it won't type check. It's like asking how you know some function in a program really gets an Int argument when you had lots of data structures you were passing around with all kinds of fields with various names and types.

              If you construct value of type `forall n, exists m, m≥n and m prime`, then you know you have such an object. Think of a `theorem` (the Lean keyword) as a function that returns such objects. If you try to return the wrong type (e.g. you build a `forall n, exists m, m≤n and m prime`) in your "function" body, you get a compile error. If you use types incorrectly in the middle (passing values and intermediate proofs to theorems incorrectly), you get a compile error.

              It's just like any programming language except the type system is rich enough that in addition to being able to have a value x: Int, (so maybe x is 5) you can have values like h: x^2≥0 (so imagine h is some object you build that proves x^2≥0).

              1. thom · · focus · HN ↗
                Lemme give an example of the kind of thing I'm picturing and you can tell me where it goes wrong. Lean Web tells me the following is fine:

                  import Mathlib
                
                  abbrev Scalar := ℂ
                  abbrev Real := Scalar
                
                  theorem negative_one_has_a_real_square_root :
                      ∃ x : Real, x * x = -1 := by
                    exact ⟨Complex.I, Complex.I_mul_I⟩
                
                But we know it's a lie. I hide those abbrevs in 100k lines of stuff that you haven't yet checked manually. I don't understand what could be stopping shenanigans like these, though I appreciate you trying to show me!
                1. ndriscoll · · focus · HN ↗
                  What people are telling you is you need to check the signature of negative_one_has_a_real_square_root (which includes the definition of Real). Generally this is an easy task. Did you define things the way that you meant to define them to make the statement that you meant to make? Then you can trust that the 500k lines that prove it are fine.

                  If you just had what you have here in the middle of a 500k line proof of some other interesting statement whose definitions you properly validated, that would be okay. The names are weird for a human, but the mathematical content is correct.

                  1. thom · · focus · HN ↗
                    Right but... the difference between "a robot may not injure a human being" and "a robot must injure a human being" is quite important in the real world, even if the "mathematical content is correct". And if my silly example is too contrived, it still seems like this sort of thing happens all the time:

                    <a href="https:&#x2F;&#x2F;proceedings.mlr.press&#x2F;v306&#x2F;ammanamanchi26a.html" rel="nofollow">https:&#x2F;&#x2F;proceedings.mlr.press&#x2F;v306&#x2F;ammanamanchi26a.html

                    1. ndriscoll · · focus · HN ↗
                      That paper seems to just be saying that people&#x27;s benchmarks are bad. Like, yeah, obviously don&#x27;t consider the problem solved if the bot used sorry or axiom. This is trivial to check for and consider a compile error. It&#x27;s (literally) the same as just not allowing throwing exceptions in a program. And vacuous statements aren&#x27;t actually a problem. And they claim that sometimes it makes mistakes formalizing an informal statement, but that this becomes noticeable when you try to actually prove it. This is like if it forgot a parameter when it was defining a function prototype. When it actually goes to write the function, it realizes it needed some extra information, so it&#x27;ll just add it. So, again, what&#x27;s the problem? This isn&#x27;t the type of error that can go uncaught because it won&#x27;t be able to proceed with the actual proof unless it&#x27;s actually true, in which case, great, it proved a stronger statement than was asked for.

                      A robot may not injure a human being is not a formal statement, so it doesn&#x27;t even make sense to talk about here. Maybe it decides to name `Manifold` (the concept) `HumanFinalSolution`, whatever. As long as a `HumanFinalSolution` has the same formal properties as a manifold, I can still talk about the Poincaré conjecture. For formal statements, there&#x27;s a compiler. Use it. It&#x27;s like if I&#x27;m writing a program and I don&#x27;t want it to use null, I do `-Wnull -Werror` or whatever. The task isn&#x27;t done until the build is successful. I don&#x27;t have to read through all the code to make sure that it didn&#x27;t do null pointer dereferences or casts because the compiler doesn&#x27;t allow it.

                      You can literally give e.g. codex a linear algebra text like Axler and a Lean setup and ask it to formalize it without using any external libraries. Do it all from scratch. I&#x27;ve found it&#x27;ll generally be verbose, and it might e.g. use Classical.choose where it could be avoided with more work, so it doesn&#x27;t prove the absolute strongest version of a theorem (which is typical for normal math exposition too), but I haven&#x27;t observed it to make any of these sorts of mistakes.

                      After you make all your basic definitions, you can ask it to do something like solve an actual system of linear equations with a formal proof. If all of your definitions were correct, it can easily do this with a short proof from the theory. You can even give it a couple axioms from calculus, like that some functions called sine and cosine exist or whatever, along with their derivative rules, and linearity of derivatives, chain rule, etc. without actually defining what real numbers and derivatives are, and it can likewise use that to solve linear differential equations with proofs the way a high school student that memorized all of these facts would.

                      I actually see this as a potentially super powerful technique for teaching because you can be explicit about what did you actually prove in class versus what was a fact that I told you and maybe pictorially or otherwise hand-wavy justified, and then proceed formally from there. I think there&#x27;s a lot of potential to push logic and proof assistants down to lower levels of education.

                      1. thom · · focus · HN ↗
                        So the types of errors in that paper could never lead a mathematician to make a claim about a Lean proof that was later found to be incorrect?
                        1. rramadass · · focus · HN ↗
                          From the Lean reference manual;

                          Validating a Lean Proof - <a href="https:&#x2F;&#x2F;lean-lang.org&#x2F;doc&#x2F;reference&#x2F;latest&#x2F;ValidatingProofs&#x2F;" rel="nofollow">https:&#x2F;&#x2F;lean-lang.org&#x2F;doc&#x2F;reference&#x2F;latest&#x2F;ValidatingProofs&#x2F;

                2. 6gvONxR4sf7o · · focus · HN ↗
                  I think you&#x27;re misreading Lean Web. Over on the right side with that you&#x27;ll see:

                      `Real` has already been declared
                  
                  for your `abbrev Real`, and you&#x27;ll see our Application type mismatch: The argument Complex.I has type ℂ but is expected to have type ℝ in the application Exists.intro Complex.I

                  for the first term in your `exact` brackets.

                  It means that it rejects the proof. I suspect it may have been because you came back to the page too long after opening it, at which point you&#x27;ll see `Lean server has stopped.` and won&#x27;t get any type checking at all on Lean Web until you refresh.

                  But if I understand the point you&#x27;re trying to make, that if you did shadow the name Real to actually mean Complex, then it would check, then yeah you&#x27;re correct. But that&#x27;s still where you just have to understand the statement, not the proof. The statements are never the giant 500k LoC part.

                  In a fixed version of your example, that means

                  - it&#x27;s on you to understand that the statement unpacks to `∃ x : ℂ, x * x = -1`

                  - it&#x27;s on you to look at whether the proof checker allows the proof (and potentially with what axioms, depending on your level of care)

                  - it&#x27;s not on you to understand the proof

            2. SkiFire13 · · focus · HN ↗
              &gt; Surely “the proof is the one you think it is” is the whole ballgame?

              No, the important part is that the proof is proving what you think it&#x27;s proving, meaning you only care about the statement being proved (i.e. the signature&#x2F;type of the proof term), not the proof itself. Generally the statement will be much smaller than the proof, so manually checking it (or manually writing it) is much more viable than checking&#x2F;writing the proof.

              &gt; I have installed a Lean environment and it seems trivial to misname something, to mistake the order of arguments, to compare to the wrong value. There are infinitely many Lean programs that check but don’t do what they say they do, like every other language.

              Sure, that&#x27;s why you have the check that the statement being proven is what you want it to be. Then any error in the proof _will_ be captured by the type checker.

        2. whateverboat · · focus · HN ↗
          The core of a proof checker is a formally validated core that is based on a small amount of concepts. Every single line in the proof reduces to that core, therefore, as long as you can trust the core, you can trust the execution of the proofs.

          Therefore, whether the proof is generated by LLM or not is immaterial, since you know that core will always evaluate correctly whether the proof is correct or not.

          What is much more important is to evaluate the theorem and determine if that theorem is actually the theorem you wanted to prove.

          1. thom · · focus · HN ↗
            Yes but why do we write the last bit as an aside as if that’s not a massive yawning black hole for errors to hide in? All the other stuff is irrelevant, just like when Haskell and Rust people claim that if their code compiles it is correct.
            1. ndriscoll · · focus · HN ↗
              Because in practice, it&#x27;s very straightforward to check that all of the types for class members are what you expect. Especially when you actually use that class in other code. You have things in mind that the class should do. If you get compile errors doing those things, you notice the inconsistency.
        3. herni · · focus · HN ↗
          I think you misunderstand what a LEAN proof entails.

          A statement corresponds to a type and to proof that statement means to show that this type is inhabitetd (i.e., there is actually a value of that type).

          A proof is then &quot;just a program&quot; in &quot;just a programming language&quot;. Crucially, you do not care what &quot;this program computes&quot; but you care only that the program actually has the given type.

          There is no concept of a &quot;bug&quot; in a proof term because you do not actually care what a proof term &quot;computes&quot;. You care that it exists and that it is well typed.

          1. thom · · focus · HN ↗
            How would you characterise the 4833 issues surfaced by this work?

            <a href="https:&#x2F;&#x2F;proceedings.mlr.press&#x2F;v306&#x2F;ammanamanchi26a.html" rel="nofollow">https:&#x2F;&#x2F;proceedings.mlr.press&#x2F;v306&#x2F;ammanamanchi26a.html

            1. UltraSane · · focus · HN ↗
              They should be fixed?
            2. herni · · focus · HN ↗
              This might sound pedantic but it is worth being precise here. These kind of issues are either &quot;Proving the wrong statements&quot; or &quot;using dubious axioms&quot;. Both of these are issues not issues of the proof. You do not need to understand the proof term to identify such issues nor do you need to understand the proof term to verify their absence.

              I am not saying that LEAN proofs are without issues. What I am trying to say is that the proof term itself is not the trusted part.

              To be convinced of a proof, an expert will have to (1) review the definitions, (2) review the axioms, and (3) trust the kernel.

              Issues can arise in all 3 but the proof itself is untrusted.

              Definitions in a formal language are hard and error prone. Axioms are mostly not an issue. Most LEAN proofs should only make use of the three standard axioms (Choice, Propext, and Quotient soundness) and it is well studied what these axioms mean. Actual bugs happen in 3. Kernel bugs can for example mean that the kernel incorrectly checks a proof of &quot;False&quot;. Kernel bugs do happen but there are measures against it (e.g., using multiple kernels to check your proof) and it is quite rare for a human to accidentally trigger one. LLMs have accidentally triggered kernel bugs in the past.

        4. nilkn · · focus · HN ↗
          You only need to trust your spec and the Lean kernel. If you trust those two things, you don&#x27;t ever need to manually inspect or check the proof. That&#x27;s the job of the Lean kernel, and your trust of it transfers over to trust of any proof verified by it.

          Making sure your spec is correct -- and that it&#x27;s expressed at a level of abstraction that both captures what matters, without overly constraining implementation details within the repo -- is the hard part. The more the spec constrains implementation details, the less you can ask an agent to, e.g., try many ideas to optimize a specific component without breaking correctness. And of course if the spec isn&#x27;t enforcing what you wanted it to, then all the proofs don&#x27;t count for much.

          Some people say the spec issues are enough reason to avoid formal methods, but I&#x27;m not convinced. Part of me truly thinks AI formal methods is the future of a lot of software engineering. The basis of that belief for me is that the industry has collectively put almost no effort yet into solving the spec problem, and that&#x27;s because for decades formal methods were too expensive to seriously consider most of the time. Now that AI is fundamentally shifting the economics, there&#x27;s now, for the first time ever, a real incentive to try to solve the spec problem. And frankly it seems solvable to me; I view it as more of a product design and UI problem than a purely technical one.

Open on Hacker News to reply ↗

Unofficial Hacker News client; not affiliated with Y Combinator.