How to solve multivariate inequalities with python and sympy? - python

I'm quite new using python and Sympy... And got a problem to solve multivariate inequalities using sympy.
Let's say I have a lot of functions in a file which look like this :
cst**(sqrt(x)/2)/cst
exp(sqrt(cst*x**(1/4)))
log(log(sqrt(cst + exp(x))))
(y**(1/4) + y)**cst
sqrt(y/log(x))/cst
sqrt(cst**log(cst) + x)
(y**2)**(x/4)
sqrt(y*sqrt(cst**y))
log(sqrt(2)*sqrt(cst)*x)
I need to derivate them, set the value of the constant and check if, for each functions f,
df/dx > 0
df/dy < 0
With x in [0, +oo) and y in [0, 1].
To derivate i use :
dx = diff(f, x)
dy = diff(f, y)
Then when i try :
cst = 2 #(for example)
solve(dx > 0)
I got this error :
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/sympy/solvers/solvers.py", line 634, in solve
symbols=symbols)
File "/usr/local/lib/python2.7/dist-packages/sympy/solvers/inequalities.py", line 374, in reduce_inequalities
raise NotImplementedError("only univariate inequalities are supported")
NotImplementedError: only univariate inequalities are supported
But if i try that :
x=Symbol('x', real=True, postive=True, nonzero=True)
y=Symbol('y', real=True, postive=True, nonzero=True)
solve(x**2+y > 0)
I got :
True
Which is good and workable answer.
Is there anyway to solve multivariate inequalities and always get an workable answer?
For example I would like to get :
solve(x**2-y>0)
Or(x>-sqrt(y), x>sqrt(y))

When trying to solve this with SymPy you get a pretty clear error message: NotImplementedError: only univariate inequalities are supported. Be aware that this means that the SymPy team will be very happy if you contribute an algorithm that solves this problem.
Now that it is clear that sympy.solve is not powerful enough you can try another approach. Recently (in 0.7.2) an implicit plotting routine was added to sympy that can plot where an expression evaluates to True. Regretfully it is only a numeric solution, not a symbolic one that you can get from solve but it might be enough:
From the image you can see that there is only a single line where the expression changes sign, so solving for expr==0 might give you what you want. And this is indeed the case:

There is a multivariate inequality solver in mystic, which is built on top of sympy. It uses optimization and the (mathematical) mapping of sets to provide this feature. It's not perfect, but works for many cases.
>>> equations = '''
... 2*A + 3*B >= C
... A*B > D
... C < 4*A
... D == 0
... '''
>>>
>>> import mystic.symbolic as ms
>>> var = list('ABCD')
>>> eqns = ms.simplify(equations, variables=var)
>>> print eqns
D == 0
B > 0
A > C/4
A >= -3*B/2 + C/2
A > D/B
>>>
>>> # generate a constraints function, which maps one space to another
>>> constrain = ms.generate_constraint(ms.generate_solvers(eqns, var))
>>> solution = constrain([1,2,3,4])
>>> print solution
[1, 2, 3, 0]
>>> # here's the solution...
>>> dict(zip(var,solution))
{'A': 1, 'C': 3, 'B': 2, 'D': 0}
>>>
>>> A=1; C=3; B=2; D=0
>>> 2*A + 3*B >= C
True
>>> A*B > D
True
>>> C < 4*A
True
>>> D == 0
True
>>>
Let's do it again, with the suggested test:
>>> equations = """x**2 - y >= 0
... x + y = 0
... """
>>> eqns = ms.simplify(equations, variables=var)
>>> constrain = ms.generate_constraint(ms.generate_solvers(eqns, var))
>>> solution = constrain([1,3])
>>> solution
[-3, 3]
>>> dict(zip(var, solution))
{'y': 3, 'x': -3}
>>> y=3; x=-3
>>> x**2 - y >= 0
True
>>> x+y == 0
True
>>>
mystic uses a combination of sympy and numerical optimization to simplify inequalities; and when presented with an initial guess solution, can (most of the time, but not always) generate a valid solution to the equations. mystic won't actually solve the inequalities per se, but it will (usually) generate a valid solution to the inequalities.

Related

Sympy evalf doesn't work with 1 variable subs() in 2 variable expression

