1

The Goal is to format a polynomial with more than 6 parameters into a plot title. Here is my polynomial parameter to string expression function, inspired by this answer, followed by sym.latex():

def func(p_list):
    str_expr = ""       
    for i in range(len(p_list)-1,-1,-1):
        if (i%2 == 0 and i !=len(p_list)):
            str_expr =str_expr + " \n "
        if p_list[i]>0:
            sign = " +"
        else:
            sign = ""
        if i > 1:
            str_expr = str_expr+" + %s*x**%s"%(p_list[i],i)
        if i == 1:
            str_expr = str_expr+" + %s*x"%(p_list[i])
        if i == 0:
            str_expr = str_expr+sign+" %s"%(p_list[i])
        print("str_expr",str_expr)
        return sym.sympify(str_expr)
                    
popt = [-2,1,1] # some toy data
tex = sym.latex(func(popt))
print("tex",tex)

Outputs:

str_expr
  + -1*x**2 + 1*x
  -2
tex - x^{2} + x - 2 

in str_expr the line breaks from \n are visible, yet in the sympy.latex output the are gone.

How to propagate this linebreak?

Edit: I took @ wsdookadr answer and modified it, so that plt.title takes the result of the function as text the argument


def tex_multiline_poly(e, chunk_size=2, separator="\n"):
    tex = ""
    # split into monomials
    print("reversed(e.args)",reversed(e.args))
   
    mono = list(e.args)
    print("mono",mono)
    mono.reverse()
    print("mono",mono)
    # we're going split the list of monomials into chunks of chunk_size
    # serialize each chunk, and insert separators between the chunks
    for i in range(0,len(mono),chunk_size):
        chunk = mono[i:i + chunk_size]
        print("sum(chunk)",sum(chunk))
        print("sym.latex(sum(chunk))",sym.latex(sum(chunk)))
        if i == 0:
            tex += r'$f(x)= %s$'%(sym.latex(sum(chunk)))+separator
        else:
            tex += '$%s$'%(sym.latex(sum(chunk))) + separator
    return tex

popt = est.params
x = sym.symbols('x')
p = sym.Poly.from_list(reversed(popt),gens=x)
tex = tex_multiline_poly(p.as_expr(),chunk_size=2)
plt.title(text=tex)
wsdookadr
  • 2,584
  • 1
  • 21
  • 44
Alexander Vocaet
  • 188
  • 1
  • 10

1 Answers1

1

In your code, you're inserting a linebreak for every even-power monomial, except for the last one.

   if (i%2 == 0 and i !=len(p_list)):
       str_expr =str_expr + " \n "

Since you are just building a polynomial from a list of coefficients, your code can be simplified.

Generally what we want is to build/transform/handle things symbolically, and only at the end serialize them and print the result in some specific format

import sympy as sym

x = sym.symbols('x')

def func(p_list):
    expr = 0
    for i in range(len(p_list)-1,-1,-1):
        expr += p_list[i] * (x ** i)
    return sym.sympify(expr)

popt = [-2,1,1]
p = func(popt)
p_tex = sym.latex(p)
p_str = str(p)
print("str:", p_str)
print("tex:", p_tex)

Output:

str: x**2 + x - 2
tex: x^{2} + x - 2

We could simplify this even further by using SymPy's built-in functions to build the poly from a list of coefficients:

import sympy as sym
from sympy import symbols

popt = [-2,1,1]

x = symbols('x')
p = sym.Poly.from_list(reversed(popt),gens=x)
p_tex = sym.latex(p.as_expr())
p_str = str(p.as_expr())
print("str:", p_str)
print("tex:", p_tex)

Output:

str: x**2 + x - 2
tex: x^{2} + x - 2

Does the output look like what you would expect?

UPDATE:

After learning more about the use-case, here's a version that inserts separators every N=2 monomials in the latex form of your expression.

import sympy as sym
from sympy import symbols

popt = [-2,1,1]

x = symbols('x')
p = sym.Poly.from_list(reversed(popt),gens=x)

def tex_multiline_poly(e, chunk_size=2, separator="\n"):
    tex = ""
    # split into monomials
    mono = list(reversed(e.args))
    # we're going split the list of monomials into chunks of chunk_size
    # serialize each chunk, and insert separators between the chunks
    for i in range(0,len(mono),chunk_size):
        chunk = mono[i:i + chunk_size]
        tex += sym.latex(sum(chunk)) + separator
    return tex

p_tex = tex_multiline_poly(p.as_expr(),chunk_size=2)
p_str = str(p.as_expr())

print("str:",p_str)
print("tex:",p_tex)

Output:

str: x**2 + x - 2
tex: x^{2} + x
-2

Edit: wrong edit

Alexander Vocaet
  • 188
  • 1
  • 10
wsdookadr
  • 2,584
  • 1
  • 21
  • 44
  • your simplifications are really good ! thanks, but this is not what I expectet. See edit – Alexander Vocaet Dec 22 '20 at 11:11
  • 1
    @AlexanderVocaet are you trying to break the expression into multiple lines so it can fit some specific width ? – wsdookadr Dec 22 '20 at 11:24
  • exactly thats what i want – Alexander Vocaet Dec 22 '20 at 11:50
  • the function `sym.Poly.from_list(reversed(popt),gens=x)` changes the order of the polynomials, is there a way to keep the order? – Alexander Vocaet Dec 22 '20 at 12:11
  • 1
    The convention is for the order of coefficients passed to `from_list` to be from the largest monomial, to the smallest monomial. – wsdookadr Dec 22 '20 at 12:21
  • 1
    The `reversed` is there, so the same ordering can be replicated as in your initial example. You can alter [the ordering of monomials](https://en.wikipedia.org/wiki/Monomial_order#Examples) by using either `lex` or `old` or `grevlex` [as described here](https://stackoverflow.com/questions/56652669/printing-a-polynomial-so-that-the-powers-of-x-or-s-are-in-ascending-or-descendin#comment99910480_56652669). It looks like `sym.init_printing(order='old')` works for this, and reverses the order of the monomials. – wsdookadr Dec 22 '20 at 12:30