Zulip Chat Archive

Stream: lean4

Topic: Using `partial_fixpoint`


François G. Dorais (Feb 18 2025 at 04:15):

How can I make this work?

def find (i : Nat) (p : Nat  Bool) : Nat :=
  if p i then i else find (i+1) p
partial_fixpoint

theorem find_eq_true_of_exists_ge (p : Nat  Bool) (i : Nat) (h :  j  i, p j) : p (find i p) = true := by
  rw [find]
  split
  · assumption
  · next hi =>
    have :  j  i+1, p j := by
      match h with
      | j, hj =>
        by_cases heq : i = j
        · exfalso
          apply hi
          rw [heq, hj.2]
        · exists j
          constructor
          · omega
          · exact hj.2
    exact find_eq_true_of_exists_ge p (i+1) this
partial_fixpoint -- doesn't work, what should I use?

Kyle Miller (Feb 18 2025 at 04:25):

I think partial_fixpoint only works if the type is inhabited, so for theorems it's not so useful

François G. Dorais (Feb 18 2025 at 04:25):

I understand that this is not the intended usage since find is not total but this is a natural use case, isn't it?

François G. Dorais (Feb 18 2025 at 04:27):

Kyle Miller said:

I think partial_fixpoint only works if the type is inhabited, so for theorems it's not so useful

Indeed, but there might be some mechanisms in place to reason about the domain of a partial_fixpoint function.

Kyle Miller (Feb 18 2025 at 04:29):

My understanding is that you'd need to prove the theorem first to prove that the theorem's type is inhabited, and then you'd be able to use partial_fixpoint on the theorem (defeating the purpose of trying to use partial_fixpoint in the first place). Just pointing this out given that your example ends with partial_fixpoint on a theorem.

Yes, it should be possible to reason about find.

François G. Dorais (Feb 18 2025 at 04:34):

Should it be? All functions are total in Lean's type theory, so perhaps Lean just assumes that find is not acceptable until I prove it is total? (It's not total, of course.)

Kyle Miller (Feb 18 2025 at 04:42):

Yeah, definitely provable:

def find (i : Nat) (p : Nat  Bool) : Nat :=
  if p i then i else find (i+1) p
partial_fixpoint

theorem find_eq_true_of_exists_ge (p : Nat  Bool) (i : Nat) (h :  j  i, p j) : p (find i p) = true := by
  obtain j, h, hpj := h
  obtain k, rfl := Nat.exists_eq_add_of_le h
  clear h
  induction k generalizing i with
  | zero =>
    rw [Nat.add_zero] at hpj
    rw [find]
    simp [hpj]
  | succ k ih =>
    rw [find]
    split
    · assumption
    · apply ih
      simpa only [Nat.add_succ, Nat.succ_add] using hpj

Kyle Miller (Feb 18 2025 at 04:42):

You can see that the equation lemma find.eq_1 is very usable.

François G. Dorais (Feb 18 2025 at 05:03):

Very nice!

François G. Dorais (Feb 18 2025 at 07:16):

I'm still perplexed how to make sense of partial_fixpoint in Lean's type theory and things like find 0 fun _ => false being a closed term.

Note that Lean can now decide the halting problem and then some!

def find (i : Nat) (p : Nat  Bool) : Nat :=
  if p i then i else find (i+1) p
partial_fixpoint

theorem find_eq_true_of_exists_ge (p : Nat  Bool) (i : Nat) (h :  j  i, p j) : p (find i p) = true := by
  obtain j, h, hpj := h
  obtain k, rfl := Nat.exists_eq_add_of_le h
  clear h
  induction k generalizing i with
  | zero =>
    rw [Nat.add_zero] at hpj
    rw [find]
    simp [hpj]
  | succ k ih =>
    rw [find]
    split
    · assumption
    · apply ih
      simpa only [Nat.add_succ, Nat.succ_add] using hpj

theorem find_eq_find_zero_of_forall_eq_false (hp :  i, p i = false) : find i p = find 0 p := by
  induction i with
  | zero => rfl
  | succ i hi =>
    conv => rhs; rw [ hi, find]; simp [hp]

def test (p : Nat  Bool) : Bool := p (find 0 p)

theorem test_eq_true_of_exists (h :  j, p j) : test p = true :=
  find_eq_true_of_exists_ge p 0 (let j, hj := h; j, Nat.zero_le _, hj)

theorem test_eq_true_iff_exists : test p   j, p j := by
  constructor
  · intro h
    rw [test] at h
    exists find 0 p
  · exact test_eq_true_of_exists

I can't tell whether this is neat or scary.

#print opaques find reveals the secret but that's not completely comforting either.

James Gallicchio (Feb 18 2025 at 07:22):

This is no worse than using Classical, though, right? Everything (including the halting problem) is already decidable in Prop.

James Gallicchio (Feb 18 2025 at 07:24):

And compiled Lean is only intended to have partial correctness wrt the type theory spec, e.g. panic! is non-total

François G. Dorais (Feb 18 2025 at 07:42):

It is using Classical.choice.

François G. Dorais (Feb 18 2025 at 08:13):

One danger is that one can write seemingly sensible code like this function that returns the minimum value of f : Nat → Nat:

def min (f : Nat  Nat) : Nat := f (find 0 fun i => !test fun j => f j < f i)

It's not difficult to prove that min is total and that min (fun _ => 0) = 0. One might reasonably expect that min (fun _ => 0) will return 0. However, min f never halts no matter what f is.


Last updated: May 02 2025 at 03:31 UTC