For a college project in python using Sympy, i need to evaluate the output of a 2 var symbolic function after partial substitution, eg:
f = (x-1)/sqrt((x-10)^2+(y-10)^2) / x = 1 => f = 0
If i use an 1 var func, it works fine:
>>> from sympy import *
>>> x = symbols('x')
>>> F = 1-x
>>> F.evalf(subs={x: 1})
0.e-125
But in the case of a 2 var function, subs doesn't seem to do the substitution right:
>>> from sympy import *
>>> x,y = symbols('x y')
>>> F = (x-1)/sqrt((x-10)**2+(y-10)**2)
>>> F.evalf(subs={x: 1})
(x - 1.0)*((x - 10.0)**2 + (y - 10.0)**2)**(-0.5)
I expected F = 0 since x = 1
I'm not a sympy expert but I found a counter example when the value of y makes the expression non-zero:
>>> F.evalf(subs={x:1,y:float("nan")})
nan
when y is nan, the result is not zero. So it's not possible to simplify the expression not knowing the value of y (note: sympy happily evaluates F.evalf(subs={x:1,y:oo}) to 0 even if the result should be nan as well)
As mentioned above if y = nan creates a math exception, and so the substitution never happens.
But i found out if i just use subs then i get what i want:
>>> F.subs(x,1)
0
now if i explicitly substitute y = nan only then i get the nan answer
>>> F.subs(y,nan)
nan
Im guessing evalf is just checking for more exceptions before it evaluates the substitution.

Sympy simplification of polynomial with complex coefficients

Working on a problem with polynomials with complex coefficients,
I am stuck with the following problem:
Let's say I have a polynomial P = λ^16*z + λ^15*z^2, where λ is complex.
I want to simplify having the following constraint: λ^14 = 1.
So, plugging in, we should get:
P = λ^2*z + λ*z^2.
I have tried P.subs(λ**14,1) but it doesn't work, because it assumes λ is real I guess. So it returns the original expression: P = λ^16*z + λ^15*z^2, without factoring out λ^14...
I don't know any simple way to achieve what you want in sympy, but you could substitute each value explicitly:
p = (λ**16)*z + (λ**15)*(z**2)
p = p.subs(λ**16, λ**2).subs(λ**15, λ**1)
>>> z**2*λ + z*λ**2
Why subs fails to work here:
subs only substitutes an expression x**m in x**n when m is a factor of n, e.g.:
p.subs(λ, 1)
>>> z**2 + z
p.subs(λ**2, 1)
>>> z**2*λ**15 + z
p.subs(λ**3, 1)
>>> z**2 + z*λ**16
p.subs(λ**6, 1)
>>> z**2*λ**15 + z*λ**16
etc.
If you assume that λ is real, this works:
lambda_, z = sym.symbols('lambda z', real=True)
print((lambda_**16*z + lambda_**15*z**2).subs(lambda_**14, 1))
z**2 + z
Edit:
It shouldn't actually work anyway because λ may be negative. What you want is only true if λ is a positive number.
You can use the ratsimpmodprime() function to reduce a polynomial modulo a set of other polynomials. There is also the reduce() function, which does something similar.
>>> P = λ**16*z + λ**15*z**2
>>> ratsimpmodprime(P, [λ**14 - 1])
z**2*λ + z*λ**2
This works:
P.simplify().subs(λ**15,1).expand()

Find zeros of the characteristic polynomial of a matrix with Python

