Zulip Chat Archive

Stream: new members

Topic: Computation with dfinsupp and multisets


Tej Chajed (Sep 09 2022 at 14:41):

I'm doing some work that involves a theory over dfinsupp (functions with finite support), which I'm using over finsupp specifically for computation. It looks like I can reduce dfinsupp.support fine, but I was having trouble with multiset.sort. For example, the following theorem doesn't work:

import data.multiset.sort

lemma reduce_multiset_sort :
  multiset.sort () {1, 2, 3} = [1, 2, 3] := rfl.

and furthermore #reduce doesn't work (after a while it produces "(deterministic) timeout"):

#reduce multiset.sort () {1, 2, 3}.

Am I doing something wrong?

Tej Chajed (Sep 09 2022 at 15:46):

I did just discover that #eval works, which I guess uses the VM instead. Is there a way to access that from tactics (along with other forms of normalization)?

Mario Carneiro (Sep 09 2022 at 15:47):

when you run code in a tactic it's using the equivalent of #eval

Mario Carneiro (Sep 09 2022 at 15:48):

if you have access only to the expression you need to use eval_expr to turn it into a multiset and evaluate it

Tej Chajed (Sep 09 2022 at 15:54):

What I want to do is make one definition be the normalization of another, and ideally I'd know how to do that with unfold/simp/#eval. I thought I'd use a tactic block to do this, but I'm not sure how that would work. I'm basically trying to mimic Coq's eval compute in ....

Tej Chajed (Sep 09 2022 at 16:02):

I may want to do more metaprogramming in Lean 3, but I'm not able to find much in the way of resources. There is the mathlib generated documentation (eg, https://leanprover-community.github.io/mathlib_docs/init/meta/interactive.html#tactic.interactive.simp), which is helpful, but I'm wondering if there's a resource I don't know about.

Junyan Xu (Sep 09 2022 at 16:29):

As a subproblem I figured out how to evaluate list.merge_sort:

lemma reduce_list_sort : list.merge_sort () [3,2,1] = [1,2,3] :=
begin
  iterate 2 { rw list.merge_sort_cons_cons, swap, refl },
  simp only [list.merge_sort],
  /- `refl` should work here if `list.merge` reduces nicely -/
  rw [list.merge, if_neg],
  simp only [list.merge],
  { refl },
  { dec_trivial },
end

I understand that docs#list.merge_sort is defined using well-founded recursion so we need to rewrite by non-defeq equational lemmas, but I'm surprised that docs#list.merge also behave similarly. Indeed when I #print list.merge._main._pack I see has_well_founded.wf.fix in it. Surely we can redefine list.merge to have good reduction behavior?

Junyan Xu (Sep 09 2022 at 16:40):

For reduce_multiset_sort you can start with change list.merge_sort (≤) [1,2,3] = _, but I haven't found a short and automatic way to do it.

Eric Rodriguez (Sep 09 2022 at 16:43):

does dec_trivial not work?

Junyan Xu (Sep 09 2022 at 16:46):

It doesn't.

Mario Carneiro (Sep 09 2022 at 16:47):

IIRC there is a set_option you can use to force unfolding lemmas to make dec_trivial work on wf recursions

Junyan Xu (Sep 09 2022 at 16:47):

By the way when I do simp [list.merge_sort, list.merge], dsimp,it reduces to something involving and.rec that doesn't seem to reduce further.

Mario Carneiro (Sep 09 2022 at 16:48):

I think it would be simpler to just prove the result is sorted and a permutation of the input

Eric Rodriguez (Sep 09 2022 at 16:48):

lemma reduce_list_sort : list.merge_sort () [3,2,1] = [1,2,3] :=
begin
  refine list.eq_of_perm_of_sorted _ (list.sorted_merge_sort () [3, 2, 1]) _,
  refine (list.perm_merge_sort _ _).trans _,
  dec_trivial,
  dec_trivial,
end

Eric Rodriguez (Sep 09 2022 at 16:48):

works for me

Mario Carneiro (Sep 09 2022 at 16:49):

^ that

Junyan Xu (Sep 09 2022 at 17:06):

