Comment @Ophion very well explains what is happening. Calling array() inside f(x) introduces some overhead, which reduces the use of matrix multiplication in the expression h * f(x[step]) .
And, as he says, you might be interested in scipy.integrate look at scipy.integrate for a good set of numerical integrators.
To solve the problem by vectorizing your code, you want to avoid re-creating the array every time you call f . You want to initialize the array once and return it with every call. It looks like the static variable is in C / C ++.
You can achieve this with a mutable default argument, which is interpreted once during the definition of the function f(x) and has a local scope. Since it needs to be changed, you encapsulate it in a list of one element:
def f(x,static_tmp=[empty((2,2))]): static_tmp[0][0]=x[1] static_tmp[0][1]=-x[0] - a * x[1] * (x[0] ** 2 + x[1] ** 2 - 1) return static_tmp[0]
With this modification of your code, the overhead of creating an array disappears, and on my machine I get a slight improvement:
%timeit forward_euler() #258ms %timeit forward_euler_vector() #248ms
This means that the gain in optimizing matrix multiplication using numpy is pretty small, at least on the problem.
You can immediately get rid of the function f by doing your operations in a for loop, getting rid of the overhead of the call. However, this default argument trick can also be applied to scipy more general time integrators, where you must provide the function f .
EDIT: as Jaime noted, another way is to treat static_tmp as an attribute of function f and create it after declaring the function, but before calling it:
def f(x): f.static_tmp[0]=x[1] f.static_tmp[1]=-x[0] - a * x[1] * (x[0] ** 2 + x[1] ** 2 - 1) return f.static_tmp f.static_tmp=empty((2,2))