| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795 |
- """
- Puiseux rings. These are used by the ring_series module to represented
- truncated Puiseux series. Elements of a Puiseux ring are like polynomials
- except that the exponents can be negative or rational rather than just
- non-negative integers.
- """
- # Previously the ring_series module used PolyElement to represent Puiseux
- # series. This is problematic because it means that PolyElement has to support
- # negative and non-integer exponents which most polynomial representations do
- # not support. This module provides an implementation of a ring for Puiseux
- # series that can be used by ring_series without breaking the basic invariants
- # of polynomial rings.
- #
- # Ideally there would be more of a proper series type that can keep track of
- # not just the leading terms of a truncated series but also the precision
- # of the series. For now the rings here are just introduced to keep the
- # interface that ring_series was using before.
- from __future__ import annotations
- from sympy.polys.domains import QQ
- from sympy.polys.rings import PolyRing, PolyElement
- from sympy.core.add import Add
- from sympy.core.mul import Mul
- from sympy.external.gmpy import gcd, lcm
- from typing import TYPE_CHECKING
- if TYPE_CHECKING:
- from typing import Any, Unpack
- from sympy.core.expr import Expr
- from sympy.polys.domains import Domain
- from collections.abc import Iterable, Iterator
- def puiseux_ring(
- symbols: str | list[Expr], domain: Domain
- ) -> tuple[PuiseuxRing, Unpack[tuple[PuiseuxPoly, ...]]]:
- """Construct a Puiseux ring.
- This function constructs a Puiseux ring with the given symbols and domain.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x, y = puiseux_ring('x y', QQ)
- >>> R
- PuiseuxRing((x, y), QQ)
- >>> p = 5*x**QQ(1,2) + 7/y
- >>> p
- 7*y**(-1) + 5*x**(1/2)
- """
- ring = PuiseuxRing(symbols, domain)
- return (ring,) + ring.gens # type: ignore
- class PuiseuxRing:
- """Ring of Puiseux polynomials.
- A Puiseux polynomial is a truncated Puiseux series. The exponents of the
- monomials can be negative or rational numbers. This ring is used by the
- ring_series module:
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> from sympy.polys.ring_series import rs_exp, rs_nth_root
- >>> ring, x, y = puiseux_ring('x y', QQ)
- >>> f = x**2 + y**3
- >>> f
- y**3 + x**2
- >>> f.diff(x)
- 2*x
- >>> rs_exp(x, x, 5)
- 1 + x + 1/2*x**2 + 1/6*x**3 + 1/24*x**4
- Importantly the Puiseux ring can represent truncated series with negative
- and fractional exponents:
- >>> f = 1/x + 1/y**2
- >>> f
- x**(-1) + y**(-2)
- >>> f.diff(x)
- -1*x**(-2)
- >>> rs_nth_root(8*x + x**2 + x**3, 3, x, 5)
- 2*x**(1/3) + 1/12*x**(4/3) + 23/288*x**(7/3) + -139/20736*x**(10/3)
- See Also
- ========
- sympy.polys.ring_series.rs_series
- PuiseuxPoly
- """
- def __init__(self, symbols: str | list[Expr], domain: Domain):
- poly_ring = PolyRing(symbols, domain)
- domain = poly_ring.domain
- ngens = poly_ring.ngens
- self.poly_ring = poly_ring
- self.domain = domain
- self.symbols = poly_ring.symbols
- self.gens = tuple([self.from_poly(g) for g in poly_ring.gens])
- self.ngens = ngens
- self.zero = self.from_poly(poly_ring.zero)
- self.one = self.from_poly(poly_ring.one)
- self.zero_monom = poly_ring.zero_monom # type: ignore
- self.monomial_mul = poly_ring.monomial_mul # type: ignore
- def __repr__(self) -> str:
- return f"PuiseuxRing({self.symbols}, {self.domain})"
- def __eq__(self, other: Any) -> bool:
- if not isinstance(other, PuiseuxRing):
- return NotImplemented
- return self.symbols == other.symbols and self.domain == other.domain
- def from_poly(self, poly: PolyElement) -> PuiseuxPoly:
- """Create a Puiseux polynomial from a polynomial.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.rings import ring
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R1, x1 = ring('x', QQ)
- >>> R2, x2 = puiseux_ring('x', QQ)
- >>> R2.from_poly(x1**2)
- x**2
- """
- return PuiseuxPoly(poly, self)
- def from_dict(self, terms: dict[tuple[int, ...], Any]) -> PuiseuxPoly:
- """Create a Puiseux polynomial from a dictionary of terms.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x = puiseux_ring('x', QQ)
- >>> R.from_dict({(QQ(1,2),): QQ(3)})
- 3*x**(1/2)
- """
- return PuiseuxPoly.from_dict(terms, self)
- def from_int(self, n: int) -> PuiseuxPoly:
- """Create a Puiseux polynomial from an integer.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x = puiseux_ring('x', QQ)
- >>> R.from_int(3)
- 3
- """
- return self.from_poly(self.poly_ring(n))
- def domain_new(self, arg: Any) -> Any:
- """Create a new element of the domain.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x = puiseux_ring('x', QQ)
- >>> R.domain_new(3)
- 3
- >>> QQ.of_type(_)
- True
- """
- return self.poly_ring.domain_new(arg)
- def ground_new(self, arg: Any) -> PuiseuxPoly:
- """Create a new element from a ground element.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring, PuiseuxPoly
- >>> R, x = puiseux_ring('x', QQ)
- >>> R.ground_new(3)
- 3
- >>> isinstance(_, PuiseuxPoly)
- True
- """
- return self.from_poly(self.poly_ring.ground_new(arg))
- def __call__(self, arg: Any) -> PuiseuxPoly:
- """Coerce an element into the ring.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x = puiseux_ring('x', QQ)
- >>> R(3)
- 3
- >>> R({(QQ(1,2),): QQ(3)})
- 3*x**(1/2)
- """
- if isinstance(arg, dict):
- return self.from_dict(arg)
- else:
- return self.from_poly(self.poly_ring(arg))
- def index(self, x: PuiseuxPoly) -> int:
- """Return the index of a generator.
- >>> from sympy.polys.domains import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x, y = puiseux_ring('x y', QQ)
- >>> R.index(x)
- 0
- >>> R.index(y)
- 1
- """
- return self.gens.index(x)
- def _div_poly_monom(poly: PolyElement, monom: Iterable[int]) -> PolyElement:
- ring = poly.ring
- div = ring.monomial_div
- return ring.from_dict({div(m, monom): c for m, c in poly.terms()})
- def _mul_poly_monom(poly: PolyElement, monom: Iterable[int]) -> PolyElement:
- ring = poly.ring
- mul = ring.monomial_mul
- return ring.from_dict({mul(m, monom): c for m, c in poly.terms()})
- def _div_monom(monom: Iterable[int], div: Iterable[int]) -> tuple[int, ...]:
- return tuple(mi - di for mi, di in zip(monom, div))
- class PuiseuxPoly:
- """Puiseux polynomial. Represents a truncated Puiseux series.
- See the :class:`PuiseuxRing` class for more information.
- >>> from sympy import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x, y = puiseux_ring('x, y', QQ)
- >>> p = 5*x**2 + 7*y**3
- >>> p
- 7*y**3 + 5*x**2
- The internal representation of a Puiseux polynomial wraps a normal
- polynomial. To support negative powers the polynomial is considered to be
- divided by a monomial.
- >>> p2 = 1/x + 1/y**2
- >>> p2.monom # x*y**2
- (1, 2)
- >>> p2.poly
- x + y**2
- >>> (y**2 + x) / (x*y**2) == p2
- True
- To support fractional powers the polynomial is considered to be a function
- of ``x**(1/nx), y**(1/ny), ...``. The representation keeps track of a
- monomial and a list of exponent denominators so that the polynomial can be
- used to represent both negative and fractional powers.
- >>> p3 = x**QQ(1,2) + y**QQ(2,3)
- >>> p3.ns
- (2, 3)
- >>> p3.poly
- x + y**2
- See Also
- ========
- sympy.polys.puiseux.PuiseuxRing
- sympy.polys.rings.PolyElement
- """
- ring: PuiseuxRing
- poly: PolyElement
- monom: tuple[int, ...] | None
- ns: tuple[int, ...] | None
- def __new__(cls, poly: PolyElement, ring: PuiseuxRing) -> PuiseuxPoly:
- return cls._new(ring, poly, None, None)
- @classmethod
- def _new(
- cls,
- ring: PuiseuxRing,
- poly: PolyElement,
- monom: tuple[int, ...] | None,
- ns: tuple[int, ...] | None,
- ) -> PuiseuxPoly:
- poly, monom, ns = cls._normalize(poly, monom, ns)
- return cls._new_raw(ring, poly, monom, ns)
- @classmethod
- def _new_raw(
- cls,
- ring: PuiseuxRing,
- poly: PolyElement,
- monom: tuple[int, ...] | None,
- ns: tuple[int, ...] | None,
- ) -> PuiseuxPoly:
- obj = object.__new__(cls)
- obj.ring = ring
- obj.poly = poly
- obj.monom = monom
- obj.ns = ns
- return obj
- def __eq__(self, other: Any) -> bool:
- if isinstance(other, PuiseuxPoly):
- return (
- self.poly == other.poly
- and self.monom == other.monom
- and self.ns == other.ns
- )
- elif self.monom is None and self.ns is None:
- return self.poly.__eq__(other)
- else:
- return NotImplemented
- @classmethod
- def _normalize(
- cls,
- poly: PolyElement,
- monom: tuple[int, ...] | None,
- ns: tuple[int, ...] | None,
- ) -> tuple[PolyElement, tuple[int, ...] | None, tuple[int, ...] | None]:
- if monom is None and ns is None:
- return poly, None, None
- if monom is not None:
- degs = [max(d, 0) for d in poly.tail_degrees()]
- if all(di >= mi for di, mi in zip(degs, monom)):
- poly = _div_poly_monom(poly, monom)
- monom = None
- elif any(degs):
- poly = _div_poly_monom(poly, degs)
- monom = _div_monom(monom, degs)
- if ns is not None:
- factors_d, [poly_d] = poly.deflate()
- degrees = poly.degrees()
- monom_d = monom if monom is not None else [0] * len(degrees)
- ns_new = []
- monom_new = []
- inflations = []
- for fi, ni, di, mi in zip(factors_d, ns, degrees, monom_d):
- if di == 0:
- g = gcd(ni, mi)
- else:
- g = gcd(fi, ni, mi)
- ns_new.append(ni // g)
- monom_new.append(mi // g)
- inflations.append(fi // g)
- if any(infl > 1 for infl in inflations):
- poly_d = poly_d.inflate(inflations)
- poly = poly_d
- if monom is not None:
- monom = tuple(monom_new)
- if all(n == 1 for n in ns_new):
- ns = None
- else:
- ns = tuple(ns_new)
- return poly, monom, ns
- @classmethod
- def _monom_fromint(
- cls,
- monom: tuple[int, ...],
- dmonom: tuple[int, ...] | None,
- ns: tuple[int, ...] | None,
- ) -> tuple[Any, ...]:
- if dmonom is not None and ns is not None:
- return tuple(QQ(mi - di, ni) for mi, di, ni in zip(monom, dmonom, ns))
- elif dmonom is not None:
- return tuple(QQ(mi - di) for mi, di in zip(monom, dmonom))
- elif ns is not None:
- return tuple(QQ(mi, ni) for mi, ni in zip(monom, ns))
- else:
- return tuple(QQ(mi) for mi in monom)
- @classmethod
- def _monom_toint(
- cls,
- monom: tuple[Any, ...],
- dmonom: tuple[int, ...] | None,
- ns: tuple[int, ...] | None,
- ) -> tuple[int, ...]:
- if dmonom is not None and ns is not None:
- return tuple(
- int((mi * ni).numerator + di) for mi, di, ni in zip(monom, dmonom, ns)
- )
- elif dmonom is not None:
- return tuple(int(mi.numerator + di) for mi, di in zip(monom, dmonom))
- elif ns is not None:
- return tuple(int((mi * ni).numerator) for mi, ni in zip(monom, ns))
- else:
- return tuple(int(mi.numerator) for mi in monom)
- def itermonoms(self) -> Iterator[tuple[Any, ...]]:
- """Iterate over the monomials of a Puiseux polynomial.
- >>> from sympy import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x, y = puiseux_ring('x, y', QQ)
- >>> p = 5*x**2 + 7*y**3
- >>> list(p.itermonoms())
- [(2, 0), (0, 3)]
- >>> p[(2, 0)]
- 5
- """
- monom, ns = self.monom, self.ns
- for m in self.poly.itermonoms():
- yield self._monom_fromint(m, monom, ns)
- def monoms(self) -> list[tuple[Any, ...]]:
- """Return a list of the monomials of a Puiseux polynomial."""
- return list(self.itermonoms())
- def __iter__(self) -> Iterator[tuple[tuple[Any, ...], Any]]:
- return self.itermonoms()
- def __getitem__(self, monom: tuple[int, ...]) -> Any:
- monom = self._monom_toint(monom, self.monom, self.ns)
- return self.poly[monom]
- def __len__(self) -> int:
- return len(self.poly)
- def iterterms(self) -> Iterator[tuple[tuple[Any, ...], Any]]:
- """Iterate over the terms of a Puiseux polynomial.
- >>> from sympy import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x, y = puiseux_ring('x, y', QQ)
- >>> p = 5*x**2 + 7*y**3
- >>> list(p.iterterms())
- [((2, 0), 5), ((0, 3), 7)]
- """
- monom, ns = self.monom, self.ns
- for m, coeff in self.poly.iterterms():
- mq = self._monom_fromint(m, monom, ns)
- yield mq, coeff
- def terms(self) -> list[tuple[tuple[Any, ...], Any]]:
- """Return a list of the terms of a Puiseux polynomial."""
- return list(self.iterterms())
- @property
- def is_term(self) -> bool:
- """Return True if the Puiseux polynomial is a single term."""
- return self.poly.is_term
- def to_dict(self) -> dict[tuple[int, ...], Any]:
- """Return a dictionary representation of a Puiseux polynomial."""
- return dict(self.iterterms())
- @classmethod
- def from_dict(
- cls, terms: dict[tuple[Any, ...], Any], ring: PuiseuxRing
- ) -> PuiseuxPoly:
- """Create a Puiseux polynomial from a dictionary of terms.
- >>> from sympy import QQ
- >>> from sympy.polys.puiseux import puiseux_ring, PuiseuxPoly
- >>> R, x = puiseux_ring('x', QQ)
- >>> PuiseuxPoly.from_dict({(QQ(1,2),): QQ(3)}, R)
- 3*x**(1/2)
- >>> R.from_dict({(QQ(1,2),): QQ(3)})
- 3*x**(1/2)
- """
- ns = [1] * ring.ngens
- mon = [0] * ring.ngens
- for mo in terms:
- ns = [lcm(n, m.denominator) for n, m in zip(ns, mo)]
- mon = [min(m, n) for m, n in zip(mo, mon)]
- if not any(mon):
- monom = None
- else:
- monom = tuple(-int((m * n).numerator) for m, n in zip(mon, ns))
- if all(n == 1 for n in ns):
- ns_final = None
- else:
- ns_final = tuple(ns)
- terms_p = {cls._monom_toint(m, monom, ns_final): coeff for m, coeff in terms.items()}
- poly = ring.poly_ring.from_dict(terms_p)
- return cls._new(ring, poly, monom, ns_final)
- def as_expr(self) -> Expr:
- """Convert a Puiseux polynomial to :class:`~sympy.core.expr.Expr`.
- >>> from sympy import QQ, Expr
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x = puiseux_ring('x', QQ)
- >>> p = 5*x**2 + 7*x**3
- >>> p.as_expr()
- 7*x**3 + 5*x**2
- >>> isinstance(_, Expr)
- True
- """
- ring = self.ring
- dom = ring.domain
- symbols = ring.symbols
- terms = []
- for monom, coeff in self.iterterms():
- coeff_expr = dom.to_sympy(coeff)
- monoms_expr = []
- for i, m in enumerate(monom):
- monoms_expr.append(symbols[i] ** m)
- terms.append(Mul(coeff_expr, *monoms_expr))
- return Add(*terms)
- def __repr__(self) -> str:
- def format_power(base: str, exp: int) -> str:
- if exp == 1:
- return base
- elif exp >= 0 and int(exp) == exp:
- return f"{base}**{exp}"
- else:
- return f"{base}**({exp})"
- ring = self.ring
- dom = ring.domain
- syms = [str(s) for s in ring.symbols]
- terms_str = []
- for monom, coeff in sorted(self.terms()):
- monom_str = "*".join(format_power(s, e) for s, e in zip(syms, monom) if e)
- if coeff == dom.one:
- if monom_str:
- terms_str.append(monom_str)
- else:
- terms_str.append("1")
- elif not monom_str:
- terms_str.append(str(coeff))
- else:
- terms_str.append(f"{coeff}*{monom_str}")
- return " + ".join(terms_str)
- def _unify(
- self, other: PuiseuxPoly
- ) -> tuple[
- PolyElement, PolyElement, tuple[int, ...] | None, tuple[int, ...] | None
- ]:
- """Bring two Puiseux polynomials to a common monom and ns."""
- poly1, monom1, ns1 = self.poly, self.monom, self.ns
- poly2, monom2, ns2 = other.poly, other.monom, other.ns
- if monom1 == monom2 and ns1 == ns2:
- return poly1, poly2, monom1, ns1
- if ns1 == ns2:
- ns = ns1
- elif ns1 is not None and ns2 is not None:
- ns = tuple(lcm(n1, n2) for n1, n2 in zip(ns1, ns2))
- f1 = [n // n1 for n, n1 in zip(ns, ns1)]
- f2 = [n // n2 for n, n2 in zip(ns, ns2)]
- poly1 = poly1.inflate(f1)
- poly2 = poly2.inflate(f2)
- if monom1 is not None:
- monom1 = tuple(m * f for m, f in zip(monom1, f1))
- if monom2 is not None:
- monom2 = tuple(m * f for m, f in zip(monom2, f2))
- elif ns2 is not None:
- ns = ns2
- poly1 = poly1.inflate(ns)
- if monom1 is not None:
- monom1 = tuple(m * n for m, n in zip(monom1, ns))
- elif ns1 is not None:
- ns = ns1
- poly2 = poly2.inflate(ns)
- if monom2 is not None:
- monom2 = tuple(m * n for m, n in zip(monom2, ns))
- else:
- assert False
- if monom1 == monom2:
- monom = monom1
- elif monom1 is not None and monom2 is not None:
- monom = tuple(max(m1, m2) for m1, m2 in zip(monom1, monom2))
- poly1 = _mul_poly_monom(poly1, _div_monom(monom, monom1))
- poly2 = _mul_poly_monom(poly2, _div_monom(monom, monom2))
- elif monom2 is not None:
- monom = monom2
- poly1 = _mul_poly_monom(poly1, monom2)
- elif monom1 is not None:
- monom = monom1
- poly2 = _mul_poly_monom(poly2, monom1)
- else:
- assert False
- return poly1, poly2, monom, ns
- def __pos__(self) -> PuiseuxPoly:
- return self
- def __neg__(self) -> PuiseuxPoly:
- return self._new_raw(self.ring, -self.poly, self.monom, self.ns)
- def __add__(self, other: Any) -> PuiseuxPoly:
- if isinstance(other, PuiseuxPoly):
- if self.ring != other.ring:
- raise ValueError("Cannot add Puiseux polynomials from different rings")
- return self._add(other)
- domain = self.ring.domain
- if isinstance(other, int):
- return self._add_ground(domain.convert_from(QQ(other), QQ))
- elif domain.of_type(other):
- return self._add_ground(other)
- else:
- return NotImplemented
- def __radd__(self, other: Any) -> PuiseuxPoly:
- domain = self.ring.domain
- if isinstance(other, int):
- return self._add_ground(domain.convert_from(QQ(other), QQ))
- elif domain.of_type(other):
- return self._add_ground(other)
- else:
- return NotImplemented
- def __sub__(self, other: Any) -> PuiseuxPoly:
- if isinstance(other, PuiseuxPoly):
- if self.ring != other.ring:
- raise ValueError(
- "Cannot subtract Puiseux polynomials from different rings"
- )
- return self._sub(other)
- domain = self.ring.domain
- if isinstance(other, int):
- return self._sub_ground(domain.convert_from(QQ(other), QQ))
- elif domain.of_type(other):
- return self._sub_ground(other)
- else:
- return NotImplemented
- def __rsub__(self, other: Any) -> PuiseuxPoly:
- domain = self.ring.domain
- if isinstance(other, int):
- return self._rsub_ground(domain.convert_from(QQ(other), QQ))
- elif domain.of_type(other):
- return self._rsub_ground(other)
- else:
- return NotImplemented
- def __mul__(self, other: Any) -> PuiseuxPoly:
- if isinstance(other, PuiseuxPoly):
- if self.ring != other.ring:
- raise ValueError(
- "Cannot multiply Puiseux polynomials from different rings"
- )
- return self._mul(other)
- domain = self.ring.domain
- if isinstance(other, int):
- return self._mul_ground(domain.convert_from(QQ(other), QQ))
- elif domain.of_type(other):
- return self._mul_ground(other)
- else:
- return NotImplemented
- def __rmul__(self, other: Any) -> PuiseuxPoly:
- domain = self.ring.domain
- if isinstance(other, int):
- return self._mul_ground(domain.convert_from(QQ(other), QQ))
- elif domain.of_type(other):
- return self._mul_ground(other)
- else:
- return NotImplemented
- def __pow__(self, other: Any) -> PuiseuxPoly:
- if isinstance(other, int):
- if other >= 0:
- return self._pow_pint(other)
- else:
- return self._pow_nint(-other)
- elif QQ.of_type(other):
- return self._pow_rational(other)
- else:
- return NotImplemented
- def __truediv__(self, other: Any) -> PuiseuxPoly:
- if isinstance(other, PuiseuxPoly):
- if self.ring != other.ring:
- raise ValueError(
- "Cannot divide Puiseux polynomials from different rings"
- )
- return self._mul(other._inv())
- domain = self.ring.domain
- if isinstance(other, int):
- return self._mul_ground(domain.convert_from(QQ(1, other), QQ))
- elif domain.of_type(other):
- return self._div_ground(other)
- else:
- return NotImplemented
- def __rtruediv__(self, other: Any) -> PuiseuxPoly:
- if isinstance(other, int):
- return self._inv()._mul_ground(self.ring.domain.convert_from(QQ(other), QQ))
- elif self.ring.domain.of_type(other):
- return self._inv()._mul_ground(other)
- else:
- return NotImplemented
- def _add(self, other: PuiseuxPoly) -> PuiseuxPoly:
- poly1, poly2, monom, ns = self._unify(other)
- return self._new(self.ring, poly1 + poly2, monom, ns)
- def _add_ground(self, ground: Any) -> PuiseuxPoly:
- return self._add(self.ring.ground_new(ground))
- def _sub(self, other: PuiseuxPoly) -> PuiseuxPoly:
- poly1, poly2, monom, ns = self._unify(other)
- return self._new(self.ring, poly1 - poly2, monom, ns)
- def _sub_ground(self, ground: Any) -> PuiseuxPoly:
- return self._sub(self.ring.ground_new(ground))
- def _rsub_ground(self, ground: Any) -> PuiseuxPoly:
- return self.ring.ground_new(ground)._sub(self)
- def _mul(self, other: PuiseuxPoly) -> PuiseuxPoly:
- poly1, poly2, monom, ns = self._unify(other)
- if monom is not None:
- monom = tuple(2 * e for e in monom)
- return self._new(self.ring, poly1 * poly2, monom, ns)
- def _mul_ground(self, ground: Any) -> PuiseuxPoly:
- return self._new_raw(self.ring, self.poly * ground, self.monom, self.ns)
- def _div_ground(self, ground: Any) -> PuiseuxPoly:
- return self._new_raw(self.ring, self.poly / ground, self.monom, self.ns)
- def _pow_pint(self, n: int) -> PuiseuxPoly:
- assert n >= 0
- monom = self.monom
- if monom is not None:
- monom = tuple(m * n for m in monom)
- return self._new(self.ring, self.poly**n, monom, self.ns)
- def _pow_nint(self, n: int) -> PuiseuxPoly:
- return self._inv()._pow_pint(n)
- def _pow_rational(self, n: Any) -> PuiseuxPoly:
- if not self.is_term:
- raise ValueError("Only monomials can be raised to a rational power")
- [(monom, coeff)] = self.terms()
- domain = self.ring.domain
- if not domain.is_one(coeff):
- raise ValueError("Only monomials can be raised to a rational power")
- monom = tuple(m * n for m in monom)
- return self.ring.from_dict({monom: domain.one})
- def _inv(self) -> PuiseuxPoly:
- if not self.is_term:
- raise ValueError("Only terms can be inverted")
- [(monom, coeff)] = self.terms()
- domain = self.ring.domain
- if not domain.is_Field and not domain.is_one(coeff):
- raise ValueError("Cannot invert non-unit coefficient")
- monom = tuple(-m for m in monom)
- coeff = 1 / coeff
- return self.ring.from_dict({monom: coeff})
- def diff(self, x: PuiseuxPoly) -> PuiseuxPoly:
- """Differentiate a Puiseux polynomial with respect to a variable.
- >>> from sympy import QQ
- >>> from sympy.polys.puiseux import puiseux_ring
- >>> R, x, y = puiseux_ring('x, y', QQ)
- >>> p = 5*x**2 + 7*y**3
- >>> p.diff(x)
- 10*x
- >>> p.diff(y)
- 21*y**2
- """
- ring = self.ring
- i = ring.index(x)
- g = {}
- for expv, coeff in self.iterterms():
- n = expv[i]
- if n:
- e = list(expv)
- e[i] -= 1
- g[tuple(e)] = coeff * n
- return ring(g)
|