Reputation: 329
I wrote an expression f in SymPy as shown in the code and then converted into a function using lambdify
. Then, I vectorzed it using np.vectorize(f)
to be able to apply it on a numpy array.
import numpy as np
from math import exp
a = Symbol('a')
x = Symbol('x')
b = Symbol('b')
c = Symbol('c')
from sympy import *
f = exp(-(a+b+c)*x)*(4+exp(-(a+b)*x) -2*exp(-a*x) - 2*exp(-c*x))
f = lambdify([x,(a,b,c)], f)
vff = np.vectorize(f)
t = np.arange(0, 5, 0.01, dtype=np.float64)
y = vff(t, (1,1,1)) # (1,1,1) stands for (a,b,c)
But while doing so, the last line throws following error.
TypeError: _lambdifygenerated() missing 1 required positional argument: '_Dummy_191'
.
I think the syntax might be wrong. I searched on internet, but could not find the right syntax. Can anyone tell me the correct syntax?
Upvotes: 1
Views: 741
Reputation: 231605
In [8]: print(f.__doc__)
Created with lambdify. Signature:
func(x, arg_1)
Expression:
(exp(x*(-a - b)) + 4 - 2*exp(-c*x) - 2*exp(-a*x))*exp(x*(-a - b - c))
Source code:
def _lambdifygenerated(x, _Dummy_166):
[a, b, c] = _Dummy_166
return ((exp(x*(-a - b)) + 4 - 2*exp(-c*x) - 2*exp(-a*x))*exp(x*(-a - b - c)))
Imported modules:
f
can be run with:
In [9]: f(np.arange(3),(1,1,1))
Out[9]: array([1. , 0.13262366, 0.00861856])
Since f
works with an array input, I don't think you need the vectorized
form.
I get your error if I call vff
with one argument:
In [14]: vff(np.arange(3))
---------------------------------------------------------------------------
...
TypeError: _lambdifygenerated() missing 1 required positional argument: '_Dummy_166'
With 2 arguments I get a different error:
In [15]: vff(np.arange(3),(1,1,1))
---------------------------------------------------------------------------
...
<lambdifygenerated-1> in _lambdifygenerated(x, _Dummy_166)
1 def _lambdifygenerated(x, _Dummy_166):
----> 2 [a, b, c] = _Dummy_166
3 return ((exp(x*(-a - b)) + 4 - 2*exp(-c*x) - 2*exp(-a*x))*exp(x*(-a - b - c)))
TypeError: 'numpy.int64' object is not iterable
That's because vectorize
is passing scalar 1
as the second argument. vectorize
passes scalar tuples to the function, not arrays. It's designed from functions that take scalar arguments, not arrays or even tuples. There are some ways around that - but keep in mind that vectorize
is not a speed tool.
Since you don't want vectorize
to iterate on the (a,b,c) argument, we can 'exclude' it:
In [16]: vff = np.vectorize(f, excluded=[1])
In [17]: vff(np.arange(3),(1,1,1))
Out[17]: array([1. , 0.13262366, 0.00861856])
That works the same as f
(above). But it is much slower:
In [18]: timeit vff(np.arange(300),(1,1,1))
5.29 ms ± 11.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [19]: timeit f(np.arange(300),(1,1,1))
103 µs ± 41 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
vectorize
is even slower than a list comprehension:
In [20]: timeit np.array([f(i,(1,1,1)) for i in range(300)])
3.91 ms ± 6.17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Upvotes: 2