Zulip Chat Archive
Stream: lean4
Topic: better way to write `rangeStepAux`
Joseph O (Feb 22 2022 at 14:57):
I have this function, but it is not structural recursion.
protected def List.rangeStepAux (m n : Nat) (l : List Nat): List Nat :=
if n = m then
l
else
List.rangeStepAux m (n+2) (l.take n |>.append $ l.drop (n+1) |>.take (l.length - (n+1)))
What different/better ways are there to do this where I won't have to prove termination, and, I would prefer no code, and rather verbal suggestions so I can try to work this out myself. Thanks in advance!
Mario Carneiro (Feb 22 2022 at 15:07):
use partial
Mario Carneiro (Feb 22 2022 at 15:08):
I don't really know what your function is supposed to do, but it infinite loops when I test it
Mario Carneiro (Feb 22 2022 at 15:09):
so proving termination is probably going to be difficult
Joseph O (Feb 22 2022 at 15:48):
Mario Carneiro said:
I don't really know what your function is supposed to do, but it infinite loops when I test it
Make a range from 0-n, and skip every m
th term
Mario Carneiro (Feb 22 2022 at 15:50):
so like 1,2,4,5,7,8
?
Mario Carneiro (Feb 22 2022 at 15:50):
Your algorithm also looks cubic
Joseph O (Feb 22 2022 at 15:52):
Mario Carneiro said:
so like
1,2,4,5,7,8
?
yes. that would be skipping every third term
Mario Carneiro (Feb 22 2022 at 15:52):
What do you do if m
is negative, if the start point is greater than the end point, or if the end point is not a multiple of the step? Lots of things cause that code to miss its exit point
Joseph O (Feb 22 2022 at 15:52):
Mario Carneiro said:
Your algorithm also looks cubic
im thinking of scratching my algorithm
Joseph O (Feb 22 2022 at 15:52):
Mario Carneiro said:
What do you do if
m
is negative, if the start point is greater than the end point, or if the end point is not a multiple of the step? Lots of things cause that code to miss its exit point
m will never be negative
Mario Carneiro (Feb 22 2022 at 15:52):
your function signature doesn't say so
Joseph O (Feb 22 2022 at 15:53):
(m n : Nat)
Mario Carneiro (Feb 22 2022 at 15:53):
er I meant the other thing
Mario Carneiro (Feb 22 2022 at 15:53):
the start point can be greater than the end point in the aux function
Mario Carneiro (Feb 22 2022 at 15:54):
also a step size of 0 is bad
Joseph O (Feb 22 2022 at 15:54):
Yes, but I use this in a function rangeStep
where I use it correctly
Mario Carneiro (Feb 22 2022 at 15:55):
okay, then rangeStepAux
needs hypotheses asserting that it's being called correctly
Joseph O (Feb 22 2022 at 15:55):
But I would like to use a different algorithm? Do you have ideas for a different one?
Mario Carneiro (Feb 22 2022 at 15:55):
sure, this sounds like (range n).filter (\lam i, i % m != 0)
Joseph O (Feb 22 2022 at 15:55):
Mario Carneiro said:
okay, then
rangeStepAux
needs hypotheses asserting that it's being called correctly
ok. I cant do that, but I dont really have to worry about what you said, because i'm pretty sure that cant appen
Mario Carneiro (Feb 22 2022 at 15:57):
You misunderstand. You say that rangeStep
checks that certain boundary cases don't happen, while rangeStepAux
relies on those boundary cases not being inputs. That means that rangeStepAux
needs to assert that the bad cases are not legal in its type signature or else you won't be able to prove termination
Mario Carneiro (Feb 22 2022 at 15:58):
because rangeStepAux
is just a regular function, people can call it themselves if they want
Mario Carneiro (Feb 22 2022 at 16:00):
For example
protected def List.rangeStepAux (start stop step : Nat) (h : start ≤ stop) (s0 : step ≠ 0) : List Nat := sorry
Joseph O (Feb 22 2022 at 16:01):
but I made it protected so people cant use it
Mario Carneiro (Feb 22 2022 at 16:02):
nope, that's not what protected means
Joseph O (Feb 22 2022 at 16:02):
or do I have to use private
, if that is a keyword
Mario Carneiro (Feb 22 2022 at 16:02):
protected means you have to write List.rangeStepAux
even if you open List
Joseph O (Feb 22 2022 at 16:02):
Ah. If I use private, will that allow people not to use it?
Mario Carneiro (Feb 22 2022 at 16:02):
private
makes the function not easily accessible outside the current section
Joseph O (Feb 22 2022 at 16:03):
oh
Mario Carneiro (Feb 22 2022 at 16:03):
but it's not air tight and more to the point it is not something you can use to do proofs
Mario Carneiro (Feb 22 2022 at 16:03):
if you want to know that a fact is true, you take it as an argument
Joseph O (Feb 22 2022 at 16:03):
right
Mario Carneiro (Feb 22 2022 at 16:04):
this makes the function inconvenient to call, but that's fine since this is an aux function
Joseph O (Feb 22 2022 at 16:04):
Mario Carneiro said:
For example
protected def List.rangeStepAux (start stop step : Nat) (h : start ≤ stop) (s0 : step ≠ 0) : List Nat := sorry
I dont take start as an argument
Joseph O (Feb 22 2022 at 16:04):
At least with my current algorithm
Mario Carneiro (Feb 22 2022 at 16:04):
in your code start
was n
and stop
was m
and step
was... 2?
Mario Carneiro (Feb 22 2022 at 16:05):
So start
is the current value, it increases until it exceeds stop
Joseph O (Feb 22 2022 at 16:07):
Mario Carneiro said:
in your code
start
wasn
andstop
wasm
andstep
was... 2?
sorry if it was very unclear. m
was stop, and n
was step
Joseph O (Feb 22 2022 at 16:07):
I should have made that more clear
Mario Carneiro (Feb 22 2022 at 16:10):
If that's the case, then your step increased by 2 on every iteration
Mario Carneiro (Feb 22 2022 at 16:11):
maybe you and I mean different things by "step"
Joseph O (Feb 22 2022 at 16:23):
ah, that is not what i meant. I meant to increase by step
Joseph O (Feb 22 2022 at 16:23):
Let me see what fixing my mistake does.
Joseph O (Feb 22 2022 at 16:25):
I still seem to need to prove termination
Mario Carneiro (Feb 22 2022 at 16:26):
well yes, but now it's actually provable (hopefully)
Mario Carneiro (Feb 22 2022 at 16:27):
if you don't want to prove termination you can use partial
, but if it is actually nonterminating for some inputs then users will not like it
Mario Carneiro (Feb 22 2022 at 16:28):
but if your definition is not structurally recursive then in all likelihood you will need to prove termination manually assuming no partial
Mario Carneiro (Feb 22 2022 at 16:29):
note that mathlib will almost certainly want such a function to not be partial
because you can't prove facts about partial
functions
Joseph O (Feb 22 2022 at 16:31):
Yes. The only problem now is, how can we prove termination? On what case. I think I have an idea.
Joseph O (Feb 22 2022 at 16:32):
protected def List.rangeStepAux (stop step : Nat) (l : List Nat): List Nat :=
if step = stop then
l
else
List.rangeStepAux stop (step*2) (l.take step |>.append $ l.drop (step+1) |>.take (l.length - (step+1)))
termination_by List.rangeStepAux m n _ => n = m
i think I am close?
Mario Carneiro (Feb 22 2022 at 16:33):
This code has exactly the same nontermination issues as the original
Mario Carneiro (Feb 22 2022 at 16:34):
I would rename step
to start
btw
Mario Carneiro (Feb 22 2022 at 16:34):
the step is 2 there
Mario Carneiro (Feb 22 2022 at 16:35):
your termination measure should be stop - start
most likely
Joseph O (Feb 22 2022 at 16:35):
Wait, I still didn't fix the previous bug
Joseph O (Feb 22 2022 at 16:35):
Actually nevermind
Joseph O (Feb 22 2022 at 16:36):
Mario Carneiro said:
I would rename
step
tostart
btw
Why?
Mario Carneiro (Feb 22 2022 at 16:36):
because it's not a step
Mario Carneiro (Feb 22 2022 at 16:37):
it's the first value produced by the code
Joseph O (Feb 22 2022 at 16:46):
Ok
Joseph O (Feb 22 2022 at 16:46):
Right
Joseph O (Feb 22 2022 at 16:50):
Mario Carneiro said:
your termination measure should be
stop - start
most likely
hmm
Joseph O (Feb 22 2022 at 17:39):
And what could I compare that with? stop - start = 0
?
Mario Carneiro (Feb 22 2022 at 18:27):
nothing, termination_by
is expecting an expression of type Nat (or other well ordered type)
Joseph O (Feb 22 2022 at 18:46):
Ah. So just that. I will try it
Joseph O (Feb 22 2022 at 19:43):
I get this image.png
Mauricio Collares (Feb 22 2022 at 19:53):
The argument order in your recursive call seems wrong
Joseph O (Feb 22 2022 at 20:07):
You may be right
Joseph O (Feb 22 2022 at 20:10):
So now it results into something that will be false most of the time image.png
Mauricio Collares (Feb 22 2022 at 20:12):
Why? This is now true unless start == 0. By the way, originally you had +2
and now you have *2
, is that intentional?
Mauricio Collares (Feb 22 2022 at 20:16):
I guess I am having trouble understanding what the function is supposed to do, so it's entirely possible that my suggestion makes no sense
Mauricio Collares (Feb 22 2022 at 20:18):
I would recommend making the function partial and running it to check if the outputs match what you expect, before proving termination
Joseph O (Feb 22 2022 at 20:26):
Mauricio Collares said:
Why? This is now true unless
start = 0
. By the way, originally (before renaming fromm n
tostart stop
) you had+2
and now you have*2
, is that intentional?
The +2
was a mistake
Joseph O (Feb 22 2022 at 20:27):
Mauricio Collares said:
I guess I am having trouble understanding what the function is supposed to do, so it's entirely possible that my suggestion makes no sense
Joseph O (Feb 22 2022 at 20:27):
Mario Carneiro said:
sure, this sounds like
(range n).filter (\lam i, i % m != 0)
I missed this entirely
Joseph O (Feb 22 2022 at 20:28):
This is a filter, I just didn’t know how to keep track of something. thanks again,
Mario Carneiro (Feb 22 2022 at 20:47):
Joseph O said:
Mauricio Collares said:
Why? This is now true unless
start = 0
. By the way, originally (before renaming fromm n
tostart stop
) you had+2
and now you have*2
, is that intentional?The
+2
was a mistake
The *2
looks like even more of a mistake. start
will go over powers of 2, which doesn't seem to have anything to do with your goal
Joseph O (Feb 22 2022 at 20:47):
Now I solved this without recursion or an auxiliary function
def List.rangeStep (start stop step : Nat) :=
(List.drop start $ List.range stop).filter (fun i => i % step ≠ 0)
Joseph O (Feb 22 2022 at 20:48):
Thanks so much for your algorithm idea
Mario Carneiro (Feb 22 2022 at 20:48):
I think there is a variant on range
that lets you control the starting point
Joseph O (Feb 22 2022 at 20:48):
Is there?
Mario Carneiro (Feb 22 2022 at 20:49):
/-- `range' s n` is the list of numbers `[s, s+1, ..., s+n-1]`.
It is intended mainly for proving properties of `range` and `iota`. -/
@[simp]
def range' : ℕ → ℕ → List ℕ
| s, 0 => []
| s, n+1 => s :: range' (s+1) n
Mario Carneiro (Feb 22 2022 at 20:50):
so it is start
and len
Mario Carneiro (Feb 22 2022 at 20:50):
where len
is stop - start
Joseph O (Feb 22 2022 at 20:50):
Ah thank you
Joseph O (Feb 22 2022 at 20:50):
I didn't know
Mario Carneiro (Feb 22 2022 at 20:50):
that's in mathlib(4)
Joseph O (Feb 22 2022 at 20:53):
Ah, im afraid I cant use it then
Reid Barton (Feb 22 2022 at 20:57):
Well, in another sense, it's on Zulip, in Mario's message.
Joseph O (Feb 22 2022 at 21:07):
Well yes, but I would have to either inline it into my functions or make it private which in lean 4 i dont think is possible
Mario Carneiro (Feb 22 2022 at 21:09):
or just copy it into your project
Mario Carneiro (Feb 22 2022 at 21:09):
you can't inline it anyway, it's a recursive function
Joseph O (Feb 22 2022 at 21:10):
Mario Carneiro said:
you can't inline it anyway, it's a recursive function
I meant localize it with let rec
Joseph O (Feb 22 2022 at 21:10):
Mario Carneiro said:
or just copy it into your project
under the given circumstances, I cant do that either
Mario Carneiro (Feb 22 2022 at 21:10):
I don't really understand your decision procedures
Mario Carneiro (Feb 22 2022 at 21:10):
what are the circumstances?
Joseph O (Feb 22 2022 at 21:23):
I’m contributing somewhere
Henrik Böving (Feb 22 2022 at 21:25):
Besides you can make it private Lean 4 does have a private keyword.
Joseph O (Feb 22 2022 at 21:29):
Mario told me it is not exactly private
Joseph O (Feb 22 2022 at 21:29):
and can be accessed
Henrik Böving (Feb 22 2022 at 21:49):
And why is it an issue if some helper range function can be accessed?
Joseph O (Feb 22 2022 at 23:36):
I don’t know if the maintainers would like that, since they should be declared under the List namespace
Joseph O (Feb 22 2022 at 23:37):
I could add them there, but as said, I don’t think the Lean4 maintainers want new list methods in their std
Kevin Buzzard (Feb 22 2022 at 23:38):
My understanding from the Lean 4 FAQ is that right now unrequested pull requests are not welcome.
Kevin Buzzard (Feb 22 2022 at 23:39):
OTOH the more experimental #mathlib4 repo is much more open to PRs.
Joseph O (Feb 23 2022 at 00:02):
Kevin Buzzard said:
My understanding from the Lean 4 FAQ is that right now unrequested pull requests are not welcome.
Oh.
Joseph O (Feb 23 2022 at 00:31):
I guess I will make an issue to see if they would want it.
Kevin Buzzard (Feb 23 2022 at 00:36):
My advice would be to leave them alone for now.
Kevin Buzzard (Feb 23 2022 at 00:37):
They haven't even made an official release yet and have other things to worry about.
Joseph O (Feb 23 2022 at 00:56):
Ok. Yet I wasn't bothering them in the first place. Either way, I will keep this change until they accept pull requests
Notification Bot (Feb 23 2022 at 01:02):
Joseph O has marked this topic as resolved.
Notification Bot (Feb 23 2022 at 01:15):
Joseph O has marked this topic as unresolved.
Joseph O (Feb 23 2022 at 01:17):
They did say I could share my idea on Zulip
here it is:
add a toList
method for the Range
structure found in Init.Data.Range
so that you can use the list methods to apply transformations on it.
As well as a toString
and Repr
instance, but that isnt important
Last updated: Dec 20 2023 at 11:08 UTC