Design Exploration: Sviluppo telaio per vettura formula saeMarco Basilici
La relazione approfondirà maggiormente gli aspetti teorici legati al Design Exploration effettuata sulla piattaforma di Ansys Workbench, dopo aver fatto un preambolo sullo sviluppo del prodotto.
Design Exploration: Sviluppo telaio per vettura formula saeMarco Basilici
La relazione approfondirà maggiormente gli aspetti teorici legati al Design Exploration effettuata sulla piattaforma di Ansys Workbench, dopo aver fatto un preambolo sullo sviluppo del prodotto.
In questa sessione vedremo come realizzare un Data Access Layer basato su una implementazione del Repository pattern ed in grado di essere interrogabile mediante query LINQ, eventualmente delegate ad O/RM quali Entity Framework e/o NHibernate. Vedremo inoltre come fare utilizzo dei Code Contracts del FX4 per specificare "una tantum" le regole comuni a tutti i repository di un Domain Model.
Tesi di laurea triennale
Esplorare le tecniche statistiche nel data mining, comprenderne in linee generali le modalità di utilizzo in ambito aziendale ed analizzarne la diffusione in Italia.
Il presente articolo è un'introduzione ad un settore teoricamente molto esteso, di notevole importanza e con possibili margini di innovazione, ancor oggi.
Nel seguito l'autore percorre la storia degli algoritmi di clustering, ne esamina i vari punti di forza e le debolezze, nell'ottica di individuare una soluzione implementativa valida, da utilizzare in un progetto software e tenendo sempre come punto di attenzione importanti caratteristiche come le prestazioni, la scalabilità, l'affidabilità e l'esistenza di un prodotto di mercato, certificato e supportato.
The project is a simple classification problem with a data frame composed by a set of Smartphones products and their characteristics. With the use of statistical tools we defined a number of methods to correctly classify the level of the variable Price_range.
Stefano Bragaglia MSc Thesis, awarded as Best Italian thesis in AI 2009/2010Stefano Bragaglia
My MSc Thesis (only in Italian) introduces Logic Programs with Annotated Disjunction (LPADs) a Prolog's probabilistic extension, and my work on CPLINT (https://sites.google.com/a/unife.it/ml/cplint) to reason on them. My goal was to implement and test several approximated algorithms to balance speed and accuracy when solving probabilistic problems. It was awarded by the Italian Association for Artificial Intelligence (AIxIA) as the best Italian thesis in Artificial Intelligence of 2009/2010.
Seminario Basi di Dati - Architetture Distribuite - Università degli Studi di...Andrea Cannella
Seminario sulle Architetture Distribuite per le basi di dati. Presentato durante il Corso di DataBase presso l'Università degli Studi di Catania - Corso di Laurea in Informatica (Facoltà di Scienze Matematiche, Fisiche e Naturali)
(Copyleft) Andrea Cannella 2010
BisPy: un pacchetto Python per il calcolo della massima bisimulazione di graf...Francesco Andreuzzi
[Versione senza link]
Tesi triennale per il corso di laurea triennale in Ingegneria Informatica, Università degli Studi di Trieste (Dipartimento di Ingegneria e Architettura).
BisPy: un pacchetto Python per il calcolo della massima bisimulazione di graf...Francesco Andreuzzi
Tesi triennale per il corso di laurea triennale in Ingegneria Informatica, Università degli Studi di Trieste (Dipartimento di Ingegneria e Architettura).
Apache Hadoop HDFS Re-documentation taking into account both the source code of both the existing documentation available to https://hadoop.apache.org/ site. They were identified known software patterns that exist between NameNode and DataNode for the distributed file system management.
In questa sessione vedremo come realizzare un Data Access Layer basato su una implementazione del Repository pattern ed in grado di essere interrogabile mediante query LINQ, eventualmente delegate ad O/RM quali Entity Framework e/o NHibernate. Vedremo inoltre come fare utilizzo dei Code Contracts del FX4 per specificare "una tantum" le regole comuni a tutti i repository di un Domain Model.
Tesi di laurea triennale
Esplorare le tecniche statistiche nel data mining, comprenderne in linee generali le modalità di utilizzo in ambito aziendale ed analizzarne la diffusione in Italia.
Il presente articolo è un'introduzione ad un settore teoricamente molto esteso, di notevole importanza e con possibili margini di innovazione, ancor oggi.
Nel seguito l'autore percorre la storia degli algoritmi di clustering, ne esamina i vari punti di forza e le debolezze, nell'ottica di individuare una soluzione implementativa valida, da utilizzare in un progetto software e tenendo sempre come punto di attenzione importanti caratteristiche come le prestazioni, la scalabilità, l'affidabilità e l'esistenza di un prodotto di mercato, certificato e supportato.
The project is a simple classification problem with a data frame composed by a set of Smartphones products and their characteristics. With the use of statistical tools we defined a number of methods to correctly classify the level of the variable Price_range.
Stefano Bragaglia MSc Thesis, awarded as Best Italian thesis in AI 2009/2010Stefano Bragaglia
My MSc Thesis (only in Italian) introduces Logic Programs with Annotated Disjunction (LPADs) a Prolog's probabilistic extension, and my work on CPLINT (https://sites.google.com/a/unife.it/ml/cplint) to reason on them. My goal was to implement and test several approximated algorithms to balance speed and accuracy when solving probabilistic problems. It was awarded by the Italian Association for Artificial Intelligence (AIxIA) as the best Italian thesis in Artificial Intelligence of 2009/2010.
Seminario Basi di Dati - Architetture Distribuite - Università degli Studi di...Andrea Cannella
Seminario sulle Architetture Distribuite per le basi di dati. Presentato durante il Corso di DataBase presso l'Università degli Studi di Catania - Corso di Laurea in Informatica (Facoltà di Scienze Matematiche, Fisiche e Naturali)
(Copyleft) Andrea Cannella 2010
BisPy: un pacchetto Python per il calcolo della massima bisimulazione di graf...Francesco Andreuzzi
[Versione senza link]
Tesi triennale per il corso di laurea triennale in Ingegneria Informatica, Università degli Studi di Trieste (Dipartimento di Ingegneria e Architettura).
BisPy: un pacchetto Python per il calcolo della massima bisimulazione di graf...Francesco Andreuzzi
Tesi triennale per il corso di laurea triennale in Ingegneria Informatica, Università degli Studi di Trieste (Dipartimento di Ingegneria e Architettura).
Apache Hadoop HDFS Re-documentation taking into account both the source code of both the existing documentation available to https://hadoop.apache.org/ site. They were identified known software patterns that exist between NameNode and DataNode for the distributed file system management.
Metodi matematici per l’analisi di sistemi complessi
Tesi andrea cingolani
1. Facoltà di Ingegneria Informatica Automatica e Gestionale
Corso di Laurea Triennale in Ingegneria Dei Sistemi Informatici
Tesi di Laurea
Implementazione e valutazione di un algoritmo per il
partizionamento di grafi su piattaforma SPARK
Relatore: Laureando:
Prof. Leonardo Querzoni Andrea Cingolani
Matricola: 1477231
Anno accademico 2016/2017
2.
3.
4. 4
Indice
Introduzione................................................................................................................................ 5
Capitolo 1: L’algoritmo HDRF ............................................................................................... 10
Capitolo 2: Strumenti usati per l’analisi................................................................................ 13
2.1: Apache Spark................................................................................................................. 13
2.1.1: Caratteristiche di Apache Spark........................................................................... 13
2.1.2: Spark Core............................................................................................................... 14
2.1.3: Spark SQL................................................................................................................ 15
2.1.4: Spark Streaming ..................................................................................................... 15
2.1.5: MLlib........................................................................................................................ 15
2.1.6: GraphX..................................................................................................................... 16
2.4: Scala................................................................................................................................. 21
Capitolo 3: Realizzazione e implementazione del progetto............................................... 22
3.1: Acquisizione del file di testo da utilizzare come grafo............................................ 22
3.2: Creazione del grafo....................................................................................................... 22
3.3 Implementazione algoritmo.......................................................................................... 24
Capitolo 4 – Validazione e Test.............................................................................................. 33
Capitolo 5: Conclusioni ........................................................................................................... 34
Bibliografia................................................................................................................................ 35
5. 5
Introduzione
Negli ultimi anni abbiamo assistito a un'enorme crescita di produzioni di
informazioni.
Alcune società come IBM stimano che "ogni giorno vengono creati 2,5
quintilioni di byte di dati", pari al 90% dei dati nel mondo creati negli
ultimi due anni.
Di fronte a questa crescita, i ricercatori del mondo accademico e
industriale si sono concentrati per la progettazione di nuovi, efficienti,
approcci per analisi parallela dei dati in grado di resistere al diluvio di dati
atteso nei prossimi anni. Data la proliferazione di dati che possono essere
rappresentati come grafici di vertici interconnessi, un paradigma di
calcolo basato sul grafico fornisce un'astrazione piacevole, adatta, per
eseguire calcoli su di esso.
Grandi quantità di dati, in particolare grafici a invarianza di scala o grafici
power-law rientrano in questo paradigma. Inoltre, il calcolo basato sui
grafici trova applicazione in molti campi diversi e importanti come i social
network, la biologia, la chimica e la sicurezza informatica. Un problema
chiave nel calcolo dei grafici è che è spesso difficile scalare con l’aumento
delle dimensioni dei dati in input poiché i grafici non sono facilmente
partizionabili in sotto grafi indipendenti che possono essere calcolati in
parallelo.
Per poter lavorare su dataset di grandi dimensioni, framework
Graphcomputing (DGC) distribuiti (come GraphLab o Pregel) separa
forzatamente il grafo di input posizionando i suoi elementi costituenti,
siano essi vertici o bordi, in partizioni distinte, una per ciascuna risorsa di
6. 6
calcolo disponibile. Durante la fase di partizionamento, gli elementi di dati
che condividono le connessioni con altri elementi già posizionati in altre
partizioni risultano avere connessioni remote tra di loro. Poiché queste
partizioni vengono solitamente posizionate su macchine diverse, ciò può
comportare una rete non necessaria o eccessiva e costi di calcolo.
Per risolvere questo problema, frequentemente la tecnica utilizzata è
quella di creare e posizionare localmente delle repliche di dati connessi in
remoto tra queste partizioni. Mentre questo riduce il costo di accesso, gli
elementi di dati replicati devono essere sincronizzati durante il calcolo in
modo da evitare che gli stati di replica divergano e generino risultati di
calcolo privi di significato. Questa sincronizzazione può ostacolare in
modo significativo le prestazioni poiché costringe le repliche a coordinare
e scambiare dati più volte durante il calcolo.
Il modo in cui il set di dati di input è partizionato ha un grande impatto
sulle prestazioni del calcolo del grafico. Una strategia di partizionamento
ingenua può finire per replicare una grande parte degli elementi di input
su diverse partizioni, intralciando gravemente le prestazioni inducendo
un over head di sincronizzazione delle repliche di grandi dimensioni
durante la fase di calcolo. Inoltre, la fase di partizionamento dovrebbe
produrre partizioni bilanciate in modo uniforme (cioè partizioni con
dimensioni simili) per evitare possibili distorsioni del carico in un cluster
di macchine su cui i dati sono partizionati. Diversi approcci recenti hanno
esaminato questo problema. Qui focalizziamo la nostra attenzione su
algoritmi di partizionamento grafico, ovvero gli algoritmi che
partizionano gli elementi in entrata uno alla volta sulla base delle sole
proprietà dell'elemento corrente e su assegnazioni precedenti alle
7. 7
partizioni (nessuna conoscenza globale sul grafico di input). Inoltre, questi
algoritmi sono solitamente one-pass, cioè si astengono dal modificare
l'assegnazione di un elemento di dati ad una partizione una volta fatto
questo. Questi algoritmi sono i candidati ideali nelle impostazioni in cui le
dimensioni dei dati di input e i vincoli sulle risorse disponibili limitano il
tipo di soluzioni che può essere impiegato.
Altre caratteristiche dei dati di input svolgono anche un ruolo importante
nel partizionamento. È stato dimostrato che gli algoritmi vertex-cut sono
l'approccio migliore per gestire grafici di input caratterizzati da
distribuzioni di gradi di power-law. Questo lavoro precedente ha anche
delineato chiaramente l'importante ruolo svolto dai nodi ad alta
gradazione dal punto di vista della qualità del partizionamento. Tuttavia,
pochi algoritmi tengono conto di questo aspetto. Comprensibilmente,
questo è un problema complesso da risolvere per gli approcci basati sul
flusso a causa della loro natura a un solo passaggio. In questo progetto,
facciamo leva sull'idea che un algoritmo di partizionamento dovrebbe fare
del suo meglio per tagliare, cioè replicare, vertici con grado elevato. In
particolare, introduciamo High Degree (are) Replicated First (HDRF), un
algoritmo di partizionamento grafico basato su un avido vertex-cut che
sfrutta le informazioni sui gradi dei vertici. HDRF è caratterizzato dalle
seguenti proprietà desiderabili:
8. 8
Emette partizioni con il più piccolo fattore di replicazione medio tra tutte
le soluzioni concorrenti quando vengono applicate grafici power-law
mentre fornisce un bilanciamento del carico quasi perfetto.
Il primo è ottenuto replicando avidamente i vertici con gradi maggiori,
mentre quest'ultimo è fornito da un termine di bilanciamento
parametrizzabile il cui impatto può essere regolato per adattare il
comportamento dell'algoritmo a qualsiasi ordine di input dei dati. Da un
lato, ridurre il fattore di replicazione medio è importante per ridurre il
costo della larghezza di banda della rete, l'utilizzo della memoria e il
sovraccarico della sincronizzazione delle repliche a tempo di calcolo.
Un'equa distribuzione del carico sulle partizioni, d'altra parte, consente un
utilizzo più efficiente delle risorse di calcolo disponibili. HDRF tiene conto
9. 9
di entrambi questi aspetti in modo integrato, riducendo significativamente
il tempo necessario per eseguire calcoli su grafici su larga scala. Nei
capitoli successivi spiegheremo cosa è più nel dettaglio l’algoritmo HDRF,
cosa abbiamo usato per implementarlo, e infine tutto il procedimento
svolto.
10. 10
Capitolo 1: L’algoritmo HDRF
L’algoritmo HDRF è un algoritmo adatto ai grafi power-law. Due
informatici hanno dimostrato che solo alcuni vertici di grado alto vengono
rimossi da un grafo power-law, e vengono trasformati in un gruppo di
cluster isolati. Inoltre nei grafici power-law la distribuzione dei coefficienti
di cluster diminuisce con l’aumento del grado dei vertici. Ciò implica che
i vertici a basso grado appartengono ai sottogruppi molto densi e quei
sotto-grafi sono collegati tra loro attraverso vertici di grado alto. Questo
schema di partizionamento sfrutta queste proprietà concentrandosi sulle
località dei vertici di basso grado. In particolare, cerca di posizionare
ciascun elemento fortemente connesso con i vertici di basso grado in una
singola partizione, tagliando i vertici di alto grado e replicandoli su un
gran numero di partizioni. Poiché il numero dei vertici di alto grado nei
grafici power-law è molto basso, incoraggiare la replica per questi vertici
porta ad una riduzione complessiva del fattore di replica. In concreto,
quando HDRF crea una replica, lo fa per il vertice con massimo grado.
Tuttavia, ottenere gradi di vertice per un grafico che viene consumato in
streaming non è banale. Per evitare il sovraccarico di un passo di pre-
elaborazione (dove il grafo di input viene scandito per calcolare i gradi
esatti del vertice) è possibile mantenere una tabella con gradi parziali dei
vertici che viene aggiornata mentre l’input viene analizzato. Poiché ogni
nuovo bordo viene considerato nell’input, i valori di gradazione dei vertici
corrispondenti vengono aggiornati nella tabella. I valori di grado parziale
raccolti in fare di run-time sono di solito un buon indicatore per il grado
11. 11
di un vertice poiché è più probabile che un bordo osservato appartiene ad
un vertice di alto grado, piuttosto che a uno di basso grado.
Matematicamente quando elaborano i vertici vi e vj di bordo appartenente
ad E (bordi), l’algoritmo HDRF recupera i loro gradi parziali e li
incrementa di uno. Sia (vi) il grado parziali di vi e (vj) quelli di vj. I valori
di grado vengono normalizzati in modo da arrivare ad uno:
Per quanto riguarda l’elogio euristico, l’algoritmo HDRF calcola un
punteggio CHDRF (vi, vj, p) per tutte le partizioni p appartenenti a P e poi
assegna e alla partizione p* che massimizza CHDRF. Il punteggio per ogni
partizione p appartenente a P è definito come segue:
Il parametro λ consente di controllare l’entità dello squilibrio di
dimensione delle partizioni nel calcolo dei punteggi. È stato introdotto
perché equilibra partizioni particolarmente squilibrate. Per vedere questo
problema nota che CBAL
greedy
(p) è sempre < 1 mentre CREP
greedy
e CREP
HDRF
sono
sempre o 0 o > 1. Per questa ragione, il termine di equilibrio CBAL va messo
o nell’algoritmo greedy o quando 0 < λ <= 1 è usato solo per scegliere tra
partizione che presentano lo stesso valore per il termine di replica CREP,
12. 12
rompendo così la simmetria. Tuttavia, questo potrebbe non essere
sufficiente per garantire il saldo del carico. Ad esempio, se il flusso di bordi
è ordinato secondo un ordine di visita sul grafo (ad esempio la prima
ricerca di larghezza e profondità), quando si elaborano i vertici vi e vj di
bordo e appartenente ad E, c’è sempre una singola partizione p* con
CREP
Greedy (vi, vj, p*) >= 1 (o CHDRF
REP(vi,vj,p) > 1) e tutte le altre partizioni p
appartenente a P se p =! p* hanno CREP
Greedy (vi,vj,p*) = 0 (o CHDRF
REP(vi,vj,p) =
0). In questo caso il termine di equilibrio è inutile in quanto non esiste una
simmetria per rompere l’euristica, finisce per mettere tutti i bordi in una singola
partizione p*. Questo problema può essere risolto impostando un valore λ > 1.
In questa valutazione si è studiato l’andamento del fattore di replicazione
e il bilanciamento del carico variando λ. Inoltre, da notare quando λ -> ∞
l’algoritmo somiglia a un euristico casuale, dove le osservazioni passate
sono ignorate e contano solo partizioni di uguale dimensione. HDRF può
essere eseguito come un singolo processo o in istanze parallele per
aumentare la velocità della fase di partizionamento. Come per i greedy,
HDRF ha anche bisogno di uno stato da condividere tra istanze parallele
durante la partizione.
13. 13
Capitolo 2: Strumenti usati per l’analisi
Il progetto è stato realizzato con il framework Apache Spark su sistema
Linux Lubuntu 17.04 programmato in Scala. Nei prossimi paragrafi viene
spiegato il funzionamento sia di Scala che di Spark, descrivendo alcune
delle librerie da noi usate di Spark.
2.1: Apache Spark
Apache Spark è una piattaforma di calcolo a cluster costruita per essere
veloce e multiuso. Il suo obiettivo è di eseguire più attività possibile su un
unico sistema dove prima questo non era possibile. Alcune di esse sono:
• Streaming di dati
• Query interattive
• Algoritmi iterativi
• Applicazioni di batch
Supportando tutti questi carichi di lavoro su un'unica macchina, Spark
rende semplice e poco costoso la combinazione di più processi.
2.1.1: Caratteristiche di Apache Spark
Nel suo core, Spark offre un motore di computazione che viene sfruttato
da tutti gli altri suoi componenti. Questo significa che un’ottimizzazione
al core di Spark si manifesta su tutte le applicazioni che usato questo
framework. Il core di Spark si occupa di:
• Scheduling
14. 14
• Distribuzione
• Controllo dell’esecuzione dell’applicazione dell’utente
A sfruttare il core di ci sono vari tools:
• Spark SQL
• Spark Streaming
• MLlib
• GraphX
Essi si integrano tra di loro, facendo sì che un’applicazione possa usare
tutti i componenti di Spark contemporaneamente. Tutti i moduli di Spark
sono di seguito descritti.
2.1.2: Spark Core
Lo Spark Core contiene le funzionalità base di Spark, includendo:
• Componenti della pianificazione
• Management di memoria
• Recupero dei guasti
• Interazione con i sistemi di memorizzazione
15. 15
Lo Spark Core include le API che definiscono gli RDD (Resilient
Distributed Datasets) che rappresentano dati distribuiti sul cluster e su cui
vengono svolte operazioni di trasformazione per poi recuperare i dati
modificati
2.1.3: Spark SQL
Spark SQL permette di fare interrogazioni su dati strutturati e semi
strutturati usando HiveQL, una variante del linguaggio SQL. Si possono
interrogare file JSON, file di testo e qualunque altro formato supportato
da Hive.
2.1.4: Spark Streaming
Spark Streaming permette di analizzare i flussi di dati in tempo reale come
i log di errori o un flusso di tweet. Un esempio di utilizzo è l’analisi di file
di log generati da server web, come:
• Filtraggio di particolari indirizzi IP in funzione della sicurezza
• L’analisi degli aggiornamenti di stato degli utenti su una
piattaforma social
2.1.5: MLlib
Spark nasce con una libreria che contiene le più comuni funzionalità di
Machine Learning (ML), chiamata MLlib.
Essa offre vari algoritmi di machine learning che includono:
• Classificazione
16. 16
• Regressione
• Clustering
• Collaborative filtering
• Modelli di valutazione e acquisizione dati
Questi algoritmi sono eseguiti sugli RDD.
Gli algoritmi di MLlib vengono eseguiti in parallelo, ed hanno
performance ottime sui cluster. La libreria è valida quando si usano questi
algoritmi su dataset molto grandi.
2.1.6: GraphX
GraphX è una libreria usata per l’analisi dei grafi talmente grandi che non
potrebbero essere analizzati da una singola macchina (ad esempio i grafi
dei social network). Un grafo è una collezione di nodi (o vertici) collegati
da archi. Ad esempio i nodi possono essere le persone e gli archi le
amicizie. La libreria offre algoritmi come PageRank (per misurare
l’importanza di ogni nodo di un grafo), il calcolo delle componenti
connesse, il calcolo dei triangoli e molto altro.
2.1.6.1 Partition Strategy
Per elaborare il grafo in uno stile distribuito, il grafo ha bisogno di essere
rappresentato in uno schema distribuito. Normalmente, ci sono due tipi
17. 17
di grafi partizionati, l’approccio vertex-cut e l’approccio edge-cut.
Spark Graphx adotta un vertex-cut per partizionare dei grafi distribuiti.
La strategia è programmata nel PartitionStrategy.scala. Guardiamo questo
file:
1. case object RandomVertexCut extends PartitionStrategy{
2. override def getPartition(src: VertexId, dst: VertexId, numParts: Part
itionID): PartitionID = {
3. math.abs((src, dst).hashCode()) % numParts
}
RandomVertexCut calcola il valore hash degli ID Vertex di origine e
destinazione, usando il modulo (da numberOfParts) come IP partizione
del bordo. I bordi partizionati nella stessa partizione di due vertici hanno
la stessa direzione.
CanonicalRandomVertexCut partiziona i bordi indipendentemente dalla
direzione
1. case object CanonicalRandomVertexCut extends PartitionStrategy {
18. 18
2. override def getPartition(src: VertexId, dst: VertexId, numParts: Part
itionID): PartitionID = {
3. if (src < dst) {
4. math.abs((src, dst).hashCode()) % numParts
5. } else {
6. math.abs((dst, src).hashCode()) % numParts
7. }
8. }
9. }
Altri due schemi di partizionamento sono EdgePartition1D e
EdgePartition2D. In EdgePartition1D, i bordi vengono assegnati alle
partizioni solo in base ai loro vertici di origine.
1. case object EdgePartition1D extends PartitionStrategy {
2. override def getPartition(src: VertexId, dst: VertexId, numParts: Part
itionID): PartitionID = {
3. val mixingPrime: VertexId = 1125899906842597L
4. (math.abs(src * mixingPrime) % numParts).toInt
5. }
6. }
Un principio molto grande (mixingPrime) viene usato per bilanciare le
partizioni. Ma questa operazione non elimina completamente il problema.
EdgePartition2D è un po’ più complesso. Usa sia il vertice di origine che
quello di destinazione per calcolare la partizione. È basato sulla matrice di
adiacenza del bordo sparso. Ecco un esempio estratto dal codice sorgente.
19. 19
Supponiamo di avere un grafo con 12 vertici che vogliamo dividere su 9
macchine. Possiamo usare la seguente rappresentazione di matrice sparsa:
Come si vede, E<v11, v1> è partizionato nel P6. Ma è anche chiaro che P0
contiene troppi bordi (molti di più di altre partizioni) che provocano uno
squilibrio di partizionamento. Così mixingPrime viene usato anche in
EdgePartition2D.
1. case object EdgePartition2D extends PartitionStrategy {
2. override def getPartition(src: VertexId, dst: VertexId, numParts: Part
itionID): PartitionID = {
3. val ceilSqrtNumParts: PartitionID = math.ceil(math.sqrt(numParts))
.toInt
4. val mixingPrime: VertexId = 1125899906842597L
5. val col: PartitionID = (math.abs(src * mixingPrime) % ceilSqrtNumP
arts).toInt
20. 20
6. val row: PartitionID = (math.abs(dst * mixingPrime) % ceilSqrtNum
Parts).toInt
7. (col * ceilSqrtNumParts + row) % numParts
8. }
9. }
Guarda questo grafico realistico.
I vertici A, B e C sono in una partizione; D, E, F sono nell’altra. I bordi sono
suddivisi in due partizioni come nel Edge Table. La tabella di
instradamento è molto utile perché registra lo stato di taglio dei vertici.
21. 21
2.4: Scala
Scala è un linguaggio di programmazione funzionale orientato ad oggetti.
Compilando un sorgente scala viene generato un bytecode eseguibile in
una Java Virtual Machine. Esso fornisce un linguaggio diretto per definire
funzioni anonime (dichiarate senza essere legate ad un nome), supporta
funzioni di ordine superiore e permette alle funzioni di essere annidate e
supporta funzioni parziali. Scala ha supporto nativo per il pattern
matching, permette di potenziale l’elaborazione di dati XML con il
supporto di espressioni regolari. Il suo nome deriva da “Scalable
Language” ovvero linguaggio scalabile. Ciò che rende Scala un linguaggio
scalabile è la fusione tra programmazione funzionale e programmazione
ad oggetti.
22. 22
Capitolo 3: Realizzazione e
implementazione del progetto
Per svolgere il progetto sono passato per diverse fasi:
1. Acquisizione del file di testo da utilizzare come grafo
2. Creazione del grafo
3. Implementazione algoritmo
a. Caso 1: Vertici non assegnati alle partizioni
b. Caso 2: Solo un vertice assegnato
c. Caso 3: Vertici assegnati, partizione comune
d. Caso 4: Vertici assegnati a due partizioni diverse
3.1: Acquisizione del file di testo da utilizzare come grafo
Il file di testo è un file scaricato da
http://files.grouplens.org/datasets/movielens/ml-10m.zip. Il file zip al suo
interno contiene vari file, tra cui ratings.dat che è il file che ho usato per
formare il grafico, questo file al suo interno contiene:
10 milioni di valutazioni fatte da 72000 utenti su 10000 film presi dal
famosissimo servizio MovieLens. I dati sono ordinati casualmente.
3.2: Creazione del grafo
Per la creazione del grafo ci sono stati vari problemi sulla formattazione
del file, inizialmente avevamo pensato di usare la classe di GraphX
GraphLoader con al suo interno il metodo:
23. 23
1. edgeListFile(SparkContext sc, String path, boolean canon
icalOrientation, int numEdgePartitions, StorageLevel edg
eStorageLevel, StorageLevel vertexStorageLevel)
ma non avrebbe funzionato, perché essa carica un grafico da un file
formattato in un elenco di bordi in cui ogni riga contiene due numeri: un
ID sorgente e un ID destinazione, noi avevamo un file formattato in una
maniera diversa.
Il file nostro era formattato in questa maniera: un ID utente, un ID film, il
voto che l’utente ha dato al film e un numero chiamato Timestamp che a
noi non serviva, tutto diviso da un t. Il modo più facile per formattare il
file in modo da creare dei bordi automaticamente era questo:
1. val edges:RDD[Edge[String]]=
2. sc.textFile("data/u1.base").map{ line =>
3. val fields= line.split("t")
4. Edge(fields(0).toLong,fields(1).toLong,fields(2)
)
5. }
Ora una volta creati i bordi il lavoro era all’80% dell’opera, mancava solo
creare il grafo.
Per creare il grafo abbiamo usato la classe Graph con al suo interno il
metodo:
1. fromEdges[VD, ED](edges: RDD[Edge[ED]],defaultValue: VD)
: Graph[VD, ED]
in questo modo:
24. 24
1. val graph: Graph[Any,String] =Graph.fromEdges(edges,"def
aultProperty")
Il grafico era creato, per controllare che bordi e vertici erano stati creati
abbiamo usato graph.numEdges e graph.numVertices in modo da capire
anche quanti erano sia i bordi che i vertici.
3.3 Implementazione algoritmo
Per l’implementazione dell’algoritmo ho usato la classe PartitionStrategy
di GraphX e l’ho modificata. Nel Main ho creato il valore partition che è
un PartitionID ed ho usato partitionBy in questo modo:
1. val partition= graph.partitionBy(HDRF,numPartition)
Mentre in HDRF.scala sono andato ad aggiungere l’estensione
PartitionStrategy, qui dentro ho implementato tutto l’algoritmo.
L’implementazione dell’algoritmo ha seguito questi 4 casi:
a) Caso 1: Se nessuno dei due vertici di e è stato mai inserito in qualche
partizione
Scelgo la partizione più scarica e li assegno a quella
b) Caso 2: Se uno dei due vertici è già presente in almeno una partizione
25. 25
Scelgo la partizione più scarica tra quelle in cui è presente almeno un
vertice e ci replico l’altro vertice
c)Caso 3: Entrambi i vertici sono presenti in diverse partizioni ed esiste
un’intersezione dei set non nulla (cioè esiste almeno una partizione che li
contiene entrambi)
Scelto nell’intersezione dei set la partizione più scarica
d) Caso 4: Entrambi i vertici sono presenti in diverse partizioni ma
l’intersezione dei set è nulla (cioè non esiste alcuna partizione che li
contiene entrambi)
Scelgo tra le partizioni a cui è assegnato uno dei due, in quella più scarica
ci copio l’altro vertice
26. 26
Come ultima cosa, una volta partizionato il grafico ho calcolato il fattore
di replicazione medio e la deviazione standard relativa.
Questa è la porzione di codice di come ho implementato il tutto:
1. package app
2.
3.
4. import org.apache.spark.graphx._
5.
6. import scala.collection.concurrent.TrieMap
7.
8. object HDRF extends PartitionStrategy{
9. private var init=0; //lo puoi usare per controllare una fase di inizializzaz
ione che viene eseguita solo la prima volta
10.
11. private var partitionsLoad:Array[Long] = Array.empty[Long] //carico (n
umero di archi) di ogni partizione
12. private val vertexIdListPartitions: TrieMap[Long, List[AnyVal]] = Trie
Map.empty[Long,List[AnyVal]] //lista di partizioni associate a ogni verti
ce
13. private val vertexIdEdges: TrieMap[Long, Long] = TrieMap() //grado di
ogni vertice
14.
15. private var edges = 0
16.
17. override def getPartition(src:VertexId, dst:VertexId, numParts:Int): Parti
tionID ={
18. var valoreMax:Long =Int.MaxValue
19. var partScarica:Int = -1
20. if(init==0){
21. init=1
22. partitionsLoad=Array.fill[Long](numParts)(0)
23.
27. 27
24. }
25.
26.
27. //AGGIORNA IL GRADO CONOSCIUTO DEI VERTICI src E dst NEL
LA VARIABILE vertexIdEdges
28.
29. vertexIdEdges.update(src,+1)
30. vertexIdEdges.update(dst,+1)
31.
32. //PARTIZIONA IL GRAFO
33. if((!vertexIdListPartitions.contains(src))&&(!vertexIdListPartitions.con
tains(dst))){
34. //NESSUNO DEI DUE VERTICI E' STATO MAI INSERITO IN QUAL
CHE PARTIZIONE
35. //SCELGO LA PARTZIIONE PIU' SCARICA E LI ASSEGNO A QUEL
LA
36. for(c<-0 until numParts){
37. if(partitionsLoad(c)<valoreMax){
38. valoreMax=partitionsLoad(c)
39. partScarica=c
40. }
41. }
42. if(partScarica != -1) {
43. partitionsLoad(partScarica)=partitionsLoad(partScarica)+1
44. vertexIdListPartitions.put(src, List(partScarica))
45. vertexIdListPartitions.put(dst, List(partScarica))
46. //vertexIdListPartitions.update(src, vertexIdListPartitions.getOrElse(
src, List()) ++ List(partScarica))
47. //vertexIdListPartitions.update(dst, vertexIdListPartitions.getOrElse(
dst, List()) ++ List(partScarica))
48. }
49.
50. }else if((vertexIdListPartitions.contains(src) &&(!vertexIdListPartitions
.contains(dst)))||((!vertexIdListPartitions.contains(src))&& vertexIdListP
artitions.contains(dst))){
51. //UNO SOLO DEI DUE VERTICI E' GIA' PRESENTE IN ALMENO U
NA PARTIZIONE
52. if(vertexIdListPartitions.contains(src) &&(!vertexIdListPartitions.cont
ains(dst))){
53. //SI TRATTA DI src
28. 28
54. //SCELGO LA PARTIZIONE PIU' SCARICA TRA QUELLE IN CUI
E' PRESENTE src E CI REPLICO dst
55. for(c<- 0 until numParts){
56. if(partitionsLoad(c)<valoreMax){
57. if(vertexIdListPartitions(src).contains(c)) {
58. valoreMax = partitionsLoad(c)
59. partScarica = c
60. }
61. }
62. }
63. if(partScarica != -1) {
64. partitionsLoad(partScarica) = partitionsLoad(partScarica) + 1
65. vertexIdListPartitions.put(dst, List(partScarica))
66. //vertexIdListPartitions.update(dst, vertexIdListPartitions.getOrEls
e(dst, List()) ++ List(partScarica))
67. }
68.
69. }else{
70. //SI TRATTA DI dst
71. //SCELGO LA PARTZIIONE PIU' SCARICA TRA QUELLE IN CUI
E' PRESENTE dst E CI REPLICO src
72.
73. for(c<- 0 until numParts){
74. if(partitionsLoad(c)<valoreMax){
75. if(vertexIdListPartitions(dst).contains(c)) {
76. valoreMax = partitionsLoad(c)
77. partScarica = c
78. }
79. }
80. }
81. if(partScarica != -1) {
82. partitionsLoad(partScarica) = partitionsLoad(partScarica) + 1
83. vertexIdListPartitions.put(src, List(partScarica))
84. //vertexIdListPartitions.update(src, vertexIdListPartitions.getOrEls
e(src, List()) ++ List(partScarica))
85. }
86.
87. }
88. }else if(vertexIdListPartitions(src).intersect(vertexIdListPartitions(dst))
.nonEmpty){
29. 29
89. //ENTRAMBI I VERTICI SONO PRESENTI IN DIVERSE PARTIZION
I ED ESISTE UNA INTERSEZIONE DEI SET NON NULLA (CIOE' ESIST
E ALMENO UNA PARTIZIONE CHE LI CONTIENE ENTRAMBI)
90. //SCELGO NELL'INTERSEZIONE DEI SET LA PARTIZIONE PIU' S
CARICA
91. for(c<- 0 until numParts){
92. if (partitionsLoad(c) < valoreMax) {
93. if (vertexIdListPartitions(src).contains(c) && vertexIdListPartitions
(dst).contains(c)) {
94. valoreMax = partitionsLoad(c)
95. partScarica = c
96. }
97. }
98. }
99. if(partScarica != -1) {
100. partitionsLoad(partScarica) = partitionsLoad(partScarica) + 1
101. }
102.
103. }else {
104. //ENTRAMBI I VERTICI SONO PRESENTI IN DIVERSE PART
IZIONI MA L'INTERSEZIONE DEI SET E' NULLA (CIOE' NON ESISTE
ALCUNA PARTIZIONE CHE LI CONTIENE ENTRAMBI)
105. if(vertexIdEdges(src) >= vertexIdEdges(dst)){
106. //SCELGO TRA LE PARTIZIONI A CUI E' ASSEGNATO dst
QUELLA PIU' SCARICA E CI COPIO src
107.
108. for(c<-0 until numParts){
109. if(partitionsLoad(c)<valoreMax){
110. if(vertexIdListPartitions(dst).contains(c)) {
111. valoreMax = partitionsLoad(c)
112. partScarica = c
113. }
114. }
115. }
116. if(partScarica != -1) {
117. partitionsLoad(partScarica) = partitionsLoad(partScarica) + 1
118. vertexIdListPartitions.update(src, vertexIdListPartitions(src).
union(List(partScarica)))
119. //vertexIdListPartitions.update(src, vertexIdListPartitions.get
OrElse(src, List()) ++ List(partScarica))
30. 30
120.
121. }
122.
123. }else{
124. //SCELGO TRA LE PARTIZIONI A CUI E' ASSEGNATO src
QUELLA PIU' SCARICA E CI COPIO dst
125.
126. for(c<-0 until numParts){
127. if(partitionsLoad(c)<valoreMax){
128. if(vertexIdListPartitions(src).contains(c)) {
129. valoreMax = partitionsLoad(c)
130. partScarica = c
131. }
132. }
133. }
134. if(partScarica != -1) {
135. partitionsLoad(partScarica) = partitionsLoad(partScarica) + 1
136. vertexIdListPartitions.update(dst, vertexIdListPartitions(dst)
.union(List(partScarica)))
137. //vertexIdListPartitions.update(dst, vertexIdListPartitions.ge
tOrElse(dst, List()) ++ List(partScarica))
138. }
139. }
140.
141. }
142.
143. edges=edges+1
144. if(edges==10000054){
145. //Calcolo il fattore di replicazione medio dei vertici
146. //vertexIdListPartitions.foreach(println)
147. var fattore_replicazione:Double = 0
148. for ((_,v) <- vertexIdListPartitions) fattore_replicazione += v.siz
e
149. fattore_replicazione /= vertexIdListPartitions.size
150. //Calcolo il carico medio delle partizioni
151. var carico_medio:Double = 0
152. for (l <- partitionsLoad) carico_medio += l
153. //Calcolo la relativa devizaione standard
154. var tmp:Double = 0
31. 31
155. for (l <- partitionsLoad) tmp += scala.math.pow(l - carico_medi
o, 2)
156. tmp /= numParts-1
157. val stdev = scala.math.sqrt(tmp)
158. //Calcolo la devizione standard relativa
159. val rsd = 100 * stdev / carico_medio
160. println("n ---* RISULTATI DEL PARTIZIONAMENTO *---
n")
161. println("tFattore di replicazione medio: " + fattore_replicazion
e)
162. println("tDeviazione standard relativa: " + rsd)
163. }
164. partScarica
165. }
166. }
Di seguito c’è anche il Main:
1. package app
2.
3. import org.apache.spark.graphx._
4. import org.apache.spark._
5. import org.apache.spark.rdd.RDD
6.
7. object Main{
8.
9.
10. def main(args: Array[String]) {
11.
12. val sc = new SparkContext(new SparkConf().setMaster("local").setApp
Name("HDRF"))
13.
14. // mostra solo i log in caso di errore
32. 32
15. sc.setLogLevel("ERROR")
16.
17. //modifico il file di testo preso in ingresso
18. val edges:RDD[Edge[String]]=
19. sc.textFile("data/ratings.dat").map{ line =>
20. val fields= line.split("::")
21. Edge(fields(0).toLong,fields(1).toLong,fields(2))
22. }
23.
24. val graph: Graph[Any,String] =Graph.fromEdges(edges,"defaultPrope
rty")
25. println("Benvenuto nel partizionatore HDRF:")
26. println("Il file scelto è: ratings.dat")
27. println("Esso contiene:")
28. println("Un numero di vertici pari a: "+ graph.numVertices)
29. println("e un numero di nodi pari a: "+ graph.numEdges)
30. println()
31. println("Partizionamento in corso...")
32. println("...")
33. println("...")
34. val result=graph.partitionBy(HDRF,4)
35.
36. result.edges.count()
37. }
38. }
33. 33
Capitolo 4 – Validazione e Test
Per i test abbiamo usato IntelliJIDEA sul mio PC con Linux Lubuntu e il
risultato è il seguente:
1. Benvenuto nel partizionatore HDRF:
2. Il file scelto è : ratings.dat
3. Esso contiene:
4. Un numero di vertici pari a: 70155
5. e un numero di nodi pari a: 10000054
6.
7. Partizionamento in corso...
8. ...
9. ...
10.
11. ---* RISULTATI DEL PARTIZIONAMENTO *---
12.
13. Fattore di replicazione medio: 2.6129855320362054
14. Deviazione standard relativa: 88.19171036899517
34. 34
Capitolo 5: Conclusioni
• HDRF è un algoritmo di partizionamento grafico dei vertici a
passaggio singolo.
• Mi sono basato su un approccio vertex-cut greedy che sfrutta
informazioni sui gradi dei vertici
• In questa tesi forniamo un’analisi teorica e pratica dell’algoritmo
HDRF con un limite superiore del caso medio per il fattore di
replicazione dei vertici.
• Gli esperimenti studiati mostrano che:
o HDRF fornisce il fattore di replica più piccola e vicina al
bilanciamento di carico ottimale
o HDRF riduce in modo significativo il tempo necessario per
eseguire calcolo su grafici
35. 35
Bibliografia
[1] Apache Spark
https://spark.apache.org/docs/latest/
[2] Yuhc
Partition Strategy in GraphX – Simple Note
http://note.yuhc.me/2015/03/graphx-partition-strategy/
[3] Gianluca Tartaggia
Linguaggio di programmazione Scala
http://tesi.cab.unipd.it/40851/1/Tesina.pdf
[4] Camil Demetrescu
Dispensa programmazione funzionale
https://docs.google.com/document/d/1M1EtsCXsbIdqXLAF4dxy2ppLIBeX5Y3gdany-
MYx_9A/edit?pref=2&pli=1#
[5] Tutorialspoint
Apache Spark installation and Core Programming
https://www.tutorialspoint.com/apache_spark/apache_spark_installation.htm
https://www.tutorialspoint.com/apache_spark/apache_spark_core_programming.htm
[6] Mapr
How to get started using Apache Spark Graphx Scala
https://mapr.com/blog/how-get-started-using-apache-spark-graphx-scala/
[7] Ampcamp
Graph Analytics with Graphx