5

I think there must be a simple answer to this, but I searched long and couldn't find it.

Example: A simple Laurent Polynomial, so that

>> p = 2*y*x**2+4*y/x

A factorization gives

>> factor(p) 2*y*(x**3 + 2)/x How do I extract the factor 2*y/x ? Is there a simple way to get the common factor in the expression when the expression isn't a polynomial? I have tried a lot, but haven't found anything remotely satisfying. The factorization must exist somewhere in the steps of factor(), right?

Felix
  • 51
  • 2
  • 3
    `factor(p).args` returns the top level terms in the product: `(2, y, 1/x, x**3 + 2)`. Is this what you are looking for? – unutbu Dec 12 '17 at 22:53
  • yes!! thank you so much... – Felix Dec 12 '17 at 23:12
  • @unutbu not quite, because `factor_list` only works on polynomials proper, whereas `factor` is smart with rational functions and factors the numerator and denominator separately. – asmeurer Dec 13 '17 at 05:03

1 Answers1

3

As @unutbu points out, from this question you can see that factor_list gives the factors of a polynomial in a easier to use fashon.

Unfortunately, SymPy doesn't actually deal with Laurent polynomials (see https://github.com/sympy/sympy/issues/5131). You might actually not get what you are expecting for higher order terms. What SymPy does is it sees that p is a rational function, so it factors the numerator and denominator separately.

If you also want to do this, you can use something like

n, d = fraction(cancel(p))
factor_list(n)
factor_list(d)

and manipulate the factors separately.

The cancel will ensure that there are no duplicates, which would otherwise happen automatically with factor(p), and it also puts the expression in rational form so that fraction will work. You could also use p.as_numer_denom() to skip this step (if it ends up being slow).

Alternately, you may want to consider x and 1/x as separate generators of the polynomial. Here's a (corrected) function from the above issue

def aspoly1t(p, t, z=Symbol('z')):
    """
    Rewrite p, a polynomial in t and 1/t, as a polynomial in t and z=1/t
    """
    pa, pd = cancel(p).as_numer_denom()
    pa, pd = Poly(pa, t), Poly(pd, t)
    assert pd.is_monomial
    d = pd.degree(t)
    one_t_part = pa.slice(0, d + 1)
    t_part = pa - one_t_part
    t_part = t_part.to_field().quo(pd)
    one_t_part = Poly.from_list(reversed(one_t_part.rep.rep), *one_t_part.gens, domain=one_t_part.domain)
    one_t_part = one_t_part.replace(t, z) # z will be 1/t
    ans = t_part.as_poly(t, z) + one_t_part.as_poly(t, z)
    return ans

(some day Poly will support this natively as Poly(p, x, 1/x)) You can then use factor_list on this:

>>> aspoly1t(p, x)
Poly(2*y*x**2 + 4*y*z, x, z, domain='ZZ[y]')
>>> factor_list(aspoly1t(p, x))
(2, [(Poly(y, x, y, z, domain='ZZ'), 1), (Poly(x**2 + 2*z, x, y, z, domain='ZZ'), 1)])

Note that the factors here are not the same, so it really does matter how you want to interpret and factor your Laurent polynomials.

asmeurer
  • 86,894
  • 26
  • 169
  • 240
  • Thank you, this seems to be a more elegant solution. For the time being, I'll use a simple fix: I get the degree of the largest negative power using the `factor.args` and keeping this "shift" in mind when evaluating the polynomial coefficients obtained from `all_coeffs(Poly(px))`, where px is now the polynomial part. – Felix Dec 13 '17 at 10:32