Zulip Chat Archive
Stream: new members
Topic: tactic for "apply in some order"
Chris M (Jul 06 2020 at 04:15):
Is there a tactic that allows me to just say: "apply these 3 terms/lemmas in whichever order is needed to prove the result"?
Alex J. Best (Jul 06 2020 at 04:20):
solve_by_elim
might work:
variables {A B C :Prop}
lemma f : A → C := sorry
lemma g : C → B := sorry
example (h : A) : C :=
begin
solve_by_elim [f, g],
end
Chris M (Jul 06 2020 at 04:21):
This is motivated by this massively long proof:
import order.lattice
import tactic
variables {α : Type*} [lattice α]
variables x y z : α
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
begin
apply le_antisymm,
{
apply le_inf,
calc
(x ⊓ y) ⊓ z ≤ (x ⊓ y) : inf_le_left
... ≤ x : inf_le_left,
apply le_inf,
calc
(x ⊓ y) ⊓ z ≤ (x ⊓ y) : inf_le_left
... ≤ y : inf_le_right,
calc
(x ⊓ y) ⊓ z ≤ z : inf_le_right
},
{
apply le_inf,
apply le_inf,
calc
x ⊓ (y ⊓ z) ≤ x : inf_le_left,
calc
x ⊓ (y ⊓ z) ≤ (y ⊓ z) : inf_le_right
... ≤ y : inf_le_left,
calc
x ⊓ (y ⊓ z) ≤ (y ⊓ z) : inf_le_right
... ≤ z : inf_le_right
}
end
Where I want to just say something like:
apply le_antisymm,
repeat {use [inf_le_right, inf_le_left, le_inf]}
Chris M (Jul 06 2020 at 04:30):
@Alex J. Best The following doesn't work:
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
begin
apply le_antisymm,
repeat
{
solve_by_elim [inf_le_right, inf_le_left, le_inf]
}
end
It doesn't improve the goal state.
Alex J. Best (Jul 06 2020 at 04:37):
(deleted)
Kenny Lau (Jul 06 2020 at 04:37):
this is like Coq Ltac
Kenny Lau (Jul 06 2020 at 04:37):
if you rewrite inf_assoc then you have already cleared the goal lol
Alex J. Best (Jul 06 2020 at 04:37):
oh lol!
Kenny Lau (Jul 06 2020 at 04:37):
the point is to prove inf_assoc
Kenny Lau (Jul 06 2020 at 04:37):
using inf_le_left etc
Kenny Lau (Jul 06 2020 at 04:40):
@Mario Carneiro do you know Coq Ltac?
Kenny Lau (Jul 06 2020 at 04:40):
they can say things like, "if the goal looks like foo, then apply bar"
Kenny Lau (Jul 06 2020 at 04:41):
I've been wanting to ask about this
Mario Carneiro (Jul 06 2020 at 04:41):
you can do that in non-interactive tactics
Mario Carneiro (Jul 06 2020 at 04:41):
you can also write non-interactive tactics inside a begin-end block if it's not reusable
Kenny Lau (Jul 06 2020 at 04:42):
but do we have that functionality currently?
Chris M (Jul 06 2020 at 04:44):
what are non-interactive tactics?
Kenny Lau (Jul 06 2020 at 04:45):
Ltac perm_aux n :=
match goal with
| |- (perm _ ?l ?l) => apply perm_refl
| |- (perm _ (?a :: ?l1) (?a :: ?l2)) =>
let newn := eval compute in (length l1) in
(apply perm_cons; perm_aux newn)
| |- (perm ?A (?a :: ?l1) ?l2) =>
match eval compute in n with
| 1 => fail
| _ =>
let l1' := constr:(l1 ++ a :: nil) in
(apply (perm_trans A (a :: l1) l1' l2);
[ apply perm_append | compute; perm_aux (pred n) ])
end
end.
Kenny Lau (Jul 06 2020 at 04:45):
^ an example of Ltac match goal with
Kenny Lau (Jul 06 2020 at 04:45):
so we can build tactics that apply inf_le_left and le_inf
Kenny Lau (Jul 06 2020 at 04:46):
like a custom solver for lattice equations
Mario Carneiro (Jul 06 2020 at 04:47):
private meta def infs :=
`[refl <|> {apply inf_le_left_of_le, infs} <|> {apply inf_le_right_of_le, infs}]
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
by apply le_antisymm; repeat {apply le_inf}; {infs}
Chris M (Jul 06 2020 at 04:53):
@Mario Carneiro Did you define infs
specifically for this purpose?
Mario Carneiro (Jul 06 2020 at 04:54):
yes
Chris M (Jul 06 2020 at 04:55):
does <|>
mean "try either the left or right hand side, whichever works"?
Kenny Lau (Jul 06 2020 at 04:55):
the left hand side first
Mario Carneiro (Jul 06 2020 at 04:55):
The only reason it is not literally in the begin-end block of the theorem is because it's recursive and that's annoying to write without a global definition
Mario Carneiro (Jul 06 2020 at 04:57):
I think that these kind of "light weight custom tactics" are underrated in proof scripts
Kenny Lau (Jul 06 2020 at 05:03):
import order.lattice
meta def infs :=
`[refl <|> {apply inf_le_left_of_le, infs} <|> {apply inf_le_right_of_le, infs}]
meta def tactic.interactive.lattice :=
`[apply le_antisymm; repeat { apply le_inf }; infs]
variables {L : Type*} [lattice L] {x y z : L}
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
by lattice
example : x ⊓ y = y ⊓ x :=
by lattice
Kenny Lau (Jul 06 2020 at 05:11):
import order.lattice
meta def infs_and_sups :=
`[refl <|> {apply inf_le_left_of_le, infs_and_sups} <|> {apply inf_le_right_of_le, infs_and_sups} <|>
{apply le_sup_left_of_le, infs_and_sups} <|> {apply le_sup_right_of_le, infs_and_sups}]
meta def tactic.interactive.lattice :=
`[try { apply le_antisymm }; repeat { apply le_inf <|> apply sup_le }; infs_and_sups]
variables {L : Type*} [lattice L] {a b c x y z : L}
example : a ⊔ (b ⊓ c) ≤ (a ⊔ b) ⊓ (a ⊔ c) :=
by lattice
example : a ⊔ (a ⊓ b) = a :=
by lattice
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
by lattice
example : x ⊓ y = y ⊓ x :=
by lattice
Mario Carneiro (Jul 06 2020 at 05:12):
I think you just wrote a prover for intuitionistic logic
Kenny Lau (Jul 06 2020 at 05:12):
oh right they're the same as lattices
Kenny Lau (Jul 06 2020 at 05:12):
where's my complement
Mario Carneiro (Jul 06 2020 at 05:12):
heyting lattices to be precise
Chris M (Jul 06 2020 at 05:13):
@Kenny Lau , did you just write the lattice
tactic for the first time, or is it already part of the library?
Kenny Lau (Jul 06 2020 at 05:13):
for the first time
Chris M (Jul 06 2020 at 05:13):
nice
Mario Carneiro (Jul 06 2020 at 05:13):
generally speaking if it appears in a code block on zulip it was just written
Mario Carneiro (Jul 06 2020 at 05:15):
the procedure is pretty simple: split on everything and apply all assumptions
Scott Morrison (Jul 06 2020 at 06:04):
solve_by_elim
is good at this, although note that its default max_depth
is very low (3!)
Scott Morrison (Jul 06 2020 at 06:04):
import order.lattice
meta def tactic.interactive.lattice :=
`[solve_by_elim
[le_antisymm, le_inf, sup_le, le_refl, inf_le_left_of_le, inf_le_right_of_le,
le_sup_left_of_le, le_sup_right_of_le]
{max_depth := 30}]
variables {L : Type*} [lattice L] {a b c x y z : L}
example : a ⊔ (b ⊓ c) ≤ (a ⊔ b) ⊓ (a ⊔ c) :=
by lattice
example : a ⊔ (a ⊓ b) = a :=
by lattice
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
by lattice
example : x ⊓ y = y ⊓ x :=
by lattice
Scott Morrison (Jul 06 2020 at 06:06):
(In this case, because of the shape of the lemmas, there's no danger setting a high max_depth
; it would be fine probably to set it to 1000.)
Scott Morrison (Jul 06 2020 at 06:06):
I'm not claiming that this is superior to Kenny's hand-rolled tactic; just that this sort of machinery is available "out of the box" as well.
Chris M (Jul 08 2020 at 04:43):
Mario Carneiro said:
private meta def infs := `[refl <|> {apply inf_le_left_of_le, infs} <|> {apply inf_le_right_of_le, infs}] example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) := by apply le_antisymm; repeat {apply le_inf}; {infs}
I am confused as to why the following doesn't work:
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
begin
apply le_antisymm;
{repeat {apply le_inf};
repeat {refl <|> {apply inf_le_left_of_le} <|> {apply inf_le_right_of_le}}}
end
I would have guessed that this is equivalent to your solution using infs
.
Kenny Lau (Jul 08 2020 at 05:32):
because infs
allows for tree-like search while your translation can only apply one tactic at a time
Chris M (Jul 09 2020 at 05:37):
Ah I see it now, thanks :)
Chris M (Jul 09 2020 at 06:08):
So a followup question:
In this code by @Mario Carneiro, we use inf_le_left_of_le
instead of inf_le_left
as I do in the original calc
proof:
private meta def infs :=
`[refl <|> {apply inf_le_left_of_le, infs} <|> {apply inf_le_right_of_le, infs}]
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
by apply le_antisymm; repeat {apply le_inf}; {infs}
I understand this is because calc
has its own implicit use of le_trans
and similar terms, and hence we can't just write
private meta def infs :=
`[refl <|> {apply inf_le_left, infs} <|> {apply inf_le_right, infs}]
However, it would be nice if we can somehow define infs
instead to say something like "use inf_le_left
and inf_le_right
and make a calc
proof from them using the appropriate transitivity terms", without either having to know the names of those transitivity terms or the name inf_le_left_of_le
. Is this possible?
Mario Carneiro (Jul 09 2020 at 06:10):
It is possible to change the tactic to use le_trans
and inf_le_left
instead of inf_le_left_of_le
, but general proof search using le_trans
is messy because it has an undetermined middle variable
Mario Carneiro (Jul 09 2020 at 06:11):
private meta def infs :=
`[refl <|> {transitivity, apply inf_le_left, infs} <|> {transitivity, apply inf_le_right, infs}]
example : (x ⊓ y) ⊓ z = x ⊓ (y ⊓ z) :=
by apply le_antisymm; repeat {apply le_inf}; {infs}
Last updated: Dec 20 2023 at 11:08 UTC