Zulip Chat Archive

Stream: lean4

Topic: restricting axioms


awalterschulze (Jan 10 2025 at 16:25):

Is there a way to limit the axioms used. For example to prevent the use of law of excluded middle?

I want to prove decidability of a language: String -> Prop for denotations of my inductive type.
So basically I want to show that this is a DecidablePred and make sure via github's CI that I didn't use LEM by accident and make sure that I won't make the mistake in future as I update the code.

Currently I know how to use #print axioms decidableDenote and this prints the axioms [propext, Quot.sound]. I want to make sure that this list is not expanded upon by accident, by me or my collaborators, which sounds like a job for CI.

For reference here is my current proof: https://github.com/katydid/proofs/blob/main/Katydid/Regex/SimpleRegex.lean#L262

Ruben Van de Velde (Jan 10 2025 at 16:26):

I think someone (@Mario Carneiro ?) made this for lean 3, but I don't know if it exists for lean 4

Edward van de Meent (Jan 10 2025 at 18:56):

you could use #guard_msgs to ensure the message doesn't change?

Edward van de Meent (Jan 10 2025 at 18:57):

i believe that raises an error when the provided message doesn't match the one it catches?

Henrik Böving (Jan 10 2025 at 19:46):

Why do you want to exclude em for this? If you show DecidablePred for something you have to provide an executable algorithm as a decision procedure, unless that one is marked noncomputable its always executable. Whether the proof of correctness for the procedure uses em doesnt matter for being able to execute the procedure.

Kyle Miller (Jan 10 2025 at 19:58):

It should look something like

def decidableDenote (r: Regex α): DecidablePred (denote r) :=
  fun xs => decidable_of_iff _ (by rw [validate_commutes])

(Untested of course, but you shouldn't be using tactics to create DecidablePred instances. They tend to have issues with reduction.)

Kyle Miller (Jan 10 2025 at 20:01):

Like Henrik is asking, what's your specific use case for avoiding LEM?

Asei Inoue (Feb 25 2025 at 07:12):

here is a implementation of command which detect classical axiom

https://lean-ja.github.io/lean-by-example/Diagnostic/Print.html#%E8%88%9E%E5%8F%B0%E8%A3%8F

Damiano Testa (Feb 25 2025 at 08:51):

Since this shows up every once in a while and I wanted to have something similar to grade my module, here is a linter that flags uses of Classical.choice.

import Lean.Util.CollectAxioms
import Mathlib.Tactic.DeclarationNames

/-!
#  The "detectClassical" linter

The "detectClassical" linter emits a warning on declarations that depend on the `Classical.choice`
axiom.
-/

open Lean Elab Command

namespace Mathlib.Linter

/--
The "detectClassical" linter emits a warning on declarations that depend on the `Classical.choice`
axiom.
-/
register_option linter.detectClassical : Bool := {
  defValue := true
  descr := "enable the detectClassical linter"
}

/--
The `linter.verbose.detectClassical` option is a flag to make the `detectClassical` linter emit
a confirmation on declarations that depend *not* on the `Classical.choice` axiom.
-/
register_option linter.verbose.detectClassical : Bool := {
  defValue := false
  descr := "enable the verbose setting for the detectClassical linter"
}

namespace DetectClassical

@[inherit_doc Mathlib.Linter.linter.detectClassical]
def detectClassicalLinter : Linter where run := withSetOptionIn fun stx  do
  unless Linter.getLinterValue linter.detectClassical ( getOptions) do
    return
  if ( get).messages.hasErrors then
    return
  let nms := ( getNamesFrom (stx.getPos?.getD default)).filter (! ·.getId.isInternal)
  let verbose? := Linter.getLinterValue linter.verbose.detectClassical ( getOptions)
  for constStx in nms do
    let constName := constStx.getId
    let axioms  collectAxioms constName
    if axioms.isEmpty then
      if verbose? then
        logInfoAt constStx m!"'{constName}' does not depend on any axioms"
      return
    if !axioms.contains `Classical.choice then
      if verbose? then
        logInfoAt constStx
          m!"'{constName}' is non-classical and depends on axioms:\n{axioms.toList}"
    else
      Linter.logLint linter.detectClassical constStx
        m!"'{constName}' depends on 'Classical.choice'.\n\nAll axioms: {axioms.toList}\n"

initialize addLinter detectClassicalLinter

end DetectClassical

end Mathlib.Linter

Damiano Testa (Feb 25 2025 at 08:52):

You can configure it to emit information on all declarations, confirming that they do not depend on any axiom/do not depend on Classical.choice, using set_option linter.verbose.detectClassical true.

Damiano Testa (Feb 25 2025 at 08:54):

By default, it will only emit a warning on declarations that depend on Classical.choice.

Kevin Buzzard (Feb 25 2025 at 09:00):

Since this shows up every once in a while and I wanted to have something similar to grade my module, here is a linter that flags uses of Classical.choice.

You don't allow AC in your module??

Damiano Testa (Feb 25 2025 at 09:01):

I want to know what depends on sorry, but the logic of the linter is very similar!

Damiano Testa (Feb 25 2025 at 09:04):

Instead of checking Classical.choice, I use sorryAx.

Kevin Buzzard (Feb 25 2025 at 09:09):

I allow sorrys in my students' projects, but I usually try and either prove or disprove them when I'm marking them; it would be great if you had a linter for figuring out whether they're true or false but this might be quite a tough challenge.

Damiano Testa (Feb 25 2025 at 09:15):

I do the same, but, when I cannot prove the sorries, then I want to check the extent of the dependence on them.

Damiano Testa (Feb 25 2025 at 09:16):

So, the sorryLinter is not required to be silent on successful projects: it is just a diagnostic that I use to see where some issues might be.

Damiano Testa (Feb 25 2025 at 09:17):

With respect to autofiguring out true or false: if the lemmas are in scope for plausible, you could use that. Of course, in general it is hopeless.

awalterschulze (Mar 10 2025 at 11:40):

Damiano Testa said:

Since this shows up every once in a while and I wanted to have something similar to grade my module, here is a linter that flags uses of Classical.choice.

import Lean.Util.CollectAxioms
import Mathlib.Tactic.DeclarationNames

/-!
#  The "detectClassical" linter

The "detectClassical" linter emits a warning on declarations that depend on the `Classical.choice`
axiom.
-/

open Lean Elab Command

namespace Mathlib.Linter

/--
The "detectClassical" linter emits a warning on declarations that depend on the `Classical.choice`
axiom.
-/
register_option linter.detectClassical : Bool := {
  defValue := true
  descr := "enable the detectClassical linter"
}

/--
The `linter.verbose.detectClassical` option is a flag to make the `detectClassical` linter emit
a confirmation on declarations that depend *not* on the `Classical.choice` axiom.
-/
register_option linter.verbose.detectClassical : Bool := {
  defValue := false
  descr := "enable the verbose setting for the detectClassical linter"
}

namespace DetectClassical

@[inherit_doc Mathlib.Linter.linter.detectClassical]
def detectClassicalLinter : Linter where run := withSetOptionIn fun stx  do
  unless Linter.getLinterValue linter.detectClassical ( getOptions) do
    return
  if ( get).messages.hasErrors then
    return
  let nms := ( getNamesFrom (stx.getPos?.getD default)).filter (! ·.getId.isInternal)
  let verbose? := Linter.getLinterValue linter.verbose.detectClassical ( getOptions)
  for constStx in nms do
    let constName := constStx.getId
    let axioms  collectAxioms constName
    if axioms.isEmpty then
      if verbose? then
        logInfoAt constStx m!"'{constName}' does not depend on any axioms"
      return
    if !axioms.contains `Classical.choice then
      if verbose? then
        logInfoAt constStx
          m!"'{constName}' is non-classical and depends on axioms:\n{axioms.toList}"
    else
      Linter.logLint linter.detectClassical constStx
        m!"'{constName}' depends on 'Classical.choice'.\n\nAll axioms: {axioms.toList}\n"

initialize addLinter detectClassicalLinter

end DetectClassical

end Mathlib.Linter

Thank you, this works quite well:
https://github.com/katydid/regex-deriv-lean/pull/134

It finds uses of Classical.choice
and there are many more than I thought there would be

awalterschulze (Mar 10 2025 at 11:47):

Asei Inoue said:

here is a implementation of command which detect classical axiom

https://lean-ja.github.io/lean-by-example/Diagnostic/Print.html#%E8%88%9E%E5%8F%B0%E8%A3%8F

I tried this, but Command was an unknown import for some reason

Aaron Liu (Mar 10 2025 at 11:48):

Try import Lean

awalterschulze (Mar 10 2025 at 11:50):

Edward van de Meent said:

you could use #guard_msgs to ensure the message doesn't change?

I do not know how to use gaurd_msgs with print axioms, an example would be very helpful

awalterschulze (Mar 10 2025 at 11:52):

Henrik Böving said:

Why do you want to exclude em for this? If you show DecidablePred for something you have to provide an executable algorithm as a decision procedure, unless that one is marked noncomputable its always executable. Whether the proof of correctness for the procedure uses em doesnt matter for being able to execute the procedure.

I was to state in a paper that the property is decidable. Maybe I am a little over perfectionist in this case and it being executable is enough, but I am not sure.

awalterschulze (Mar 10 2025 at 12:01):

Kyle Miller said:

It should look something like

def decidableDenote (r: Regex α): DecidablePred (denote r) :=
  fun xs => decidable_of_iff _ (by rw [validate_commutes])

(Untested of course, but you shouldn't be using tactics to create DecidablePred instances. They tend to have issues with reduction.)

I tried it, but unfortunately it doesn't work.
Also was wondering about not being allowed to use tactics to create DecidablePred instances.

  1. Shouldn't I be warned in some way about this?
  2. Is it okay that validate_commutes uses tactics?
  3. How is it possible to do this correctly and know that it is correct?

awalterschulze (Mar 10 2025 at 12:02):

Kyle Miller said:

Like Henrik is asking, what's your specific use case for avoiding LEM?

I want to say in a paper that the language has been proven to be decidable. I assume this is the way to do it, but maybe I am wrong?

Henrik Böving (Mar 10 2025 at 12:18):

If your proof of a decision procedure is using em you have still shown that the thing you are proving is decidable, there are different combinations possible here:

  • derive a decision procedure using em and verify it (with or without em) would not be decidability in the usual sense
  • derive a decision procedure without em and verify it without em is decidability and the proof for its correctness is constructive
  • derive a decision procedure without em and verify it with em is decidability and the proof for its correctness is non constructive

Regardless the last case still shows decidability of a predicate.

Asei Inoue (Mar 14 2025 at 21:26):

@Damiano Testa

what this line does in your detectClassical linter?

  if ( get).messages.hasErrors then
    return

Damiano Testa (Mar 14 2025 at 21:30):

It makes sure that the linter says nothing if the declaration contains errors.

Damiano Testa (Mar 14 2025 at 21:30):

It is fairly standard for linters to ignore commands that have errors.

Damiano Testa (Mar 14 2025 at 21:31):

In particular, in this case, if the declaration contains errors, it likely will not have added anything to the environment, so there would be nothing to check for axioms anyway.

Asei Inoue (Mar 14 2025 at 21:41):

thanks!

awalterschulze (Mar 20 2025 at 09:56):

Henrik Böving said:

If your prove of a decision procedure is using em you have still shown that the thing you are proving is decidable, there are different combinations possible here:

  • derive a decision procedure using em and verify it (with or without em) would not be decidability in the usual sense
  • derive a decision procedure without em and verify it without em is decidability and the proof for its correctness is constructive
  • derive a decision procedure without em and verify it with em is decidability and the proof for its correctness is non constructive

Regardless the last case still shows decidability of a predicate.

Thank you this is very helpful
Let me just do a triple check that I understand correctly.
Here is a minimal example that I wonder if it is true in general:

-- Step 1: define some language

inductive Regex: Type where ...

def Lang: Type := String -> Prop

def denote (r: Regex): Lang := ...

-- Step 2: define a matching function

def match (r: Regex) (s: String): bool := ...
  -- matching r is decidable, since it returns a bool and is computable (does not have noncomputable tag)

-- Step 3: prove that the matching function is correct

theorem commutes (r: Regex) (s: String): (execute r s = true) = (denote r s) := by
  -- feel free to use EM and tactics
  -- using EM just means the proof is not constructive, but doesn't affect decidability

-- decidability of r is proven via match, the next step is really just a formality and not sure if it is really needed.

-- Step 4: prove decidability

def decidable (r: Regex): DecidablePred (denote r) := by
  -- proven via commutes
  -- feel free to use more EM and tactics

-- decidability of r is proven, but the proof is not constructive, which is fine

Kyle Miller (Mar 20 2025 at 15:37):

For step 4, a clarification: you don't prove decidability, you provide a decision procedure. If you do make a mistake and accidentally use EM in a computationally relevant way, you'll notice because Lean will say the definition must be noncomputable. If it's not noncomputable, it's fine.

Just like any definition, you don't generally want to define it with tactics. Here's how it could be defined, modulo bugs from typing it directly into Zulip:

def decidable (r: Regex): DecidablePred (denote r) :=
  fun _ => decidable_of_decidable_of_eq _ _

This causes execute to be used to evaluate denote.

awalterschulze (May 01 2025 at 13:37):

Thank you that worked perfectly https://github.com/katydid/regex-deriv-lean/commit/b8acdfd4ecce5a1c7148b3a53f23a12b7b0c0069

So just to conclude and check if I understood correctly: it sounds like it is generally frowned upon to use tactics in definitions, but technically it would not have made the proof that the predicate is decidable invalid, if tactics were used.


Last updated: May 02 2025 at 03:31 UTC