Sympy : simplification with expression substitution - python

I have several expressions involving the norm or norm squared of a vector u. I'd like to simplify these expressions by substituting a known value for the norm of u. However, it seems that obvious expressions involving even simple multiples of the norm are not simplified.
As an example, this code does what I would expect :
import sympy as sp
u1,u2,u3 = sp.symbols('u_1, u_2, u_3',real=True,positive=True)
utu = u1**2 + u2**2 + u3**2
print("Ex. 1")
print(utu.subs(utu,1))
This produces the expected output
Ex. 1
1
However, 2*utu does not simplify in the way I would expect :
print("Ex 2")
print((2*utu).subs(utu,1))
Ex 2
2*u_1**2 + 2*u_2**2 + 2*u_3**2
I can explicitly force the substitution with this :
print("Ex 3")
print((2*utu).subs(2*utu,2))
which produces the expected output :
Ex 3
2
Ideally, I'd like to substitute under a norm function, but the run into the same issue.
u = sp.Matrix(3, 1, [u1,u2,u3])
print("Ex 4")
print(u.norm().subs(utu,1))
print("Ex 5")
print((2*u).norm().subs(utu,1))
print("Ex 6")
print((2*u).norm().subs(4*utu,4))
which produces
Ex 4
1
Ex 5
sqrt(4*u_1**2 + 4*u_2**2 + 4*u_3**2)
Ex 6
2
Are there tricks I am missing that will catch these obvious (to me at least - maybe not to Sympy?) simplifications? I've tried factor and expand, without much luck.

