簡體   English   中英

用 sympy 求解耦合微分方程

[英]Solving coupled differential equations with sympy

我正在嘗試解決以下一階耦合微分方程系統:

 - dR/dt=k2*Y(t)-k1*R(t)*L(t)+k4*X(t)-k3*R(t)*I(t)
 - dL/dt=k2*Y(t)-k1*R(t)*L(t)
 - dI/dt=k4*X(t)-k3*R(t)*I(t)
 - dX/dt=k1*R(t)*L(t)-k2*Y(t)
 - dY/dt=k3*R(t)*I(t)-k4*X(t)   

已知的初始條件為: X(0)=0, Y(0)=0
已知常數值為: k1=6500, k2=0.9

這個方程定義了一個動力學模型,我需要求解它們以獲得 Y(t) 函數來擬合我的數據並找到 k3 和 k4 值。 為此,我嘗試使用 sympy 以符號學的方式解決系統問題。 有我的代碼:

import matplotlib.pyplot as plt
import numpy as np
import sympy 
from sympy.solvers.ode.systems import dsolve_system
from scipy.integrate import solve_ivp
from scipy.integrate import odeint
    
k1 = sympy.Symbol('k1', real=True, positive=True)
k2 = sympy.Symbol('k2', real=True, positive=True)
k3 = sympy.Symbol('k3', real=True, positive=True)
k4 = sympy.Symbol('k4', real=True, positive=True)
t = sympy.Symbol('t',real=True, positive=True)
L = sympy.Function('L')
R = sympy.Function('R')
I = sympy.Function('I')
Y = sympy.Function('Y')
X = sympy.Function('X')

f1=k2*Y(t)-k1*R(t)*L(t)+k4*X(t)-k3*R(t)*I(t)
f2=k2*Y(t)-k1*R(t)*L(t)
f3=k4*X(t)-k3*R(t)*I(t)
f4=-f2
f5=-f3

eq1=sympy.Eq(sympy.Derivative(R(t),t),f1)
eq2=sympy.Eq(sympy.Derivative(L(t),t),f2)
eq3=sympy.Eq(sympy.Derivative(I(t),t),f3)
eq4=sympy.Eq(sympy.Derivative(Y(t),t),f4)
eq5=sympy.Eq(sympy.Derivative(X(t),t),f5)

Sys=(eq1,eq2,eq3,eq4,eq5])
solsys=dsolve_system(eqs=Sys,funcs=[X(t),Y(t),R(t),L(t),I(t)], t=t, ics={Y(0):0, X(0):0})

有答案:

NotImplementedError: 
The system of ODEs passed cannot be solved by dsolve_system.

我也嘗試過 dsolve ,但我得到了同樣的結果。
有沒有其他我可以使用的求解器或某種方法可以讓我獲得擬合的功能? 我在 Windows64 中使用帶有 Anaconda 的 Spider 中的 python 3.8。

謝謝!

# 更新
下列的

你說的是“實驗”。 因此,您有數據並希望將模型擬合到它們,至少為 k3 和 k4 找到合適的值,也許對於所有系數和初始條件(第一個測量數據點可能不是最佳擬合的初始條件)? 請參閱 stackoverflow.com/questions/71722061/... 以了解最近對此類任務的嘗試。 – Lutz Lehmann 23 小時

有我的新代碼:

t=[0,0.25,0.5,0.75,1.5,2.27,3.05,3.82,4.6,5.37,6.15,6.92,7.7,8.47,13.42,18.42,23.42,28.42,33.42,38.42,43.42,48.42,53.42,58.42,63.42,68.42,83.4,98.4,113.4,128.4,143.4,158.4,173.4,188.4,203.4,218.4,233.4,248.4]
yexp=[832.49,1028.01,1098.12,1190.08,1188.97,1377.09,1407.47,1529.35,1431.72,1556.66,1634.59,1679.09,1692.05,1681.89,1621.88,1716.77,1717.91,1686.7,1753.5,1722.98,1630.14,1724.16,1670.45,1677.16,1614.98,1671.16,1654.03,1661.84,1675.31,1626.76,1638.29,1614.41,1594.31,1584.73,1599.22,1587.85,1567.74,1602.69]
def derivative(S, t, k3, k4):
    k1=1798931
    k2=0.2629
    x, y,r,l,i = S
    doty = k1*r*l+k2*y
    dotx = k3*r*i-k4*x
    dotr = k2*y-k1*r*l+k4*x-k3*r*i
    dotl = k2*y-k1*r*l
    doti = k4*x-k3*r*i
    return np.array([doty, dotx, dotr, dotl, doti])
