Использование dopri5 для построения системы ОДУ в матричной форме

Система уравнений, которые меня интересуют, выглядит следующим образом:

введите здесь описание изображения

Я смог построить их, изменив пример, который кто-то опубликовал, выполнив следующие действия:

import scipy as sp
import pylab as plt
import numpy as np
import scipy.integrate as spi

#Constants
c13 = 4.2
c14 = 4.2
c21 = 4.3
c32 = 4.4
c34 = 4.4
c42 = 4.4
c43 = 4.4

e12 = 1.9
e23 = 2.5
e24 = 2.2
e31 = 2.0
e41 = 2.0

#Time
t_end = 700
t_start = 0
t_step = 1
t_interval = sp.arange(t_start, t_end, t_step)

#Initial Condition
r = [0.2,0.3,0.3,0.5]

def model(t,r):
    Eqs= np.zeros((4))
    Eqs[0] = (r[0]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])-c21*((r[1]*r[1])*r[0])+e31*((r[2]*r[2])*r[0])+e41*((r[3]*r[3])*r[0]))
    Eqs[1] = (r[1]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])+e12*((r[0]*r[0])*r[1])-c32*((r[2]*r[2])*r[1])-c42*((r[3]*r[3])*r[1]))
    Eqs[2] = (r[2]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])-c13*((r[0]*r[0])*r[2])+e23*((r[1]*r[1])*r[2])-c43*((r[3]*r[3])*r[2]))
    Eqs[3] = (r[3]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])-c14*((r[0]*r[0])*r[3])+e24*((r[1]*r[1])*r[3])-c34*((r[2]*r[2])*r[3]))
    return Eqs

ode =  spi.ode(model)

ode.set_integrator('dopri5')
ode.set_initial_value(r,t_start)
ts = []
ys = []

while ode.successful() and ode.t < t_end:
    ode.integrate(ode.t + t_step)
    ts.append(ode.t)
    ys.append(ode.y)

t = np.vstack(ts)
x1,x2,x3,x4 = np.vstack(ys).T

plt.subplot(1, 1, 1)
plt.plot(t, x1, 'r', label = 'x1')
plt.plot(t, x2, 'b', label = 'x2')
plt.plot(t, x3, 'g', label = 'x3')
plt.plot(t, x4, 'purple', label = 'x4')
plt.xlim([0,t_end])
plt.legend()
plt.ylim([-0.2,1.5])

plt.show()

Это, безусловно, дает мне сюжет, который я хочу. Однако я хочу завершить стохастический анализ с этим набором ОДУ, и по этой причине его гораздо проще смоделировать, если система ОДУ записана в матричной форме (таким образом, я могу легко изменить размерность ОДУ). шума и посмотреть, как это влияет на ОДУ). Я понимаю, как математически записать уравнение в матричной форме, но я не понимаю, как изменить свой код, чтобы в части «def model (t, r):» он читался как массив/матрица. Чтобы преобразовать уравнения в матричную форму, я могу определить:

b = np.array([1, 1, 1, 1]) 
A = np.array([[1, 1+c21, 1-e31, 1-e41], 
              [1-e12, 1, 1+c32, 1+c42],
              [c13+1, 1-e23, 1, 1+c43],
              [c14+1, 1-e24, 1+c34, 1]])

И тогда система уравнений будет (где x — вектор (x1,x2,x3,x4)):

х (т) = диаг (х) [b ^ {T}-Адиаг (х) х]

Итак, мой вопрос: как мне изменить место, где я определил свои ОДУ, чтобы я мог вводить их в виде матрицы вместо того, чтобы записывать каждое уравнение по отдельности? (это также упростит задачу, если я позже рассмотрю систему с более чем 4 измерениями)


