Skip to content
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

Speed up polynomial mappings #4124

Merged

Conversation

HechtiDerLachs
Copy link
Collaborator

@fingolfin mentioned in his course this week that in the RingFlattenings there was lots of potential improvements available via the use of MPolyBuildCtx. I tried a little bit, but could not achieve any better performance. I'm wondering why, though, so I put it up here for discussion.

@joschmitt joschmitt marked this pull request as draft September 19, 2024 15:55
r = ngens(S)
ctx = MPolyBuildCtx(S)
for (c, e) in zip(AbstractAlgebra.coefficients(u), AbstractAlgebra.exponent_vectors(u))
ee = [0 for _ in 1:r]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ee = [0 for _ in 1:r]
ee = Vector{Int}(undef, r)

should be an epsilon faster

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One should be able to use the "same" vector ee for the whole loop as it gets copied anyway. I try to optimize this further.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An undefined vector will not work. We need the zeroes.

@fingolfin fingolfin changed the title Speedup polynomial mappings Speed up polynomial mappings Sep 20, 2024
src/Rings/MPolyMap/MPolyAnyMap.jl Outdated Show resolved Hide resolved
src/Rings/MPolyMap/MPolyAnyMap.jl Outdated Show resolved Hide resolved
src/Rings/MPolyMap/MPolyAnyMap.jl Outdated Show resolved Hide resolved
@HechtiDerLachs
Copy link
Collaborator Author

Unfortunately this still does not seem to make a difference. I was running the tests for elliptic surfaces with and without the changes made here (since @fingolfin pointed to that as a potential example). The result is

Without this PR:

500.418518 seconds (2.93 G allocations: 112.836 GiB, 12.64% gc time, 0.28% compilation time)

With this PR:

508.444454 seconds (2.90 G allocations: 111.645 GiB, 12.23% gc time, 0.16% compilation time)

The different timings might also be due to randomization of parts of the tests on the lattice theoretic side (@simonbrandhorst : Is this correct?), but the allocations do not go down significantly.

One thing I noticed is that finish does a lot of sorting (which has already been pointed out to me by @lgoettgens ). This creates some overhead. But in general, another problem I witnessed with the RingFlattenings is that we have massive is_zero checks whenever we do arithmetic with SRows. These can not be avoided. But when we do this with a quotient ring, then we always need to translate the representative to the Singular-side. If this quotient ring is put to work via flattenings, then additionally we first have to translate to the flattened ring. This kills performance for sure.

In my opinion it would therefore be desirable to have a singular_representative in a MPolyQuoRingElem and to do the arithmetic with these by default whenever they are defined. Only memorizing whether an element is reduced does not help in these cases.

Orthogonal to that, the question remains: Why does this PR not help speeding things up for mappings?

@HechtiDerLachs
Copy link
Collaborator Author

HechtiDerLachs commented Sep 25, 2024

So: In the end this is useful. Just not for all the flattenings used in the elliptic surfaces (yet). I created the following test polynomial.

Run the following code:

g = load("poly.txt")
S = parent(g)
flat = Oscar.flatten(S)
f = copy(g);
@time flat(f);

With this PR:


julia> f = copy(g);

julia> @time flat(f);
  0.335777 seconds (686.51 k allocations: 49.636 MiB)

julia> f = copy(g);

julia> @time flat(f);
  0.334014 seconds (690.14 k allocations: 48.630 MiB)

julia> f = copy(g);

julia> @time flat(f);
  0.328450 seconds (705.82 k allocations: 49.011 MiB)

julia> f = copy(g);

julia> @time flat(f);
  0.330575 seconds (711.82 k allocations: 49.188 MiB)

without this PR:

julia> f = copy(g);

julia> @time flat(f);
  4.988692 seconds (975.34 k allocations: 654.598 MiB, 27.35% gc time)

julia> f = copy(g);

julia> @time flat(f);
  5.196591 seconds (750.61 k allocations: 652.844 MiB, 27.13% gc time)

julia> f = copy(g);

julia> @time flat(f);
  5.379392 seconds (673.50 k allocations: 652.256 MiB, 26.06% gc time)

julia> f = copy(g);

julia> @time flat(f);
  5.556177 seconds (659.54 k allocations: 652.183 MiB, 25.55% gc time)

@thofma
Copy link
Collaborator

thofma commented Sep 25, 2024

Looks good so far. I think this is going in the right direction. (It reminds of the MPolyHom_vars we once had and some ideas of Dan).

The logic for the evaluation was already hard to follow before (blame on me), and it seems it won't be getting better. But it is easer to talk in persons about this.

@HechtiDerLachs
Copy link
Collaborator Author

I added a keyword argument to the internal constructor for the maps to be able to avoid the internal checks whether you have a mapping of variables to variables. These are not yet forwarded to the user-facing constructors, but should this ever become a performance issue, this possibility exists.

@HechtiDerLachs
Copy link
Collaborator Author

Looks like we're slowly getting somewhere here. I ran the elliptic_surfaces.jl test and got the following:
Before:

560.589288 seconds (3.01 G allocations: 119.270 GiB, 10.71% gc time, 21.51% compilation time)

