Using scipy.linalg.solve with symmetric coefficient matrix (assume_a='sym') - python

I am trying to solve a system of linear equations A * x = b for the unknown x using scipy's linalg.solve function. Here is an example that works fine:
import numpy as np
import scipy.linalg as linalg
A = np.array([[ 0.18666667, 0.06222222, -0.01777778],
[ 0.01777778, 0.18666667, 0.01777778],
[-0.01777778, 0.06222222, 0.18666667]])
b = np.array([0.26666667, -0.26666667, -0.4])
x = linalg.solve(A, b, assume_a='gen')
It results in x = [1.77194417, -1.4555256, -1.48892533], which is a correct solution. This can be verified by computing A.dot(x), which results in [0.26666667, -0.26666667, -0.4]. As this is the same as b, the solution is correct.
However, the matrix of coefficients A is symmetrical, i.e., the values above and below the main diagonal are the same. If I understand the documentation correctly, for solving such a problem more efficiently, the solve function allows to set the argument assume_a='sym'. Unfortunately, using the following code (given the same A and b) results in an incorrect solution being found:
x = linalg.solve(A, b, assume_a='sym')
It results in x = [1.88811181, -1.88811181, -1.78321672], which is different from the solution above. Computing A.dot(x) results in [0.26666667, -0.35058274, -0.48391607]. As this is different from b, the solution seems to be incorrect.
I am wondering, if there is any problem with my code, or if my understanding of symmetric matrices or the expected result is simply wrong!? Maybe the matrix must satisfy additional constraints to be used together with assume_a='sym'?
I appreciate your answers. Thanks in advance!

In think it won't happen. I provide a short answer about it.
Non-symmetric A
import numpy as np
import scipy.linalg as linalg
A = np.array([[ 0.18666667, 0.06222222, -0.01777778],
[ 0.01777778, 0.18666667, 0.01777778],
[-0.01777778, 0.06222222, 0.18666667]])
b = np.array([0.26666667, -0.26666667, -0.4])
x = linalg.solve(A, b, assume_a='gen')
np.allclose(A # x,b)
Out:
True
Which shows the solver works well.
Symmetric A
# use you upper triangular A to get a symmetric matrix
A_symm = (np.triu(A) + np.triu(A).T -np.diag(A.diagonal()))
# solve the equations
x = linalg.solve(A_symm, b, assume_a='sym')
np.allclose(A_symm # x,b)
Out:
True
It still works.
If you pass a non-symmetric matrix A to the solver , and then specify the assume_a = 'sym', solver will only use upper triangular matrix of A, see below:
x = linalg.solve(A, b, assume_a='sym')
np.allclose(A # x,b),x
Out:
(False, array([ 1.88811181, -1.88811181, -1.78321672]))
The result shows that solver works "wrong", but the result x is the same with result of linalg.solve(A_symm, b, assume_a='sym')

Related

solving multiple non linear equation (with power function)

I would like to solve this kind of equations:
a*85**b+c=100
a*90**b+c=66
a*92**b+c=33
I tried this
import scipy.optimize
def fun(variables) :
(a,b,c)= variables
eq0=a*85**b+c-100
eq1=a*90**b+c-66
eq2=a*92**b+c-33
return [eq0,eq1,eq2]
result = scipy.optimize.fsolve(fun, (1, -1, 0))
print(result)
But I get ValueError: Integers to negative integer powers are not allowed.
Then I tried the equivalent
def fun(variables) :
(a,b,c)= variables
eq0=log(a)+b*log(85)-log(100-c)
eq1=log(a)+b*log(90)-log(66-c)
eq2=log(a)+b*log(92)-log(33-c)
return [eq0,eq1,eq2]
result = scipy.optimize.fsolve(fun, (1, -1, 0))
print(result)
I get a solution but that is equal to the initial values (1, -1, 0)
Thus when I test fun(result), I get values different from zero.
I have noticed that for this example the same problem is observed
import scipy.optimize
def fun(variables) :
(x,y)= variables
eqn_1 = x**2+y-4
eqn_2 = x+y**2+3
return [eqn_1,eqn_2]
result = scipy.optimize.fsolve(fun, (0.1, 1))
print(result)
fun(result)
Does anyone would know how I could do ? Thank you
PS I have posted here about sympy last week
Resolution of multiple equations (with exponential)
When the initial condition is not well known, sometimes its best to try other methods first . For small problems, simplex minimization is useful:
import numpy as np
def func(x):
a,b,c= x
eq0=np.log(a)+b*np.log(85)-np.log(100-c)
eq1=np.log(a)+b*np.log(90)-np.log(66-c)
eq2=np.log(a)+b*np.log(92)-np.log(33-c)
return eq0**2+ eq1**2 + eq2**2
def func_vec(x):
a,b,c= x
eq0=np.log(a)+b*np.log(85)-np.log(100-c)
eq1=np.log(a)+b*np.log(90)-np.log(66-c)
eq2=np.log(a)+b*np.log(92)-np.log(33-c)
return eq0, eq1, eq2
from scipy.optimize import fsolve, minimize
out = minimize(func, [1,1,0], method="Nelder-Mead", options={"maxfev":100000})
print("roots:", out.x)
print("value at roots:", func_vec(out.x))
# roots: [ 7.87002460e+11 -1.07401055e-09 -7.87002456e+11]
# value at roots: (6.0964566728216596e-12, -1.2086331935279304e-11, 6.235012506294879e-12)
Note, I also tried [1,1,1] as an initial condition and found it converged to the wrong solution. Further increasing maxfev from 1e5 to 1e7 allowed [1,1,1] to converged to the proper solution, but then perhaps there are better methods to solve this.

System of First Order ODEs in Python

I have seen how to solve systems of ODEs in Python, but all of the examples I have seen were "standard" equations. What I mean by standard is that the equations do not say "derivative of one function = expression that contains derivative of another function".
Here is a sample system I am trying to solve numerically. Initial conditions are x(0) = 5, y(0) = 3, z(0) = 2, and all initial derivatives are 0:
x'(t) + 4y(t) = -3y'(t)
y'(t) + ty(t) = -2z'(t)
z'(t) = -2y(t) + x'(t)
I am not 100% sure how to code this. Here is what I have tried:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import math
def ODESystem(f,t):
x = f[0]
y = f[1]
z = f[2]
Now, what do I define first: dydt, dxdt or dzdt. Is there a way for me to define one expression that "hangs around" before I use it to define another expression?
You do not need to solve anything manually, you can just as well do
def ODESystem(f,t):
x,y,z = f
return np.linalg.solve([[1,3,0],[0,1,2],[-1,0,1]], [-4, -t, -2])*y
Nevermind; I am stupid. I can keep on substituting into the third equation until I get an equation for z'(t) that does not include any other derivatives.

How to estimate coefficients for a+xb=c?

I am trying to find the right python or R package/function to approximate x in the equation a + xb = c.
a, b, and c are tuples/vectors, so if I have:
a = (1,2,1)
b = (2,3,2)
c = (5,8,5)
then I would like the function to give me x = 2.
I feel like some form of least squares approach might be the right way to go, but I cannot seem to find a function that does this. Maybe I am looking with the wrong terms, because it seems like such an obvious thing, I don't know.
You can use Sympy for Python , here how it works :
from sympy.solvers import solve
from sympy import Symbol
x = Symbol('x')
solve(x**2 - 1, x)
[-1, 1] # output

How to solve equation with two matrices in python?

I need to find matrix A that A*B*A = B*A*B, and A^2 =1 if B =(1,0,0)(0,-1,0)(0,0,1)
I tried that do it in sympy:sympy.solve(A*B*A - B*A*A)
import sympy as sp
a,b,c,d,e,f,g,h,k = sp.symbols('a b c d e f g h k')
B = sp.Matrix([[1,0,1],[0,-1,0],[0,0,1]])
A = sp.Matrix([[a,b,c],[d,e,f],[g,h,k]])
sp.solve([A*B*A - B*A*B,B**2-sp.eye(3)])
and also I tried numpy.linalg, but I don't have any results
First of all, linalg will not help, as this is not a linear problem: unknown A gets multiplied by itself. You want to solve a system of 18 quadratic equations with 9 unknowns. For a generic system we'd expect no solutions, but there is a lot of structure here.
In my version of SymPy (1.1.1) direct attempts to solve even one of the matrix equations A*B*A=B*A*B or A*A=I fail to finish in reasonable time. So let's follow the advice of saintsfan342000 and approach the problem numerically, as a minimization problem. This is how I did it:
import numpy as np
from scipy.optimize import minimize
B = np.array([[1,0,0], [0,-1,0], [0,0,1]])
def func(A, B):
A = A.reshape((3, 3))
return np.linalg.norm(A.dot(B).dot(A)-B.dot(A).dot(B))**2 + np.linalg.norm(A.dot(A)-np.eye(3))**2
while True:
guess = np.random.uniform(-2, 2, size=(9,))
res = minimize(func, guess, args=(B,))
if res.fun < 1e-15:
A = res.x.reshape((3, 3))
print(A)
The function to minimize is the sum of squares of Frobenius norms of A*B*A-B*A*B and A*A-I. I put minimization in a loop because there are some local minima where minimize will get stuck; so when the minimal value found is not sufficiently close to zero, I ignore the result and start over. After running for a while, the script will print a bunch of matrices like
[[ 0.70386835 0.86117949 -1.40305355]
[ 0.17193376 0.49999999 0.81461157]
[-0.25409118 0.73892171 -0.20386834]]
which all share two important features:
the central element A[1,1] is 1/2
the trace of the matrix (the sum of diagonal elements) is 1.
Let's use this information to help SymPy solve the system. I still don't want to throw both equations at it, so I try to get one at a time.
from sympy import *
var('a:h') # a quick way to declare a bunch of symbols
B = Matrix([[1, 0, 0], [0, -1, 0], [0, 0, 1]])
A = Matrix([[a, b, c], [d, S(1)/2, f], [g, h, S(1)/2-a]]) # S(1)/2 is a way to get rational 1/2 instead of decimal 0.5
print(solve(A*B*A - B*A*B))
print(solve(A*A - eye(3)))
Now solve succeeds and prints the following:
[{b: h*(4*f*h - 3)/(2*g), d: -g/(2*h), a: 2*f*h - 1/2, c: -f*h*(4*f*h - 3)/g}]
[{b: h*(4*f*h - 3)/(2*g), d: -g/(2*h), a: 2*f*h - 1/2, c: -f*h*(4*f*h - 3)/g}]
Whoa! With the two constraints that we found numerically, both matrix equations are equivalent! I did not expect that. So we already have the solution:
A = Matrix([[2*f*h - S(1)/2, h*(4*f*h - 3)/(2*g), -f*h*(4*f*h - 3)/g], [-g/(2*h), S(1)/2, f], [g, h, 1 - 2*f*h]])
for arbitrary f, g, h.
Note: A=B is a trivial solution, which was excluded above by the requirement of A[1,1]=1/2. I imagine this was the intent; it appears you were looking for faithful 3-dimensional representations of the symmetric group S3.

Euclidean distance with weights

I am currently using SciPy to calculate the euclidean distance
dis = scipy.spatial.distance.euclidean(A,B)
where; A, B are 5-dimension bit vectors. It works fine now, but if I add weights for each dimension then, is it still possible to use scipy?
What I have now: sqrt((a1-b1)^2 + (a2-b2)^2 +...+ (a5-b5)^2)
What I want: sqrt(w1(a1-b1)^2 + w2(a2-b2)^2 +...+ w5(a5-b5)^2) using scipy or numpy or any other efficient way to do this.
Thanks
The suggestion of writing your own weighted L2 norm is a good one, but the calculation provided in this answer is incorrect. If the intention is to calculate
then this should do the job:
def weightedL2(a,b,w):
q = a-b
return np.sqrt((w*q*q).sum())
Simply define it yourself. Something like this should do the trick:
def mynorm(A, B, w):
import numpy as np
q = np.matrix(w * (A - B))
return np.sqrt((q * q.T).sum())
If you want to keep using scipy function you could pre-process the vector like this.
def weighted_euclidean(a, b, w):
A = a*np.sqrt(w)
B = b*np.sqrt(w)
return scipy.spatial.distance.euclidean(A, B)
However it's look slower than
def weightedL2(a, b, w):
q = a-b
return np.sqrt((w*q*q).sum())
The present version of scipy (v1.9.3 as of writing) supports weighted L2 distance. From scipy.spatial.distance.euclidean
where: w(N,) array_like, optional
The weights for each value in u and v. Default is None, which gives each value a weight of 1.0

Categories

Resources