-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
polys: make ANP use DMP internally #25764
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
Conversation
✅ Hi, I am the SymPy bot. I'm here to help you write a release notes entry. Please read the guide on how to write release notes. Your release notes are in good order. Here is what the release notes will look like:
This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.13. Click here to see the pull request description that was parsed.
Update The release notes on the wiki have been updated. |
sympy/polys/polyclasses.py
Outdated
@property | ||
def rep(self): | ||
raise RuntimeError("ANP.rep") | ||
return self._rep.to_list() | ||
|
||
@property | ||
def mod(self): | ||
raise RuntimeError("ANP.mod") | ||
return self._mod.to_list() |
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.
This always raise errors. Are you leaving this because this is part of some incomplete work?
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.
It is incomplete. I haven't decided whether or not to deprecate these. The rep
attribute can be replaced with .to_list()
just like for DMP
. The mod argument does not have an existing replacement. It is perhaps also not too important to deprecate them but I do want to make sure that they aren't use within the sympy codebase itself.
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.
I think that although keeping the rep, dom
as list is backward compatible, I don't think that it is more useful than using instance of DMP
directly.
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.
I guess at least there should be new public methods/attributes to get the rep and mod as DMP.
Benchmark results from GitHub Actions Lower numbers are good, higher numbers are bad. A ratio less than 1 Significantly changed benchmark results (PR vs master) Significantly changed benchmark results (master vs previous release) | Change | Before [8059df73] <sympy-1.12^0> | After [3bb2ebda] | Ratio | Benchmark (Parameter) |
|----------|------------------------------------|---------------------|---------|----------------------------------------------------------------------|
| - | 86.4±2ms | 56.7±0.1ms | 0.66 | integrate.TimeIntegrationRisch02.time_doit(10) |
| - | 86.0±2ms | 55.9±0.4ms | 0.65 | integrate.TimeIntegrationRisch02.time_doit_risch(10) |
| + | 22.2±0.07μs | 35.0±0.05μs | 1.58 | integrate.TimeIntegrationRisch03.time_doit(1) |
| - | 6.97±0.02ms | 3.77±0.01ms | 0.54 | logic.LogicSuite.time_load_file |
| - | 2.12±0ms | 656±2μs | 0.31 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(3, 'sparse') |
| - | 10.4±0.01ms | 1.96±0ms | 0.19 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(5, 'sparse') |
| - | 368±0.5μs | 82.2±0.2μs | 0.22 | polys.TimePREM_QuadraticNonMonicGCD.time_op(1, 'sparse') |
| - | 4.91±0.01ms | 365±1μs | 0.07 | polys.TimePREM_QuadraticNonMonicGCD.time_op(3, 'sparse') |
| - | 10.7±0.04ms | 1.10±0ms | 0.1 | polys.TimePREM_QuadraticNonMonicGCD.time_op(5, 'sparse') |
| - | 6.27±0.01ms | 3.96±0.01ms | 0.63 | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(2, 'sparse') |
| - | 27.4±0.04ms | 12.1±0.04ms | 0.44 | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(3, 'sparse') |
| - | 6.94±0.03ms | 1.17±0.01ms | 0.17 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(1, 'sparse') |
| - | 16.3±0.03ms | 9.23±0.01ms | 0.57 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(2, 'sparse') |
| - | 214±0.3ms | 70.7±0.1ms | 0.33 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(3, 'sparse') |
| - | 6.44±0.02ms | 535±1μs | 0.08 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(3, 'sparse') |
| - | 28.2±0.06ms | 856±2μs | 0.03 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(5, 'sparse') |
| - | 614±3μs | 199±0.6μs | 0.32 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(1, 'sparse') |
| - | 6.58±0.01ms | 202±1μs | 0.03 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(3, 'sparse') |
| - | 17.3±0.01ms | 209±5μs | 0.01 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(5, 'sparse') |
| - | 164±0.7μs | 89.0±0.1μs | 0.54 | solve.TimeMatrixOperations.time_rref(3, 0) |
| - | 316±0.3μs | 108±0.2μs | 0.34 | solve.TimeMatrixOperations.time_rref(4, 0) |
| - | 32.2±0.2ms | 13.5±0.01ms | 0.42 | solve.TimeSolveLinSys189x49.time_solve_lin_sys |
Full benchmark results can be found as artifacts in GitHub Actions |
Current state: $ cat t.py
from sympy import *
N = 20
M = randMatrix(N) + randMatrix(N)*sqrt(2) + randMatrix(N)*sqrt(3) + randMatrix(N)*sqrt(5)
dM = M.to_DM(extension=True)
dM.inv()
$ time python t.py
real 0m38.798s
user 0m38.550s
sys 0m0.214s
$ time SYMPY_GROUND_TYPES=flint python t.py
real 0m2.179s
user 0m2.013s
sys 0m0.170s More can be done to improve that but maybe for now this is good enough in terms of basic speed. Profiles suggest that most of the time is spent in python-flint rather than in overheads in the Python code so big speed improvements would need to be from a change of algorithm rather than micro-optimisation (at least for the things I have tested). |
I've optimised some of the ANP methods a bit more. This is without flint: In [2]: cat t.py
from sympy import *
from time import time
def rand(N):
M = (
randMatrix(N)
+ randMatrix(N)*sqrt(2)
+ randMatrix(N)*sqrt(3)
+ randMatrix(N)*sqrt(5)
+ randMatrix(N)*sqrt(7)
+ randMatrix(N)*sqrt(11)
)
return M.to_DM(extension=True)
In [3]: run t.py
In [4]: for n in range(1, 5):
...: dM = rand(n)
...: start = time()
...: dM.inv()
...: time_taken = time() - start
...: print(f'{n}x{n} matrix: {time_taken:.3g} seconds')
...:
1x1 matrix: 0.193 seconds
2x2 matrix: 2.39 seconds
3x3 matrix: 6.87 seconds
4x4 matrix: 18.1 seconds This is with flint: In [3]: for n in range(1, 10):
...: dM = rand(n)
...: start = time()
...: dM.inv()
...: time_taken = time() - start
...: print(f'{n}x{n} matrix: {time_taken:.3g} seconds')
...:
1x1 matrix: 0.00954 seconds
2x2 matrix: 0.0395 seconds
3x3 matrix: 0.106 seconds
4x4 matrix: 0.218 seconds
5x5 matrix: 0.406 seconds
6x6 matrix: 0.679 seconds
7x7 matrix: 1.11 seconds
8x8 matrix: 1.61 seconds
9x9 matrix: 2.27 seconds This is about 100x faster for a 4x4 matrix but the difference increases for larger sizes. |
I noticed while timing this that primitive_element still internally uses |
I think that the main thing missing here is just to add public methods for getting |
This is now ready for review |
Looks good. |
Thanks! |
References to other Issues or PRs
Followup from gh-25722
Brief description of what is fixed or changed
Make the ANP class that is used for algebraic numberfields use DMP internally. This means that ANP can automatically benefit from any speedups in DMP that arise from using e.g. Flint rather than the existing
dmp_*
functions.Other comments
The same should also be done for
DMF
.Release Notes
(I will add a release note later)
QQ<sqrt(2)>
can benefit from the speed improvements of python-flint when the ground types are set to flint.