I have the following code for a symbolic matrix calculation
from sympy import *
A = MatrixSymbol( 'A', 3, 3 )
B = MatrixSymbol( 'B', 3, 3 )
C = MatrixSymbol( 'C', 3, 3 )
Z = A * ( A + B ) * C.inverse() * ( A + B ).transpose()
Z.expand()
This gives me the following result
A( A + B ) C^-1 ( A^T + B^T )
But I want the expanded result
A * A * C^-1 * A^T + A * B C^-1 * A^T + A * A C^-1 * B^T + A * B C^-1 * B^T
Could I ask you guys that how to do it ? Thank you very much
Currently, there is no way to expand matrix operations in sympy. The expression from the result you have above is the only way to view the result. Expansion functions available in sympy include expand(), expand_trig(), expand_power_exp(), expand_power_base(), expand_log(), expand_func() and hyperexpand(), none of which seem to work with your matrix expression.
The simplify function doesn't seem to work either if we wanted to evaluate your desired output to compare to the sympy output of Z
simplify(A*A*C.inverse()*A.transpose() + A*A*C.inverse()*B.transpose() + A*B*C.inverse()*A.transpose() + A*B*C.inverse()*B.transpose())
>>> A*A*C^-1*A.T + A*A*C^-1*B.T + A*B*C^-1*A.T + A*B*C^-1*B.T
which clearly isn't in the same form as the sympy expression for Z.
Even if you were to just test your desired output against the output of sympy, you still get a False result
Z == A * A * C.inverse() * A.transpose() + A * B*C.inverse() * A.transpose() + A * A*C.inverse() * B.transpose() + A * B*C.inverse() * B.transpose()
>>> False
We cannot define matrices A, B and C with known values for each index and then evaluate the expressions you have above to compare their outputs because MutableDenseMatrix objects do not have an inverse attribute.
If you want to actually compare the two expressions to see if they are equal, you could use numpy and compare both expressions with known matrix values. This is quite convoluted and wont work for very complex expressions, but it does work for simple expressions.
e.g.
import numpy as np
# define 3 numpy arrays
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
B = np.array([[0,0.1,-4],[19,0.67,6],[-1,99,5]])
C = np.array([[1,2,-1],[4,5,6],[-2,2,0]])
# sympy expression, compute iteratively
Z1 = np.dot(A, A + B)
Z1 = np.dot(Z1, np.linalg.inv(C))
Z1 = np.dot(Z1, A.T + B.T)
print(Z1)
>>>[[ 143.22088889 -672.5076 -100.24 ]
[-239.47685185 4558.45416667 3597.5 ]
[ 65.1457037 -94.81393333 519.56 ]]
# desired expression
p1 = np.dot(A, A)
p1 = np.dot(p1, np.linalg.inv(C))
p1 = np.dot(p1, A.T)
p2 = np.dot(A, B)
p2 = np.dot(p2, np.linalg.inv(C))
p2 = np.dot(p2, A.T)
p3 = np.dot(A, A)
p3 = np.dot(p3, np.linalg.inv(C))
p3 = np.dot(p3, B.T)
p4 = np.dot(A, B)
p4 = np.dot(p4, np.linalg.inv(C))
p4 = np.dot(p4, B.T)
Z2 = p1 + p2 + p3 + p4
print(Z2)
>>>[[ 143.22088889 -672.5076 -100.24 ]
[-239.47685185 4558.45416667 3597.5 ]
[ 65.1457037 -94.81393333 519.56 ]]
np.isclose(Z1,Z2)
>>>[[ True, True, True],
[ True, True, True],
[ True, True, True]
So we can see that the sympy expression Z1 and your desired expression Z2 are in fact the same, given the matrices above.
Related
numpy version 1.20.1
Bob lawblaw's Law Blog, I need more details, this post has tooooo much code.
def quadratic_formula(a, b, c):
"""just like the song
Args:
a (numpy.ndarray): shape(N,1)
b (numpy.ndarray): shape(N,1)
c (numpy.ndarray): shape(N,1)
Returns:
numpy.ndarray: shape(N,2)
* [soln_a, soln_b]
* [np.NaN, np.NaN] when discriminant is negative
* [soln_a, soln_a] single soln when discriminate == 0]
Notes:
.. math::
ax^2 + bx + c = 0
solns = \\frac{-b \\pm \\sqrt{b^2 -4ac}}{2a}
"""
# TODO: raise value error if any a == 0
a = np.array(a)
b = np.array(b)
c = np.array(c)
det = b ** 2 - 4 * a * c
res_a = np.where(
det >= 0,
(-b + np.sqrt(det)) / (2 * a),
np.NaN,
)
res_b = np.where(
det >= 0,
(-b - np.sqrt(det)) / (2 * a),
np.NaN,
)
res = np.array([res_a, res_b]).T
return res
a = [1,2]
b = [1,0]
c = [0,1]
res = quadratic_formula(a,b,c)
print(res)
>>> [[0, -1],
[NaN, NaN]]
works, but raise RuntimeWarning: invalid value encountered in sqrt.
Why is the square root even evaluated for a negative discriminant?
Any suggestions for implementation?
Note that you are still computing np.sqrt(det) for all values of det hence the warning. The where filters the x and y arrays after they have been computed.
The implementation can be fixed by simply casting the a,b and c arrays to complex.
a = np.array(a).astype(complex)
b = np.array(b).astype(complex)
c = np.array(c).astype(complex)
That way numpy knows to use the complex version of sqrt. Once you are there you can completely omit the np.where and check after the fact if your solutions are real, if that is what you are interested on only.
I have to compute {sin, cos}(arctan2(x)) on a regular basis. These expressions have much cheaper equivalents, namely
import numpy as np
np.random.seed(0)
a, b = np.random.rand(2)
print(np.cos(np.arctan2(b, a)))
print(a / np.sqrt(a ** 2 + b ** 2))
# or a / np.hypot(a, b)
print()
print(np.sin(np.arctan2(b, a)))
print(b / np.hypot(a, b))
0.6087819565465009
0.6087819565465009
0.7933375885355579
0.793337588535558
Unfortunately, for the important border case a == b == 0.0, the alternative yields and error and gives nan. I'd like to avoid both.
Is there a specialized function that computes {sin, cos}(arctan2(x)) or perhaps another more appropriate expression? Like the above, it needs to work for scalar and vector inputs.
One possible trick to get around this issue is to just add a small epsilon to the values when you are dividing by zero.
import numpy as np
np.random.seed(0)
a = np.random.rand(10)
b = np.random.rand(10)
a[0] = 0
b[0] = 0
eps = 1e-9
p1 = np.cos(np.arctan2(b, a))
p2 = (a+eps) / ((np.sqrt(a ** 2 + b ** 2))+eps)
print(np.allclose(p1, p2))
p1 = np.sin(np.arctan2(b, a))
p2 = b / (np.hypot(a, b)+eps)
print(np.allclose(p1, p2))
I want to calculate the gradient of the following function h(x) = 0.5 x.T * A * x + b.T + x.
For now I set A to be just a (2,2) Matrix.
def function(x):
return 0.5 * np.dot(np.dot(np.transpose(x), A), x) + np.dot(np.transpose(b), x)
where
A = A = np.zeros((2, 2))
n = A.shape[0]
A[range(n), range(n)] = 1
a (2,2) Matrix with main diagonal of 1 and
b = np.ones(2)
For a given Point x = (1,1) numpy.gradient returns an empty list.
x = np.ones(2)
result = np.gradient(function(x))
However shouldn't I get something like that: grad(f((1,1)) = (x1 + 1, x2 + 1) = (2, 2).
Appreciate any help.
It seems like you want to perform symbolic differentiation or automatic differentiation which np.gradient does not do. sympy is a package for symbolic math and autograd is a package for automatic differentiation for numpy. For example, to do this with autograd:
import autograd.numpy as np
from autograd import grad
def function(x):
return 0.5 * np.dot(np.dot(np.transpose(x), A), x) + np.dot(np.transpose(b), x)
A = A = np.zeros((2, 2))
n = A.shape[0]
A[range(n), range(n)] = 1
b = np.ones(2)
x = np.ones(2)
grad(function)(x)
Outputs:
array([2., 2.])
Starting with:
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
B[diag,diag]=1-1j/np.sqrt(3)
this produces an n*n grid that acts as a matrix.
n is just a number chosen to represent the indices, i.e. an a*b matrix where a and b both go up to n.
Where z is a constant I choose to replace a row and column with the B[z,b] and B[a,z] formulas. (Essentially the same formula but with a small number added to the np.abs(a-b))
The diagonal of the matrix is given by the bottom line:
B[diag,diag]=1-1j/np.sqrt(3)
where,
diag=np.arange(n+1)
I would like to repeat this code 50 times where the only thing that changes is x so I will end up with 50 versions of the B np.ogrid. x is a randomly generated number between -0.8 and 0.8 each time.
x=np.random.uniform(-0.8,0.8)
I want to generate 50 versions of B with random values of x each time and take a geometric average of the 50 versions of B using the definition:
def geo_mean(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=-1)
I have tried to set B as a function of some index and then use a for _ in range(): loop, this doesn't work. Aside from copy and pasting the block 50 times and denoting each one as B1, B2, B3 etc; I can't think of another way of working this out.
EDIT:
I'm now using part of a given solution in order to show clearly what I am looking for:
#A matrix with 50 random values between -0.8 and 0.8 to be used in the loop
X=np.random.uniform(-0.8,0.8, (50,1))
#constructing the base array before modification by random x values in position z
a,b = np.ogrid[0:n+1:1,0:n+1:1]
B = np.exp(1j * ( np.pi / 3) * np.abs( a - b ))
B[diag,diag] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
randomarrays = []
for i in range( 0,50 ):
#copy array and modify it
Bnew = np.copy( B )
Bnew[z, b] = np.exp( 1j * ( np.pi / 3 ) * np.abs(z - b + X[i]))
Bnew[a, z] = np.exp( 1j * ( np.pi / 3 ) * np.abs(a - z + X[i]))
randomarrays.append(Bnew)
Bstack = np.dstack(randomarrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
B0 = geo_mean(Bstack)
From this example, every iteration of i uses the same value of X, I can't seem to get a way to get each new loop of i to use the next value in the matrix X. I am unsure of the ++ action in python, I know it does not work in python, I just don't know how to use the python equivalent. I want a loop to use a value of X, then the next loop to use the next value and so on and so forth so I can dstack all the matrices at the end and find a geo_mean for each element in the stacked matrices.
One pedestrian way would be to use a list comprehension or generator expression:
>>> def f(n, z, x):
... diag = np.arange(n+1)
... a,b=np.ogrid[0:n+1:1,0:n+1:1]
... B=np.exp(1j*(np.pi/3)*np.abs(a-b))
... B[z,b] = np.exp(1j * (np.pi/3) * np.abs(z - b +x))
... B[a,z] = np.exp(1j * (np.pi/3) * np.abs(a - z +x))
... B[diag,diag]=1-1j/np.sqrt(3)
... return B
...
>>> X = np.random.uniform(-0.8, 0.8, (10,))
>>> np.prod((*map(np.power, map(f, 10*(4,), 10*(2,), X), 10 * (1/10,)),), axis=0)
But in your concrete example we can do much better than that;
using the identity exp(a) x exp(b) = exp(a + b) we can convert the geometric mean after exponentiation to an arithmetic mean before exponentition. A bit of care is required because of the multivaluedness of the complex n-th root which occurs in the geometric mean. In the code below we normalize the angles occurring to range -pi, pi so as to always hit the same branch as the n-th root.
Please also note that the geo_mean function you provide is definitely wrong. It fails the basic sanity check that taking the average of copies of the same thing should return the same thing. I've provided a better version. It is still not perfect, but I think there actually is no perfect solution, because of the nonuniqueness of the complex root.
Because of this I recommend taking the average before exponentiating. As long as your random spread is less than pi this allows a well-defined averaging procedure with an average that is actually close to the samples
import numpy as np
def f(n, z, X, do_it_pps_way=True):
X = np.asanyarray(X)
diag = np.arange(n+1)
a,b=np.ogrid[0:n+1:1,0:n+1:1]
B=np.exp(1j*(np.pi/3)*np.abs(a-b))
X = X.reshape(-1,1,1)
if do_it_pps_way:
zbx = np.mean(np.abs(z-b+X), axis=0)
azx = np.mean(np.abs(a-z+X), axis=0)
else:
zbx = np.mean((np.abs(z-b+X)+3) % 6 - 3, axis=0)
azx = np.mean((np.abs(a-z+X)+3) % 6 - 3, axis=0)
B[z,b] = np.exp(1j * (np.pi/3) * zbx)
B[a,z] = np.exp(1j * (np.pi/3) * azx)
B[diag,diag]=1-1j/np.sqrt(3)
return B
def geo_mean(y):
y = np.asarray(y)
dim = len(y.shape)
y = np.atleast_2d(y)
v = np.prod(y, axis=0) ** (1.0 / y.shape[0])
return v[0] if dim == 1 else v
def geo_mean_correct(y):
y = np.asarray(y)
return np.prod(y ** (1.0 / y.shape[0]), axis=0)
# demo that orig geo_mean is wrong
B = np.exp(1j * np.random.random((5, 5)))
# the mean of four times the same thing should be the same thing:
if not np.allclose(B, geo_mean([B, B, B, B])):
print('geo_mean failed')
if np.allclose(B, geo_mean_correct([B, B, B, B])):
print('but geo_mean_correct works')
n, z, m = 10, 3, 50
X = np.random.uniform(-0.8, 0.8, (m,))
B0 = f(n, z, X, do_it_pps_way=False)
B1 = np.prod((*map(np.power, map(f, m*(n,), m*(z,), X), m * (1/m,)),), axis=0)
B2 = geo_mean_correct([f(n, z, x) for x in X])
# This is the recommended way:
B_recommended = f(n, z, X, do_it_pps_way=True)
print()
print(np.allclose(B1, B0))
print(np.allclose(B2, B1))
I think you should rely more on numpy functionality, when approaching your problem. Not a numpy expert myself, so there is surely room for improvement:
from scipy.stats import gmean
n = 2
z = 1
a = np.arange(n + 1).reshape(1, n + 1)
#constructing the base array before modification by random x values in position z
B = np.exp(1j * (np.pi / 3) * np.abs(a - a.T))
B[a, a] = 1 - 1j / np.sqrt(3)
#list to store all modified arrays
random_arrays = []
for _ in range(50):
#generate random x value
x=np.random.uniform(-0.8, 0.8)
#copy array and modify it
B_new = np.copy(B)
B_new[z, a] = np.exp(1j * (np.pi / 3) * np.abs(z - a + x))
B_new[a, z] = np.exp(1j * (np.pi / 3) * np.abs(a - z + x))
random_arrays.append(B_new)
#store all B arrays as a 3D array
B_stack = np.stack(random_arrays)
#calculate the geometric mean value along the axis that was the row in 2D arrays
geom_mean_for_rows = gmean(B_stack, axis = 2)
It uses the geometric mean function from scipy.stats module to have a vectorised approach for this calculation.
I have the following problem: I have two surface equations, and I am looking at what point they are zero. So I have the following:
b = np.arange(0,2,0.1)
k = np.arange(0,50,1)
b,k = np.meshgrid(b,k)
with these I produce z1 and z2, massive formulas, but they both use b and k:
z1 = ((0.5*rho*k**2 * Vd**2 * c)*(Cl * 0.1516*b**3 +
Cd*(((b*np.sqrt(b**2 * k**2 +1))/(2*k**2)) -
((np.log(np.sqrt(b**2 * k**2 + 1) + b*k))/(2*k**3)))) - F)
z2 = ((Cl * 0.1516 * b**3 * k**(-1)) -
((Cd/(8*k**4))*((3*np.log(np.sqrt(b**2 * k**2 + 1) + b*k)) +
(b*np.sqrt(b**2 * k**2 +1)*(2*b**2 * k**2 -3)*k))))
Now I know how to find the closest point at which z1 and z2 are zero. Just like below:
print min(z1[(-0.1<z1)&(z1<0.1)]), min(z2[(-0.1<z2)&(z2<0.1)])
but with these I only get the z-value which gives me a close value to zero. What I need is to find which b and k values correspond to that given result of either z1 or z2.
I tried to index it, but I seem not to do it correctly.
In this case, your "is close to zero" expression (-0.1<z1)&(z1<0.1) is an array of booleans. To find the indices of the True items you simply need to use nonzero().
(-0.1<z1) & (z1<0.1).nonzero()
For example:
>>> np.array([False, False, True, False, True, True, False]).nonzero()
(array([2, 4, 5]),)
Why not using argmin on the absolute value of z1 and z2?
Example for how to use argmin:
>>> t = np.random.rand(4,3)
>>> t
array([[ 0.55809884, 0.34630428, 0.78713403],
[ 0.15125035, 0.07231524, 0.06975387],
[ 0.63233302, 0.48026985, 0.21420412],
[ 0.32713745, 0.95168682, 0.34994332]])
>>> np.min(t)
0.069753866909461171
>>> np.argmin(t)
5
>>> t[5//3, 5%3] # 5 is the argmin, 3 is my number of columns
0.069753866909461171