Let's analyze this expression:
expr = 2*utu
# out: 2*u_1**2 + 2*u_2**2 + 2*u_3**2
The multiplication has been evaluated. This is SymPy's default behavior: it evaluates things. We can work with the expression manipulation functions to achieve our goal.
For example:
expr = collect_const(expr)
# out: 2*(u_1**2 + u_2**2 + u_3**2)
expr.subs(utu, 1)
# out: 2
Another example:
expr = (2 * u).norm()
# out: sqrt(4*u_1**2 + 4*u_2**2 + 4*u_3**2)
expr = expr.simplify() # Note that expr.factor() produces the same result with this expression
# out: 2*sqrt(u_1**2 + u_2**2 + u_3**2)
expr.subs(utu, 1)
# out: 2
If you play (and modify) with these examples, you will realize that the same result can be achieved with different functions (factor, simplify, collect, collect_const, ...), but even one little change in the expression might prevent one function from "doing its work", while others might be able to. Expression manipulation is kind of an art that one should practice (a lot).
For completeness, I'm going to show you UnevaluatedExpr, which allows a particular expression to remain unevaluated during expression manipulation, though it might not always be the best choice:
n = UnevaluatedExpr(utu)
# out: u_1**2 + u_2**2 + u_3**2
expr = 4 * n
# out: 4*(u_1**2 + u_2**2 + u_3**2)
Note that SymPy didn't proceed with the full evaluation. Now:
expr.subs(utu, 1)
# out: 4*1
Why is there a 4*1 instead of 4? The 1 refers to the UnevaluateExpr object that we created earlier: to evaluate it we can use the doit() method:
expr.subs(utu, 1).doit()
# 4
Keep in mind that while using UnevaluateExpr, the expression becomes non-commutative (I think it's a bug with SymPy), which will prevent other functions to produce the expected results.

Substituting compound expressions is problematic. For the most part you should only expect subs to work if the expression to be replaced is known to always appear literally as part of the expression tree that you are substituting into. When possible then it is better to rearrange for a single symbol like:
In [10]: utu
Out[10]:
2 2 2
u₁ + u₂ + u₃
In [11]: (2*utu).subs(u1**2, 1 - u2**2 - u3**2)
Out[11]: 2
Even here we are substituting for a power of a symbol (u1**2) which is potentially fragile if we can't be sure that exactly that power will always appear in the expression. More generally there are functions that can simplify expressions based on knowing some polynomial relation e.g. ratsimpmodprime:
In [16]: e = (1 - u1**2) / (u1**2 + u2**2 + u3**2)
In [17]: e
Out[17]:
2
1 - u₁
───────────────
2 2 2
u₁ + u₂ + u₃
In [18]: ratsimpmodprime(e, [u1**2 + u2**2 + u3**2 - 1])
Out[18]:
2 2
u₂ + u₃
Other possibilities could be using resultants or Groebner bases to do similar things. Note that u.norm() has a square root which is symbolically awkward so it is better to work with the square of the norm (same as when working on pen and paper):
In [20]: ratsimpmodprime((2*u).norm()**2, [u1**2 + u2**2 + u3**2 - 1])
Out[20]: 4
Also if you just want a more powerful version of subs then you can use replace but with patterns:
In [21]: a = Wild('a')
In [22]: p = a*u1**2 + a*u2**2 + a*u3**2
In [23]: (2*utu).replace(p, a)
Out[23]: 2
In [24]: (2*u).norm().replace(p, a)
Out[24]: 2

Both solid answers already. If you have an arbitrary expression that you expect to be a factor in another, factor_terms is what I try first to make that factor appear. It will collect common factors without doing factoring. But if this doesn't work and you know you have a factor, div is a nice way to check and see the expression with the factor removed:
>>> expr = 2*(x + y)
>>> factor_terms(expr)
2*(x + y)
>>> e2 = expand(expr*(x -y)) # 2*x**2 - y**2
>>> factor_terms(e2)
2*(x**2 - y**2)
>>> div(_,-x-y)
(-2*x + 2*y, 0)
>>> _[0]*z # if you wanted to replace factor -x-y with z
z*(-2*x + 2*y)

Related

How to use sympy to convert all standalone integers in an expression that aren't exponents to 1

Is there a way to use sympy to find/replace all standalone integers (that aren't exponents) to 1.
For example, converting the following:
F(x) = (2/x^2) + (3/x^3) + 4
To:
F(x) = (1/x^2) + (1/x^3) + 1
I've searched extensively on stackoverflow for sympy expression.match/replace/find solutions, and have tried using a Wildcard symbol to find and replace all numbers in the expression but I keep running into the issue of matching and replacing the exponents (2 and 3 in this example) as well as they are also considered numbers.
Is there a simple (pythonic) way to achieve the above?
Thanks!
setdefault used with replace is a nice way to go. The single expression below has 3 steps:
mask off powers and record
change Rationals to 1 (to handle integers in numer or denom)
restore powers
>>> from sympy.abc import x
>>> from sympy import Dummy
>>> eq = (2/x**2) + (3/x**3) + 4 + 1/x/8
>>> reps = {}
>>> eq = eq.replace(lambda x: x.is_Pow, lambda x: reps.setdefault(x, Dummy())
).replace(lambda x: x.is_Rational, lambda x: 1
).xreplace({v:k for k,v in reps.items()})
1 + 1/x + 1/x**2 + 1/x**3
You can write a function that will recurse into your expression. For any expression expr, expr.args will give you the components of that expression. expr.is_constant() will tell you if it's a constant. expr.is_Pow will tell you if it's an exponential expression, so you can choose not to drill down into these expressions.
import sympy
def get_constants(expr):
c = set()
for x in expr.args:
if x.is_constant(): c |= {x}
if not x.is_Pow:
c |= get_constants(x)
return c
Now, you can get all the constants in said expression, and replace each of these constants using expr.replace().
def replace_constants(expr, repl):
for const in get_constants(expr):
expr = expr.replace(const, repl)
return expr
With your example expression, we get:
x = sympy.symbols('x')
F = 2/x**2 + 3/x**3 + 4
G = replace_constants(F, 1)
print(F) # 4 + 2/x**2 + 3/x**3
print(G) # 1 + x**(-2) + x**(-3)

Convert sympy to numpy

I have two NumPy array (two variables), which contains complex numbers. Since they have been defined in NumPy, so the complex notation is denoted by "j".
for i in range(0, 8):
cg1 = np.array(eigVal1[i])
det_eval = eval(det)
AA = det_eval[0:2,0:2]
bb = det_eval[0:2,2] * (-1)
roots = np.append(roots, solve(AA, bb))
a = np.append(a, roots[i])
b = np.append(b, roots[i+1])
Output:
a = array([-9.03839731e-04+0.00091541j, 3.02435614e-07-0.00043776j,
-9.03839731e-04-0.00091541j, 3.02435614e-07+0.00043776j,
9.03812649e-04+0.00092323j, 4.17553402e-07+0.00043764j,
9.03812649e-04-0.00092323j, 4.17553402e-07-0.00043764j])
b = array([ 3.02435614e-07-0.00043776j, -9.03839731e-04-0.00091541j,
3.02435614e-07+0.00043776j, 9.03812649e-04+0.00092323j,
4.17553402e-07+0.00043764j, 9.03812649e-04-0.00092323j,
4.17553402e-07-0.00043764j, -5.53769989e-05-0.00243369j])
I also have a long equation which some variables have been defined symbolic (y).
u_n = A0*y**(1322.5696672125 + 1317.38942049453*I) + A1*y**(1322.5696672125 - 1317.38942049453*I) + A2*y**(-1322.5696672125 + 1317.38942049453*I) + A3*y**(-1322.5696672125 - 1317.38942049453*I) + ..
My problem is when I want to substitute the two variables (a and b) into the equation, all complex numbers change to "I" and it makes the equation more complex, because I am not able to simplify the equation further.
Is there any solution to convert "I" to "j" in sympy.
for i in range(0, 8):
u_n = u_n.subs(A[i], (a[i] * C[i]))
The result is:
u_n = C0*y**(1322.5696672125 + 1317.38942049453*I)*(-0.000903839731101097 + 0.000915407724097998*I) + C1*y**(1322.5696672125 - 1317.38942049453*I)*(3.02435613673241e-7 - 0.000437760318205723*I) +..
As you see I can not simplify it further, even if I use simplify(u_n). However, in numpy for example,(2+3j)(5+6j) will be reduced to (-8+27j), but when a symbolic notation comes to my equation it won't be simplified further. y**(2+3j)(5+6j) -->> y**(2+3I)(5+6*I).
I would like to have y**(-8+27j) which y is symbolic.
I would appreciate it if someone help me with that.
From you last paragraph:
The python expression:
In [38]: (2+3j)*(5+6j)
Out[38]: (-8+27j)
sympy: (y is a sympy symbol):
In [39]: y**(2+3j)*(5+6j)
Out[39]:
With corrective () to group the multiply before the power:
In [40]: y**((2+3j)*(5+6j))
Out[40]:
Even in plain python the operator order matters:
In [44]: 1**(2+3j)*(5+6j)
Out[44]: (5+6j)
In [45]: 1**((2+3j)*(5+6j))
Out[45]: (1+0j)

How to order terms in polynomial expression (sympy, python) according to increasing degree (univariate case)?

In sympy (python) it seems that, by default, terms in univarate polynomials are ordered according to decreasing degrees: highest degree first, then second to highest, and so on. So, for example, a polynomial like
x + 1 + x^3 + 3x^6
will be printed out as 3x^6 + x^3 + x + 1.
I would like to reverse this order of polynomial terms in sympy to be increasing in the degrees. For the same example, the print-out should read 1 + x + x^3 + 3x^6. A solution that globally changes some parameter in program preamble is preferred but other options are also welcome.
Here is an MWE to play around with. It is different from the actual program I am working with. One part of the actual program (not the MWE) is printing out a list of recursively defined polynomials, e.g., P_n(x) = P_(n-1)(x) + a_n * x^n. It is easier for me to compare them when they are ordered by increasing degree. This is the motivation to change the order; doing it globally would probably just keep the code more readable (aesthetically pleasing). But the MWE is just for the same simple polynomial given in example above.
import sympy as sym
from sympy import *
x = sym.Symbol('x')
polynomial = x + 1 + x**3 + 3*x**6
print(polynomial)
Output of MWE:
>>> 3*x**6 + x**3 + x + 1
Desired output for MWE:
>>> 1 + x + x**3 + 3*x**6
You can get the leading term using sympy.polys.polytools.LT:
LT(3x ** 6 + x ** 3 + x + 1) == 3x**6
So at least you can churn out terms recursively and print it in your own way.
Unfortunately I’ve been trying to find some way to print the terms in some fix order for a long while and find no solution better than this
It's seems that there isn't an explicit way to do that and I found this approach to the problem:
to modify the print-representation of the object you can subclass its type and override the corresponding printing method (for LaTeX, MathML, ...) see documentation.
In this case _sympystr is used to "generates readable representations of SymPy expressions."
Here a basic implementation:
from sympy import Poly, symbols, latex
class UPoly(Poly):
"""Modified univariative polynomial"""
def _sympystr(self, printer) -> str:
"""increasing order of powers"""
if self.is_multivariate: # or: not self.is_univariate
raise Exception('Error, Polynomial is not univariative')
x = next(iter(expr.free_symbols))
poly_print = ""
for deg, coef in sorted(self.terms()):
term = coef * x**deg[0]
if coef.is_negative:
term = -term # fix sign
poly_print += " - "
else:
poly_print += " + "
poly_print += printer._print(term)
return poly_print.lstrip(" +-")
def _latex(self, printer):
return latex(self._sympystr(printer)) # keep the order
x = symbols('x')
expr = 2*x + 6 - x**5
up = UPoly(expr)
print(up)
#6 + 2*x - x**5
print(latex(up))
#6 + 2 x - x^{5}

SymPy does not recognize equal values

Simple calculations in SymPy quickly create unwieldy results like the three should_be values below.
Comparisons to the correct values give False (although math.isclose gives True).
from sympy import sqrt
phi = (1 + sqrt(5)) / 2
should_be_phi = -(1/2 + sqrt(5)/2)**2 + (1/2 + sqrt(5)/2)**3
should_be_half = -sqrt(5)/8 + 1/8 + (1/2 + sqrt(5)/2)**2/4
should_be_one = -sqrt(5)/4 + 1/4 + (1/2 + sqrt(5)/2)**2/2
print(should_be_phi == phi, should_be_half == 1/2, should_be_one == 1)
These are the same formulas formatted by Wolfram Alpha:
phi:
half:
one:
should_be_phi was created as phi**3 - phi**2 btw.
Currently I always copy these monstrosities to Wolfram Alpha to get decent formulas and to remove duplicates.
Do you also get False for each comparison? I use Python 3.6.8 and SymPy 1.4.
Is there a way do do symbolic calculations in Python that actually works?
SymPy seems to be unable to do the things it is supposedly made for.
I presume that what you want is for those expressions to be simplified so just use the simplify function:
In [6]: from sympy import *
In [7]: phi = (1 + sqrt(5)) / 2
In [8]: should_be_phi = -(S(1)/2 + sqrt(5)/2)**2 + (S(1)/2 + sqrt(5)/2)**3
In [9]: should_be_phi
Out[9]:
2 3
⎛1 √5⎞ ⎛1 √5⎞
- ⎜─ + ──⎟ + ⎜─ + ──⎟
⎝2 2 ⎠ ⎝2 2 ⎠
In [10]: simplify(should_be_phi)
Out[10]:
1 √5
─ + ──
2 2
Note that you should use S(1)/2 rather than 1/2 which gives a float.
If you want to compare expressions then the obvious way would be to use == but that is for "structural equality" in SymPy. What that means is that expr1 == expr2 will give True only when the expressions are in the exact same form. If you want to test for mathematical equality then you should use Eq(lhs, rhs) or simplify(lhs-rhs):
In [11]: should_be_phi == phi # Expressions are not in the same form
Out[11]: False
In [12]: Eq(should_be_phi, phi)
Out[12]: True
In [13]: simplify(should_be_phi - phi)
Out[13]: 0
Is there a way do do symbolic calculations in Python that actually works?
SymPy seems to be unable to do the things it is supposedly made for.
Unlike Wolfram Alpha, SymPy is not designed to be usable or understandable to someone who has not read any of the documentation. Your questions above would be answered if you had read the first few pages of the SymPy tutorial:
https://docs.sympy.org/latest/tutorial/index.html#tutorial

Smart rewriting of expressions in sympy

I found this to be tricky to explain, but I'll do my best through an example.
Consider the expression assigned to the variable grad below
from sympy import *
a, x, b = symbols("a x b")
y_pred = a * x
loss = log(1 + exp(- b * y_pred))
grad = diff(loss, x, 1)
grad has the following expression:
-a*b*exp(-a*b*x)/(1 + exp(-a*b*x))
Now I want to manipulate grad in two ways.
1) I want sympy to try rewrite the expression grad such that none of its terms look like
exp(-a*b*x)/(1 + exp(-a*b*x)).
2) I also want it to try to rewrite the expression such that it has at least one term that look like this 1./(1 + exp(a*b*x)).
So at the end, grad becomes:
-a*b/(1 + exp(a*b*x)
Note that 1./(1 + exp(a*b*x)) is equivalent to exp(-a*b*x)/(1 + exp(-a*b*x)) but I don't want to mention that to sympy explicitly :).
I'm not sure if this is feasible at all, but it would be interesting to know whether it's possible to do this to some extent.
cancel does this
In [16]: cancel(grad)
Out[16]:
-a⋅b
──────────
a⋅b⋅x
ℯ + 1
This works because it sees the expression as -a*b*(1/A)/(1 + 1/A), where A = exp(a*b*x), and cancel rewrites rational functions as canceled p/q (see the section on cancel in the SymPy tutorial for more information).
Note that this only works because it uses A = exp(a*b*x) instead of A = exp(-a*b*x). So for instance, cancel won't do the similar simplification here
In [17]: cancel(-a*b*exp(a*b*x)/(1 + exp(a*b*x)))
Out[17]:
a⋅b⋅x
-a⋅b⋅ℯ
────────────
a⋅b⋅x
ℯ + 1
Are you just looking for simplify?
>>> grad
-a*b*exp(-a*b*x)/(1 + exp(-a*b*x))
>>> simplify(grad)
-a*b/(exp(a*b*x) + 1)

Categories