Problem
I am working with an arbitrary MxN numpy matrix where each element is a sympy expression, potentially with differing symbols. For the purposes of visualization, let's work with the following matrix test
import sympy as sp
import numpy as np
a,b,c=sp.symbols('a b c');
test=np.array([[a**2,a+b],[a*c+b,b/c]]);
When run, test will look like:
In [25]: test
Out[25]:
array([[a**2, a + b],
[a*c + b, b/c]], dtype=object)
I would like to be able to replace one variable in this array with a number and return a new array that has the same dimensions as test but has replaced the specified variable with it's new value. For example, if I wanted to replace b with 2, the new array should look like:
array([[a**2, a + 2],
[a*c + 2, 2/c]], dtype=object)
Attempt at a Solution
I first tried to use the sympy function subs but I received the following error:
test.subs({b:2})
Traceback (most recent call last):
File "<ipython-input-29-a9a04d63af37>", line 1, in <module>
test.subs({b:2})
AttributeError: 'numpy.ndarray' object has no attribute 'subs'
I looked at using lambdify but I believe that it returns a numeric lambda expression which isn't what I want. I need a new symbolic expression, just one that doesn't depend on b anymore. I found some literature in the Wolfram Mathematica documentation under pattern matching that seems to be what I need http://www.wolfram.com/language/fast-introduction-for-programmers/en/patterns/ but I can't figure out how to implement this in Python, or if it's even possible to do so. Any help would be greatly appreciated.
Just use sympy. No need for numpy, at least not for the substitution:
In [117]: import sympy
In [118]: a,b,c=sympy.symbols('a b c')
In [120]: M=sympy.Matrix([[a**2, a+b],[a*c+b, b/c]])
In [121]: M
Out[121]:
Matrix([
[ a**2, a + b],
[a*c + b, b/c]])
In [123]: M.subs({b:2})
Out[123]:
Matrix([
[ a**2, a + 2],
[a*c + 2, 2/c]])
array is not an expression, therefore it has not the method subs
Related
I need to solve an equation, but spider returns me this error:
import sympy as sym
import sympy as sympy
Re=100
Epsilon=0.00000075
D=0.01
f=symbols('f')
eq=(((1/(-2* sympy.log((Epsilon/D/ 3.7)+( 2.51 / (Re*(f**0.5))), 10)))**2 -f),0)
sym.solve((eq), (f))
print(f)
NotImplementedError: could not solve -4*f*log(202702702702703/10000000000000000000 + 251/(10000*sqrt(f)))**2 + log(10)**2
The solve function is for finding analytic solutions but that isn't possible for your equation (not all equations have analytic solutions):
In [11]: eq[0]
Out[11]:
2
log (10)
-f + ──────────────────────────────────────────
2⎛ -0.5 ⎞
4⋅log ⎝0.0251⋅f + 2.02702702702703e-5⎠
You can find a numeric solution with nsolve:
In [12]: sym.nsolve(eq[0], f, 0.1)
Out[12]: 0.169438052045717
https://docs.sympy.org/latest/modules/solvers/solvers.html#sympy.solvers.solvers.nsolve
This can be represented in terms of the LambertW function, but it needs some help. SymPy doesn't recognize the inverted sqrt(f) in the log. If you replace that with 1/y and solve for y and transform the solutions for y back to f you can get a symbolic solution.
It's easier in this case to let SymPy work with symbols instead of Floats and substitute the values later. Your equation looks like this:
>>> from sympy import a,b,c
>>> seq = a**2/log(b/sqrt(f) + c)**2 - f
>>> reps = {a:log(10)/2, c:Epsilon/D/3.7, b:2.51/Re}
Replace that sqrt(f) with 1/y
>>> ysol = solve(seq.xreplace(sqrt(f), 1/y), y)
Calculate the corresponding solutions for f:
>>> fsol = [((1/i)**2) for i in ysol]
Substitute in your values -- or any values that you are interested in:
>>> [i.xreplace(reps).n(3) for i in fsol]
[0.00771 - 0.107*I, 0.169]
Testing in the original shows that the second solution is valid
>>> [eq.subs(f,i).n() for i in _]
[0.148289010493944 + 0.206688429851791*I, 6.05179945288758e-6]
So your symbolic solution is
>>> fsol[1]
(-c/b + LambertW(a*exp(a*c/b)/b)/a)**(-2)
The nice thing about this form is that you can substitute any values of a,b,c and get the corresponding value of f. Whether this is faster than using nsolve, you will have to see.
I just accidentally discovered that I can mix sympy expressions up with numpy arrays:
>>> import numpy as np
>>> import sympy as sym
>>> x, y, z = sym.symbols('x y z')
>>> np.ones(5)*x
array([1.0*x, 1.0*x, 1.0*x, 1.0*x, 1.0*x], dtype=object)
# I was expecting this to throw an error!
# sum works and collects terms etc. as I would expect
>>> np.sum(np.array([x+0.1,y,z+y]))
x + 2*y + z + 0.1
# dot works too
>>> np.dot(np.array([x,y,z]),np.array([z,y,x]))
2*x*z + y**2
>>> np.dot(np.array([x,y,z]),np.array([1,2,3]))
x + 2*y + 3*z
This is quite useful for me, because I'm doing both numerical and symbolic calculations in the same program. However, I'm curious about the pitfalls and limitations of this approach --- it seems for example that neither np.sin nor sym.sin are supported on Numpy arrays containing Sympy objects, since both give an error.
However, this numpy-sympy integration doesn't appear to be documented anywhere. Is it just an accident of how these libraries are implemented, or is it a deliberate feature? If the latter, when is it designed to be used, and when would it be better to use sympy.Matrix or other solutions? Can I expect to keep some of numpy's speed when working with arrays of this kind, or will it just drop back to Python loops as soon as a sympy symbol is involved?
In short I'm pleased to find this feature exists, but I would like to know more about it!
This is just NumPy's support for arrays of objects. It is not specific to SymPy. NumPy examines the operands and finds not all of them are scalars; there are some objects involved. So it calls that object's __mul__ or __rmul__, and puts the result into an array of objects. For example: mpmath objects,
>>> import mpmath as mp
>>> np.ones(5) * mp.mpf('1.23')
array([mpf('1.23'), mpf('1.23'), mpf('1.23'), mpf('1.23'), mpf('1.23')],
dtype=object)
or lists:
>>> np.array([[2], 3])*5
array([list([2, 2, 2, 2, 2]), 15], dtype=object)
>>> np.array([2, 3])*[[1, 1], [2]]
array([list([1, 1, 1, 1]), list([2, 2, 2])], dtype=object)
Can I expect to keep some of numpy's speed when working with arrays of this kind,
No. NumPy object arrays have no performance benefits over Python lists; there is probably more overhead in accessing elements than would be in a list. Storing Python objects in a Python list vs. a fixed-length Numpy array
There is no reason to use such arrays if a more specific data structure is available.
I just came across a relevant note in the latest numpy release notes (https://docs.scipy.org/doc/numpy-1.15.1/release.html)
Comparison ufuncs accept dtype=object, overriding the default bool
This allows object arrays of symbolic types, which override == and other operators to return expressions, to be compared elementwise with np.equal(a, b, dtype=object).
I think that means this works, but didn't before:
In [9]: np.array([x+.1, 2*y])==np.array([.1+x, y*2])
Out[9]: array([ True, True])
I have written a script in python that uses sympy to compute a couple of vector/matrix formulas. However, when I try to convert those to functions that I can evaluate with sympy.lambdify, I get a
SyntaxError : EOL while scanning string literal
Here's some code with the same error, so that you can see what I mean.
import sympy
x = sympy.MatrixSymbol('x',3,1)
f = sympy.lambdify(x, x.T*x)
So, the syntax error has to do with the expression "x'.dot(x)" and the conversion of ".T" to '.
How can I work around this to correctly define f from the above lambdify?
Found a work around, although not the cleanest looking solution... but it works.
Use the implemented_function() method from sympy to define your function. Read full documentation here: http://docs.sympy.org/latest/modules/utilities/lambdify.html
Here is the code:
import sympy
import numpy as np
from sympy.utilities.lambdify import implemented_function
x = sympy.MatrixSymbol('x',3,1)
f = implemented_function(sympy.Function('f'), lambda x: x.T*x)
lam_f= sympy.lambdify(x, f(x))
Hope this solves your problem :)
It has been solved in sympy version >= 1.1
Edit:
Example
when u define this
x = sympy.MatrixSymbol('x',3,1)
you are creating a matrix,
you can check its indexing and shape using
print(sympy.Matrix(x))
Now that you want to multiply Transpose of x to x, you will have to give x a matrix of same shape that you have defined before
here try this:
from sympy import MatrixSymbol, lambdify, Matrix
x = MatrixSymbol('x', 3, 1)
f = lambdify(x, x.T*x)
a = Matrix([[1], [2], [3]])
print(f(a))
you can check this link out to understand lambdify better:
http://docs.sympy.org/latest/modules/utilities/lambdify.html
I suggested it could be
np.linalg.inv(np.sqrt(matrix))
but having compared result with MATLAB I saw big difference:
This was in MATLAB
0.2622 -0.0828 -0.0708
-0.0828 0.2601 -0.0792
-0.0708 -0.0792 0.2664
And this was in Python:
0.8607 -0.4417 -0.3536
-0.4417 0.8967 -0.4158
-0.3536 -0.4158 0.8525
Input was
34.502193 27.039107 24.735074
27.039107 36.535737 26.069613
24.735074 26.069613 32.798584
There is no "matrix" class in python. From your code it looks you're talking about numpy.
A possible gotcha for matlab users is that in numpy array operations are elementwise by default, and if you want matrix operations, you need to request them: np.dot for matrix multiplications, np.linalg.inv for inversion etc.
np.linalg.inv(np.sqrt(a)) first takes the square root of each element of a, and then inverts the result in the linear algebra sense. I suspect this is not what you meant to mean.
If you meant elementwise operations, i.e. you wanted to raise each element to power -1/2, then like #Benoit_11 suggests, use
1 / np.sqrt(a).
If what you want is actually a linear algebra operation, then use scipy.linalg.sqrtm
In [14]: a
Out[14]:
array([[ 34.502193, 27.039107, 24.735074],
[ 27.039107, 36.535737, 26.069613],
[ 24.735074, 26.069613, 32.798584]])
In [15]: from scipy.linalg import sqrtm
In [16]: sq = sqrtm(a)
In [17]: np.dot(sq, sq) - a
Out[17]:
array([[ 4.97379915e-14, 4.97379915e-14, 2.84217094e-14],
[ 5.32907052e-14, 6.39488462e-14, 4.61852778e-14],
[ 3.55271368e-14, 3.19744231e-14, 3.55271368e-14]])
It looks like using Python you calculated the inverse of the square root of the matrix (sounds weird sorry) instead of raising the matrix to the power -0.5.
For instance, running this command with Matlab I get your output with python:
m = [34.502193 27.039107 24.735074
27.039107 36.535737 26.069613
24.735074 26.069613 32.798584]
A = inv(sqrt(m))
A =
0.8608 -0.4417 -0.3537
-0.4417 0.8967 -0.4159
-0.3537 -0.4159 0.8525
versus this:
B = m^(-.5)
B =
0.2622 -0.0828 -0.0708
-0.0828 0.2601 -0.0792
-0.0708 -0.0792 0.2664
For the correct Python code please look at #ev-br's answer
Beware that there is such a thing as the matrix square root, which for a matrix M is defined as:
A*A = M
and does not correspond at all to the square root of each element in the matrix M taken individually. The matrix square root is obtained in Matlab using the sqrtm function and is equivalent to m^(.5).
I am using Python with Sympy.
I need to solve the following equation, finding the 4 roots (omega is my unknown):
deter= 0.6*omega**4*cos(omega*t)**2 - 229.0*omega**2*cos(omega*t)**2 + 5880.0*cos(omega*t)**2
I tried to use solve:
eqcarr=solve(deter,omega,exclude=[t])
I get this output:
[-18.8143990830350, -5.26165884593044, 5.26165884593044, 18.8143990830350, 1.5707963267949/t, 4.71238898038469/t]
I only need the first 4 values, and not the values with the t coefficient. I expect the cos(omega*t)**2 to be simplified in solve, but this doesn't happen.
According to documentation solve will not solve for any of the free symbols passed in the exclude.
'exclude=[] (default)'
don't try to solve for any of the free symbols in exclude;
if expressions are given, the free symbols in them will
be extracted automatically.
It is not meant to filter solution.
You can solve your problem by doing this:
In [10]: from sympy import *
In [11]: from sympy.abc import omega, t
In [12]: deter= 0.6*omega**4*cos(omega*t)**2 - 229.0*omega**2*cos(omega*t)**2 + 5880.0*cos(omega*t)**2
In [13]: eqcarr=solve(deter,omega,exclude=[t])
In [14]: filtered = [i for i in eqcarr if not i.has(t)]
In [15]: filtered
Out[15]: [-18.8143990830350, -5.26165884593044, 5.26165884593044, 18.8143990830350]