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

unify alias substitute for subs #37210

Merged
merged 8 commits into from
Feb 13, 2024
4 changes: 1 addition & 3 deletions src/sage/interfaces/macaulay2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1241,7 +1241,7 @@ def structure_sheaf(self):
deprecation(27848, 'The function `structure_sheaf` is deprecated. Use `self.sheaf()` instead.')
return self.parent()('OO_%s' % self.name())

def substitute(self, *args, **kwds):
def subs(self, *args, **kwds):
"""
Note that we have to override the substitute method so that we get
the default one from Macaulay2 instead of the one provided by Element.
Expand All @@ -1260,8 +1260,6 @@ def substitute(self, *args, **kwds):
"""
return self.__getattr__("substitute")(*args, **kwds)

subs = substitute

def _tab_completion(self):
"""
Return a list of tab completions for ``self``.
Expand Down
4 changes: 1 addition & 3 deletions src/sage/rings/asymptotic/asymptotic_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -2453,7 +2453,7 @@ def exp(self, precision=None):
"""
return self.rpow('e', precision=precision)

def substitute(self, rules=None, domain=None, **kwds):
def subs(self, rules=None, domain=None, **kwds):
r"""
Substitute the given ``rules`` in this asymptotic expansion.

Expand Down Expand Up @@ -2677,8 +2677,6 @@ def substitute(self, rules=None, domain=None, **kwds):
TypeError('Cannot apply the substitution rules %s on %s '
'in %s.' % (rules, self, self.parent())), e)

subs = substitute

def _substitute_(self, rules):
r"""
Substitute the given ``rules`` in this asymptotic expansion.
Expand Down
4 changes: 2 additions & 2 deletions src/sage/rings/fraction_field_FpT.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ cdef class FpTElement(FieldElement):
"""
return self.numer()(*args, **kwds) / self.denom()(*args, **kwds)

def subs(self, *args, **kwds):
def subs(self, in_dict=None, *args, **kwds):
"""
EXAMPLES::

Expand All @@ -280,7 +280,7 @@ cdef class FpTElement(FieldElement):
sage: f.subs(X=2)
(t + 1)/(t + 10)
"""
return self.numer().subs(*args, **kwds) / self.denom().subs(*args, **kwds)
return self.numer().subs(in_dict, *args, **kwds) / self.denom().subs(in_dict, *args, **kwds)

def valuation(self, v):
"""
Expand Down
42 changes: 42 additions & 0 deletions src/sage/rings/fraction_field_element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,48 @@ cdef class FractionFieldElement(FieldElement):
"""
return self._numerator(*x, **kwds) / self._denominator(*x, **kwds)

def subs(self, in_dict=None, *args, **kwds):
r"""
Substitute variables in the numerator and denominator of ``self``.

If a dictionary is passed, the keys are mapped to generators
of the parent ring. Otherwise, the arguments are transmitted
unchanged to the method ``subs`` of the numerator and the
denominator.

EXAMPLES::

sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens()
sage: f = x^2 + y + x^2*y^2 + 5
sage: (1/f).subs(x=5)
1/(25*y^2 + y + 30)

TESTS:

Check that :issue:`37122` is fixed::

sage: P = PolynomialRing(QQ, ["x%s" % i for i in range(10000)])
sage: PF = P.fraction_field()
sage: p = sum(i*P.gen(i) for i in range(5)) / sum(i*P.gen(i) for i in range(8))
sage: v = P.gen(4)
sage: p.subs({v: 100})
(x1 + 2*x2 + 3*x3 + 400)/(x1 + 2*x2 + 3*x3 + 5*x5 + 6*x6 + 7*x7 + 400)
"""
if isinstance(in_dict, dict):
gens = self.parent().gens()

def to_R(m):
try:
mi = gens.index(m)
except ValueError:
return m
return mi
in_dict = {to_R(m): v for m, v in in_dict.items()}

num = self._numerator.subs(in_dict, *args, **kwds)
den = self._denominator.subs(in_dict, *args, **kwds)
return num / den

def _is_atomic(self):
"""
EXAMPLES::
Expand Down
10 changes: 8 additions & 2 deletions src/sage/rings/polynomial/laurent_polynomial_mpair.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1345,14 +1345,20 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial):

sage: x.subs({x: 2}, x=1)
1

sage: f.subs({1: 2}, x=1)
3*z + 5
"""
cdef list variables = list(self._parent.gens())
cdef Py_ssize_t i
for i in range(len(variables)):
if str(variables[i]) in kwds:
variables[i] = kwds[str(variables[i])]
elif in_dict and variables[i] in in_dict:
variables[i] = in_dict[variables[i]]
elif in_dict:
if variables[i] in in_dict:
variables[i] = in_dict[variables[i]]
elif i in in_dict:
variables[i] = in_dict[i]
return self(tuple(variables))

def is_constant(self):
Expand Down
22 changes: 12 additions & 10 deletions src/sage/rings/polynomial/multi_polynomial_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ def __call__(self, *x, **kwds):
except AttributeError:
K = self.parent().base_ring()
y = K(0)
for (m,c) in self.element().dict().items():
y += c*prod([ x[i]**m[i] for i in range(n) if m[i] != 0])
for m, c in self.element().dict().items():
y += c * prod(v ** e for v, e in zip(x, m) if e)
return y

def _richcmp_(self, right, op):
Expand Down Expand Up @@ -1388,7 +1388,7 @@ def is_term(self):
"""
return len(self.element()) == 1

