Zulip Chat Archive

Stream: general

Topic: timeout why?


Thorsten Altenkirch (Feb 22 2021 at 17:38):

timeout.lean
I get a timeout for this toy compiler on a small expression? Why?

Gabriel Ebner (Feb 22 2021 at 17:45):

Please paste your example in a code block.

Johan Commelin (Feb 22 2021 at 17:46):

or a github gist... if it's too long

Thorsten Altenkirch (Feb 22 2021 at 17:48):

inductive Expr : Type
| const :   Expr
| var : string  Expr
| plus : Expr  Expr  Expr
| times : Expr  Expr  Expr

open Expr

inductive Instr : Type
| pushC :   Instr
| pushV : string  Instr
| add : Instr
| mult : Instr

open Instr

def Code : Type
  := list Instr

def Stack : Type
  := list 

def comp_aux : Expr  Code  Code
| (const n) c := pushC n :: c
| (var x) c := pushV x :: c
| (plus l r) c := comp_aux l (comp_aux r (add :: c))
| (times l r) c := comp_aux l (comp_aux r (mult :: c))

def comp (e :Expr) : Code
  := comp_aux e []

def e1 : Expr
  := times (var "x") (plus (var "y") (const 2))

#reduce (comp e1)

Kenny Lau (Feb 22 2021 at 17:48):

maybe it's the string bug

Kenny Lau (Feb 22 2021 at 17:48):

I remember there was a bug with evaluating strings because of some "undefined" string

Gabriel Ebner (Feb 22 2021 at 17:48):

You should use #eval instead of #reduce.

Rob Lewis (Feb 22 2021 at 17:48):

It is related to strings, but it's not a bug. This comes up pretty often. https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/deep.20recursion.20error.20reducing.20strings

Rob Lewis (Feb 22 2021 at 17:49):

We need to work on SEO for the Zulip archive. It's too bad that googling "lean prover string reduce" doesn't turn up one of the relevant threads.

Thorsten Altenkirch (Feb 22 2021 at 17:50):

Gabriel Ebner said:

You should use #eval instead of #reduce.

I see but then I have a different problem:

35:1: result type does not have an instance of type class 'has_repr', dumping internal representation

Johan Commelin (Feb 22 2021 at 17:51):

So I guess you need to provide an

instance : has_repr Code :=
...

Johan Commelin (Feb 22 2021 at 17:52):

Are there derive handlers for has_repr? If so you can put @[derive has_repr] before most of your defs.

Gabriel Ebner (Feb 22 2021 at 17:52):

Unfortunately not. But they would be pretty useful.

Gabriel Ebner (Feb 22 2021 at 17:55):

https://github.com/gebner/mathlib/blob/e66976e99d5e1b7811e80bb7ca41421a5e31a322/src/tactic/derive_to_string.lean

Gabriel Ebner (Feb 22 2021 at 17:56):

