Sympy's subs limitations - python

I am working with some long equations but not really complex, and I wanted to use sympy to simplify and "factorize" them. But I have encountered a few problems. Here is a list of some minimal examples:
Problem 1: symmetry
from sympy import *
from __future__ import division
a = symbols('a')
b = symbols('b')
expr = 1/12*b + 1
expr.subs(1/12*b, a)
expr.subs(b*1/12, a)
The first line gives the expected result (ie. a+1) while in the second one there is no substitution.
Problem 2: factorized expressions
Some parts of the expression are factorized and when I expand the expression they get simplified, thus making the substitution impossible. For example
(((x+1)**2-x).expand()).subs(x**2+2*x, y+1)
will give x^2+x+1 and what I am looking for is y+2-x.
Question
Is there a way to solve these problems ? Or maybe I should use another symbolic mathematical tool ? Any suggestions are welcomed.

There is a major gotcha in SymPy, which is that, because of the way Python works, number/number gives a floating point (or does integer division if you use Python 2 and don't from __future__ import division).
In the first case and in your original expression, Python evaluates 1/12*b from left to right. 1/12 is evaluated by Python to give 0.08333333333333333, which is then multiplied by b. In the second case, b*1 is evaluated as b. Then b/12 is evaluated by SymPy (because b is a SymPy object), to give Rational(1, 12)*b.
Due to the inexact nature of floating point numbers, SymPy does not see the float 0.08333333333333333 as equal to the rational 1/12.
There is some more discussion of this issue here. As a workaround, you should avoid direct integer/integer without wrapping it somehow, so that SymPy can create a rational. The following will all create a rational:
b/12
Rational(1, 12)*b
S(1)/12*b
For (((x+1)**2-x).expand()).subs(x**2+2*x, y+1) the issue is that x**2 + 2*x does not appear exactly in the expression, which is x**2 + x + 1. SymPy generally only replaces things that it sees exactly.
It seems you don't mind adding and subtracting an x to make the replacement work. So I would suggest doing instead (((x+1)**2-x).expand()).subs(x**2, y+1 - 2*x). By only substituting a single term (x**2), the substitution will always work, and the 2*x will cancel out to leave whatever x term remains (in this case, -x).

Here's a possible solution to your problems:
from sympy import *
a = symbols('a')
b = symbols('b')
expr = 1 / 12 * b + 1
print(expr.subs((1 / 12) * b, a))
print(expr.subs(b * (1 / 12), a))
x = symbols('x')
y = symbols('y')
expr = ((x + 1)**2 - x).expand()
print(expr.subs(x**2 + x, y - x + 1))

Regarding problem 1, note that 1/12*b and b*1/12 are not the same thing in sympy. The first is a floating number mutliplied by a symbol, whereas the second is an exact symbolic expression (you can check it out by a simple print statement). Since expr contains 1/12*b, it is not surprising that the second subs does not work.
Regarding problem 2, the subs rule you provide is ambiguous. In particular the substitution rule implies that equation x**2+2*x==y+1. However, this equation has many interpretations, e.g,
x**2 == y + 1 - 2*x (this is the one you consider),
x**2 + x == y + 1 - x,
x == (y + 1 - x**2)/2,
For this reason, I consider sympy refusing to perform a substitution is actually a correct approach.
If it is the first interpretation you want, it is better to explicitly provide it in the subs rule, i.e.,
(((x+1)**2-x).expand()).subs(x**2, -2*x + y + 1)
-x + y + 2

Related

Solving algebra equations in python

I'm new to programming and I'm having a hard time solving this equation using Python.
I would like for the system to give me the value for X.
((X-5)/(2) + (X/4) + (X-12)/(3))
Sympy looks promising, especially section 3.2.4, "Equation Solving".
Assuming your right hand side of equation is some given value or expression:
import sympy as sym
x = sym.Symbol("x")
RHS = 13
LHS = (x - 5)/2 + x/4 + (x - 12)/3
eqn = LHS - RHS
soln = sym.solve(eqn, x)
print(soln)
This yields the solution x = 18. Replace RHS with your own value or expression. If there is nothing, simply put RHS to be 0; which gives the solution x = 6.
If you want to solve your equation step by step, you are still going to need SymPy:
https://docs.sympy.org/latest/install.html

Simplifying large coefficients in SymPy

I use SymPy for symbolic calculations in Python and get e.g. an expression like
Z = 2.02416176758139e+229 / (2.42579446609411e+232 * s + 9.8784848517664e+231)
Is there a function in SymPy (e.g. in sympy.simplify?) to get something like
Z = 2.024 / (2425.8 * s + 987.8)
The intent of your request can be met by allowing cancellation:
>>> 1/(1/Z)
1/(1198.41926912424*second + 488.028427864731)
or
>>> factor(Z)
0.00083443251102827/(1.0*second + 0.407226786516346)
But there is no built-in method of accessing the exponent in an exponential form.

Finding roots of a non linear expression when multiplied with a linear expression

Here is a simple polynomial equation:
b^2 + 2b + 1 = 0
I could easily solve this as:
import numpy as np
from scipy.optimize import fsolve
eq = lambda b : np.power(b,2) + 2*b + 1
fsolve(eq, np.linspace(0,1,2))
Similarly I could solve any equation, that has finite number of terms. But how do I solve an equation with infinite number of terms which is given as :
The above equation could be written as :
5 = (1 - l) * (5.5 + 4.0*l + 4*l^2 + 6*l^3 + 5*l^4 + 5*l^5 + 5*l^6 + 5*l^7 + 5*l^8 + 5*l^9 + 5*l^10 )
when n goes from 1 to 10. But I want to solve this for sufficiently large value of n such that LHS ~= RHS.
I know the values of LHS and G1 -> Ginf but cannot understand how could I compute the value of lambda here.
I tried looking at numpy polynomial functions but could not find a function that is relevant here.
The following glosses over the fact that I do not 100% understand the coefficient notation G_t:t+n (what kind of dependency is that supposed to indicate exactly?)
Obviously, the solution will depend on coefficients. If as your example suggests, the coefficients are all equal above some index n_0 then your r.h.s. expression is a telescoping sum and equal to G_t:1 + sum_1^n_0 [G_t:n - G_t:n+1] l^n`. Be sure to note that this sum is finite, so you know how to proceed from here.
One caveat: you must have |l| < 1 otherwise the series does not converge and the r.h.s. is undefined, although some kind of continuation argument may be possible.

Obtaining coefficients of complex expressions in sympy

I have a relatively simple complex sympy expression which one can easily read the coefficients off of the variables. However coeff function does not appear to be working correctly
import sympy as sp
a,b = sp.symbols("a, b")
expr = 2640.0*a - 4.5*(1 + 1j)*(264.0*a + 264.0*b) - 4.5*(+1 - 1j)*(264.0*a + 264.0*b)
print(expr.coeff(a))
> 2640.00000000000
print(sp.simplify(expr))
> 264.0*a - 2376.0*b
I would expect the output of expr.coeff(a) to return 264.0 but it clearly isnt? Any help is appreciated.
coeff gives coefficients of the expression at the top level. If you use expand before looking for the coefficient then you will get the mathematical (not expression-dependent-literal) coefficient. If you know the expression is linear in the symbol of interest, you could also differentiate once:
>>> expr.diff(a)
264.000000000000
>>> expr.expand().coeff(a)
264.000000000000
Poly automatically expands expressions and allows queries for monomials, too:
>>> Poly(expr).coeff_monomial(a)
264.000000000000
Your first expression has 2640.0 as the coefficient of a. As you can see, the coefficient becomes zero only after simplifying it. Indeed, if you print the coefficient after simplifying the expression, you get 264.0
import sympy as sp
a,b = sp.symbols("a, b")
expr = 2640.0*a - 4.5*(1 + 1j)*(264.0*a + 264.0*b) - 4.5*(+1 - 1j)*(264.0*a + 264.0*b)
print(expr.coeff(a))
# 2640.00000000000
print(sp.simplify(expr))
# 264.0*a - 2376.0*b
print(sp.simplify(expr).coeff(a)) # <--- Simplified expression
# 264.000000000000

How do I replace floats with rationals in a sympy expression?

I have an expression from a sympy calculation:
sqrt(pi)*(0.333333333333333*a + 0.333333333333333*b - 2.66666666666667*c**2)
where a,b,c are symbols, and would like to parse it so that the floats are replaced with rationals like in
sqrt(pi)*(1/3*a + 1/3*b - 8/3*c**2)
I know how to do one by hand,
In[24] Rational(str(0.333333333333333)).limit_denominator(1000)
Out[24]: 1/3
but do not quite know how to go about parsing the atoms and picking only the ones that are floats, and substituting back the rational number approximation.
What is the smartest way of doing these substitutions in the expression?
Use nsimplify:
>>> print(nsimplify(sqrt(pi)*(0.333333333333333*a + 0.333333333333333*b - 2.66666666666667*c**2)))
sqrt(pi)*(a/3 + b/3 - 8*c**2/3)
After a bit of fiddling, I think I have found a way to do it, but I am not sure that it will cover all the corner cases. At any rate here it is. Any suggestions for improvement?
import sympy
def rationalize_coeffs(expr):
for i in expr.atoms(sympy.Float):
r = sympy.Rational(str(i)).limit_denominator(1000)
expr = expr.subs(i, r)
return expr
if __name__=='__main__':
# given a sympy expression expr
x,y,z = sympy.symbols('x y z')
# expr_orig = 2/57.*x + 3./4.*y + 3./4.*z
expr = 0.0350877192982456*x + 0.75*y + 0.75*z
print rationalize_coeffs(expr)

Categories