Given an N x N symmetric matrix C and an N x N diagonal matrix I, find the solutions of the equation det(λI-C)=0. In other words, the (generalized) eigenvalues of C are to be found.
I know few ways how to solve this in MATLAB using build-in functions:
1st way:
function lambdas=eigenValues(C,I)
syms x;
lambdas=sort(roots(double(fliplr(coeffs(det(C-I*x))))));
2nd way:
[V,D]=eig(C,I);
However, I need to use Python. There are similar function in NumPy and SymPy, but, according to docs (numpy, sympy), they take only one matrix C, as the input. Though, the result's different from the result produced by Matlab. Also, symbolic solutions produced by SymPy aren't helpful. Maybe I am doing something wrong? How to find solution?
Example
MATLAB:
%INPUT
I =
2 0 0
0 6 0
0 0 5
C =
4 7 0
7 8 -4
0 -4 1
[v,d]=eig(C,I)
%RESULT
v =
-0.3558 -0.3109 -0.5261
0.2778 0.1344 -0.2673
0.2383 -0.3737 0.0598
d =
-0.7327 0 0
0 0.4876 0
0 0 3.7784
Python 3.5:
%INPUT
I=np.matrix([[2,0,0],
[0,6,0],
[0,0,5]])
C=np.matrix([[4,7,0],[7,8,-4],[0,-4,1]])
np.linalg.eigh(C)
%RESULT
(array([-3., 1.91723747, 14.08276253]),
matrix(
[[-0.57735027, 0.60061066, -0.55311256],
[ 0.57735027, -0.1787042 , -0.79670037],
[ 0.57735027, 0.77931486, 0.24358781]]))
At least if I has positive diagonal entries you can simply solve a transformed system:
# example problem
>>> A = np.random.random((3, 3))
>>> A = A.T # A
>>> I = np.identity(3) * np.random.random((3,))
# transform
>>> J = np.sqrt(np.einsum('ii->i', I))
>>> B = A / np.outer(J, J)
# solve
>>> eval_, evec = np.linalg.eigh(B)
# back transform result
>>> evec /= J[:, None]
# check
>>> A # evec
array([[ -1.43653725e-02, 4.14643550e-01, -2.42340866e+00],
[ -1.75615960e-03, -4.17347693e-01, -8.19546081e-01],
[ 1.90178603e-02, 1.34837899e-01, -1.69999003e+00]])
>>> eval_ * (I # evec)
array([[ -1.43653725e-02, 4.14643550e-01, -2.42340866e+00],
[ -1.75615960e-03, -4.17347693e-01, -8.19546081e-01],
[ 1.90178603e-02, 1.34837899e-01, -1.69999003e+00]])
OP's example. IMPORTANT: must use np.arrays for I and C, np.matrix will not work.
>>> I=np.array([[2,0,0],[0,6,0],[0,0,5]])
>>> C=np.array([[4,7,0],[7,8,-4],[0,-4,1]])
>>>
>>> J = np.sqrt(np.einsum('ii->i', I))
>>> B = C / np.outer(J, J)
>>> eval_, evec = np.linalg.eigh(B)
>>> evec /= J[:, None]
>>>
>>> evec
array([[-0.35578356, -0.31094779, -0.52605088],
[ 0.27778714, 0.1343625 , -0.267297 ],
[ 0.23826117, -0.37371199, 0.05975754]])
>>> eval_
array([-0.73271478, 0.48762792, 3.7784202 ])
If I has positive and negative entries use eig instead of eigh and before taking the square root cast to complex dtype.
Differing from other answers, I assume that by the symbol I you mean the identity matrix, Ix=x.
What you want to solve, Cx=λIx, is the so-called standard eigenvalue problem,
and most eigenvalue solvers tackle the problem described in that format, hence the
Numpy function has the signature eig(C).
If your C matrix is a symmetric matrix and your problem is indeed a standard eigenvalue problem I'd recommend the use of numpy.linalg.eigh, that is optimized for this type of problems.
On the contrary if your problem is really a generalized eigenvalue problem, as, e.g., the frequency equation Kx=ω²Mx you could use scipy.linalg.eigh, that supports that type of problem statement for symmetric matrices.
eigvals, eigvecs = scipy.linalg.eigh(C, I)
With respect to the discrepancies in eigenvalues, the Numpy implementation gives no guarantees w/r to their ordering, so it could be just a different ordering, but if your problem is indeed a generalized problem (I not being the identity matrix...) the solution is of course different and you have to use the Scipy implementation of eigh.
If the discrepancies is within the eigenvectors, please remember that the eigenvectors are known within an arbitrary scale factor and, again, the ordering could be undefined (but, of course, their order is the same order in which you have the eigenvalues) — the situation is a little different for scipy.linalg.eigh because in this case the eigenvalues are sorted and the eigenvectors are normalized with respect to the second matrix argument (I in your example).
Ps: scipy.linalg.eigh behaviour (i.e., sorted eigenvalues and normalized eigenvectors) is so convenient for my use cases that I use to use it also to solve standard eigenvalue problems.
Using SymPy:
>>> from sympy import *
>>> t = Symbol('t')
>>> D = diag(2,6,5)
>>> S = Matrix([[ 4, 7, 0],
[ 7, 8,-4],
[ 0,-4, 1]])
>>> (t*D - S).det()
60*t**3 - 212*t**2 - 77*t + 81
Computing the exact roots:
>>> roots = solve(60*t**3 - 212*t**2 - 77*t + 81,t)
>>> roots
[53/45 + (-1/2 - sqrt(3)*I/2)*(312469/182250 + sqrt(797521629)*I/16200)**(1/3) + 14701/(8100*(-1/2 - sqrt(3)*I/2)*(312469/182250 + sqrt(797521629)*I/16200)**(1/3)), 53/45 + 14701/(8100*(-1/2 + sqrt(3)*I/2)*(312469/182250 + sqrt(797521629)*I/16200)**(1/3)) + (-1/2 + sqrt(3)*I/2)*(312469/182250 + sqrt(797521629)*I/16200)**(1/3), 53/45 + 14701/(8100*(312469/182250 + sqrt(797521629)*I/16200)**(1/3)) + (312469/182250 + sqrt(797521629)*I/16200)**(1/3)]
Computing floating-point approximations of the roots:
>>> for r in roots:
... r.evalf()
...
0.487627918145732 + 0.e-22*I
-0.73271478047926 - 0.e-22*I
3.77842019566686 - 0.e-21*I
Note that the roots are real.

Isolate all variables to LHS in sympy?

I am using sympy to process some equations. I want to write the equations in a canonical form such that variables of interest are all on LHS. For eg. if I have,
lhs = sympify("e*x +f")`
rhs = sympify("g*y + t*x +h")`
eq = Eq(lhs,rhs)
e*x + f == g*y + h + t*x
I need a function which can isolate a list of given variables (my so called canonical form), like
IsolateVariablesToLHS(eq,[x,y]) # desired function
(e-t)*x - g*y == h-f # now x and y are on LHS and remaining are on RHS
I have the assurance that I will only get linear equations, so this is always possible.
>>> import sympy as sm
>>> lhs = sm.sympify('e*x + f')
>>> rhs = sm.sympify('g*y + t*x + h')
>>> eq = sm.Eq(lhs, rhs)
Here's a simple construct
def isolateVariablesToLHS(eq, syms):
l = sm.S.Zero
eq = eq.args[0] - eq.args[1]
for e in syms:
ind = eq.as_independent(e)[1]
l += ind
eq -= ind
return sm.Eq(l, eq)
>>> isolateVariablesToLHS(eq, [x, y])
Eq(e*x - g*y - t*x, f - h)
With the equation as provided in the question combine all the terms and construct a filter for discovering the required variables.
>>> from itertools import filterfalse
>>> terms = eq.lhs - eq.rhs
>>> vars = ['x', 'y']
>>> filt = lambda t: any(t.has(v) for v in vars)
>>> result = Eq(sum(filter(filt, terms.args)), - sum(filterfalse(filt, terms.args))
>>> result
e*x - g*y - t*x == -f + h
I'm not familiar with sympy but I think this will work for equations consisting of proper atoms such as Symbols. Make sure you replace the vars list with the actual instantiated Symbols x, y instead of the 'ascii' representations. This is probably required to combine terms that have variables in common.
filter and filterfalse might have different names in python 2.x, but this functionality is probably still in the itertools package.

how to combine exponents? (x**a)**b => x**(a*b)?

how to simplify exponents in equations in sympy
from sympy import symbols
a,b,c,d,e,f=symbols('abcdef')
j=(a**b**5)**(b**10)
print j
(a**(b**5))**(b**10) #ans even after using expand simplify
# desired output
a**(b**15)
and if it is not possible with sympy which module should i import in python?
edit
even if i define 'b' as real,and also all other symbols
b=symbols('b',real=True)
not getting simplified exponents
it simplifies only if exponents are constants
a=symbols('a',real=True)
b=symbols('b',real=True)
(a**5)**10
a**50 #simplifies only if exp are numbers
(a**b**5)**b**10
(a**(b**5))**b**10 #no simplification
(xm)n = xmn is true only if m, n are real.
>>> import math
>>> x = math.e
>>> m = 2j*math.pi
>>> (x**m)**m # (e^(2πi))^(2πi) = 1^(2πi) = 1
(1.0000000000000016+0j)
>>> x**(m*m) # e^(2πi×2πi) = e^(-4π²) ≠ 1
(7.157165835186074e-18-0j)
AFAIK, sympy supports complex numbers, so I believe this simplification should not be done unless you can prove b is real.
Edit: It is also false if x is not positive.
>>> x = -2
>>> m = 2
>>> n = 0.5
>>> (x**m)**n
2.0
>>> x**(m*n)
-2.0
Edit(by gnibbler): Here is the original example with Kenny's restrictions applied
>>> from sympy import symbols
>>> a,b=symbols('ab', real=True, positive=True)
>>> j=(a**b**5)**(b**10)
>>> print j
a**(b**15)
a,b,c=symbols('abc',real=True,positive=True)
(a**b**5)**b**10
a**(b**15)#ans
This may be related to this bug.

Categories