Zulip Chat Archive

Stream: new members

Topic: Inductive definition on (Fin n)?


Ching-Tsun Chou (Apr 29 2025 at 23:20):

Is it possible to make an inductive definition on Fin n (more precisely, Fin (n + 1) where n : ℕ) in a manner similar to an inductive definition on ℕ? I tried something like the following:

def MakeFinRun (M : DetAutomata A) (n : ) (as : Fin n  A) : Fin (n + 1)  M.State
  | 0 => M.get_init
  | k + 1 => M.get_next (MakeinRun M n as k) (as k)

and got the following error message on the k + 1 case which I don't understand:

invalid patterns, k is an explicit pattern variable, but it only occurs in positions that are inaccessible to pattern matching
⟨.(match ↑k + 1 % (n + 1), n + 1 with
| 0, x => 0
| n@h:n_1.succ, m => if m ≤ n then n.modCore m else n),
⋯⟩

Is there a way to make the above work?

Matt Diamond (Apr 29 2025 at 23:27):

if structural recursion doesn't work, maybe docs#Fin.induction will?

Ching-Tsun Chou (Apr 29 2025 at 23:35):

How do I use an induction theorem to make an inductive definition? Is there an example I can look at?

Ching-Tsun Chou (Apr 30 2025 at 01:58):

Fortunately, well-founded recursion works here:

def MakeFinRun (M : DetAutomata A) (n : ) (as : Fin n  A) (k : Fin (n + 1)) : M.State :=
  if h : k > 0 then
    have h1 : k - 1 < k := by exact Fin.sub_one_lt_iff.mpr h
    have h2 : k - 1  Fin.last n := by exact Fin.ne_last_of_lt h1
    M.get_next (MakeFinRun M n as (k - 1)) (as (Fin.castPred (k - 1) h2))
  else M.get_init

Edward van de Meent (Apr 30 2025 at 13:05):

you can also do this:

def MakeFinRun (M : DetAutomata A) (n : ) (as : Fin n  A) : Fin (n + 1)  M.State
  | 0 => M.get_init
  | k + 1,_⟩ => M.get_next (MakeFinRun M n as k,by omega) (as k,by omega)

Edward van de Meent (Apr 30 2025 at 13:06):

the thing is: Fin is an inductive type, just maybe not in the way you might have expected

Edward van de Meent (Apr 30 2025 at 13:06):

so you just need to know how to properly match on it

Ching-Tsun Chou (Apr 30 2025 at 15:52):

@Edward van de Meent Thanks a lot! Do you know where I can look up such information? Or you just have to know it?

Edward van de Meent (Apr 30 2025 at 16:29):

If you look at the definition of Fin, you'll see it's a subtype, which always can be destructed in this way

Ching-Tsun Chou (Apr 30 2025 at 17:21):

I see. Thanks! In a proof, how do I unfold such a definition? For an recursive definition on ℕ, simp usually works. But simp seems unable to reduce the implicit match in the MakeFinRun. Are there additional theorems I need to give to simp, or I need to use some special tactics?

Ching-Tsun Chou (Apr 30 2025 at 17:35):

For example, how do I prove the following?

def foo (n : ) : Fin (n + 1)  
  | 0 => 13
  | k + 1, _⟩ => 42 + k

example (n : ) : foo 0 = 13 := by sorry

example (n : ) : foo 7 = 48 := by sorry

Robin Arnez (Apr 30 2025 at 17:37):

This example doesn't work? foo takes 2 parameters and you only provided one?
If you do specify both parameters though, rfl will do it:

notation "ℕ" => Nat

def foo (n : ) : Fin (n + 1)  
  | 0 => 13
  | k + 1, _⟩ => 42 + k

example (_ : ) : foo 10 0 = 13 := by rfl
example (_ : ) : foo 10 7 = 48 := by rfl

Robin Arnez (Apr 30 2025 at 17:38):

As an alternative, simp [foo] also works

Ching-Tsun Chou (Apr 30 2025 at 17:45):

Sorry, my mistake. The type of goals I want to prove are:

example (n : ) : foo n 0 = 13 := by rfl

example (n : ) (h : n > 7) : foo n 7 = 48 := by sorry

