sort expression for power series

145 views
Skip to first unread message

Thomas Ligon

unread,
Mar 5, 2023, 3:31:58 AM3/5/23
to sympy
I have a lot of power series that look like this (but going up to t**12):
exp1 = -2867.70035529489*t**5 + 147.938724526848*t**3 - 2.56500070531002*t
While trying to gain some insight into the mathematics that creates them, I want to print a shorter version, such as
- 2.565 t + 147.93872 t**3- 2867.70036 t**5
but the best I have achieved is
- 2867.70036 t^{5} + \left(147.93872 t^{3} - 2.565 t\right)
Rounding the numbers was easy, but I would prefer to round to 5 digits total, not 5 digits after the decimal point. I was able to convert the expression to a list and sort the list, but when I converted the list back to an expression, I didn't succeed in producing the order I wanted.
Here is the code:
global t
t = symbols('t')
def sortSeries(valIn):
    valOut = valIn
    for num in (valOut.atoms(sympy.Float)):
        valOut = valOut.xreplace({num: 1}) # remove coefficient
    if valOut == 1:
        return 1 # set t**0 to 1
    else:
        valOut = valOut.subs(t, 2) # change t**n to 2**n, which is a sortable number
    return valOut

def printSeries(expIn):
    expOut = expIn
    expOut = collect(expOut, t)
    for num in (expOut.atoms(Number)):
        expOut = expOut.xreplace({num: num.round(5)})
    expList = expOut.as_ordered_terms() # convert expression to list
    expList.sort(key=sortSeries) # sort list by values of t**n
    for ind in range(0, len(expList)): # convert list to expression
        term = expList[ind]
        if ind == 0:
            exp = term
        else:
            # exp = exp + term
            exp = Add(exp, term, evaluate=False)
            #exp = Add(term, exp, evaluate=False)
    expOut = exp
    print(latex(expOut))
    #return expOut

def test():
    #exp1 = 6.14979599379575*t**3 - 1.69088473154467*t
    #exp2 = 0.331732200302894 - 2.35512815419867*t**2
    exp1 = -2867.70035529489*t**5 + 147.938724526848*t**3 - 2.56500070531002*t
    exp2 = 701.066644698213*t**4 - 23.5908063706474*t**2 + 0.13771861708229
    printSeries(exp1)
    printSeries(exp2)
    print('end of test')

Md Affan

unread,
Mar 5, 2023, 2:14:22 PM3/5/23
to sympy
Thank you for sharing your code and explaining your challenge. It seems like you have put a lot of effort into trying to print a shorter version of your power series. Your approach of converting the expression to a list and sorting it by the values of t**n is clever. 
Have you considered using the built-in round function to round the coefficients to 5 significant digits instead of 5 decimal places?
That way, you could achieve the desired rounding without losing significant digits. Try it i think this should work

Oscar Benjamin

unread,
Mar 5, 2023, 2:53:32 PM3/5/23
to sy...@googlegroups.com
On Sun, 5 Mar 2023 at 08:32, Thomas Ligon <thomas...@gmail.com> wrote:
>
> I have a lot of power series that look like this (but going up to t**12):
> exp1 = -2867.70035529489*t**5 + 147.938724526848*t**3 - 2.56500070531002*t
> While trying to gain some insight into the mathematics that creates them, I want to print a shorter version, such as
> - 2.565 t + 147.93872 t**3- 2867.70036 t**5
> but the best I have achieved is
> - 2867.70036 t^{5} + \left(147.93872 t^{3} - 2.565 t\right)
> Rounding the numbers was easy, but I would prefer to round to 5 digits total, not 5 digits after the decimal point. I was able to convert the expression to a list and sort the list, but when I converted the list back to an expression, I didn't succeed in producing the order I wanted.

You can get 5 digits by using exp1.evalf(5).

It is surprisingly difficult to control the order of terms in sympy's
printing functionality but it is possible:

In [1]: exp1 = -2867.70035529489*t**5 + 147.938724526848*t**3 -
2.56500070531002*t

In [2]: doprint = StrPrinter(settings={'order':'none'}).doprint

In [3]: series = Add(*sorted(exp1.evalf(5).args, key=degree), evaluate=False)

In [4]: print(doprint(series))
-2.565*t + 147.94*t**3 - 2867.7*t**5

There are two steps to controlling the order:

- Ordering the terms in the expression itself (the series variable above).
- Getting the printer to respect that ordering ('order':'none').

It should be possible to set order=None with init_printing and that
does work for the pretty printer but not for string printing (i.e.
print(expr) or str(expr)).

--
Oscar

Thomas Ligon

unread,
Mar 6, 2023, 9:53:49 AM3/6/23
to sympy
Thanks! That's a big help, but I am not finished yet. Here is where I am now:

Step 1: Use your code.
name 'StrPrinter' is not defined
changed StrPrinter to sympy.printing.str.StrPrinter
https://docs.sympy.org/latest/modules/printing.html
name 'degree' is not defined
I tried key=sympy.printing.str.degree, but that gave me
module 'sympy.printing.str' has no attribute 'degree'
Then I tried key=sympy.degree and that worked.
Now the two series are being printed in the desired order.

