Reputation: 260
I have a function in sympy f(M,X) which is linear in V but nonlinear in X, both large n dimensional vectors. Give some X, how do I extract the linear vector that gives this function? At the moment I just lambdify and evaluate it for all the unit vectors in M, which seems very inefficient.
For a very simple example f([m1,m2],[x1,x2])= m1*(x1*x2) + m2*x2**2
. Given x=[1,2] I want to get [2,4] since f([m1,m2],[1,2])= m1*2 + m2*4
, without computing both f([1,0],[1,2]) and f([0,1],[1,2]) separately.
EDIT: added code below and fixed equations above.
Here's the code for the simple example:
import sympy as sym
m1,m2,x1,x2 = sym.symbols('m1,m2,x1,x2')
f= m1*x1*x2 + m2*x2**2
Lf = sym.lambdify([m1,m2,x1,x2],f)
m1_coeff = Lf(1,0,1,2)
m2_coeff = Lf(0,1,1,2)
print(m1_coeff,m1_coeff)
**output**= 2 2
Here's a slightly more complicated version that shows how inefficient this method seems to be:
import sympy as sym
import numpy as np
n=5
m= sym.symarray('m',n)
x= sym.symarray('x',n)
f= sum(m[i]*x[i]**2 for i in range(n))
Lf = sym.lambdify([m,x],f,'numpy')
x_arr=np.arange(n)
m_coeff=np.zeros(n)
for i in range(n):
m_arr = np.zeros(n)
m_arr[i] = 1
m_coeff[i] = Lf(m_arr,x_arr)
print(m_coeff)
**output** = [ 0. 1. 4. 9. 16.]
Upvotes: 0
Views: 155
Reputation: 231605
In an isympy
session:
In [1]: m1,m2,x1,x2 = symbols('m1,m2,x1,x2')
...: f= m1*x1*x2 + m2*x2**2
...: Lf = lambdify([m1,m2,x1,x2],f)
In [2]: f
Out[2]:
2
m₁⋅x₁⋅x₂ + m₂⋅x₂
In [9]: print(Lf.__doc__)
Created with lambdify. Signature:
func(m1, m2, x1, x2)
Expression:
m1*x1*x2 + m2*x2**2
Source code:
def _lambdifygenerated(m1, m2, x1, x2):
return (m1*x1*x2 + m2*x2**2)
In sympy we can use subs
:
In [11]: f.subs(dict(x1=1, x2=2))
Out[11]: 2⋅m₁ + 4⋅m₂
And get the coeffs (see that method's docs):
In [15]: f.subs(dict(x1=1, x2=2)).coeff(m1)
Out[15]: 2
In [16]: f.subs(dict(x1=1, x2=2)).coeff(m2)
Out[16]: 4
With the Python function, we can pass array arguments:
In [20]: Lf(np.array([1,0]),np.array([0,1]),1,2)
Out[20]: array([2, 4])
(don't pass lists, which have different meanings for * and +).
In [22]: n=5
...: m= symarray('m',n)
...: x= symarray('x',n)
...: f= sum(m[i]*x[i]**2 for i in range(n))
...: Lf = lambdify([m,x],f,'numpy')
In [23]: f
Out[23]:
2 2 2 2 2
m₀⋅x₀ + m₁⋅x₁ + m₂⋅x₂ + m₃⋅x₃ + m₄⋅x₄
In [24]: print(Lf.__doc__)
Created with lambdify. Signature:
func(m, x)
Expression:
m_0*x_0**2 + m_1*x_1**2 + m_2*x_2**2 + m_3*x_3**2 + m_4*x_4**2
Source code:
def _lambdifygenerated(_Dummy_22, _Dummy_23):
[m_0, m_1, m_2, m_3, m_4] = _Dummy_22
[x_0, x_1, x_2, x_3, x_4] = _Dummy_23
return (m_0*x_0**2 + m_1*x_1**2 + m_2*x_2**2 + m_3*x_3**2 + m_4*x_4**2)
Using subs
with a dictionary:
In [25]: x
Out[25]: array([x_0, x_1, x_2, x_3, x_4], dtype=object)
In [27]: {i:j for i,j in zip(x,np.arange(5))}
Out[27]: {x_0: 0, x_1: 1, x_2: 2, x_3: 3, x_4: 4}
In [28]: f.subs({i:j for i,j in zip(x,np.arange(5))})
Out[28]: m₁ + 4⋅m₂ + 9⋅m₃ + 16⋅m₄
The numpy
version is simpler - though it may be harder to visualize :)
In [29]: Lf(np.eye(5),np.arange(5))
Out[29]: array([ 0., 1., 4., 9., 16.])
Upvotes: 2