Vector definition through sympify method in Sympy - python

I use Sympy in my Python project, were the string definition of the expression converted to Sympy through sympify method. For example:
import sympy as sp
exp1 = sp.sympify('Add(Rational(1, 5), pi)')
While I am able to describe almost all functionality, I ran into the problem of Vector description to be understood by sympify method. Vector definition in normal way:
from sympy.vector import CoordSys3D
R = CoordSys3D('R')
v = 3*R.i + 4*R.j + 5*R.k
Next example (one of a variety) is failing:
exp2 = sp.sympify('Vector(Coord3D().i*3, Coord3D().j*4, Coord3D().k*5')
I would like to keep Vector format to support the curl, divergence and gradient functionality. Definition of 1-row matrix does not suit here for me. Documentation research did not give results.
I would be grateful for an example or suggestion of the right way for the sympy.vector definition for the sympify method.

If you pass in a locals dictionary, will that do what you want:
>>> from sympy.vector import *
>>> sympify('Vector(R.i*3, R.j*4, R.k*5)',
... locals=dict(R=CoordSys3D('R'), Vector=Vector))
Vector(3*R.i, 4*R.j, 5*R.k)
>>> type(_)
<class 'sympy.vector.vector.Vector'>

Related

How to solve the 'mul objective has no attribute 'cos' '

How to solve the multi object problem?
import sympy as sym
from sympy import lambdify
x = sym.Symbol('x')
n = sym.Symbol('n')
f = sym.sin(n*x)
derivative_f = f.diff(x)
derivative_f = lambdify(x, derivative_f)
x = float(input('x:'))
print(derivative_f(x))
print(derivative_f)
If I input 2, the expected result should be 2*cos(2*x).
Your code contains a few misconceptions. One problem is an important general programming rule: try to use different variable names for variables with different meanings. So, x shouldn't be assigned a float, as it was a symbolic variable before. And derivative_f being a symbolic expression, shouldn't be assigned the result of lambdify.
Note that sympy's symbolic world doesn't mix well with the numeric world of non-sympy functions. lambdify forms a bridge between these worlds, from completely symbolic to completely numeric. E.g. the function created with lambdify doesn't have access to the symbolic n anymore.
The code lambdify(x, derivative_f) contains an error. derivative_f is a symbolic expression containing two symbolic variables (x and n), so it needs to be called as derivative_f_x_n = lambdify((x, n), derivative_f) (also giving the result a different name). Afterwards, you can use numeric expressions as derivative_f_x_n(7, 8), but you can't use symbolic parameters anymore.
For what you seem to be trying to do, lambdify isn't adequate. To get the derivative with x substituted, you call .subs(x, new_value) directly on the symbolic version of derivative_f:
import sympy as sym
from sympy import lambdify
x = sym.Symbol('x')
n = sym.Symbol('n')
f = sym.sin(n * x)
derivative_f_x = f.diff(x)
x_float = 2.0
print(derivative_f_x.subs(x, x_float))
Output: n*cos(2.0*n)
Also note that sympy strongly prefers to work with exact symbolic expressions, and using floats inevitably brings in approximations. Whenever possible, integers, sympy fractions (sym.S(1)/2) or symbolic expressions (sym.sqrt(5)) are recommended.
You call also use the derivative with respect to x and then substitute n:
print(f.diff(x).subs(n, 2))
Output: 2*cos(2*x)
To use that function later on in numeric calculations, after substitution you only have one symbolic variable left(x):
g = lambdify(x, f.diff(x).subs(n, 2))
You can type help(g) to see its generated source code:
Source code:
def _lambdifygenerated(x):
return (2*cos(2*x))
Then you can use g e.g. to create matplotlib plot. After lambdify nothing is symbolic anymore.
import matplotlib.pyplot as plt
import numpy as np
xs = np.linspace(0, 10)
plt.plot(xs, g(xs))
The other answer hit the key point, that you needed to account for the symbolic n when lambdifying.
Look at the help of your function:
In [2]: help(derivative_f)
Help on function _lambdifygenerated:
_lambdifygenerated(x)
Created with lambdify. Signature:
....
Source code:
def _lambdifygenerated(x):
return (n*cos(n*x))
It's a function of x, drawing n from the global environment, here a symbol.
The full error message when given an array argument:
In [5]: derivative_f(np.array([.1,.2,.3]))
AttributeError: 'Mul' object has no attribute 'cos'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<ipython-input-5-e04e9f3c0a8e>", line 1, in <module>
derivative_f(np.array([.1,.2,.3]))
File "<lambdifygenerated-1>", line 2, in _lambdifygenerated
return (n*cos(n*x))
TypeError: loop of ufunc does not support argument 0 of type Mul which has no callable cos method
See the return line in the traceback. That's the 'source code' in the help, and tells you what numpy is trying to execute.
test the inner expression of that line:
In [6]: n*np.array([.1,.2,.3])
Out[6]: array([0.1*n, 0.2*n, 0.3*n], dtype=object)
This is an object dtype array. Math on such an array delegates the calculation to each of the elements. Specifically it tries
(0.1*n).cos()
0.1*n is a sympy mul expression, hence the error. symbols often don't work in numpy expressions.
lambdify is a handy way of creating numpy functions from sympy expressions, but it needs to be used with care, and full awareness of what it does, and does not, do.

