Zulip Chat Archive
Stream: general
Topic: Mutual recursion with dependency on an inductve proof
Naruyoko (Jul 18 2023 at 19:06):
I'm trying to define functions with mutual recursion. The recursion also depends on properties of the previous step. The following code does not work:
mutual def f, g, p
with f : ℕ → ℕ → ℕ
| i 0 := i
| i (j + 1) := i + g i j
with g : ℕ → ℕ → ℕ
| i j := (i + 2) ^ (nat.cases_on j 1 (λ j', (list.range (g i j')).nth_le (f i j') (lt_of_lt_of_eq (p j' i) (list.length_range _).symm)) : ℕ)
with p : ∀ (j : ℕ), (∀ (i : ℕ), f i j < g i j)
| 0 := sorry
| (j + 1) := sorry
The specific definitions and predicate are placeholders.
There seems to be 2 problems here:
- The definition has "ill-formed match/equations expression"
- Even if I remove mutual recursion, I get am told that it is an "invalid mutual definition, result types must be in the same universe"
Additionally, as in g
, I want to split by cases of j
inside a larger expression, so I can reuse the structure. I want this in a later mutually recursed proof which only needs to see that shared structure.
How can I achieve this?
Reid Barton (Jul 18 2023 at 19:25):
How about defining a function of type ℕ -> ℕ -> { a : ℕ \x ℕ // a.1 < a.2 }
?
Naruyoko (Jul 18 2023 at 20:04):
Thank you, I guess that is an option too, and the individual functions and theorems would be extracted from that definition. I am not sure how I can write it in a way that doesn't go in a mess though as I am going to put many theorems that depend on others. I also don't know a way which allow delaying the case splitting either, or have a friendly name for individual functions in the recursive definition and proofs, though those are cosmetics.
Naruyoko (Jul 20 2023 at 01:10):
I decided to define a structure to bundle the needed functions and properties and explicitly recurse on j
(slightly inconvenient since I'd have to repeat some parts). Interestingly, I get a time-out if I use the case matching notation with let prev := ... in
on j
, but no problem at all if I write out nat.rec
.
Arthur Adjedj (Jul 20 2023 at 08:25):
Do you have an #mwe for the timeout ?
Naruyoko (Jul 21 2023 at 20:35):
Arthur Adjedj said:
Do you have an #mwe for the timeout ?
Is this good?
https://gist.github.com/Naruyoko/f377dd2577d7ebb2911fbea13ff172c9
I tried to see the minimum complexity to get the timeout. I think I got it too close though, since it times out on Linux (in VirtualBox) but not on Windows 11 for me.
Lean (version 3.50.3, commit 855e5b74e3a5, Release)
Edit: Now the example does not time out anywhere, so I will have to add complexity back in.
Kevin Buzzard (Jul 21 2023 at 20:42):
Oh you're using lean 3 still?
Naruyoko (Jul 21 2023 at 20:44):
Yes. Should I migrate?
Updated the gist so I think it should time out now.
Kevin Buzzard (Jul 21 2023 at 22:02):
Well maybe migrating will fix the problem!
Kyle Miller (Jul 21 2023 at 22:16):
My guess without testing anything is that Lean is doing zeta reduction on the let
(i.e., substituting the value into the body of the let
) while doing some analysis, and this recursively calculated value appears multiple times in the body.
It looks like the entire body of the j+1
case is actually a non-recursive function resultAt x -> resultAt x
that takes in the value of recursive_function_pattern_match j
. Maybe try lifting that out into its own function?
Kyle Miller (Jul 21 2023 at 22:17):
By the way, you can write
dite (v.val < x.length)
(assume h, coe <$> (pnat.has_sub.sub <$> (f i) <*> (f ⟨v.val, h⟩)))
(assume _, none)
as
if h : v.val < x.length
then coe <$> (pnat.has_sub.sub <$> (f i) <*> (f ⟨v.val, h⟩))
else none
Naruyoko (Jul 24 2023 at 00:38):
In my case, I found that only a few properties needed to be proved, and the rest could be inferred. Therefore, I ended up defining a builder function (something like resultBuilder : Π (x : list ℕ) (f : fin x.length → option ℕ+) (g : fin x.length → option ℕ) (property1 : _) -> resultAt x
) that takes the few input functions and propositions and creates the object I wanted. This reduced the repetition between the 0
and j+1
cases. I think it also had a similar effect as your suggestion to make the recursion a separate function (let
works now!).
I didn't know if then else
syntax could be dependent. Thank you for the tip.
And thanks to everyone who looked at this.
Last updated: Dec 20 2023 at 11:08 UTC