[ add ] inequalities for 2 ^ log₂ n#2925
Conversation
This adds the inequalities `2 ^ ⌊log₂ n ⌋ ≤ n` for non-zero `n` and `n ≤ 2 ^ ⌈log₂ n ⌉` for all natural numbers `n`. Similar to the existing properties of the logarithm, the main proofs that keep track of the accumulators are added to `Data.Nat.Logarithm.Core`. Both use a lemma that `2 * (ℕ.suc n) ≡ 2 + (n + n)` which is added to `Data.Nat.Logarithm.Core` as well. The respective simplified versions on `⌊log₂_⌋` and `⌈log₂_⌉` are added directly to `Data.Nat.Logarithm`.
src/Data/Nat/Logarithm/Core.agda
Outdated
| suc n ∎ | ||
| where open ≡-Reasoning | ||
|
|
||
| 2*suc-n≡2+n+n : ∀ n → 2 * (ℕ.suc n) ≡ 2 + (n + n) |
There was a problem hiding this comment.
The convention is to write 2*suc[n]≡2+n+n for this type of lemma.
It should probably be moved to Data.Nat.Properties.
There was a problem hiding this comment.
Thanks! I've renamed the lemma to 2*suc[n]≡2+n+n and have moved it to Data.Nat.Properties. Is the location in there OK? I put it right under *-suc
jamesmckinna
left a comment
There was a problem hiding this comment.
This looks great, but I think could be 'better' streamlined.
And... you'll need a CHANGELOG entry for the additional lemmas (once their statements have stabilised...).
src/Data/Nat/Logarithm/Core.agda
Outdated
| 2^⌊log2n⌋≤n : ∀ n → (acc : Acc _<_ (1 + n)) → 2 ^ (⌊log2⌋ (1 + n) acc) ≤ 1 + n | ||
| 2^⌊log2n⌋≤n ℕ.zero _ = s≤s z≤n | ||
| 2^⌊log2n⌋≤n (ℕ.suc n) (acc rs) = | ||
| begin |
There was a problem hiding this comment.
This is very nice, but I think that it would be 'better' (for a suitable definition of 'better' ;-) certainly 'more in line with the std-lib library style'...) to use the NonZero predicate as an (irrelevant) instance as part of the type, rather than relying on the knowledge that suc n/1 + n is automatically NonZero...
Accordingly:
| 2^⌊log2n⌋≤n : ∀ n → (acc : Acc _<_ (1 + n)) → 2 ^ (⌊log2⌋ (1 + n) acc) ≤ 1 + n | |
| 2^⌊log2n⌋≤n ℕ.zero _ = s≤s z≤n | |
| 2^⌊log2n⌋≤n (ℕ.suc n) (acc rs) = | |
| begin | |
| 2^⌊log2n⌋≤n : ∀ n .{{_ : NonZero n}} → (acc : Acc _<_ n) → 2 ^ (⌊log2⌋ n acc) ≤ n | |
| 2^⌊log2n⌋≤n (ℕ.suc n) (acc rs) = | |
| begin |
I haven't checked that the rest of the proof goes through unchanged, but... it shouldn't require much more work to fix things?
There was a problem hiding this comment.
My proof needs the case
2^⌊log2n⌋≤n ℕ.zero _ = s≤s z≤n
at some point because the proof is by induction on n and I can't image how the induction should work without the base case. I tried it but I don't see how to update the proof such that it still compiles for your suggested type
2^⌊log2n⌋≤n : ∀ n .{{_ : NonZero n}} → (acc : Acc _<_ n) → 2 ^ (⌊log2⌋ n acc) ≤ n
There was a problem hiding this comment.
Yes, you're quite right. See below!
There was a problem hiding this comment.
2^⌊log2n⌋≤n : ∀ n .{{_ : NonZero n}} → (acc : Acc _<_ n) → 2 ^ (⌊log2⌋ n acc) ≤ n
2^⌊log2n⌋≤n (suc zero) _ = ≤-refl
2^⌊log2n⌋≤n (suc (suc n)) (acc rs) = begin
2 ^ (⌊log2⌋ (suc (suc n)) (acc rs)) ≡⟨⟩
2 * 2 ^ (⌊log2⌋ (suc ⌊ n /2⌋) _) ≤⟨ *-monoʳ-≤ 2 (2^⌊log2n⌋≤n _ _) ⟩
2 * (suc ⌊ n /2⌋) ≡⟨ 2*suc[n]≡2+n+n _ ⟩
2 + (⌊ n /2⌋ + ⌊ n /2⌋) ≤⟨ +-monoʳ-≤ (2 + ⌊ n /2⌋) (⌊n/2⌋≤⌈n/2⌉ _) ⟩
2 + (⌊ n /2⌋ + ⌈ n /2⌉) ≡⟨ cong (2 +_) (⌊n/2⌋+⌈n/2⌉≡n _) ⟩
2 + n
∎
where open ≤-ReasoningNB. style-guide mandates that begin should be on the same line as the LHS of the definition.
There was a problem hiding this comment.
Thanks! I've updated it (also in the Changelog). However, I find the definition in Logarithm.agda then more difficult to understand, because it is not visible that the property of n being NonZero is passed to the call:
2^⌊log₂n⌋≤n : ∀ n → .{{ _ : NonZero n }} → 2 ^ ⌊log₂ n ⌋ ≤ n
2^⌊log₂n⌋≤n n = 2^⌊log2n⌋≤n n (<-wellFounded n)
But it's fine for me. :-)
There was a problem hiding this comment.
Ah!
There are two ways to think about this problem (there may be others... I offer only two here!?), of which the first is what I take to be your 'difficulty' above:
instancearguments are mysterious, because we neither see them explicitly bound on the LHS, nor see them explicitly passed as arguments on the RHS;instancearguments are fantastically useful, especially as here, when used as irrelevant arguments, because we neither see them explicitly bound on the LHS, nor see them explicitly passed as arguments on the RHS.
Spelt out, the definition above amounts to (and I believe this is what the type checker is more or less doing):
2^⌊log₂n⌋≤n n {{nz}} = 2^⌊log2n⌋≤n n (<-wellFounded n) {{something}}
where
something = <a proof of NonZero n>in such a way that:
somethingcan be uniquely computed from the 'visible' arguments, and any other declaredinstances, in scope (this is the requirement for instance inference to succeed)- argument
nzcannot actually be used in a relevant way, but that's OK, because here, the function2^⌊log2n⌋≤nalso expects its instance argument to be used in an irrelevant way - so... taking
something = nzseems the only available choice, and moreover it satisfies the conditions above.
Ie.
2^⌊log₂n⌋≤n n {{nz}} = 2^⌊log2n⌋≤n n (<-wellFounded n) {{nz}}or even, to emphasise that we are not allowed even to consider using nz in a relevant way
2^⌊log₂n⌋≤n n {{_}} = 2^⌊log2n⌋≤n n (<-wellFounded n) {{_}}where the use of _ on the LHS means "I don't care", while on the RHS means "you figure it out", in the dialogue game between user ("I") and typechecker ("you").
Once we arrive at the version with underscores, well, then they really are just noise, so...
src/Data/Nat/Logarithm.agda
Outdated
| ⌊log₂[2^n]⌋≡n n = ⌊log2⌋2^n≡n n | ||
|
|
||
| 2^⌊log₂n⌋≤n : ∀ n → .{{ _ : NonZero n }} → 2 ^ ⌊log₂ n ⌋ ≤ n | ||
| 2^⌊log₂n⌋≤n (ℕ.suc n) = 2^⌊log2n⌋≤n n (<-wellFounded (ℕ.suc n)) |
There was a problem hiding this comment.
If you implement the above changes, then this could/should now simply be:
| 2^⌊log₂n⌋≤n (ℕ.suc n) = 2^⌊log2n⌋≤n n (<-wellFounded (ℕ.suc n)) | |
| 2^⌊log₂n⌋≤n n = 2^⌊log2n⌋≤n n (<-wellFounded n) |
|
I notice as well that your changes are on your This is (more or less) fine for a first PR, but generally not recommended as a future-proof strategy! |
|
Thanks for the pointer to |
src/Data/Nat/Logarithm/Core.agda
Outdated
| 2^⌊log2n⌋≤n : ∀ n → (acc : Acc _<_ (1 + n)) → 2 ^ (⌊log2⌋ (1 + n) acc) ≤ 1 + n | ||
| 2^⌊log2n⌋≤n ℕ.zero _ = s≤s z≤n | ||
| 2^⌊log2n⌋≤n (ℕ.suc n) (acc rs) = | ||
| begin |
There was a problem hiding this comment.
Yes, you're quite right. See below!
CHANGELOG.md
Outdated
|
|
||
| * In `Data.Nat.Logarithm.Core` | ||
| ```agda | ||
| 2^⌊log2n⌋≤n : ∀ n → (acc : Acc _<_ (1 + n)) → 2 ^ (⌊log2⌋ (1 + n) acc) ≤ 1 + n |
There was a problem hiding this comment.
Again...
| 2^⌊log2n⌋≤n : ∀ n → (acc : Acc _<_ (1 + n)) → 2 ^ (⌊log2⌋ (1 + n) acc) ≤ 1 + n | |
| 2^⌊log2n⌋≤n : ∀ n .{{_ : NonZero n}} → (acc : Acc _<_ n) → 2 ^ (⌊log2⌋ n acc) ≤ n |
There was a problem hiding this comment.
I fixed your proof.
Oh, for some reason, the suggestion didn't get recorded?
2^⌊log2n⌋≤n : ∀ n .{{_ : NonZero n}} → (acc : Acc _<_ n) → 2 ^ (⌊log2⌋ n acc) ≤ n
2^⌊log2n⌋≤n (suc zero) _ = ≤-refl
2^⌊log2n⌋≤n (suc (suc n)) (acc rs) = begin
2 ^ (⌊log2⌋ (suc (suc n)) (acc rs)) ≡⟨⟩
2 * 2 ^ (⌊log2⌋ (suc ⌊ n /2⌋) _) ≤⟨ *-monoʳ-≤ 2 (2^⌊log2n⌋≤n _ _) ⟩
2 * (suc ⌊ n /2⌋) ≡⟨ {!2*suc[n]≡2+n+n _!} ⟩
2 + (⌊ n /2⌋ + ⌊ n /2⌋) ≤⟨ +-monoʳ-≤ (2 + ⌊ n /2⌋) (⌊n/2⌋≤⌈n/2⌉ _) ⟩
2 + (⌊ n /2⌋ + ⌈ n /2⌉) ≡⟨ cong (2 +_) (⌊n/2⌋+⌈n/2⌉≡n _) ⟩
2 + n
∎
where open ≤-ReasoningNB style-guide mandates that begin be on the same line as the LHS of the definition...
Co-authored-by: jamesmckinna <31931406+jamesmckinna@users.noreply.github.com>
Co-authored-by: jamesmckinna <31931406+jamesmckinna@users.noreply.github.com>
|
Thank you for accepting! I also commited your suggestions regarding the removal of unnecessary →. |
This adds the inequalities
2 ^ ⌊log₂ n ⌋ ≤ nfor non-zeronandn ≤ 2 ^ ⌈log₂ n ⌉for all natural numbersn. Similar to the existing properties of the logarithm, the main proofs that keep track of the accumulators are added toData.Nat.Logarithm.Core. Both use a lemma that2 * (suc n) ≡ 2 + (n + n)which is added toData.Nat.Logarithm.Coreas well.The respective simplified versions on
⌊log₂_⌋and⌈log₂_⌉are added directly toData.Nat.Logarithm.