Sympy subs not outputting a substituted expression?

I am using sympy to differentiate a function in python. After differentiating the function, I would like to add in the numerical value of the variable that I differentiated with. However, using .subs() does not return a different answer. Does anyone have an idea s to what my issue is?
Code:
CA1 = CA0 * sympy.exp(-(A1*sympy.exp(-E1/(R*T)))*t)
dCa_dA12 = diff(CA1, A1)
print("No substitution:", dCa_dA12)
dCa_1 = dCa_dA12.subs(A1, theta[0])
print("Substitution:", dCa_1)
Output:
I had the same problem and was tinkering around a bit:
This works:
>>> sympify("k").evalf(subs={"k":1})
1.00000000000000
This doesn't work:
>>> sympify("k+x").evalf(subs={"k":1})
k + x
This again works:
>>> sympify("k+x").evalf(subs={"k":1, "x":2})
3.00000000000000
So it seems the substitution doesn't work if the result is not a number. Strangely, this only applies to the subs part:
>>> sympify("2/3*x")
2*x/3
>>> sympify("2/3*x").evalf()
0.666666666666667*x
This looks like a bug to me. At least, it should be documented properly.

Unexpected behaviour when parsing string with sympy

I'm trying to perform the derivative of an equation with sympy, but, while if I write the equation by hand the derivative is correct; when I pass the equation as a string, the output is wrong. Can anyone explain me how to solve this issue? I'm using python 3.6 and sympy 1.5.1.
>>>from sympy import *
>>>from operator import *
>>> x1 = symbols('x1')
>>> f = add(sin(x1), mul(x1, x1))
>>> diff(f, x1)
2*x1 + cos(x1) ## Correct output
>>>> f = 'add(sin(x1), mul(x1, x1))' ## Equation provided as string
>>>> diff(f, x1)
(Subs(Derivative(mul(_xi_1, x1), _xi_1), _xi_1, x1) + Subs(Derivative(mul(x1, _xi_2), _xi_2), _xi_2, x1))*Subs(Derivative(add(sin(x1), _xi_2), _xi_2), _xi_2, mul(x1, x1)) + cos(x1)*Subs(Derivative(add(_xi_1, mul(x1, x1)), _xi_1), _xi_1, sin(x1)) ## Wrong output
This is happening because f = 'add(sin(x1), mul(x1, x1))' is not a valid mathematical equation that can be parsed by parse_expr. This function is designed to parse equations written in mathematical syntax, not in terms of Sympy functions. To get this function in particular to be parsed correctly, you would need to use, for example:
>>> f = 'sin(x1) + x1^2'
>>> diff(f, x1)
2*x1 + cos(x1)
If you really need to use that specific string, you could use eval():
>>> f = 'add(sin(x1), mul(x1, x1))'
>>> diff(eval(f), x1)
2*x1 + cos(x1)
If you want to write it in this fashion, be sure to use the actual SymPy object names (which are capitalized). I use S(...) to interpret the expression and that is the same thing that any function would do, too:
>>> S('Add(sin(x1), Mul(x1, x1))')
x1**2 + sin(x1)
But you can also use the mathematical operators + and *:
>>> S('sin(x1) + x1*x1')
x1**2 + sin(x1)
You shouldn't pass strings directly to SymPy functions. Rather, first parse them with sympify (which is the same as S). You can pass a dictionary of names as the second argument to sympify if you want non-standard names like add to map to existing SymPy ones, like
sympify('add(x, y)', {'add': Add}) # Gives x + y
Otherwise sympify will assume that any unknown functions are undefined functions like f(x).
Sympy provides parser functions to turn string into sympy object, see doc parse_expr.
For custom functions translation you can make a mapping with those provide by sympy and pass it as argument, local_dict, to the parser.
from sympy import Add, Mul, sin
from sympy.parsing.sympy_parser import parse_expr
f = parse_expr('add(sin(x1), mul(x1, x1))', local_dict={'add': Add, 'mul': Mul})
f.diff('x1')
Output
2x1 + cos(x1)

