# latex printing of polynomials

I have a code that generates some symbolic polynomials. I would like to generate the latex code for this polynomials to present them in the form: $a_n \ s^n + a_{n-1} \ s^{n-1} + ... + a_1 \ s + a_0$, where the coefficients $a_n, ..., a_0$ can be Real or Complex. The snippet below shows how I accomplish this.

var("s")
polynomial(s) = 115109024454248/712169248941107*(8*s^4 + 8*s^2 + 1)/pi - 1406886797940560/2486758794631057*(2*s^2 + 1)/pi + 59613932955136480/18544180962144109/pi
type(polynomial(s))
type(polynomial(s).polynomial(RR))
latex(polynomial(s).polynomial(RR))
# type(polynomial(s).polynomial(CC))
# latex(polynomial(s).polynomial(CC))

polynomial2(s) = 0.411591379742810*s^4 + 0.000000000000000*s^3 + 0.0514229729855488*s^2 + 0.000000000000000*s + 0.894634727759564
type(polynomial2(s))
latex(polynomial2(s))


the code outputs

s
<type 'sage.symbolic.expression.Expression'>
<type 'sage.rings.polynomial.polynomial_real_mpfr_dense.PolynomialRealDense'>
0.411591379742810 s^{4} + 0.000000000000000 s^{3} + 0.0514229729855488 s^{2} + 0.000000000000000 s + 0.894634727759564
<type 'sage.symbolic.expression.Expression'>
0.411591379742810 \, s^{4} + 0.0514229729855488 \, s^{2} + 0.894634727759564


Now, the questions are:

1) Is printing polynomials terms with zero coefficients the intended behavior for the _latex()_ command when operating over type 'sage.rings.polynomial.polynomial_real_mpfr_dense.PolynomialRealDense' and class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_field_with_category.element_class'? the _latex()_ command behaves differently when operating over symbolic polynomials.

2) Is there a way to set the displayed number of digits?

3) Is there a better way to present such symbolic polynomials in latex code in the desired format?

Cordially

edit retag close merge delete

1

1) Yes.

Does the following help

sage: R.<s> = QQ[]
sage: P = s^5+s+1
sage: latex(P)
s^{5} + s + 1
sage: latex(P.change_ring(RR))
s^{5} + 0.000000000000000 s^{4} + 0.000000000000000 s^{3} + 0.000000000000000 s^{2} + s + 1.00000000000000
sage: latex(P.change_ring(RealField(10)))
s^{5} + 0.00 s^{4} + 0.00 s^{3} + 0.00 s^{2} + s + 1.0
sage: latex(P.change_ring(RealField(20)))
s^{5} + 0.00000 s^{4} + 0.00000 s^{3} + 0.00000 s^{2} + s + 1.0000


(The zero is shown, the one is not...)

(2) The precision of the real field did it above.

(3) Depends on the needs, Since we can access the coefficients and the powers of the monomials, we can print our own way, e.g. using a custom function. (Then python could format via %f or %g or %e the coefficients.)

I have two new questions:

1) Why the latex function when operating on ring types/classes behaves differently, printing zeroed terms, then when operating on symbolic types, when it does not print the zeroed terms?

2) Considering the example below, based on my answer, I was expecting, for example, that the numerical approximation of the term of order zero (0.36199913930075) would be returned as 0.362, but the value returned was 0.3617. Why the slight difference?

print(print_poly(e, 14, 10^-10))
print(print_poly(e, 4, 10^-10))


Output

s^6 + 2.5234940770578*s^5 + 4.5714914424575*s^4 + 4.9509592139705*s^3 + 3.7020907647966*s^2 + 1.6452145832311*s + 0.36199913930075
s^6 + 2.522*s^5 + 4.569*s^4 + 4.947*s^3 + 3.699*s^2 + 1.644*s + 0.3617


Thank you!

1

(1) latex is different from type to type, as one should expect. Try latex?? to see how ramified it is the work delegated to subroutines.

(2) The error may be understood from the following:

sage: c = e.coefficients()
sage: x4 = c.n(digits=4)
sage: x4
0.3617
sage: x4 == 0.3617
False
sage: x4.str( base=2 )
'0.010111001001100101'
sage: (0.3617).str( base=2 )
'0.010111001001100001011111000001101111011010010100010010'


where e is as in the answer. See also ??N, where

    sage: x=N(pi, digits=3); x
3.14
sage: y=N(3.14, digits=3); y
3.14
sage: x==y
False
sage: x.str(base=2)
'11.001001000100'
sage: y.str(base=2)
'11.001000111101'


is an other example.

Sort by » oldest newest most voted

Thank you, @dan_fulea!

