Skip to content

Commit 5e31da5

Browse files
Merge pull request #28416 from Jatinbhardwaj-093/update/from_list
[RingSeries]: Support higher-precision series elements beyond ring precision
2 parents d6e34fd + 79b052f commit 5e31da5

File tree

5 files changed

+84
-12
lines changed

5 files changed

+84
-12
lines changed

sympy/polys/series/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ def from_list(self, coeffs: list[Er], prec: int | None = None, /) -> TSeries:
8989
"""Create a power series from a list of coefficients."""
9090
...
9191

92+
def from_element(self, s: TSeries, /) -> TSeries:
93+
"""Convert a power series element into the corresponding element of this ring."""
94+
...
95+
9296
def to_list(self, s: TSeries, /) -> list[Er]:
9397
"""Return the coefficients of a power series as a list."""
9498
...

sympy/polys/series/ring.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ def from_element(self, element: TSeriesElement[Er]) -> PowerSeriesElement[Er]:
241241
"""Convert a lower power series element to a PowerSeriesElement."""
242242
return PowerSeriesElement(self, element)
243243

244+
def from_powerserieselement(
245+
self, element: PowerSeriesElement[Er]
246+
) -> PowerSeriesElement[Er]:
247+
"""Convert a power series element into the corresponding element of this ring."""
248+
R = self.ring
249+
s = R.from_element(element.series)
250+
return self.from_element(s)
251+
244252
def from_int(self, arg: int) -> PowerSeriesElement[Er]:
245253
"""Convert an integer to a power series element."""
246254
g = self.domain_new(arg)
@@ -285,9 +293,13 @@ def domain_new(self, arg: Er | int) -> Er:
285293
"""Convert arg to the element of ground domain of ring."""
286294
return self.domain.convert(arg, self.domain)
287295

288-
def ring_new(self, arg: Expr | Er | int) -> PowerSeriesElement[Er]:
296+
def ring_new(
297+
self, arg: PowerSeriesElement[Er] | Expr | Er | int
298+
) -> PowerSeriesElement[Er]:
289299
"""Create a power series element from various types."""
290-
if isinstance(arg, Expr):
300+
if isinstance(arg, PowerSeriesElement):
301+
return self.from_powerserieselement(arg)
302+
elif isinstance(arg, Expr):
291303
return self.from_expr(arg)
292304
elif isinstance(arg, int):
293305
return self.from_int(arg)
@@ -1041,6 +1053,11 @@ def removeO(self) -> PowerSeriesElement[Er]:
10411053
series = R.from_list(coeffs)
10421054
return self._new(series)
10431055

1056+
@property
1057+
def prec(self) -> int | None:
1058+
"""Return the precision of the series."""
1059+
return self.ring.series_prec(self.series)
1060+
10441061
@property
10451062
def is_ground(self) -> bool | None:
10461063
"""Check if the series is a ground element."""

sympy/polys/series/ringflint.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,23 @@
1111
from sympy.utilities.decorator import doctest_depends_on
1212
from sympy.polys.polyerrors import NotReversible
1313

14+
_require_flint_version = False
1415

1516
if TYPE_CHECKING:
1617
from flint import fmpq_poly, fmpq_series, fmpz_poly, fmpz_series, ctx # type: ignore
1718
from flint.utils.flint_exceptions import DomainError # type: ignore
1819
elif GROUND_TYPES == "flint":
1920
from flint import fmpq_poly, fmpq_series, fmpz_poly, fmpz_series, ctx
2021
from flint.utils.flint_exceptions import DomainError
22+
23+
# Required in specific cases where the 0.8 implementation provides faster
24+
# performance for working with Flint series methods.
25+
# This can be removed when python-flint 0.8 becomes the minimum supported version.
26+
import flint
27+
28+
_major, _minor, *_ = flint.__version__.split(".")
29+
if (int(_major), int(_minor)) >= (0, 8):
30+
_require_flint_version = True
2131
else:
2232
fmpq_poly = fmpq_series = fmpz_poly = fmpz_series = ctx = None
2333

@@ -205,11 +215,23 @@ def from_list(self, coeffs: list[MPZ], prec: int | None = None) -> ZZSeries:
205215
if len(coeffs) <= self._prec:
206216
return fmpz_poly(coeffs)
207217
prec = self._prec
208-
else:
209-
prec = min(prec, self._prec)
210218

211219
return fmpz_series(coeffs, prec=prec)
212220

221+
def from_element(self, s: ZZSeries) -> ZZSeries:
222+
"""Convert a power series element into the corresponding element of this ring."""
223+
ring_prec = self._prec
224+
if isinstance(s, fmpz_poly):
225+
if s.degree() >= ring_prec:
226+
return fmpz_series(s, prec=ring_prec)
227+
return s
228+
229+
prec = min(_get_series_precision(s), ring_prec)
230+
if _require_flint_version:
231+
return fmpz_series(s, prec=prec)
232+
else:
233+
return fmpz_series(s.coeffs(), prec=prec)
234+
213235
def to_list(self, s: ZZSeries) -> list[MPZ]:
214236
"""Returns the list of series coefficients."""
215237
return s.coeffs()
@@ -652,11 +674,23 @@ def from_list(self, coeffs: list[MPQ], prec: int | None = None) -> QQSeries:
652674
if len(coeffs) <= self._prec:
653675
return fmpq_poly(coeffs)
654676
prec = self._prec
655-
else:
656-
prec = min(prec, self._prec)
657677

