Sei sulla pagina 1di 104

UNIVERSITA’ DELLA CALABRIA

Facoltà di Ingegneria
Corso di Laurea Specialistica in Ingegneria Civile
Dipartimento di Modellistica per l’Ingegneria

Tesi di Laurea Specialistica

Utilizzo di librerie di algebra lineare in meccanica


computazionale

Relatore Candidato
Alessandro DE ROSIS
Prof.Ing. Giovanni GARCEA
matr. 117811

............................ ............................
Corelatore
Ing. Giuseppe ZAGARI

............................

Anno Accademico 2008/2009


A tutti coloro che sono fieri,
severi, orgogliosi, solitari...
...e con l’aria un po’ triste

1
Indice

Introduzione 5

1 Introduzione all’architettura dei calcolatori 8


1.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2 La tecnologia pipeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 I moderni sistemi cache-based . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.1 La memoria virtuale . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4 Tecniche per l’ottimizzazione delle prestazioni . . . . . . . . . . . . . . . . . 13

2 I livelli di analisi e gli strumenti utilizzati 17


2.1 Livello 1: operazioni tra vettori . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.1.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.1.2 BLITZ++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.1.3 EIGEN 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.1.4 HAND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.5 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.6 MTL4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.7 Confronti tra le durate delle analisi . . . . . . . . . . . . . . . . . . . 20
2.2 Livello 2: operazioni tra matrici e vettori . . . . . . . . . . . . . . . . . . . 22
2.2.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.2 BLITZ++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.3 EIGEN 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.2.4 HAND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2.5 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2.6 MTL4 Dense . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2.7 MTL4 Sparse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.2.8 Confronti tra le durate delle analisi . . . . . . . . . . . . . . . . . . . 24
2.3 Livello 3: operazioni tra matrici . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3.2 BLITZ++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3.3 EIGEN 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3.4 HAND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.3.5 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

2
2.3.6 MTL4 Dense . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3.7 MTL4 Sparse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3.8 Confronti tra le durate delle analisi . . . . . . . . . . . . . . . . . . . 28

3 Metodi diretti per la risoluzione di sistemi lineari 30


3.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.2 Soluzione di sistemi triangolari . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.3 Il Metodo di Eliminazione di Gauss (GEM) . . . . . . . . . . . . . . . . . . 32
3.4 La fattorizzazione LU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.5 La fattorizzazione di Cholesky . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4 Metodi diretti per la risoluzione di sistemi lineari - Applicazioni 38


4.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.2 Stima delle performance dei solutori . . . . . . . . . . . . . . . . . . . . . . 39
4.3 Solutori per matrici generiche . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.3.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.3.2 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.4 Solutori per matrici simmetriche . . . . . . . . . . . . . . . . . . . . . . . . 41
4.4.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.4.2 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.4.3 MTL 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.5 Solutori per matrici bandate . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.5.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.5.2 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.5.3 BANDEC/BANDSOL . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.5.4 Confronti tra le durate delle analisi . . . . . . . . . . . . . . . . . . . 44
4.6 Solutori per matrici sparse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.6.1 CHOLMOD SuiteSparse . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.6.2 UMFPACK SuiteSparse . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.6.3 SPOOLES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.6.4 PARDISO MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.6.5 Confronti tra le analisi . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.7 L’impatto del calcolo high performance sulla risoluzione dei sistemi lineari . 59

5 Approssimazione di autovalori e autovettori 60


5.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.2 Concetti fondamentali alla base della risoluzione dei problemi agli autovalori 61
5.2.1 Proprietà degli autovettori . . . . . . . . . . . . . . . . . . . . . . . . 61
5.2.2 Lo shifting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
5.2.3 Trasformazione di un problema generalizzato in uno standard . . . . 63
5.2.4 Stima dell’errore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.3 L’iterazione inversa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
5.4 L’iterazione diretta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
5.5 Il metodo di Jacobi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

3
5.6 L’iterazione inversa Householder-QR . . . . . . . . . . . . . . . . . . . . . . 69
5.6.1 La riduzione di Householder . . . . . . . . . . . . . . . . . . . . . . . 69
5.6.2 L’iterazione QR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5.6.3 Calcolo degli autovettori . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.7 Il metodo di Lanczos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

6 Approssimazione di autovalori e autovettori - Applicazioni 73


6.1 Autovalori di una matrice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
6.1.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
6.1.2 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.2 Problema generalizzato agli autovalori . . . . . . . . . . . . . . . . . . . . . 76
6.2.1 ACML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
6.2.2 MKL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
6.2.3 ARPACK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
6.2.4 Confronti tra le durate delle analisi . . . . . . . . . . . . . . . . . . . 79

7 Applicazione di meccanica delle strutture 83


7.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.2 Sistemi lineari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
7.2.1 Test 1 - Cbeam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.2.2 Test 2 - Ibeam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.3 Problema agli autovalori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
7.3.1 Test 1 - Cupola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
7.3.2 Test 2 - Traliccio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

8 Conclusioni 93

A Richiami di algebra lineare 96


A.1 Introduzione alle matrici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
A.2 Matrici particolari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
A.3 Prodotto tra matrici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

4
Introduzione

Molti fenomeni fisici di interesse applicativo possono essere opportunamente descritti in


termini di equazioni differenziali alle derivate parziali. Anche nel più semplice caso in cui
le equazioni che regolano il problema siano lineari è praticamente impossibile risolvere in
termini analitici problemi del genere specie quando i domini su cui gli stessi sono definiti
hanno forme arbitrarie come in effetti avviene per molte delle applicazioni ingegneristiche.
Il metodo degli elementi finiti, che nel seguito indicheremo brevemente con la sigla
(FEM), è una tecnica tramite la quale la formulazione differenziale di partenza del pro-
blema viene trasformate in una forma algebrica, più semplice da risolvere per mezzo di un
computer e che comunque da una soluzione numerica approssimata dello stesso. In effetti
ad oggi il metodo degli elementi finiti è il più affermato ed utilizzato per risolvere le più
svariate classi di problemi e milioni di ingegneri e ricercatori in tutto il mondo usano for-
mulazioni FEM per predire il comportamento e progettare sistemi strutturali, meccanici,
termici, elettrici e chimici.
Semplificando molto, l’idea alla base del metodo è quella di dividere il dominio su cui
è definito il problema in sotto-domini, chiamati elementi finiti o semplicemente elemen-
ti, legati gli uni agli altri per mezzo di nodi. Il processo di creazione dei sottodomini si
chiama di discretizzazione o di generazione della mesh. Il metodo FEM descrive il proble-
ma differenziale approssimando le funzioni continue che lo definiscono, in termini di una
interpolazione delle stesse sull’elemento, definita in termini dei valori che le quantità da
interpolare assumono ai nodi. Tali quantità nodali diventano le incognite del problema
algebrico. Il generico problema differenziale di partenza è trasformato quindi tramite la
tecnica FEM ,in un equivalente problema algebrico, generalmente definito da un sistema
di equazioni lineari. Il numero di incognite del problema dicretizzato è legato al numero
dei nodi della discretizzazione. Poichè per ottenere soluzioni ragionevolmente accurate in
genere è necessario risolvere problemi con migliaia di incognite, l’uso del computer diventa
essenziale. Inoltre l’accuratezza della soluzione migliora all’aumentare del numero di nodi
cosı̀ come aumenta anche il costo computazionale richiesto per ottenere la soluzione. Il
ricorso a tecniche numeriche efficienti diventa quindi indispensabile [6].
Il problema discretizzato diventa pertanto un problema algebrico descritto da matrici
e vettori, semplici da trattare in termini numerici, facendo ricorso a tutta una classe di
librerie capaci di manipolare queste quantita quali prodotti scalari e matriciali, soluzioni
di sistemi lineari, problemi agli autovalori etc, tipiche dell’algebra lineare e dell’analisi
numerica. In ambito FEM lo sviluppo di sofisticate routine di algebra lineare riveste
quindi un ruolo cruciale. Si pone quindi la questione di come e fino a che punto sia

5
possibile migliorare le performance di codici FEM migliorando ed ottimizzando l’utilizzo
di tali librerie. La necessità è sentita sia nel caso di analisi lineare, vista le notevoli
dimensioni che può assumere il problema, ma ancor di più in contesti di analisi nonlineare,
che solitamente richiedono al soluzione in successione di diversi sotto-problemi lineari.
In quest’ottica si inserisce questo lavoro di tesi che è volto a passare in rassegna al-
cuni tra i più efficaci strumenti computazionali a disposizione e a mettere in luce quanto
alcuni più di altri influenzino fortemente le prestazioni computazionali. In quest’ottica
un punto basilare che bisogna affrontare è quello che riguarda la tecnologia dei moderni
microprocessori utilizzati dai calcolatori e come essi interagiscono con il software numerico
che implementa le librerie di analisi numerica.
In effetti fino a qualche hanno fa l’architettura dell’hardware, a parità di risorse di cal-
colo, aveva poca importanza nella performance del codice [20]. La ricerca è stata pertanto
essenzialmente indirizzata a come sviluppare delle tecniche numeriche efficienti capaci di
tener conto della particolare struttura del problema e di avvantaggiarsene nella soluzione.
Se pensiamo sempre all’esempio della soluzione dei sistemi lineari, che hanno cosı̀ tanta
importanza nelle formulazione FEM, si sono create una serie di routines capace di sfrut-
tare al meglio ad esempio le caratteristiche della matrice che governa il sistema lineare,
sviluppando procedure ad hoc per matrici definite positive, simmetriche, bandate o sparse.
Oltre a questa ottimizzazione oggi è necessario però soffermarsi anche sull’ottimizzazio-
ne di altri aspetti legati a come i dati e le operazioni vengono eseguite all’interno del
microprocessore E’ necessario cioè sfruttare al massimo anche glia accessi alle risorse di
memoria Cache e allocare in queste zone di accesso superveloce le quantità utilizzate, evi-
tando di effettuare operazioni che comportano l’accesso a quantità allocate in aree a lento
accesso, tipicamente la aree di memoria RAM. Cosı̀ per esemplificare anche se si utilizza
un efficiente metodo numerico che effettua la decomposizione di una matrice simmetrica
di dimensione n e banda m esattamente in bn2 /2 operazioni elementari, cioè il minimo
teorico, se la programmazione non cura l’aspetto hardware il metodo stesso può risultare
molto meno efficiente di una differente implementazioni anche meno sofisticata dal punto
di vista delle tecniche di analisi numerica, ma che però sfrutta al meglio l’hardware del
microprocessore.
Ad oggi esistono una serie di librerie di algebra numerica ad uso gratuito e facilmen-
te disponibili su internet. La tesi propone una serie di test sulle librerie più importanti
disponibili in rete a partire sai test delle più banali operazioni matriciali e vettoriali alle
soluzione di sistemi lineari in vari contesti ed alla soluzione di problemi agli autovalori
utilizzando sia matrici e vettori forniti in [4], ma anche sistemi algebrici derivanti da pro-
blemi di meccanica delle strutture. Infine le procedure sono state implementate all’interno
di codici FEM per l’analisi lineare e nonlineare di strutture a pannelli sottili o travi 3D.
Anche in questo caso sono stati riportati una serie di test numerici per verificare l’efficienza
computazionale dell’implementazione proposta.
La tesi è cosı̀ organizzata: nel primo capitolo verrà esposta l’architettura dei moderni
sistemi, soffermandosi in particolar modo sull’aspetto del memory management e sulle
strategie per migliorare l’accesso ai dati; il secondo capitolo si propone di utilizzare alcune
librerie di algebra lineare per eseguire operazioni tra vettori e matrici e di misurare la pre-
stazionalità di queste in relazione al tempo impiegato; il terzo capitolo espone gli aspetti

6
teorici che stanno alla base della risoluzione di un sistema lineare di equazioni, quali il
metodo di eliminazione di Gauss, la fattorizzazione LU e quella alla Cholesky; il quarto
capitolo si preoccupa di passare in rassegna i vari strumenti utilizzabili e di stimarne le
performance; il quinto capitolo discute della teoria del calcolo di autovalori e autovettori,
tanto nel caso che siano richieste tutte le autosoluzioni (algoritmo QR), quanto che ne
siano desiderate solo alcune (metodo di Lanczos); il sesto capitolo espone i risultati della
campagna di sperimentazione relativa alla risoluzione di un problema generalizzato agli
autovalori; il settimo capitolo dimostra come tutti gli strumenti finora discussi siano facil-
mente utilizzabili in contesti tipici della meccanica computazionale e che la loro adozione
non può che portare benefici all’intero processo di analisi; segue quindi l’ottavo capitolo
dove vengono messi in luce tutti gli aspetti riguardanti la campagna di sperimentazione.

7
Capitolo 1

Introduzione all’architettura dei


calcolatori

1.1 Introduzione
Si supponga di dover svolgere il prodotto scalare di due vettori di dimensione n: la com-
plessità del problema, intesa come numero di operazioni elementari da svolgere, è pari a
2n. Si sarebbe portati a pensare che su una stessa macchina questa operazione svolta da
librerie diverse impieghi durate dell’analisi del tutto uguali, ma ciò non è vero. Le librerie
a disposizione, a differenza di un’implementazione a mano delle operazioni, sono in grado
di sfruttare la tecnologia del calcolatore e quindi traggono risorse (in termini di risparmio
sulla durata dell’analisi) dalla particolare architettura del calcolatore [12], [13].
Si rende pertanto necessario discutere al riguardo della tecnologia dei moderni micro-
processori che equipaggiano i calcolatori [17].

1.2 La tecnologia pipeline


La pipeline dati è una tecnologia utilizzata dai microprocessori per incrementare il through-
put, ovvero la quantità di istruzioni eseguite in una data quantità di tempo. L’elaborazione
di un’istruzione da parte di un processore si compone di cinque passaggi fondamentali:

• IF: Lettura dell’istruzione da memoria

• ID: Decodifica istruzione e lettura operandi da registri

• EX: Esecuzione dell’istruzione

• MEM: Attivazione della memoria (solo per certe istruzioni)

• WB: Scrittura del risultato nel registro opportuno

Praticamente ogni CPU in commercio è gestita da un clock centrale e ogni operazione


elementare richiede almeno un ciclo di clock per poter essere eseguita. Le prime CPU

8
erano formate da un’unità polifunzionale che svolgeva in rigida sequenza tutti e cinque
i passaggi legati all’elaborazione delle istruzioni. Una CPU classica (1.1) richiede quindi
almeno cinque cicli di clock per eseguire una singola istruzione.

Figura 1.1: Esecuzione delle istruzioni in un microprocessore senza pipeline

Con il progresso della tecnologia si è potuto integrare un numero maggiore di transistor


in un microprocessore e quindi si sono potute parallelizzare alcune operazioni riducendo
i tempi di esecuzione. La pipeline dati è la massima parallelizzazione del lavoro di un
microprocessore.
Una CPU con pipeline (1.2) è composta da cinque stadi specializzati, capaci di eseguire
ciascuno una operazione elementare di quelle sopra descritte. La CPU lavora come in una
catena di montaggio e quindi ogni stadio provvede a svolgere solo un compito specifico.
Quando la catena è a regime, ad ogni ciclo di clock esce dall’ultimo stadio un’istruzione
completata. Nello stesso istante ogni unità sta elaborando in parallelo i diversi stadi
delle successive istruzioni. In sostanza si guadagna una maggior velocità di esecuzione a
prezzo di una maggior complessità circuitale del microprocessore, che non deve essere più
composto da una sola unità ma da cinque unità che devono collaborare tra loro.

Figura 1.2: Esecuzione delle istruzioni in un microprocessore con pipeline

Le singole pipeline affrontano due problemi principalmente:

• la presenza di istruzioni che possono richiedere l’elaborazione di dati non ancora


disponibili;

• la presenza di salti condizionati.


Il primo problema deriva dal lavoro parallelo delle unità. Supponiamo che la CPU con
pipeline debba eseguire il seguente frammento di codice:
C = A + B
D = C - 1

La prima istruzione deve prelevare i numeri contenuti nelle variabili A e B, sommarli e porli
nella variabile C. La seconda istruzione deve prelevare il valore contenuto nella variabile

9
C, sottrarlo di uno e salvare il risultato in D. Ma la seconda istruzione non potrà essere
elaborata (EX) fino a quando il dato della prima operazione non sarà disponibile in memo-
ria (MEM) e quindi la seconda operazione dovrà bloccarsi per attendere il completamento
della prima e quindi questo ridurrà il throughput complessivo.
Il secondo problema consiste nei salti condizionati. I programmi contengono delle istru-
zioni condizionate che se una specifica condizione è verificata provvedono a interrompere
il flusso abituale del programma e a mandare in esecuzione un altro pezzo di programma
indicato dall’istruzione di salto. Ogni volta che questo accade il microprocessore si trova a
dover eseguire un nuovo flusso di operazioni e quindi deve svuotare la pipeline del prece-
dente flusso e caricare il nuovo flusso. Ovviamente queste operazioni fanno sprecare cicli di
clock e quindi deprimono il throughput. Per ridurre questo problema le CPU adottano del-
le unità chiamate unità di predizione delle diramazioni (in inglese Branch Prediction Unit)
che fanno delle previsioni sul flusso del programma. Queste unità riducono notevolmente
i cicli persi per i salti.

1.3 I moderni sistemi cache-based


Gli sviluppi della tecnologia hanno permesso di realizzare dei microprocessori sempre più
veloci rispetto alla memoria principale tant’è che l’accesso alla memoria rappresenta un
vero e proprio collo di bottiglia nella performance complessiva di un’applicazione. Già
nel 1970, Moore avanzò la sua legge secondo la quale nei successivi trent’anni la velocità
della CPU sarebbe cresciuta sempre più, mentre quella della RAM si sarebbe mantenuta
pressochè costante (si faccia riferimento alla figura (1.3)).

Figura 1.3: Evoluzione della velocità della CPU e della RAM

Di conseguenza un’applicazione può perdere molto tempo in attesa che le vengano

10
forniti i dati di cui necessita. Tutto ciò non solo ha un impatto negativo sulla performance
globale, ma in aggiunta impedisce all’applicazione di sfruttare l’elevata velocità della CPU.
Un modo per aggirare questo problema è quello di inserire una piccola memoria ad alta
velocità tra il processore e la memoria principale (1.4); questa memoria buffer è nota
come cache. L’applicazione trae vantaggio dal caricamento dei dati dalla cache anzichè
dalla memoria principale; infatti, essendo il tempo di accesso alla cache molto ridotto, le
performance subiscono un miglioramento.

Figura 1.4: Moderni sistemi cache-based

