-
Notifications
You must be signed in to change notification settings - Fork 373
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - perf: make Decidable Injective
approximately twice as fast
#21975
Conversation
PR summary 1f00aeb576Import changes for modified filesNo significant changes to the import graph Import changes for all files
Declarations diff
You can run this locally as follows## summary with just the declaration names:
./scripts/declarations_diff.sh <optional_commit>
## more verbose report:
./scripts/declarations_diff.sh long <optional_commit> The doc-module for No changes to technical debt.You can run this locally as
|
What's the standard way to benchmark a theorem? I did a quick test with the snippet below, and did confirm that the old code is ~ 2x slower, but it would be nice to be more precise about this. def testFn (i : Fin 100) : Fin 100 := i + 42
set_option maxRecDepth 100000 in
example : Function.Injective testFn := by decide |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments: the implementation looks straightforward; I haven't really thought if there are better options.
Fixed conflicts from #21831. |
Decidable Injective
approximately twice as fastDecidable Injective
approximately twice as fast
Do we want better benchmarking to confirm this? Otherwise, this looks good to me. Thanks for PRing it! |
🚀 Pull request has been placed on the maintainer queue by grunweg. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this seems straightforward and the benefit is self-evident, I'm hesitant to merge this myself because I don't know if it's possible to create diamonds here. Of course, it's a Subsingleton
, so it shouldn't be too much of an issue, but I want to be sure. For that reason:
maintainer merge
🚀 Pull request has been placed on the maintainer queue by j-loreaux. |
It's a pity we can't have a more efficient implementation if the codomain is a |
The existing implementation takes the definition (`∀ x y, f x = f y → x = y`) directly. This is suboptimal because: 1. Every pair of values is checked twice (both `(x, y)` and `(y, x)`) 2. The diagonals `(x, x)` are checked redundantly 3. The outputs of `f` are not cached, which might be a problem if `f` is expensive This PR proposes a new implementation of `Decidable Injective` and `Decidable InjOn`. It collects the outputs in a `Multiset`, then decides `Multiset.Nodup`. Because `Nodup` is built upon `List.Pairwise`, which only checks unordered non-diagonal pairs, the above problems are addressed automatically. This also allows for dropping `DecidableEq` on the input type, as that was to work around the diagonal case. Thank you @edegeltje and @plp127 for suggesting `List.Pairwise`, and many other people in the [Zulip thread] for the discussion around `Sym2` (which ended up being unused here, but was still valuable). [Zulip thread]: https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Idea.3A.20Helper.20for.20speeding.20up.20Decidable.20on.20symmetric.20props
Pull request successfully merged into master. Build succeeded: |
Decidable Injective
approximately twice as fastDecidable Injective
approximately twice as fast
With this code and this commit set_option profiler true in
set_option profiler.threshold 10 in
theorem pieces_injective_of_ne_O {p : Piece} (hp : p ≠ .O) : Function.Injective (pieces p) := by
cases p
case O => contradiction
all_goals decide Before:
After:
So I'm very happy with this change 😄 |
The existing implementation takes the definition (
∀ x y, f x = f y → x = y
) directly. This is suboptimal because:(x, y)
and(y, x)
)(x, x)
are checked redundantlyf
are not cached, which might be a problem iff
is expensiveThis PR proposes a new implementation of
Decidable Injective
andDecidable InjOn
. It collects the outputs in aMultiset
, then decidesMultiset.Nodup
. BecauseNodup
is built uponList.Pairwise
, which only checks unordered non-diagonal pairs, the above problems are addressed automatically.This also allows for dropping
DecidableEq
on the input type, as that was to work around the diagonal case.Thank you @edegeltje and @plp127 for suggesting
List.Pairwise
, and many other people in the Zulip thread for the discussion aroundSym2
(which ended up being unused here, but was still valuable).