sympy parser doesn't identify expm1 as a symbolic function

The function expm1 is not parsed properly in the following example:
from sympy.parsing.sympy_parser import parse_expr
print parse_expr('expm1(x)').diff('x')
gives
Derivative(expm1(x), x)
How can I get sympy identifying expm1 as symbolic function, so that I get the same result as
print parse_expr('exp(x) - 1').diff('x')
which gives exp(x)?
Since there is no built-in expm1 in SymPy, the parser does not know anything about this notation. The parameter local_dict of parse_expr can be used to explain the meaning of unfamiliar functions and symbols to SymPy.
expm1 = lambda x: exp(x)-1
parse_expr('expm1(x)', local_dict={"expm1": expm1})
This returns exp(x) - 1.
For expm1 to remain a single function with known derivative, rather than exp(x)-1, define it as a SymPy function (see tutorial for more such examples).
class expm1(Function):
def fdiff(self, argindex=1):
return exp(self.args[0])
A confirmation that this works:
e = parse_expr('expm1(x)', local_dict={"expm1": expm1})
print(e) # expm1(x)
print(e.diff(x)) # exp(x)
f = lambdify(x, e)
print(f(1)) # 1.718281828459045
print(f(1e-20)) # 1e-20, unlike exp(x)-1 which would evaluate to 0

Python (SymPy, SciPy), create symbol lambda from string

I've struggling to take text inputs of an equation and evaluate it as a definite integral. I need a callable function to pass to scipy.integrate.
eq = "x**2"
func = lambda x: eq
func(2)
# outputs:
# x**2
# but if I:
func = lambda x: x**2
func(2)
# outputs:
# 4
Not sure, but maybe you are looking for
eq = "x**2"
func = eval("lambda x: " + eq)
Note that using eval() is dangerous if eq is from an untrusted source (e.g. user input).
You need to use eval to run eq as code and not treat it as a string.
eq = "x**2"
func = lambda x: eval(eq)
func(2)
# outputs:
# 4
sympify may be what you are looking for. It converts a string expression into an sympy object.
For example:
import sympy as sp
f=sp.sympify('x**2+sin(y)')
And you can use autowrap to convert a sympy object into a callable function.
Try asteval or numexpr for possibly safer alternatives to eval() and Sympy.sympify().evalf().
I continually got a syntax error for the following and relentlessly was trying to find out why:
E="2x+1"
F=lambda x: (x, eval(E)) # to get an xy coordinate
But the issue worked as expected when I changed E to:
E="2*x+1"
Rookie mistake. :)

Categories