I found that simp does not work in the first example. In the second example, the assumption h has to play a role somehow.

Robin Arnez (Apr 30 2025 at 18:54):

If you make your definition a bit simpler then this works:

import Mathlib.Data.Fin.Basic

def foo (n : ) (x : Fin (n + 1)) :  :=
  match x.val with
  | 0 => 13
  | k + 1 => 42 + k

example (_ : ) : foo 10 0 = 13 := by simp [foo]

example (_ : ) : foo 10 7 = 48 := by simp [foo]

example (n : ) : foo n 0 = 13 := by simp [foo]

example (n : ) (h : 7  n) : foo n 7 = 48 := by
  simp [foo, Fin.coe_ofNat_eq_mod, Nat.mod_eq_of_lt (Nat.lt_add_one_of_le h)]

Ching-Tsun Chou (Apr 30 2025 at 19:06):

That's a neat idea. But, unfortunately, my real use case involves a recursive definition (see the earlier part of this thread). So I would need to produce something of type Fin (n + 1) to feed into the recursive call.

Aaron Liu (Apr 30 2025 at 19:11):

You can use something like docs#Fin.succRec

Aaron Liu (Apr 30 2025 at 19:12):

or docs#Fin.induction

Aaron Liu (Apr 30 2025 at 19:13):

You can also make an auxiliary definition that takes the proof of x < n + 1 unbundled

Ching-Tsun Chou (Apr 30 2025 at 19:14):

is there an example of how to use such induction principles to make recursive definitions?

Ching-Tsun Chou (Apr 30 2025 at 19:43):

I just realized that instead of struggling with a recursive definition on Fin (n+1) and the associated proofs, I can just recurse on and then chop off the tail that I don't need. Perhaps the following definitions will be easier to work with. Time will tell.

def MakeInfRun (M : DetAutomata A) (as :   A) :   M.State
  | 0 => M.get_init
  | k + 1 => M.get_next (MakeInfRun M as k) (as k)

def MakeFinRun (M : DetAutomata A) (n : ) (as : Fin n  A) : Fin (n + 1)  M.State :=
  match n with
  | 0 => fun _  M.get_init
  | _ + 1 =>
    let as' := fun k :   if k < n + 1 then as k else as 0
    fun k  MakeInfRun M as' k.toNat

Ching-Tsun Chou (Apr 30 2025 at 23:22):

After more trials and errors, I found that the definition using well-founded recursion given below is the easier to work with.

def MakeFinRun (M : DetAutomata A) (n : ) (as : Fin n  A) (k : Fin (n + 1)) : M.State :=
  if h : k > 0 then
    have h1 : k - 1 < k := by exact Fin.sub_one_lt_iff.mpr h
    have h2 : k - 1  Fin.last n := by exact Fin.ne_last_of_lt h1
    M.get_next (MakeFinRun M n as (k - 1)) (as (Fin.castPred (k - 1) h2))
  else M.get_init

Shreyas Srinivas (Apr 30 2025 at 23:58):

docs#Fin2 is already inductive

Ching-Tsun Chou (May 01 2025 at 00:45):

Thanks for pointing this out. I did not know Fin2. Unfortunately, I already have a lot of definitions and proofs that use Fin. It is a bit too late to switch to Fin2 at this point.

Aaron Liu (May 01 2025 at 00:46):

Use docs#Fin.cases

Aaron Liu (May 01 2025 at 00:48):

I don't know if this works because you don't have a #mwe but

def MakeFinRun (M : DetAutomata A) (n : ) (as : Fin n  A) (k : Fin (n + 1)) : M.State :=
  k.cases M.get_init fun k =>
    M.get_next (MakeFinRun M n as k.castSucc) (as k)

Matt Diamond (May 01 2025 at 01:06):

if termination-checking fails, there's docs#Fin.induction (as I suggested earlier... sorry that I didn't have the time to provide an example then):

def MakeFinRun (M : DetAutomata A) (n : ) (as : Fin n  A) (k : Fin (n + 1)) : M.State :=
  k.induction M.get_init fun j IH => M.get_next IH (as j)

Last updated: May 02 2025 at 03:31 UTC