Step 2: Use latex
I tried changing print(doprint(series)) to print(latex(doprint(series)))
and the result is
\mathtt{\text{-2.565*t + 147.94*t**3 - 2867.7*t**5}}
which doesn't format correctly in Word's equation editor or in www.overleaf.com,
where it formats correctly, but issues an error
Undefined control sequence.
LaTeX Error: \mathtt allowed only in math mode.
I tried changing the string to
-2.565*t + 147.94*t**3 - 2867.7*t**5
and this formats correctly in Word and in Overleaf.
Then I tried
doprint = sympy.printing.latex.LatexPrinter(settings={'order':'none'}).doprint
but that throws an exception
'_PrintFunction' object has no attribute 'LatexPrinter'
even though the class
class sympy.printing.latex.LatexPrinter(settings=None)
is defined in
https://docs.sympy.org/latest/modules/printing.html#module-sympy.printing.latex

Still to do:
Learn how this works.
Learn why evalf(5) works. The documentation at
https://docs.sympy.org/latest/modules/evalf.html
doesn't make it clear to me what the possible arguments are and what they mean.
Find documentation for sorted and key=degree
I searched for sympy degree and found polynomials and angles, but still don't understand this one.


Oscar Benjamin

unread,
Mar 6, 2023, 11:45:51 AM3/6/23
to sy...@googlegroups.com
The degree function can be imported like

from sympy import degree

It is used to get the degree of a polynomial expression.
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/d48be44a-e979-41fd-aa37-1fabadde9b7an%40googlegroups.com.

Chris Smith

unread,
Mar 6, 2023, 10:08:45 PM3/6/23
to sympy
I thought I would try this in ChatGPT since one of the answers sounded gpt-ish. Quite surprised by the results in terms of level of detail about what is going on in the routine that it presented. I marked the line that was needed to make the function of the gpt-answer work:

    def printSeries(expIn):
     expOut = expIn
     expOut = collect(expOut, t)
     expList = expOut.as_ordered_terms() # convert expression to list
     expList = [i.as_coeff_Mul() for i in expList]  # <---- needed this line
     expList = [(t, round(coeff, 5)) for coeff, t in expList] # round coefficients to 5 digits
     expList.sort(key=lambda term: term[0].as_poly().degree(), reverse=True) # sort terms by degree of t
     expStr = ' '.join([f"{coeff:.5g} {t}" for t, coeff in expList]) # format terms as strings
     print(expStr)

/c

Chris Smith

unread,
Mar 6, 2023, 10:17:05 PM3/6/23
to sympy
When I pointed out " This gives an error. You have to split the ordered terms into coefficients and factors first with `as_coeff_Mul()`" it identified why the previous answer was incorrect and gave this modified line:

expList = [(round(coeff, 5), t.as_poly()) for coeff, t in (term.as_coeff_Mul() for term in expList)]  

Thomas Ligon

unread,
Mar 7, 2023, 4:04:55 PM3/7/23
to sympy
Thanks again to everyone who helped. The code is now working well and has been reduced to:
def printSeries(expIn):
    from sympy import degree
    expOut = Add(*sorted(expIn.evalf(5).args, key=sympy.degree), evaluate=False)
    print(latex(expOut, order='none'))


Based on the post from Chris Smith, I tried the chatGPT. Specifically, I used the newest version of Bing, which in turn used a new version of chatGPT.

Here is a problem that I had

https://docs.sympy.org/latest/modules/printing.html#module-sympy.printing.latex
shows
sympy.printing.latex.latex
but
doprintLatex = sympy.printing.latex.latex(settings={'order':'None'}).doprint
raises
AttributeError: '_PrintFunction' object has no attribute 'latex'
Also
doprintLatex = sympy.printing.latex.LatexPrinter(settings={'order':'None'}).doprint
raises
AttributeError: '_PrintFunction' object has no attribute 'LatexPrinter'

I gave that to the Bing chat that uses chatGPT and got this suggestion:

I see. It seems you are trying to create a custom instance of LatexPrinter with your own settings. According to one of the web search results1, you need to import LatexPrinter explicitly from sympy.printing.latex before using it. For example:

from sympy import *
from sympy.printing.latex import LatexPrinter
my_printer = LatexPrinter(settings={"mul_symbol": "dot"})
var('x y z')
print(my_printer.doprint(x*y+y*z))

This should output:

x \cdot y + y \cdot z
Alternatively, you can use the latex() function with the same settings as an argument. For example:

from sympy import *
var('x y z')
print(latex(x*y+y*z, mul_symbol="dot"))


The first suggestion reduces my code to
def printSeries(expIn):
    from sympy import degree #, StrPrinter #, LatexPrinter
    from sympy.printing.str import StrPrinter
    from sympy.printing.latex import LatexPrinter
    doprintStr = StrPrinter(settings={'order':'none'}).doprint
    doprintLatex = LatexPrinter(settings={'order':'none'}).doprint
    expOut = Add(*sorted(expIn.evalf(5).args, key=sympy.degree), evaluate=False)
    print(doprintStr(expOut))
    print(doprintLatex(expOut))


But, I only wanted to print LaTeX, so the second suggestion reduces my code to
def printSeries(expIn):
    from sympy import degree
    expOut = Add(*sorted(expIn.evalf(5).args, key=sympy.degree), evaluate=False)
    print(latex(expOut, order='none'))
Reply all
Reply to author
Forward
0 new messages