Zulip Chat Archive

Stream: general

Topic: Inferring decidability


Bolton Bailey (Apr 17 2022 at 01:40):

The following mwe comes from some experiments we were running on the speed of the Miller-Rabin test. Lean has no trouble figuring out that equality on zmod is decidable when evaluating fast_strong_probable_prime, but it somehow won't infer this when evaluating fermat_strong_probable_prime. Why is this? In fact, why is it even necessary to write or.decidable to prove fast_strong_probable_prime is decidable? Shouldn't the typeclass inference system know this already without having to be told?

import data.zmod.basic

def binpow {M} [has_one M] [has_mul M] (m : M) :   M :=
nat.binary_rec 1 (λ b _ ih, let ih2 := ih * ih in cond b (m * ih2) ih2)

def fast_two_multiplicity :    :=
nat.binary_rec 0 (λ b _ ih, cond b 0 (ih+1))

def fast_odd_part (n : ) := n / (2 ^ fast_two_multiplicity n)

def fast_strong_probable_prime (n : nat) (a : zmod n) : Prop :=
binpow a (fast_odd_part (n-1)) = 1
 ( r : , r < fast_two_multiplicity (n-1)  binpow a (2^r * fast_odd_part(n-1)) = -1)

def fermat_strong_probable_prime (n : nat) (a : zmod n) : Prop :=
binpow a (n-1) = 1

instance {n : } {a : zmod n} : decidable (fast_strong_probable_prime n a) := or.decidable

#eval to_bool (fast_strong_probable_prime 1241213 3)

-- instance {n : ℕ} {a : zmod n} : decidable (fermat_strong_probable_prime n a) := sorry

#eval to_bool (fermat_strong_probable_prime 1241213 3)

Kyle Miller (Apr 17 2022 at 02:23):

Definitions are by default semireducible, and semireducible definitions aren't unfolded during typeclass inference. If you unfold it yourself, then it's able to infer it automatically:

instance {n : } {a : zmod n} : decidable (fast_strong_probable_prime n a) :=
by { unfold fast_strong_probable_prime, apply_instance }

When you do or.decidable, what's happening is that the elaborator unfolds the definition for you since during unification semireducible definitions are unfolded as needed.

Kyle Miller (Apr 17 2022 at 02:24):

If you make the definition reducible, then typeclass inference will unfold the definition if needed:

@[reducible]
def fast_strong_probable_prime (n : nat) (a : zmod n) : Prop :=
binpow a (fast_odd_part (n-1)) = 1
 ( r : , r < fast_two_multiplicity (n-1)  binpow a (2^r * fast_odd_part(n-1)) = -1)

#eval to_bool (fast_strong_probable_prime 1241213 3)

Kyle Miller (Apr 17 2022 at 02:25):

Both of these methods adapt for the last #eval. Here's creating a decidable instance by unfolding:

instance {n : } {a : zmod n} : decidable (fermat_strong_probable_prime n a) :=
by { unfold fermat_strong_probable_prime, apply_instance }

#eval to_bool (fermat_strong_probable_prime 1241213 3)

Kyle Miller (Apr 17 2022 at 02:29):

You can also avoid this by making your functions be bool-valued:

def fast_strong_probable_prime (n : nat) (a : zmod n) : bool :=
binpow a (fast_odd_part (n-1)) = 1
 ( r : , r < fast_two_multiplicity (n-1)  binpow a (2^r * fast_odd_part(n-1)) = -1)

def fermat_strong_probable_prime (n : nat) (a : zmod n) : bool :=
binpow a (n-1) = 1

#eval fast_strong_probable_prime 1241213 3

#eval fermat_strong_probable_prime 1241213 3

There's a coercion from bool to Prop so you can still use these inside propositions.

#eval to_bool (fast_strong_probable_prime 1241213 3  fermat_strong_probable_prime 1241213 3)

Last updated: Dec 20 2023 at 11:08 UTC