Modelli Dinamici Con Matlab
Modelli Dinamici Con Matlab
dinamici
Simone Onorati
Università di Pisa
Corso di Cibernetica Fisiologica
a. a. 2016-17
2 Simulazione
In generale, per performare una simulazione in un software occorre conoscere:
• la morfologia delle EDO o dello schema a blocchi del modello;
• il valore numerico di tutti i parametri (che devono essere stati identificati o stimati);
1
Figura 1: I tre problemi generali riguardanti un modello risolvibili con Matlab.
dove x è la concentrazione dei linfociti CD4+ sani, y quella dei CD4+ infetti e v quella del
virus HIV libero; λ il tasso di proliferazione cellulare naturale, d quello di mortalità naturale,
β il tasso di infezione virale, a la mortalità delle cellule infette, k la proliferazione virale e u
il tasso di inattivazione naturale del virus.
Il codice del main.m sarà del tipo:
2
Figura 2: Struttura degli script Matlab suggerita per la simulazione.
clear
close all
clc
model_parameters;
N = 30*36;
grafici
lambda = 7;
d = 0.007;
beta = 4.2163e-7;
a = 0.0999;
k = 90.67;
u = 0.2;
Conviene inizializzare in uno script a parte i parametri, per trovare facilmente (specie nel
caso di main lunghi) il luogo dove modificare eventualmente i valori. Notare che i parametri
sono definiti come global.
3
Di solito si evita di dichiarare variabili globali (cioé visibili a tutte le function e script
nella cartella di lavoro in cui sono dichiarate come global, ed accessibili tramite il loro nome)1
per non rischiare di impiegare altrove variabili locali con lo stesso nome, il che darebbe errore
o malfunzionamento. Nel caso della simulazione di modelli, però, ciò si rivela utile poiché
in questo modo la function handle da risolvere (v. sotto) può leggere i parametri da usare
dall’esterno, senza bisogno che le vengano passati cone argomenti in ingresso.
A questo proposito, è opportuno ricordare la distinzione tra due categorie di file .m, che
si comportano in modo molto diverso rispetto alle variabili:
• Gli script (o procedure), file .m che iniziano con un’istruzione, e sono visibili sem-
plicemente come un blocco di codice che Matlab legge interamente prima di passare
all’istruzione successiva nello script chiamante. Le variabili finora presenti nel workspa-
ce sono accessibili dallo script chiamato, e quelle create durante l’esecuzione di questo
restano nel workspace al suo termine.
Pertanto, c’è bisogno di dichiarare i parametri del modello come global solo perché siano
accessibili nella fun.m (usata poi nel risolutore come function handle). Invece, nello script
grafici.m sono disponibili gli output di ode45 in quanto variabili create entro lo script main.m.
Anche il vettore delle CI, creato entro model_parameters, è disponibile nel main, e quindi
non occorre dichiararlo come global.
In alternativa, se i dati numerici (parametri, CI) sono numerosi, si può renderli disponibili
come file di variabile (.mat) e caricarlo nell’ambiente.
Si definisce poi il tempo di simulazione come numero di campioni (istanti discreti), N (le
simulazioni in Matlab sono sempre discrete, anche se il modello è a tempo continuo!). Per
come sono definiti i parametri, il tempo discreto è in giorni. Nel nostro caso, simuliamo 3
anni di HIV, quindi N = 30 · 36 giorni (3g/mese · 36mesi). Quindi si chiamano la function
risolutrice (ode45, sulla cui sintassi v. sotto) e lo script per generare i grafici.
La function risolutrice
La function che risolve numericamente il problema è propria di Matlab, ed implementa un
metodo numerico per risolvere una singola o un sistema di EDO del primo ordine (un’ EDO
di ordine superiore va sempre riscritta come sistema per poter essere risolta in Matlab). La
scelta del risolutore dipende dalla tolleranza ammessa dal problema, dalla complessità delle
1 In Matlab occorre dichiarare esplicitamente solo variabili global
2 Invece, funzioni chiamate entro altre funzioni, pur avendo un proprio workspace, possono accedere in
lettura e scrittura alle variabili usate nelle function di livello superiore.
4
Tabella 1: Risolutori numerici di problemi differenziali nativi di Matlab.
[t,y] = ode45(odefun,tspan,y0)
Dove:
• t è il vettore con gli istanti temporali a cui è calcolata la soluzione numerica, che
dipendono dalla scelta di tspan.
• y contiene i campioni della soluzione. Nel caso più generale, y è una matrice le cui
colonne sono le soluzioni numeriche per ogni componente della funzione vettoriale
presente nella odefun, e le cui righe corrispondono agli istanti temporali (o, in generale,
ascisse) presenti in t.
5
• odefun è la function handle, ossia l’EDO o il sistema di EDO da risolvere, specificato
in uno dei due modi descritti più sotto.
• y0 sono le condizioni iniziali (il vettore deve avere la stessa dimensione dell’uscita
definita nella odefun).
Attenzione È obbligatorio specificare come ingresso alla fun sia la variabile dipendente
X (scalare o vettoriale) sia il tempo, anche nel caso in cui l’EDO o il sistema non siano
esplicitamente dipendenti dal tempo.
3 Viene eseguita un’interpolazione della soluzione «interna» se gli istanti scelti non sono multipli di h (?)
6
Notare che la function dichiara i parametri come global per poterne leggere i valori dal
workspace degli script.
È importante preallocare le variabili vettoriali o matriciali come oggetti di zeri: questo
incrementa molto la velocità di calcolo, poiché non obbliga Matlab a far posto continuamente
in memoria per adattare progressivamente la dimensione dell’oggetto, ma essa è nota (e la
memoria predisposta) sin dall’inizio.
3 Identificazione parametrica
L’identificazione (o stima, o taratura) dei parametri di un modello matematico viene
effettuata con una delle seguenti modalità:
7
Figura 3: Schema Simulink del modello di Wodartz e Nowak.
• si ottengono per data fitting, cioè si usano i valori che consentono la migliore approssi-
mazione dell’andamento simulato a un andamento misurato (a parità di CI) dal sistema
che si sta modellando.
Per quanto riguarda la terza modalità, si consideri l’esempio seguente, nel modello di
Wodartz e Nowak. Allo stato di regime senza infezione (v ≡ 0∀t), la prima EDO diventa:
ẋ = λ − dx − βxv ⇒ 0
|{z} = λ − dx∞ −
βxv
|{z}
ẋ = 0 a regime v≡0
λ = d · x∞
8
Si tratta quindi di risolvere un problema di ottimizzazione (minimizzazione). La funzione
di costo più comune, che si vuole minimizzare (come un costo totale) è l’indice di qualità
ISE a tempo discreto,
n
X 2
ISE = ci · x̂(i) − x(i)
i=1
dove x̂(i) sono i campioni predetti (in uscita) dal modello, x(i) quelli sperimentali e ci dei
coefficienti di peso. L’ISE, quindi, rappresenta la somma pesata degli errori quadratici del
campione predetto rispetto a quello misurato.
Se i pesi sono tutti uguali, il punto di minimo non varia con il peso comune c e l’ISE
rappresenta, per c = 1, l’errore quadratico «totale» tra le due sequenze.
La minimizzazione dell’ISE si appoggia sulle funzioni matlab fminunc e fmincon, in grado
di trovare il più vicino punto di minimo locale a una posizione iniziale x0 di una funzione
argomento, rispettivamente senza vincoli e sotto diversi tipi di vincolo. Le function risolvono
quindi problemi di minimo vincolato e non («unc» sta per unconstrained, e «con» per
constrained). Le loro sintassi più comuni sono:
[x,fval] = fminunc(fun,x0)
[x,fval] = fmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon)
Per entrambe, l’uscita x rappresenta il punto di minimo della funzione (scalare o vettoriale,
a seconda del dominio di quella) trovato dall’algoritmo, e fval il corrispondente valore della
funzione (argomento omissibile). Gli ingressi sono, in ordine:
• fun: la funzione ’di costo’ da minimizzare, data come function handle, con le stes-
se regole che per le odeXXX: può essere espressa direttamente con c_fun = @(x)
ESPRESSIONE(x) (dove x può essere vettoriale e quindi l’espressione una funzione delle
sue componenti), oppure può essere descritta in un file function a sè (c_fun.m), e
chiamata in argomento con ’c_fun’.
• x0: la posizione iniziale nel dominio, da cui iniziare la ricerca del punto di minimo.
• Aeq, beq: come sopra, ma il vincolo lineare è ora di uguaglianza (Aeq x = beq ).
• nonlcon: vincoli non lineari su x. Vanno espressi come function handle con due uscite,
[c(x), ceq(x)], due funzioni rappresentanti i vincoli: c(x) ≤ 0 e ceq (x) = 0.
nota In fmincon si possono imporre uno o più tipi di vincolo a piacere. Qualora in
fmincon non si voglia imporre alcuni argomenti in posizione intermedia (ad esempio i due
vincoli ’obbligatori’ A e b), li si imposta a [] (come sarà fatto nell’esempio sottostante). nota
È fortemente consigliato aggiungere un terzo argomento di uscita, exitflag, un numero
9
intero che indica se la ricerca del minimo è andata a buon fine (+1) o meno (altro numero).
Si veda doc fmincon per i dettagli. In particolare, un exitflag di −2 indica che non è stato
trovato alcun punto di minimo locale sotto i vincoli specificati.
Le function minimizzatrici seguono un algoritmo numerico iterativo: data la scelta iniziale
x0, calcolano il valore corrispondente della fun da ottimizzare, e da questa informazione
modificano l’ingresso x in modo da avvicinarsi al punto di minimo. Il ciclo si ripete finché
non viene soddisfatto un criterio di arresto – x si trova allora molto vicino al punto di minimo
(vincolato) più prossimo a x0 , se esiste.
Quale esempio, supponiamo che i parametri λ, a e u del modello di Wodartz e Nowak non
siano noti dalla letteratura, e si voglia stimarli per data fitting a partire da dati sperimentali4 .
Nella nostra applicazione, è necessario creare un file c_fun.m, poiché l’espressione della
funzione di costo è laboriosa da ottenere: essa dipende dai campioni sperimentali e da quelli
previsti dal modello con i parametri attualmente impostati (in fase di taratura). Bisognerà
quindi eseguire la simulazione con odeXXX all’interno di c_fun.m (v. l’esempio sottostante).
Ad ogni iterazione dell’algoritmo di minimizzazione, verrà valutato il valore della c_fun con i
parametri attualmente presenti, per poi modificare la variabile indipendente (i parametri) in
modo da avvicinarsi al punto di minimo.
La funzione di costo sarà l’ISE in funzione dei tre parametri incogniti, x = (λ, a, u). I
dati sperimentali sono disponibili nel file experimental_data.mat. Si userà fmincon, volendo
specificare valori massimi e minimi per i parametri dato semplicemente il loro significato.
La function troverà quindi i 3 parametri tali che la curva in uscita dal modello si avvicina
massimamente alla sperimentale. La famiglia di curve è decisa dal tipo di EDO ma talvolta
anche dal valore degli stessi parametri.
La c_fun.m sarà così implementata:
lambda = Uvec(1);
a = Uvec(2);
u = Uvec(3);
tempo = 0:T_f;
[~,XE] = ode45(’fun’,tempo,X0); % L’uscita ’vettore dei tempi’ non viene
% prelevata (non occorre, è uguale a ’tempo’)
(proprio con i valori usati in precedenza); considerarli come fossero dati sperimentali. La stima dei parametri
sarà buona se i valori stimati saranno uguali, a meno di una piccola tolleranza, a quelli usati per generare i
dati "sperimentali"
10
CF_val = 0; % Inizializzazione
for i = 1:length(tdata)
CF_val = CF_val + ...
(1/dvstd(i,1))*((XE(tdata(i),1) - data(i,1)))^2+ ...
(1/dvstd(i,2))*((XE(tdata(i),2) - data(i,2)))^2 + ...
(1/dvstd(i,3))*((XE(tdata(i),3) - data(i,3)))^2;
end
La function utilizza diverse variabili definite all’infuori di essa, prelevate grazie al loro
ambito globale: i parametri già noti (d, beta, k), gli andamenti sperimentali (tdata, istanti
temporali; data, matrice le cui colonne sono le sequenze sperimentali di x(t), y(t) e v(t);
dvstd, matrice con i valori di deviazione standard delle varie misure rispetto alle sequenze
medie, presenti in data), il numero dei campioni delle sequenze (T_f) e infine le scelte iniziali
di tentativo per i parametri (vettore x0).
Come si vede dalle ultime righe, la funzione di costo (calcolata con i parametri attuali ad
ogni iterazione di fmincon) è costituita dalla somma di tutti gli scarti quadratici dei campioni
simulati da quelli sperimentali, sommati sia per istanti temporali (indice i) che per variabile
(x, colonna 1, y, colonna 2, v, colonna 3), e pesati inversamente alla deviazione standard di
ciascun campione.
È molto importante scegliere razionalmente i pesi dei vari contributi nell’indice. Molto
spesso, si sceglie di dare pesi diversi invece che tutti uguali (unitari, per semplicità) per
diverse ragioni:5
11
nel minimizzare l’errore o l’energia di controllo differenziando i pesi dei due gruppi di
addendi di una stessa quantità.
Il nostro modello rientra nel secondo caso. Siccome nella pratica i dati del virus sono
numericamente maggiori delle concentrazioni cellulari (di alcuni ODG), esso conterebbe
molto più dei CD4 nel tarare i parametri, ottenendo un’ottima aderenza della curva del virus
a quella sperimentale, ma non altrettanto per le altre due. Allora si peserà meno il contributo
della carica virale, normalizzando per la deviazione standard inter-sequenze sperimentali
(lo stesso si fa per le altre due variabili, ottenendo così scarti non influenzati dall’ordine di
grandezza delle misure).6
Per eseguire la simulazione del modello, lo script main.m si modificherà semplicemente
così, premettendo al lancio della simulazione vera e propria la stima dei parametri non noti
da altre fonti tramite fmincon:
clear
close all
clc
model_parameters;
6 Poiché in questo esempio didattico si ha a disposizione una sola sequenza per variabile di stato, poiché –
come detto nella nota precedente – gli andamenti sono in realtà provenienti da simulazione, la deviazione
standard è assunta uguale ed unitaria per ogni sequenza, e quindi i pesi risulteranno uguali. Nella pratica ,
invece, si disporrà di più misurazioni di grandezze nel tempo, e si prenderà come sequenza su cui eseguire il
fitting quella media, accompagnata dai valori di deviazione standard, che plasmeranno i pesi dei contributi
all’ISE.
12
% Taratura dei parametri per data fitting
[U_vec,fval,exitflag] = fmincon(’c_fun’,Uvec0,[],[],[],[],LB,UB,[]);
lambda = U_vec(1);
a = U_vec(2);
u = U_vec(3);
tempo = 0:T_f;
[time,XE] = ode45(’fun’,tempo,X0);
grafici
13