In [27]: 0*x
Out[27]: 0
Thats way I did the string operation. I also created two test for it.
Thanks! It fails one doctest, that is trivial to fix:
$ ./setup.py test_doc
running test_doc
Testing docstrings.
...........................................................................................................................................................................F.
======================================================================
FAIL: Doctest: sympy.utilities.lambdify.lambdastr
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.5/doctest.py", line 2128, in runTest
raise self.failureException(self.format_failure(new.getvalue()))
AssertionError: Failed doctest test for sympy.utilities.lambdify.lambdastr
File "/home/ondra/ext/sympy/sympy/utilities/lambdify.py", line 27,
in lambdastr
----------------------------------------------------------------------
File "/home/ondra/ext/sympy/sympy/utilities/lambdify.py", line 33, in
sympy.utilities.lambdify.lambdastr
Failed example:
lambdastr([z,y,x], [x,y,z])
Expected:
'lambda x,y,z: (z,y,x)'
Got:
'lambda x,y,z: (z+0*x+0*y,y+0*x+0*z,x+0*y+0*z)'
----------------------------------------------------------------------
Ran 173 tests in 8.401s
FAILED (failures=1)
so I fixed that, and I also moved the numpy tests to test_numpy, see
the attached patch.
However, I am not sure -- do we want lambdify to return stuff like
"0*x" etc? Let's discuss it more and if we agree we do, I'll push it
in.
Ondrej
This was the original aim to get constant functions (to identify numbers
with constant functions).
> >> f = lambdify(1, x)
Hmm, what do you mean? I get this:
In [1]: from numpy import array
In [2]: f=lambdify(1,[x])
In [3]: f(array([0,1,2]))
Out[3]: [1 1 1]
Did you applied the patch to the given revision (befor your changes)?
By, Friedrich
Ok, that's the (sub)problem from #800. In this issue we decided that a
tuple/list means a elementwise evaluation of a function
(numpy behaviour). In this way In[3] means:
0 -> 1
1 -> 1
2 -> 1
...
thats ok, I think. The function f(x) := 1 maps any _number_ to one. If you
what that f(x) := 1 maps vectors to one, we need a new object:
[x,y,z] -> 1
[1,5,3] -> 1
...
Do you know what I mean?
By,
Friedrich
Yes. I think we need some way to distinguish between these two cases,
as clearly, both are needed. Any ideas are welcome.
Ondrej
Thats looks good. Then we should have the elementwise evaluation _only_
with the vectorize function. See at the end for the one (easy) solution of
the problem with constant functions.
> But I'm still not convinced if this should be the default behavior,
> since as I pointed out in my first reply, there might be functions
> that take a matrix and return a scalar. Then the element wise
> evaluation would be wrong.
Thats why we have to distinguish these different objects: scalar, vector,
matrix, tensor, function, distribution, ...
Maybe the sympycore could do this whith the algebra approach?
[...]
> But look at this example:
> >> f = lambdify([x,y], dot(0*x,y)
> >> f([1,2],[1,2])
The input should looks like
>>> f(vector([1,2]), vector([1,2]))
for other input objects the dot function should raise an error.
> wanted:0
> patch:[0,0]
You are right but not quite, it should raise an error.
# Assuming to have a ZeroVector = [0,0,...] object
>>> lambdastr([x,y], dot(ZeroVector, y))
'lambda x,y: dot(ZeroVector, y) + 0*x'
This would add an scalar to a vector which is not defined.
> vectorize:[[0,0],[0,0]]
> Now your vectorize and your patch fails. Vectorize at least is
> consequent and always returns a Matrix, whereas your patch would
> return sometimes a vector and sometimes a scalar.
Right, thats inconsequent.
> However, since both versions give not the wanted answer, I'm not sure
> how we should deal with this.
With the vectorize comment above the way would be
In [1]: F=lambdify(1,[x])
In [2]: F=vectorize(0)(F)
In [3]: F([0,1,2,3])
Out[3]: [1, 1, 1, 1]
And with this we dont need the cumbersome way to add '+0*x'. The vectorize
should be the way to evaluate a function elementwise.
By,
Friedrich
Cool. Let's write a test for this and also let's rebase the lambda
arguments patch and push it in.
Ondrej
For me, any solution is fine as long as it is easy to maintain and it
doesn't break things.
So either way if fine.
Ondrej
Why do you think so? Your following ideas are "only" new structures, right?
> An alternative would be to add a new module like
> "ndimensional", "ndim", "multidimensional" that provides all kind of
I am for "ndim" because its the shortest.
> things for working in more dimensions. So for example matrices would
> go in there and vectorize and, if someone writes it, vector and tensor
> classes. Also special functions like dot, vectorproduct and so on
> should be placed there.
> The point would be, that all functions that are defined in
> sympy.functions for scalars are in the scope sympy.ndim.functions in a
> vectorized form. Also lambdify may get an own vectorized version.
This sounds not bad, but what is your real problem?
> What do you think?
I like the way you did it before. Maybe you could include the vectorize
methode in the Function-Class directly so that every function inherits
the ability for vectorizing?
I mean the "only" thing we need to take care are the signatures of the
functions:
scalar -> scalar
scalar -> vector
vector -> scalar
vector -> vector
...
By, Friedrich
We also have
(4) -- redefine the __new__ for Functions that don't need vectorize
and don't call the decorator, thus all is fine.
I am for [4].
Ondrej
Hm, maybe this is not a problem. Take for example the dot function:
dot : list x list --> scalar
So the dot function should only accept two lists as arguments. The job for
the generalised verctorize decorator would be to take two lists of lists
[listA1, listA2, ...] x [listB1, listB2, ...] --> [scalar1, scalar2, ...]
and return a list of scalars. If we can implement this behaviour for
vectorize then I hope everything is fine, right? Or didnt I catch the
problem?
By,
Friedrich
To get this working the sympy.vectorize should behave like numpy.vectorize.
For now sympy.vectorize behave like this:
In [49]: prod = lambda x,y: x*y
In [50]: vprod = vectorize(0,1)(prod)
In [51]: vprod([2,3], [x,y])
Out[51]: [[2*x, 2*y], [3*x, 3*y]]
The result is ok, but the input for numpy is different:
In [52]: import numpy
In [53]: nvprod = numpy.vectorize(prod)
In [54]: X,Y = numpy.meshgrid([2,3], [x,y])
In [55]: X
Out[55]:
[[2 3]
[2 3]]
In [56]: Y
Out[56]:
[[x x]
[y y]]
In [57]: nvprod(X, Y)
Out[57]:
[[2*x 3*x]
[2*y 3*y]]
I think sympy.vectorize should do the same thing for elementwise
evaluation. And then we can have an generalised vectorize for all
functions. What do you mean?
By, Friedrich
which patch do you mean? The lambdify_change_order.patch from Sebastian?
Friedrich
Yes, thats good :-)
> ""
> To get this working the sympy.vectorize should behave like
> numpy.vectorize.
> ""
>
> ""
> pass a function as creation argument and vectorize then returns a
> function
> that is vectorized in all arguments (or maybe only the first).
> ""
But there is a difference, Out[4] and Out[7] are not the same:
In [1]: import numpy
In [2]: prod = lambda x,y: x*y
In [3]: prod = vectorize(0,1)(prod)
In [4]: prod([2,3], [x,y])
Out[4]: [[2*x, 2*y], [3*x, 3*y]]
In [5]: prod = lambda x,y: x*y
In [6]: prod = num
numbers numerics numpy
In [6]: prod = numpy.ve
In [6]: prod = numpy.vectorize(prod)
In [7]: prod([2,3], [x,y])
Out[7]: [2*x 3*y]
Thats why, numpy uses the meshgrid matrices. Should we port meshgrid to
sympy?
Friedrich
But I think this should be the final solution. The only way to get out
of this problem is to use different objects for the different things
(as we decided before). Then the evaluation of (matrix-like) nested lists
should be unique for vectorize:
# vec is the new proposed object
dot([vec1, vec2], [vec3, vec4]) == [dot(vec1, vec3), dot(vec2, vec4)]
> Thus, as Ondrej pointed out, we should use the current
> implementation of vectorize so that most things work as expected. For
> functions that don't fall into this scheme, the __new__ function has
> to be rewritten.
That sounds good. And if we have the vector-object we dont need to
rewrite any __new__ function (I hope).
> Maybe we should improve the vectorize function so that one can easily
> define that for example the elements the function should be applied
> are the innermost lists or something like that.
Maybe, I am unsure. Lets try to implement the Vector, Matrix object
at first.
> Another, I think useful improvement would be the possibility, to pass
> a function as creation argument and vectorize then returns a function
> that is vectorized in all arguments (or maybe only the first). This
> would look nicer to use for users:
> >> def mycoolfunction(x,y): ...
> >> vectorize(mycoolfunction)
You mean a wrapper around vectorize
def vectorize(func, *args):
return Vectorize(*args)(func)
? (then the capital 'Vectorize' should the class name)
Friedrich
You are right. Its easier not to use the numpy.meshgrid here. And I
have tested the numpy.meshgrid didnt solve our nested list problem :-(
Friedrich
Yeah me too. Feel free to start the wiki page.
Ondrej
Hm, but now I have one thing against the behavior in [9] again. What is one
would only this behaviour:
>>> diff([(x+y)**2, (x+3*y), z**2], [x,y,z])
[2*x + 2*y, 3, 2*z]
So that every item-pair should be evaluted. Then the current solution in
[9] does a littel bit too much. The way out would be to take a meshgrid
port again?
Friedrich
I can not start a new wiki page. Thats why I try to sum up my opinion
here.
*********************************************************************
(1) The argument of a function should be a matrix:
>>> M = Matrix(1,3,[x,y,z])
>>> f = Function('f')
>>> f(M)
f(M)
(1a) With this possibility a convenient vector object could be created:
>>> r = vector(M)
>>> (r[0], r[1], r[2]) == (r.x, r.y, r.z)
True
>>> f(r)
f(r)
I would like to have this r.x syntax because in manualy calculation one
would write r := (r_x, r_y, r_z).
(1b) Then the derivative should be generalised (jacobian matrix)
>>> diff(r.x**2 + r.y**2 + r.z**2, r)
[2*r.x, 2*r.y, 2*r.z]
The result should be a matrix (or a vector in this simpel case).
With this matrix syntax the div, grad, rot operators could be implemented
(Ondrej wrote a script in #823).
(2) All multiple elementwise evaluation of a function should be done
with the vectorize function.
(2a) And I think it's better if sympy.vectorize behaves exactly like
numpy.vectorize because I want this
In [29]: import numpy
In [30]: f=Function('f')
In [31]: F=numpy.vectorize(f)
In [32]: F([x,y], [2,3])
Out[32]: [f(x, 2) f(y, 3)]
And not this
In [33]: F=vectorize(0,1)(f)
In [34]: F([x,y], [2,3])
Out[34]: [[f(x, 2), f(x, 3)], [f(y, 2), f(y, 3)]]
(2b) For the nested evaluations like in [34] the meshgrid should be used
In [36]: F=numpy.vectorize(f)
In [37]: X, Y = numpy.meshgrid([x,y], [2,3])
In [38]: F(X, Y)
Out[38]:
[[f(x, 2) f(y, 2)]
[f(x, 3) f(y, 3)]]
(2c) With the suggestions above the multiple evaluation of functions
with vector arguments should'nt be a problem
>>> v1 = vector(1,2)
>>> dot(v1, v1)
5
>>> dot = vectorize(dot)
>>> v2 = vector(1,3)
>>> dot([v1, v2], [v1, v2])
[5, 10]
*********************************************************************
I hope I explaind my thoughts clearly. What do you think?
By,
Friedrich