% % % % F.Marcuzzi 13/05/2005 % clc disp('Esercitazione sui sistemi con matrice sparsa :'); disp(' '); disp('(ad ogni interruzione del programma, premi un tasto per continuare)'); disp(' '); disp(' '); disp('Prenderemo in considerazione la fattorizzazione LU di una matrice.'); disp(' '); disp('Sappiamo già che la fattorizzazione LU di una matrice nxn richiede in generale circa n^3 flops.'); disp(' '); disp('Quando però ci sono elementi nulli nella matrice, si creano notevoli possibilità di ridurre'); disp('il numero di operazioni necessarie. '); pause clc disp('Ad es. consideriamo la matrice A (vedi Figura 1): '); n=100; A=rand(n); A=(A+A')./2+5*diag(diag(ones(n))); I=find(A<0.85); A(I)=0; origA=A; figure(1); clf, spy(A); title(['matrice A (isp = ' num2str( nnz(A)/(n^2) ) ')']); figure(2); clf, spy(A); title(['matrice A (isp = ' num2str( nnz(A)/(n^2) ) ')']); pause disp(' '); disp('e vediamo il numero di flops necessari alla fattorizzazione LU'); disp(' '); disp('senza tenere conto degli zeri:'); A=origA; nf = 0; for i=1:n-1 A(i+1:n,i) = A(i+1:n,i)./A(i,i); A(i+1:n,i+1:n) = A(i+1:n,i+1:n) - A(i+1:n,i)*A(i,i+1:n); nf = nf + (n-i) + 2*(n-i)^2; figure(2); clf, hold on, spy(A); plot(i,i,'ro') title(['matrice L+U (isp = ' num2str( nnz(A)/(n^2) ) ')']); tic, while toc<0.5 figure(2); drawnow, end; end; disp(['flops necessari = ' num2str(nf)]); disp(' '); disp('ed, invece, tenendo conto degli zeri:'); Az=origA; nf = 0; for i=1:n-1 I=find([zeros(i,1); Az(i+1:n,i)]); if ~isempty(I) Az(I,i)=Az(I,i)./Az(i,i); Az(I,I)=Az(I,I) - Az(I,i)*Az(i,I); nf = nf + length(I) + 2*length(I)^2; end; end; disp(['flops necessari = ' num2str(nf)]); pause disp(' '); disp(' '); disp(' '); disp(' '); disp('E'' da notare,pero'', che la fattorizzazione ha prodotto due fattori (L e U) che complessivamente'); disp('possiedono più elementi diversi da zero di quanti ne avesse la matrice di partenza:'); disp(['elementi non-zero di A = ' num2str(nnz(origA))]); disp(['elementi non-zero di L+U = ' num2str(nnz(A))]); disp(' '); disp('questo fenomeno è chiamato "fill-in"'); disp(' '); disp('Il fill-in è un fenomeno da tenere in grande considerazione, in quanto la sua presenza produce'); disp('un aumento considerevole della quantità di flops necessari durante la fattorizzazione.'); disp(' '); disp('Per rendersene conto, basta guardare le ultime righe di codice scritte qui sopra e notare ad es. che'); disp('il prodotto esterno "Az(I,i)*Az(i,I)" richiede una frazione considerevole del numero di flops complessivi'); disp(' '); pause pause pause clc disp('Il fill-in dipende fortemente dall''ordine con cui vengono eliminate le variabili'); disp('durante il processo di fattorizzazione.'); disp('Vediamo a titolo di esempio la seguente matrice (vedi Figura 1) :'); n=12; A=diag(diag(ones(n))); A(n,1:n-1)=repmat(0.1,1,n-1); A(1:n-1,n)=repmat(0.1,n-1,1); Afreccia=A; figure(1); clf, spy(A); title('matrice A'); pause, disp(' '); disp('e vediamo il numero di flops necessari alla fattorizzazione LU :'); Az=Afreccia; nf = 0; for i=1:n-1 I=find([zeros(i,1); Az(i+1:n,i)]); if ~isempty(I) Az(I,i)=Az(I,i)./Az(i,i); Az(I,I)=Az(I,I)-Az(I,i)*Az(i,I); nf = nf + length(I) + 2*length(I)^2; end; end; disp(['flops necessari = ' num2str(nf)]); disp(' '); figure(2); clf, spy(Az); title('matrice L+U'); pause disp('mentre, utilizzando l''ordinamento esattamente opposto (vedi Figura 3) : '); Az2=Afreccia(n:-1:1,n:-1:1); figure(3); clf, spy(Az2); title('matrice A riordinata nel senso opposto'); pause nf = 0; for i=1:n-1 I=find([zeros(i,1); Az2(i+1:n,i)]); if ~isempty(I) Az2(I,i)=Az2(I,i)./Az2(i,i); Az2(I,I)=Az2(I,I)-Az2(I,i)*Az2(i,I); nf = nf + length(I) + 2*length(I)^2; end; end; disp(['flops necessari = ' num2str(nf)]); figure(4); clf, spy(Az2); title('matrice L+U'); pause disp(' '); disp(' '); disp(' '); disp(' '); disp('Vediamo quale è il fill-in risultante :'); disp(['elementi non-zero di A = ' num2str(nnz(Afreccia))]); disp(['elementi non-zero di L+U secondo l''ordinamento diretto di A = ' num2str(nnz(Az))]); disp(['elementi non-zero di L+U secondo l''ordinamento opposto = ' num2str(nnz(Az2))]); disp(' '); disp('Notare che utilizzando l''ordinamento opposto, i fattori L e U risultano essere addirittura densi!'); disp(['(vedi Figura 4 ed osserva che ' num2str(nnz(Az2)) ' = ' num2str(n) 'x' num2str(n) ' )']); pause close all; clc pause disp('In generale è molto difficile sapere a-priori quale è l''ordinamento ottimale, ovvero'); disp('quello che crea meno fill-in in assoluto. '); disp('E'' un problema di complessità "NP-complete" (cresce esponenzialmente con la dimensione della matrice).'); disp(' '); disp('Per questo motivo si utilizzano di solito degli algoritmi di pre-ordinamento di tipo euristico,'); disp('basati sulla Teoria dei Grafi (perchè ad una matrice sparsa viene generalmente associato un grafo,'); disp('i cui vertici sono le variabili ed i cui lati sono gli elementi non-nulli).'); disp(' '); disp('Di questi ne citiamo alcuni (che ritroviamo implementati nel seguito):'); disp(' '); disp('1) l''algoritmo di Cuthill-McKee (symrcm), il cui obbiettivo è quello di rendere minima la banda'); disp(' della matrice riordinata.'); disp(' '); disp('2) l''algoritmo del grado minimo (colmmd e symmmd), che agisce scegliendo per l''eliminazione la colonna'); disp(' (o riga) di grado minimo (intendendo per grado il numero di lati uscenti dal vertice corrispondente alla'); disp(' colonna nel grafo associato).'); disp(' '); disp('3) l''algoritmo del grado minimo approssimato (COLAMD), che aggiorna il grado dei vertici rimanenti in modo'); disp(' approssimato e rappresenta il miglior risultato ottenuto ad oggi in questa classe di algoritmi.'); disp(' Esso e'' disponibile gratuitamente al sito http://www.cise.ufl.edu/research/sparse/colamd/ '); pause clc scelta=1; while scelta~=0 clc n=input('inserisci il numero di righe (colonne) "n" della matrice : '); s=input('inserisci un numero da 0 a 1 che indichi l''indice di densità della matrice\n1=matrice densa, 0=matrice nulla : '); A=rand(n); A=(A+A')./2+5*diag(diag(ones(n))); I=find(A<(1-s)); A(I)=0; origA=A; stessamat=1; while stessamat==1 sA=sparse(origA); disp('Algoritmi di ordinamento:'); disp('1) colmmd'); disp('2) symmmd'); disp('3) symrcm'); disp('4) colperm'); disp('5) dmperm'); disp('6) colamd (COLAMD)'); disp('7) symamd (COLAMD)'); disp('8) nessun pre-ordinamento'); m=input('scegli l''algoritmo (0 per terminare) : '); switch m case 0, break; case 1, order=colmmd(sA); case 2, order=symmmd(sA); case 3, order=symrcm(sA); case 4, order=colperm(sA); case 5, order=dmperm(sA); case 6, order=colamd(sA); case 7, order=symamd(sA); case 8, order=1:n; otherwise error('numero di algoritmo inesistente !'); end; figure(1); clf, spy(A); title('matrice A'); pause, reordA=sA(order,order); figure(2); spy(reordA); title('matrice A secondo l''ordinamento scelto'); nf = 0; for i=1:n-1 I=find([zeros(i,1); reordA(i+1:n,i)]); if ~isempty(I) reordA(I,i)=reordA(I,i)./reordA(i,i); reordA(I,I)=reordA(I,I)-reordA(I,i)*reordA(i,I); nf = nf + length(I) + 2*length(I)^2; end; figure(3); clf, spy(reordA,'m'); title('matrice L+U'), hold on, spy(sA(order,order)); plot(i,i,'ro') tic, while toc<0.5 figure(3); drawnow, end; end; disp(['flops necessari = ' num2str(nf)]); disp(['elementi non-zero di L+U = ' num2str(nnz(reordA))]); stessamat=input('come vuoi continuare : 1-stessa matrice, 2-cambia matrice, 0-uscire --> '); if stessamat==0 scelta=0; end; end; end;