Si supponga di dover eseguire una certa applicazione. Dapprima il sistema copia i dati
di cui necessita la CPU dalla memoria alla cache e poi da questa in un registro della CPU.
Qualora l’applicazione dovesse aver di nuovo bisogno di questi dati, il tempo di accesso a
questi si riduce notevolmente se essi sono ancora contenuti nella cache. Per ammortizzare
il costo di questo trasferimento di memoria, viene caricato nella cache più di un elemento.
L’unita di trasferimento si chiama cache block o cache line; l’accesso a un singolo dato
porta un’intera linea nella cache. A ciò fa riferimento il concetto di sub-blocking. Con il
sub-blocking la cache alloca una linea/blocco con una lunghezza che è multipla della cache
line. Gli slot interni al blocco più grande sono poi riempiti con le singole cache line (o
sub-block). Tale modo di fare funziona bene se l’accesso alle line è consecutivo.
Quando il processore vuole leggere o scrivere in una data collocazione in memoria
principale, inizialmente controlla se il contenuto di questa posizione è caricato in cache.
Questa operazione viene effettuata confrontando l’indirizzo della posizione di memoria
con tutte le etichette nella cache che potrebbero contenere quell’indirizzo. Se il processore
trova che la posizione di memoria è in cache, si parla di cache hit (accesso avvenuto con
successo), altrimenti di cache miss (fallimento d’accesso). Nel caso di un cache hit, il
processore legge o scrive immediatamente il dato sulla linea di cache. Il rapporto tra
cache hit e accessi totali è chiamato anche hit rate ed è una misura dell’efficacia della
cache stessa.
Nel caso di un cache miss, la maggior parte delle cache crea una nuova entità, che
comprende l’etichetta appena richiesta dal processore ed una copia del dato dalla memoria.
Un fallimento del genere è relativamente lento, in quanto richiede il trasferimento del dato
dalla memoria principale, il cui tempo di risposta è molto maggiore di quello della memoria
cache.
Per poter fare spazio a nuovi dati nel caso di un cache miss, la cache generalmente deve

11
eliminare il contenuto di una delle linee. L’euristica che utilizza per scegliere quale dato
eliminare è chiamata politica di rimpiazzamento. Il problema fondamentale di ogni politica
di rimpiazzamento è quello di dover predire il dato della cache che verrà richiesto nel futuro
con minor probabilità. Predire il futuro è difficile, soprattutto per le cache hardware che
devono sfruttare regole facilmente implementabili in circuiteria, perciò esistono una serie
di politiche di rimpiazzamento e nessuna di esse può essere ritenuta perfetta. Una delle
più popolari, la LRU (dall’inglese Least Recently Used, cioè usato meno recentemente),
rimpiazza, appunto, il dato al quale si è fatto accesso meno recentemente.
Quando un dato è scritto nella cache, dopo un po’ di tempo deve comunque essere
scritto in memoria principale. La decisione del momento in cui questa scrittura deve aver
luogo è controllata dalla politica di scrittura. In una cache write-through, ogni scrittura
sulla cache comporta una scrittura contemporanea nella memoria principale. In alternati-
va, una cache write-back non esegue immediatamente questa azione: al contrario, la cache
tiene traccia delle linee che contegono dati da aggiornare settando opportunamente quello
che viene chiamato il dirty bit. Il dato viene effettivamente scritto in memoria solo quando
esso deve essere eliminato dalla cache per far spazio a nuove informazioni. Per questa ra-
gione, una ricerca fallita in una cache write-back spesso genera due accessi alla memoria:
uno per leggere il nuovo dato, l’altro per scrivere la vecchia informazione (se indicato dal
dirty bit).Sia il write-back che il write-through servono a mantenere la coerenza tra i livelli
di memoria.
Esistono anche alcune politiche intermedie. La cache potrebbe essere ad esempio write-
through, ma le scritture potrebbero essere temporaneamente inserite in una coda, cosı̀ da
processare insieme scritture multiple, ottimizzando l’accesso al bus.
I dati in memoria principale, dei quali esiste una copia nella cache, potrebbero essere
modificati da altre cause (evento non improbabile, ad esempio, in un sistema multiproces-
sore), perciò i dati nella cache potrebbero diventare obsoleti. I protocolli di comunicazione
tra i sistemi di gestione delle cache che conservano la consistenza dei dati sono chiamati
protocolli di coerenza.
Il tempo impiegato per leggere un dato dalla memoria (latenza di lettura) è importante,
perché spesso una CPU potrebbe completare la propria coda di operazioni mentre aspetta
l’arrivo del dato richiesto. Quando un microprocessore raggiunge questo stato, si parla
di stallo della CPU. Con l’aumento della velocità dei microprocessori, l’andare in stallo
per cache miss spreca molta potenza di calcolo; le CPU moderne, infatti, possono eseguire
centinaia di istruzioni nello stesso tempo necessario per caricare un singolo dato dalla
memoria.

1.3.1 La memoria virtuale


La maggior parte delle CPU comunemente utilizzate implementano un qualche tipo di me-
moria virtuale. In pratica, ogni programma che gira sulla macchina vede il proprio spazio
di memoria, che contiene codice e dati per il solo programma stesso, in maniera semplifi-
cata. Ogni programma mette tutto nel proprio spazio di memoria senza preoccuparsi di
quello che gli altri programmi fanno nei loro rispettivi spazi di memoria.
La memoria virtuale richiede che il processore traduca gli indirizzi virtuali generati dal

12
programma in indirizzi fisici nella memoria principale. La porzione del processore che fa
questa traduzione è conosciuta come la memory management unit (MMU ). La MMU può
accedere velocemente alla tabella di traduzioni attraverso il Translation Lookaside Buffer
(TLB), che è una cache di mappature per la page table del sistema operativo.
La traduzione degli indirizzi ha tre caratteristiche importanti:

• Latenza: generalmente, la MMU rende disponibile l’indirizzo fisico pochi cicli dopo
che l’indirizzo virtuale è computato dal generatore di indirizzi;

• Aliasing: più indirizzi virtuali possono riferirsi ad uno stesso indirizzo fisico. La
maggior parte dei processori garantisce che tutti gli aggiornamenti al singolo indirizzo
fisico vengano eseguiti in ordine. Per permettere ciò, il processore deve assicurarsi
che, in ogni istante, esista in cache una sola copia di ogni indirizzo fisico;

• Granularità: lo spazio degli indirizzi virtuali è suddiviso in pagine. Per esempio,


uno spazio virtuale di indirizzi di 4GB potrebbe essere spezzettato in 1048576 pagine
da 4Kb, ognuna delle quali può essere referenziata indipendentemente.

L’esistenza di indirizzi fisici e virtuali pone la questione su quali di essi utilizzare per
le etichette e gli indici della cache. La motivazione di usare indirizzi virtuali è la velocità:
una cache di dati con indici ed etichette virtuali esclude la MMU dalle operazioni di
caricamento ed uso dei dati dalla memoria. Il ritardo provocato dal caricamento dei dati
dalla memoria RAM (load latency) è cruciale per le prestazioni della CPU: per questo
motivo, la maggior parte delle cache di livello 1 sono indicizzate con indirizzi virtuali,
permettendo alla MMU di ricercare nella TLB in parallelo con il recupero dei dati dalla
cache della RAM.
L’indirizzamento virtuale non è sempre la scelta migliore: introduce, ad esempio, il
problema degli alias virtuali, cioè la cache potrebbe immagazzinare in più locazioni il
valore di uno stesso indirizzo fisico. Il costo per la gestione degli alias virtuali cresce con
la dimensione della cache e, come risultato, la maggior parte delle cache di livello 2 e
superiori sono indicizzate con indirizzi fisici.
Nei microprocessori moderni è possibile trovare alcuni tipi di cache che differiscono tra
di loro non solo per la dimensione e le funzioni, ma anche per la particolare organizzazione
interna. Trasformare un indirizzo virtuale di un dato in uno fisico è un operazione piuttosto
costosa. La TLB è una cache che memorizza questi indirizzi trasformati. Ogni elemento
della TLB fa riferimento a una memoria virtuale. La CPU può solo operare su dati
e istruzioni che hanno il riferimento nella TLB; qualora questo non dovesse esserci, il
sistema deve crearlo di nuovo e ciò comporta un certo costo.

1.4 Tecniche per l’ottimizzazione delle prestazioni


Una tecnica per eliminare i tempi morti nell’accesso ai dati è il Prefetch. Raggiungere i
dati presenti nella memoria principale ha solitamente un costo rilevante. La tecnica del
Prefetch è usata per eliminare (o quantomeno ridurre) il tempo che il processo spende

13
Figura 1.5: La memoria virtuale

Figura 1.6: Tecnica del Prefetch

14
attendendo che i dati arrivino nei registri. Con questa tecnica i dati sono spostati più
prossimi all’utilizzo da parte della CPU.
Un’altra tecnica che si può utilizzare per l’incremento delle prestazioni è nota come
loop-unrolling. Essa si traduce nello srotolamento del ciclo consistente nel modificare il
controllo del ciclo e nel replicare opportunamente le istruzioni all’interno dello stesso. I
vantaggi di questa tecnica sono:

• utilizzo ottimale dei processori con architettura pipelined;

• riduzione dell’overhead del ciclo di iterazione;

• riduzione del numero di trasferimenti fra i vari livelli memoria;

• aumento delle operazioni concorrenti.

L’overhead del programma e il numero di trasferimenti di dati dai livelli più bassi di
memoria ai registri si riducono di un fattore proporzionale alla nuova lunghezza del ciclo
(profondità dell’unrolling). Questa tecnica ha però anche degli svantaggi:

• aumento della memoria destinata al programma;

• perdita della generalità del codice.


Si pensi ad esempio a una procedura che deve operare il prodotto scalare tra due vettori
di 100 elementi:
for(int i = 0; i < 100; i++)
c += a[i]*b[i];

Usare la tecnica del loop-unrolling si traduce in


for(int i = 0; i < 100; i += 5)
{
c += a[i]*b[i];
c += a[i+1]*b[i+1];
c += a[i+2]*b[i+2];
c += a[i+3]*b[i+3];
c += a[i+4]*b[i+4];
}

Il nuovo programma non deve fare più 100 loop, bensı̀ 20. D’altro canto sono state
inserite delle righe di codice in più e il compilatore deve allocare più registri per memoriz-
zare le variabili nell’iterazione del loop allargato. In aggiunta a quanto appena esposto,
sono disponibili anche altre tecniche per l’ottimizzazione delle prestazioni. Esse vengono
eseguite lanciando il compilatore con delle particolari opzioni [19]; in particolare l’opzione
più interessante è -O3 che oltre al prefetch e al loop-unrolling utilizza altre tecniche inte-
ressanti come la common subexpression elimination che ottimizza un ciclo for quando in
esso appaiono delle quantità costanti. Ma ne esistono anche molte altre che ottimizzano i
riferimenti a strutture dati, riducono la dimensione di queste, pianificano il flusso di dati
e allineano gli stessi.

15
Alla luce di tutto ciò lo scopo di questa tesi è quella di dimostrare come, a parità di
algoritmo, le routines che riescono a sfruttare meglio proprietà quale l’organizzazione della
cache risultano essere le più performanti.
Per la la campagna di test numerici, condotta nei capitoli successivi è stata utilizzata
una macchina dotata di processore Intel(R) Pentium(R) 4 Dual Core CPU 3.20GHz, L2
cache 1024 KB, RAM 2GB, equipaggiata con sistema operativo Ubuntu Jaunty Jackalope
9.04.

16
Capitolo 2

I livelli di analisi e gli strumenti


utilizzati

In questo capitolo verranno passati in rassegna alcuni strumenti messi a disposizione per
compiere operazioni tra vettori e matrici. Questi strumenti sono delle librerie di algebra
lineare utilizzate per condurre delle analisi organizzate in livelli cosı̀ classificabili:

• Livello 1: operazioni tra vettori;

• Livello 2: operazioni tra vettori e matrici;

• Livello 3: operazioni tra matrici.

Per ogni livello di analisi sono stati implementati dei codici di calcolo sfruttando le
seguenti librerie:

• AMD Core Math Library (nel seguito ACML), costituite da subroutines di al-
gebra lineare (BLAS) ottimizzate per fornire elevate prestazioni sui processori AMD
e da una suite completa di algebra lineare (LAPACK [5]);

• Intel Math Kernel Library (nel seguito MKL), costituita dalle stesse librerie
della suite precedente, ma ottimizzata per i processori Intel;

• BLITZ++ Numerical Library [18], nata con lo scopo di sviluppare tecniche in


grado di rendere C++ all’altezza della velocità del Fortran nel compiere operazioni
numeriche;

• The Matrix Template Library 4 [14] (nel seguito MTL 4), una libreria che
associa alla velocità delle operazioni una sintassi molto semplice e intuitiva;

• Eigen 2 [1], una libreria ottimizzata per fornire elevate prestazioni con il compilatore
GCC.

Sono state inoltre implementate delle routines che svolgono le operazioni manualmente,
cioè senza l’ausilio di librerie addizionali.

17
Nel seguito verranno passati in rassegna tutti i livelli dell’analisi e verranno illustrati i
codici sviluppati ad hoc per l’utilizzo di ogni libreria. Lo scopo di queste analisi sarà quello
di fornire una classificazione delle prestazioni delle librerie sopra elencate in funzione del
tempo impiegato per l’analisi e del numero di operazioni effettuate nell’unità di tempo
(flops).

2.1 Livello 1: operazioni tra vettori


Questo capitolo affronterà il problema della prodotto scalare tra due vettori esprimibile
nella classifica formula c = a ∗ b utilizzando le metodologie precedentemente elencate.
Tutte le routines ricevono in input un file di testo dal qual ricavano il vettore a; viene
assegnato di default un secondo vettore b posto avere tutti i termini unitari e tramite una
o più istruzioni viene di volta in volta calcolato il prodotto scalare c.

2.1.1 ACML
L’utilizzo della libreria ACML richiede l’inclusione del header file acml.h e si concretizza
nell’ istruzione

double ddot(int n, double *a, int inca, double *b, int incb)

dove n è la dimensione dei vettori a e b, inca e incb sono rispettivamente gli incrementi
con i quali devono essere scanditi i vettori a e b. Il vettore a di dimensione M ∗ N è stato
ricavato a partire dall’input di una matrice A la quale è stata poi per colonne disposta in
un unico vettore.

2.1.2 BLITZ++
La BLITZ++ Numerical Library richiede l’header file blitz/array.h. La sintassi con la
quale si dichiara un generico vettore è:

Array<type,dimension> name(length)

dove type è la tipologia degli elementi che costiuiscono il vettore (double, int, float, com-
plex), dimension è l’ordine dello stesso (1=vettore, 2=matrice), name è il nome che gli si
intende dare e length è il numero di elementi che lo costituiscono.
E’ interessante notare come la sintassi della funzione che calcola il prodotto scalare sia
molto semplice e intuitiva:

c = sum(a(i) * b(i))

2.1.3 EIGEN 2
La dichiarazione di un vettore passa attraverso l’istruzione

VectorXd name(length,1);

18
dove la lettera d indica che si tratta di un array di double. Questa libreria consente
anche con una semplice istruzione di inizializzare a 0 o a 1 tutti i termini di un vettore:

a = VectorXd::Zero(M*N);
b = VectorXd::Ones(M*N);

L’istruzione dalla sintassi molto semplice che consente di calcolare il prodotto scalare
è:

double c = a.dot(b);

2.1.4 HAND
Questa routine calcola il prodotto scalare tra due vettori manualemente, cioè un ciclo f or
innesta il prodotto tra componenti con lo stesso indice e incrementa di questo risultato
una certa variabile inizializzata precedentemente a zero. Quando la routine esce dal ciclo,
la variabile assume il valore del prodotto scalare.

double c = 0.0;
for(int i=0; i<M*N; i++)
c += a[i]*b[i];

2.1.5 MKL
L’utilizzo della libreria MKL richiede l’inclusione del header file mkl.h e si concretizza nella
stessa istruzione usata con le librerie ACML.

2.1.6 MTL4
La libreria Matrix Template Library 4 richiede l’header file boost/numeric/mtl/mtl.hpp.
La dichiarazione di un vettore è fatta nella forma:

dense_vector<type> name(length)

dove type è la tipologia degli elementi che costiuiscono il vettore (double, int, float, com-
plex), name è il nome che gli si intende dare e length è il numero di elementi che lo
costituiscono. L’operazione di prodotto scalare si concretizza nell’espressione:

c = dot(a,b)

piuttosto simile a quella delle librerie ACML.

19
2.1.7 Confronti tra le durate delle analisi
Vengono adesso proposti dei confronti tra le durate delle analisi con le metodologie espo-
ste nei paragrafi precedenti. Tutte le routines ricevono in input una matrice quadrata
simmetrica generata random tramite l’ausilio di Matlab.

Prodotto scalare tra due vettori


0.16
acml
blitz
eigen
0.14 hand
mkl
mtl
0.12
Durata dell’analisi [s]

0.1

0.08

0.06

0.04

0.02

0
0 2e+06 4e+06 6e+06 8e+06 1e+07 1.2e+07 1.4e+07 1.6e+07
Dimensione

Figura 2.1: Confronti tra i tempi dell’analisi del prodotto scalare tra due vettori

Prodotto scalare tra due vettori


10
acml
blitz
eigen
hand
mkl
mtl

1
GFlops [log]

0.1

0.01
100 1000 10000 100000 1e+06 1e+07 1e+08
Dimensione [log]

Figura 2.2: Confronti tra i GFlops dell’analisi del prodotto scalare tra due vettori

20
sqrt(dim) ACML BLITZ EIGEN2 HAND MKL MTL4
10 0.0 0.0 0.0 0.0 0.0 0.0
20 0.0 0.0 0.0 0.0 0.0 0.0
30 0.0 0.0 0.0 0.0 0.0 0.0
40 0.0 0.0 0.0 0.0 0.0 0.0
50 0.0 0.0 0.0 0.0 0.0 0.0
60 0.0 0.0 0.0 0.0 0.0 0.0
70 0.0 0.0 0.0 0.0 0.0 0.0
80 0.0 0.0 0.0 0.0 0.0 0.0
90 0.0 0.0 0.0 0.0 0.0 0.0
100 0.00003 0.00007 0.00008 0.00004 0.00008 0.00005
200 0.0001 0.0003 0.00033 0.00016 0.00014 0.00024
300 0.00047 0.0007 0.00074 0.0004 0.00035 0.0005
400 0.001 0.00144 0.0014 0.0009 0.0009 0.0011
500 0.0018 0.0023 0.0022 0.00178 0.00176 0.00188
600 0.0027 0.00336 0.0033 0.00276 0.00265 0.00274
700 0.0036 0.00458 0.00451 0.0037 0.00364 0.00377
800 0.00477 0.00589 0.0059 0.0049 0.00476 0.00488
900 0.006 0.00757 0.00746 0.0062 0.006 0.00618
1000 0.006 0.0089 0.009 0.007 0.007 0.0075
1200 0.008 0.01296 0.013 0.011 0.0108 0.0107
1500 0.013 0.0204 0.0204 0.017 0.0168 0.01717
1700 0.016 0.0263 0.027 0.022 0.022 0.02223
2000 0.022 0.036 0.0363 0.0306 0.0304 0.03081
2200 0.03 0.044 0.044 0.03656 0.0369 0.03739
2500 0.044 0.0571 0.0567 0.0478 0.0473 0.04795
2700 0.052 0.0667 0.0664 0.0559 0.0551 0.05569
3000 0.061 0.0823 0.0818 0.0692 0.0679 0.0688
3200 0.0749 0.0939 0.0936 0.0786 0.078 0.0783
3500 0.0875 0.1128 0.1118 0.0932 0.0918 0.0937
3700 0.0977 0.1260 0.1257 0.1044 0.1006 0.1047
4000 0.1174 0.1466 0.1472 0.1215 0.1154 0.122

Tabella 2.1: Durate delle analisi del prodotto tra due vettori

