I use sympy for my study in electrical engineering to get symbolic
expression. After some calculations (most ().diff and ().subs) I want to
evaluate these expressions with numpy arrays, e.g. for plotting or
root-finding. So I wrote my own version of the Lambda class:
class MyLambda():
def __init__(self, *args):
list = [i for i in args]
self.expr = list.pop()
self.vars = list
def apply(self, *args):
self.sub = {}
for v, a in zip(self.vars, args):
self.sub[v.tostr()] = a
import numpy as p
return eval(self.expr.tostr(), self.sub, vars(p))
def __call__(self, *args):
return self.apply(*args)
So I can do something like that:
>>> from sympy import *
>>> var('x y')
(x, y)
>>> f=MyLambda(x,y, x**2+sin(y))
>>> f(2, 0)
4.0
>>> from numpy import linspace, pi
>>> t = linspace(0, 2*pi, 2000)
>>> y = f(2, t)
>>> from pylab import plot, show
>>> plot(t , y)
[<matplotlib.lines.Line2D instance at 0x8bbeb0c>]
>>> show()
And that eval is really fast :-) If you like it, please integrate this
in the sympy environment. I read the source for the sympy Lambda class,
but I dont have the whole overview to do the right things.
By,
Friedrich Hagedorn
thank you very much for sharing the code.
> class MyLambda():
^^ it's interesting, that this works in python2.5, but in python2.4, you need
to delete the "()".
I think we almost have this in SymPy already, see below.
We should implement multivariate lambdas:
http://code.google.com/p/sympy/issues/detail?id=619
But those are meant to return sympy expression and not depend on numpy.
What you need is a fast evaluation of sympy expressions (for plotting
and other use) right?
In SymPy, we also have this:
http://hg.sympy.org/sympy/file/94e3b53cc0a2/sympy/utilities/lambdify.py
Look at the docstrings, it's pretty much self explaining:
28 >>> from sympy import symbols
29 >>> x,y,z = symbols('xyz')
30 >>> lambdastr(x**2, [x])
31 'lambda x: (x**2)'
32 >>> lambdastr([z,y,x], [x,y,z])
33 'lambda x,y,z: (z,y,x)'
and
10 >>> from sympy import symbols, sqrt
11 >>> x,y,z = symbols('xyz')
12 >>> f = lambdify(x**2, [x])
13 >>> f(2)
14 4
15 >>> f = lambdify([z,y,x], [x,y,z])
16 >>> f(1,2,3)
17 (3, 2, 1)
18 >>> f = lambdify(sqrt(x), [x])
19 >>> f(4)
20 2.0
Only the order of arguments of lambdify should be swapped.
That is what we use in sympy plotting.
Here is how you would use it to reproduce your example above:
In [1]: from sympy.utilities.lambdify import lambdastr
In [2]: fstr = lambdastr(x**2+sin(y), [x, y])
In [3]: import numpy as p
In [4]: f=eval(fstr, vars(p))
In [5]: f
Out[5]: <function <lambda> at 0xb67f66f4>
In [6]: fstr
Out[6]: 'lambda x,y: (x**2 + sin(y))'
In [7]: from numpy import linspace, pi
In [8]: t = linspace(0, 2*pi, 2000)
In [9]: y = f(2, t)
In [10]: from pylab import plot, show
In [11]: plot(t , y)
Out[11]: [<matplotlib.lines.Line2D instance at 0xb55c8c4c>]
In [12]: show()
What do you think is the most convenient way of using it?
I think something like creating
lambdify_numpy, or something is enough, isn't it?
The code of such a function will be trivial:
fstr = lambdastr(x**2+sin(y), [x, y])
import numpy as p
return eval(fstr, vars(p))
The other option is to instruct Lambda to use numpy, something like this
Lambda([x, y], x**2+sin(y), use_numpy = True)
But I think, from the users point of view, all I need is just a fast
way to evaluate the expressions and I don't care about lambdas, so a
usage
like this:
f = lambdify_numpy(x**2+sin(y))
t = linspace(0, 2*pi, 2000)
y = f(2, t)
is all I need. I think it is actually even faster than your version,
as the "f" is a regular python lambda function.
Let's discuss this and then implement it and document it.
Ondrej
> f = lambdify_numpy(x**2+sin(y))
> t = linspace(0, 2*pi, 2000)
> y = f(2, t)
> is all I need. I think it is actually even faster than your version,
> as the "f" is a regular python lambda function.
This is Good. However, what is bad from a user point of view is that the
user has to be aware that he is manipulating numpy arrays. Most beginners
don't know this (import numpy is magic for them), they are not really
aware of types. How can this be made magic? I have no solution, I just
know some people are going to hate this, but I hope clever people on this
list are going to find a way to make this friendlier.
Gaël
Yes.
> In SymPy, we also have this:
>
> http://hg.sympy.org/sympy/file/94e3b53cc0a2/sympy/utilities/lambdify.py
>
> Look at the docstrings, it's pretty much self explaining:
>
> 28 >>> from sympy import symbols
> 29 >>> x,y,z = symbols('xyz')
> 30 >>> lambdastr(x**2, [x])
> 31 'lambda x: (x**2)'
> 32 >>> lambdastr([z,y,x], [x,y,z])
> 33 'lambda x,y,z: (z,y,x)'
>
> and
>
> 10 >>> from sympy import symbols, sqrt
> 11 >>> x,y,z = symbols('xyz')
> 12 >>> f = lambdify(x**2, [x])
> 13 >>> f(2)
> 14 4
> 15 >>> f = lambdify([z,y,x], [x,y,z])
> 16 >>> f(1,2,3)
> 17 (3, 2, 1)
> 18 >>> f = lambdify(sqrt(x), [x])
> 19 >>> f(4)
> 20 2.0
>
> Only the order of arguments of lambdify should be swapped.
> That is what we use in sympy plotting.
I read this, but I havent noticed that this fill my needs :-)
BTW, I didnt realized the difference between Lambda and lambdify yet.
> Here is how you would use it to reproduce your example above:
>
> In [1]: from sympy.utilities.lambdify import lambdastr
>
> In [2]: fstr = lambdastr(x**2+sin(y), [x, y])
>
> In [3]: import numpy as p
>
> In [4]: f=eval(fstr, vars(p))
>
> In [5]: f
> Out[5]: <function <lambda> at 0xb67f66f4>
>
> In [6]: fstr
> Out[6]: 'lambda x,y: (x**2 + sin(y))'
>
> In [7]: from numpy import linspace, pi
>
> In [8]: t = linspace(0, 2*pi, 2000)
>
> In [9]: y = f(2, t)
>
> In [10]: from pylab import plot, show
>
> In [11]: plot(t , y)
> Out[11]: [<matplotlib.lines.Line2D instance at 0xb55c8c4c>]
>
> In [12]: show()
Oh, thats fine :-)
> What do you think is the most convenient way of using it?
> I think something like creating
>
> lambdify_numpy, or something is enough, isn't it?
>
> The code of such a function will be trivial:
>
> fstr = lambdastr(x**2+sin(y), [x, y])
> import numpy as p
> return eval(fstr, vars(p))
If you know the tools, its really simple ;-)
> The other option is to instruct Lambda to use numpy, something like this
>
> Lambda([x, y], x**2+sin(y), use_numpy = True)
Thats not bad.
> But I think, from the users point of view, all I need is just a fast
> way to evaluate the expressions and I don't care about lambdas, so a
> usage
> like this:
>
> f = lambdify_numpy(x**2+sin(y))
> t = linspace(0, 2*pi, 2000)
> y = f(2, t)
But I want to define my order of the varibales.
I have no time to think too deep, but my suggestion is:
f = Lambda(x, y, x**2+sin(y)).eval_numpy()
So you can decide if f is a sympy.Function or a Python lambda function
in the numpy namespace. I prefer the Lambda name because it does the same
thing but return different objects.
By,
Friedrich
This is simpel, as far as the names of the functions are equal in sympy
and numpy. Ive found the first difference:
numpy.arccos <-> sympy.acos
By,
Friedrich
Right. Does your code work for this?
I made an issue for it:
http://code.google.com/p/sympy/issues/detail?id=632
Feel free to help us fix it. :)
Ondrej