Zulip Chat Archive

Stream: new members

Topic: Computable equivalence of types


Jair Taylor (Jan 15 2024 at 08:38):

Hello! As an exercise for myself, I am trying to prove some facts about binary trees, namely that they are counted by Catalan numbers. I was hoping to show that the type of trees with n leaves is a Fintype, so that I could do an #eval and actually list off the trees with n leaves. Unfortunately, Lean warns me this definition is noncomputable, and I was wondering if anyone could give me tips on how to fix that.

I have the definitions

inductive BT where -- A binary tree is either
  | leaf : BT -- a leaf or
  | node : BT  BT  BT -- a pair of binary trees.

open BT

def NumLeaves (T : BT) :  :=
  match T with
  | leaf => 1
  | node T2 T3 => NumLeaves T2 + NumLeaves T3

def HasNumLeaves (T: BT) (n: ) : Prop := NumLeaves T = n
def BTWithNumLeaves (n : ) : Type := {T : BT // (HasNumLeaves T n)}

and I was able to prove the equivalence

def TreeEq (n: ℕ) (nz: n > 1): BTWithNumLeaves n ≃ Σ (k : Fin (n-1)), ( (BTWithNumLeaves (k+1)) × (BTWithNumLeaves (n-k-1)))

and used this with strong induction to show

def Treefin (n: ℕ) (nz: n > 0): Fintype (BTWithNumLeaves n)

but Lean warns me this is noncomputable. The problem seems to be with the base case

def LeafFinite: Fintype (BTWithNumLeaves 1)

which is also noncomputable. My proof of LeafFinite is here.

I wonder if the problem is this lemma where I use contradiction:

lemma SingletonLeaf (T: BT) (h: NumLeaves T = 1): T = leaf := by {
  induction T with
  | leaf => {
    rfl
   }
  | node T2 T3 _ => {
    have h3: NumLeaves T2 + NumLeaves T3  2 := Nat.add_le_add (BTsPosLeaves1 T2) (BTsPosLeaves1 T3)
    have h4: NumLeaves T2 + NumLeaves T3  1 :=  Nat.ne_of_lt' h3
    exact (h4 h).elim
  }
}

and if so if there's a way to prove this in a computable way.

Kevin Buzzard (Jan 15 2024 at 08:39):

If you're just trying to prove a theorem then you typically don't need to worry about whether things are computable or not.

Jair Taylor (Jan 15 2024 at 08:45):

That's true. I just find it more aesthetically pleasing if things are computable.

Mario Carneiro (Jan 15 2024 at 08:52):

no I think that's not the issue, usage of classical logic is independent of whether things are noncomputable

Mario Carneiro (Jan 15 2024 at 08:52):

the way in which you prove a theorem will not impact noncomputable

Mario Carneiro (Jan 15 2024 at 08:54):

The problem is that you marked an Equiv as a lemma (LeafEquiv is data, it also needs to be computable), and the reason this is noncomputable is because you used Equiv.equivOfUnique which skips the construction of an inverse function

Mario Carneiro (Jan 15 2024 at 08:58):

oh no, actually everything you used is computable already. You just mistakenly marked LeafInhabited, LeafUnique, and LeafEquiv as lemma instead of def

Mario Carneiro (Jan 15 2024 at 08:58):

#lint will tell you about this

Jair Taylor (Jan 15 2024 at 09:03):

oh, nice! OK, that's a simple fix. Thanks so much.

Jair Taylor (Jan 15 2024 at 09:07):

Ah, but now another issue: in my proof of Fintype (BTWithNumLeaves n) I am seeing

failed to compile definition, consider marking it as 'noncomputable' because it depends on 'Nat.strongInductionOn', and it does not have executable code

So is there an alternative to Nat.strongInductionOn that is computable?

Mario Carneiro (Jan 15 2024 at 09:17):

usually you can rewrite it as a well founded recursion, where the termination arguments correspond to uses of the inductive hypothesis

Jair Taylor (Jan 15 2024 at 09:23):

OK, I will look into well founded recursion. Thank you!

Eric Wieser (Jan 15 2024 at 09:44):

Jair Taylor said:

Ah, but now another issue: in my proof of Fintype (BTWithNumLeaves n) I am seeing

failed to compile definition, consider marking it as 'noncomputable' because it depends on 'Nat.strongInductionOn', and it does not have executable code

Am I right in thinking this is a bug?

Joachim Breitner (Jan 15 2024 at 09:46):

Maybe more a missing feature, I'd say.

Matt Diamond (Jan 15 2024 at 17:30):

kinda weird that docs#IsWellFounded.fix is computable while docs#WellFounded.fix isn't

Matt Diamond (Jan 15 2024 at 17:33):

in fact, the former appears to be defined in terms of the latter so I'm not sure how that's possible

Joachim Breitner (Jan 15 2024 at 18:37):

There is some trickery in Std to define a computable fix and then set

@[csimp] private theorem fix_eq_fixC : @fix = @fixC := rfl

Full details at https://github.com/leanprover/std4/blob/975b766b35f07e81fc34d66d026ad5bb2c1dbf45/Std/WF.lean#L10-L39

Matt Diamond (Jan 15 2024 at 20:13):

I'm curious if @Jair Taylor's computability problem would be fixed by switching from docs#Nat.strongInductionOn to docs#Nat.strong_induction_on

unless the fundamental problem is that it's a theorem and therefore can't be used to create data

Jair Taylor (Jan 15 2024 at 20:25):

No, it's a definition.

def Treefin (n: ) (nz: n > 0): Fintype (BTWithNumLeaves n) := by {
  induction n using Nat.strongInductionOn with
  | ind n ih => { ... }

I wasn't familiar with strong_induction_on but I think I can't use it here since it's only for Prop. I get

type mismatch when assigning motive
  fun n => 
has type
    Type : Type 1
but is expected to have type
    Prop : TypeLean 4

Matt Diamond (Jan 15 2024 at 20:30):

Sorry, I meant the issue being that Nat.strongInductionOn and Nat.strong_induction_on are theorems. But you're right about Nat.strong_induction_on only working for Prop... I didn't notice that difference, so I don't think it will help after all.

Matt Diamond (Jan 15 2024 at 20:34):

I'll just add that creating data in tactic mode is generally a bad idea anyway (or at least it was in Lean 3)

Jair Taylor (Jan 15 2024 at 20:43):

Oh I see. Actually, if I just copy the definition of strongInductionOn but change it from theorem to def, that seems to resolve the issue!

Joachim Breitner (Jan 15 2024 at 21:02):

Matt Diamond said:

I'll just add that creating data in tactic mode is generally a bad idea anyway (or at least it was in Lean 3)

Is that so? Depends on what you do; I'd expect some set of tactics (apply, cases, induction etc.) to be fairly innocent

Joachim Breitner (Jan 15 2024 at 21:03):

Often a recursive definition is easier to work with than strong_induction_on. (As Mario already said earlier)

Eric Wieser (Jan 15 2024 at 21:04):

It's often very handy to assemble large terms with refine in tactic mode; in Lean 3, sometimes it was ever necessary for better performance

Matt Diamond (Jan 15 2024 at 21:05):

ah, I see


Last updated: May 02 2025 at 03:31 UTC