amzon-ex
amzon-ex

Reputation: 1744

SymPy: lambdify with 2-D inputs

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

Answers (2)

Oscar Benjamin
Oscar Benjamin

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

JohanC
JohanC

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

Related Questions