I ended by taking a different approach. I created two functions, one for cropping the real and/or imaginary parts that are below a threshold, as the Mathematica function, and another that apply this crop function to generate a new polynomial with coefficients approximated to a specified number of digits. This function also converts the real numbers with zero fractional part (e.g. 1.0000) to the equivalent integer, so that the coefficient of any term that is exactly 1, do not show.

e = 1/46656*(9*(36*s^2 - 3*(6*s + 1/tan(55/72))/(2/tan(14/9) - 1/tan(55/72)) + 1)*(4*s^2 + 1) - 7*(4*(9*s^2 + 1)*(6*s + 1/tan(55/72)) + 5*(36*s^2 - 3*(6*s + 1/tan(55/72))/(2/tan(14/9) - 1/tan(55/72)) + 1)/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72))))/(4/(5/(4/tan(29/9) - 1/tan(55/72)) - 1/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72)))))*(36*s^2 + 25) - 11/46656*(4*(4*(9*s^2 + 1)*(6*s + 1/tan(55/72)) + 5*(36*s^2 - 3*(6*s + 1/tan(55/72))/(2/tan(14/9) - 1/tan(55/72)) + 1)/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72))))*(9*s^2 + 4) + 9*(9*(36*s^2 - 3*(6*s + 1/tan(55/72))/(2/tan(14/9) - 1/tan(55/72)) + 1)*(4*s^2 + 1) - 7*(4*(9*s^2 + 1)*(6*s + 1/tan(55/72)) + 5*(36*s^2 - 3*(6*s + 1/tan(55/72))/(2/tan(14/9) - 1/tan(55/72)) + 1)/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72))))/(4/(5/(4/tan(29/9) - 1/tan(55/72)) - 1/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72)))))/(16/(7/(8/(5/tan(295/72) - 1/tan(55/72)) - 1/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72)))) - 7/(4/(5/(4/tan(29/9) - 1/tan(55/72)) - 1/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72))))))/(20/(27/(32/(35/(6/tan(5) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72)))) - 7/(4/(5/(4/tan(29/9) - 1/tan(55/72)) - 1/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72))))) - 9/(16/(7/(8/(5/tan(295/72) - 1/tan(55/72)) - 1/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72)))) - 7/(4/(5/(4/tan(29/9) - 1/tan(55/72)) - 1/(2/tan(14/9) - 1/tan(55/72))) - 5/(8/(3/tan(19/8) - 1/tan(55/72)) - 3/(2/tan(14/9) - 1/tan(55/72))))))
f = (s + 0.267263403945694 + 0.717946300381457*I)*(s + 0.267263403945694 - 0.717946300381457*I)*(s + 0.0662957565747883 + 0.945082838443603*I)*(s + 0.0662957565747883 - 0.945082838443603*I)*(s + 1.58872258534575e-15 + 0.323790242740243*I)*(s + 1.58872258534575e-15 - 0.323790242740243*I)
p = -481858107122483/34956364583988030*(32*s^6 + 48*s^4 + 18*s^2 + 1)/pi + 421794849676389/2806321699992460*(8*s^4 + 8*s^2 + 1)/pi - 3217349902914616/4621771216286985*(2*s^2 + 1)/pi + 732820056818024/435368484578163/pi

def crop(CC_value, RR_threshold):
if abs(CC_value.real()) > RR_threshold and abs(CC_value.imag()) > RR_threshold:
return CC_value
elif abs(CC_value.real()) > RR_threshold and abs(CC_value.imag()) < RR_threshold:
return CC_value.real()
elif abs(CC_value.real()) < RR_threshold and abs(CC_value.imag()) > RR_threshold:
return CC_value.imag()*i
else:
return 0

def print_poly(SymbolicExpression_poly, ZZ_digits, RR_threshold):
coeff = []
for coefficient in SymbolicExpression_poly.coefficients():
coeff.append((Integer(int(coefficient)),coefficient) if frac(coefficient) == 0 else (__crop(coefficient.n(digits=ZZ_digits),RR_threshold),coefficient))
return sum([a*s^b for a,b in coeff])

print(print_poly(e, 3, 10^-10))
print(print_poly(f, 4, 10^-10))
print(print_poly(p, 5, 10^-10))


The output is

s^6 + 2.54*s^5 + 4.59*s^4 + 4.98*s^3 + 3.73*s^2 + 1.66*s + 0.364
s^6 + 0.6671*s^5 + 1.660*s^4 + 0.6275*s^3 + 0.6898*s^2 + 0.05846*s + 0.05523
-0.14041*s^6 + 0.17213*s^4 - 0.13941*s^2 + 0.35765


Surely the code is not optimized and may contain errors, but it worked fine in my tests. So, I will accept my own answer :-/.

more

You should actually accept your own answer by clicking the "accept" button near the top of the answer.

I have accepted it now. I hadn't done it before because I hadn't sufficient "karma" points.