21
2.2 Livello 2: operazioni tra matrici e vettori
In questo paragrafo le librerie vengono utilizzate per compiere l’operazione di prodotto tra
una matrice e un vettore. La matrice viene fornita come input tramite un file di testo,
mentre il vettore è assegnato di default come unitario.

2.2.1 ACML
Le routines ACML sono sritte in Fortran e leggono una matrice A a partire da un vettore a
che contiene tutte le colonne della matrice in fila. Il cuore di questa routine è la chiamata
alla funzione che calcola il prodotto tra una matrice e un vettore:

dgemv(transa, m, n, alpha, a, lda, b, incb, beta, *c, incc);

dove transa specifica se si desidera la trasposta della matrice (’t’) oppure meno (’n’),
m e n sono le dimensioni di A, α è uno scalare che moltiplica A, a è il vettore che contiene
la successione delle colonne di A, lda è il numero di righe di A, b è il vettore che moltiplica
A, incb è l’incremento dei termini di b, β è uno scalare che moltiplica il vettore c, c è un
vettore che in output contiene la soluzione, incc è l’incremento dei termini di c.

2.2.2 BLITZ++
Anche in questo caso, grazie a una sintassi molto semplice

firstIndex i;
secondIndex j;
y = sum(A(i,j) * b(j), j);

è possibile effettuare la chiamata alla routine che calcola il prodotto tra la matrice A e il
vettore b e lo restituisce nel vettore y.

2.2.3 EIGEN 2
La sintassi tramite la quale di dichiara una matrice è del tipo:

Matrixd name(nrows,ncols);

e anche per la matrice c’è la possibilità di inizializzare a 0 tutti i suoi termini:

name = MatrixXd::Zero(nrows,ncols);

L’istruzione che permette di calcolare il vettore prodotto è banale:

C = A * b;

22
2.2.4 HAND
Il vettore prodotto viene generato tramite due cicli f or

for(int i=0; i<M; i++)


for(int j=0;j<N;j++)
y[i] += A[i][j]*b[j];

2.2.5 MKL
Essendo la sintassi utilizzata estremamente simili a quella della libreria ACMl sopra
esposta, si evita di dilungarsi sulla descrizione della stessa.

2.2.6 MTL4 Dense


E’ interessante notare la particolare sintassi nel dichiarare una matrice

dense2D<type> name(M, N)

Una sintassi molto intuitiva, tra l’altro identica a quella di MATLAB, rende di immediata
stesura la routine che calcola il prodotto tra una matrice e un vettore grazie alle librerie
MTL4.

2.2.7 MTL4 Sparse


Rispetto alla routine precedente in questo caso la matrice di input viene allocata sparsa:

compressed2D<double> A(M, N);


set_to_zero(A);
fill(A, I, J, val, nz);

23
2.2.8 Confronti tra le durate delle analisi
Vengono adesso proposti dei confronti tra le durate delle analisi con le metodologie esposte
nel paragrafo precedente.

Prodotto tra una matrice e un vettore


0.16
acml
blitz
eigen
0.14 hand
mkl
mtl dense
mtl sparse
0.12
Durata dell’analisi [s]

0.1

0.08

0.06

0.04

0.02

0
0 500 1000 1500 2000 2500 3000 3500 4000
Dimensione

Figura 2.3: Confronti tra i tempi dell’analisi del prodotto tra una matrice e un vettore

Prodotto tra una matrice e un vettore


1000
acml
blitz
eigen
hand
mkl
100 mtl dense
mtl sparse

10
GFlops [log]

0.1

0.01
10 100 1000 10000
Dimensione [log]

Figura 2.4: Confronti tra i GFlops dell’analisi del prodotto tra una matrice e un vettore

24
dim ACML BLITZ EIGEN2 HAND MKL MTL4 D MTL4 S
10 0.0 0.0 0.0 0.0 0.0 0.0 0.0
20 0.0 0.0 0.0 0.0 0.0 0.0 0.0
30 0.0 0.0 0.0 0.0 0.0 0.0 0.0
40 0.0 0.0 0.0 0.0 0.0 0.0 0.0
50 0.0 0.0 0.0 0.0 0.0 0.0 0.0
60 0.0 0.0 0.0 0.0 0.0 0.0 0.0
70 0.0 0.0 0.0 0.0 0.0 0.0 0.0
80 0.0 0.0 0.0 0.0 0.0 0.0 0.0
90 0.0 0.0 0.0 0.0 0.0 0.0 0.0
100 0.00002 0.00006 0.00004 0.00004 0.00003 0.00005 0.00001
200 0.00009 0.00022 0.00012 0.00016 0.00012 0.00032 0.00001
300 0.00029 0.00048 0.00026 0.00036 0.00023 0.00079 0.00002
400 0.00056 0.00085 0.0005 0.00066 0.00047 0.00149 0.00002
500 0.00095 0.00138 0.00077 0.0011 0.00073 0.00230 0.00003
600 0.00146 0.0021 0.0013 0.00172 0.00129 0.00336 0.00004
700 0.00212 0.00291 0.00185 0.00242 0.00186 0.00459 0.00005
800 0.00285 0.00383 0.00259 0.0032 0.00259 0.00599 0.00005
900 0.00368 0.00484 0.00322 0.00406 0.00322 0.00759 0.00006
1000 0.0035 0.00598 0.00391 0.00502 0.00391 0.00775 0.00007
1200 0.00508 0.00859 0.00596 0.00723 0.00593 0.00834 0.00008
1500 0.00771 0.01345 0.00892 0.01127 0.00885 0.01309 0.00009
1700 0.00994 0.01727 0.01141 0.01452 0.01168 0.01956 0.00011
2000 0.01381 0.02415 0.01693 0.02017 0.01688 0.03090 0.00013
2200 0.01737 0.0302 0.02034 0.02522 0.02033 0.04018 0.00014
2500 0.02488 0.04015 0.02514 0.0335 0.0255 0.05451 0.00016
2700 0.0308 0.0469 0.02976 0.03911 0.02973 0.06494 0.00018
3000 0.03819 0.05806 0.03758 0.04829 0.0376 0.08150 0.0002
3200 0.04519 0.06616 0.04418 0.05483 0.04356 0.09428 0.00022
3500 0.05416 0.07872 0.05011 0.06556 0.04989 0.11371 0.00023
3700 0.06234 0.08792 0.05528 0.07321 0.05595 0.12720 0.00025
4000 0.07639 0.10297 0.06916 0.08553 0.06832 0.14902 0.00026

Tabella 2.2: Durate delle analisi del prodotto tra una matrice e un vettore

25
2.3 Livello 3: operazioni tra matrici
Questo paragrafo affronterà il problema della prodotto tra due matrici esprimibile nella
forma classica C = A ∗ B utilizzando le metodologie precedentemente elencate. Tutte le
routines ricevono in input un file di testo dal quale ricavano la matrice A; viene assegnato
di default una matrice B posta avere tutti i termini unitari e tramite una o più istruzioni
viene di volta in volta calcolato la matrice prodotto C.

2.3.1 ACML
Questa routine calcola il prodotto tra le matrici A e B e lo restituisce nella matrice C. Le
matrici sono tutte scritte in forma di vettore. La chiamata che effettua l’operazione del
prodotto è
dgemm(transa, transb, int m, int n, int k, double alpha, double *a,
int lda, double *b, int ldb, double beta, double *c, int ldc);
dove transa e transb ci consentono di inserire le matrici A e B trasposte o meno, m e
n sono le dimensioni di A, n e k sono le dimensioni di B, α è uno scalare che moltiplica
A, a, b e c sono le matrici A, B e C scritte in forma di vettore, lda, ldb e ldc sono
rispettivamente il numero di righe delle matrici, β è uno scalare che moltiplica il vettore
c. Questo vettore contiene la soluzione.

2.3.2 BLITZ++
Evitando discussioni sulla dichiarazione della matrici, risulta interessante la funzione che
ne calcola il prodotto:
firstIndex i;
secondIndex j;
thirdIndex k;
y = sum(A(i,j) * B(j,k), j);

2.3.3 EIGEN 2
Anche in questo caso la sintassi per dichiarare la matrice prodotto è molto semplice. E’
interessante notare come non sia necessario dichiarare a priori la dimensione della matrice
prodotto, ma come essa venga gestita dinamicamente, cioè viene allocata run-time e non
compile-time.

2.3.4 HAND
In questo caso sono necessari tre cicli f or:
for (int i=0; i<M; i++)
for (int k=0; k<N; k++)
for (int j=0; j<M; j++)
C[i][j]+= A[i][k] * B[k][j];

26
2.3.5 MKL
Si evita di discutere circa la sintassi poichè banalmente deducibile da quella delle librerie
ACML.

2.3.6 MTL4 Dense


E’ estremamente intuitiva la sintassi che calcola il prodotto tra le due matrici:

C = A * B;

2.3.7 MTL4 Sparse


Come già visto per l’operazione di prodotto tra una matrice e un vettore, qui la matrice
viene allocata come sparsa. Si evita di riportare la chiamata poichè identica a quella
dell’operazione precedente.

27
2.3.8 Confronti tra le durate delle analisi
Vengono adesso proposti dei confronti tra le durate delle analisi con le metodologie esposte
nei paragrafi precedenti.

Prodotto tra due matrici


2
acml
blitz
1.8 eigen
hand
mkl
1.6 mtl dense
mtl sparse
1.4
Durata dell’analisi [s]

1.2

0.8

0.6

0.4

0.2

0
0 100 200 300 400 500 600 700 800 900
Dimensione

Figura 2.5: Confronti tra i tempi dell’analisi del prodotto tra due matrici

Prodotto tra due matrici


100
acml
blitz
eigen
hand
mkl
mtl dense
10 mtl sparse
GFlops [log]

0.1

0.01
10 100 1000
Dimensione [log]

Figura 2.6: Confronti tra i GFlops dell’analisi del prodotto tra due matrici

28
dim ACML BLITZ EIGEN2 HAND MKL MTL4 D MTL4 S
10 0.00002 0.0 0.00001 0.00001 0.00001 0.00001 0.00001
20 0.00003 0.00004 0.00003 0.00004 0.00002 0.00005 0.00002
30 0.00008 0.00018 0.00009 0.0001 0.00007 0.00012 0.00004
40 0.00021 0.00046 0.00019 0.00024 0.00016 0.00025 0.00006
50 0.00039 0.00089 0.00038 0.00044 0.00031 0.00048 0.00010
60 0.00054 0.00152 0.00063 0.00078 0.00054 0.00081 0.00014
70 0.00092 0.00248 0.00103 0.00112 0.00086 0.00141 0.00020
80 0.00135 0.00376 0.00148 0.00148 0.00128 0.00204 0.00024
90 0.00173 0.00528 0.00203 0.00173 0.00184 0.00286 0.00030
100 0.00183 0.00396 0.00286 0.002 0.00153 0.00401 0.00038
200 0.01570 0.03139 0.02139 0.01955 0.01183 0.03209 0.00145
300 0.05672 0.19803 0.07131 0.08275 0.05277 0.10333 0.00331
400 0.13930 0.69930 0.16674 0.21775 0.15169 0.24717 0.00593
500 0.26786 1.65613 0.32238 0.50052 0.30302 0.47375 0.01001
600 0.47248 NaN 0.55876 0.97139 0.52770 0.82997 0.01364
700 0.75553 NaN 0.87678 1.67441 0.82313 1.28785 0.01873
800 1.11161 NaN 1.31649 NaN 1.23904 1.93624 0.02401
900 1.56742 NaN 1.85368 NaN 1.77554 NaN 0.03074

Tabella 2.3: Durate delle analisi del prodotto tra due matrici

29
Capitolo 3

Metodi diretti per la risoluzione di


sistemi lineari

3.1 Introduzione
Questo capitolo tratterà gli aspetti prettamente teorici che riguardano le metodologie
cosiddette dirette per la soluzione dei sistemi lineare [16], [6], [9]. Un sistema di m equazioni
lineari in n incognite è un insieme di relazioni algebriche esprimibile nella forma:
n
X
Aij xj = bi , i = 1, ..., m (3.1)
j=1

dove xj sono le incognite, Aij sono i coefficienti del sistema e bi sono le componenti
del vettore dei termini noti. Il sistema (3.1) può essere espresso in notazione matriciale
compatta:
Ax = b (3.2)
dove sono stati indicati con A = (Aij ) ∈ Cmxn i coefficienti della matrice, con b =
(bi ) ∈ Cm il vettore dei termini noti e con x = (xj ) ∈ Cn il vettore delle incognite. Si
definisce soluzione di (3.2) qualsiasi n-upla di valori di xj che soddisfi (3.1). Questo capitolo
focalizzerà l’attenzione sui sistemi di equazioni reali con matrici di coefficienti quadrate
di ordine n. In questo caso l’esistenza e l’unicità della soluzione di (3.1) è garantita se è
rispettata una delle seguenti condizioni:

• A è invertibile;

• rank(A) = n;

• il sistema omogeneo Ax = 0 ammette solo soluzione nulla.

La soluzione del sistema (3.2) è ottenibile tramite la regola di Cramer :

∆j
xj = , j = 1, ...n, (3.3)
det(A)

30
dove ∆j è il determinante di quella matrice ottenuta sostituendo la j-esima colonna di A
con il vettore b. L’applicazione di questa formula implica un numero elevato di operazioni.
Sono stati allora proposti dei metodi alternativi, detti metodi diretti, che consentono di
ricavare la soluzione in un numero finito di passi.

3.2 Soluzione di sistemi triangolari


Si consideri il sistema triangolare inferiore nonsingolare di dimensione 3x3:
    
L11 0 0 x1 b1
 L21 L22 0   x2  =  b2  (3.4)
L31 L32 L33 x3 b3

Se la matrice non è singolare, è possibile risolvere il sistema nel seguente modo:

x1 = b1 /L11
x2 = (b2 − L21 x1 )/L22 (3.5)
x3 = (b3 − L31 x1 − L32 x2 )/L22

Questo algoritmo può essere esteso ai sistemi n x n ed è detto forward substitution. Nel
caso in esame, cioè di sistema esprimibile nella forma Lx = b, con L matrice triangolare
inferiore non singolare di ordine n (con n ≥ 2), il metodo può cosı̀ essere espresso:
b1
x1 = L11 ,
 P 
xi = 1
Lii bi − i−1
j=1 L ij xj , i = 2, ..., n (3.6)

Si possono trarre conclusioni simili dal sistema lineare Ux = b, con U matrice triango-
lare superiore non singolare di ordine n (con n ≥ 2). In questo caso si utilizza un metodo
detto backward substitution:
bn
xn = Unn ,
 P 
xi = 1
Uii bi − nj=i+1 Uij xj , i = n − 1, ..., 1 (3.7)

31
3.3 Il Metodo di Eliminazione di Gauss (GEM)
Il Metodo di Eliminazione di Gauss (GEM) ha lo scopo di ridurre il sistema Ax=b in
b dove U è una matrice triangolare superiore e b
un altro equivalente Ux = b, b è un vettore
dei termini noti aggiornato. Quest’ultimo sistema può essere risolto tramite backward
substitution.
Una qualsiasi matrice A può essere trasformata in una matrice a gradini per righe
operando una sequenza di operazioni (operazioni elementari sulle righe) di uno dei seguenti
tipi:
• si scambiano tra di loro due righe;

• si moltiplica una riga per uno scalare;

• si somma a una riga un multiplo di un’altra riga.


Ad esempio, sia  
1 −2 −1 3 1
A =  2 −4 1 0 5  (3.8)
1 −2 2 −3 4
Sottraendo alla seconda riga il doppio della prima riga e alla terza riga la prima riga
si ottiene:
 
1 −2 −1 3 1
B= 0 0 3 −6 3  (3.9)
0 0 3 −6 3
Sottraendo alla terza riga la seconda e dividendo per 3 la seconda si ottiene:
 
1 −2 −1 3 1
C= 0 0 1 −2 1  (3.10)
0 0 0 0 0
La matrice C è a gradini per righe, ma non in forma ridotta; per ottenere la forma
ridotta si somma alla prima riga la seconda riga; cosı̀ si ottiene:
 
1 −2 0 1 2
D= 0 0 1 −2 1  (3.11)
0 0 0 0 0
Come è noto, i sistemi lineari
       

 x 1 
  0   x 1   0 

    
 
  
  

 x2    0 
  x2 
    0 

A x3 = 0 , D x3 = 0 , (3.12)

       
 x4 
  
  0 
  x4 
    0 

    
 
  
  

x5 0 x5 0
hanno lo stesso insieme di soluzioni, ma il secondo è molto più facile da risolvere.

32
Definita matrice elementare E una matrice quadrata che si ottiene operando una
singola operazione elementare sulle righe della matrice identità I, è possibile affermare
che eseguire una operazione elementare sulle righe di una matrice A (mxm) equivale a
fare il prodotto EA, dove E è la matrice elementare mxm ottenuta eseguendo la stessa
operazione elementare sulle righe della matrice identità Im .
Si supponga di applicare a una matrice A una sequenza di k operazioni elementari sul-
le righe (come descritto nell’algoritmo di Gauss) per ottenere una matrice U triangolare
superiore; la prima di queste operazioni elementari la si realizza effettuando il prodotto
E1 A, essendo E1 la matrice elementare corrispondente a tale operazione elementare; poi
bisogna effettuare una seconda operazione sulle righe, a cui corrisponde la matrice elemen-
tare E2 e quindi si effettua il prodotto E2 (E1 A). L’algoritmo di Gauss descrive quindi
questa sequenza di passi:

A 7→ E1 A 7→ E2 E1 A 7→ ... 7→ Ek ...E2 E1 A = U; (3.13)

cioè

U = PA (3.14)
dove
P = Ek ...E2 E1 (3.15)
Ciò vuol dire che la matrice P si ottiene operando sulla matrice identità la stessa
sequenza di operazioni elementari utilizzate per passare dalla matrice A alla matrice U.

33
3.4 La fattorizzazione LU
Nel paragrafo precedente è stato visto come una matrice A possa essere trasformata me-
diante operazioni elementari sulle righe in una matrice U triangolare superiore. Se non si
pretende di avere U nella forma ridotta, allora la riduzione si può operare aggiungendo
a righe successive multipli delle righe precedenti, ed eventualmente scambiando righe, ma
mai aggiungendo a una riga precedente multipli di una riga successiva; in questo modo le
matrici Ei o corrispondono a scambi di righe oppure sono triangolari inferiori. La matrice
A viene detta inferiormente ridotta se può essere ridotta nella forma a gradini senza utiliz-
zare scambi di righe. In tal caso la matrice P = Ek ...E2 E1 è prodotto di matrici triangolari
inferiori e quindi è a sua volta triangolare inferiore; anche L = P−1 è triangolare inferiore
e la matrice A si può scrivere nella forma:

A = LU (3.16)

dove L è una matrice triangolare inferiore invertibile e U è una matrice triangolare


superiore; una fattorizzazione di questo tipo si chiama LU-fattorizzazione.

Esempio
Trovare la fattorizzazione LU per la seguente matrice
 
0 2 −6 −2 4
A =  0 −1 3 3 2  (3.17)
0 −1 3 7 10

Soluzione
 
0 2 −6 −2 4
A =  0 −1 3 3 2  (3.18)
0 −1 3 7 10
 
0 1 −3 −1 2
7→  0 0 0 2 4  (3.19)
0 0 0 12 6
 
