Reputation: 1744
I'm computing a square matrix V
, each element of which is an integral that I compute with sympy
. I compute only one definite integral V_nm
, the result of which is a numerical expression with symbolic indices m
and n
. Say V_nm
looks like this:
>>> V_nm
sin(3*n)*cos(m)
Now I wish to make a 2-D numerical (not symbolic!) matrix out of V_nm
using m
and n
as indices of the array. Say for a 2 x 2 matrix, the result for the given V_nm
would be:
[[sin(3)cos(1) sin(3)cos(2)]
[sin(6)cos(1) sin(6)cos(2)]]
i.e., n
specifies the column and m
specifies the rows. (Note: I start m
and n
at 1 and not 0, but that's no concern).
How do I achieve this?
I know I can use V_nm.subs([(n, ...), (m, ...)])
in a list comprehension followed by evalf()
but that's the long route. I wish to achieve this using lambdify
. I know how to use lambdify
for 1-D arrays. Can you please tell me how to implement it for 2-D arrays?
Upvotes: 2
Views: 741
Reputation: 14500
There is sympy's FunctionMatrix
which is intended for this kind of case. Note that it uses zero-based indexing:
In [1]: m, n, i, j = symbols('m, n, i, j')
In [2]: V_nm = FunctionMatrix(m, n, Lambda((i, j), 100*(i+1) + (j+1)))
In [3]: V_nm
Out[3]: [100⋅i + j + 101]
In [4]: V_nm.subs({m:2, n:3}).as_explicit()
Out[4]:
⎡101 102 103⎤
⎢ ⎥
⎣201 202 203⎦
In [5]: lambdify((m, n), V_nm)(2, 3)
Out[5]:
array([[101., 102., 103.],
[201., 202., 203.]])
Upvotes: 2
Reputation: 80449
What you're asking doesn't look like a standard functionality. But in two steps it's possible. First lambdify the expression, and then create a function that generates the intended 2D array via numpy's broadcasting:
from sympy import sin, cos, lambdify
from sympy.abc import m, n
import numpy as np
V_mn = sin(3 * n) * cos(m)
V_mn_np = lambdify((m, n), V_mn)
# using list comprehension:
# V_mn_np2D = lambda m, n: np.array([[V_mn_np(i, j) for j in range(n)] for i in range(m)])
# using numpy's broadcasting (faster for large arrays):
V_mn_np2D = lambda m, n: V_mn_np(np.arange(m)[:, None], np.arange(n))
V_mn_np2D(2, 2)
To have the numbering start at 1 instead of 0, use np.arange(1, m+1)
and np.arange(1, n+1)
.
As a test, a function such as 100 * m + n
makes it easy to verify that the approach works as intended.
W_mn = 100 * m + n
W_mn_np = lambdify((m, n), W_mn)
W_mn_np2D = lambda m, n: W_mn_np(np.arange(1, m+1)[:, None], np.arange(1, n+1))
W_mn_np2D(2, 3)
Output:
array([[101, 102, 103],
[201, 202, 203]])
Upvotes: 1