2016-02-16 34 views
12

मैं sympy में लिखा ODEs की एक प्रणाली है कार्य करने के लिए Convert sympy भाव:NumPy सरणी

from sympy.parsing.sympy_parser import parse_expr 

xs = symbols('x1 x2') 
ks = symbols('k1 k2') 
strs = ['-k1 * x1**2 + k2 * x2', 'k1 * x1**2 - k2 * x2'] 
syms = [parse_expr(item) for item in strs] 

मैं, एक वेक्टर महत्वपूर्ण समारोह में इस कन्वर्ट करने के लिए चाहते हैं एक्स मूल्य का एक -1 डी numpy सरणी को स्वीकार करने, के मानों की एक 1 डी numpy सरणी, उन बिंदुओं पर मूल्यांकन समीकरणों की एक 1 डी numpy सरणी लौट रहा है। हस्ताक्षर कुछ इस तरह दिखेगा:

import numpy as np 
x = np.array([3.5, 1.5]) 
k = np.array([4, 2]) 
xdot = my_odes(x, k) 

कारण मैं चाहता हूँ कुछ इस तरह scipy.integrate.odeint को यह समारोह दे रहा है, तो यह तेजी से होने की जरूरत है।

प्रयास 1: बाद के चरणों

बेशक

, मैं subs के चारों ओर एक आवरण लिख सकते हैं:

def my_odes(x, k): 
    all_dict = dict(zip(xs, x)) 
    all_dict.update(dict(zip(ks, k))) 
    return np.array([sym.subs(all_dict) for sym in syms]) 

लेकिन यह जो बहुत बड़ा है और चलाया जाता है विशेष रूप से मेरे वास्तविक प्रणाली के लिए सुपर धीमी है, कई बार। मुझे इस ऑपरेशन को सी कोड में संकलित करने की आवश्यकता है।

प्रयास 2: थेनो

मैं sympy's integration with theano के साथ निकट प्राप्त कर सकते हैं:

from sympy.printing.theanocode import theano_function 

f = theano_function(xs + ks, syms) 

def my_odes(x, k): 
    return np.array(f(*np.concatenate([x,k])))) 

यह प्रत्येक अभिव्यक्ति है, लेकिन यह सब पैकिंग संकलित करता है तथा इनपुट और आउटपुट के खोल इसे वापस धीमा। theano_function द्वारा लौटाया गया फ़ंक्शन तर्क के रूप में numpy arrays स्वीकार करता है, लेकिन प्रत्येक प्रतीक के लिए प्रत्येक तत्व के बजाय प्रत्येक प्रतीक के लिए एक सरणी की आवश्यकता होती है। यह functify और ufunctify के लिए भी वही व्यवहार है। मुझे प्रसारण व्यवहार की आवश्यकता नहीं है; मुझे सरणी के प्रत्येक तत्व को एक अलग प्रतीक के रूप में समझने की आवश्यकता है।

प्रयास 3: DeferredVector

अगर मैं DeferredVector का उपयोग मैं एक समारोह है कि NumPy सरणी स्वीकार करता है बना सकते हैं, लेकिन मैं इसे सी कोड को संकलित या इसे अपने आप को पैकेजिंग के बिना एक numpy सरणी वापस नहीं लौट सकते।

import numpy as np 
import sympy as sp 
from sympy import DeferredVector 

x = sp.DeferredVector('x') 
k = sp.DeferredVector('k') 
deferred_syms = [s.subs({'x1':x[0], 'x2':x[1], 'k1':k[0], 'k2':k[1]}) for s in syms] 
f = [lambdify([x,k], s) for s in deferred_syms] 

def my_odes(x, k): 
    return np.array([f_i(x, k) for f_i in f]) 