after:

484.957044 seconds (2.48 G allocations: 92.375 GiB, 11.66% gc time, 0.02% compilation time)

ping: @fingolfin

@HechtiDerLachs HechtiDerLachs marked this pull request as ready for review September 30, 2024 13:36
@HechtiDerLachs
Copy link
Collaborator Author

HechtiDerLachs commented Sep 30, 2024

@HereAround : Might it be the case that your test in the FTheoryTools has some randomness? See this commit with the local changes from my machine.

Anyways: If the tests pass, then from my side, this is good to go.

Copy link

codecov bot commented Sep 30, 2024

Codecov Report

Attention: Patch coverage is 98.18182% with 2 lines in your changes missing coverage. Please review.

Project coverage is 84.69%. Comparing base (2fe164e) to head (1892dab).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/Rings/MPolyMap/MPolyRing.jl 98.14% 1 Missing ⚠️
src/Rings/mpolyquo-localizations.jl 97.77% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4124      +/-   ##
==========================================
+ Coverage   84.68%   84.69%   +0.01%     
==========================================
  Files         628      628              
  Lines       84471    84578     +107     
==========================================
+ Hits        71533    71636     +103     
- Misses      12938    12942       +4     
Files with missing lines Coverage Δ
...Schemes/AffineSchemeOpenSubscheme/Rings/Methods.jl 86.86% <100.00%> (+0.09%) ⬆️
...etry/Schemes/AffineSchemes/Morphisms/Attributes.jl 79.66% <ø> (ø)
...ry/Schemes/AffineSchemes/Morphisms/Constructors.jl 100.00% <ø> (ø)
...ometry/Schemes/AffineSchemes/Objects/Attributes.jl 90.47% <ø> (ø)
src/Rings/MPolyMap/MPolyAnyMap.jl 93.10% <100.00%> (+0.24%) ⬆️
src/Rings/MPolyMap/MPolyRing.jl 94.11% <98.14%> (+4.53%) ⬆️
src/Rings/mpolyquo-localizations.jl 73.52% <97.77%> (+0.60%) ⬆️

... and 1 file with indirect coverage changes

@lgoettgens
Copy link
Member

@HereAround : Might it be the case that your test in the FTheoryTools has some randomness? See this commit with the local changes from my machine.

Anyways: If the tests pass, then from my side, this is good to go.

Unfortunately these kind of doctests (printing a large vector) are dependent on the windows size of the terminal you run the doctest_fix from

@thofma
Copy link
Collaborator

thofma commented Sep 30, 2024

I added a keyword argument to the internal constructor for the maps to be able to avoid the internal checks whether you have a mapping of variables to variables. These are not yet forwarded to the user-facing constructors, but should this ever become a performance issue, this possibility exists.

Why does this exist if it is not used here?

@HechtiDerLachs
Copy link
Collaborator Author

Why does this exist if it is not used here?

I thought, it would be nice to give future developers a hint that they can safely disable this feature by using the keyword argument. If you think, it's causing more confusion than insight, then I can remove it. Also, it seems that we can reunite both internal constructors into one constructor again, now that I have allowed more rings in the codomain.

@thofma
Copy link
Collaborator

thofma commented Sep 30, 2024

I thought, it would be nice to give future developers a hint that they can safely disable this feature by using the keyword argument. If you think, it's causing more confusion than insight, then I can remove it. Also, it seems that we can reunite both internal constructors into one constructor again, now that I have allowed more rings in the codomain.

I had hoped that a no one would ever create a map by calling MPolyAnyMap directly :) I am happy to keep it. I just wanted to ask why this unused (and not tested) feature exists.

@HereAround
Copy link
Member

HereAround commented Sep 30, 2024

@HereAround : Might it be the case that your test in the FTheoryTools has some randomness? See this commit with the local changes from my machine.

Anyways: If the tests pass, then from my side, this is good to go.

The computation of generic_section of line bundles picks random coefficients. This is the element of randomness in the FTheoryTools test. Everything else is (or so I hope at least) deterministic.

# The following methods are only safe, because they are called
# exclusively in a setup where variables map to variables
# and no denominators are introduced.
_coefficients(x::MPolyRingElem) = coefficients(x)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but maybe you want the following here?

Suggested change
_coefficients(x::MPolyRingElem) = coefficients(x)
_coefficients(x::MPolyRingElem) = AbstractAlgebra.coefficients(x)

And also below with exponent_vectors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw the AA-qualifier elsewhere, too. But I thought, it was redundant. Isn't it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. It is the difference between painfully slow and fast when iterating over coefficients/exponents. There used to be an explanation in the documentation, but it was removed for reasons I don't know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #4168

@HechtiDerLachs
Copy link
Collaborator Author

Again: Ready for merge from my side.

@thofma
Copy link
Collaborator

thofma commented Oct 1, 2024

Thanks, it looks good.

@simonbrandhorst simonbrandhorst enabled auto-merge (squash) October 1, 2024 13:22
@simonbrandhorst simonbrandhorst merged commit 4875c11 into oscar-system:master Oct 1, 2024
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants