Zulip Chat Archive

Stream: new members

Topic: fail to show termination


Xiyu Zhai (Sep 27 2022 at 14:21):

Hi, everyone. In the following code, the compiler fails to show termination for X.n2, but X.n1 is fine.

Probably because X.n2 involves a map.

inductive X
  | None
  | Single (x : X)
  | Many (xs : List X)

def X.n1 : X -> Nat
  | None => 0
  | Single x => x.n1
  | Many xs => xs.length

def X.n2 : X -> Nat
  | None => 0
  | Single x => x.n2
  | Many xs => match (xs.map X.n2).maximum? with
    | some v => v
    | none => 0

Is this a feature or is this to be improved in the future?
If this will be okay in the future, how to circumstance it for now?
Thanks a lot.

Xiyu Zhai (Sep 27 2022 at 14:24):

The following also doesn't work for X.n2

inductive X
  | None
  | Single (x : X)
  | Many (xs : List X)

def X.n1 : X -> Nat
  | None => 0
  | Single x => x.n1
  | Many xs => xs.length

def X.n2 : X -> Nat
  | None => 0
  | Single x => x.n2
  | Many xs => match xs with
    | [] => 0
    | x::xs => match (xs.map X.n2).maximum? with
      | some v => max x.n2 v
      | none => x.n2

Arthur Adjedj (Sep 27 2022 at 14:52):

Would this work for you ?

def X.n2 : X -> Nat
  | None => 0
  | Single x => x.n2
  | Many [] => 0
  | Many (x::xs) => max x.n2 (Many xs).n2

Kyle Miller (Sep 27 2022 at 15:02):

Xiyu Zhai said:

Is this a feature or is this to be improved in the future?

To get it to work you need to help Lean out with a termination_by clause. This is incomplete, but here's one way to try to set it up that I think is possible to complete:

inductive X
  | None
  | Single (x : X)
  | Many (xs : List X)

def X.n1 : X -> Nat
  | None => 0
  | Single x => x.n1
  | Many xs => xs.length

def List.attach {α : Type _} : (xs : List α)  List {x // x  xs}
| [] => []
| x::xs => x, by constructor :: xs.attach.map (λ y => y.val, by { constructor; exact y.2 }⟩)

def X.n2 : X -> Nat
  | None => 0
  | Single x => x.n2
  | Many xs =>
    have :  (x : {x // x  xs}), sizeOf x.val < 1 + sizeOf xs := by
      intro x
      sorry
    match (xs.attach.map (λ x => have := this x; X.n2 x.val)).maximum? with
    | some v => v
    | none => 0
termination_by X.n2 x => x

Kyle Miller (Sep 27 2022 at 15:02):

The definition by @Arthur Adjedj is much simpler.

Xiyu Zhai (Sep 27 2022 at 15:18):

Arthur Adjedj said:

Would this work for you ?

def X.n2 : X -> Nat
  | None => 0
  | Single x => x.n2
  | Many [] => 0
  | Many (x::xs) => max x.n2 (Many xs).n2

Thanks a lot! This works amazingly.

Xiyu Zhai (Sep 27 2022 at 15:21):

Kyle Miller said:

Xiyu Zhai said:

Is this a feature or is this to be improved in the future?

To get it to work you need to help Lean out with a termination_by clause. This is incomplete, but here's one way to try to set it up that I think is possible to complete:

inductive X
  | None
  | Single (x : X)
  | Many (xs : List X)

def X.n1 : X -> Nat
  | None => 0
  | Single x => x.n1
  | Many xs => xs.length

def List.attach {α : Type _} : (xs : List α)  List {x // x  xs}
| [] => []
| x::xs => x, by constructor :: xs.attach.map (λ y => y.val, by { constructor; exact y.2 }⟩)

def X.n2 : X -> Nat
  | None => 0
  | Single x => x.n2
  | Many xs =>
    have :  (x : {x // x  xs}), sizeOf x.val < 1 + sizeOf xs := by
      intro x
      sorry
    match (xs.attach.map (λ x => have := this x; X.n2 x.val)).maximum? with
    | some v => v
    | none => 0
termination_by X.n2 x => x

Thanks a lot! I really need to learn a lot about well-defined recursion.


Last updated: Dec 20 2023 at 11:08 UTC