Klasyczne modele oparte na równaniach
różniczkowych zwyczajnych zakładają, że w danej chwili reakcja systemu (zmiana wartości) jest natychmiastowa i zależy od jej wartości w tym momencie (czyli, że
. Jednak bywają sytuacje, kiedy takie założenie jest nierealistyczne i należy uwzględnić fakt, iż — wskutek swego rodzaju
bezwładności układu — wartość pochodnej rozwiązania może zależeć nie tylko od jego bieżącej kondycji, lecz również od wartości z chwil wcześniejszych. W pierwszym przybliżeniu taka koncepcja prowadzi to do tzw. równań różniczkowych ze stałym opóźnieniem
:
Pakiet OdePkg, rozszerzający możliwości Octave i dostępny w Octave-Forge [20], zawiera w sobie kilka solverów równań różniczkowych ze stałym opóźnieniem
. Drobną ich wadą jest to, że dopuszczają jedynie stały warunek początkowy (w równaniach z opóźnieniem warunek początkowy musi być zadany nie w jednym punkcie, ale na odcinku,
. Wszystkie solvery wykorzystują tzw. włożone jawne metody Rungego–Kutty, od metody niskiego rzędu ode23d, po metodę wysokiego rzędu ode78d. Warto pamiętać, że w przypadku równań z opóźnieniem rozwiązania mogą nie być zbyt gładkie i wówczas korzystniej może być stosować schemat niższego rzędu.
Sposób wykorzystania pakietu jest bardzo dokładnie opisany w [25], na którym, jak się wydaje, opiera się zestaw solverów z OdePkg. My ograniczymy się do krótkiego omówienia sposobu rozwiązania jednego z najprostszych równań.
Rozważmy równanie postaci
| (9.1) | ||||
| (9.2) |
Jest to tzw.równanie logistyczne z opóźnieniem. Dodatni współczynnik
jest bardzo istotnym parametrem. Okazuje się bowiem, że gdy
, to — dla dostatecznie długich czasów — rozwiązania (9.1) stabilizują się na poziomie
, natomiast dla
rozwiązania wpadają w niegasnące, regularne oscylacje. Więcej na temat logistycznego równania z opóźnieniem można dowiedzieć się z wykładu Modelowanie matematyczne w biologii i medycynie.
Zacznijmy od zapisania skryptu, rozwiązującego nasze równanie dla wartości
parametru
równych
, na przedziale czasowym długości
(dostatecznie długim, byśmy zaobserwowali prawdopodobną tendencję rozwiązania).
global a;
lag = 1;
T = 100;
% parametry solvera
opts = odeset ('Stats', 'on', ...
'RelTol', 1e-4, ... % tolerancja bledu wzglednego
'AbsTol', 1e-4, ... % tolerancja bledu bezwzglednego
'InitialStep', 1e-5, ... % poczatkowy krok schematu
'MaxStep', 1e-2); % przy okazji steruje liczba punktow probkowania rozwiazania
filename = 'logdel';
for a = [0.1, pi/2 - 0.1, pi/2 + 0.1]
sol = ode23d(@logdelf, [0,T], 0.2, lag, 0.2, opts);
t = sol.x; Y = sol.y; sol.stats
Ylag = interp1(t,Y,t-lag,'cubic'); % rozwiazanie opoznione
Ydot = logdelf(t,Y,Ylag); % pochodna rozwiazania
plot(t,Y);
xlabel('t'); ylabel('y(t)'); title('Rozwiazanie y(t)');
print('-depsc', strcat(filename, '-a-', ...
strrep(num2str(a,'%g'),'.','p'), '.eps') )
pause(1);
plot(Y,Ydot);
xlabel('y(t)'); ylabel('y''(t)'); title('Wykres fazowy');
print('-depsc', strcat(filename, '-a-', ...
strrep(num2str(a,'%g'),'.','p'), '-phase.eps') )
pause(1);
end
Kluczowa jest linijka
sol = ode23d(@logdelf, [0,T], 0.2, lag, 0.2, opts);
która w rzeczywistości rozwiązuje nasze równanie. Parametrami funkcji
ode23d są kolejno:
funkcja określająca prawą stronę23Funkcja musi być przekazana jako wskaźnik, a nie jako nazwa! (u nas jest
to logdelf), do której parametr a przekazujemy z przyzwyczajenia za
pomocą zmiennej globalnej24W rzeczywistości funkcje z pakietu OdePkg umożliwiają wygodne przekazywanie listy dodatkowych parametrów do wnętrza funkcji prawej strony.),
przedział czasowy, na
którym wyznaczamy rozwiązanie, tutaj:
,
warunek początkowy,
wartości opóźnień (u nas jest tylko jedno, bo równanie jest skalarne) lag = 1), no i ewentualnie
struktura, zmieniająca domyślne parametry pracy solvera.
function ydot = logdelf(t,y,ylag) global a; ydot = (a - ylag).*y; end
W odróżnieniu od lsode, ode23d zwraca wartości rozwiązania w
przez siebie wyznaczonych punktach przedziału
. Aby więc określić jego
wartość w zadanych przez nas punktach,
t = linspace(0,T,10*T);musimy interpolować wartości; warto także wcześniej zadać w opcjach maksymalną długość kroku, która zagwarantuje nam wystarczającą rozdzielczość próbkowania; dla danych opóźnionych, a następnie wyznacz
Ylag = interp1(t,Y,t-lag,'cubic'); % rozwiązanie opóźnione Ydot = logdelf(t,Y,Ylag); % pochodna rozwiązania
która zwraca nam wartości obliczonego przybliżenia rozwiązania (i jego
pochodnej!) w punktach
. Potem wystarczy je tylko narysować, komendami
plot(t, Y); plot(Y, Ydot);
— najpierw rozwiązanie w zależności od czasu, potem — wykres fazowy.
Wyniki, które, przynajmniej w sposób jakościowy, zgadzają się z przewidywaniami teoretycznymi, przedstawiają poniższe wykresy.
Aby nabrać więcej przekonania do ilościowych wyników, zobaczymy, czy stukrotne zmniejszenie parametrów tolerancji błędu spowoduje wyraźną zmianę uzyskanych wartości rozwiązania. Sprawdzenie przeprowadzimy dla parametru
.
Najpierw wyznaczymy w standardowy sposób wartości rozwiązania w interesujących nas punktach:
a = pi/2 + 0.1;
opts = odeset ('Stats', 'on', ...
'RelTol', 1e-4, ... % tolerancja błędu względnego
'AbsTol', 1e-4, ... % tolerancja błędu bezwzględnego
'InitialStep', 1e-5, ... % początkowy krok schematu
'MaxStep', 1e-2); % przy okazji steruje liczbą punktów próbkowania
sol = ode23d(@logdelf, [0,T], 0.2, lag, 0.2, opts);
t = sol.x; Y = sol.y; sol.stats
a następnie powtórzymy obliczenia dla ostrzejszych kryteriów:
opts = odeset ('Stats', 'on', ...
'RelTol', 1e-6, ... % tolerancja błędu względnego
'AbsTol', 1e-6, ... % tolerancja błędu bezwzględnego
'InitialStep', 1e-7, ... % początkowy krok schematu
'MaxStep', 1e-2); % przy okazji steruje liczbą punktów próbkowania
sol = ode23d(@logdelf, [0,T], 0.2, lag, 0.2, opts);
tref = sol.x; Yref = sol.y; sol.stats
Jednak kłopot w tym, że — w przeciwieństwie do lsode
— funkcja ode23d zwraca rozwiązanie nie we wskazanych przez nas wartościach sol.x.
Dlatego musimy napierw sprowadzić wyznaczone wartości do wspólnego zestawu chwil czasowych
. Wydaje się rozsądne, by za ten bazowy zestaw wziąć po prostu t — wartości wyznaczone przy mniej restrykcyjnych parametrach pracy solvera. Następnie, musimy interpolować25To może wprowadzić dodatkowe zaburzenia w wynikach, więc musimy być czujni! wartości (tref,Yref) na t, do czego idealnie nadaje się funkcja interp1.
Yreft = interp1(tref, Yref, t);
Następnie sprawdzamy, jak bardzo różnią się rozwiązania Y i Yref:
octave:> norm(Yreft-Y, Inf) ans = 4.8362e-06 octave:> semilogy(abs(Yreft-Y));
A więc maksymalna różnica pomiędzy rozwiązaniami wynosi jedynie około
, co jest raczej uspokajającą wiadomością (choć należałoby jeszcze wykluczyć na przykład zjawisko aliasingu, które mocno dało się nam we znaki, gdy rozwiązywaliśmy zadanie o transformatorze, z rozdziału 6).
Potwierdź eksperymentalnie, że właśnie dla
następuje zmiana długoterminowego charakteru rozwiązań, z gasnących do zera, na oscylacyjne.
Napisz funkcję, pozwalającą rozwiązać równanie logistyczne z opóźnieniem o zadanych parametrach i jednocześnie od razu zweryfikować w opisany powyżej sposób jakość rozwiązania. Zadbaj o komunikaty i wizualizacje dla niecierpliwego użytkownika. Następnie sprawdź pozostałe uzyskane powyżej wyniki.
Napisz skrypt Octave lub MATLABa, rozwiązujący inną wersję równania logistycznego z opóźnieniem, pochodzącą z pewnego modelu wzrostu nowotworu u myszy [4]:
Dodatnie współczynniki
i
są parametrami modelu. Zweryfikuj jakość uzyskanych aproksymacji rozwiązania i jego pochodnej w przypadku, gdy
,
, a opóźnienie wynosi
. Jako funkcję początkową przyjmij stałą równą
.
Treść automatycznie generowana z plików źródłowych LaTeXa za pomocą oprogramowania wykorzystującego LaTeXML.
strona główna | webmaster | o portalu | pomoc
© Wydział Matematyki, Informatyki i
Mechaniki UW, 2009-2010.
Niniejsze materiały są udostępnione bezpłatnie na licencji Creative Commons Uznanie autorstwa-Użycie niekomercyjne-Bez utworów zależnych 3.0 Polska.
Projekt współfinansowany przez Unię Europejską w ramach
Europejskiego Funduszu Społecznego.
Projekt współfinansowany przez Ministerstwo Nauki i Szkolnictwa Wyższego i przez Uniwersytet Warszawski.