658678
return fmpq_series(coeffs, prec=prec)
659679

680+
def from_element(self, s: QQSeries) -> QQSeries:
681+
"""Convert a power series element into the corresponding element of this ring."""
682+
ring_prec = self._prec
683+
if isinstance(s, fmpq_poly):
684+
if s.degree() >= ring_prec:
685+
return fmpq_series(s, prec=ring_prec)
686+
return s
687+
688+
prec = min(_get_series_precision(s), ring_prec)
689+
if _require_flint_version:
690+
return fmpq_series(s, prec=prec)
691+
else:
692+
return fmpq_series(s.coeffs(), prec=prec)
693+
660694
def to_list(self, s: QQSeries) -> list[MPQ]:
661695
"""Returns the list of series coefficients."""
662696
return s.coeffs()

sympy/polys/series/ringpython.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def _useries(
5454

5555
prec = min(series_prec, ring_prec)
5656
if deg < prec:
57-
return coeffs, series_prec
57+
return coeffs, prec
5858

5959
coeffs = dup_truncate(coeffs, prec, dom)
6060
return coeffs, prec
@@ -1194,14 +1194,17 @@ def from_list(self, coeffs: list[MPZ], prec: int | None = None) -> USeries[MPZ]:
11941194
return coeffs, None
11951195
else:
11961196
prec = self._prec
1197-
else:
1198-
prec = min(prec, self._prec)
11991197

12001198
if len(coeffs) > prec:
12011199
coeffs = dup_truncate(coeffs, prec, self._domain)
12021200

12031201
return coeffs, prec
12041202

1203+
def from_element(self, s: USeries[MPZ]) -> USeries[MPZ]:
1204+
"""Convert a power series element into the corresponding element of this ring."""
1205+
coeffs, prec = s
1206+
return _useries(coeffs, prec, self._domain, self._prec)
1207+
12051208
def to_list(self, s: USeries[MPZ]) -> list[MPZ]:
12061209
"""Returns the list of series coefficients."""
12071210
coeffs, _ = s
@@ -1462,14 +1465,17 @@ def from_list(self, coeffs: list[MPQ], prec: int | None = None) -> USeries[MPQ]:
14621465
return coeffs, None
14631466
else:
14641467
prec = self._prec
1465-
else:
1466-
prec = min(prec, self._prec)
14671468

14681469
if len(coeffs) > prec:
14691470
coeffs = dup_truncate(coeffs, prec, self._domain)
14701471

14711472
return coeffs, prec
14721473

1474+
def from_element(self, s: USeries[MPQ]) -> USeries[MPQ]:
1475+
"""Convert a power series element into the corresponding element of this ring."""
1476+
coeffs, prec = s
1477+
return _useries(coeffs, prec, self._domain, self._prec)
1478+
14731479
def to_list(self, s: USeries[MPQ]) -> list[MPQ]:
14741480
"""Return the list of series coefficients."""
14751481
coeffs, _ = s

sympy/polys/series/tests/test_ring.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def test_basics(ring_int):
115115
assert R.series_prec(R([1, 2, 3])) == None
116116
assert R.series_prec(R([1, 2, 3], None)) == None
117117
assert R.series_prec(R([1, 2, 3, 4, 5, 6, 7])) == 6
118-
assert R.series_prec(R([1, 2, 3, 4, 5, 6, 7], 10)) == 6
118+
assert R.series_prec(R([1, 2, 3, 4, 5, 6, 7], 10)) == 10
119119

120120

121121
def test_positive(ring_int):
@@ -1315,10 +1315,21 @@ def test_PowerSeriesRing_from_expr():
13151315

13161316
def test_PowerSeriesRing_ring_new():
13171317
R = PowerSeriesRingRing(QQ, "x", 5)
1318+
R3 = PowerSeriesRingRing(QQ, "y", 3)
1319+
x = R.gen
1320+
y = R3.gen
13181321

13191322
assert R.ring_new(QQ(7)) == R.from_ground(QQ(7))
13201323
assert R.ring_new(3) == R.from_int(3)
13211324

1325+
assert R.ring_new(y + y**2 + y**3).ring == R.ring
1326+
assert R.ring_new(y + y**2 + y**3) == R.from_list([QQ(0), QQ(1), QQ(1)], 3)
1327+
assert (
1328+
R3.ring_new(R3.from_list([QQ(0), QQ(1), QQ(1), QQ(1)], 4))
1329+
== y + y**2 + R3.order_term()
1330+
)
1331+
assert R3.ring_new(x + x**4 + x**5) == y + R3.order_term()
1332+
13221333

13231334
def test_PowerSeriesRing_arith(groundring_int):
13241335
SeriesRing = groundring_int

0 commit comments

Comments
 (0)