# Substitute square in polynomial

Suppose I have a univariate polynomial, say from QQ['x'], and for some reason I know that the odd coefficients will all be zero. So in fact this is a polynomial in x^2. What's the most elegant syntax to substitute some other value for x^2?

The best I could come up with so far is something like this:

sage: x = QQ['x'].0
sage: p = x^6 - 3*x^4 + 5*x^2 - 9
sage: assert not any(p[i] for i in range(1,p.degree()+1,2))
sage: p.parent([p[i] for i in range(0,p.degree()+1,2)])
x^3 - 3*x^2 + 5*x - 9
sage: _(7)
222


Of course, if the value I want to plug in comes from some field where I can compute square roots, I could simply plug one of those in, like p(sqrt(7)) in the example above, but I'm more interested in substituting a multivariate polynomial or something like this.

edit retag close merge delete

1

Note that in the symbolic ring you can use substitute_expression, for example after sage: x = var('x') and sage: f = x^2 + 1 then f.subs_expr(x^2 == x) gives x+1. Not sure going to the symbolic ring and back could be called elegant, though.

( 2015-01-10 10:52:22 -0600 )edit

Sort by » oldest newest most voted

A shortcut for the first line is:

sage: x = polygen(QQ)


Then it's a matter of taste which is more elegant: your code, or

sage: p = x^6 - 3*x^4 + 5*x^2 - 9
sage: assert not any(p.list()[1::2])
sage: p.parent(p.list()[::2])
x^3 - 3*x^2 + 5*x - 9


This code is shorter, but yours iterates instead of constructing a list.

Sadly, p[1::2] and p[::2] don't give the expected result, the final :2 is silently ignored. The code for that is in the __getitem__ method of the class Polynomial_rational_flint, which is the class of p. Inspecting this method:

sage: from sage.rings.polynomial.polynomial_rational_flint import Polynomial_rational_flint
sage: Polynomial_rational_flint.__getitem__??


gives

def __getitem__(self, n):
"""
Returns coefficient of the monomial of degree n if n is an integer,
returns the monomials of self of degree in slice n if n is a slice.

INPUT:

- n - Degree of the monomial whose coefficient is to be returned
or a slice.

EXAMPLES::

sage: R.<t> = QQ[]
sage: f = 1 + t + t^2/2 + t^3/3 + t^4/4
sage: f[-1], f[0], f[3], f[5]            # indirect doctest
(0, 1, 1/3, 0)
sage: f[1:3]                             # indirect doctest
1/2*t^2 + t
"""
cdef Rational z = PY_NEW(Rational)
cdef Polynomial_rational_flint res = self._new()
cdef bint do_sig = _do_sig(self.__poly)
if isinstance(n, slice):
start, stop, step = n.indices(self.degree() + 1)
if do_sig: sig_on()
fmpq_poly_get_slice(res.__poly, self.__poly, start, stop)
if do_sig: sig_off()
return res
else:
if 0 <= n and n < fmpq_poly_length(self.__poly):
fmpq_poly_get_coeff_mpq(z.value, self.__poly, n)
return z


in which we see that in the case where n is an instance of the type slice, it is split into start, stop, step, and then step is silently dropped when calling fmpq_poly_get_slice(res.__poly, self.__poly, start, stop).

Fixing this is not immediate since the flint function fmpq_poly_get_slice only takes start and stop arguments, not a step, see flint source code. Maybe this could be a feature request for Flint, and then we could make the __get_item__ method above work!

In the meantime, I wonder if a warning, or a "not implemented" error should be raised when calling __get_item__ with a step other than 1, since the result is likely to be wrong with respect to the user's expectation.

Going beyond this exploration of your simple example: of course if you wanted to deal with very general polynomials, including sparse polynomials or multivariate polynomials, you would need to deal differently with different flavours of polynomials, since they have different implementations.

more