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

Optimize RootOf's caching #741

Merged
merged 3 commits into from Dec 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -2,6 +2,7 @@ language: python
dist: xenial
cache: pip
python:
- 3.6
- 3.7
- pypy3.5
env:
Expand Down
36 changes: 24 additions & 12 deletions diofant/polys/rootoftools.py
Expand Up @@ -170,8 +170,8 @@ def _eval_expand_func(self, **hints):

def _eval_is_real(self):
try:
return self.index < len(_reals_cache[self.poly.rep])
except KeyError:
return int(self.index) < int(self.poly.count_roots())
except DomainError:
pass
_eval_is_extended_real = _eval_is_real

Expand Down Expand Up @@ -214,7 +214,7 @@ def _eval_conjugate(self):
if self.is_real:
return self
elif self.poly.domain.is_IntegerRing:
nreals = len(_reals_cache[self.poly.rep])
nreals = self.poly.count_roots()
ci = self.index + 2*((self.index - nreals + 1) % 2) - 1
return self._new(self.poly, ci)

Expand All @@ -236,14 +236,20 @@ def all_roots(cls, poly, radicals=True):
def _get_reals_sqf(cls, factor):
"""Compute real root isolating intervals for a square-free polynomial. """
if factor.rep not in _reals_cache:
_reals_cache[factor.rep] = dup_isolate_real_roots_sqf(factor.rep.rep, factor.rep.domain, blackbox=True)
reals = dup_isolate_real_roots_sqf(factor.rep.rep, factor.rep.domain, blackbox=True)
if not reals:
_reals_cache[factor.rep] = []
return reals
return _reals_cache[factor.rep]

@classmethod
def _get_complexes_sqf(cls, factor):
"""Compute complex root isolating intervals for a square-free polynomial. """
if factor.rep not in _complexes_cache:
_complexes_cache[factor.rep] = dup_isolate_complex_roots_sqf(factor.rep.rep, factor.rep.domain, blackbox=True)
complexes = dup_isolate_complex_roots_sqf(factor.rep.rep, factor.rep.domain, blackbox=True)
if not complexes:
_complexes_cache[factor.rep] = []
return complexes
return _complexes_cache[factor.rep]

@classmethod
Expand Down Expand Up @@ -271,6 +277,10 @@ def _get_complexes(cls, factors):
@classmethod
def _reals_sorted(cls, reals):
"""Make real isolating intervals disjoint and sort roots. """
factors = list({f for _, f, _ in reals})
if len(factors) == 1 and factors[0].rep in _reals_cache:
return reals

cache = {}

for i, (u, f, k) in enumerate(reals):
Expand All @@ -297,8 +307,10 @@ def _reals_sorted(cls, reals):
@classmethod
def _complexes_sorted(cls, complexes):
"""Make complex isolating intervals disjoint and sort roots. """
if not complexes:
return []
factors = list({f for _, f, _ in complexes})
if len(factors) == 1 and factors[0].rep in _complexes_cache:
return complexes

cache = {}

for i, (u, f, k) in enumerate(complexes):
Expand Down Expand Up @@ -355,7 +367,7 @@ def _complexes_index(cls, complexes, index):
if factor == poly:
index += 1

index += len(_reals_cache[poly.rep])
index += poly.count_roots()

return poly, index
else:
Expand Down Expand Up @@ -390,10 +402,10 @@ def _indexed_root(cls, poly, index):
_, factors = poly.factor_list()

reals = cls._get_reals(factors)
reals = cls._reals_sorted(reals)
reals_count = cls._count_roots(reals)

if index < reals_count:
reals = cls._reals_sorted(reals)
return cls._reals_index(reals, index)
else:
complexes = cls._get_complexes(factors)
Expand Down Expand Up @@ -513,7 +525,7 @@ def interval(self):
if self.is_real:
return _reals_cache[self.poly.rep][self.index]
else:
reals_count = len(_reals_cache[self.poly.rep])
reals_count = self.poly.count_roots()
return _complexes_cache[self.poly.rep][self.index - reals_count]

def refine(self):
Expand All @@ -522,7 +534,7 @@ def refine(self):
root = _reals_cache[self.poly.rep][self.index]
_reals_cache[self.poly.rep][self.index] = root.refine()
else:
reals_count = len(_reals_cache[self.poly.rep])
reals_count = self.poly.count_roots()
root = _complexes_cache[self.poly.rep][self.index - reals_count]
_complexes_cache[self.poly.rep][self.index - reals_count] = root.refine()

Expand All @@ -545,7 +557,7 @@ def _eval_evalf(self, prec):

try:
interval = self.interval
except KeyError:
except DomainError:
return super()._eval_evalf(prec)

while True:
Expand Down
9 changes: 9 additions & 0 deletions diofant/polys/tests/test_rootoftools.py
Expand Up @@ -581,3 +581,12 @@ def test_diofantissue_730():
assert e.is_imaginary is False
assert e.n(3) == Float('0.00498962', dps=3) + I*Float('0.31604', dps=3)
assert e.conjugate().conjugate() == e


@pytest.mark.timeout(120)
@pytest.mark.slow
def test_diofantissue_723():
p = x**5 + sqrt(3)*x - 2
for i in range(20):
for j in (1, 2):
RootOf(p, j)
2 changes: 1 addition & 1 deletion docs/release/notes-0.10.rst
Expand Up @@ -14,7 +14,7 @@ New features
Major changes
=============

* Stable enumeration of polynomial roots in :class:`~diofant.polys.rootoftools.RootOf`, see :pull:`633` and :pull:`658`.
* Stable enumeration of polynomial roots in :class:`~diofant.polys.rootoftools.RootOf`, see :pull:`633`, :pull:`658` and :pull:`741`.
* Support root isolation for polynomials with algebraic coefficients, see :pull:`673` and :pull:`630`.
* Polynomials with algebraic coefficients will use algebraic number domains per default, see :pull:`478`.

Expand Down