DeferredVector का उपयोग करते हुए मैं आदानों खोल की जरूरत नहीं है, लेकिन मैं अभी भी आउटपुट पैक करने के लिए की जरूरत है। इसके अलावा, मैं lambdify का उपयोग कर सकता हूं, लेकिन ufuncify और theano_function संस्करण नष्ट हो गए हैं, इसलिए कोई तेज़ सी कोड उत्पन्न नहीं हुआ है।

from sympy.utilities.autowrap import ufuncify 
f = [ufuncify([x,k], s) for s in deferred_syms] # error 

from sympy.printing.theanocode import theano_function 
f = theano_function([x,k], deferred_syms) # error 
+0

मैं एक कठिन समय का पालन करना आप का उपयोग कर कोड पोस्ट कर सकते हैं '' subs'' आप की कोशिश की? –

उत्तर

8

आप sympy फ़ंक्शन lambdify का उपयोग कर सकते हैं। उदाहरण के लिए,

from sympy import symbols, lambdify 
from sympy.parsing.sympy_parser import parse_expr 
import numpy as np 

xs = symbols('x1 x2') 
ks = symbols('k1 k2') 
strs = ['-k1 * x1**2 + k2 * x2', 'k1 * x1**2 - k2 * x2'] 
syms = [parse_expr(item) for item in strs] 

# Convert each expression in syms to a function with signature f(x1, x2, k1, k2): 
funcs = [lambdify(xs + ks, f) for f in syms] 


# This is not exactly the same as the `my_odes` in the question. 
# `t` is included so this can be used with `scipy.integrate.odeint`. 
# The value returned by `sym.subs` is wrapped in a call to `float` 
# to ensure that the function returns python floats and not sympy Floats. 
def my_odes(x, t, k): 
    all_dict = dict(zip(xs, x)) 
    all_dict.update(dict(zip(ks, k))) 
    return np.array([float(sym.subs(all_dict)) for sym in syms]) 

def lambdified_odes(x, t, k): 
    x1, x2 = x 
    k1, k2 = k 
    xdot = [f(x1, x2, k1, k2) for f in funcs] 
    return xdot 


if __name__ == "__main__": 
    from scipy.integrate import odeint 

    k1 = 0.5 
    k2 = 1.0 
    init = [1.0, 0.0] 
    t = np.linspace(0, 1, 6) 
    sola = odeint(lambdified_odes, init, t, args=((k1, k2),)) 
    solb = odeint(my_odes, init, t, args=((k1, k2),)) 
    print(np.allclose(sola, solb)) 

True जब स्क्रिप्ट चलाया जाता है छपा है।

यह बहुत तेजी से होता है (ध्यान दें समय परिणामों की इकाइयों में परिवर्तन):

In [79]: t = np.linspace(0, 10, 1001) 

In [80]: %timeit sol = odeint(my_odes, init, t, args=((k1, k2),)) 
1 loops, best of 3: 239 ms per loop 

In [81]: %timeit sol = odeint(lambdified_odes, init, t, args=((k1, k2),)) 
1000 loops, best of 3: 610 µs per loop 
+0

यह वास्तव में मेरे संस्करणों की तुलना में काफी तेज़ है। मुझे 'subs' के लिए 120 एमएस,' theano_function' के लिए 8.7 एमएस और 'lambdify' के लिए 0.6 एमएस मिला। – drhagen

+0

यदि हम प्रत्येक पुनरावृत्ति सूचियों के साथ पैकिंग और अनपॅकिंग के बजाय सी में पूरे मूल्यांकन को कैसे करना है, तो हम और भी अधिक बचत करने में सक्षम होना चाहिए। – drhagen

+0

यदि मैं फ्लेक्स में f के लिए स्पैटिंग '[f (* np.concatenate ([x, k])) का उपयोग करने के लिए' lambdafied_odes' में कॉल को परिवर्तित करता हूं] ', जो आवश्यक है जब राज्यों की संख्या भिन्न होती है, समय बढ़ जाता है 1.4 एमएस तक थोड़ा-अभी भी सबसे अच्छा है। – drhagen