1

I have the following minimal example:

import sympy as sp

eq = sp.S("-p**2*(3*p**2 - 9*p + 7)/(p**2 - 3*p + 3) + q")

# This eventually terminates after a few seconds
sp.solve(eq, dict=True)
# Result: [{q: p**2*(3*p**2 - 9*p + 7)/(p**2 - 3*p + 3)}]

# If I instead write it like this
sp.solve([eq], dict=True)
# it does not terminate in reasonable time (even after hours I do not get a result).

What is the reason for this behaviour?

I would expect that both solve calls should terminate in roughly the same amount of time, as the list only contains just a single element which is identical to the non-iterable solve() call

WaveL
  • 11
  • 3

1 Answers1

3

There are separate codepaths for single equations vs systems of equations.

In any case this equation can be solved much more quickly if you modify the argument somewhat:

In [17]: p, q = symbols('p, q')

In [18]: eq = -p**2*(3*p**2 - 9*p + 7)/(p**2 - 3*p + 3) + q

In [19]: eq
Out[19]: 
   2 ⎛   2          ⎞    
  p ⋅⎝3⋅p  - 9⋅p + 7⎠    
- ─────────────────── + q
       2                 
      p  - 3⋅p + 3       

In [20]: %time solve(eq, q)
CPU times: user 87.1 ms, sys: 0 ns, total: 87.1 ms
Wall time: 87.4 ms
Out[20]: 
⎡ 2 ⎛   2          ⎞⎤
⎢p ⋅⎝3⋅p  - 9⋅p + 7⎠⎥
⎢───────────────────⎥
⎢     2             ⎥
⎣    p  - 3⋅p + 3   ⎦

It is very easy to solve for q. Solving for p in terms of q is more involved. It can be done but requires the quartic formula which gives very large expressions and is usually best avoided:

In [23]: eq2 = numer(together(eq)).expand().collect(p)

In [24]: eq2
Out[24]: 
     4      3    2                      
- 3⋅p  + 9⋅p  + p ⋅(q - 7) - 3⋅p⋅q + 3⋅q

In [26]: len(str(roots(eq2, p)))
Out[26]: 7864

Given a quartic like this if it can be factorised then that is great but this one doesn't factorise and so the quartic formula is needed. Depending on what it is that you want to do with the output of solve it is usually better just to keep this polynomial form as an implicit specification of p rather than try to get an explicit representation of p.

Oscar Benjamin
  • 12,649
  • 1
  • 12
  • 14