def solver(XY,t,para): 
    return odeint(derivative, XY, t, args = para, atol=1e-8, rtol=1e-11)
def integration(XY_arr,*para):
    XY0 = para[:5]
    para = para[5:]
    T = np.arange(len(XY_arr))
    res0 = solver(XY0,T, para)
    res1 = [ solver(XY0,[t,t+1],para)[-1] 
             for t,XY in enumerate(XY_arr[:-1]) ]
    
    return np.concatenate([res0,res1]).flatten()
XData =yexp
YData = np.concatenate([ yexp,yexp,yexp,yexp,yexp,yexp[1:],yexp[1:],yexp[1:],yexp[1:],yexp[1:]]).flatten()
p0 =[0,0,100,10,10,1e8,0.01]

params, info = curve_fit(integration,XData,YData,p0=p0, maxfev=5000)
XY0, para = params[:5], params[5:]
print(XY0,tuple(para))

t_plot = np.linspace(0,len(t),500)
x_plot = solver(XY0, t_plot, tuple(para))  

但輸出不正確,與初始條件 p0 相同:

[  0.   0. 100.  10.  10.] (100000000.0, 0.01)

圖形

我知道函數'integration'在每個時刻為每個函數提供了y的打包值,但我不知道如何解包它們以分別制作curve_fitt。 也許我不太明白它是如何工作的。

謝謝!

正如您所觀察到的,sympy 無法解決此系統。 這可能意味着

  • 在 sympy 中對 ODE 進行分類的過程不夠完整,或者
  • 在 sympy 中實現的標准方法集之上需要一些技巧/方法,或者
  • 沒有象征性的解決方案。

最后一種情況是通用的,取一個符號可解的 ODE,添加一些隨機項,幾乎可以肯定得到的 ODE 不再是符號可解的。


正如我對評論所了解的那樣,您通過具有狀態空間(cX,cY,cR,cL,cI)的 ODE 系統擁有一個模型,該模型具有具有 4 個參數k1,k2,k3,k4的方程,並且通過反應系統R+I <-> X, R+L <-> Y的結構R+I <-> X, R+L <-> Y ,和cR+cX+cY, cL+cY, cI+cX都是常數。

對於模型近似表示的其他一些過程,您有Y分量的時間序列數據t[k],y[k] 你也有關於初始狀態和參數集的部分信息。 如果有足夠多的數據點,人們也可以忘記這些,適合所有參數,並比較給定參數與計算參數的距離。

有幾個模塊和包以或多或少的抽象方式解決了這個擬合任務。 我認為pyomo和gekko都可以使用。 更直接的是,可以使用 scipy.odr 或 scipy.optimize 的工具。

定義轉換時間和參數的前向函數

def model(t,u,k1,k2,k3,k4):
    X,Y,R,L,I = u
    dL = k2*Y - k1*R*L
    dI = k4*X - k3*R*I
    dR = dL+dI
    dX = -dI
    dY = -dL
    return dX,dY,dR,dL,dI

def solver(t,u0,k):
    res = solve_ivp(model, [0, t[-1]], u0, args=tuple(k), t_eval=t, 
                           method="DOP853", atol=1e-7, rtol=1e-10)
    return res.y

准備一些數據和噪音

k1o = 6.500; k2o=0.9
T = np.linspace(0,0.05,21)
U = solver(T, [0,0,50,40,25], [k1o, k2o, 5.400, 0.7])
Y = U[1] # equilibrium slightly above 30
Y += np.random.uniform(high=0.05, size=Y.shape)

准備分割初始狀態和系數的組合參數向量的函數,調用曲線擬合函數

from scipy.optimize import curve_fit

def partial(t,R,L,I,k3,k4): 
    print(R,L,I,k3,k4)
    U = solver(t,[0,0,R,L,I],[k1o,k2o,k3,k4])
    return U[1]

params, info = curve_fit(partial,T,Y, p0=[30,20,10, 0.3,3.000])
R,L,I, k3,k4 = params
print(R,L,I, k3,k4)

事實證明, curve_fit進入了具有大負值的奇怪區域。 一個可能的原因是Y分量最終與所有其他分量的耦合不夠強,這意味着某些參數的較大變化對Y的影響最小,因此Y中的最小噪聲可能導致較大的偏差在這些參數中。 在這里,這顯然(首先)發生在k3上。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM