Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
"""Sparse rational function fields. """
from __future__ import print_function, division
from operator import add, mul, lt, le, gt, ge
from sympy.core.compatibility import is_sequence, reduce, string_types from sympy.core.expr import Expr from sympy.core.symbol import Symbol from sympy.core.sympify import CantSympify, sympify from sympy.polys.rings import PolyElement from sympy.polys.orderings import lex from sympy.polys.polyerrors import CoercionFailed from sympy.polys.polyoptions import build_options from sympy.polys.polyutils import _parallel_dict_from_expr from sympy.polys.domains.domainelement import DomainElement from sympy.polys.domains.polynomialring import PolynomialRing from sympy.polys.domains.fractionfield import FractionField from sympy.polys.constructor import construct_domain from sympy.printing.defaults import DefaultPrinting from sympy.utilities import public from sympy.utilities.magic import pollute
@public def field(symbols, domain, order=lex): """Construct new rational function field returning (field, x1, ..., xn). """ _field = FracField(symbols, domain, order) return (_field,) + _field.gens
@public def xfield(symbols, domain, order=lex): """Construct new rational function field returning (field, (x1, ..., xn)). """ _field = FracField(symbols, domain, order) return (_field, _field.gens)
@public def vfield(symbols, domain, order=lex): """Construct new rational function field and inject generators into global namespace. """ _field = FracField(symbols, domain, order) pollute([ sym.name for sym in _field.symbols ], _field.gens) return _field
@public def sfield(exprs, *symbols, **options): """Construct a field deriving generators and domain from options and input expressions.
Parameters ---------- exprs : :class:`Expr` or sequence of :class:`Expr` (sympifiable) symbols : sequence of :class:`Symbol`/:class:`Expr` options : keyword arguments understood by :class:`Options`
Examples ========
>>> from sympy.core import symbols >>> from sympy.functions import exp, log >>> from sympy.polys.fields import sfield
>>> x = symbols("x") >>> K, f = sfield((x*log(x) + 4*x**2)*exp(1/x + log(x)/3)/x**2) >>> K Rational function field in x, exp(1/x), log(x), x**(1/3) over ZZ with lex order >>> f (4*x**2*(exp(1/x)) + x*(exp(1/x))*(log(x)))/((x**(1/3))**5) """ single = False if not is_sequence(exprs): exprs, single = [exprs], True
exprs = list(map(sympify, exprs)) opt = build_options(symbols, options) numdens = [] for expr in exprs: numdens.extend(expr.as_numer_denom()) reps, opt = _parallel_dict_from_expr(numdens, opt)
if opt.domain is None: # NOTE: this is inefficient because construct_domain() automatically # performs conversion to the target domain. It shouldn't do this. coeffs = sum([list(rep.values()) for rep in reps], []) opt.domain, _ = construct_domain(coeffs, opt=opt)
_field = FracField(opt.gens, opt.domain, opt.order) fracs = [] for i in range(0, len(reps), 2): fracs.append(_field(tuple(reps[i:i+2])))
if single: return (_field, fracs[0]) else: return (_field, fracs)
_field_cache = {}
class FracField(DefaultPrinting): """Multivariate distributed rational function field. """
def __new__(cls, symbols, domain, order=lex):
def _gens(self): """Return a list of polynomial generators. """
def __getnewargs__(self): return (self.symbols, self.domain, self.order)
def __hash__(self):
def __eq__(self, other):
def __ne__(self, other): return self is not other
def raw_new(self, numer, denom=None): def new(self, numer, denom=None):
def domain_new(self, element): return self.domain.convert(element)
def ground_new(self, element): except CoercionFailed: domain = self.domain
if not domain.has_Field and domain.has_assoc_Field: ring = self.ring ground_field = domain.get_field() element = ground_field.convert(element) numer = ring.ground_new(ground_field.numer(element)) denom = ring.ground_new(ground_field.denom(element)) return self.raw_new(numer, denom) else: raise
def field_new(self, element): if self == element.field: return element else: raise NotImplementedError("conversion") raise NotImplementedError("parsing") return self.from_expr(element) else:
__call__ = field_new
def _rebuild_expr(self, expr, mapping): domain = self.domain
def _rebuild(expr): generator = mapping.get(expr)
if generator is not None: return generator elif expr.is_Add: return reduce(add, list(map(_rebuild, expr.args))) elif expr.is_Mul: return reduce(mul, list(map(_rebuild, expr.args))) elif expr.is_Pow and expr.exp.is_Integer: return _rebuild(expr.base)**int(expr.exp) else: try: return domain.convert(expr) except CoercionFailed: if not domain.has_Field and domain.has_assoc_Field: return domain.get_field().convert(expr) else: raise
return _rebuild(sympify(expr))
def from_expr(self, expr): mapping = dict(list(zip(self.symbols, self.gens)))
try: frac = self._rebuild_expr(expr, mapping) except CoercionFailed: raise ValueError("expected an expression convertible to a rational function in %s, got %s" % (self, expr)) else: return self.field_new(frac)
def to_domain(self):
def to_ring(self):
class FracElement(DomainElement, DefaultPrinting, CantSympify): """Element of multivariate distributed rational function field. """
def __init__(self, numer, denom=None): raise ZeroDivisionError("zero denominator")
def raw_new(f, numer, denom): def new(f, numer, denom):
def to_poly(f): if f.denom != 1: raise ValueError("f.denom should be 1") return f.numer
def parent(self):
def __getnewargs__(self): return (self.field, self.numer, self.denom)
_hash = None
def __hash__(self):
def copy(self): return self.raw_new(self.numer.copy(), self.denom.copy())
def set_field(self, new_field): return self else:
def as_expr(self, *symbols):
def __eq__(f, g): else: return f.numer == g and f.denom == f.field.ring.one
def __ne__(f, g): return not f.__eq__(g)
def __nonzero__(f):
__bool__ = __nonzero__
def sort_key(self): return (self.denom.sort_key(), self.numer.sort_key())
def _cmp(f1, f2, op): if isinstance(f2, f1.field.dtype): return op(f1.sort_key(), f2.sort_key()) else: return NotImplemented
def __lt__(f1, f2): return f1._cmp(f2, lt) def __le__(f1, f2): return f1._cmp(f2, le) def __gt__(f1, f2): return f1._cmp(f2, gt) def __ge__(f1, f2): return f1._cmp(f2, ge)
def __pos__(f): """Negate all coefficients in ``f``. """ return f.raw_new(f.numer, f.denom)
def __neg__(f): """Negate all coefficients in ``f``. """
def _extract_ground(self, element):
except CoercionFailed: if not domain.has_Field and domain.has_assoc_Field: ground_field = domain.get_field()
try: element = ground_field.convert(element) except CoercionFailed: pass else: return -1, ground_field.numer(element), ground_field.denom(element)
return 0, None, None else:
def __add__(f, g): """Add rational functions ``f`` and ``g``. """
return f else: return f.new(f.numer*g.denom + f.denom*g.numer, f.denom*g.denom) elif isinstance(g, field.ring.dtype): return f.new(f.numer + f.denom*g, f.denom) else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__radd__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__radd__(f)
return f.__radd__(g)
def __radd__(f, c): if isinstance(c, f.field.ring.dtype): return f.new(f.numer + f.denom*c, f.denom)
op, g_numer, g_denom = f._extract_ground(c)
if op == 1: return f.new(f.numer + f.denom*g_numer, f.denom) elif not op: return NotImplemented else: return f.new(f.numer*g_denom + f.denom*g_numer, f.denom*g_denom)
def __sub__(f, g): """Subtract rational functions ``f`` and ``g``. """
else: elif isinstance(g, field.ring.dtype): return f.new(f.numer - f.denom*g, f.denom) else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__rsub__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__rsub__(f)
op, g_numer, g_denom = f._extract_ground(g)
if op == 1: return f.new(f.numer - f.denom*g_numer, f.denom) elif not op: return NotImplemented else: return f.new(f.numer*g_denom - f.denom*g_numer, f.denom*g_denom)
def __rsub__(f, c): if isinstance(c, f.field.ring.dtype): return f.new(-f.numer + f.denom*c, f.denom)
op, g_numer, g_denom = f._extract_ground(c)
if op == 1: return f.new(-f.numer + f.denom*g_numer, f.denom) elif not op: return NotImplemented else: return f.new(-f.numer*g_denom + f.denom*g_numer, f.denom*g_denom)
def __mul__(f, g): """Multiply rational functions ``f`` and ``g``. """
else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__rmul__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__rmul__(f)
return f.__rmul__(g)
def __rmul__(f, c): return f.new(f.numer*c, f.denom)
elif not op: return NotImplemented else: return f.new(f.numer*g_numer, f.denom*g_denom)
def __truediv__(f, g): """Computes quotient of fractions ``f`` and ``g``. """
raise ZeroDivisionError elif isinstance(g, field.ring.dtype): return f.new(f.numer, f.denom*g) else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__rtruediv__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__rtruediv__(f)
op, g_numer, g_denom = f._extract_ground(g)
if op == 1: return f.new(f.numer, f.denom*g_numer) elif not op: return NotImplemented else: return f.new(f.numer*g_denom, f.denom*g_numer)
__div__ = __truediv__
def __rtruediv__(f, c): if not f: raise ZeroDivisionError elif isinstance(c, f.field.ring.dtype): return f.new(f.denom*c, f.numer)
op, g_numer, g_denom = f._extract_ground(c)
if op == 1: return f.new(f.denom*g_numer, f.numer) elif not op: return NotImplemented else: return f.new(f.denom*g_numer, f.numer*g_denom)
__rdiv__ = __rtruediv__
def __pow__(f, n): """Raise ``f`` to a non-negative power ``n``. """ if n >= 0: return f.raw_new(f.numer**n, f.denom**n) elif not f: raise ZeroDivisionError else: return f.raw_new(f.denom**-n, f.numer**-n)
def diff(f, x): """Computes partial derivative in ``x``.
Examples ========
>>> from sympy.polys.fields import field >>> from sympy.polys.domains import ZZ
>>> _, x, y, z = field("x,y,z", ZZ) >>> ((x**2 + y)/(z + 1)).diff(x) 2*x/(z + 1)
""" x = x.to_poly() return f.new(f.numer.diff(x)*f.denom - f.numer*f.denom.diff(x), f.denom**2)
def __call__(f, *values): if 0 < len(values) <= f.field.ngens: return f.evaluate(list(zip(f.field.gens, values))) else: raise ValueError("expected at least 1 and at most %s values, got %s" % (f.field.ngens, len(values)))
def evaluate(f, x, a=None): if isinstance(x, list) and a is None: x = [ (X.to_poly(), a) for X, a in x ] numer, denom = f.numer.evaluate(x), f.denom.evaluate(x) else: x = x.to_poly() numer, denom = f.numer.evaluate(x, a), f.denom.evaluate(x, a)
field = numer.ring.to_field() return field.new(numer, denom)
def subs(f, x, a=None): if isinstance(x, list) and a is None: x = [ (X.to_poly(), a) for X, a in x ] numer, denom = f.numer.subs(x), f.denom.subs(x) else: x = x.to_poly() numer, denom = f.numer.subs(x, a), f.denom.subs(x, a)
return f.new(numer, denom)
def compose(f, x, a=None): raise NotImplementedError |