But the automatically generated instances are not particularly nice. (It's hard to know if you should parenthesize or not. That is, var 5 or var (5).)

Thorsten Altenkirch (Feb 22 2021 at 17:56):

I am not sure what is going on. Surely lean knows how to evaluate this expression? Or not? And it should also know how to print a list. And Instr which is just a sum???

Johan Commelin (Feb 22 2021 at 17:57):

It doesn't unfold Code when trying to figure out how to print things, afaik.

Johan Commelin (Feb 22 2021 at 17:57):

You can write @[derive has_repr] above def Code, but apparently for Instr you will have to write the instance by hand.

Thorsten Altenkirch (Feb 22 2021 at 17:57):

Johan Commelin said:

It doesn't unfold Code when trying to figure out how to print things, afaik.

It's not this - I tried substitute Code by list Instr.

Johan Commelin (Feb 22 2021 at 17:58):

I guess it got stuck on Instr at that point

Thorsten Altenkirch (Feb 22 2021 at 17:59):

Actually I also have a related problem. How do I use ++ on Code (which is defined as list Instr)?

Thorsten Altenkirch (Feb 22 2021 at 17:59):

Is there a way to just have a type synonym, without indroducing a new type?

Gabriel Ebner (Feb 22 2021 at 18:03):

You could use @[reducible] def instead.

Gabriel Ebner (Feb 22 2021 at 18:03):

Or you could use @[deriv has_append, deriv has_repr] def to copy the instances explicitly.

Gabriel Ebner (Feb 22 2021 at 18:03):

Type-class inference can see through reducible definitions, so you get all instances from list automatically.

Thorsten Altenkirch (Feb 22 2021 at 18:06):

Gabriel Ebner said:

You could use @[reducible] def instead.

Thank you - this does the trick.

Thorsten Altenkirch (Feb 22 2021 at 18:11):

I still don't understand why #reduce hangs. Btw all I wanted is a nice example for the code without having to create it by hand... :-(

Gabriel Ebner (Feb 22 2021 at 18:12):

Please read the link that Rob posted earlier, it explains the issue.

Thorsten Altenkirch (Feb 22 2021 at 18:21):

Ah I sort of ignored this because I couldn't imagine it had anything to do with strings. I see. Weird.

Rob Lewis (Feb 22 2021 at 18:30):

Gabriel Ebner said:

But the automatically generated instances are not particularly nice. (It's hard to know if you should parenthesize or not. That is, var 5 or var (5).)

Is the heuristic "does the argument contain a space" good enough?

Rob Lewis (Feb 22 2021 at 18:32):

It will still use unnecessary parens, but not all the time

Rob Lewis (Feb 22 2021 at 18:33):

(I think we should add this derive handler, I've missed it before too)

Gabriel Ebner (Feb 22 2021 at 20:09):

Rob Lewis said:

Gabriel Ebner said:

But the automatically generated instances are not particularly nice. (It's hard to know if you should parenthesize or not. That is, var 5 or var (5).)

Is the heuristic "does the argument contain a space" good enough?

That seems slow. I would rather go for a typeclass/attribute class does_not_require_parens (α : Sort*) : Prop. The proper way is of course to add four classes has_to_{repr,string,format,tactic_format}_parens which return tuples consisting of (left-binding power, string, right-binding power).

Rob Lewis (Feb 22 2021 at 21:06):

That still seems trickier than I care to waste my time on. What if we just add the dumb version? People can still implement their own has_reprs if they don't like the ugly derived one.

Gabriel Ebner (Feb 22 2021 at 21:23):

I won't complain about a "bad" version. I think any derive handler is better than none, and we can improve it afterwards.

Alexandre Rademaker (Mar 29 2021 at 14:30):

Most of the discussion so far seems to blame string, right? I have a similar problem using nat. Following the idea from https://github.com/leanprover/lean/blob/72a965986fa5aeae54062e98efb3140b2c4e79fd/tests/lean/run/soundness.lean, we are defining atomic symbols in our logic with nat. Everything looks fine until we need to implement a function to convert between representations.

Mario Carneiro (Mar 29 2021 at 14:33):

Do you have a #mwe? nat should be fine for small numbers

Mario Carneiro (Mar 29 2021 at 14:34):

num may perform better if you have larger numbers and still need kernel computation

Alexandre Rademaker (Mar 29 2021 at 14:34):

Sorry, yes, I didn't complete my post. The relevant part is https://github.com/arademaker/alc-lean/blob/atomics/src/alc.lean#L21-L39. and https://github.com/arademaker/alc-lean/blob/atomics/src/sequent.lean#L7-L40. Line 40 is the problem... reduce produces a timeout.

Alexandre Rademaker (Mar 29 2021 at 14:37):

The function sigma' is a recursive function to transform the lconcept (labeled concepts) into regular concepts of the logic. The idea is that labeled concepts are concepts were the quantifier prefix were separated into a list. The reduce is to test if the function definition is right, I can't understand why it doesn't work.

Alexandre Rademaker (Mar 29 2021 at 14:45):

The reduction of lconcept and concept terms works. The non-standard facts about sigma' is the pattern match over the structure and the way the recursion calls are constructed. the recursive call is done inside the term being produced.

Mario Carneiro (Mar 29 2021 at 15:05):

The issue is that sigma' is written in a way that is not straightforwardly structural recursive, so lean is falling back on well founded recursion and this performs poorly in the kernel. There is an easy fix:

def sigma'_aux : list Label  Concept  Concept
| [] c := c
| (Forall r :: ls) c := Every r (sigma'_aux ls c)
| (Exists r :: ls) c := Some r (sigma'_aux ls c)

def sigma' : LConcept  Concept
| roles, c := sigma'_aux roles c

Alexandre Rademaker (Mar 29 2021 at 15:07):

Hi @mario, indeed, I ended up with the same approach that I was about to post. Thank you...

Alexandre Rademaker (Mar 29 2021 at 15:08):

def aux : list Label -> Concept -> Concept
 | [] c := c
 | ((Forall r) :: ls) c := Every r (aux ls c)
 | ((Exists r) :: ls) c := Some r (aux ls c)

def sigma2 (lc : LConcept) : Concept := aux lc.roles lc.concept

#reduce sigma2 (LConcept.mk [Forall R#0, Exists R#1] (Concept.Bot))

Can we see this as a Lean bug? So maybe we can expect better behaviour in Lean 4?

Mario Carneiro (Mar 29 2021 at 15:09):

What would the bug be exactly? This is an interaction of components that are all working as designed

Mario Carneiro (Mar 29 2021 at 15:11):

Also, I think the days of hoping that everything will magically be better in lean 4 are over, because the real thing is out. Many things are better, many other things can be made better in user space, and some things aren't any different. Right now I think well founded recursion isn't completely supported yet but I don't think the overall structure of how the equation compiler picks a compilation strategy will change much if at all

Alexandre Rademaker (Mar 29 2021 at 15:12):

well, the pattern matching was used as expected. The syntax for the definition is right. So why the reduction is not working? You said that it is not straightforwardly structural recursive but it is a recursive definition. Can we provide any hint to Lean? Maybe?!

Alexandre Rademaker (Mar 29 2021 at 15:12):

I see.

Mario Carneiro (Mar 29 2021 at 15:13):

Structural recursion means it has to be written in a certain form. You pattern match on an inductive type and call the function on subterms

Mario Carneiro (Mar 29 2021 at 15:13):

sigma' isn't written like that because (LConcept.mk ls c) is not a subterm of { LConcept . roles := (Forall r :: ls) , concept := c}

Mario Carneiro (Mar 29 2021 at 15:14):

I'm not saying it can't be better, I'm saying there is a nontrivial design question that has to be resolved to improve the situation

Mario Carneiro (Mar 29 2021 at 15:16):

well founded recursion is much more flexible, you can call the function on anything with a smaller "size", where size is a natural number defined automatically on inductive types. sizeof (LConcept.mk ls c) < sizeof { LConcept . roles := (Forall r :: ls) , concept := c} which is why the wf recursion compilation strategy succeeds

Alexandre Rademaker (Mar 29 2021 at 15:17):

I see, thank you for the explanation.

Mario Carneiro (Mar 29 2021 at 15:17):

However, the issue with well founded recursion is that it requires unfolding proofs in order for the definition to definitionally reduce in the kernel (which is what #reduce does), and lean / mathlib generally make no effort to write proofs that can be computed well (indeed, most proofs use axioms and those block computation entirely)

Mario Carneiro (Mar 29 2021 at 15:18):

The real answer is "don't use #reduce" though

Mario Carneiro (Mar 29 2021 at 15:18):

It is slow and mostly useless

Mario Carneiro (Mar 29 2021 at 15:19):

#eval works fine on the wf definition

Alexandre Rademaker (Mar 29 2021 at 15:19):

But for using eval I will need to understand how to provide the has_repr instance to may structures and inductive types..

Mario Carneiro (Mar 29 2021 at 16:31):

@Alexandre Rademaker If the only reason you aren't using #eval is because #reduce gets better printing, here's a life hack for you

meta def print {α} [has_reflect α] (e : α) : tactic unit :=
tactic.pp (reflect e) >>= tactic.trace

#eval print $ sigma' (LConcept.mk [Forall R#0, Exists R#1] (Concept.Bot))
-- Ax R#0:Ex R#1:Bot

That way you just need @[derive has_reflect] on all your inductive types, and you get the same printing as lean will normally do, meaning that notations and things work as normal

Alexandre Rademaker (Mar 29 2021 at 16:48):

Thank you, I should have missed someting, I spread the @[derive has_reflect] all over the places but now eval does not produce anything

https://github.com/arademaker/alc-lean/blob/atomics/src/sequent.lean

Alexandre Rademaker (Mar 29 2021 at 16:50):

Oh, no. Sorry. It is working, the Emacs mode is not showing it with the C-c C-b (Toogle message boxes).

Mario Carneiro (Mar 29 2021 at 16:50):

you might also try using run_cmd instead of #eval; it should look the same but maybe emacs shows it differently


Last updated: Dec 20 2023 at 11:08 UTC