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 mth 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 was n and stop was m and step 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 to start 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 from m n to start 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

https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/better.20way.20to.20write.20.60rangeStepAux.60/near/272821546

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 from m n to start 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