RELAZIONE DEL PROGETTO PER L’ESAME DI AUTOMAZIONE INDUSTRIALE Prof. Ferrarini A.A. 1999/2000 OTTIMIZZAZIONE DEGLI ALGORITMI PER IL CALCOLO DI P-INVARIANTI Davide Pozzi Politecnico di Milano Facoltà di Ingegneria di Como Indice ABSTRACT ...................................................................................................................................................................... 3 1.INTRODUZIONE.......................................................................................................................................................... 3 2.MODALITÀ D’USO ..................................................................................................................................................... 3 3.FUNZIONALITÀ IMPLEMENTATE ........................................................................................................................ 4 4.IMPLEMENTAZIONE DEGLI ALGORITMI .......................................................................................................... 6 4.1.DESCRIZIONE DELL'ALGORITMO ................................................................................................................................. 6 4.1.1.CONFRONTO DEI SUPPORTI ..................................................................................................................................... 6 4.1.2.SELEZIONE DELLA COLONNA DA ANNULLARE............................................................................................................ 6 4.1.3.CLASSIFICAZIONE DELLE RIGHE DELLA MATRICE ...................................................................................................... 7 4.1.4.PRE-ELIMINAZIONE DELLE SEQUENZE...................................................................................................................... 7 4.1.5.FAST TEST OF MINIMALITY - TEST OF MINIMALITY ..................................................................................................... 7 4.2.OTTIMIZZAZIONI UTILIZZATE NEL CALCOLO DEI P-INVARIANTI FINALIZZATO AL CALCOLO DEI SIFONI DI UNA PN….8 4.2.1.ELIMINAZIONE DI P-INVARIANTI DI CSTAR_AUX CONTENENTI P-INVARIANTI DI C……………………………………….8 4.2.2.ELIMINAZIONE DI P-INVARIANTI DI CSTAR_AUX CHE CONTENGONO RIGHE NON MINIME DELLA SOLUZIONE…………..9 5.CONCLUSIONI ........................................................................................................................................................... 10 APPENDICE A: CODICE MATLAB .......................................................................................................................... 11 APPENDICE B: GRAFICI DI PESIM DI ALCUNE RETI UTILIZZATE PER IL TESTING DEL PROGRAMMA .............................................................................................................................................................. 29 2 Abstract I P-invarianti di una rete di Petri sono essenziali per studiarne le proprietà. Gli algoritmi per calcolare i P-invarianti minimi di una rete di Petri sono però molto complessi. Alcuni studiosi hanno però proposto algoritmi per ridurre questo problema. In particolare qui di seguito viene descritta la realizzazione software di uno di questi algoritmi: quello proposto da J. M. Colom e M. Silva. Inoltre viene inoltre presentata l'implementazione di una versione di questo algoritmo appositamente ottimizzata per il calcolo dei sifoni di una rete di Petri. 1.Introduzione Lo scopo di questo progetto è di realizzare algoritmi ottimizzati per il calcolo dei P-invarianti in una rete di Petri, a partire dalla sua matrice di incidenza. Due sono i principali programmi realizzati: uno generico per il calcolo dei P-invarianti in una rete di Petri, e uno appositamente studiato per il calcolo dei P-invarianti finalizzato al calcolo dei sifoni di una rete di Petri. Per ogni programma sono state realizzate due versioni che differiscono leggermente per l'algoritmo implementato (come sarà spiegato in seguito). L’implementazione è nel linguaggio degli script di Matlab, e ne rispecchia pertanto lo stile; dei programmi realizzati il primo sostituisce quello analogo presente nel tool software per le reti di Petri realizzato dallo studente Stefano Maggi, il secondo invece non è presente in questo tool. Il primo programma non richiede l'uso di altri script per il suo funzionamento (quello originale richiedeva l'uso di uno script che calcolava il supporto minimo di un insieme di vettori, suppmin.m, ma per l'algoritmo realizzato ciò non è necessario), ma può essere utilizzato da altri programmi (presenti nel tool di Maggi). Nella realizzazione del programma si è puntato a realizzare uno script robusto e efficiente, che terminasse l'esecuzione dove quello originario non riusciva e che realizzasse le stesse funzioni dello script originario con minore dispendio di risorse. Il secondo programma richiede l'uso di due script per il funzionamento: uno è lo script suppmin.m, che era presente nel tool di maggi ma che è stato sostituito da una nuova versione realizzata dal professor Ferrarini; l'altro è una versione da me modificata dello script sifoni.m, sempre fornitami dal professor Ferrarini e sostituente l'analogo script realizzato da Maggi. L'algoritmo per il calcolo dei P-invarianti di una rete di Petri implementato negli script è descritto nell'articolo: "Convex Geometry and Semiflows in P/T Nets. A Comparative Study of Algorithms for Computation of Minimal P-Semiflows." di J. M. Colom e M. Silva. La versione di Matlab utilizzata è la 5.3. Per studiare casi di test con cui verificare il corretto funzionamento del programma si è utilizzato Pesim. 2.Modalità d’uso Gli script forniti sono utilizzabili come qualsiasi altra funzione di Matlab, digitando al prompt dello stesso il nome della funzione da chiamare con i dovuti parametri di ingresso e uscita. Si presuppone comunque una conoscenza di base dell’ambiente di lavoro di Matlab, nel quale bisogna comunque operare ad esempio per inserire le matrici. 3 3.Funzionalità implementate Calcolo dei P-invarianti non canonici - versione con fast test of minimality P = PINVARIANTI_FToM(C) calcola i P-invarianti di una rete di Petri avente C come matrice di incidenza. Versione che utilizza il "Fast Test of Minimality". Ritorna una matrice di dimensioni <numero di P-invarianti> * <numero di posti della rete> in cui ogni riga rappresenta un Pinvariante. Calcolo dei P-invarianti non canonici - versione con test of minimality P = PINVARIANTI_ToM(C) calcola i P-invarianti di una rete di Petri avente C come matrice di incidenza. Versione che utilizza il "Test of Minimality". Ritorna una matrice di dimensioni <numero di P-invarianti> * <numero di posti della rete> in cui ogni riga rappresenta un Pinvariante. Calcolo dei P-invarianti ottimizzato per il calcolo dei sifoni di una PN versione con fast test of minimality PINVARIANTI_S_FToM(P, Cstar_aux) calcola i P-invarianti di una rete di Petri modificata per il calcolo dei sifoni avente Cstar_aux come matrice di incidenza; tra i P-invarianti di Cstar_aux vi sono anche i P-invarianti della rete originaria, contenuti in P, essi vengono esclusi dal calcolo dei Pinvarianti a partire da Cstar_aux e vengono aggiunti al termine dell'algoritmo agli altri P-invarianti trovati. Versione che utilizza il "Fast Test of Minimality". Ritorna una matrice di dimensioni <numero di P-invarianti> * <numero di posti della rete> in cui ogni riga rappresenta un Pinvariante. Calcolo dei P-invarianti ottimizzato per il calcolo dei sifoni di una PN versione con test of minimality PINVARIANTI_S_ToM(P, Cstar_aux) calcola i P-invarianti di una rete di Petri modificata per il calcolo dei sifoni avente Cstar_aux come matrice di incidenza; tra i P-invarianti di Cstar_aux vi sono anche i P-invarianti della rete originaria, contenuti in P, essi vengono esclusi dal calcolo dei Pinvarianti a partire da Cstar_aux e vengono aggiunti al termine dell'algoritmo agli altri P-invarianti trovati. Versione che utilizza il "Test of Minimality". Ritorna una matrice di dimensioni <numero di P-invarianti> * <numero di posti della rete> in cui ogni riga rappresenta un P-invariante. Calcolo dei sifoni di una PN - versione con fast test of minimality SIFONI_FToM(I,O) ritorna una matrice in cui ogni riga rappresenta un sifone della rete di Petri avente I come matrice di ingresso e O come matrice di uscita. Usa la versione di Pinvarianti_S con il "Fast Test of Minimality". Ogni riga contiene gli indici (riferiti alla matrice di incidenza) dei posti che fanno parte del sifone. Calcolo dei sifoni di una PN - versione con test of minimality SIFONI_ToM(I,O) ritorna una matrice in cui ogni riga rappresenta un sifone della rete di Petri avente I come matrice di ingresso e O come matrice di uscita. Usa la versione di Pinvarianti_S con il "Test of Minimality". Ogni riga contiene gli indici (riferiti alla matrice di incidenza) dei posti che fanno parte del sifone. 4 Ho realizzato anche due semplicissime funzioni per ricavare la matrici degli ingressi I e la matrice delle uscite O di una PN a partire dalla matrice di incidenza C della rete per agevolare l'utilizzo degli script per il calcolo dei sifoni. Queste funzioni vengono riportate solo in Appendice A. 5 4.Implementazione degli algoritmi Vengono qui brevemente descritte le caratteristiche principali dell'algoritmo utilizzato per gli script di calcolo dei P-invarianti (pinvarianti_ftom.m, pinvarianti_tom.m, pinvarianti_s_ftom.m, pinvarianti_tom.m) così come è stato implementato. Per una descrizione esauriente dell'algoritmo in generale si rimanda all'articolo già citato in precedenza. Per tutti i richiami di carattere teorico riguardanti le reti di Petri, si rimanda agli appunti del corso di Automazione Industriale del prof. Luca Ferrarini. 4.1.Descrizione dell'algoritmo L'algoritmo descritto da Colom e Silva propone cinque ottimizzazioni da applicare all'algoritmo non ottimizzato. Le cinque ottimizzazioni realizzabili sono le seguenti: confronto dei supporti prima di generare una nuova riga; selezione della colonna da annullare secondo un approccio euristico; classificazione delle righe della matrice, prima di annullare una colonna, in tre sottomatrici che raggruppano le righe in cui colonna da annullare è rispettivamente pari a 0, positiva o negativa; pre-eliminazione di sequenze; "fast test of minimality". Nelle prossime sezioni viene descritta l'implementazione nello script di ogni ottimizzazione. 4.1.1.Confronto dei supporti Il confronto dei supporti consiste nel verificare che il supporto di un nuovo possibile P-invariante non contenga il supporto dei P-invarianti esistenti, infatti, se così non fosse, il nuovo P-invariante non sarebbe minimo. Per questo il confronto viene fatto prima di generare una nuova riga come combinazione lineare di altre due. Nello script realizzato il confronto viene fatto tra il supporto del nuovo P-invariante trovato e tra quelli generati in precedenza e contenuti nelle righe delle tre matrici in cui viene divisa la matrice di incidenza (vedi 4.1.3.). Sono escluse dal confronto le righe che generano il nuovo P-invariante. 4.1.2.Selezione della colonna da annullare L'algoritmo proposto nell'articolo suggerisce di utilizzare approcci euristici per selezionare la colonna da annullare. In particolare questa ottimizzazione è basata sul calcolo dell' expansion factor delle colonne della matrice su cui l'algoritmo opera. L' expansion factor rappresenta il numero di nuove righe aggiunte annullando la colonna k ed è definito come: F(k): = Pk*Nk - (Pk+Nk), ove Pk e Nk sono rispettivamente il numero di elementi positivi e negativi della colonna k da annullare. Il valore di nuove righe da aggiungere, combinando paia di righe, è Pk*Nk. Chiamando k la colonna da annullare, l'ottimizzazione prevede di selezionare k in modo che sia la prima colonna non nulla della matrice su cui opera l'algoritmo tale che il suo expansion factor sia negativo; venendo a mancare una colonna che soddisfi questa condizione l'euristica prevede di scegliere la colonna k che minimizzi Pk*Nk. Inizialmente questa ottimizzazione non era stata implementata con successo, poi è stata realizzata Correttamente portando a una maggiore velocità computazionale (meno righe generate dall'algoritmo). 6 4.1.3.Classificazione delle righe della matrice Questa ottimizzazione consiste nel suddividere la matrice su cui l'algoritmo opera in tre sottomatrici a seconda del valore assunto dalla colonna da annullare nelle righe della matrice. Le righe in cui questo valore è nullo vengono poste in una sottomatrice, le altre due sottomatrici (Upos e Uneg nello script) hanno le righe in cui il valore è rispettivamente positivo o negativo. Una nuova riga viene generata combinando linearmente una riga di Upos con una riga di Uneg. Questa soluzione permette di ridurre il numero di test necessari per selezionare due righe i cui valori nella colonna k da annullare sono non nulli e di segno opposto. 4.1.4.Pre-eliminazione di sequenze L'algoritmo proposto suggeriva di annullare tutte le colonne della matrice di incidenza che avessero un solo elemento positivo e un solo elemento negativo (ovvero di eliminare le transizioni della PN con un solo arco in ingresso e un solo arco in uscita: le sequenze), prima di proseguire con il resto dell'algoritmo. L'implementazione di questa procedura ha causato vari problemi. Innanzitutto per mezzo di semplici esempi si è potuto verificare che questa operazione può portare a risultati errati nel caso l'elemento positivo e quello negativo siano sì unici ma diversi in modulo. Inoltre, sempre dopo vari test del programma, si è constatato che questa ottimizzazione tendeva a portare errori in alcune reti, anche se i valori in modulo degli elementi delle colonne da annullare erano identici, nel caso si annullassero tutte le sequenze. In particolare dopo varie prove ho potuto stabilire che non porta errori eliminare solo una di tutte le sequenze della rete, a meno che non vi sia una sola sequenza nella rete, nel qual caso questa non deve essere eliminata. In conclusione nello script questa ottimizzazione è stata implementata nel seguente modo: se la matrice di incidenza ha una sola colonna con un elemento negativo e uno positivo uguali in modulo, essa non viene annullata; se la matrice di incidenza ha più di una colonna con un elemento negativo e uno positivo uguali in modulo, solo la prima di esse viene annullata. Si è deciso di tenere questa ottimizzazione in tale forma limitata poiché essa offre pur sempre un discreto vantaggio, che diventa grande nel caso del calcolo di T-invarianti, in cui ciò causa l'annullamento di una riga della matrice su cui si calcolano i T-invarianti (si veda l'implementazione di tinvarianti.m nel tool di Maggi). 4.1.5.Fast test of Minimality - Test of Minimality L'ultima ottimizzazione prevista nell'algoritmo descritto da Colom e Silva prevede l'utilizzo di una condizione sufficiente per escludere P-invarianti non minimi prima di generare nuove righe e di effettuare il confronto dei supporti. Questa condizione prevede il calcolo del numero di elementi diversi da 0 del supporto del nuovo P-invariante ( card(||Y||) ) e il calcolo del rank-upperbound. Quest'ultimo è il limite superiore del rango di sottomatrici di C generate a partire dal P-invariante considerato. Esso viene calcolato nello script come il numero di elementi diversi da 0 del vettore (ristretto alle colonne annullate fino a quel momento) costituito dall'unione delle righe della matrice di incidenza indicizzate dal P-invariante considerato. La condizione sufficiente risulta essere la seguente: - un P-invariante Y non è minimo se card(||Y||) > rank-upperbound +1 Questa ottimizzazione non era stata inizialmente implementata del tutto correttamente, in seguito è stata realizzata con successo. L'errore era nel calcolo delle colonne annullate fino al momento di applicare il test. Il calcolo esatto, implementato negli script realizzati, prevede l'utilizzo di una 7 maschera delle colonne attivamente annullate: esse sono quelle via via fornite dall'euristica di selezione della colonna da annullare (vedi il paragrafo 4.2.2.), a meno che a seguito della selezione di questa colonna le matrici Upos e/o Uneg (vedi paragrafo 4.2.3.) non siano nulle (nel qual caso la parte principale dell'algoritmo non viene eseguita): in questo caso la colonna viene tolta dalla maschera delle colonne attivamente annullate. Solo se il test dà esito positivo viene eseguito nello script il confronto dei supporti, altrimenti si passa a un'altra riga. Il Fast Test of Minimality è una condizione sufficiente tratta dalla condizione necessaria e sufficiente data dal Test of Minimality: - un P-invariante Y è minimo se e solo se card(||Y||) = rank(subC) +1 ove rank(subC) rappresenta il rango della sottomatrice della matrice di incidenza C composta dalle righe di C indicizzate da i per le quali Y[i] è un valore diverso da 0. Nell'articolo di Colom e Silva questa condizione non viene considerata perché il calcolo del rango di una matrice era considerato troppo oneroso. In realtà, implementando questa ottimizzazione, ho potuto verificare che non risulta significativamente diversa dall'altra in termini di velocità computazionale e di efficienza. Inoltre l'algoritmo risulta utilizzando il Test of Minimality assai più semplice anche perché non è più necessario realizzare il confronto dei supporti (vedi paragrafo 4.2.1). Ho quindi deciso di implementare due versioni degli script, una con il Fast Test of Minimality, e l'atra con il Test of Minimality, lasciando la scelta della versione da utilizzare all'utente del software. 4.2.Ottimizzazioni utilizzate nel calcolo dei P-invarianti finalizzato al calcolo dei sifoni di una PN Oltre alle ottimizzazioni proposte nell'algoritmo descritto da Colom e Silva, nella realizzazione degli script pinvarianti_s_ftom.m e pinvarianti_s_tom.m sono state introdotte altre ottimizzazioni relative al caso del calcolo dei p-invarianti utilizzato per trovare il i sifoni di una PN. Sono state introdotte due ottimizzazioni per questo caso: eliminazione di P-invarianti di Cstar_aux contenenti P-invarianti di C; eliminazione di P-invarianti di Cstar_aux che contengono righe non minime della soluzione. Nelle prossime sezioni viene descritta l'implementazione nello script di ogni ottimizzazione (per comodità ci si riferirà solo allo script pinvarianti_s_tom.m, ma quanto detto vale allo stesso modo anche per pinvarianti_s_ftom.m). 4.2.1.Eliminazione di P-invarianti di Cstar_aux contenenti P-invarianti di C Lo script pinvarianti_s_tom riceve in ingresso la matrice Cstar_aux (la matrice C opportunamente estesa per il calcolo dei sifoni, corrispondente alla rete originaria modificata con posti fittizi) e deve fornire i suoi P-invarianti. Di questi poi saranno solo considerate le colonne relative alla rete originaria (che sono le prime colC colonne, ove colC è il numero di colonne di C), quindi lo stesso numero di colonne di C. Per come è strutturata Cstar_aux, nei suoi P-invarianti troviamo anche quelli di C, se consideriamo le prime colC colonne. Dato che la matrice Cstar_aux è di dimensioni molto maggiori di quelle di C, e l'algoritmo può diventare per essa assai pesante, si vuole evitare di generare durante l'algoritmo le righe che contengono i P-invarianti di C. A tale scopo in ingresso a 8 pinvarianti_s_tom.m vengono passati anche i P-invarianti di C (calcolati con pinvarianti_ftom.m o pinvarianti_tom.m): prima di generare una riga come combinazione lineare di altre due viene confrontato il supporto (limitato alle prime colC colonne) di un candidato P-invariante di Cstar_aux con i supporti dei P-invarianti di C. Se il supporto del candidato P-invariante risulta uguale a uno di questi, non si procede oltre nell'algoritmo e si passa un nuovo candidato P-invariante. In seguito a questa ottimizzazione i tempi di calcolo sono diminuiti molto, ma venivano ancora generate troppe righe e l'algoritmo risultava di conseguenza ancora pesante. Si è allora proceduto a un'ulteriore ottimizzazione: non si procede nell'algoritmo passando al candidato successivo anche se il supporto del candidato P-invariante di Cstar_aux (limitato alle prime colC colonne) contiene il supporto di uno dei P-invarianti di C (questi P-invarianti di Cstar_aux darebbero luogo a sifoni non minimi). Con questo meccanismo il numero di righe generate dall'algoritmo è diminuito nettamente e le prestazioni dello stesso sono migliorate moltissimo, in particolare come tempi di calcolo. 4.2.2.Eliminazione di P-invarianti di Cstar_aux che contengono righe non minime della soluzione Volendo ottimizzare ulteriormente l'algoritmo, ho osservato che in alcune reti su cui lo testavo, in particolare nella rete provadav2 (vedi appendice B), la soluzione relativa a pinvarianti_s_tom.m (intesa come la matrice dei P-invarianti di Cstar_aux limitata alle sue prime colC colonne) presentava righe che si ripetevano o ne contenevano altre e che davano quindi luogo a sifoni non minimi. Ho deciso allora di fare in modo che queste righe non fossero generate. A questo scopo, a mano a mano che vengono generate righe come combinazioni lineari di altre due, il supporto di queste, limitato alle prime colC colonne, viene inserito in una matrice di supporto. Il supporto di ogni nuovo candidato P-invariante (limitato alle prime colC colonne) viene allora confrontato, prima di procedere con l'algoritmo, con le righe di questa matrice: se il candidato è uguale a o contiene una delle righe di questa matrice non si procede con l'algoritmo e si passa a un nuovo candidato. Anche se l'impatto di questa ottimizzazione non è stato forte come quello della precedente, esso ha permesso in alcuni casi (come per la citata rete provadav2) di diminuire di molto le righe generate, e quindi di portare a una maggiore velocità di esecuzione. Inoltre le due ottimizzazioni hanno anche migliorato la robustezza generale dell'algoritmo, in particolare nei casi di reti di grandi dimensioni. 9 5.Conclusioni In conclusione l'implementazione degli algoritmi descritti ha portato dei vantaggi rispetto all'utilizzo degli script non ottimizzati. Per quanto riguarda gli algoritmi per il calcolo generico dei P-invarianti, vi sono alcune matrici di incidenza che causavano errori nello script di Maggi per il calcolo dei P-invarianti: in particolare esso si bloccava in corrispondenza di una reti non coperte da P-invarianti, invece di riportare un risultato nullo, e si bloccava con alcune reti di dimensioni piuttosto elevate. Il nuovo algoritmo negli stessi casi, riporta nel primo un risultato nullo e nel secondo giunge a buon fine. Inoltre in generale l'algoritmo ottimizzato genera in esecuzione meno righe del precedente. Quindi i nuovi script hanno portato due notevoli vantaggi: una maggiore robustezza in genere; maggiore efficienza computazionale. Le stesse conclusioni si possono trarre per gli script per il calcolo dei P-invarianti finalizzato al calcolo dei sifoni. In particolare grazie alle sue due ottimizzazioni presentate i tempi di calcolo sono diminuiti in maniera veramente notevole. 10 Appendice A: codice Matlab function P = pinvarianti_FToM(C) %PINVARIANTI_FToM Calcolo dei P-invarianti di una rete di Petri % PINVARIANTI_FToM(C) calcola i P-invarianti di una rete di Petri % avente C come matrice di incidenza. % Versione che utilizza il "Fast Test of Minimality". % Ritorna una matrice di dimensioni % <numero di P-invarianti> * <numero di posti della rete> % in cui ogni riga rappresenta un P-invariante. %disp('Inizio Pinvarianti'),pause cont_righe=0; % conta le righe via via generate dall'algoritmo trovato=0; % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numposti, numtrans] = size(C); U° = sparse([C eye(numposti)]); % creo la matrice U° P=[]; % matrice che conterrà i p-invarianti ann=0; % contatore colonne annullate col_ann=0; % indica quale colonna è stata annullata c=0; % contatore delle colonne da annullare % conto le colonne da annullare for k = 1:numtrans %per ogni colonna di C pos=find(C(:,k)>0); % indici degli elementi positivi della k-esima colonna di C Pk = length(pos); % numero degli elementi positivi della k-esima colonna di C neg=find(C(:,k)<0); % indici degli elementi negativi della k-esima colonna di C Nk = length(neg); % numero degli elementi negativi della k-esima colonna di C if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) % se Pk=Nk=1 e l'elemento positivo % e negativo sono uguali in modulo c=c+1; end end % prima ottimizzazione: viene annullata una colonna di U° che abbia un elemento % positivo e un elemento negativo uguali in modulo, a meno che la colonna con queste % caratteristiche non sia una sola for k = 1:numtrans %per ogni colonna di A° pos=find(U°(:,k)>0); % indici degli elementi positivi della k-esima colonna di A° Pk = length(pos); % numero degli elementi positivi della k-esima colonna di A° neg=find(U°(:,k)<0); % indici degli elementi negativi della k-esima colonna di A° Nk = length(neg); % numero degli elementi negativi della k-esima colonna di A° % se Pk=Nk=1 e non si è ancora annullata la colonna di C e l'elemento % positivo e il negativo sono uguali in modulo e la colonna da annullare non è una sola if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) & ann < 1 & ann < 1 & c > 1 U°(pos,k) = 0; % annullo la U°(neg,k) = 0; % k-esima colonna Pk=0; % ora Pk è nullo Nk=0; % e anche Nk ann=ann+1; % è stata annullata una colonna col_ann=k; % indica quale colonna è stata annullata end end % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(C(:,k)>0); % indici degli elementi positivi della k-esima colonna di C Pk = length(pos); % numero degli elementi positivi della k-esima colonna di C neg=find(C(:,k)<0); % indici degli elementi negativi della k-esima colonna di C Nk = length(neg); % numero degli elementi negativi della k-esima colonna di C if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end 11 if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end Uk1=U°; % poni la matrice Uk1 = U° act_ann=zeros(1,numtrans); % maschera della colonne attivamente annullate [numpostiUk1, numcolUk1]=size(Uk1) k=start_col % serve solo "numpostiUk1" per l'algoritmo principale; % mostra a video le due dimensioni % mostra la colonna di partenza dell'algoritmo principale act_ann(k)=1; % metti a 1 il valore della maschera corrispondente alla colonna k Uk=sparse([]); % inizializza la matrice Uk Uneg=sparse([]); % inizializza la matrice Uneg Upos=sparse([]); % inizializza la matrice Upos while k ~= 0 % inizio dell'algoritmo principale for j = 1:numpostiUk1 % per ogni riga di Uk1 if Uk1(j, k) == 0 % se l'elemento all'incrocio fra riga j e colonna k è pari a 0 Uk=[Uk; Uk1(j, :)]; % aggiungi la riga con esso ad Uk elseif Uk1(j, k) < 0 % se l'elemento all'incrocio fra riga j e colonna k è < 0 Uneg=[Uneg; Uk1(j, :)]; % aggiungi la riga con esso ad Uneg else % se l'elemento all'incrocio fra riga j e colonna k è > 0 Upos=[Upos; Uk1(j, :)]; % aggiungi la riga con esso ad Upos end end % se Uneg e/o Upos sono vuote, metti a 0 il valore della maschera corrispondente % alla colonna k if isequal(Uneg,[]) act_ann(k)=0; end if isequal(Upos,[]) act_ann(k)=0; end [x1, y1]=size(Uneg); % serve solo x1: numero delle righe di Uneg [x2, y2]=size(Upos); % serve solo x2: numero delle righe di Upos [xk, yk]=size(Uk); % serve solo xk: numero delle righe di Uk if x1 ~= 0 & x2 ~= 0 % se le matrici non hanno dimensioni nulle row1=1; % indice prima riga di Uneg while row1 <= x1 row2=1; % indice prima riga di Upos while row2 <= x2 % calcola il supporto del nuovo P-i Y come unione dei supporti dei P-I % corrispondenti alle righe row1 e row2 rispettivamente di Uneg e di Upos suppY=spones(Uneg(row1, numtrans+1:numposti+numtrans))|spones(Upos(row2, numtrans+1:numposti+numtrans)); IsuppY=find(suppY>0); % indici elementi diversi da zero del supporto di Y card_suppY=length(find(IsuppY>0)); % calcola la cardinalità del supporto di Y supp_rowC=zeros(1, numtrans); % vettore che conterrà l'unione dei supporti delle % righe di C indicizzate dal supporto di Y % calcola l'unione dei supporti delle righe di C indicizzate dal supporto di Y for f = IsuppY % per ogni valore del supporto di Y supp_rowC=spones(C(f, :))|supp_rowC; % calcola l'unione dei corrispondenti % supporti delle righe di C end 12 % calcola il rank_upperbound rank_upperbound=length(find(act_ann.*supp_rowC>0)); % ottimizzazione: esegue il "fast test of minimality" if card_suppY <= rank_upperbound + 1 v=1; % contatore includes=0; % flag usato nel "support comparison" % esecuzione del "support comparison": confronto il supporto del P-I trovato % con il supporto dei P-I presenti in Uk, Uneg o Upos % confronto con i P-I di Uneg: while v <= x1 & includes == 0 if v ~= row1 % esegui il "support comparison" fra % il nuovo P-I e quelli trovati in precedenza supp_comp=suppY|Uneg(v,numtrans+1:numposti+numtrans); if supp_comp == suppY % se il nuovo P-I contiene il supporto di altri % P-I trovati prima v=x1+1; % termina il "support comparison" includes=1; % il nuovo P-I ne contiene un altro else v=v+1; % altrimenti continua end else v=v+1; end end % confronto con i P-i di Upos: v=1; while v <= x2 & includes == 0 if v ~= row2 % esegui il "support comparison" fra % il nuovo P-I e quelli trovati in precedenza supp_comp=suppY|Upos(v,numtrans+1:numposti+numtrans); if supp_comp == suppY % se il nuovo P-I contiene il supporto di altri % P-I trovati prima v=x2+1; % termina il "support comparison" includes=1; % il nuovo P-I ne contiene un altro else v=v+1; % altrimenti continua end else v=v+1; end end % confronto con i P-i di Uk: v=1; while v <= xk & includes == 0 % esegui il "support comparison" fra % il nuovo P-I e quelli trovati in precedenza supp_comp=suppY|Uk(v,numtrans+1:numposti+numtrans); if supp_comp == suppY % se il nuovo P-I contiene il supporto di altri % P-I trovati prima v=xk+1; % termina il "support comparison" includes=1; % il nuovo P-I ne contiene un altro else v=v+1; % altrimenti continua end end if includes ~= 1 % se il P-I non contiene il supporto di altri P-I alpha=abs(Upos(row2, k)); % calcola il coefficiente alpha beta=abs(Uneg(row1, k)); % calcola il coefficiente beta new_row=alpha*Uneg(row1, :)+beta*Upos(row2, :); % calcola la nuova riga % da aggiungere a Uk cont_righe=cont_righe+1; % calcola le righe generate fino ad ora % calcola il MCD della nuova riga supp_new_row=find(new_row); MCD=new_row(supp_new_row(1)); for l = 2:length(supp_new_row) MCD=gcd(MCD, new_row(supp_new_row(l))); end Uk=[Uk; new_row/MCD]; % aggiungi a Uk la nuova riga divisa per il MCD end 13 end row2=row2+1; % passa alla successiva riga di Upos end row1=row1+1; % passa alla successiva riga di Uneg end end Uneg=sparse([]); % annulla la matrice Uneg Upos=sparse([]); % annulla la matrice Upos trovato=0; % % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numuk,num2uk]=size(Uk); if numuk == 0 start_col=0; else % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(Uk(:,k)>0); % indici degli elementi positivi della k-esima colonna di C Pk = length(pos); % numero degli elementi positivi della k-esima colonna di C neg=find(Uk(:,k)<0); % indici degli elementi negativi della k-esima colonna di C Nk = length(neg); % numero degli elementi negativi della k-esima colonna di C if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end end k=start_col % mostra la colonna di partenza dell'algortitmo principale if k ~= 0 % se k è diverso da 0, poni la posizione della maschera corrispondente a k pari a 1 act_ann(k)=1; end Uk1=Uk; % prepara la matrice di partenza Uk1 per l'algoritmo principale [numpostiUk1, numtransUk1]=size(Uk1); % serve solo "numpostiUk1" per l'algoritmo principale numpostiUk1 % mostra a video il numero di righe di Uk1 Uk=sparse([]); % setta Uk come matrice nulla end % calcola la matrice dei P-I, P for n=1:numpostiUk1 if Uk1(n,1:numtrans) == zeros(1,numtrans); P=full([Uk1(n,numtrans+1:numposti+numtrans); P]); end end cont_righe % metti i P-I trovati in P % mostra le righe generate dall'algoritmo 14 function P = pinvarianti_ToM(C) %PINVARIANTI_ToM Calcolo dei P-invarianti di una rete di Petri % PINVARIANTI_ToM(C) calcola i P-invarianti di una rete di Petri % avente C come matrice di incidenza. % Versione che utilizza il "Test of Minimality". % Ritorna una matrice di dimensioni % <numero di P-invarianti> * <numero di posti della rete> % in cui ogni riga rappresenta un P-invariante. %disp('Inizio Pinvarianti'),pause cont_righe=0; % conta le righe via via generate dall'algoritmo trovato=0; % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numposti, numtrans] = size(C); U° = sparse([C eye(numposti)]); % creo la matrice U° P=[]; % matrice che conterrà i p-invarianti ann=0; % contatore colonne annullate col_ann=0; % indica quale colonna è stata annullata c=0; % contatore delle colonne da annullare % conto le colonne da annullare for k = 1:numtrans %per ogni colonna di C pos=find(C(:,k)>0); % indici degli elementi positivi della k-esima colonna di C Pk = length(pos); % numero degli elementi positivi della k-esima colonna di C neg=find(C(:,k)<0); % indici degli elementi negativi della k-esima colonna di C Nk = length(neg); % numero degli elementi negativi della k-esima colonna di C if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) % se Pk=Nk=1 e l'elemento positivo % e negativo sono uguali in modulo c=c+1; end end % prima ottimizzazione: viene annullata una colonna di U° che abbia un elemento % positivo e un elemento negativo uguali in modulo, a meno che la colonna con queste % caratteristiche non sia una sola for k = 1:numtrans %per ogni colonna di A° pos=find(U°(:,k)>0); % indici degli elementi positivi della k-esima colonna di A° Pk = length(pos); % numero degli elementi positivi della k-esima colonna di A° neg=find(U°(:,k)<0); % indici degli elementi negativi della k-esima colonna di A° Nk = length(neg); % numero degli elementi negativi della k-esima colonna di A° % se Pk=Nk=1 e non si è ancora annullata la colonna di C e l'elemento % positivo e il negativo sono uguali in modulo e la colonna da annullare non è una sola if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) & ann < 1 & ann < 1 & c > 1 U°(pos,k) = 0; % annullo la U°(neg,k) = 0; % k-esima colonna Pk=0; % ora Pk è nullo Nk=0; % e anche Nk ann=ann+1; % è stata annullata una colonna col_ann=k; % indica quale colonna è stata annullata end end % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(C(:,k)>0); % indici degli elementi positivi della k-esima colonna di C Pk = length(pos); % numero degli elementi positivi della k-esima colonna di C neg=find(C(:,k)<0); % indici degli elementi negativi della k-esima colonna di C Nk = length(neg); % numero degli elementi negativi della k-esima colonna di C if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk 15 i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end Uk1=U°; % poni la matrice Uk1 = U° act_ann=zeros(1,numtrans); % maschera della colonne attivamente annullate [numpostiUk1, numcolUk1]=size(Uk1) k=start_col % serve solo "numpostiUk1" per l'algoritmo principale; % mostra a video le due dimensioni % mostra la colonna di partenza dell'algoritmo principale act_ann(k)=1; % metti a 1 il valore della maschera corrispondente alla colonna k Uk=sparse([]); % inizializza la matrice Uk Uneg=sparse([]); % inizializza la matrice Uneg Upos=sparse([]); % inizializza la matrice Upos while k ~= 0 % inizio dell'algoritmo principale for j = 1:numpostiUk1 % per ogni riga di Uk1 if Uk1(j, k) == 0 % se l'elemento all'incrocio fra riga j e colonna k è pari a 0 Uk=[Uk; Uk1(j, :)]; % aggiungi la riga con esso ad Uk elseif Uk1(j, k) < 0 % se l'elemento all'incrocio fra riga j e colonna k è < 0 Uneg=[Uneg; Uk1(j, :)]; % aggiungi la riga con esso ad Uneg else % se l'elemento all'incrocio fra riga j e colonna k è > 0 Upos=[Upos; Uk1(j, :)]; % aggiungi la riga con esso ad Upos end end % se Uneg e/o Upos sono vuote, metti a 0 il valore della maschera corrispondente % alla colonna k if isequal(Uneg,[]) act_ann(k)=0; end if isequal(Upos,[]) act_ann(k)=0; end [x1, y1]=size(Uneg); % serve solo x1: numero delle righe di Uneg [x2, y2]=size(Upos); % serve solo x2: numero delle righe di Upos [xk, yk]=size(Uk); % serve solo xk: numero delle righe di Uk if x1 ~= 0 & x2 ~= 0 % se le matrici non hanno dimensioni nulle row1=1; % indice prima riga di Uneg while row1 <= x1 row2=1; % indice prima riga di Upos while row2 <= x2 % calcola il supporto del nuovo P-i Y come unione dei supporti dei P-I % corrispondenti alle righe row1 e row2 rispettivamente di Uneg e di Upos suppY=spones(Uneg(row1, numtrans+1:numposti+numtrans))|spones(Upos(row2, numtrans+1:numposti+numtrans)); IsuppY=find(suppY>0); % indici elementi diversi da zero del supporto di Y card_suppY=length(find(IsuppY>0)); % calcola la cardinalità del supporto di Y subC=sparse([]); % prepara la sottomatrice di C di cui si calcolerà il rango for f = IsuppY % per ogni valore del supporto di Y subC=[subC; C(f, :).*act_ann]; % aggiungi a subC le corrispondenti % righe di C considerando solo le colonne indicate dalla maschera end rank_subC=rank(subC); % calcola il rango di subC if card_suppY == rank_subC + 1 % esegui il test of minimality alpha=abs(Upos(row2, k)); % calcola il coefficiente alpha beta=abs(Uneg(row1, k)); % calcola il coefficiente beta new_row=alpha*Uneg(row1, :)+beta*Upos(row2, :); % calcola la nuova riga 16 % da aggiungere a Uk cont_righe=cont_righe+1; % calcola le righe generate fino ad ora % calcola il MCD della nuova riga supp_new_row=find(new_row); MCD=new_row(supp_new_row(1)); for l = 2:length(supp_new_row) MCD=gcd(MCD, new_row(supp_new_row(l))); end Uk=[Uk; new_row/MCD]; % aggiungi a Uk la nuova riga divisa per il MCD end row2=row2+1; % passa alla successiva riga di Upos end row1=row1+1; % passa alla successiva riga di Uneg end end Uneg=sparse([]); % annulla la matrice Uneg Upos=sparse([]); % annulla la matrice Upos trovato=0; % % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numuk,num2uk]=size(Uk); if numuk == 0 start_col=0; else % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(Uk(:,k)>0); % indici degli elementi positivi della k-esima colonna di C Pk = length(pos); % numero degli elementi positivi della k-esima colonna di C neg=find(Uk(:,k)<0); % indici degli elementi negativi della k-esima colonna di C Nk = length(neg); % numero degli elementi negativi della k-esima colonna di C if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end end k=start_col % mostra la colonna di partenza dell'algortitmo principale if k ~= 0 % se k è diverso da 0, poni la posizione della maschera corrispondente a k pari a 1 act_ann(k)=1; end Uk1=Uk; % prepara la matrice di partenza Uk1 per l'algoritmo principale [numpostiUk1, numtransUk1]=size(Uk1); % serve solo "numpostiUk1" per l'algoritmo principale numpostiUk1 % mostra a video il numero di righe di Uk1 Uk=sparse([]); % setta Uk come matrice nulla end 17 % calcola la matrice dei P-I, P for n=1:numpostiUk1 if Uk1(n,1:numtrans) == zeros(1,numtrans); P=full([Uk1(n,numtrans+1:numposti+numtrans); P]); end end cont_righe % metti i P-I trovati in P % mostra le righe generate dall'algoritmo function P_s = pinvarianti_S_FToM(P, Cstar_aux) %PINVARIANTI_S_FToM Calcolo dei P-invarianti di una rete di Petri % ottimizzato per la rilevazione dei sifoni nella rete % PINVARIANTI_S_FToM(P, Cstar_aux) calcola i P-invarianti di una rete di Petri modificata % per il calcolo dei sifoni avente Cstar_aux come matrice di incidenza; tra i % P-invarianti di Cstar_aux vi sono anche i P-invarianti della rete originaria, contenuti % in P, essi vengono esclusi dal calcolo dei P-invarianti a partire da Cstar_aux e % vengono aggiunti al termine dell'algoritmo agli altri P-invarianti trovati. % Versione che utilizza il "Fast Test of Minimality". % Ritorna una matrice di dimensioni % <numero di P-invarianti> * <numero di posti della rete> % in cui ogni riga rappresenta un P-invariante. %disp('Inizio Pinvarianti_S'),pause cont_righe=0; % conta le righe via via generate dall'algoritmo trovato=0; % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numposti, numtrans] = size(Cstar_aux); U° = sparse([Cstar_aux eye(numposti)]); % creo la matrice U° P_s=sparse([]); % matrice che conterrà i p-invarianti suppP=sparse([]); % matrice con i supporti dei P-invarianti di P [xP, yP]=size(P); for u = 1:xP suppP=[suppP; spones(P(u,:))]; % riempio suppP con i supporti delle righe di P end suppParz=[]; % matrice usata per contenere i supporti dei P-I trovati con l'algoritmo % principale ristretti alle dimensioni di P ann=0; % contatore colonne annullate col_ann=0; % indica quale colonna è stata annullata c=0; % contatore delle colonne da annullare % conto le colonne da annullare for k = 1:numtrans %per ogni colonna di Cstar_aux pos=find(Cstar_aux(:,k)>0); % indici degli elementi positivi della k-esima colonna di Cstar_aux Pk = length(pos); % numero degli elementi positivi della k-esima colonna di Cstar_aux neg=find(Cstar_aux(:,k)<0); % indici degli elementi negativi della k-esima colonna di Cstar_aux Nk = length(neg); % numero degli elementi negativi della k-esima colonna di Cstar_aux if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) % se Pk=Nk=1 e l'elemento positivo % e negativo sono uguali in modulo c=c+1; end end % prima ottimizzazione: viene annullata una colonna di U° che abbia un elemento % positivo e un elemento negativo uguali in modulo, a meno che la colonna con queste % caratteristiche non sia una sola for k = 1:numtrans %per ogni colonna di A° pos=find(U°(:,k)>0); % indici degli elementi positivi della k-esima colonna di A° Pk = length(pos); % numero degli elementi positivi della k-esima colonna di A° neg=find(U°(:,k)<0); % indici degli elementi negativi della k-esima colonna di A° Nk = length(neg); % numero degli elementi negativi della k-esima colonna di A° % se Pk=Nk=1 e non si è ancora annullata la colonna di Cstar_aux e l'elemento % positivo e il negativo sono uguali in modulo e la colonna da annullare non è una sola if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) & ann < 1 & ann < 1 & c > 1 U°(pos,k) = 0; % annullo la 18 U°(neg,k) = 0; % k-esima colonna Pk=0; % ora Pk è nullo Nk=0; % e anche Nk ann=ann+1; % è stata annullata una colonna col_ann=k; % indica quale colonna è stata annullata end end % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(Cstar_aux(:,k)>0); % indici degli elementi positivi della k-esima colonna di Cstar_aux Pk = length(pos); % numero degli elementi positivi della k-esima colonna di Cstar_aux neg=find(Cstar_aux(:,k)<0); % indici degli elementi negativi della k-esima colonna di Cstar_aux Nk = length(neg); % numero degli elementi negativi della k-esima colonna di Cstar_aux if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end Uk1=U°; % poni la matrice Uk1 = U° act_ann=zeros(1,numtrans); % maschera della colonne attivamente annullate [numpostiUk1, numcolUk1]=size(Uk1) k=start_col % serve solo "numpostiUk1" per l'algoritmo principale; % mostra a video le due dimensioni % mostra la colonna di partenza dell'algoritmo principale act_ann(k)=1; % metti a 1 il valore della maschera corrispondente alla colonna k Uk=sparse([]); % inizializza la matrice Uk Uneg=sparse([]); % inizializza la matrice Uneg Upos=sparse([]); % inizializza la matrice Upos while k ~= 0 % inizio dell'algoritmo principale for j = 1:numpostiUk1 % per ogni riga di Uk1 if Uk1(j, k) == 0 % se l'elemento all'incrocio fra riga j e colonna k è pari a 0 Uk=[Uk; Uk1(j, :)]; % aggiungi la riga con esso ad Uk elseif Uk1(j, k) < 0 % se l'elemento all'incrocio fra riga j e colonna k è < 0 Uneg=[Uneg; Uk1(j, :)]; % aggiungi la riga con esso ad Uneg else % se l'elemento all'incrocio fra riga j e colonna k è > 0 Upos=[Upos; Uk1(j, :)]; % aggiungi la riga con esso ad Upos end end % se Uneg e/o Upos sono vuote, metti a 0 il valore della maschera corrispondente % alla colonna k if isequal(Uneg,[]) act_ann(k)=0; end if isequal(Upos,[]) act_ann(k)=0; end [x1, y1]=size(Uneg); % serve solo x1: numero delle righe di Uneg 19 [x2, y2]=size(Upos); % serve solo x2: numero delle righe di Upos [xk, yk]=size(Uk); % serve solo xk: numero delle righe di Uk if x1 ~= 0 & x2 ~= 0 % se le matrici non hanno dimensioni nulle row1=1; % indice prima riga di Uneg while row1 <= x1 row2=1; % indice prima riga di Upos while row2 <= x2 % calcola il supporto del nuovo P-i Y come unione dei supporti dei P-I % corrispondenti alle righe row1 e row2 rispettivamente di Uneg e di Upos suppY=spones(Uneg(row1, numtrans+1:numposti+numtrans))|spones(Upos(row2, numtrans+1:numposti+numtrans)); % confronto il supporto del nuovoP-i con i supporti dei P-i in suppP: % se il supporto del nuovo P-i è uguale o include uno in suppP, non continuo w=1; % contatore doppio=0; % flag di supporto uguale while w <= xP suppUnion=suppY(:, 1:yP)|suppP(w,:); % calcolo l'unione fra il supporto del % nuovo P-I e quello di suppP corrispondente all riga w if isequal(suppY(:, 1:yP), suppP(w,:)) % se i supporti sono uguali doppio=1; % ho trovato un doppione w=xP+1; % esco dal while elseif suppY(:, 1:yP) == suppUnion % se il supporto del nuovo P-I include quello di suppP doppio=1; % ho trovato un P-I inutile w=xP+1; % esco dal while else w=w+1; end end % confronto fra il supporto del P-I nuovo (ristretto alle dimensioni di suppParz) % e i supporti contenuti in suppParz: se il nuovo P-I contiene uno di questi, esso è un doppione if ~isequal(suppParz,[]) & doppio == 0 % se suppParz non è vuota w=1; [xSPZ, ySPZ]=size(suppParz); while w <= xSPZ % per ogni riga di supppParz suppUnion2=suppY(:, 1:ySPZ)|suppParz(w, :); % calcola l'unione del supporto % del nuovo P-I trovato con quello della w-esima riga di suppParz if suppY(:, 1:ySPZ) == suppUnion2 % se il nuovo P-I contiene un P-I di suppParz doppio=1; % ho trovato un P-I inutile w=xSPZ+1; % esco dal while else w=w+1; end end end if doppio ~= 1 % se non si tratta di un P-I doppio IsuppY=find(suppY>0); % indici elementi diversi da zero del supporto di Y card_suppY=length(find(IsuppY>0)); % calcola la cardinalità del supporto di Y supp_rowC=zeros(1, numtrans); % vettore che conterrà l'unione dei supporti delle % righe di Cstar_aux indicizzate dal supporto di Y % calcola l'unione dei supporti delle righe di Cstar_aux indicizzate dal supporto di Y for f = IsuppY % per ogni valore del supporto di Y supp_rowC=spones(Cstar_aux(f, :))|supp_rowC; % calcola l'unione dei corrispondenti % supporti delle righe di Cstar_aux end % calcola il rank_upperbound rank_upperbound=length(find(act_ann.*supp_rowC>0)); % ottimizzazione: esegue il "fast test of minimality" if card_suppY <= rank_upperbound + 1 v=1; % contatore includes=0; % flag usato nel "support comparison" % esecuzione del "support comparison": confronto il supporto del P-I trovato % con il supporto dei P-I presenti in Uk, Uneg o Upos % confronto con i P-I di Uneg: while v <= x1 & includes == 0 if v ~= row1 % esegui il "support comparison" fra 20 % il nuovo P-I e quelli trovati in precedenza supp_comp=suppY|Uneg(v,numtrans+1:numposti+numtrans); if supp_comp == suppY % se il nuovo P-I contiene il supporto di altri % P-I trovati prima v=x1+1; % termina il "support comparison" includes=1; % il nuovo P-I ne contiene un altro else v=v+1; % altrimenti continua end else v=v+1; end end % confronto con i P-i di Upos: v=1; while v <= x2 & includes == 0 if v ~= row2 % esegui il "support comparison" fra % il nuovo P-I e quelli trovati in precedenza supp_comp=suppY|Upos(v,numtrans+1:numposti+numtrans); if supp_comp == suppY % se il nuovo P-I contiene il supporto di altri % P-I trovati prima v=x2+1; % termina il "support comparison" includes=1; % il nuovo P-I ne contiene un altro else v=v+1; % altrimenti continua end else v=v+1; end end % confronto con i P-i di Uk: v=1; while v <= xk & includes == 0 % esegui il "support comparison" fra % il nuovo P-I e quelli trovati in precedenza supp_comp=suppY|Uk(v,numtrans+1:numposti+numtrans); if supp_comp == suppY % se il nuovo P-I contiene il supporto di altri % P-I trovati prima v=xk+1; % termina il "support comparison" includes=1; % il nuovo P-I ne contiene un altro else v=v+1; % altrimenti continua end end if includes ~= 1 % se il P-I non contiene il supporto di altri P-I alpha=abs(Upos(row2, k)); % calcola il coefficiente alpha beta=abs(Uneg(row1, k)); % calcola il coefficiente beta new_row=alpha*Uneg(row1, :)+beta*Upos(row2, :); % calcola la nuova riga % da aggiungere a Uk cont_righe=cont_righe+1; % calcola le righe generate fino ad ora if new_row(:,1:numtrans) == zeros(1,numtrans) % se la nuova riga trovata rappresenta un P-I % metti il suo supporto ristretto alle dimensioni di suppParz in suppParz suppParz=[spones(new_row(:,numtrans+1:numposti)); suppParz]; end % calcola il MCD della nuova riga supp_new_row=find(new_row); MCD=new_row(supp_new_row(1)); for l = 2:length(supp_new_row) MCD=gcd(MCD, new_row(supp_new_row(l))); end Uk=[Uk; new_row/MCD]; % aggiungi a Uk la nuova riga divisa per il MCD end end end row2=row2+1; % passa alla successiva riga di Upos end row1=row1+1; % passa alla successiva riga di Uneg 21 end end Uneg=sparse([]); Upos=sparse([]); % annulla la matrice Uneg % annulla la matrice Upos trovato=0; % % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numuk,num2uk]=size(Uk); if numuk == 0 start_col=0; else % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(Uk(:,k)>0); % indici degli elementi positivi della k-esima colonna di Uk Pk = length(pos); % numero degli elementi positivi della k-esima colonna di Uk neg=find(Uk(:,k)<0); % indici degli elementi negativi della k-esima colonna di Uk Nk = length(neg); % numero degli elementi negativi della k-esima colonna di Uk if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end end k=start_col % mostra la colonna di partenza dell'algortitmo principale if k ~= 0 % se k è diverso da 0, poni la posizione della maschera corrispondente a k pari a 1 act_ann(k)=1; end Uk1=Uk; % prepara la matrice di partenza Uk1 per l'algoritmo principale [numpostiUk1, numtransUk1]=size(Uk1); % serve solo "numpostiUk1" per l'algoritmo principale numpostiUk1 % mostra a video il numero di righe di Uk1 Uk=sparse([]); % setta Uk come matrice nulla end % calcola la matrice dei P-I, P_s for n=1:numpostiUk1 if Uk1(n,1:numtrans) == zeros(1,numtrans); P_s=full([Uk1(n,numtrans+1:numposti+numtrans); P_s]); end end cont_righe % metti i P-I trovati in P_s % mostra le righe generate dall'algoritmo function P_s = pinvarianti_S_ToM(P, Cstar_aux) %PINVARIANTI_S_ToM Calcolo dei P-invarianti di una rete di Petri % ottimizzato per la rilevazione dei sifoni nella rete % PINVARIANTI_S_ToM(P, Cstar_aux) calcola i P-invarianti di una rete di Petri modificata 22 % % % % % % % % per il calcolo dei sifoni avente Cstar_aux come matrice di incidenza; tra i P-invarianti di Cstar_aux vi sono anche i P-invarianti della rete originaria, contenuti in P, essi vengono esclusi dal calcolo dei P-invarianti a partire da Cstar_aux e vengono aggiunti al termine dell'algoritmo agli altri P-invarianti trovati Versione che utilizza il "Test of Minimality". Ritorna una matrice di dimensioni <numero di P-invarianti> * <numero di posti della rete> in cui ogni riga rappresenta un P-invariante. %disp('Inizio Pinvarianti_S'),pause cont_righe=0; % conta le righe via via generate dall'algoritmo trovato=0; % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numposti, numtrans] = size(Cstar_aux); U° = sparse([Cstar_aux eye(numposti)]); % creo la matrice U° P_s=sparse([]); % matrice che conterrà i p-invarianti suppP=sparse([]); % matrice con i supporti dei P-invarianti di P [xP, yP]=size(P); % serve solo xP for u = 1:xP suppP=[suppP; spones(P(u,:))]; % riempio suppP con i supporti delle righe di P end suppParz=[]; % matrice usata per contenere i supporti dei P-I trovati con l'algoritmo % principale ristretti alle dimensioni di P ann=0; % contatore colonne annullate col_ann=0; % indica quale colonna è stata annullata c=0; % contatore delle colonne da annullare % conto le colonne da annullare for k = 1:numtrans %per ogni colonna di Cstar_aux pos=find(Cstar_aux(:,k)>0); % indici degli elementi positivi della k-esima colonna di Cstar_aux Pk = length(pos); % numero degli elementi positivi della k-esima colonna di Cstar_aux neg=find(Cstar_aux(:,k)<0); % indici degli elementi negativi della k-esima colonna di Cstar_aux Nk = length(neg); % numero degli elementi negativi della k-esima colonna di Cstar_aux if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) % se Pk=Nk=1 e l'elemento positivo % e negativo sono uguali in modulo c=c+1; end end % prima ottimizzazione: viene annullata una colonna di U° che abbia un elemento % positivo e un elemento negativo uguali in modulo, a meno che la colonna con queste % caratteristiche non sia una sola for k = 1:numtrans %per ogni colonna di A° pos=find(U°(:,k)>0); % indici degli elementi positivi della k-esima colonna di A° Pk = length(pos); % numero degli elementi positivi della k-esima colonna di A° neg=find(U°(:,k)<0); % indici degli elementi negativi della k-esima colonna di A° Nk = length(neg); % numero degli elementi negativi della k-esima colonna di A° % se Pk=Nk=1 e non si è ancora annullata la colonna di Cstar_aux e l'elemento % positivo e il negativo sono uguali in modulo e la colonna da annullare non è una sola if Pk * Nk == 1 & abs(U°(pos,k)) == abs(U°(neg,k)) & ann < 1 & ann < 1 & c > 1 U°(pos,k) = 0; % annullo la U°(neg,k) = 0; % k-esima colonna Pk=0; % ora Pk è nullo Nk=0; % e anche Nk ann=ann+1; % è stata annullata una colonna col_ann=k; % indica quale colonna è stata annullata end end % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(Cstar_aux(:,k)>0); % indici degli elementi positivi della k-esima colonna di Cstar_aux Pk = length(pos); % numero degli elementi positivi della k-esima colonna di Cstar_aux neg=find(Cstar_aux(:,k)<0); % indici degli elementi negativi della k-esima colonna di Cstar_aux Nk = length(neg); % numero degli elementi negativi della k-esima colonna di Cstar_aux if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 23 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end Uk1=U°; % poni la matrice Uk1 = U° act_ann=zeros(1,numtrans); % maschera della colonne attivamente annullate [numpostiUk1, numcolUk1]=size(Uk1) k=start_col % serve solo "numpostiUk1" per l'algoritmo principale; % mostra a video le due dimensioni % mostra la colonna di partenza dell'algoritmo principale act_ann(k)=1; % metti a 1 il valore della maschera corrispondente alla colonna k Uk=sparse([]); % inizializza la matrice Uk Uneg=sparse([]); % inizializza la matrice Uneg Upos=sparse([]); % inizializza la matrice Upos while k ~= 0 % inizio dell'algoritmo principale for j = 1:numpostiUk1 % per ogni riga di Uk1 if Uk1(j, k) == 0 % se l'elemento all'incrocio fra riga j e colonna k è pari a 0 Uk=[Uk; Uk1(j, :)]; % aggiungi la riga con esso ad Uk elseif Uk1(j, k) < 0 % se l'elemento all'incrocio fra riga j e colonna k è < 0 Uneg=[Uneg; Uk1(j, :)]; % aggiungi la riga con esso ad Uneg else % se l'elemento all'incrocio fra riga j e colonna k è > 0 Upos=[Upos; Uk1(j, :)]; % aggiungi la riga con esso ad Upos end end % se Uneg e/o Upos sono vuote, metti a 0 il valore della maschera corrispondente % alla colonna k if isequal(Uneg,[]) act_ann(k)=0; end if isequal(Upos,[]) act_ann(k)=0; end [x1, y1]=size(Uneg); % serve solo x1: numero delle righe di Uneg [x2, y2]=size(Upos); % serve solo x2: numero delle righe di Upos [xk, yk]=size(Uk); % serve solo xk: numero delle righe di Uk if x1 ~= 0 & x2 ~= 0 % se le matrici non hanno dimensioni nulle row1=1; % indice prima riga di Uneg while row1 <= x1 row2=1; % indice prima riga di Upos while row2 <= x2 % calcola il supporto del nuovo P-i Y come unione dei supporti dei P-I % corrispondenti alle righe row1 e row2 rispettivamente di Uneg e di Upos suppY=spones(Uneg(row1, numtrans+1:numposti+numtrans))|spones(Upos(row2, numtrans+1:numposti+numtrans)); % confronto il supporto del nuovoP-i con i supporti dei P-i in suppP: % se il supporto del nuovo P-i è uguale a uno in suppP, non continuo w=1; % contatore 24 doppio=0; % flag di supporto uguale while w <= xP suppUnion=suppY(:, 1:yP)|suppP(w,:); % calcolo l'unione fra il supporto del % nuovo P-I e quello di suppP corrispondente all riga w if isequal(suppY(:, 1:yP), suppP(w,:)) % se i supporti sono uguali doppio=1; % ho trovato un doppione w=xP+1; % esco dal while elseif suppY(:, 1:yP) == suppUnion % se il supporto del nuovo P-I include quello di suppP doppio=1; % ho trovato un P-I inutile w=xP+1; % esco dal while else w=w+1; end end % confronto fra il supporto del P-I nuovo (ristretto alle dimensioni di suppParz) % e i supporti contenuti in suppParz: se il nuovo P-I contiene uno di questi, esso è un doppione if ~isequal(suppParz,[]) & doppio == 0 % se suppParz non è vuota w=1; [xSPZ, ySPZ]=size(suppParz); while w <= xSPZ % per ogni riga di supppParz suppUnion2=suppY(:, 1:ySPZ)|suppParz(w, :); % calcola l'unione del supporto % del nuovo P-I trovato con quello della w-esima riga di suppParz if suppY(:, 1:ySPZ) == suppUnion2 % se il nuovo P-I contiene un P-I di suppParz doppio=1; % ho trovato un P-I inutile w=xSPZ+1; % esco dal while else w=w+1; end end end if doppio ~= 1 % se non si tratta di un P-I doppio IsuppY=find(suppY>0); % indici elementi diversi da zero del supporto di Y card_suppY=length(find(IsuppY>0)); % calcola la cardinalità del supporto di Y subC=sparse([]); % prepara la sottomatrice di C di cui si calcolerà il rango for f = IsuppY % per ogni valore del supporto di Y subC=[subC; Cstar_aux(f, :).*act_ann]; % aggiungi a subC le corrispondenti % righe di C considerando solo le colonne indicate dalla maschera end rank_subC=rank(subC); % calcola il rango di subC if card_suppY == rank_subC + 1 % esegui il test of minimality alpha=abs(Upos(row2, k)); % calcola il coefficiente alpha beta=abs(Uneg(row1, k)); % calcola il coefficiente beta new_row=alpha*Uneg(row1, :)+beta*Upos(row2, :); % calcola la nuova riga % da aggiungere a Uk cont_righe=cont_righe+1; % calcola le righe generate fino ad ora if new_row(:,1:numtrans) == zeros(1,numtrans) % se la nuova riga trovata rappresenta un P-I % metti il suo supporto ristretto alle dimensioni di suppParz in suppParz suppParz=[spones(new_row(:,numtrans+1:numposti)); suppParz]; end % calcola il MCD della nuova riga supp_new_row=find(new_row); MCD=new_row(supp_new_row(1)); for l = 2:length(supp_new_row) MCD=gcd(MCD, new_row(supp_new_row(l))); end Uk=[Uk; new_row/MCD]; % aggiungi a Uk la nuova riga divisa per il MCD end end row2=row2+1; % passa alla successiva riga di Upos end row1=row1+1; % passa alla successiva riga di Uneg end end Uneg=sparse([]); % annulla la matrice Uneg 25 Upos=sparse([]); % annulla la matrice Upos trovato=0; % % flag: indica che si è trovata la colonna da cui parte l'algoritmo principale strart_col=0; % colonna di partenza ottenuta calcolando l'"expansion factor" start_col2=0; % colonna di partenza che minimizza il prodotto Pk*Nk (definiti sotto) i=0; % contatore per i prodotti parziali Pk*Nk [numuk,num2uk]=size(Uk); if numuk == 0 start_col=0; else % viene ora ricercata la colonna di partenza per la parte principale dell'algoritmo for k = 1:numtrans %per ogni colonna di A° pos=find(Uk(:,k)>0); % indici degli elementi positivi della k-esima colonna di Uk Pk = length(pos); % numero degli elementi positivi della k-esima colonna di Uk neg=find(Uk(:,k)<0); % indici degli elementi negativi della k-esima colonna di Uk Nk = length(neg); % numero degli elementi negativi della k-esima colonna di Uk if Pk + Nk ~= 0 & Pk*Nk-(Pk+Nk) < 0 & trovato == 0 % se la colonna è non nulla, l'"expansion factor" è negativo e non ho ancora trovato % la colonna di partenza trovato=1; % ho trovato la colonna di partenza start_col=k; end if Pk + Nk ~= 0 & trovato == 0 % se la colonna è non nulla e % non ha un "expansion factor" negativo prod_Pk_Nk=Pk*Nk; % calcolo Pk*Nk if i == 0 % se è la prima colonna per cui calcolo Pk*Nk slack=prod_Pk_Nk; % setto una variabile di confronto a Pk*Nk i=1; % aggiorno il contatore start_col2=k; % setto la colonna di partenza secondo questa logica end if prod_Pk_Nk < slack % se trovo un prodotto Pk*Nk minore di quello % calcolato precedentemente slack=prod_Pk_Nk; % aggiorno la variabile di confronto start_col2=k; % e la colonna di partenza end end end if trovato == 0 % se non ho trovato una colonna che soddisfa il criterio % dell'"expansion factor" negativo start_col=start_col2; % la colonna di partenza è quella che minimizza Pk*Nk end end k=start_col % mostra la colonna di partenza dell'algortitmo principale if k ~= 0 % se k è diverso da 0, poni la posizione della maschera corrispondente a k pari a 1 act_ann(k)=1; end Uk1=Uk; % prepara la matrice di partenza Uk1 per l'algoritmo principale [numpostiUk1, numtransUk1]=size(Uk1); % serve solo "numpostiUk1" per l'algoritmo principale numpostiUk1 % mostra a video il numero di righe di Uk1 Uk=sparse([]); % setta Uk come matrice nulla end % calcola la matrice dei P-I, P_s for n=1:numpostiUk1 if Uk1(n,1:numtrans) == zeros(1,numtrans); P_s=full([Uk1(n,numtrans+1:numposti+numtrans); P_s]); end end cont_righe % metti i P-I trovati in P_s % mostra le righe generate dall'algoritmo function S = sifoni_FToM(I,O) %SIFONI_FToM Calcolo dei sifoni di una rete di Petri % SIFONI_FToM(I,O) ritorna una matrice in cui ogni riga rappresenta un sifone % della rete di Petri avente I come matrice di ingresso e O come matrice di uscita. % Usa la versione di Pinvarianti_S con il "Fast Test of Minimality". % Ogni riga contiene gli indici (riferiti alla matrice di incidenza) dei posti che % fanno parte del sifone. 26 %Costruzione della rete di Petri ausiliaria %La rete ausiliaria ha gli stessi posti, transizioni e archi della rete di partenza, %ma il peso di ogni arco entrante in una transizione viene moltiplicato per un coefficiante %pari alla somma dei pesi degli archi uscenti dalla medesima transizione. aux = repmat(sum(O,1),size(O,1),1); %matrice ausiliaria di righe tutte uguali contenenti la somma dei pesi degli archi uscenti da ogni transizione Istar = I .* aux; %.* è il prodotto elemento per elemento C=O-I; % matrice di incidenza della rete di Petri originaria P=full(spones(pinvarianti(C))); %calcola i P-invarianti della rete originaria Cstar = O - Istar; %matrice di incidenza della nuova rete di Petri %Soluzione del sistema x'Cstar <= 0 %La soluzione di questo problema è equivalente al calcolo dei P_invarianti della rete trasformata %a cui si aggiungano per ogni transizione un posto e un arco diretto dalla transizione al posto. Cstar_aux = [Cstar; eye(size(O,2))]; soluzione = pinvarianti_S_FToM(P, Cstar_aux); if ~isequal(soluzione,[]) % se soluzione non è una matrice vuota (cioè se gli unici sifoni % della rete sono i P-I di C) soluzione = sign(soluzione(:,1:size(I,1))); %AL POSTO DI I C'ERA CSTAR% vanno prese solo le colonne che si riferiscono ai posti della rete; le ultime colonne invece si riferiscono a posti fittizi end soluzione=[soluzione; P]; % aggiungi i supporti dei P-I di C a quelli trovati in più per Cstar_aux %disp('Inizio suppmin sifoni'),pause S = suppmin(soluzione); %disp('Fine suppmin sifoni'),pause %riscrittura dei sifoni nella forma: 1 = posto appartiene al sifone, 0 altrimenti function S = sifoni_ToM(I,O) %SIFONI_ToM Calcolo dei sifoni di una rete di Petri % SIFONI_ToM(I,O) ritorna una matrice in cui ogni riga rappresenta un sifone % della rete di Petri avente I come matrice di ingresso e O come matrice di uscita. % Usa la versione di Pinvarianti_S con il "Test of Minimality". % Ogni riga contiene gli indici (riferiti alla matrice di incidenza) dei posti che % fanno parte del sifone. %Costruzione della rete di Petri ausiliaria %La rete ausiliaria ha gli stessi posti, transizioni e archi della rete di partenza, %ma il peso di ogni arco entrante in una transizione viene moltiplicato per un coefficiante %pari alla somma dei pesi degli archi uscenti dalla medesima transizione. aux = repmat(sum(O,1),size(O,1),1); %matrice ausiliaria di righe tutte uguali contenenti la somma dei pesi degli archi uscenti da ogni transizione Istar = I .* aux; %.* è il prodotto elemento per elemento C=O-I; % matrice di incidenza della rete di Petri originaria P=full(spones(pinvarianti(C))); %calcola i P-invarianti della rete originaria Cstar = O - Istar; %matrice di incidenza della nuova rete di Petri %Soluzione del sistema x'Cstar <= 0 %La soluzione di questo problema è equivalente al calcolo dei P_invarianti della rete trasformata %a cui si aggiungano per ogni transizione un posto e un arco diretto dalla transizione al posto. Cstar_aux = [Cstar; eye(size(O,2))]; soluzione = pinvarianti_S_ToM(P, Cstar_aux); if ~isequal(soluzione,[]) % se soluzione non è una matrice vuota (cioè se gli unici sifoni % della rete sono i P-I di C) soluzione = sign(soluzione(:,1:size(I,1))); %AL POSTO DI I C'ERA CSTAR% vanno prese solo le colonne che si riferiscono ai posti della rete; le ultime colonne invece si riferiscono a posti fittizi end soluzione=[soluzione; P]; % aggiungi i supporti dei P-I di C a quelli trovati in più per Cstar_aux %disp('Inizio suppmin sifoni'),pause S = suppmin(soluzione); 27 %disp('Fine suppmin sifoni'),pause %riscrittura dei sifoni nella forma: 1 = posto appartiene al sifone, 0 altrimenti function o = OfromC(C) %OfromC Calcolo della matrice delle uscite O di una rete di Petri % OfromC(C) calcola la matrice delle uscite O di una rete di Petri % avente C come matrice di incidenza. [x, y]=size(C); o=C; for k = 1:x for j = 1:y if o(k,j) < 0 o(k,j)=0; end end end function i = IfromC(C) %IfromC Calcolo della matrice degli ingressi I di una rete di Petri % IfromC(C) calcola la matrice degli ingressi I di una rete di Petri % avente C come matrice di incidenza. [x, y]=size(C); i=C; for k = 1:x for j = 1:y if i(k,j) > 0 i(k,j)=0; end if i(k,j) < 0 i(k,j)=i(k,j)*(-1); end end end 28 Appendice B: grafici di Pesim di alcune reti utilizzate per il testing del programma (per ogni grafico è indicato il nome del file .txt contenente la matrice di incidenza della rete) P2 P1 T1 2 2 T4 T3 P3 T2 P4 P5 provadav2.txt T5 P1 P7 P4 P8 T1 T2 T6 T7 P2 5 P5 P9 T3 P3 P13 T4 5 P6 T8 P11 P10 P12 T9 prova.txt 29 P1 T1 P13 T11 T2 P2 T12 T3 P14 P3 T13 T4 P15 P4 T5 T14 P10 P5 P11 P12 P16 T6 T15 P6 T7 P17 P7 T16 T8 P18 P8 T17 T9 P19 P9 T20 T18 T10 P20 RETE.txt P21 T19 30