Oh I see that wf.fix is used essentially, because in | (a :: l) (b :: l') := if r a b then a :: merge' l (b :: l') else b :: merge' (a :: l) l' only one of the two arguments becomes smaller, so maybe we can't make list.merge reduce nicely.

Eric's solution is nice, but docs#list.decidable_perm seems to be quadratic rather than linear, so it would be nicer if list.merge_sort could compute.

Junyan Xu (Sep 09 2022 at 20:26):

I forgot that double induction is actually easy to make computable; the following works:

namespace list

variables {α : Type*} (r : α  α  Prop) [decidable_rel r]
include r
def merge' (l : list α) : list α  list α :=
begin
  induction l with a l fl,
  { exact id },
  { intro l',
    induction l' with b l' fl',
    { exact a :: l },
    { exact if r a b then a :: fl (b :: l') else b :: fl' } },
end

lemma merge'_nil_left (l : list α) : merge' r [] l = l := rfl
lemma merge'_nil_right (l : list α) : merge' r l [] = l := by cases l; refl
lemma merge'_cons_cons (a b) (l l' : list α) : merge' r (a :: l) (b :: l') =
  if r a b then a :: merge' r l (b :: l') else b :: merge' r (a :: l) l' := rfl

#print merge'
/- list.rec id
    (λ (a : α) (l : list α) (fl : list α → list α) (l' : list α),
       list.rec (a :: l) (λ (b : α) (l' fl' : list α), ite (r a b) (a :: fl (b :: l')) (b :: fl')) l')
    l -/

end list

Junyan Xu (Sep 09 2022 at 20:31):

I've found some old discussions about stuck terms involving acc.rec and about acc being inefficient to compute with in the kernel of Lean and Coq. Presumably the stuck term makes docs#well_founded.fix_F_eq not a rfl lemma. Maybe there's something in Mario's thesis as well. So there's still no satisfying solution to this?

Junyan Xu (Sep 09 2022 at 23:17):

Interestingly, Lean can realize that fix_F_eq is defeq if we insert an id:

namespace well_founded
universes u v
variables {α : Sort u} {r : α  α  Prop}
local infix `≺`:50    := r
variable {C : α  Sort v}
variable F : Π x, (Π y, y  x  C y)  C x

lemma fix_F_eq' (x : α) (acx : acc r x) :
  fix_F F x acx = F x (λ (y : α) (p : y  x), fix_F F y (acc.inv acx p)) :=
by { change fix_F F x (acc.intro x $ λ y, acx.inv) = _, refl }
/- The original proof: acc.drec (λ x r ih, rfl) acx -/
/- by { dsimp only [← acc.eq_intro_inv], refl } doesn't work, where
  lemma acc.eq_intro_inv (x : α) (acx : acc r x) : acc.intro x (λ y, acx.inv) = acx := rfl -/

#print fix_F_eq' /- id (eq.refl (fix_F F x (acc.intro x (λ (y : α), acx.inv)))) -/

end well_founded

Junyan Xu (Sep 10 2022 at 02:38):

I'm confused upon reading issue 1803. From the discussion it seems well-founded recursion should actually compute, contrary to what I observed in fix_F_eq, which doesn't seem to involve anything like propext or choice that causes a term to be stuck. Has anything changed since then? It seems to be a fairly recent issue (as far as official Lean3 is concerned).
(Update: from this other issue it seems computation isn't supposed to work: "a function application f a b cannot be reduced definitionally (using the equational lemmas) if f was defined by well founded recursion.")

Junyan Xu (Sep 10 2022 at 03:37):

Another example of defeq non-transitivity could be extracted from my previous post:

namespace well_founded
universes u v
variables {α : Sort u} {r : α  α  Prop}
local infix `≺`:50    := r
variable {C : α  Sort v}
variable F : Π x, (Π y, y  x  C y)  C x

lemma fix_F_eq0 (x : α) (acx : acc r x) :
  fix_F F x acx = fix_F F x (acc.intro x $ λ y, acx.inv) := rfl

lemma fix_F_eq1 (x : α) (acx : acc r x) :
  fix_F F x (acc.intro x $ λ y, acx.inv) = F x (λ y p, fix_F F y $ acx.inv p) := rfl

lemma fix_F_eq2 (x : α) (acx : acc r x) :
  fix_F F x acx = F x (λ y p, fix_F F y $ acx.inv p) := rfl /- fails -/

end well_founded

Junyan Xu (Sep 10 2022 at 03:40):

It's a bit mysterious why the following doesn't work; is it because of some special support for acc.rec?

namespace acc
variables {α : Sort*} {r : α  α  Prop} {C : α  Sort*} (x : α) (acx : acc r x)
variable F : Π x, (Π y, r y x  C y)  C x
lemma rec_eq0 : acx.rec (λ y _, F y) = (acc.intro x $ λ y, acx.inv).rec (λ y _, F y) := rfl /- fails -/
end acc

set_option pp.all true show the two sides agree except for the proof of acc r x.
lemma eq_intro : acx = acc.intro x (λ y, acx.inv) := rfl works without problem.


Last updated: Dec 20 2023 at 11:08 UTC