def subs(self, fixed=None, **kw):
def subs(self, fixed=None, **kwds):
"""
Fix some given variables in a given multivariate polynomial and
return the changed multivariate polynomials. The polynomial itself
Expand All @@ -1400,10 +1400,9 @@ def subs(self, fixed=None, **kw):

INPUT:


- ``fixed`` - (optional) dictionary of inputs

- ``**kw`` - named parameters
- ``**kwds`` - named parameters


OUTPUT: new :class:`MPolynomial`
Expand All @@ -1419,11 +1418,14 @@ def subs(self, fixed=None, **kw):
25*y^2 + y + 30
"""
variables = list(self.parent().gens())
for i in range(0,len(variables)):
if str(variables[i]) in kw:
variables[i] = kw[str(variables[i])]
elif fixed and variables[i] in fixed:
variables[i] = fixed[variables[i]]
for i in range(len(variables)):
if str(variables[i]) in kwds:
variables[i] = kwds[str(variables[i])]
elif fixed:
if variables[i] in fixed:
variables[i] = fixed[variables[i]]
elif i in fixed:
variables[i] = fixed[i]
return self(tuple(variables))

def monomials(self):
Expand Down
27 changes: 14 additions & 13 deletions src/sage/rings/polynomial/polynomial_element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -411,11 +411,9 @@ cdef class Polynomial(CommutativePolynomial):
return self._parent.zero()
return self * self._parent(right)

def subs(self, *x, **kwds):
def subs(self, in_dict=None, *args, **kwds):
r"""
Identical to ``self(*x)``.

See the docstring for :meth:`__call__`.
Substitute the variable in ``self``.

EXAMPLES::

Expand All @@ -434,15 +432,18 @@ cdef class Polynomial(CommutativePolynomial):
...
TypeError: keys do not match self's parent
"""
if len(x) == 1 and isinstance(x[0], dict):
g = self._parent.gen()
if g in x[0]:
return self(x[0][g])
elif len(x[0]) > 0:
raise TypeError("keys do not match self's parent")
return self
return self(*x, **kwds)
substitute = subs
if not in_dict:
return self(*args, **kwds)

if isinstance(in_dict, dict):
if len(in_dict) > 1:
raise TypeError("only the generator can be substituted, use __call__ instead")
k, v = next(iter(in_dict.items()))
if not k or k == self._parent.gen():
return self(v, *args, **kwds)
raise TypeError("keys do not match self's parent")

return self(in_dict, *args, **kwds)

@cython.boundscheck(False)
@cython.wraparound(False)
Expand Down
53 changes: 22 additions & 31 deletions src/sage/structure/element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,28 @@ cdef class Element(SageObject):
variables.append(gen)
return self(*variables)

def substitute(self, *args, **kwds):
"""
This calls :meth:`self.subs`.

EXAMPLES::

sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens()
sage: f = x^2 + y + x^2*y^2 + 5
sage: f((5,y))
25*y^2 + y + 30
sage: f.substitute({x: 5})
25*y^2 + y + 30
sage: f.substitute(x=5)
25*y^2 + y + 30
sage: (1/f).substitute(x=5)
1/(25*y^2 + y + 30)
sage: Integer(5).substitute(x=4)
5
"""
return self.subs(*args, **kwds)


def numerical_approx(self, prec=None, digits=None, algorithm=None):
"""
Return a numerical approximation of ``self`` with ``prec`` bits
Expand Down Expand Up @@ -906,37 +928,6 @@ cdef class Element(SageObject):
"""
return self.n(prec)._mpmath_(prec=prec)

def substitute(self,in_dict=None,**kwds):
"""
This is an alias for self.subs().

INPUT:

- ``in_dict`` - (optional) dictionary of inputs

- ``**kwds`` - named parameters

OUTPUT:

- new object if substitution is possible, otherwise self.

EXAMPLES::

sage: x, y = PolynomialRing(ZZ, 2, 'xy').gens()
sage: f = x^2 + y + x^2*y^2 + 5
sage: f((5,y))
25*y^2 + y + 30
sage: f.substitute({x: 5})
25*y^2 + y + 30
sage: f.substitute(x=5)
25*y^2 + y + 30
sage: (1/f).substitute(x=5)
1/(25*y^2 + y + 30)
sage: Integer(5).substitute(x=4)
5
"""
return self.subs(in_dict,**kwds)

cpdef _act_on_(self, x, bint self_on_left) noexcept:
"""
Use this method to implement ``self`` acting on ``x``.
Expand Down
4 changes: 1 addition & 3 deletions src/sage/symbolic/expression.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -5574,7 +5574,7 @@ cdef class Expression(Expression_abc):
cdef Expression p = self.coerce_in(pattern)
return self._gobj.has(p._gobj)

def substitute(self, *args, **kwds):
def subs(self, *args, **kwds):
"""
Substitute the given subexpressions in this expression.

Expand Down Expand Up @@ -5899,8 +5899,6 @@ cdef class Expression(Expression_abc):
res = self._gobj.subs_map(smap, 0)
return new_Expression_from_GEx(self._parent, res)

subs = substitute

cpdef Expression _subs_expr(self, expr) noexcept:
"""
EXAMPLES::
Expand Down
Loading