Zulip Chat Archive

Stream: lean4

Topic: Decidability of inductive propositions.


Wrenna Robson (Nov 08 2024 at 11:00):

I think this is a MWE: how do I prove the decidability of isRoot in the following?

inductive PSharedStack (β : Type u) : Type u
  | sngl : β  PSharedStack β
  | bit0 : PSharedStack β  PSharedStack β
  | bit1 : β  PSharedStack β  PSharedStack β
deriving DecidableEq

inductive isRoot : PSharedStack β  Prop
  | protected sngl : (a : β)  isRoot (sngl a)
  | protected bit0 : (p : PSharedStack β)  p.isRoot  isRoot (bit0 p)

instance : Decidable (p.isRoot) := match p with
  | sngl _ => isTrue isRoot_sngl
  | bit0 _ => sorry
  | bit1 _ _ => isFalse not_isRoot_bit1

Wrenna Robson (Nov 08 2024 at 11:00):

I have these:

@[simp] theorem isRoot_sngl : (sngl a).isRoot := by constructor

@[simp] theorem not_isRoot_bit1 : ¬ (bit1 a p).isRoot := fun h => by contradiction

@[simp] theorem isRoot_bit0_iff : (bit0 p).isRoot  p.isRoot :=
  fun h => by cases h ; assumption, fun h => by constructor ; assumption

Wrenna Robson (Nov 08 2024 at 11:01):

What I am essentially doing is I want to recursively construct the decidability - we have Decidable p.bit0.isRoot if and only if Decidable p.isRoot, and so I want to essentially go "OK, proceed by induction", but that doesn't seem to work.

Kim Morrison (Nov 08 2024 at 11:03):

Often a good approach is to define a Bool valued function.

Kim Morrison (Nov 08 2024 at 11:03):

Then write a proof that the function returns true iff the inductive predicate holds.

Kim Morrison (Nov 08 2024 at 11:03):

Then use decidable_of_iff.

Wrenna Robson (Nov 08 2024 at 11:03):

Right, actually that is what I started with (as in, isRoot was bool-valued), but in some ways it seemed less neat.

Kim Morrison (Nov 08 2024 at 11:04):

I'm suggesting you have both.

Wrenna Robson (Nov 08 2024 at 11:04):

Right. Got it. What's the naming convention?

Wrenna Robson (Nov 08 2024 at 11:04):

i.e. if the Prop is isRoot, what would we call the Bool? bIsRoot?

Wrenna Robson (Nov 08 2024 at 11:13):

def bIsRoot : PSharedStack β  Bool
  | sngl _ => true
  | bit0 p => p.bIsRoot
  | bit1 _ _ => false

@[simp] theorem bIsRoot_sngl : (sngl a).bIsRoot = true := rfl
@[simp] theorem bIsRoot_bit0 : (bit0 p).bIsRoot = p.bIsRoot := rfl
@[simp] theorem bIsRoot_bit1 : (bit1 a p).bIsRoot = false := rfl

@[simp] theorem bIsRoot_eq_true_iff : p.bIsRoot  p.isRoot := by
  induction p
  · exact iff_of_true bIsRoot_sngl isRoot_sngl
  · rw [bIsRoot_bit0, isRoot_bit0_iff]
    assumption
  · exact iff_of_false (Bool.not_eq_true _  bIsRoot_bit1) not_isRoot_bit1

instance : Decidable (p.isRoot) := decidable_of_bool _ bIsRoot_eq_true_iff

Wrenna Robson (Nov 08 2024 at 11:13):

So this sort of thing?

Kyle Miller (Nov 08 2024 at 15:32):

Looks good.

An idea we've been kicking around is to redefine Decidable as

class Decidable (p : Prop) where
  decide : Bool
  cond_decide : cond decide p (Not p)

Then defining a bool-valued function and proving it represents the truth of the proposition will look like the natural thing to do.

Wrenna Robson (Nov 08 2024 at 15:33):

Incidentally I actually avoided this because:

theorem isRoot_iff_count_eq_two_pow_lg2 : p.isRoot  p.count = 2^p.lg2 := by
  induction p with | sngl a => _ | bit0 p IH => _ | bit1 a p IH => _
  · simp_rw [isRoot_sngl, count_sngl, lg2_sngl, pow_zero]
  · simp_rw [isRoot_bit0_iff, IH, count_bit0, lg2_bit0, pow_succ', mul_eq_mul_left_iff,
      zero_lt_two.ne', or_false]
  · simp_rw [not_isRoot_bit1, count_bit1, lg2_bit1, false_iff, pow_succ']
    exact (Nat.two_mul_ne_two_mul_add_one).symm

instance : Decidable (p.isRoot) :=
  decidable_of_iff _ isRoot_iff_count_eq_two_pow_lg2.symm

Wrenna Robson (Nov 08 2024 at 15:34):

i.e. I have some other quantities that are nats and which characterise isRoot. so I can actually just use decidability of nat equality.

Wrenna Robson (Nov 08 2024 at 15:34):

That is a nice idea though.

Wrenna Robson (Nov 08 2024 at 15:35):

(Of course this might be less efficient... my way, I mean. There is a distinction between "there is a decision procedure for this prop" and "here is a specific, efficient decision procedure for the prop, which is the one I want Lean to perform when evaluating".)

Kyle Miller (Nov 08 2024 at 15:39):

What you're doing is the same idea. You're indirectly using the boolean-value function "evaluate p.count == 2^p.lg2"

Wrenna Robson (Nov 08 2024 at 15:39):

Oh, certainly.

Wrenna Robson (Nov 08 2024 at 15:40):

And that may not be the best way, actually!

Wrenna Robson (Nov 08 2024 at 15:40):

I was just musing that sometimes what is most convenient from a "defining and proving things quickly" standpoint is not what is most performant.


Last updated: May 02 2025 at 03:31 UTC