0 1 −3 −1 2
7→  0 0 0 1 2 =U (3.20)
0 0 0 0 0
Risulta A = LU, dove
 
2 0 0
L =  −1 2 0  (3.21)
−1 6 1
è la matrice ottenuta dalla matrice identità sostituendo nei posti corrispondenti le
componenti marcate nel processo di riduzione.

34
La fattorizzazione LU risulta essere molto utile. Si supponga infatti di dover risolvere
il sistema Ax = b. Se A = LU, allora

LUx = b (3.22)

Si cerca dapprima y tale che Ly = b; trovato y, si cerca x tale che Ux = y. Entrambe le


operazioni sono semplici perchè si usano le matrici L e U che sono entrambe triangolari.

35
3.5 La fattorizzazione di Cholesky
La decomposizione di Cholesky è la fattorizzazione di una matrice hermitiana e definita
positiva in una matrice triangolare inferiore e nella sua trasposta coniugata. Essa si può
considerare come un caso speciale della più generale decomposizione LU.
Sia A una matrice quadrata, hermitiana e definita positiva ; tale A può essere decom-
posta come
A = LL+ (A ∈ Km×m ) (3.23)
con L matrice triangolare inferiore con elementi diagonali positivi e L+ la matrice coniu-
gata trasposta di L.
Se la matriceA è reale e simmetrica, la coniugata trasposta di L coincide con la
trasposta e la decomposizione si semplifica

A = LLT (A ∈ Rn×n ) (3.24)

L’algoritmo di Cholesky , usato per calcolare la matrice di decomposizione L, è una


versione modificata dell’algoritmo di Gauss.
L’algoritmo ricorsivo inizia con il considerare

A(1) = A (3.25)
 
(i) Ai,i b∗i
A = (3.26)
bi B(i)
!
√1 0
Ai,i
Li = (3.27)
− A1i,i bi I
!
1 0
A(i) = L−1 (L−1
i )

(3.28)
i 0 B(i) − A1i,i bi b∗i

Si definisce per i successivi step:


1
A(i+1) = B(i) − bi b∗i (3.29)
Ai,i

in modo che  
(i) 1 0
A = L−1
i (i+1) (L−1
i )

(3.30)
0 A
Il ciclo termina dopo n passi dove A(n) = 1. Si nota che la matrice triangolare inferiore
L è calcolata come
L = L1 L2 . . . Ln (3.31)
L’algoritmo di Cholesky-Crout fornisce un procedimento per calcolare i termini della
matrice triangolare inferiore L. Esso inizia formando l’angolo superiore sinistro della
matrice L e procede a calcolare la matrice colonna per colonna.

36
v
u i−1
u X
Li,i = tAi,i − L2i,k . peri = 1, ..., m (3.32)
k=1

i−1
!
1 X
Lj,i = Aj,i − Lj,ι Li,ι perj = i + 1, ..., m (3.33)
Li,i ι=1

37
Capitolo 4

Metodi diretti per la risoluzione di


sistemi lineari - Applicazioni

4.1 Introduzione
In precedenza è stato affermato che i problemi della meccanica delle strutture possono
essere affidati a un calcolatore che si occupa della soluzione di un sistema di equazioni.
Questo capitolo si occuperà del problema dei solutori algebrici, cioè verranno implementate
delle routines allo scopo di risolvere un sistema di equazioni esprimibile nella classica forma
A ∗ x = b, dove x è il vettore delle incognite, b è il vettore dei termini noti e A è la matrice
dei coefficienti.
Sono stati implementati dei solutori che operano su matrici generiche:
• solutore genericoACML,

• solutore genericoMKL.
e altri che lavorano con matrici simmetriche:
• solutore triangolare ACML,

• solutore triangolare MKL,

• solutore triangolare MTL,


Una classica situazione che può essere riscontrata in meccanica delle strutture è quella
di lavorare con matrici di coefficienti che presentano una struttura prettamente bandata;
pertanto sono stati implementati dei solutori ad hoc per problemi bandati:
• solutore ACML,

• solutore MKL,

• solutore BANDEC/BANDSOL.
Infine sono stati implementati dei solutori per matrici sparse:

38
• solutore CHOLMOD SuiteSparse [7],

• solutore UMFPACK SuiteSparse [8],

• solutore SPOOLES [3],

• solutore PARDISO MKL [2].

4.2 Stima delle performance dei solutori


I risultati della prestazionalità dei solutori (benchmark ) sono generati eseguendo un certo
solutore su un set di problemi T e registrando delle informazioni di interesse [10]. Sia S il
set di solutori dei quali si vuole fare una stima delle performance. Si supponga che un certo
solutore i ∈ S riporti un certo valore sij ≥ 0 quando eseguito sull’esempio j dell’insieme di
test T e che minore è questo valore migliore è considerato il solutore. Per tutti i problemi
j ∈ T, si vuole paragonare la performance del solutore i con la performance del migliore
solutore dell’intero set S.
Per j ∈ T, sia sˆj = min{sij ; i ∈ S}. Allora per α ≥ 1 e per ogni i ∈ S si può definire

1 se sij ≤ αsˆj
k(sij , sˆj , α) = (4.1)
0 altrimenti
Il performance profile del solutore i è dato dalla funzione:
P
j∈T k(sij , sˆj , α)
pi (α) = , α≥1 (4.2)
|T|

Allora pi (1) fornisce la frazione di esempi per la quale il solutore i è più efficace (in
base al parametro sij , pi (2) fornisce la frazione per la quale esso è all’interno di un fattore
2 del migliore. Il parametro utilizzato per costruire i benchmark è la cpu time impiegata
per svolgere il processo di fattorizzazione.

39
4.3 Solutori per matrici generiche
Vengono di seguito esposte le routines necessarie per effettuare la risoluzione di un sistema
lineare quando la matrice dei coefficienti si presenta in forma generica.

4.3.1 ACML
La funzione che opera la fattorizzazione è:

dgetrf(M, N, A, LDA, IPIV, INFO )

dove M e N sono rispettivamente il numero di righe e di colonne della matrice A, LDA è


la dimensione principale di A, IP IV è un vettore che contiene gli indici pivot, IN F O è
un parametro indicatore della riuscita della fattorizzazione.
La funzione che opera la soluzione è

dgetrs( TRANS, N, NRHS, A, LDA, IPIV, B, LDB, INFO )

dove T RAN S indica la forma del sistema di equazioni, N è la dimensione della matrice,
N RHS il numero di colonne del vettore dei termini noti, A è la matrice dei coefficienti,
LDA ne è la dimensione principale, B è il vettore dei termini noti, LDB è la lunghezza
dello stesso, IN F O è un parametro indicatore della riuscita della soluzione.

4.3.2 MKL
Non si ci sofferma su questa routine poichè estremamente simile a quella precedentemente
esposta.

40
4.4 Solutori per matrici simmetriche
Vengono adesso presentati i solutori implementati per risolvere sistemi di equazioni quando
la matrice si presenta in forma simmetrica. In questo caso è necessario fornire in input
solo la parte triangolare inferiore della matrice stessa.

4.4.1 ACML
La funzione che opera la fattorizzazione della matrice simmetrica è

dsytrf( UPLO, N, A, LDA, IPIV, INFO )

dove uplo = ′ U ′ se la matrice è triangolare superiore o uplo = ′ L′ se triangolare inferiore,


N è la dimensione della matrice A, LDA è la sua dimensione principale, IP IV è il vettore
che contiene gli indici pivot, IN F O è un parametro indicatore della riuscita del processo
di fattorizzazione.
La funzione che risolve il sistema è

dsytrs( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO )

dove N RHS è il numero di colonne del vettore dei termini noti e LDB è la lunghezza
dello stesso.

4.4.2 MKL
La fattorizzazione avviene tramite la routine

dsytrf( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO )

dove W ORK è un vettore di dimensione LW ORK che rappresenta il workspace; se


LW ORK = −1, la dimensione di W ORK viene determinata dalla routine stessa.
La funzione che opera la soluzione è

dsytrs( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO )

indentica a quella esposta nel caso precedente.

4.4.3 MTL 4
Fornita in input la matrice simmetrica A, ne viene isolato il triangolo inferiore tramite la
funzione

AA = lower(A);

e viene poi ricavato il vettore soluzione

x = lower_trisolve(AA,b);

41
4.5 Solutori per matrici bandate
In questo paragrafo vengono esposti i solutori per matrici bandate che sono stati imple-
mentati.

4.5.1 ACML
La chiamata alla procedura che fattorizza è

dpbtrf( UPLO, N, KD, AB, LDAB, INFO )

dove uplo = ′ U ′ se la matrice è triangolare superiore o uplo = ′ L′ se triangolare inferiore,


n è l’ordine della matrice A, kd è il numero di sopradiagonali all’interno della banda
(esclusa la diagonale principale) se uplo = ′ U ′ , o il numero di sottodiagonali all’interno
della banda (esclusa la diagonale principale) se uplo = ′ L′ , ab è il vettore all’interno del
quale viene memorizzata la banda di dimensioni (ldab, n), ldab è la larghezza della banda
pari a kd + 1, inf o è un indicatore dell’esito della fattorizzazione. Se si indica con A la
matrice di input e con AB la matrice che entra nella routine di fattorizzazione, la j-esima
colonna di A in input viene memorizzata nella j-esima colonna di AB secondo la legge:
AB(i-j,j) = A(i,j) for j ≤ i ≤ min(n, j + kd).
La procedura che risolve è:

dpbtrs( UPLO, N, KD, NRHS, AB, LDAB, B, LDB, INFO )

dove nrhs è il numero di colonne di b, b è il vettore dei termini noti, ldb è il numero
delle righe di b, inf o è un indicatore dell’esito del processo di soluzione.

4.5.2 MKL
La sintassi delle chiamate per fattorizzare e risolvere è uguale a quella del caso precedente.

4.5.3 BANDEC/BANDSOL
//---------------------------------------------------------------------------------------------------
int ldlfact(int neq, int band, double** e)
{
int j, k, n, m, i;
short nlab=0;
double Zero = 1e-15;
for (n=0; n<neq; n++)
{
if ( fabs(e[0][n])> Zero )
for (m=1; (m<band)&&((i=n+m)<neq); m++)
{
double d = e[m][n]/e[0][n];
for (j=0, k=m; k<band; k++, j++)
e[j][i]-=d*e[k][n];
e[m][n]=d;
}
else nlab++;

42
}
return nlab;
}
//---------------------------------------------------------------------------------------------------
double ldlsolve(int neq, int band, double** e, double* v)
{
int n, m;
double d, dot=0.0;
double Zero= 1e-15;
for (n=0; n<neq; n++)
if ( fabs(e[0][n])>Zero)
for (m=1; (m<band)&&(n+m<neq); m++)
v[n+m] -= e[m][n]*v[n];

for (n=neq; n--;)


if ( fabs(e[0][n])>Zero )
{
d = v[n];
v[n] /= e[0][n];
dot += d*v[n];
for (m=1; (m<band)&&(n+m<neq); m++)
v[n] -= e[m][n]*v[n+m];
}
return dot;
}

43
4.5.4 Confronti tra le durate delle analisi
I solutori sopra esposti sono stati messi a paragone confrontando le durate delle analisi.
Sono state fornite in input delle matrici tutte caratterizzate dall’essere reali, simmetriche,
bandate e definite positive [4].

Figura 4.1: bcsstk14 Figura 4.2: bcsstk15 Figura 4.3: bcsstk16

Figura 4.4: bcsstk17 Figura 4.5: bcsstk18 Figura 4.6: bcsstk24

I risultati sono riportati di seguito.


Per meglio capire il significato dei risultati esposti sopra, le durate delle analisi vengono
adesso normalizzate rispetto a quella della libreria più veloce (MKL) ed espressi come
multiplo di questa.

44
file Dimensione Nonzeri Banda Banda/Dim [%]
bcsstk14 1806 63454 162 8.97
bcsstk15 3948 117816 438 11.09
bcsstk16 4884 290378 141 2.89
bcsstk17 10974 428650 522 4.76
bcsstk18 11948 149090 1244 10.41
bcsstk24 3562 159910 3334 93.60

Tabella 4.1: Matrici analizzate tramite i solutori bandati

file Acml Mkl BandDec


bcsstk14 0.05 0.03 0.32
bcsstk15 0.56 0.29 7.58
bcsstk16 0.10 0.06 0.74
bcsstk17 2.25 1.20 39.06
bcsstk18 12.24 5.28 261.41
bcsstk24 12.10 4.05 240.33

Tabella 4.2: Durate delle analisi dei solutori bandati

file Acml BandDec


bcsstk14 1.67 10.67
bcsstk15 1.93 26.14
bcsstk16 1.67 12.33
bcsstk17 1.87 32.55
bcsstk18 2.32 49.51
bcsstk24 2.99 59.34

Tabella 4.3: Durate delle analisi dei solutori bandati normalizzati rispetto a quella del
solutore MKL

45
Benchmark solutori bandati
1

0.9

0.8

0.7

0.6
Performance profile

0.5

0.4

0.3

acml
0.2
mkl
bandec

0.1

0
0 2 4 6 8 10 12 14
α

Figura 4.7: Benchmark solutori bandati

46
4.6 Solutori per matrici sparse
In questo paragrafo vengono presentati dei solutori da utilizzare quando la matrice dei
coefficienti si presenta in forma sparsa. La matrice in input è contenuta in un file di
testo dove sulla prima riga compaiono il numero di righe, quello di colonne e il numero di
nonzeri nella sola parte triangolare nel caso di CHOLMOD, PARDISO e SPOOLES, di
tutta la matrice nel caso di UMFPACK; ciò è dovuto al fatto che l’ultimo è un solutore per
matrici non simmetriche. Dalla seconda riga in poi compaiono tre colonne che contengono
rispettivamente l’indice di riga, quello di colonna e il valore degli elementi nonzeri. Questa
forma di scrivere una matrice si chiama triplet form.

4.6.1 CHOLMOD SuiteSparse


Questo solutore usa una fattorizzazione alla Cholesky della matrice dei coefficienti.
Essendo la matrice in ingresso scritta in triplet form, viene salvata dapprima come tale

cholmod_triplet *T;
T = cholmod_read_triplet(f,&c);

Poi essa viena convertita in una sparsa

cholmod_sparse *A ;
A = cholmod_triplet_to_sparse(T, T->nzmax, &c);

La matrice A subisce prima un processo di fattorizzazione simbolica

L = cholmod_analyze (A, &c) ;

e poi uno di fattorizzazione numerica

cholmod_factorize (A, L, &c) ;

Viene effettuata la soluzione del sistema

x = cholmod_solve (CHOLMOD_A, L, b, &c);

e viene calcolato il residuo r = b-Ax

r = cholmod_copy_dense (b, &c) ;


cholmod_sdmult (A, 0, m1, one, x, r, &c) ;
printf ("norm(b-Ax) %8.1e\n",
cholmod_norm_dense (r, 0, &c)) ;

che se prossimo ad essere nullo è un indicatore della riuscita dell’analisi.

47
4.6.2 UMFPACK SuiteSparse
La matrice in input in triplet form viene convertita in column compressed form

umfpack_di_triplet_to_col(M, N, nz, I, J, val, Ap, Ai, Ax, NULL);

La matrice è cosı̀ rappresentata da tre vettori:

int *Ai = new int[nz];


double *Ax = new double[nz];
int *Ap = new int[M+1];

dove M è l’ordine della matrice e nz il numero di nonzeri dell’intera matrice (si presti
attenzione al fatto che bisogna passare tutta la matrice alla routine e non solo un triangolo
anche qualora essa dovesse essere simmetrica!). Gli indici di riga degli elementi della
colonna j sono memorizzati in Ai[Ap[j]...Ap[j + 1] − 1]. Il corrispondente valore numerico
è memorizzato in Ax[Ap[j]...Ap[j + 1] − 1]. Non sono presenti indici duplicati ed essi sono
forniti per ogni colonna in ordine ascendente. Il termine Ap[0] deve essere nullo. Il numero
complessivo di nonzeri della matrice è nz = Ap[n].
La matrice subisce quindi un processo di fattorizzazione simbolica

umfpack_di_symbolic(n, n, Ap, Ai, Ax, &Symbolic, NULL, NULL);

e poi uno di fattorizzazione numerica

umfpack_di_numeric(Ap, Ai, Ax, Symbolic, &Numeric, NULL, NULL);

Quindi viene effettuata la soluzione del sistema

umfpack_di_solve(UMFPACK_A, Ap, Ai, Ax, x, b, Numeric, NULL, NULL);

48
4.6.3 SPOOLES
La matrice dei coefficienti viene costruita tramite alcune istruzioni:

InpMtx *mtxA;
InpMtx_init(mtxA, INPMTX_BY_ROWS, type, nent, neqns);
InpMtx_inputRealEntry(mtxA, I[i], J[i], val[i]);
InpMtx_changeStorageMode(mtxA, INPMTX_BY_VECTORS);

Subisce un processo di fattorizzazione simbolica

frontmtx = FrontMtx_new();
mtxmanager = SubMtxManager_new();
SubMtxManager_init(mtxmanager, NO_LOCK, 0);
FrontMtx_init(frontmtx, frontETree, symbfacIVL, type,
symmetryflag, FRONTMTX_DENSE_FRONTS, pivotingflag, NO_LOCK,
0, NULL, mtxmanager, msg1v1, msgFile);

e uno di fattorizzazione numerica

rootchv = FrontMtx_factorInpMtx(frontmtx, mtxA, tau, droptol,


chvmanager, &error, cpus, stats, msg1v1, msgFile);

Quindi, inizializzato unitario il vettore dei termini noti

mtxY = DenseMtx_new();
DenseMtx_init(mtxY, type, 0, 0, neqns, 1, 1, neqns);
DenseMtx_zero(mtxY);
for(int irow = 0; irow<M; irow++)
DenseMtx_setRealEntry(mtxY, irow, 0, 1.0);

la soluzione viene memorizzata nel vettore soluzione precedentemente inizializzato a


zero

mtxX = DenseMtx_new();
DenseMtx_init(mtxX, type, 0, 0, neqns, nrhs, 1, neqns);
DenseMtx_zero(mtxX);
FrontMtx_solve(frontmtx, mtxX, mtxY, mtxmanager, cpus, msg1v1, msgFile);

49
4.6.4 PARDISO MKL
La matrice di input in triplet form viene convertita in columns compressed form

int *JJ = new int[nz+1];


JJ[nz+1] = M+1;
int *ia = new int[M+1];
int cont = 1;
int k = 1;
ia[0] = cont;
for(int i=0; i<nz; i++)
{
cont++;
ia[k] = cont;
if(JJ[i] != JJ[i+1])
k++;
}

La matrice dei coefficienti subisce dapprima una fattorizzazione simbolica

phase = 11;
PARDISO (pt, &maxfct, &mnum, &mtype, &phase,
&M, val, ia, I, &idum, &nrhs,
iparm, &msglvl, &ddum, &ddum, &error);

e poi una fattorizzazione numerica

phase = 22;
PARDISO (pt, &maxfct, &mnum, &mtype, &phase,
&M, val, ia, I, &idum, &nrhs,
iparm, &msglvl, &ddum, &ddum, &error);

Inizializzato unitario il vettore dei termini noti, avviene infine la soluzione del sistema

phase = 33;
iparm[7] = 2; /* Max numbers of iterative refinement steps. */
/* Set right hand side to one. */
for (int i = 0; i<M; i++)
b[i] = 1.0;
PARDISO (pt, &maxfct, &mnum, &mtype, &phase,
&M, val, ia, I, &idum, &nrhs,
iparm, &msglvl, b, x, &error);

50
4.6.5 Confronti tra le analisi
I solutori sopra esposti sono stati messi a paragone confrontando le durate delle analisi.
Sono state fornite in input delle matrici tutte caratterizzate dall’essere reali, simmetriche
e definite positive [4].

Figura 4.8: 494bus Figura 4.9: 662bus Figura 4.10: 685bus

Figura 4.11: 1138bus Figura 4.12: Dubcova2 Figura 4.13: bcsstk03

Figura 4.14: bcsstk04 Figura 4.15: bcsstk05 Figura 4.16: bcsstk06

51
Figura 4.17: bcsstk07 Figura 4.18: bcsstk08 Figura 4.19: bcsstk09

Figura 4.20: bcsstk10 Figura 4.21: bcsstk11 Figura 4.22: bcsstk13

Figura 4.23: bcsstk14 Figura 4.24: bcsstk15 Figura 4.25: bcsstk16

Figura 4.26: bcsstk17 Figura 4.27: bcsstk18 Figura 4.28: bcsstk25

52
Figura 4.29: bcsstm05 Figura 4.30: bcsstm06 Figura 4.31: bcsstm08

Figura 4.32: bcsstm09 Figura 4.33: bcsstm10 Figura 4.34: bundle1

Figura 4.35: ct20stif Figura 4.36: cvxbqp1 Figura 4.37: finan512

Figura 4.38: gyrok Figura 4.39: minsurfo Figura 4.40: nos5

53
Figura 4.41: pwtk Figura 4.42: s1rmq4m1 Figura 4.43: s1rmt3m1

Figura 4.44: s2rmq4m1 Figura 4.45: s3rmt3m1 Figura 4.46: s3rmq4m1

Figura 4.47: s3rmt3m1 Figura 4.48: s3rmt3m3 Figura 4.49: smt

Figura 4.50: t3dle Figura 4.51: wathen100

54
file Dimensione Nonzeri
1138bus 1138 4054
494bus 494 1666
662bus 662 2474
685bus 685 3249
Dubcova2 65025 1030225
bcsstk03 112 376
bcsstk04 132 1890
bcsstk05 153 1288
bcsstk06 420 4140
bcsstk07 420 4140
bcsstk08 1074 12960
bcsstk09 1083 18437
bcsstk10 1086 22070
bcsstk11 1473 34241
bcsstk13 2003 83883
bcsstk14 1806 63454
bcsstk15 3948 117816
bcsstk16 4884 290378
bcsstk17 10974 219812
bcsstk18 11948 149090
bcsstk25 15439 252241
bcsstm05 153 153
bcsstm06 420 420
bcsstm07 420 3836
bcsstm09 1083 1083
bcsstm10 1086 11589
bundle1 10581 770811
ct20stif 52329 2600295
cvxbqp1 50000 349968
finan512 74752 596992
gyrok 17361 1021159
minsurfo 40806 203622
nos5 468 5172
pwtk 217918 11524432
s1rmq4m1 5489 143300
s1rmt3m1 5489 21765
s2rmq4m1 5489 263351
s2rmt3m1 5489 217681
s3rmq4m1 5489 143300
s3rmt3m1 5489 112505
s3rmt3m3 5357 207123
smt 25710 3749582
t3dle 20360 20360
wathen100 55
30401 471601

Tabella 4.4: Matrici analizzate tramite i solutori sparsi


I risultati sono riportati nella tabella 4.5, mentre la figura (4.52) mostra le performance
dei solutori sparsi.

Benchmark solutori sparsi


1

0.9

0.8

0.7

0.6
Performance profile

0.5

0.4

0.3

0.2

cholmod
0.1
pardiso
spooles
umfpack
0
0 2 4 6 8 10 12 14 16
α

Figura 4.52: Benchmark solutori sparsi

56
file Cholmod Pardiso Spooles UMF
1138bus 0.0 0.0 0.0 0.0
494bus 0.00 0.00 0.0 0.0
662bus 0.00 0.00 0.0 0.0
685bus 0.00 0.00 0.0 0.0
Dubcova2 0.71 0.39 1.04 1.73
bcsstk03 0.0 0.0 0.0 0.01
bcsstk04 0.0 0.0 0.0 0.01
bcsstk05 0.0 0.0 0.0 0.01
bcsstk06 0.0 0.0 0.0 0.01
bcsstk07 0.0 0.0 0.0 0.01
bcsstk08 0.0 0.0 0.01 0.02
bcsstk09 0.01 0.0 0.01 0.03
bcsstk10 0.01 0.0 0.0 0.02
bcsstk11 0.01 0.0 0.0 0.02
bcsstk13 0.07 0.04 0.07 0.14
bcsstk14 0.02 0.01 0.02 0.06
bcsstk15 0.13 0.08 0.19 0.29
bcsstk16 0.17 0.11 0.16 0.37
bcsstk17 0.20 0.16 0.21 0.49
bcsstk18 0.16 0.10 0.20 0.39
bcsstk24 0.05 0.05 0.06 0.14
bcsstk25 0.30 0.27 0.43 0.69
bcsstm05 0.0 0.0 0.0 0.0
bcsstm06 0.0 0.0 0.0 0.0
bcsstm07 0.0 0.0 0.0 0.0
bcsstm09 0.0 0.0 0.0 0.0
bcsstm10 0.0 0.0 0.0 0.01
bundle1 0.20 0.07 0.47 3.44
ct20stif 3.17 2.37 7.63 7.03
cvxbqp1 1.62 0.43 3.92 3.46
finan512 0.77 0.35 NaN 2.13
gyrok 0.27 0.22 0.18 0.72
minsurfo 0.25 0.17 0.37 0.61
nos5 0.0 0.0 0.01 0.01
pwtk 16.84 10.81 53.99 40.76
s1rmq4m1 0.11 0.08 0.12 0.38
s1rmt3m1 0.09 0.09 0.09 0.22
s2rmq4m1 0.12 0.11 0.12 0.43
s2rmt3m1 0.09 0.09 0.09 0.23
s3rmq4m1 0.12 0.11 0.12 0.39
s3rmt3m1 0.10 0.09 0.09 0.28
s3rmt3m3 0.08 0.07 0.06 0.25
smt 4.87 3.46 17.24 11.27
t3dle 0.0 57 0.0 0.08 0.01
wathen100 0.30 0.22 0.41 0.70

Tabella 4.5: Durate dell’analisi per solutori con matrici sparse


Le matrici analizzate tramite i solutori bandati vengono anche utilizzate dai solutori
sparsi. I risultati sono riportati in tabella 4.6 e in figura (4.53).

file Acml Mkl BandDec Cholmod Pardiso Spooles UMF


bcsstk14 0.05 0.03 0.32 0.02 0.01 0.02 0.06
bcsstk15 0.56 0.29 7.58 0.13 0.08 0.19 0.29
bcsstk16 0.10 0.06 0.74 0.17 0.11 0.16 0.37
bcsstk17 2.25 1.20 39.06 0.20 0.16 0.21 0.49
bcsstk18 12.24 5.28 261.41 0.16 0.10 0.20 0.39
bcsstk24 12.10 4.05 240.33 0.05 0.05 0.06 0.14

Tabella 4.6: Durate delle analisi dei solutori bandati e dei solutori sparsi

Benchmark solutori bandati e sparsi


1

0.9

0.8

0.7

0.6
Performance profile

0.5

0.4

0.3

0.2 acml
mkl
bandec
cholmod
0.1 pardiso
spooles
umfpack

0
0 2 4 6 8 10 12
α

Figura 4.53: Benchmark solutori bandati e sparsi

58
4.7 L’impatto del calcolo high performance sulla risoluzione
dei sistemi lineari
Come già più volte affermato, ciò che incide di più sulle prestazioni di un’applicazione che
gira sui moderni microprocessori è la lentezza nell’accesso ai dati. Molte macchine usano
una cache che trasferisce i dati alla cpu molto velocemente, ma il carico di dati che può
immagazzinare solitamente è molto ridotto. Ciò significa che se si vogliono ottenere elevate
prestazioni è necessario riutilizzare i dati nella cache il più possibile per ammortizzare il
costo del loro trasferimento dalla memoria principale. I più adatti e più largamente usato
strumenti per fare ciò sono le routines Level 3 BLAS O(n3) coinvolgono le matrici di
ordine n [11]. Le due routines più importanti usate nella fattorizzazione LU sono la
routine che opera il prodotto tra due matrici dgemm e quella che opera la soluzione di
un sistema traingolare DTRSM. Se intendiamo la fattorizzazione LU in una maniera a
blocchi o partizionata, diventa relativamente semplice mostrare come le Level 3 BLAS
possano essere utilizzate. La figura (4.54) mostra una schematizzazzione a blocchi della
fattorizzazione LU. Le operazioni di fattorizzazione sono svolte nelle regioni tratteggiate
ed è richiesto l’accesso alle regioni ombreggiate.

Figura 4.54: Varianti a blocchi della decomposizione LU

Quando si fattorizza una matrice sparsa, è di fondamentale importanza che le matrici


di permutazione siano scelte allo scopo di mantenere la sparsità. Nel caso generale di
matrice non simmetrica ciò porta a scegliere i pivot allo scopo di limitare il riempimento
della matrice (fill-in). Una strategia comune, dovuta a Markovitz, consiste nello scegliere
gli elementi in maniera tale che il prodotto del numero degli altri elementi nella riga e nella
colonna del pivot sia minimizzato. Nel caso di matrice simmetrica l’analogo di Markovitz è
il minimum degree, dove si seglie come pivot quell’elemento diagonale con il minor numero
di elementi nella sua riga.
Un concetto fondamentale nella fattorizzazione delle matrici sparse è l’eliminazione ad
albero. Essa è definita per qualunque matrice sparsa simmetrica. La fattorizzazione alla
Cholesky di una matrice sparsa per colonne può essere rappresentata come un eliminazio-
ne ad albero. Questa può essere un algoritmo left-looking, dove gli aggiornamenti sono
effettuati u ogni colonna a turno da tutte le colonne precedenti che vi contribuiscono; poi
il pivot è scelto in quella colonna. L’applicazione delle Level 3 BLAS nei solutori sparsi è
una generalizzazione della fattorizzazione sparsa per colonne. Le Level 3 BLAS possono
essere utilizzate se le colonne con un grado si sparsità comune sono considerate insieme in
un singolo blocco o supernodo.

59
Capitolo 5

Approssimazione di autovalori e
autovettori

5.1 Introduzione
Lo scopo di questo capitolo è quello di descrivere le varie tecniche utilizzabili per il calcolo
di autovalori e autovettori [16]. Prima di passare agli algoritmi, però, si ritiene necessario
introdurre delle considerazioni preliminari sulla risoluzione di tali problematiche.
Il più semplice è il cosiddetto problema standard agli autovalori esprimibile in forma:

KΦ = λΦ (5.1)

dove K è una matrice quadrata di dimensione n × n, ad esempio la matrice di rigidezza


del problema discretizzato attraverso tecniche FEM. Ci sono n autovalori e i corrispondenti
autovettori che soddisfano la (5.1). L’i-esima autocoppia è indicata con (λi , φi ) mentre gli
autovalori si soppone siano ordinati in base alla loro intensità nel modo seguente:

0 ≤ λ1 ≤ λ2 · · · ≤ λn−1 ≤ λn (5.2)

La soluzione per p autocoppie può essere scritta come:

KΦ = ΦΛ (5.3)

dove Φ è una matrice n × p le cui colonne sono gli autovettori del problema, mentre Λ è
una matrice p × p i cui termini diagonali sono gli autovalori. Se K è definita positiva allora
λi > 0, mentre se è semidefinita positiva λi ≥ 0 e il numero di autovalori nulli corrisponde
ai moti rigidi.
Il problema (5.1) viene risolto ad esempio quando bisogna valutare la matrice di
rigidezza di un elemento.
Un secondo tipo di problema, che si incontra ad esempio nelle applicazioni, è il cosid-
detto problema generalizzato agli autovalori:

KΦ = λMΦ (5.4)

60
dove K e M sono delle matrice di dimensioni n × n. Applicazioni del problema 5.4 sono
ad esempio legate alla dinamica quando si studia il problema con le tecniche dell’analisi
modale. In questo caso le due matrici sono rispettivamente le matrice delle rigidizze e
delle masse del problema. In tal caso gli autovalori λi e i corrispondenti autovettori φi
sono rispettivamente i quadrati delle pulsazioni ωi 2 e le forme modali.
In analogia a quanto fatto nella (5.3) è possibile riscrivere il problema come:

KΦ = MΦΛ (5.5)

dove le colonne di Φ sono gli autovettori, mentre la matrice diagonale Λ contiene gli
autovalori.
Il problema generalizzato (5.4) sarebbe riconducibile a quello standard (5.1) qualora la
matrice M fosse unitaria. In analogia alla soluzione di (5.1), gli autovalori di(5.4) λi > 0,
dove gli autovalori nulli corrispondono ai moti rigidi del sistema.
Un secondo tipo di problema generalizzato si incontra nell’analisi a buckling:

Kt φ = λKt−∆t φ (5.6)

dove Kt−∆t e Kt sono le matrici di rigidezza corrispondenti agli istanti (cioè ai livelli di
carico) t − ∆t e t.
Nel capitolo dedicato alla soluzione di sistemi lineari è stata già messa in luce l’im-
portanza di usare procedure di calcolo efficaci. Ciò viene avvertito in maniera ancor più
significativa nel caso dei problemi agli autovalori poichè la risoluzione di tale problema
comporta un onere computazionale superiore a quello della risoluzione dei sistemi di equa-
zioni. Sono presenti in letteratura molte metodologie per la risoluzione dei problemi agli
autovalori, ma in meccanica delle strutture l’attenzione si focalizza su quei metodi che
sfruttano le particolari proprietà delle matrici (simmetriche, definite positive, bandate e
cosı̀ via). Alcuni algoritmi traggono vantaggio da queste proprietà allo scopo di ridurre il
costo computazionale dell’operazione.

5.2 Concetti fondamentali alla base della risoluzione dei pro-


blemi agli autovalori
Prima di discutere delle procedure di risoluzione dei problemi agli autovalori è necessario
comprendere come alcune proprietà abbiano una certa influenza sulle proprietà degli auto-
valori e degli autovettori. Quando si passeranno in rassegna le procedure, si noterà come
queste tendano ad appoggiarsi a tali proprietà per trarne un vantaggio computazionale.
Sarà considerato il problema generalizzato KΦ = λMΦ, che è possibile ricondurre a uno
standard se M = I, e le considerazioni fatte per questo saranno estendibili anche agli altri
problemi.

5.2.1 Proprietà degli autovettori


In precedenza è stato affermato che la risoluzione di KΦ = λMΦ comporta la deter-
minazione di n autovalori λ1 , . . . , λn e dei corrispondenti autovettori φ1 , . . . , φn . Ogni

61
autocoppia (λi , φi ) soddisfa la (5.4) e si ha:

Kφi = λi Mφi (5.7)

Questa equazione dice che se si individua un vettore λi Mφi e lo si usa come vettore
dei carichi R nell’equazione KU = R, allora U = φi . Ciò suggerisce di usare gli algoritmi
mostrati nel capitolo sulla risoluzione dei sistemi lineari.
L’equazione (5.7) mostra anche che un autovettore è definito come multiplo di se stesso,
cioè:
K(αi φi ) = λi M(αi φi ) (5.8)
dove αi è una costante non nulla. L’autovettore che ci interessa è quello che soddisfa la
proprietà di ortonormalità rispetto a M:

φTi Mφi = 1 (5.9)

dalla quale deriva la proprietà di ortogonalità rispetto a K:

φTi Kφi = λi δij (5.10)

La soluzione della (5.4) per p autovalori desiderati è stabilita nella (5.5). Le relazioni
(5.9) e (5.10) permettono di scrivere:

ΦT KΦ = Λ (5.11)

ΦT M Φ = I (5.12)
E’ molto importante notare che le (5.11) e (5.12) sono condizioni che gli autovettori
devono soddisfare, ma se l’ortonormaltà rispetto a M e l’ortogonalità rispetto a K sono
soddisfatte i p vettori non sono necessariamente autovettori a meno che non sia p = n.
In altre parole si assuma che X contenga p vettori, con p ≤ n, e che sia XT KX = D e
XT MX = I; allora i vettori contenuti in X e gli elementi diagonali di D possono essere o
meno rispettivamente autovettori e autovalori di (5.4). Se p = n allora X = Φ e D = Λ
poichè solo gli autovalori sono la base dell’intero spazio n-dimensionale e diagonalizzano
le matrici K e M.

5.2.2 Lo shifting
Una procedura sovente utilizzata per la risoluzione dei problemi agli autovalori è quella
dello shifting il cui scopo è quello di velocizzare il processo di soluzione. Nella soluzione
di Kφ = λMφ si dà uno shift ρ a K e si calcola:

K̂ = K − ρM (5.13)

e si considera il problema
K̂ψ = µMψ (5.14)

62
Per capire come gli autovalori di Kφ = λMφ siano K̂ψ = µMψ si riscrive la (5.14)
come
Kψ = γMψ (5.15)
con γ = ρ + µ. La (5.15) è il problema Kφ = λMφ dove

λi = ρ + µ i ; φi = ψi (5.16)

In altre parole gli autovettori di K̂ψ = µMψ sono gli stessi di Kφ = λMφ, mentre gli
autovalori vanno diminuiti di ρ. Una frequenta applicazione della tecnica dello shifting si
ha quando è richiesto il calcolo dei moti rigidi e l’algoritmo utilizzato non è stato ideato
esplicitamente per la determinazione degli autovalori nulli. Da quanto appena esposto è
possibile affermare che se sono presenti dei moti rigidi è sempre possibile lavorare su una
matrice di rigidezza shiftata che presenta tutti gli autovalori positivi.

5.2.3 Trasformazione di un problema generalizzato in uno standard


La maggioranza dei problemi agli autovalori che si incontrano in campo scientifico sono
di tipo standard e gli altri tipi di problemi possono essere ricondotti a questo. Per questa
ragione la risoluzione del problema standard ha attratto fortemente l’attenzione e sono
disponibili diversi algoritmi. La trasformazione di un problema generalizzato in uno stan-
dard ha una duplice implicazione. In primo luogo, poiché la trasformazione è possibile, si
può fare uso di tutta una serie di metodologie per i problemi standard; si può anche notare
la procedura di soluzione impiegata dipende di trasformare o meno il problema in forma
standard. In secondo luogo, se se un problema generalizzato può essere scritto in forma
standard, le proprietà degli autovalori, degli autovettori e del polinomio caratteristico del
problema generalizzato possono essere dedotte dalle proprietà delle corrispondenti quantità
del problema standard. Essendo le proprietà del problema standard più facilmente valuta-
bili, è per la seconda motivazione che è interessante trasformare un problema generalizzato
in uno standard.
Si assuma che M sia definita positiva. Essa viene decomposta come

M = SST (5.17)

dove S è una matrice non singolare. Se la (5.17) viene sostituita nella (5.4) si ha

Kφ = λSST φ (5.18)

Moltiplicando ambo i membri per S−1 e definendo un vettore

φ̃ = ST φ (5.19)

si ottiene il problema standard


K̃φ̃ = λφ̃ (5.20)
dove
K̃ = S−1 KS−T (5.21)

63
E’ possibile utilizzare due decomposizioni di M: la fattorizzazione alla Cholesky e la
decomposizione spettrale. Se si usa la fattorizzazione alla Cholesky si ha

S = L˜M . (5.22)

Indicando con R la matrice degli autovettori ortonormali e con D2 la matrice diagonale


degli autovalori, secondo la decomposizione spettrale si

M = RD2 RT (5.23)

essendo
S = RD2 (5.24)
E’ da notare che quando M è diagonale le espressioni (5.22) e (5.24) di S coincidono,
mentre sono diverse quando M è bandata.
Confrontando le due decomposizioni, è possibile affermare che la fattorizzazione alla
Cholesky è computazionalmente più vantaggiosa a causa del basso numero di operazioni
richieste; d’altro canto la decomposizione spettrale può portare a una maggiore accuratezza
nella risoluzione del problema Kφ = λMφ.
Se M è mal condizionata, si può optare per decomporre K al suo posto. Il problema
generalizzato KΦ = λMΦ viene scritto come Mφ = (1/λ)Kφ e si può ottenere il problema
standard
1
M̃φ̃ = φ̃ (5.25)
λ
dove
M̃ = S−1 MS−T (5.26)
M = SST (5.27)
φ̃ = ST φ (5.28)
ed S si può ottenere come fattorizzazione alla Cholesky o decomposizione spettrale di K.

5.2.4 Stima dell’errore


Un aspetto molto importante è stimare la precisione con la quale si problema agli autovalori
viene risolto. Se questo viene risolto per via iterativa, il processo si arresta una volta
arrivato a convergenza con una tolleranza prefissata. Si assuma che sia stata ottenuta
un’autocoppia approssimata (λ̄, φ̄). Per capire con che precisione questa autocoppia è
stata valutata (cioè quanto quella approssimata sia vicina a quella esatta), si calcola un
vettore residuo
r = Kφ̄ − λ̄φ̄ (5.29)
che può essere scritto come
−1
r = Φ(Λ − λ̄I) ΦT φ̄ (5.30)
Essendo λ̄ non uguale ma solo prossimo all’autovalore:
−1
φ̄ = Φ(Λ − λ̄I) ΦT r (5.31)

64
Essendo φ̄ 2
= 1 facendo la norma si ha

−1
1 ≤ (Λ − λ̄I) krk2 (5.32)
2

ed essendo
−1 1
(Λ − λ̄I) = max (5.33)
2 i λi − λ̄
si ha
1
min ≤ krk2 (5.34)
i λi − λ̄
L’espressione (5.34) fornisce una misura dell’accuratezza dell’approssimazione effettua-
ta sull’autovalore.
Se λ̄ è un’approssimazione dell’autovalore λi , il corrispondente vettore φ̄ è un’appros-
simazione di φi :
krk2
φ̄ − αφi 2 ≤ ; min λi − λ̄ (5.35)
s
Se si usa il metodo dell’iterazione inversa è facile trovare una stima dell’errore. Sia

Kφ̄ = Mφ̂ (5.36)

Si ha ( ! )1/2
φ̂T Mφ̂  2
min λi − ρ(φ̄) ≤ − ρ(φ̄) (5.37)
i φ̄T Mφ̄
e ( )1/2
2
λi − ρ(φ̄) ρ(φ̄)
min ≤ 1− (5.38)
i λi φ̂T M φ̂/φ̄T M φ̄
dove ρ(φ̄) è il quoziente di Rayleigh

φ̄T Kφ̄
ρ(φ̄) = (5.39)
φ̄T Mφ̄
E’ interessante considerare la stima dell’errore
Kφ̄ − λMφ̄ 2
ǫ= (5.40)
Kφ̄ 2

Essendo Kφ la forza di richiamo elastica e λMφ la forza di inerzia, la (5.40) esprime


il rapporto tra la norma del residuo all’equilibrio e la norma della forza elastica. Se
¯
l’autocoppia (lambda, φ̄) è stata calcolata con sufficiente accuratezza, la quantità ǫ deve
essere molto piccola.

65
5.3 L’iterazione inversa
La tecnica dell’iterazione inversa è molto efficace per quel che concerne il calcolo di un
autovettore e del corrispondente autovalore. Si assuma che K sia definita positiva e M sia
diagonale. Si assume un vettore di iterazione x1 e al passo k = 1, 2 . . . n si ha un’iterazione
del tipo:
Kx̄k+1 = Mxk (5.41)
e
x̄k1
xk+1 = T
(5.42)
(x̄k Mx̄k+1 )1/2
da cui si ha
xk+1 = φ1 per k→∞ (5.43)
Il punto fondamentale è la risoluzione della (5.41), mentre la (5.42) esprime solo la
condizione di ortonormalità rispetto a M.
Sebbene le relazioni (5.41) e (5.42) siano i passi dell’algoritmo, è più efficiente imple-
mentare l’iterazione inversa come segue:

Kx̄k+1 = yk (5.44)

ȳk+1 = Mx̄k+1 (5.45)

x̄T yk
ρ(x̄k+1 ) = Tk+1 (5.46)
x̄k+1 ȳk+1
ȳk+1
yk+1 = T
(5.47)
(x̄k+1 ȳk+1 )1/2
da cui
yk+1 → Mφ1 e ρ(x̄k+1 ) → λ1 per k→∞ (5.48)
(k+1)
Se si indica con λ1 (=ρ(x̄k+1 )) l’approssimazione a λ1 , si misura la convergenza
con
(k+1) (k)
λ1 − λ1
(k+1)
≤ tol (5.49)
λ1
dove tol deve essere 10−2s o inferiore quando per il calcolo dell’autovalore λ1 è richiesta
una precisione 2s − digit.
Se si indica con l l’ultima iterazione

λ1 ∼
= ρ(x̄l+1 ) (5.50)

e
x̄l+1
φ1 ∼
= T
(5.51)
(x̄l+1 ȳl+1 )1/2

66
5.4 L’iterazione diretta
Il metodo dell’iterazione diretta è complementare a quello dell’interazione inversa. Esso
infatti permette di calcolare l’autovettore corrispondente all’autovalore massimo. Scelto
un vettore di iterazione iniziale x1 , nelle successive iterazioni si valuta per k = 1, 2, . . . , n:

Mx̄k+1 = Kxk (5.52)


e
x̄k+1
xk+1 = T
(5.53)
(x̄k+1 Mx̄k+1 )1/2
da cui si ha
xk+1 = φn per k→∞ (5.54)
Anche in questo caso si può optare per un’implementazione alternativa:

Mx̄k+1 = yk (5.55)

ȳk+1 = Kx̄k+1 (5.56)

x̄T
k+1 yk+1
ρ(x̄k+1 ) = (5.57)
x̄T
k+1 ȳk
ȳk+1
yk+1 = T
(5.58)
(x̄k+1 ȳk )1/2
da cui
yk+1 → Mφn e ρ(x̄k+1 ) → λn per k→∞ (5.59)
Se si indica con l l’ultima iterazione

λn ∼
= ρ(x̄l+1 ) (5.60)
e
x̄l+1
φn ∼
= T
(5.61)
(x̄l+1 ȳ)1/2

5.5 Il metodo di Jacobi


Il metodo di Jacobi è una procedura numerica per il calcolo di tutti gli autovalori e auto-
vettori di una matrice reale simmetrica. Questo metodo genera una sequenza di matrici
A(k) che converge a una matrice diagonale i cui termini sono gli autovalori di A. Ciò è
fatto sfruttando la trasformazione di similitudine di Givens come segue.
Data A(0) = A, per qualsiasi k =, 1, 2, . . ., è fissata una coppia di indici p e q, con
1 ≤ p < q ≤ n. Indicando con Gpq = G(p, q, θ), la matrice A(k) = Gpq T A(k−1) Gpq è
costruita come segue:
Aij (k) = 0 if (i, j) = (p, q) (5.62)

67
Se si indacano c = cos θ e s = sin θ, la procedura che calcola A(k) partendo da A(k−1)
può essere cosı̀ scritta

   T   
App (k) Apq (k) c s App (k−1) Apq (k−1) c s
= (5.63)
Apq (k) Aqq (k) −s c Apq (k−1) Aqq (k−1) −s c

Se Apq (k−1) = 0, è possibile soddisfare (5.62) ponendo c = 1 e s = 0. e Apq (k−1) 6= 0,


ponendo t=s/c la (5.63) richede la risoluzione dell’equazione

Aqq (k−1) − App (k−1)


t2 + 2ηt − 1 = 0, η= (5.64)
2Apq (k−1)

Se η ≥ 0 si sceglie la radice t = √1 , altrimenti si sceglie t = −1


√ ; si pone
η+ 1+η2 −η+ 1+η2
quindi
1
c= √ , s = ct. (5.65)
1 + t2
Per misurare la velocità con cui i termini fuori diagonale di A(k) tendono a zero è
conveniente introdurre, per qualsiasi matrice M ∈ Rnxn , la quantità non negativa
 1/2 !1/2
n
X n
X
Ψ(M) =  mij 2  = kMk2F − m2ii (5.66)
i,j=1i6=j i=1

Il metodo di Jacobi assicura che Ψ(A(k) ) ≤ Ψ(A(k−1) ) per qualsiasi k ≥ 1. Infatti il


calcolo di (5.66) per la matrice A(k) risulta
 2  2  2  2
Ψ(A(k) ) = Ψ(A(k−1) ) − 2 Apq (k−1) ≤ Ψ(A(k−1) ) (5.67)

La relazione (5.67) suggerisce a ogni passo k che la scelta migliore degli indici p e q è
quella che corrisponde al termine di A(k−1)

Apq (k−1) = max Aij (k−1) (5.68)


i6=j

Il costo computazionale di questa strategia è dell’ordine di n2 flops, mentre il costo


dell’operazione A(k) = Gpq T A(k−1) Gpq è dell’ordine di n flops. È conveniente rifarsi al
cosidetto metodo di Jacobi row cyclic, nel quale la scelta degli indici p e q è fatta tramite
un’operazione di scambio di righe della matrice A(k) secondo il seguente algoritmo: per
qualsiasi k = 1, 2, . . . e per qualsiasi i-esima riga di A(k) (i = 1, . . . , n − 1), si pone p = i e
q = (i + 1), . . . , n. Ogni ciclo completo richiede N = n(n + 1)/2 trasformazioni di Jacobi.
Assumendo che |λi − λj | ≥ δ per i 6= j, è possibile dimostrare che il metodo converge
quadraticamente
1  2
Ψ(A(k+N) ) ≤ √ Ψ(A(k) ) , k = 1, 2, . . . (5.69)
δ 2

68
5.6 L’iterazione inversa Householder-QR
Il metodo dell’iterazione inversa Huseholder-QR (HQRI) è una procedura per la risoluzione
dei problemi standard. Se allora viene considerato un problema generalizzato, affinchè esso
possa essere risolto è necessario prima trasformarlo in uno standard. Verrà considerato il
problema Kφ = λφ con K che può avere autovalori nulli. Il metodo HQRI si articola in
tre passi:

• trasformazioni di Householder per ridurre K in forma tridiagonale;

• iterazione QR per il calcolo di tutti gli autovalori;

• calcolo degli autovettori della matrice tridiagonale richiesti tramite iterazione inver-
sa.

Una differenza fondamentale con il metodo di Jacobi è che la matrice è prima trasfor-
mata senza iterazione in forma tridiagonale. Questa matrice può essere usata efficacemente
da questo metodo nel quale tutti gli autovalori sono calcolati. Nel seguito vengono esposti
i tre step di questo metodo.

5.6.1 La riduzione di Householder


Partendo da K1 = K si applicano n − 2 trasformazioni

Kk+1 = PT
k KPk ; k = 1, 2, . . . , n − 2 (5.70)

dove PK è la matrice di trasformazione di Householder


2
Pk = I − θwk wkT ; θ= (5.71)
wkT wk

Per capire come scegliere il vettore w si parte dal caso k = 1 e si partizionano le


matrici:    
1 0 0
P1 = ; w1 = (5.72)
0 P¯1 w̄1
 
k11 kT1
K1 = (5.73)
k1 K11
dove K11 ,
mathbf barP1 e w̄1 sono di ordine n − 1. Usando la (5.70)
" #
k11 kT ¯
1 P1
K2 = (5.74)
P¯T ¯T
1 k1 P1 K1 P1
¯

Il vettore w1 viene ricavato dalla condizione

(I − θ w̄1w¯1 T )k1 = ± kk1 k2 e1 (5.75)

69
dove e1 è il versore di dimensione n − 1. Dalla relazione precedente si ottiene
w̄1 = k1 + sign(k21 ) kk1 k2 e1 (5.76)
dove k21 è l’elemento (2,1) di K1 .
Noto w̄1 , è possibile innescare l’algoritmo che riduce K in forma tridiagonale.

5.6.2 L’iterazione QR
L’iterazione QR è applicata alla matrice tridiagonale ottenuta dalla trasformazione di
Householder di K. In ogni caso l’iterazione QR sarebbe applicabile anche a K scritta
in forma originaria, ma il passaggio attraverso una sua riscrittura in forma tridiagonale
incrementa l’efficienza del processo di soluzione.
Il nome iterazione QR deriva dalla notazione usata nell’algoritmo. Il punto fondamen-
tale di questa tecnica è la fattorizzazione di K come
K = QR (5.77)
dove Q è una matrice ortogonale e R è una matrice triangolare superiore. Quindi si crea
RQ = QT KQ (5.78)
Una buona scelta è quella di ridurre K in una matrice triangolare superiore R usando
la matrice di rotazione di Jacobi, cioè valutando:
PT T T
n,n−1 . . . P3,1 P2,1 K = R (5.79)

dove la matrice di rotazione PT j,1 è scelta in modo da azzerare l’elemento (j,i). Usare
la(5.79) corrisponde a impiegare la (5.78):
Q = P2,1 P3,1 . . . Pn,n−1 (5.80)
L’algoritmo di iterazione QR si ottiene ripetendo le (5.77) e (5.78). Utilizzando la
notazione K1 = K, si scrive
Kk = Qk Rk (5.81)
e poi
Kk+1 = Rk Qk (5.82)
dove, trascurando il fatto che autovalori e autovettori possono non trovarsi nel solito
ordine,
Kk+1 → Λ e Q1 . . . Qk+1 Qk → Φ con k→∞ (5.83)
Questa pocedura può essere usata anche con lo shifting scrivendo:
Kk − µk I = Qk Rk (5.84)
Kk+1 = Rk Qk + µk I (5.85)
e anche ora come prima
Kk+1 → Λ e Q1 . . . Qk+1 Qk → Φ con k→∞ (5.86)

70
5.6.3 Calcolo degli autovettori
Una volta calcolati tutti gli autovalori in maniera accurata,si calcolano solo gli autovettori
desiderati della matrice Kn−1 (5.70) per semplice interazione inversa con shift pari al valore
del corrispondente autovalore. Sono di solito sufficienti due step dell’iterazione inversa
partendo da un vettore unitario. Gli autovettori di Kn−1 devono poi essere trasformati
con le trasformazioni di Householder usate per ottenere gli autovettori di K; indicando
con ψi l’i-esimo autovettore di Kn−1 e usando le matrici di trasformazione Pk si ha:

φi = P1 P2 . . . Pn−2 ψi (5.87)

5.7 Il metodo di Lanczos


Sia x un vettore iniziale arbitrario e lo si normalizzi rispetto a M per ottenere x1 :
x
x1 = ; γ = (xT Mx)1/2 (5.88)
γ

Si ponga β1 = 0. L’algoritmo di Lanczos [21] permette di calcolare i vettori x2 , . . . , xq


con le seguenti equazioni per i = 2, . . . , q:

Kx̄i = Mxi−1 (5.89)

αi−1 = x̄T
i Mxi−1 (5.90)
x̃i = x̄i − αi−1 xi−1 − βi−1 xi−2 (5.91)
1/2
βi = (x̃T
i Mx̃i ) (5.92)
xi = x̃i /βi (5.93)
La sequenza di vettori xi con i = 1, . . . , q generata utilizzando le relazioni precedenti
fornisce dei vettori che sono ortonormali a M e la matrice X = [x1 , . . . , xq ] soddisfa la
relazione:
XT (MK−1 M)X = Tq (5.94)
dove Tq è una matrice tridiagonale di ordine q:
 
α1 β2
 β2 α2 β3 
 
 .. 
 . 
 
 .. 
Tq =  .  (5.95)
 
 .. 
 . 
 
 αq−1 βq 
βq αq

71
Usando la (5.94) è possibile mettere in relazione gli autovalori e autovettori di Tq quando
q = n con quelli del problema generalizzato Kφ = λMφ che può anche essere scritto come
1
Mφ = MK−1 Mφ (5.96)
λ
Usando la trasformazione
φ = Xφ̃ (5.97)
e la (5.94) s ottiene dalla (5.96)
1
Tn φ̃ = φ̃ (5.98)
λ
Gli autovalori di Tn sono i reciproci degli autovalori di Kφ = λMφ, mentre gli
autovettori sono messi in relazione dalla (5.97).

72
Capitolo 6

Approssimazione di autovalori e
autovettori - Applicazioni

Soventemente in meccanica delle strutture è possibile incontrare dei problemi retti da


sistemi di equazioni differenziali. E’ ad esempio il caso della dinamica di dei sistemi a
più gradi di libertà oppure del buckling multimodale. In tali casi si rinuncia a risolvere
in forma chiusa un sistema di n equazioni differenziali in n incongite e si sceglie una via
che permette di disaccoppiare il sistema, cioè di risolvere n equazioni ognuna delle quali
figura in una e una sola incognita. Questa metodologia passa attraverso la soluzione di un
problema agli autovalori generalizzato esprimibile nella forma

Ax = λBx (6.1)

In questo capitolo verrà focalizzata l’attenzione prima sulla determinazione degli auto-
vettori e degli autovalori di una matrice e poi su quelli del problema generalizzato agli
autovalori.

6.1 Autovalori di una matrice


Questo paragrafo illustra le routines che sono state implementate per i seguenti scopi:

• determinazione di tutti gli autovalori di una matrice;


• determinazione di tutti gli autovalori e autovettori di una matrice;
• determinazione dei primi k autovalori e autovettori di una matrice.

In tutti e tre i casi si avrà a che fare con matrici bandate.


Per questi scopi sono state implementate due routines:
• ACML,
• MKL.
Entrambe sfruttano un algoritmo di tipo QR.

73
6.1.1 ACML
E’ possibile individuare una chiamata che permette il calcolo di tutti gli autovalori e/o
autovettori:
dsbev(char jobz, char uplo, int n, int ndiag, double *a,
int lda, double *w, double *z, int ldz, int *info);
dove
• se jobz =′ N ′ viene effettuato solo il calcolo degli autovalori, mentre se jobz =′ V ′
anche quello degli autovettori;
• se uplo =′ U ′ vuol dire che si sta passando il triangolo superiore della matrice; se
uplo =′ L′ quello inferiore;
• n è la dimensione della matrice di input;
• ndiag è il numero di sovradiagonali (se uplo = ’U’) o di sottodiagonali (se uplo =
’L’);
• a è il vettore che contiene la matrice di input scritta in una forma particolare;
• lda è la dimensione di a;
• w è il vettore le cui componenti sono gli autovalori della matrice;
• z è il vettore che per colonne contiene gli autovettori della matrice;
• ldz è la dimensione di z;
• inf o è un indicatore della riuscita dell’analisi.
Molto più interessante risulta essere l’altra funzione, che permette di effettuare una
selezione sul calcolo di autovalori e autovettori.
dsbevx(char jobz, char range, char uplo, int n, int ndiag,
double *a, int lda, double *q, int ldq, double vl, double vu,
int il, int iu, double abstol, int *nfound, double *w, double *z,
int ldz, int *ifail, int *info);
dove
• se range =′ A′ vengono restituiti tutti gli autovalori, se range =′ V ′ vengono resti-
tuiti solo quelli il cui valore è compreso tra vl e vu, se range =′ I ′ vengono restituiti
glia autovalori tra l’indice il el’indice iu disposti in ordine ascendente;
• q è il vettore che contiene per colonne una matrice di dimensioni [ldq,n] usata (se
jobz=’V’) per la riduzione in forma triangolare;
• abstol è la tolleranza sull’errore nel calcolo degli autovalori;
• if ail se jobz=’V’ e info=0 i primi M elementi di ifail sono nulli; se inf o ≥ 0, ifail
contiene gli indici di quegli autovettori la cui convergenza è fallita.

74
6.1.2 MKL
Anche in questo caso è possibile scegliere una funzione che permette il calcolo di tutti gli
autovalori e/o autovettori:

dsbev( JOBZ, UPLO, N, KD, AB, LDAB, W, Z, LDZ, WORK, INFO )

dove WORK è un workspace di dimensione 3N-2.


In alternativa c’è l’altra funzione, che permette di effettuare una selezione sul calcolo
di autovalori e autovettori.

dsbevx( JOBZ, RANGE, UPLO, N, KD, AB, LDAB, Q, LDQ, VL,


VU, IL, IU, ABSTOL, M, W, Z, LDZ, WORK, IWORK,
IFAIL, INFO )

dove WORK e IWORK sono due workspace di dimensione rispettivamente 7*N e


5*N.

75
6.2 Problema generalizzato agli autovalori
Questo paragrafo illustra le routines che sono state implementate per i seguenti scopi:

• determinazione di tutti gli autovalori di un problema generalizzato;

• determinazione di tutti gli autovalori e autovettori di un problema generalizzato;

• determinazione dei primi k autovalori e autovettori di un problema generalizzato.

Per questi scopi sono state implementate le seguenti routines:

• ACML,

• MKL,

• ARPACK.

Le tre soluzioni proposte sono tutte relative a matrici bandate.


ARPACK [15] è una suite di routines scritte in Fortran77 progettate per risolvere
problemi agli autovalori di grandi dimensioni. Questa suite è progettata per calcolare
pochi autovalori e i corrispondenti autovettori di una generica matrice A di dimensioni nxn
ed è la più appropriata per matrici sparse di grandi dimensioni. ARPACK è basato su una
variante del metodo di Arnoldi denominato Implicitly Restarted Arnoldi Method (IRAM).
Quando la matrice è simmetrica esso si riduce a una variante del metodo di Lanczos
denominato Implicitly Restarted Lanczos Method (IRLM). Queste varianti possono essere
intese come una sintesi del metodo di Arnoldi/Lanczos con la tecnica Implicitly Shifted
QR che può essere sfruttata per problemi di grandi dimensioni.

6.2.1 ACML
Parallelamente a quanto esposto per la determinazione di autovalori e autovettori di una
matrice, anche in questo caso è possibile individuare due importanti funzioni. La prima
istruzione calcola autovalori e/o autovettori:

dsbgv(char jobz, char uplo, int n, int ka, int kb, double *ab,
int ldab, double *bb, int ldbb, double *w, double *z, int ldz, int *info);

dove

• se jobz =′ N ′ viene effettuato solo il calcolo degli autovalori, mentre se jobz =′ V ′


anche quello degli autovettori;

• se uplo =′ U ′ vuol dire che si sta passando il triangolo superiore della matrice; se
uplo =′ L′ quello inferiore;

• n è la dimensione della matrice di input;

76
• ka è il numero di sovradiagonali (se uplo = ’U’) o di sottodiagonali (se uplo = ’L’)
di a;

• kb è il numero di sovradiagonali (se uplo = ’U’) o di sottodiagonali (se uplo = ’L’)


di b;

• ab è il vettore che contiene la prima matrice di input;

• ldab è il numero di righe di ab;

• bb è il vettore che contiene la seconda matrice di input;

• ldab è il numero di righe di bb;

• w è il vettore le cui componenti sono gli autovalori della matrice;

• z è il vettore che per colonne contiene gli autovettori della matrice;

• ldz è la dimensione di z;

• inf o è un indicatore della riuscita dell’analisi.

La seconda istruzione rappresenta la versione expert della precedente:

dsbgvx(char jobz, char range, char uplo, int n, int ka, int kb,
double *ab, int ldab, double *bb, int ldbb, double *q, int ldq,
double vl, double vu, int il, int iu, double abstol, int *m,
double *w, double *z, int ldz, int *ifail, int *info);

Non si ci sofferma sulla sintassi di queste chiamate poichè banalmente deducibile da


quella delle routines esposte nei paragrafi precedenti.

6.2.2 MKL
Questa routine lavora in maniera del tutto analoga a quella precedentemente esposta.

6.2.3 ARPACK
Questa routine risolvere un problema agli autovalori generalizzato per matrici simmetriche.
Le matrici A e B vengono create tramite i costruttori

ARbdSymMatrix<double> A(MA, nsdiagA, a);


ARbdSymMatrix<double> B(MB, nsdiagB, b);

previa memorizzazione dei dati di input secondo la stessa procedura di storage usata dalle
routines ACML/MKL.
Tramite la chiamata

ARluSymGenEig<double> dprob(nev, A, B, 2.0);

77
è possibile determinare i primi nev autovalori prossimi al valore 2.0.
E’ possibile anche trovare gli autovettori associati a tali autovettori:

dprob.FindEigenVectors();

e ottenere in output i risultati desiderati

Solution(A, B, dprob);

78
6.2.4 Confronti tra le durate delle analisi
Verranno di seguito confrontate le routines per matrici bandate che calcolano autovalori e
autovettori di un problema generalizzato. In particolare si effettueranno tre test:

• A, determinazione di tutti gli autovalori (figura (6.10));

• B, determinazione di tutti gli autovalori e di tutti gli autovettori (figura (6.11));

• C, determinazione dei primi 5 autovalori e autovettori in ordine crescente (figura


(6.12)).

Verranno fornite in input delle matrici disponibili presso [4] e le cui caratteristiche
principali sono elencate in tabella 6.1.

file Dimensione Nonzeri Banda Banda/Dim [%]


bcsstk03 112 376 8 7.14
bcsstk04 132 1890 48 36.36
bcsstk05 153 1288 29 18.95
bcsstk06 420 4140 48 11.43
bcsstk07 420 4140 48 11.43
bcsstk09 1083 18437 63 5.82
bcsstk10 1086 22070 38 3.50
bcsstk16 4884 290378 141 2.89
bcsstk28 4884 290378 525 10.75

Tabella 6.1: Matrici analizzate tramite i solutori sparsi

La tabella 6.2 mostra i risultati dell’analisi, mentre la figura (6.13) fornisce un’analisi
della prestazionalità complessiva delle tre routines utilizzate.

ACML MKL ARPACK


file
A B C A B C A B C
bcsstk03 0.01 0.02 0.02 0.01 0.02 0.02 0.03 0.04 0.00
bcsstk04 0.05 0.08 0.09 0.07 0.10 0.05 0.05 0.05 0.00
bcsstk05 0.04 0.09 0.05 0.05 0.08 0.05 0.07 0.05 0.00
bcsstk06 0.37 1.24 0.64 0.41 1.10 0.80 0.61 1.08 0.01
bcsstk07 0.38 1.25 0.63 0.42 1.09 0.79 0.67 1.13 0.01
bcsstk09 2.90 18.71 9.02 3.06 15.72 10.05 15.83 28.79 0.06
bcsstk10 1.63 14.44 8.01 1.73 12.66 8.41 13.39 28.68 0.03
bcsstk16 136.79 1677.61 1205.25 134.75 1602.04 1159.38 1528.37 Nan 0.07
bcsstk28 516.80 1689.09 1351.68 490.27 1543.40 1256.62 1356.19 Nan 4.39

Tabella 6.2: Risultati analisi agli autovalori

79
Figura 6.1: bcsstk03 Figura 6.2: bcsstk04 Figura 6.3: bcsstk05

Figura 6.4: bcsstk06 Figura 6.5: bcsstk07 Figura 6.6: bcsstk09

Figura 6.7: bcsstk10 Figura 6.8: bcsstk16 Figura 6.9: bcsstk28

80
Determinazione di tutti gli autovalori
1

0.9

0.8

0.7

0.6
Performance profile

0.5

0.4

0.3 acml
mkl

0.2 arpack

0.1

0
0 0.5 1 1.5 2 2.5 3 3.5

Figura 6.10: Benchmark problema A

Determinazione d tutte le autosoluzioni


1

0.9

0.8

0.7

0.6
Performance profile

0.5

0.4

0.3 acml
mkl

0.2 arpack

0.1

0
0 0.2 0.4 0.6 0.8 1 1.2

Figura 6.11: Benchmark problema B

81
Determinazione delle prime 5 autosoluzioni
1

0.9

0.8

0.7

0.6
Performance profile

0.5

0.4

0.3

0.2

acml
0.1
mkl
arpack
0
0 2 4 6 8 10 12 14 16 18

Figura 6.12: Benchmark problema C

Benchmark problema agli autovalori


1

0.9

0.8

0.7

0.6
Performance profil

0.5

0.4

0.3

0.2

acml
0.1
mkl
arpack
0
0 5 10 15 20 25 30 35 40

Figura 6.13: Benchmark problema generalizzato agli autovalori

82
Capitolo 7

Applicazione di meccanica delle


strutture

7.1 Introduzione
Come già discusso in precedenza, in meccanica delle strutture frequente dover affrontare
due tipi di problemi:

• la risoluzione di sistemi lineari tipici di tutte le formulazioni ad elementi finiti in


campo lineare o nonlineare;

• la risoluzione di un problema generalizzato agli autovalori, situazione tipica dell’a-


nalisi modale e del buckling.

In questo capitolo saranno esposte una serie di applicazioni meccaniche delle routines
esposte nei capitoli precedenti.

7.2 Sistemi lineari


Vengono di seguito presentati i risultati dell’analisi elastico-lineare condotta con il pro-
gramma KASP (Koiter Analysis of Slender Panels) che consente di analizzare pannelli in
parete sottile in campo nonlineare.

83
7.2.1 Test 1 - Cbeam
Si faccia riferimento alla Figura 7.1.

Figura 7.1: Cbeam

La struttura ha subito diverse discretizzazioni, a ognuna delle quali corrisponde una


certa matrice di coefficienti per usata per condurre l’analisi lineare.

Figura 7.2: 100-15 Figura 7.3: 40-25 Figura 7.4: 40-30

Figura 7.5: 40-40 Figura 7.6: 20-30 Figura 7.7: 20-40

La tabella 7.1 mostra le durate dei processi di fattorizzazione dei vari solutori impie-
gati, mentre la figura (7.8) fornisce una misura della prestazionalità dei solutori in questa
situazione.

84
Mesh Chol Pard Spool UMF Acml Mkl Bandec Skyd N band/N [%]
100-15 0.72 1.02 0.82 1.26 1.67 2.56 16.52 1.93 14600 2.06
40-25 0.45 0.48 0.44 0.86 3.62 3.61 35.72 3.14 9560 5.03
40-30 0.62 0.48 0.55 1.20 6.00 5.87 62.57 5.59 11390 5.01
40-40 0.93 0.75 0.87 1.80 13.64 12.80 152.80 16.72 15050 4.99
20-30 0.22 0.20 0.17 0.38 2.81 2.89 26.46 2.84 5790 9.86
20-40 0.31 0.25 0.25 0.57 6.50 6.27 74.60 7.67 7650 9.81

Tabella 7.1: Durate del processo di fattorizzazione per CBeam

Benchmark per Cbeam


1

0.9

0.8

cholmod
0.7
pardiso
spooles
umfpack
acml
0.6
mkl
Performance profile

bandec
skydec

0.5

0.4

0.3

0.2

0.1

0
0 1 2 3 4 5 6 7 8 9
α

Figura 7.8: Benchmark Cbeam

85
Per una più immediata comprensione di questi risultati gli stessi vergono normalizzati
rispetto al più veloce, cioè Pardiso, ed espressi come multipli di questo nella tabella 7.2.

MESH Cholmod Spooles UMF Acml Mkl Bandec Skydec


100-15 0.88 1.00 1.54 2.04 3.20 20.15 2.35
40-25 1.02 1.09 1.95 8.23 8.20 81.18 7.14
40-30 1.12 0.87 2.18 10.91 10.67 113.76 10.16
40-40 1.07 0.86 2.97 15.68 14.72 175.63 19.23
20-30 1.29 1.18 2.23 16.57 17 155.65 15.19
20-40 1.24 1 2.28 26 25.08 298.4 30.68

Tabella 7.2: Durate del processo di fattorizzazione per CBeam normalizzati rispetto a
Pardiso

86
7.2.2 Test 2 - Ibeam
Si faccia riferimento alla Figura 7.9.

Figura 7.9: Ibeam

Come già visto nel caso precedente, anche qui la struttura ha subito diverse discretiz-
zazioni e per ognuna di queste è stata condotta l’analisi lineare.
La tabella 7.3 mostra le durate dei processi di fattorizzazione dei vari solutori impiegati,
mentre la figura (7.14) fornisce informazioni sulle performance dei solutori.

Mesh Chol Pard Spool UMF Acml Mkl Bandec Skyd N band/N [%]
40-25 0.71 0.65 0.72 1.28 21.85 21.11 260.13 22.88 15826 5.95
40-30 0.95 0.78 0.90 1.84 37.81 34.98 1262.85 43.15 18876 5.95
40-40 1.41 1.20 1.39 6 2.78 89.60 77.59 NaN 97.40 24976 5.94
20-30 0.27 0.31 0.29 0.48 12.79 15.88 230.57 17.08 9596 11.70
20-40 0.40 0.39 0.40 0.66 37.51 37.22 549.23 44.98 12696 11.68

Tabella 7.3: Durate del processo di fattorizzazione per IBeam

87
Figura 7.10: 40-30 Figura 7.11: 40-40 Figura 7.12: 20-30

Figura 7.13: 20-40

Benchmark per Ibeam


1

0.9

0.8

0.7

0.6
Performance profile

0.5

0.4

0.3

cholmod
0.2
pardiso
spooles
umfpack
acml
0.1
mkl
bandec
skydec

0
0 2 4 6 8 10
α

Figura 7.14: Benchmark Ibeam

88
Per una più immediata comprensione di questi risultati gli stessi vengono normalizzati
rispetto al più veloce, cioè Pardiso, ed espressi come multipli di questo nella tabella 7.4.

MESH Cholmod Spooles UMF Acml Mkl Bandec Skydec


40-25 0.99 0.90 1.78 30.35 29.32 361.29 31.78
40-30 1.06 0.87 2.04 42.01 38.87 1403.17 47.94
40-40 1.01 0.86 20 64.46 55.82 NaN 70.07
20-30 0.93 1.07 1.65 44.10 39.93 795.07 58.90
20-40 1 0.975 1.65 93.76 93.05 1373,07 112.45

Tabella 7.4: Durate del processo di fattorizzazione per IBeam normalizzati rispetto a
Pardiso

La figura (7.15) mostra la misura della performance complessiva dei solutori sui due
test effettuati.

Benchmark KASP
1

0.9

0.8

0.7

0.6

0.5

0.4
cholmod
pardiso

0.3 spooles
umfpack
acml
0.2 mkl
bandec
skydec
0.1

0
0 2 4 6 8 10

Figura 7.15: Benchmark KASP

89
7.3 Problema agli autovalori
Le procedure di calcolo agli autovalori sono state implementate nell’ambito di un codice di
calcolo per l’analisi elastica nonlineare alla Koiter di telai 3D. L’attenzione stata focalizzata
principalmente sulla ricerca del punto di biforcazione.
Gli spostamenti u[λ] sono considerati talmente piccoli che si può far riferimento alla
configurazione iniziale indeformata della struttura e il legame tensioni-deformazioni può
essere ritenuto lineare. Grazie a questa ipotesi la matrice di rigidezza tangente Kt [λ]
definita dall’equivalenza energetica

δuT Kt [λ]û = Φ′′ [λ]ûδu (7.1)

può essere scritta nella forma


Kt [λ] = K0 + λKg (7.2)
dove K0 è la consueta matrice di rigidezza della configurazione iniziale, mentre Kg tiene
conto della presenza delle tensione σ[λ] che variano linearmente con λ. La condizione
critica di Eulero permette di ricondursi a un problema agli autovalori

K0 v + λKg v = 0. (7.3)

Verranno quindi esposti i risultati della risoluzione di questo problema.

90
7.3.1 Test 1 - Cupola
E’ stato utilizzato una struttura che presenta 2912 variabili che ha generato le matrici
in figura (7.16) e (7.17). Nella figura (7.18) sono riportate le durate dell’analisi volta a
ricercare le prime k autosoluzioni con le librerie ARPACK.

Figura 7.16: Cupola - Matrice K0 Figura 7.17: Cupola - Matrice Kg

Durata dell’analisi al variare del numero di autosoluzioni richieste


1000

900

800

700
Durata dell’analisi [s]

600

500

400

300

200

100

0
0 200 400 600 800 1000 1200 1400 1600
Numero di autosoluzioni richieste

Figura 7.18: Risultati dell’analisi per Cupola

91
7.3.2 Test 2 - Traliccio
Stavolta è stato utilizzato una struttura che presenta 5910 variabili che ha generato le
matrici in figura (7.19) e (7.20). Nella figura (7.18) sono riportate le durate dell’analisi
volta a ricercare le prime k autosoluzioni con le librerie ARPACK.

Figura 7.19: Traliccio - Matrice K0 Figura 7.20: Traliccio - Matrice Kg

Durata dell’analisi al variare del numero di autosoluzioni richieste


2000

1800

1600

1400
Durata dell’analisi

1200

1000

800

600

400

200

0
0 200 400 600 800 1000 1200 1400 1600 1800 2000
Numero di autosoluzioni richieste

Figura 7.21: Risultati dell’analisi per Traliccio

92
Capitolo 8

Conclusioni

L’obiettivo di questa tesi è stato mettere in luce come le librerie numeriche di nuova gene-
razione, che la tecnologia mette a disposizione, riescano ad incrementare le prestazioni di
calcolo in maniera sensibile rispetto a strumenti che non sfruttano appieno l’architettura
del calcolatore. In alcuni casi l’incremento di prestazioni ottenuto in termini di tempo di
calcolo è stato di ordini di grandezza superiore a parità di algoritmo utilizzato. Questa
differenza enorme nelle prestazioni è dovuta essenzialmente alla maniera con cui, trami-
te opportuna implementazione del software, la CPU accede ai dati di cui ha prioritaria
necessita e ciò si traduce in:

• riduzione drastica dei tempi di latenza;

• riduzione dell’attività di ricerca dei dati sulla rom e sulla ram da parte della CPU;

• generale e sensibile incremento della prestazioni.

Poiché l’algebra lineare in meccanica computazionale delle strutture ha un ruolo de-


cisivo, nasce l’idea di poter sfruttare questi strumenti a vantaggio di una riduzione della
durata delle analisi che si vogliono condurre.
Per prima cosa ci si è preoccupati di testare semplici operazioni di prodotto tra vettori e
matrici per le quali è nota la complessità del problema (2n k , con k=1 per il prodotto scalare
tra due vettori, k=2 per il prodotto tra una matrice e un vettore, k=3 per il prodotto tra
due matrici). Tutte le metodologie utilizzate si differenziano per il modo in cui gestiscono
l’accesso ai dati, non già per l’algoritmo implementato. Si pensi all’operazione di prodotto
tra due matrici. Le figure (2.5) e (2.6) mostrano come le routines che si appoggiano alle
suite BLAS/LAPACK siano molto più performanti delle altre; ancor più eclatante è il
risultato della routine MTL Sparsa, le cui risorse dipendono dalla particolare allocazione
della matrice. Sempre la figura (2.6) mostra come oltre una certa soglia tutte le routine
tendano a saturare la propria capacità in termini di numero di operazioni che riescono ad
eseguire nell’unità di tempo, mentre la MTL Sparsa possieda ancora risorse di gran lunga
superiori alle altre.
Quindi si è passati alla soluzione di sistemi di equazioni lineari del tipo Ku=p che
costituiscono la base di tutti i codici FEM che riguardano le applicazioni di meccanica

93
computazionale e di analisi agli elementi finiti in generale. Solitamente in problemi strut-
turali la matrice K è simmetrica bandata e definita positiva. Pertanto, passate in rassegna
le varie soluzioni proposte per matrici generiche e simmetriche indefinite, è stata focaliz-
zata l’attenzione sui solutori bandati. In particolare è stato dimostrato come procedure
sofisticate dal punto di vista della programmazione, ma basate su metodi tradizionali di
progettazione del software e quindi prive di risorse extra in termini di gestione dell’ac-
cesso ai dati, forniscano risultati decisamente peggiori rispetto ai solutori bandati ACML
e MKL che si appoggiano al pacchetto BLAS/LAPACK. La migliore prestazione del so-
lutore bandato MKL è da ricercare nel fatto che la libreria è ottimizzata per processori
Intel, quale quello che equipaggia la macchina usata per la campagna di sperimentazione.
è ragionevole presumere che se tale campagna fosse stata condotta su una macchina con
processore AMD il solutore ACML sarebbe stato più performante.
Si è quindi passati a studiare i solutori sparsi sempre nell’ambito di problemi governa-
ti da matrici simmetriche e definite positive. La campagna di sperimentazione condotta
su 44 matrici ha messo in luce come il solutore UMFPACK SuiteSparse sia quello me-
no performante. Ciò è dovuto alla natura dello stesso poichè progettato per matrici non
simmetriche. La stessa SuiteSparse implementa il solutore CHOLMOD per matrici sim-
metriche e definite positive che si ha fornito prestazioni di tutto rispetto essendo inferiore,
nelle performance, solo al solutore sparso PARDISO MKL, la cui elevata prestazionalità
è figlia in maniera particolare dell’utilizzo delle librerie MKL su una macchina Intel. Il
solutore SPOOLES ha dimostrato delle prestazioni accettabili.
La curiosità ha poi indotto ad estendere la campagna di sperimentazione dei solutori
sparsi anche alle matrici utilizzate per i solutori bandati ed è stato dimostrato come anche
nel caso di matrici di coefficienti bandate i solutori sparsi siano sempre più performanti
almeno nella vasta casistica di test numerici condotti che solo in maniera sintetica è stata
riportata nella tesi. Alla luce di queste considerazioni è possibile affermare che l’adozione
di un solutore sparso come PARDISO MKL, possa essere presa in considerazione anche
nei casi in cui la struttura della matrice del problema è bandata avendo mostrato delle
performance sempre paragonabili o superiori a quelle dei solutori bandati.
Un altro problema frequente in meccanica computazionale è quello della soluzione di
un problema agli autovalori generalizzato. La sperimentazione ha riguardato una routine
ACML, una MKL e una ARPACK ed ha messo in luce come le routines ACML e MKL
siano in vantaggio rispetto a quella ARPACK quando si tratta di calcolare l’intero set di
autovalori e/o autovettori. La situazione si ribalta quando la ricerca è finalizzata a un
numero di autosoluzioni decisamente piccolo rispetto alla dimensione delle matrici. Ciò è
ovviamente intimamente legato al diverso algoritmo che il pacchetto Arpack implementa
rispetto al pacchetto Blas/Lapack. Il primo, infatti, riconduce il calcolo delle autosoluzioni
all’utilizzo di una variante del metodo di Lanczos, mentre il secondo a una variante dell’al-
goritmo QR (si faccia riferimento al capitolo sugli aspetti teorici del calcolo di autovalori
e autovettori).
Si è quindi passati all’implementazione ed alla sperimentazione numerica su pacchetti
agli elementi finiti riguardanti problemi prettamente strutturali. In particolare sono state
inserite le routine per l’analisi lineare e per l’analisi agli autovalori nel codice KASP (Koiter
Analysis of Slender Panels) che effettua l’analisi asintototica di strutture a pannelli e di un

94
codice per l’analisi nonlineare di strutture costituite da travi 3D. Anche in questo contesto,
i test condotti su una serie di benchmark hanno mostrato lo stesso tipo di performance
ottenute nel caso di matrici generiche e non ottenute da formulazioni FEM, sia per quel che
riguarda i solutori di sistemi lineari, sia per il problema agli autovalori generalizzato. Si
può concludere quindi che questi strumenti possono, se correttamente utilizzati, fortemente
ottimizzare le prestazioni di codici FEM esistenti.
Si può concludere affermando che la moderna programmazione in ambito strutturale
non può prescindere da una più accurata conoscenza del calcolatore che si concretizza
nell’adozione di strumenti efficaci per la risoluzione di un dato problema.

95
Appendice A

Richiami di algebra lineare

L’utilizzo dell’analisi agli elementi finiti è basato sull’impiego dell’algebra dei vettori e
delle matrici poichè la rappresentazione matriciale permette di poter esprimere il processo
di soluzione in una notazione tanto compatta quanto elegante.
Da un punto di vista pratico l’algebra lineare può quindi essere considerata come uno
strumento efficace per manipolare in maniera pratica un gran numero di dati.

A.1 Introduzione alle matrici


L’efficacia dell’uso delle matrici viene messo in luce quando si considera la soluzione di un
sistema di equazioni lineari. Un sistema di equazioni lineari è il dato di un certo numero
m di equazioni lineari in n incognite, e può essere scritto nel modo seguente:


 a1,1 x1 + a1,2 x2 + · · · + a1,n xn = b1

 a2,1 x1 + a2,2 x2 + · · · + a2,n xn = b2
.. (A.1)

 .


am,1 x1 + am,2 x2 + · · · + am,n xn = bm
dove x1 , . . . , xn sono le incognite e i numeri aij , detti i coefficienti, sono elementi di un
campo, ad esempio dei numeri reali o complessi. Anche i termini noti bi sono elementi del
campo. Una n-upla (x1, . . . , xn) di elementi nel campo è una soluzione se soddisfa tutte
le m equazioni.
Usando le matrici ed il prodotto fra matrici e vettori si possono separare agevolmente
i coefficienti, le incognite ed i termini noti del sistema, e scriverlo nel modo seguente:
    
a11 a12 · · · a1n x1 b1
 a21 a22 · · · a2n   x2   b2 
    
 .. .. .. ..   ..  =  ..  (A.2)
 . . . .   .   . 
am1 am2 · · · amn xn bm
In modo molto più sintetico si scrive
Ax = b, (A.3)

96
dove A è la matrice (mxn) dei coefficienti, x è il vettore delle n incognite e b è il vettore
degli m termini noti.

A.2 Matrici particolari


Quando gli elementi di una certa matrice corrispondono a una legge particolare si parla
di matrici speciali.
Nell’ambito matematico dell’analisi numerica una matrice sparsa è una matrice i cui
valori sono quasi tutti pari a zero.

Figura A.1: Matrice sparsa

Quando delle matrici sparse vengono memorizzate e gestite su un computer, risulta


proficuo, e spesso anche una necessità, utilizzare algoritmi specializzati e strutture dati che
tengono conto della natura sparsa della matrice. Svolgere delle operazioni utilizzando le
strutture e gli algoritmi matriciali usuali risulta un’operazione molto lenta, e porta anche
a grandi sprechi di memoria, se la matrice da gestire è sparsa. I dati sparsi sono, per loro
natura, facilmente comprimibili, e la loro compressione comporta quasi sempre un utilizzo
significativamente inferiore di memoria.
La struttura dati classica di un matrice è quella di un array bidimensionale. Ogni
elemento dell’array rappresenta il generico elemento ai,j , a cui si può avere accesso tramite
i due indici i e j. Per una matrice (m × n) deve essere disponibile almeno il minimo
quantitativo di memoria necessario ad immagazzinare gli (m × n) valori della matrice.
Molti, se non tutti i valori di una matrice sparsa sono pari a zero. Il principio di
base che si segue per memorizzare una matrice sparsa è di salvare, invece di tutti i valori,
soltanto quelli diversi da zero. A seconda del numero e della distribuzione dei valori diversi
da zero, si possono utilizzare diverse strutture dati ed ottenere risparmi considerevoli in
termini di memoria impossibili da ottenere con l’approccio usuale.
Un esempio di un formato per memorizzare matrici sparse è il Column Compressed
Form, che memorizza una matrice sparsa m × n, in una riga usando tre array monodimen-
sionali. Sia N N Z il numero di valori diversi da zero. Il primo array è Ax, di lunghezza
N N Z, chee memorizza tutti i valori diversi da zero, ordinati da sinistra a destra e dall’alto
verso il basso. Il secondo array è Ia; Ia(i) contiene l’indice del primo elemento di A diverso
da zero della riga i-esima. La lunghezza della riga i è determinata da Ia(i + 1) − Ia(i), per

97
cui Ia dev’essere di lunghezza m + 1. Il terzo array, Ja, contiene l’indice della colonna di
ciascun elemento di A, quindi Ja è lungo N N Z.
Ad esempio, la matrice  
1 2 0 0
 0 3 9 0  (A.4)
0 1 4 0
è una matrice (3x4) con sei elementi diversi da zero, per cui
 
A= 1 2 3 9 1 4 (A.5)

 
Ia = 1 3 5 7 (A.6)

 
Ja = 1 2 2 3 2 3 (A.7)
Una matrice bandata è una matrice sparsa i cui elementi diversi da zero sono tutti
posti in una banda diagonale che comprende la diagonale principale e, opzionalmente, una
o più diagonali alla sua destra od alla sua sinistra.
Formalmente una matrice (n × n) A = (ai,j ) è una matrice a banda se tutti gli elementi
dalla matrice all’esterno di una striscia diagonale la cui ampiezza è determinata dalle
costanti k1 e k2 :

ai,j = 0 se j < i − k1 o j > i + k2 ; k1 , k2 ≥ 0. (A.8)

Le quantità k1 e k2 sono le semiampiezze di banda rispettivamente sinistra e destra. La


banda della matrice è k1 + k2 + 1.
Una matrice a banda con k1 = k2 = 0 è una matrice diagonale; una matrice con
k1 = k2 = 1 è una matrice tridiagonale. Se si impone che k1 = 0 e k2 = n − 1, si ottiene
la definizione di una matrice triangolare inferiore; similmente, con k1 = n − 1 e k2 = 0
si ottiene una matrice triangolare superiore.
Le matrici utilizzate nei problemi risolvibili tramite il metodo degli elementi finiti od
il metodo delle differenze finite sono spesso a banda. Queste matrici possono essere viste
come la descrizione dell’accoppiamento tra le variabili del problema.
Da un punto di vista computazionale, lavorare con matrici a banda è sempre preferibile
a farlo con matrici quadrate dense di pari dimensioni. Una matrice a banda può essere
assimilata, quanto a complessità, a una matrice rettangolare in cui la dimensione delle
righe è pari alla banda della matrice di partenza. Per questo motivo, l’impiego di risorse
per svolgere operazioni quali le moltiplicazioni si riduce significativamente, e si arriva
spesso a grandi risparmi in termini di tempo e di complessità di calcolo.
Le matrici a banda sono spesso memorizzate tenendo conto della banda stessa.

98
Per esempio, si consideri una matrice tridiagonale con banda pari a 3. La matrice 6x6:
 
B11 B12 0 ··· ··· 0
 .. 
 B21 B22 B23 . . . . . . . 
 
 .. 
 0 B32 B33 B34 . . . . 
  (A.9)
 .. . . 
 . . B43 B44 B45 0 
 
 .. .. .. 
 . . . B B 54 B 55 56
0 ··· ··· 0 B65 B66

può essere memorizzata come una matrice 6x3:


 
0 B11 B12
 B21 B22 B23 
 
 B32 B33 B34 
  (A.10)
 B43 B44 B45 
 
 B54 B55 B56 
B65 B66 0

Un risparmio ancora maggiore è possibile quando la matrice è simmetrica. Ad esempio,


si consideri una matrice simmetrica 6x6 con banda destra pari a 2:
 
A11 A12 A13 0 ··· 0
 .. .. 
 A22 A23 A24 . . 
 
 A33 A34 A35 0 
  (A.11)
 A 44 A 45 A 46

 
 sym A55 A56 
A66

Questa matrice può essere memorizzata come una matrice 6x3:


 
A11 A12 A13
 A22 A23 A24 
 
 A33 A34 A35 
  (A.12)
 A44 A45 A46 
 
 A55 A56 0 
A66 0 0

Una matrice skyline, o matrice a banda variabile, è una strategia di memorizzazione di


una matrice quadrata che riduce lo spazio allocato rispetto alla memorizzazione a banda.
In quest’ultimo tipo di allocazione, tutti gli elementi della matrice inclusi all’interno di una
certa distanza dalla diagonale vengono memorizzati; nella memorizzazione skyline solo gli
elementi a partire dal primo all’ultimo non nulli di ogni colonna (o riga) sono memorizzati.
Se la matrice è simmetrica si memorizza solo la parte triangolare.
Si faccia riferimento alla Figura A.2

99
Figura A.2: Matrice skyline

A.3 Prodotto tra matrici


Due matrici possono essere moltiplicate, sotto certe condizioni, dando luogo ad un’altra
matrice.
Se A è una matrice (mxn) e B è una matrice (nxp), il loro prodotto A × B è una
matrice (mxp) data da
n
X
(A × B)ij = air brj = ai1 b1j + ai2 b2j + · · · + ain bnj . (A.13)
r=1
per ogni i e j.
È importante notare che due matrici possono essere moltiplicate fra loro solo se il
numero di colonne della prima è uguale al numero di righe della seconda.
Una matrice può essere moltiplicata con se stessa se e solo se è quadrata. In questo
caso, il prodotto A × A si denota con A2 . Più in generale, la potenza n-esima di una
matrice è:
A×A {z· · · A}
An = | (A.14)
n
dove n è un numero naturale.
La Figura A.3 mostra il caso in cui A è 3 × 2 e B è 2 × 3 e si voglia calcolare l’elemento
AB(12) della matrice prodotto AB 3x3.

Figura A.3: Prodotto tra due matrici

2
X
(A × B)12 = a1r br2 = a11 b12 + a12 b22 (A.15)
r=1

100
Una matrice con una sola riga, cioè di dimensione 1 × n, è un vettore riga. Analoga-
mente, una matrice con una sola colonna, cioè di dimensione m × 1 è un vettore colonna.
Nell’operazione di moltiplicazione questi due oggetti si comportano in modo differente.
Generalmente, per prodotto di una matrice per un vettore si intende il prodotto di
matrici Av, dove A è una matrice m × n e v è un vettore colonna n × 1. Il risultato di
questa operazione è un altro vettore colonna di dimensione m × 1.

101
Bibliografia

[1] Eigen Tutorial.

[2] PARDISO Solver User’s Guide.

[3] SPOOLES 2.2 : SParse Object Oriented Linear Equations Solver.

[4] Uf sparse matrix collection.

[5] E. Anderson, Z. Bai, C. Bischof, S. Blackford, J. Demmel, J. Dongarra, J. Du Croz,


A. Greenbaum, S. Hammarling, A. Mckenney, and D. Sorensen. LAPACK Users’
Guide (Software, Environments and Tools). Society for Industrial Mathematics, 3
edition, January 1987.

[6] Klaus-Jurgen Bathe. Finite Element Procedures. Klaus-Jurgen Bathe, February 2007.

[7] T. Davis. User Guide for CHOLMOD: a sparse Cholesky factorization and
modification package, 2008.

[8] T. Davis. UMFPACK Version 5.4.0 User Guide, 2009.

[9] Timothy A. Davis. Direct Methods for Sparse Linear Systems (Fundamentals of Al-
gorithms). Society for Industrial and Applied Mathematic, illustrated edition edition,
September 2006.

[10] Elizabeth D. Dolan and Jorge J. Moré. Benchmarking optimization software with
performance profiles. Mathematical Programming, 91(2):201–213, January 2002.

[11] I. Duff. The impact of high-performance computing in the solution of linear systems:
trends and problems. Journal of Computational and Applied Mathematics, 123(1-
2):515–530, November 2000.

[12] Frantisek Franek. Memory as a Programming Concept in C and C++. Cambridge


University Press, November 2003.

[13] Kris Kaspersky. Code Optimization: Effective Memory Usage. A-List Publishing,
September 2003.

102
[14] Lie-Quan Lee and Andrew Lumsdaine. Generic programming for high-performance
scientific applications. Concurrency and Computation: Practice and Experience, 17(7-
8):941–965, 2005.

[15] Richard B. Lehoucq, Danny C. Sorensen, and C. Yang. Arpack User’s Guide: Solu-
tion of Large-Scale Eigenvalue Problems With Implicityly Restorted Arnoldi Methods
(Software, Environments, Tools). Soc for Industrial & Applied Math.

[16] Alfio Quarteroni, Riccardo Sacco, and Fausto Saleri. Numerical Mathematics (Texts
in Applied Mathematics). Springer, 2nd edition, November 2006.

[17] Andrew S. Tanenbaum. Structured Computer Organization (5th Edition). Prentice


Hall, 5 edition, June 2005.

[18] T. Veldhuizen. Blitz++ User’s Guide, 2006.

[19] William von Hagen. The Definitive Guide to GCC, Second Edition. Apress, 2 edition,
August 2006.

[20] A. Willembohm. On the performance of pure and impure parallel functional programs.
Parallel Computing, 25(13-14):1723–1740, December 1999.

[21] Ralph Wiloughby. Lanczos Algorithms for Large Symmetric Eigenvalue Computations
Volume 1: Theory (Classics in Applied Mathematics). SIAM: Society for Industrial
and Applied Mathematics, 1st edition, September 2002.

103

Potrebbero piacerti anche