person Brenton    schedule 10.12.2015    source источник
comment
Что вам мешает сделать именно так, как вы написали, заменив model этой одной строкой? Если я правильно помню, вы уже делали это один раз. -- Подумайте о замене переменных x на u[k]=x[k]^2 или x[k]=exp(u[k]) для получения более качественных числовых результатов.   -  person Lutz Lehmann    schedule 11.12.2015
comment
@LutzL Потому что я продолжал получать ошибки, когда пытался ввести. Например: › np.array[Eqs[0],Eqs[1],Eqs[2],Eqs[3]].T = np.dot(np.diag(r),bT-np.dot(np. точка (A, np.diag (r)), np.array [r [0], r [1], r [2], r [3]])) дает мне ошибку: объект «builtin_function_or_method» не имеет атрибута 'getitem' Еще одна попытка: › np.array[Eqs[0],Eqs[1],Eqs[2],Eqs[3]].T = np.dot(np.diag(r [0],r[1],r[2],r[3]),bT-np.dot(np.dot(A,np.diag(r[0],r[1],r[2] ,r[3]))),np.array[r[0],r[1],r[2],r[3]])) Выдает ошибку: diag() принимает не более 2 аргументов (4 заданных )   -  person Brenton    schedule 12.12.2015
comment
@LutzL, прошу прощения, я не могу заставить код красиво отображаться в разделе комментариев. Вы предлагаете изменить переменные, чтобы графики были более плавными с менее внезапными изменениями, чтобы у интегратора было меньше проблем с их построением (например, у меня были проблемы с odeint, но изменение переменных может решить это?)   -  person Brenton    schedule 12.12.2015
comment
Вы можете добавить более сложные части комментариев к ответу в качестве новых разделов вопроса, поскольку они разъясняют, что вы уже пробовали, соответственно. куда вы хотите пойти с ним. -- Пожалуйста, ознакомьтесь с логикой присваивания в компьютерных языках, то, что вы пытаетесь сделать, не будет работать в нефункциональных языках и, возможно, не в большинстве функциональных языков. Я добавлю ответ.   -  person Lutz Lehmann    schedule 12.12.2015


Ответы (1)


Используя реализованное предпочтение для numpy.array операций действовать поэлементно (в отличие от numpy.matrix операций, которые работают матричным способом), формула для системы уравнений просто

def model(t,x):
    return x*(b-A.dot(x*x))

x*x создает вектор поэлементных квадратов, x**2 будет еще одним вариантом, A.dot(x2) выполняет произведение матрицы на вектор для numpy.array объектов, а x*(b-...) снова является векторным поэлементным произведением двух векторов операндов.


Использование u=x*x в качестве переменных уменьшает систему до

dotu = 2*u*(b-A.dot(u))

таким образом, он имеет на одну степень меньше и квадратичен по u, что может помочь в исследовании стационарных точек. Подозреваю, что все они гиперболические, так что асимптотически устойчивого решения нет.


Используя замену u=log(x) и, таким образом,

dotu = b-A.dot(exp(2*u))

скрывает стационарные точки на минус бесконечности, поэтому аналитическая ценность этой замены может быть ограничена. Однако положительность x=exp(u) встроена, что может позволить использовать более агрессивные численные методы или обеспечить немного большую точность, используя ту же осторожность, что и раньше.

person Lutz Lehmann    schedule 12.12.2015
comment
Ах, представить это в матричной форме оказалось гораздо проще, чем я думал. Я поиграю с заменами и посмотрю, смогу ли я на этот раз повезти больше с odeint. Спасибо! - person Brenton; 12.12.2015
comment
Я также пытался добавить шума в систему. Итак, я попытался (установив sigma1 = 0,01): def model(t,x): return x*(bA.dot(xx))+sigma1*xnp.random.normal(0,1 ), но это не удается. Я не получаю никаких сообщений об ошибках, просто пустой график. Есть ли альтернативный способ построить SDE? Обычно есть часть dt, а затем часть белого шума находится на временной шкале sqrt(dt), но графики метода Эйлера-Маруямы не работали (возможно, замены исправят это, я посмотрю) - траектории, кажется, останавливаются езда на велосипеде после определенного момента, поэтому я надеялся, что есть способ сделать это с помощью этого метода. - person Brenton; 12.12.2015
comment
Это точно не сработает. Адаптер размера шага требует гладкой функции для прогнозирования вклада ошибки, случайный шум диаметрально противоположен этому. И масштабирование выключено, оно должно быть 1/sqrt(dt), но это невозможно закодировать таким образом. Я не знаю лучшего способа, чем Эйлер-Маруяма. - person Lutz Lehmann; 13.12.2015
comment
У меня не было проблем с заменой журнала, не то чтобы это изменило общее качество результата. Я не вижу, где в этом ODE может произойти целочисленное преобразование чисел с плавающей запятой. - person Lutz Lehmann; 16.12.2015