Zulip Chat Archive
Stream: Is there code for X?
Topic: Dumping derivations of inductive predicate
Thomas Vigouroux (Dec 06 2023 at 10:57):
So I have an inductive predicate Finset A -> A -> Prop
, and I'd like to "dump" the derivation to a string, so that I can make simple programs computing such derivations and then outputting them.
Is there a simple way to do that ?
Eric Wieser (Dec 06 2023 at 12:38):
Can you give a mwe that indicates the behavior you want?
Thomas Vigouroux (Dec 06 2023 at 13:21):
I'll write something but basically take GlimpseOfLean, in the file intuitionistic propositional logic, I'd like to dump a specific derivation of ProvableFrom
Eric Wieser (Dec 06 2023 at 13:22):
It's very helpful if you can write a self-contained example on Zulip itself, even if you just copy-paste from GlimpseOfLean
Thomas Vigouroux (Dec 06 2023 at 13:23):
Yeah I'll write that in a sec, I'm afk for a few minutes
Thomas Vigouroux (Dec 06 2023 at 13:28):
Here it is:
import Mathlib.Tactic
import Mathlib.Data.Finset.Basic
abbrev Variable := String
-- Formulas
inductive Formula
| bot
| var : Variable → Formula
| and : Formula → Formula → Formula
| or : Formula → Formula → Formula
| imp : Formula → Formula → Formula
deriving DecidableEq
instance : HAnd Formula Formula Formula where
hAnd := Formula.and
instance : HOr Formula Formula Formula where
hOr := Formula.or
notation:max (priority := high) "#" v:max => Formula.var v
infixr:27 (priority := high) " ⟹ " => Formula.imp
instance: Bot Formula where
bot := Formula.bot
set_option hygiene false
infix:27 (priority:=high) " ⊢ " => Sequent
-- Derivations
inductive Sequent : Finset Formula → Formula → Prop
| Ax : #v ∈ Γ → Γ ⊢ #v
| ExFalso : ⊥ ∈ Γ → Γ ⊢ G
| AndI : Γ ⊢ A → Γ ⊢ B → Γ ⊢ A &&& B
| AndE : insert A (insert B Γ) ⊢ C → insert (A &&& B) Γ ⊢ C
| OrI1 : Γ ⊢ A → Γ ⊢ (A ||| B)
| OrI2 : Γ ⊢ B → Γ ⊢ (A ||| B)
| OrE : insert A Γ ⊢ C → insert B Γ ⊢ C → insert (A ||| B) Γ ⊢ C
| ImpI : insert A Γ ⊢ B → Γ ⊢ (A ⟹ B)
-- Now the ImpE rules, that are exploded by cases
-- Γ, a, B ⊢ C → Γ, a, a ⇒ B ⊢ C
| ImpEAtom : insert #v (insert B Γ) ⊢ C → insert #v (insert (#v ⟹ B) Γ) ⊢ C
| ImpEBot : Γ ⊢ C → insert (⊥ ⟹ B) Γ ⊢ C
| ImpEAnd : insert (A₁ ⟹ A₂ ⟹ B) Γ ⊢ C → insert ((A₁ &&& A₂) ⟹ B) Γ ⊢ C
| ImpEOr : insert (A₁ ⟹ B) (insert (A₂ ⟹ B) Γ) ⊢ C → insert ((A₁ ||| A₂) ⟹ B) Γ ⊢ C
| ImpEImp : insert A₁ (insert (A₂ ⟹ B) Γ) ⊢ A₂ → insert B Γ ⊢ C → insert ((A₁ ⟹ A₂) ⟹ B) Γ ⊢ C
def Sequent.dump (S: Γ ⊢ C): String := sorry
Thomas Vigouroux (Dec 06 2023 at 13:29):
And the point of interest being the function Sequent.dump
Thomas Vigouroux (Dec 06 2023 at 13:32):
Especially, this tentative implementation fails:
/-
tactic 'cases' failed, nested error:
tactic 'induction' failed, recursor 'Sequent.casesOn' can only eliminate into Prop
after processing
_, _
the dependent pattern matcher can solve the following kinds of equations
- <var> = <term> and <term> = <var>
- <term> = <term> where the terms are definitionally equal
- <constructor> = <constructor>, examples: List.cons x xs = List.cons y ys, and List.cons x xs = List.nil
-/
def Sequent.dump (S: Γ ⊢ C): String := match S with
| Ax v => "Ax"
| _ => sorry
Eric Wieser (Dec 06 2023 at 13:36):
Right, you can't ask a Prop
"how were you constructed" unless you promise you are only going to use that information to prove something
Eric Wieser (Dec 06 2023 at 13:36):
That's what
tactic 'induction' failed, recursor 'Sequent.casesOn' can only eliminate into Prop
means
Thomas Vigouroux (Dec 06 2023 at 13:37):
Then here goes my other question: what if I have Decidable (G |- C)
, then I can turn my Prop into a bool and use that ?
Yaël Dillies (Dec 06 2023 at 13:38):
Nonono, you're off by one level.
Eric Wieser (Dec 06 2023 at 13:38):
What's your end goal here: do you just want to see how a proof was built, or do you want to prove things about how the proof was built?
Thomas Vigouroux (Dec 06 2023 at 13:38):
Yaël Dillies said:
Nonono, you're off by one level.
Oh, because Bool: Type 1
right ?
Eric Wieser (Dec 06 2023 at 13:39):
If the former, you can just use #print some_sequent_proof
and it will show you the construction
Eric Wieser (Dec 06 2023 at 13:39):
Which is fine because #print
operates in the meta-theory on Expr
objects
Yaël Dillies (Dec 06 2023 at 13:39):
Here's an example of what Eric is saying: If h : p ∨ q
, you can't say "Oh I'll map h
to 0
if it came from p
and to 1
if it came from q
" because it could come from both p
and q
!
Yaël Dillies (Dec 06 2023 at 13:39):
If you replace p ∨ q
by p || q
, you still get the same problem.
Thomas Vigouroux (Dec 06 2023 at 13:40):
My goal is to make a simple "prover" app for intuitionistic propositional logic, to which you give your context + goal, and it autoproves (if a proof exists)
Thomas Vigouroux (Dec 06 2023 at 13:41):
For that I thus need to output the resulting derivation
Eric Wieser (Dec 06 2023 at 13:42):
I don't think your conclusion necessarily follows there, it's quite possible that inspecting the Expr
would work for you
Eric Wieser (Dec 06 2023 at 13:42):
Unless you want to prove the app itself is correct
Thomas Vigouroux (Dec 06 2023 at 13:42):
Eric Wieser said:
Unless you want to prove the app itself is correct
That I am fine doing be reasonning directly in the formalisation
Thomas Vigouroux (Dec 06 2023 at 13:42):
Eric Wieser said:
I don't think your conclusion necessarily follows there, it's quite possible that inspecting the
Expr
would work for you
How would I do that then ?
Eric Wieser (Dec 06 2023 at 13:43):
The other easy way out is to replace
inductive Sequent : Finset Formula → Formula → Prop
with
inductive Sequent : Finset Formula → Formula → Type
but that might make things unpleasant for you elsewhere
Eric Wieser (Dec 06 2023 at 13:43):
Thomas Vigouroux said:
How would I do that then ?
Can you add an example proof in your code so that I can demonstrate how to print it?
Thomas Vigouroux (Dec 06 2023 at 13:44):
Yup, because having that as a predicate helps for proving things like consistency and completeness
Thomas Vigouroux (Dec 06 2023 at 13:46):
Here's an example:
def v: Variable := "v"
def u: Variable := "u"
lemma Sequent.orComm: {#u ||| #v} ⊢ #v ||| #u := by {
rw [← insert_emptyc_eq]
apply OrE
· apply OrI2
apply Ax
simp only [insert_emptyc_eq, Finset.mem_singleton]
· apply OrI1
apply Ax
simp only [insert_emptyc_eq, Finset.mem_singleton]
}
Thomas Vigouroux (Dec 06 2023 at 13:47):
I'd really like to get the derivation printed out for Sequent.orComm
in a way that I have control over
Eric Wieser (Dec 06 2023 at 13:48):
I guess
#print Sequent.orComm
-- theorem Sequent.orComm : Sequent {#u ||| #v} (#v ||| #u) :=
-- Eq.mpr (id ((insert_emptyc_eq (#u ||| #v)).symm ▸ Eq.refl (Sequent {#u ||| #v} (#v ||| #u))))
-- (Sequent.OrE
-- (Sequent.OrI2
-- (Sequent.Ax
-- (of_eq_true (((congrArg (Membership.mem #u) (insert_emptyc_eq #u)).trans _auxLemma.1).trans (eq_self #u)))))
-- (Sequent.OrI1
-- (Sequent.Ax
-- (of_eq_true (((congrArg (Membership.mem #v) (insert_emptyc_eq #v)).trans _auxLemma.1).trans (eq_self #v))))))
isn't very helpful
Eric Wieser (Dec 06 2023 at 13:48):
(it's easy to write your own function in CoreM
that gives that output, but presumably you don't want the Eq.mpr
s?
Thomas Vigouroux (Dec 06 2023 at 13:50):
Well it kinda is, but let's say I'd like to output to be:
u || v |- v || u by OrE
. u |- v || u by OrI2
u |- u by Ax
. v |- v || u by OrI1
v |- v by Ax
Thomas Vigouroux (Dec 06 2023 at 13:51):
I think you understood what I meant
Eric Wieser (Dec 06 2023 at 13:53):
I think inductive Sequent : Finset Formula → Formula → Type
is going to be the best approach here
Eric Wieser (Dec 06 2023 at 13:54):
You can maybe have both
inductive SequentData : Finset Formula → Formula → Type
inductive Sequent : Finset Formula → Formula → Prop
with the same constructors, and then hopefully show that the former implies the latter
Yakov Pechersky (Dec 06 2023 at 13:55):
It seems like the desired output more closely matches the tactic steps and the goal state at each step. That would also result in the desired outcome of different tactic proofs for the same proposition giving different outputs. Would it be possible to have a tactic that records the tactics used during a proof which makes this record available elsewhere?
Thomas Vigouroux (Dec 06 2023 at 13:55):
Changing to -> Type
typecheks (and I have proofs about soundness and related), so I guess that's good
Thomas Vigouroux (Dec 06 2023 at 14:01):
Okay but now it complains about depending on lemma
s which don't have executable code
Thomas Vigouroux (Dec 06 2023 at 14:01):
Nvm, I just had to change to def
Thomas Vigouroux (Dec 06 2023 at 14:02):
It works great now, that you very much !
Notification Bot (Dec 06 2023 at 14:02):
Thomas Vigouroux has marked this topic as resolved.
Thomas Vigouroux (Dec 06 2023 at 15:14):
Sorry to bother again, but in the process I'd like to print out a Finset Formula
.
I am fine with the order changing, apparently it requires me to use choice
which I don't want, how would I do ?
Thomas Vigouroux (Dec 06 2023 at 15:17):
My first guess was to do something like
def finsetFmlToString: Finset Formula → String
| ∅ => ""
| insert A ∅ => A.toString
| insert A S => A.toString ++ ", " ++ S.toString
But that does not work because insert
is not a match pattern
Yaël Dillies (Dec 06 2023 at 15:20):
A Finset
is a List
without duplicate, quotiented out by permutation. Hence it is an element of a quotient of a thing you can print.
Thomas Vigouroux (Dec 06 2023 at 15:21):
While I follow to theory, I'm not sure about the steps to go from that to an actual working conversion from Finset
to String...
Yaël Dillies (Dec 06 2023 at 15:22):
I mean, have you tried it? #print your_finset
Thomas Vigouroux (Dec 06 2023 at 15:23):
yeah
Thomas Vigouroux (Dec 06 2023 at 15:23):
That works, but how do I get the actual value so that I can manipulate it ?
Yaël Dillies (Dec 06 2023 at 15:23):
Thomas Vigouroux (Dec 06 2023 at 15:26):
Well... unsafe instance Finset.instReprFinset
Yaël Dillies (Dec 06 2023 at 15:26):
What do you mean?
Thomas Vigouroux (Dec 06 2023 at 15:27):
Because it is unsafe I cannot use it in any other block that unsafe ?
Yaël Dillies (Dec 06 2023 at 15:28):
And that's fine, right? You're trying to prove things about it.
Thomas Vigouroux (Dec 06 2023 at 15:28):
Well not really, I'm really just wanting to get it to dump the value
Thomas Vigouroux (Dec 06 2023 at 15:29):
I'm not using it to any other thing than dumping it to the screen
Yaël Dillies (Dec 06 2023 at 15:29):
So unsafe
is fine and there's no reason to be scared.
Thomas Vigouroux (Dec 06 2023 at 15:29):
Yaël Dillies said:
So
unsafe
is fine and there's no reason to be scared.
I realized it as I was typing it :wink:
Thanks for your help !
Thomas Vigouroux (Dec 06 2023 at 15:39):
Okay, so I think I've got something working !
The following proof
def Sequent.orComm: {#u ||| #v} ⊢ #v ||| #u := by {
rw [← insert_emptyc_eq]
apply OrE
· apply OrI2
apply Ax
simp only [insert_emptyc_eq, Finset.mem_singleton]
· apply OrI1
apply Ax
simp only [insert_emptyc_eq, Finset.mem_singleton]
}
Dumps the following derivation:
{(u | v)} |- (v | u) OrE
{u} |- (v | u) OrIR
{u} |- u.
{v} |- (v | u) OrIL
{v} |- v.
Yakov Pechersky (Dec 06 2023 at 15:52):
def finsetFmlToString: Finset Formula → String := fun s => (s.image (fun A => A.toString)).sort.intercalate ", "
Thomas Vigouroux (Dec 06 2023 at 16:06):
Little remark though, now that I have the dumping algorithm done, I can create a binary that just dumps a derivation
Sadly that binary is 91M, which seems huge.
Is there a way to reduce the binary size?
Thomas Vigouroux (Dec 07 2023 at 12:12):
Okay so it turns out that the fact that I changed to -> Type
is a little anoying when doing proofs.
Is there any other way to get around that ? Furthermore, I have trouble understanding why this proble arises in the first place, is there any reason why one could not eliminate a T -> Prop
to anything other than Prop
?
Notification Bot (Dec 07 2023 at 12:12):
Thomas Vigouroux has marked this topic as unresolved.
Yaël Dillies (Dec 07 2023 at 12:13):
There's one reason: it's inconsistent. :point_down:
Yaël Dillies said:
Here's an example of what Eric is saying: If
h : p ∨ q
, you can't say "Oh I'll maph
to0
if it came fromp
and to1
if it came fromq
" because it could come from bothp
andq
!
Thomas Vigouroux (Dec 07 2023 at 12:13):
Oh, right
Thomas Vigouroux (Dec 07 2023 at 12:14):
So then, I have to just work around that in my proofs, right ?
Yaël Dillies (Dec 07 2023 at 12:15):
I don't really know. It seems you're confusing proving and printing.
Thomas Vigouroux (Dec 07 2023 at 12:19):
How so?
Printing is just actually showing the object to the screen, proving is about constructing functions to build my objects, right?
Eric Wieser (Dec 07 2023 at 12:19):
Do you have an example of a proof where Type
gets in the way?
Thomas Vigouroux (Dec 07 2023 at 12:19):
Eric Wieser said:
Do you have an example of a proof where
Type
gets in the way?
Yup, give me a sec and I'll send it here
Eric Wieser (Dec 07 2023 at 12:20):
It sounds like the problem is that there are proof steps that aren't actually part of your inductive type (eg Eq.mpr), and you're having to choose between having them appear in the print output, or not using them in the proofs
Thomas Vigouroux (Dec 07 2023 at 12:21):
Hmm there's probably that
Thomas Vigouroux (Dec 07 2023 at 12:21):
I'll send you the example in a few minutes
Thomas Vigouroux (Dec 07 2023 at 12:26):
So basically I have this:
import Lseq.Basic
import Mathlib.Algebra.BigOperators.Basic
section Completeness
def Formula.weight: Formula → ℕ
| ⊥ | #_ => 0
| A &&& B | A ⟹ B => 1 + A.weight + B.weight
| A ||| B => 2 + A.weight + B.weight
def setWeight (S: Finset Formula): ℕ := S.sum Formula.weight
def zeroWeight (hC: C.weight = 0) (hΓ: setWeight Γ = 0): Γ ⊢ C := by {
cases C with
| bot => {
have Γne : Γ.Nonempty
· sorry
/-
tactic 'cases' failed, nested error:
tactic 'induction' failed, recursor 'Exists.casesOn' can only eliminate into Prop
case bot
Γ: Finset Formula
hΓ: setWeight Γ = 0
h: Γ ⊨ Formula.bot
hC: Formula.weight Formula.bot = 0
Γne: Finset.Nonempty Γ
x✝: ∃ x, x ∈ Γ
⊢ Sequent Γ Formula.bot
-/
obtain ⟨A, hA⟩ := Finset.Nonempty.bex Γne
}
| var _ => sorry
| and _ _
| or _ _
| imp _ _ => {
simp [Formula.weight] at hC
}
}
end Completeness
I have removed some hypothesis (thus the sorry in the have
)
Thomas Vigouroux (Dec 07 2023 at 12:30):
(The actual proof does not work, but that's not the question here, right ?)
Eric Wieser (Dec 07 2023 at 12:34):
Ah, what you're seeing here is that by changing Prop
into Type
, you are now forcing yourself to use only constructive proofs (in a much stronger sense than usual); so using Finset.Nonempty
isn't going to work for you, as you can't use it to "construct" a witness
Eric Wieser (Dec 07 2023 at 12:35):
Note the above isn't a #mwe as Lseq.Basic
doesn't exist for me
Thomas Vigouroux (Dec 07 2023 at 14:58):
Oh, right, sorry for that!
So I have to construct it manually then, right?
Thomas Vigouroux (Dec 07 2023 at 15:07):
Oh okay, so I have found Finset.choose
which seems to help a litle, but my problem is that I can prove existence but now unicity...
Kyle Miller (Dec 07 2023 at 16:11):
Why is it that it's necessary to try to work with this inductive type as a Type
? Tactics are themselves programs that manipulate the Expr
s representing a terms, with no need to have the terms be able to be evaluated. (For example, the tauto
tactic is able to automatically prove classical propositional logic.)
If you really need to output terms and leave Lean, in principle you should be able to take a theorem and output a derivation. It will take some figuring out for how to transform the proof terms into the desired output.
Something that's related is #explode
, which is like #print
but gives Fitch-style proof tables. https://leanprover-community.github.io/mathlib4_docs/Mathlib/Tactic/Explode.html
Kyle Miller (Dec 07 2023 at 16:12):
I guess you said you wanted to do intuitionistic propositional logic -- maybe using Prop
is cheating, since it allows you to use classical logic to prove Sequent
s exist
Thomas Vigouroux (Dec 07 2023 at 16:13):
I am not sure I follow what you mean
Last updated: Dec 20 2023 at 11:08 UTC