SlideShare une entreprise Scribd logo
1  sur  11
Télécharger pour lire hors ligne
RouteSharp PathFinder: Un’applicazione Microsoft F# per
   la ricerca di cammini minimi in una rete complessa
            mediante l’algoritmo di ricerca A*
                         Davide G. Monaco, Andrea Tino
                        Prof. Ing. A. Faro @ DIIEI UNICT

                                      Marzo 2011


1 Introduzione generale
RouteSharp ` un’applicazione Microsoft .NET per l’acquisizione di reti complesse e la
             e
ricerca di cammini minimi su esse. L’intera applicazione si divide in due grandi unit`:
                                                                                     a

  1. PathFinder Si tratta del core di sistema scritto in F# in grado di caricare reti
     complesse da file (tramite un microlinguaggio usato per la specifica dei nodi, delle
     connessioni e delle query) e fornire API per la ricerca dei cammini minimi.

  2. RouteSharp Si tratta di tutta l’applicazione comprensiva del core in F# e dei
     futuri moduli di interfaccia scritti in C#.

1.1 Funzionalit` implementate
               a
L’applicazione attualmente sviluppata consta del solo core in F# che mette a dispo-
sizione le seguenti funzionalit`:
                               a

  1. Definizione delle reti: E’ possibile scrivere da file la rete complessa da cari-
     care. I file (con estensione .rs denominati in maniera pi` specifica netfile) vengono
                                                              u
     scritti tramite un microlinguaggio line-based e permettono di definire i nodi e le
     connessioni della rete. E’ inoltre possibile definire le query di ricerca negli stessi
     netfile.

  2. Caricamento e traduzione: I file vengono caricati dall’applicazione e tradotti
     in strutture interne che concorreranno alla generazione dei nodi e del loro vicinato.

  3. Net traversal: E’ possibile, dalle query definite, effettuare la ricerca di cammini
     minimi all’interno della rete caricata. La ricarca avviene tramite algoritmo A*.

  4. Modalit` di interazione: L’applicazione, il core in F#, dialoga tramite riga
              a
     di comando. E’ possibile specificare diverse opzioni in esecuzione tra le quali
     anche una modalit` interattiva mediate la quale il netfile viene esaminato, le query
                        a
     ignorate, e la rete viene dunque costruita e all’utente viene infine richiesto di
     definire, di volta in volta, lo start point e l’end point del percorso da cercare.




                                           1
2 Introduzione al paradigma funzionale
La programmazione funzionale ` un paradigma di programmazione in cui la computazione
                                e
viene trattata come la valutazione di funzioni matematiche, evitando l’uso stati e dati
mutabili. Le origini della programmazione funzionale possono essere ricondotte al lambda
calcolo ed alla ricorsione.

    La programmazione funzionale pone maggior accento sulla definizione di funzioni,
rispetto ai paradigmi procedurali e imperativi, che prediligono la specifica di una se-
quenza di comandi da eseguire. In questi ultimi, i valori vengono calcolati cambiando
lo stato del programma attraverso delle assegnazioni; un programma funzionale ` im-
                                                                                  e
mutabile: i valori non vengono trovati cambiando lo stato del programma, ma costruendo
nuovi stati a partire dai precedenti.

    La differenza tra il concetto di funzione in un linguaggio imperativo ed in un linguag-
gio funzionale consiste nel fatto che nel primo una funzione pu` avere dei side-effects (o
                                                                o
effetti collaterali), mentre nel secondo no. Ci` rappresenta uno dei maggiori vantaggi
                                                 o
nell’uso del paradigma funzionale, in quanto non solo sar` molto pi` semplice verifi-
                                                              a         u
care la correttezza e la mancanza di bug del programma, ma sar` anche pi` efficace
                                                                     a          u
l’ottimizzazione dello stesso.

Impiego
Tipicamente, come tutti i linguaggi di alto livello, i linguaggi funzionali sono meno effi-
cienti in merito all’uso di CPU e memoria rispetto a linguaggi imperativi di pi` basso
                                                                                  u
livello come il C. Ad ogni modo ` possibile affermare che per programmi che effettuano
                                   e
computazioni numeriche intense, gestiscono grosse matrici o database multidimension-
ali, alcuni linguaggi funzionali riescono a raggiungere le prestazioni di poco inferiori al
C. Inoltre, l’immutabilit` dei dati, in molti casi, aumenta l’efficienza dell’esecuzione,
                          a
permettendo al compilatore di fare assunzioni che sarebbero poco sicure nel caso di lin-
guaggio imperativo.

    Si potrebbe quindi pensare che i linguaggi funzionali possano essere impiegati in
modo efficace solamente per scopi puramente accademici.
Molti linguaggi funzionali, invece, sono stati utilizzati per scopi commerciali o indus-
triali negli ultimi decenni, basti pensare ad Erlang che venne impiegato negli anni 80
dalla Ericsson per implementare sistemi di telecomunicazioni fault-tolerant, oppure F#,
che essendo incluso nell’ambiente .NET della Microsoft sta trovando impiego in ambito
commerciale.

    La decisione di implementare l’algoritmo A* in F# ` scaturita dalla volont` di ri-
                                                      e                       a
considerare la programmazione funzionale e proporre un’alternativa non presente in
letteratura.


3 L’algoritmo A*
A* (A star) ` un noto algoritmo per la ricerca su grafi che individua un percorso da
            e
un nodo iniziale ad un nodo destinazione, descritto per la prima volta nel 1968 come



                                            2
estenzione dell’algoritmo di Dijkstra.


3.1 Introduzione
A* utilizza ricerche di tipo best-first per trovare il percorso a costo minore; inoltre,
utilizzando una funzione euristica di tipo distance-plus-cost, riesce ad essere molto
performante e a determinare l’ordine in cui i nodi verranno esplorati. Tale funzione, che
chiameremo f (x), viene determinata secondo la relazione:

   f (x) = g(x) + h(x)

dove:

    - g(x) ` una funzione che tiene conto del costo di spostamento all’interno del grafo
           e
      dal nodo di partenza al nodo corrente.

    - h(x) ` una funzione di stima euristica che calcola la distanza dal nodo corrente al
            e
      nodo destinazione. Deve essere un’euristica ammissibile, ovvero una funzione che
      mai sovrastima il costo del raggiungimento della destinazione. Per questo motivo,
      in genere, soprattutto in ambito routing, la distanza considerata ` una distanza
                                                                          e
      ”in linea retta” (es. la norma in uno spazio vettoriale).

3.2 Principio
A* attraversa il grafo verso la destinazione seguendo il percorso a minor costo conosciuto,
mantenendo ordinata una priority queue di segmenti alternativi lungo la strada. Se,
quindi, in un qualsiasi punto un possibile percorso ha un costo superiore rispetto ad
un altro gi` incontrato precedentemente, l’algortmo abbandona il percorso a costo pi`
            a                                                                             u
alto in favore del segmento a costo pi` basso. Questo processo viene applicato finch´ la
                                         u                                              e
destinazione non viene raggiunta. Come accennato precedentemente, il costo preso in
esame non ` costituito unicamente dal costo del singolo spostamento dal nodo corrente
             e
al nodo vicino, bens` considera tutti gli spostamenti effettuati fino all’attuale posizione,
                      ı
g(x), e la distanza in linea retta dalla destinazione, h(x), potendo quindi valutare quanto
effettivamente ci si avvicina alla destinazione con il successivo spostamento.

3.3 Funzionamento
Iniziando dal nodo di partenza, come precedentemente accennato, viene mantenuta una
priority queue, nota convenzionalmente come open set. Il nodo con f-score, valore
di f (x), minore all’interno della coda ha maggiore priorit`. Ad ogni step, il nodo con
                                                            a
priorit` pi` alta viene rimosso dall’open set, vengono calcolati ed annotati gli f-score dei
       a u
suoi vicini e gli stessi vengono aggiunti all’open set.
L’algoritmo continua finch´ il nodo di destinazione ha un f-score minore di qualsiasi
                             e
altro nodo nell’open set, ritornando il percorso con successo, o altrimenti finch´ non vi
                                                                                   e
sono pi` nodi nell’open set, nel qual caso il percorso non esiste.
        u

3.4 Propriet`
            a
A* ` un algoritmo completo, quindi siamo sicuri che trover` sempre una soluzione, se
   e                                                       a
questa esiste. Inoltre, esso ` sia ammissibile che ottimo rispetto agli altri algoritmi
                             e

                                             3
di ricerca ammissibili: ha una stima ottimistica del costo del percorso attraverso ogni
nodo considerato. L’ottimismo consiste anche nel sapere che il vero costo del percorso
attraverso ciascun nodo verso la destinazione varr` almeno quanto vale la nostra stima.
                                                    a
Quindi la conoscenza di A* ` cruciale. Per definizione, quando l’algoritmo ha terminato
                             e
la sua ricerca avr` trovato un percorso il cui costo attuale ` pi` basso del costo stimato
                  a                                          e u
per ogni percorso attraverso tutti i nodi rimasti nell’open set, essendo sicuri di non aver
trascurato alcun percorso dal costo minore, quindi A* ` ammissibile.
                                                         e

    Se viene utilizzato un closed set per tener traccia dei nodi gi` esaminati, incremen-
                                                                    a
tando le prestazioni dell’algoritmo, la funzione euristica h(x) oltre che ammissibile deve
essere monotona, ovvero deve soddisfare la relazione:

   h(x) ≤ d(x, y) + h(y)

dove d(x, y) ` la distanza tra i nodi x e y.
             e

3.5 Complessit`
              a
La complessit` di A* dipende dalla funzione euristica utilizzata.
             a
Nel caso peggiore, il numero di nodi espansi ` esponenziale rispetto alla lunghezza della
                                             e
soluzione, ma ` polinomiale quando lo spazio di ricerca ` un albero, la destinazione ` un
              e                                          e                           e
singolo nodo e la funzione euristica h(x) soddisfa la seguente relazione:

   |h(x) − h∗ (x)| = O(log(h∗ (x)))

dove h∗ (x) ` l’euristica ottima, ovvero la reale distanza che intercorre tra il nodo desti-
            e
nazione e il nodo x; detto in altri termini, se l’errore della funzione h(x) non cresce pi`
                                                                                          u
velocemente del logaritmo dell’euristica ottima h∗ (x).


4 A* in RouteSharp
In RouteSharp l’algoritmo A* ` stato inizialmente prototipato in Perl e successivamente
                             e
implementato in F#.

4.1 Implementazione
Essendo F# un linguaggio multiparadigma focalizzato sulla programmazione funzionale,
in RouteSharp l’implementazione dell’algoritmo differisce dalla canonica, presentata in
letteratura con approccio procedurale. Sono state implementate diverse funzioni, la
maggior parte delle quali ricorsive, al fine di ricoprire tutti gli aspetti implicati nel cor-
retto funzionamento dell’algoritmo.
Di seguito viene riportato il codice delle funzioni implementate, AStar(), Scan(),
Update(), RebuildPath() e PrintPath(), accompagnato da commenti esplicativi per
ciascuna funzione.
  type public Cost = double

  type public StarNode = {
        Node : NetworkNode ;
        FCost : Cost ;
        GCost : Cost ;


                                               4
HCost : Cost ;
           Parent : NetworkNode option ;
  }
    Sono stati definiti i tipi Cost, mappato sul tipo base double, e StarNode, che in-
capsula il nodo corrente nella rete, Node di tipo NetworkNode, e associandogli i valori
di f (x), g(x) e h(x) di cui l’algoritmo ha bisogno. Viene inoltre tenuta traccia del nodo
di provenienza, Parent per poter ricostruire il percorso una volta trovato. La keyword
option denota che il campo ` opzionale e che quindi potrebbe non essere presente.
                                e
let public AStar start goal =
  let norm_start = Norm ( start , goal )

  let start1 = {
    Node = start ;
    FCost = norm_start ;
    GCost = 0.0;
    HCost = norm_start ;
    Parent = None
  }

  let goal1 = {
    Node = goal ;
    FCost = 0.0;
    GCost = 0.0;
    HCost = 0.0;
    Parent = None
  }

   let ol = [ start1 ]
   let cl = [ ]

   Scan ol cl goal1 Map . empty
    La funzione AStar() accetta in ingresso due nodi, start e goal, nel nostro caso
saranno sempre NetworkNode, calcola la distanza in linea retta tra essi, tramite la fun-
zione Norm(), e ne inizializza i relativi StarNode associati, start1 e goal1. Infine,
dopo aver inizializzato le due liste ol e cl, rispettivamente open list e closed list, e viene
invocata la funzione Scan(), passando come parametri la open list, la closed list, lo
StarNode associato al nodo destinazione ed una mappa vuota.
   let rec public Scan ol cl goal come_from =
     match ol with
     | [] -> ( cl , come_from )
     | h :: ol_tail ->
       if h . Node . Name = goal . Node . Name then
         ( cl , come_from )

        else
          let ol2 = ol_tail
          let cl2 = h :: cl

           let starnodes = List . map ( fun ( x : N e t w o r k N o d e N e i g h b o u r ) -> {
             StarNode . Node = x . Node ;
             StarNode . FCost = h . GCost + x . Weight ;
             StarNode . GCost = h . GCost + x . Weight ;


                                              5
StarNode . HCost = 0.0;
             StarNode . Parent = None
           }) h . Node . Neighbourhood

           let ( ol3 , cl3 , come_from2 ) = Update ol2 cl2 starnodes h come_from

           Scan ol3 cl3 goal come_from2
     La funzione Scan() ` una funzione ricorsiva che accetta in ingresso due liste ol e
                          e
cl, rispettivamente la open e la closed, il nodo destinazione, goal, ed una mappa che
permette di stabilire i rapporti di parentela tra i nodi, come from.
Se ol, ` una lista vuota, [], viene ritornata una tupla contenente la closed list e la
         e
mappa di nodi, (cl, come from). In caso contrario, viene estratto il nodo in testa
dalla open list, h, ovvero il nodo con f-score migliore, e vengono effettuati dei controlli.
Se h corrisponde al nodo di destinazione, viene ritornata la tupla contenente la closed
list e la mappa di nodi; in caso contrario, h viene inserito in closed list, affinch´ si abbia
                                                                                  e
memoria del fatto che tale nodo ` stato esaminato, e viene creata una lista contenente
                                   e
i suoi vicini; per aggiornare open list, closed list e mappa di nodi, viene chiamata la
funzione Update(), che torner` una tupla contenente i tre elementi richiesti, ed infine
                                a
viene effettuata la chiamata ricorsiva a Scan().

   La funzione Update() ` un po’ pi` complessa, quindi, per semplificarne la spie-
                            e          u
gazione, verr` illustrata passo passo.
             a
  let rec public Update ol cl neigh wn come_from =
    match neigh with
    | [] -> ( ol , cl , come_from )
    | h :: neigh_tail ->
      [...]
    Update() ` una funzione ricorsiva che ritorna una tupla contenente la open list, la
                e
closed list e la mappa di nodi aggiornata in base alle informazioni passate.
Accetta in ingresso ol e cl, rispettivamente open e closed list, wn, il nodo corrente,
neigh, la lista dei vicini del nodo corrente, e come from, la mappa di nodi gi` descritta
                                                                                   a
precedentemente.
Se la lista dei vicini ` vuota, la funzione ritorna la tupla, altrimenti prosegue esaminando
                       e
il primo vicino in neigh che chiameremo h.
  if List . exists ( fun x -> x . Node . Name = h . Node . Name ) cl then
    Update ol cl neigh_tail wn come_from

  else
    *(1)
   Se il nodo h ` presente in closed list passa al vicino successivo chiamando ricorsiva-
                e
mente Update(), altrimenti prosegui esaminando h.
-*(1) -
  let testG = wn . GCost + ( wn . Node . G e t W e i g h t T o N e i g h b o u r ( h . Node . Name ))

  if List . exists ( fun x -> x . Node . Name = h . Node . Name ) ol then
    *(2)

  else
    let h2 = {
       Node = h . Node ;


                                               6
FCost = testG ;
         GCost = testG ;
         HCost = h . HCost ;
         Parent = Some wn . Node
     }

     let ol2 = h2 :: ol
     let ol3 = List . sortBy ( fun x -> x . FCost ) ol2
     let come_from2 = Map . add h . Node . Name wn . Node . Name come_from

     Update ol3 cl neigh_tail wn come_from2
   Calcola il valore di g(x), testG, sul nodo h.
Se h ` presente in open list prosegui valutando ulteriori casi, altrimenti incapsula h in
     e
uno StarNode e aggiungilo alla open list. Ordina la open list per f-score e aggiorna
come from, ponendo wn come predecessore di h. Infine invoca ricorsivamente Update
con la nuova open list e lista dei nodi.
-*(2) -
  let comp_node = List . find ( fun x -> x . Node . Name = h . Node . Name ) ol

  if testG < comp_node . GCost then
    let h2 = {
      Node = h . Node ;
      FCost = testG (* h . FCost *);
      GCost = testG (* h . GCost *);
      HCost = h . HCost ;
      Parent = Some wn . Node
    }

     let replacer ( x ) =
       if x . Node . Name = h . Node . Name then
         h2
       else
         x

     let ol2 = List . map ( fun x -> ( replacer x )) ol
     let ol3 = List . sortBy ( fun x -> x . FCost ) ol2

     let come_from2 = Map . add h . Node . Name wn . Node . Name come_from

     Update ol3 cl neigh_tail wn come_from2

  else
    let h2 = {
       Node = h . Node ;
       FCost = testG (* h . FCost *);
       GCost = testG (* h . GCost *);
       HCost = h . HCost ;
       Parent = h . Parent
    }

     let replacer ( x ) =
     if x . Node . Name = h . Node . Name then
       h2
     else


                                           7
x

        let ol2 = List . map ( fun x -> ( replacer x )) ol
        let ol3 = List . sortBy ( fun x -> x . FCost ) ol2

        Update ol3 cl neigh_tail wn come_from
    In questo caso abbiamo trovato h nella open list. Confrontiamo il valore di g(x)
precedentemente annotato con testG. Se il valore di test ` minore, aggiorna i valori
                                                           e
in ol e come from, riordina la open list e invoca ricorsivamente Update(); altrimenti
aggiorna solamente come from e invoca sempre Update().
   let rec public RebuildPath finalpath nodemap node =
     let init lst =
       match lst with
       | [] -> [ node ]
       | _ -> lst
     try
       let par = Map . find node nodemap
       RebuildPath ( par :: ( init finalpath )) nodemap par
     with
       | :? K e y N o t F o u n d E x c e p t i o n -> finalpath
    La funzione RebuildPath() ` una funzione ricorsiva che ritorna il percorso.
                                  e
Accetta in ingresso il percorso da ritornare, finalpath, la mappa di nodi elaborata
durante il processamento di A*, nodemap, e il nodo corrente, node.
La ricostruzione viene fatta risalendo dalla destinazione al nodo di partenza, valutando
il nodo predecessore annotato nella nodemap.
Viene sollevata un’eccezione nel caso in cui il percorso non ` stato trovato.
                                                             e


5 Analisi comparativa con un’applicazione Prolog: PathFinder.exe vs
  Traffic.exe
Per determinare una scala di performance e un ordine di efficienza di A* scritto in F#,
` stata condotta una serie di esecuzioni diagnostiche (run delle applicazioni in contesti
e
controllati) per accertare le attivit` svolte da PathFinder nel caricare la rete e nel cercare
                                     a
il cammino minimo. Le stesse attivit` sono state condotte su Traffic, un’applicazione
                                         a
scritta in Prolog avente gli stessi obiettivi di PathFinder.

5.1 Perch` questa analisi
         e
La possibilit` di esaminare le prestazioni di PathFinder tramite un’analisi comparata
             a
con un’applicazione Prolog permette di stabilire un’ordine di grandezza circa le perfor-
mance di una’aplicazione scritta tramite un linguaggio multiparadigma (OO + Func-
tional) e un’applicazione scritta tramite un linguaggio logico. Considerando inoltre che
A* possiede, attualmente, pochissime implementazioni funzionali e multiparadigma in
letteratura, questa analisi mette anche a fuoco potenziali sviluppi di F# nel campo delle
reti complesse.

5.1.1    Limiti e considerazioni
Malgrado si tratti di un’analisi comparata condotta mediante rigidi schemi e col maggior
rigore possibile; ` necessario puntualizzare che i run controllati e i dati raccolti non
                  e

                                              8
possono essere considerati per la definizione di una precisa tabella delle performance
(dalla quale ` possibile stabilire, con certezza, quale sia l’applicazione migliore per una
             e
certa caratteristica in esame) a causa dei seguenti motivi:

   1. Numero di run: Il numero di esecuzioni controllate non ` tale da stabilire una
                                                                e
      corretta descrizione generale del comportamento delle due applicazioni.

   2. Contesto software: Il contesto software in cui le applicazioni sono state eseguite
      non rispecchiava un classico scenario di esame. Ovvero la presenza di processi in
      background di sistema e di altre applicazioni ha inquinato l’ambiente di lavoro e
      ha, pertanto, reso le grandezze in esame, dipendenti da parametri rumorosi.

   3. Contesto hardware: La macchina su cui sono state condotte le esecuzioni non
      rispecchiava una vera e propria macchina per test controllati.

Per questo motivo si considerino i risultati seguenti come il frutto di un’analisi volta a
determinare le dinamiche generali delle due applicazioni.

5.2 Strumentazione
Al fine di ottenere dati precisi per analisi in profondit` delle performance delle due
                                                            a
applicazioni, coerentemente al contesto software di base (sistema operativo), ` stata
                                                                                   e
utilizzata un’applicazione Microsoft per il profiling di una sessione di lavoro. Microsoft
Windows Performance Analysis (WPA) ` stata scelta come tool principale per l’analisi
                                           e
delle prestazioni sopratutto a fronte della sua perfetta integrazione coi sistemi Windows
(il set di tool interagisce perfettamente con le funzionalit` di sistema, rendendo la suite
                                                            a
un componente nativo di Windows). Il risultato ` la possibilit` di ottenere informazioni
                                                   e             a
davvero precise circa l’esecuzione di una qualsiasi applicazione.

5.2.1   Algoritmo di generazione delle reti complesse
Per generare le reti complesse utilizzate nelle sessioni di run, ` stato utilizzato l’algoritmo
                                                                 e
di Watts e Strogatz.

5.3 Sessione di esecuzione a reti dimensione-variata
Questa sessione di esecuzioni ha visto i due programmi competere su reti a dimensione
variabile. Lo scopo ` quello di verificare l’andamento delle prestazioni al crescere del
                     e
numero dei nodi, mantenendo costante, nei limiti del possibile, la struttura di vicinato
della rete.

5.3.1   Reti caricate
Le reti caricate dai programmi rispecchiano in tutto 11 configurazioni complesse con un
numero di nodi variabile da 50 a 8000. Tramite l’applicazione di uno stress considirevole,
al crescere del numero dei nodi, si verifica la risposta delle applicazioni all’avanzamento
di tale complessit`.
                  a

5.3.2   Risposta dei tempi di esecuzione
I tempi di esecuzione hanno avuto due differenti andamenti:



                                              9
• PathFinder: L’applicazione ha mostrato un andamento crescente dei tempi. La
     dilatazione temporale evidenziata da PathFinder mette in mostra una dipendenza
     coerente tra numero di nodi e tempi. La crescita diviene considerevole a partire
     dai 1000 nodi in su, dove il salto in ordine di grandezza determina una netta
     spaccatura nella complessit` dele operazioni da eseguire. Tuttavia, l’applicazione
                                a
     risponde bene ed in maniera controllata e predicibile.

   • Traffic: L’applicazione mostra un pattern fortemente irregolare nei tempi di es-
     ecuzione al crescere del numero dei nodi. Sono presenti inoltre forti sbalzi al
     crescere del numero dei nodi. La rete ad 8000 nodi non viene caricata al completo
     e l’applicazione sperimanta un crash non occasionale (run multipli sulla configu-
     razione a 8000 nodi mostrano che Traffic interrompe l’esecuzione a seguito dello
     stesso crash).

5.3.3   Risposta dei tempi di IO
I tempi di IO (vengono presi in esame solamente i file di condifugrazione della rete per
i due programmi) hanno avuto due differenti andamenti:

   • PathFinder: L’applicazione ha mostrato un andamento crescente a tratti dei
     tempi. La crescita dei tempi di IO, tuttavia, in rapporto ai tempi di computazione,
     avviene in maniera differente, mettendo in luce la controparte relativa ai tempi di
     ricerca del percorso ottimo (tempo di pura computazione). I dati mostrano, infatti,
     un andamento crescente delle attivit` di IO che, per`, riscontrano significativi in-
                                           a               o
     nalzamenti solamente nella transizione da certi numeri di nodi. Dunque la crescita
     ` si presente, ma non costante, tuttavia regolare. Da notare, il raggiungimento dei
     e
     7000 e 8000 nodi dove un brusco innalzamento viene rilevato. Si tratta, ed ` bene
                                                                                  e
     sottolinearlo, di un pattern comunque regolare, infatti la curva dei tempi mostra
     variazioni d’ordine e tratti di non variazione d’ordine susseguirsi regolarmente.

   • Traffic: L’applicazione mostra un pattern fortemente irregolare nei tempi di IO
     al crescere del numero dei nodi. La crescita si mantiene piuttosto regolare fino al
     raggiungimento dei 1000 nodi, oltre i quali vengono sperimentati sbalzi acuti dei
     tempi. Tale comportamento evidenzia molte irregolarit` nell’esecuzione.
                                                            a

5.3.4   Rapporto dei tempi di computazione ed IO
I tempi di IO e i tempi di pura computazione determinano una percentuale sui tempi
di esecizione totale per ambedue i programmi. L’esame di tali rapporti mette in luce
il comportamento delle due applicazioni circa le loro attivit` con il filesystem dandoci
                                                             a
la possibilit` di valutare quante parte dell’esecuzione viene materialmente spesa nelle
             a
operazioni sui file di configrazione:

   • PathFinder: L’applicazione ha mostrato un andamento quasi costante del rap-
     porto computazione/IO. Osservando l’andamento complessivo al crescere del nu-
     mero dei nodi, si nota come l’applicazione comunque mantenga sempre una per-
     centuale al di sotto del 25% dedicata alla computazione vera e propria, mentre un
     complementare 75% circa viene sempre dedicato alla gestione delle operazioni su
     file. Tale risultato mette in luce la propriet` pi` importante di PathFinder: la
                                                    a u
     lentezza crescente, al crescere della rete, ` dovuta nettamente ai tempi necessari
                                                 e
     affinch` la rete possa essere caricata e tradotta.
            e


                                          10
• Traffic: L’applicazione mette in luce una percentuale estremamente a favore dei
     tempi di computazione, segno del fatto che i tempi di accesso e modifica dei file di
     configurazione, constano di un parte davvero minima nell’esecuzione complessiva.

5.4 Sessione di esecuzione a reti struttura-variata
Questa sessione di esecuzioni ha visto i due programmi competere su reti generate sec-
ondo le seguenti distribuzioni:
   • Watts e Strogatz: La rete viene generata a partire da una topologia di base e il
     processo itera i vari nodi generando nuove connessioni con una data probabilit`.
                                                                                   a
   • Barabasi: La rete viene generata a partire da una rete triangolare. Alla rete
     vengono dunque aggiunti i vari nodi con un dato numero di connessioni. La prob-
     abilit` che tali connessioni vengano agganciate ai vari nodi ` proporzionale al loro
           a                                                      e
     vicinato.
   • Bernoulli: La rete viene generata a partire da una rete di base con tutti i nodi. Le
     connessioni vengono create secondo lo schema stocastico a tentativi di Bernoulli.
Lo scopo ` quello di verificare l’andamento delle prestazioni al crescere del connession-
          e
ismo per le varie tipologie di reti.

5.4.1   Reti caricate
Le reti caricate dai programmi rispecchiano in tutto 10 configurazioni complesse con un
numero di nodi fissato a 30. Tramite l’applicazione di uno stress considirevole, al crescere
del connessionismo delle reti, si verifica la risposta delle applicazioni all’avanzamento di
tale complessit`.
                a

5.4.2   Risultati in generale
I run hanno messo in luce un comportamento molto statico dei tempi di esecuzione
da parte di Traffic, mentre una maggiore sensibilit` viene riscontrata da PathFinder.
                                                   a
Riguardo le percentuali di effettiva computazione ed IO, troviamo sempre una grande
disparit` tra le due applicazioni, dove Traffic utilizza sempre non meno del 25% del
        a
tempo totale, in operazioni di IO.

5.5 Sessione di esecuzione a reti heuristic-aware
Questa sessione di esecuzioni ha visto i due programmi competere su reti spazialmente
collocate all’interno di un sistema di riferimento bidimensionale. I nodi hanno coordi-
nate e lo spazio normato in esame viene considerato per trovare, appunto nella norma
euclidea, l’euristica utilizzabile da A*. In tale contesto si vuole misurare l’efficienza di
A* nel raggiungere l’obiettivo quanto prima mediante l’uso delle euristiche.

5.5.1   Risultati in generale
Un notevole abbattimento dei tempi di computazione da parte di A* viene riscontrato
grazie all’attivazione del meccanismo ad euristica. I tempi di PathFinder si riducono
notevolmente e la forbice con Traffic si allarga. Traffic rimane pi` lento, considerando il
                                                               u
puro tempo di computazione (eliminando l’IO complessivo), PathFinder riesce a trovare
il cammino minimo in pochi millisecondi.

                                            11

Contenu connexe

En vedette

Grow talks presentation final
Grow talks presentation finalGrow talks presentation final
Grow talks presentation finalDealmaker Media
 
Cis 100 power point
Cis 100 power pointCis 100 power point
Cis 100 power pointTwitchKid27
 
ALLVP Annual Report 2015
ALLVP Annual Report 2015ALLVP Annual Report 2015
ALLVP Annual Report 2015ALLVP
 
2009年中国数学会学术年会与会者通讯录
2009年中国数学会学术年会与会者通讯录2009年中国数学会学术年会与会者通讯录
2009年中国数学会学术年会与会者通讯录Xu jiakon
 
Spohrer darwin woi 20151119 v2
Spohrer darwin woi 20151119 v2Spohrer darwin woi 20151119 v2
Spohrer darwin woi 20151119 v2ISSIP
 
World Heritage in Danger
World Heritage in DangerWorld Heritage in Danger
World Heritage in Dangerflyhigh3698
 
Airitibooks user guide201512
Airitibooks user guide201512Airitibooks user guide201512
Airitibooks user guide201512jack_airiti
 
ClearedJobs.Net Job Search
ClearedJobs.Net Job SearchClearedJobs.Net Job Search
ClearedJobs.Net Job SearchClearedJobs.Net
 
Civilizacao Solar
Civilizacao Solar Civilizacao Solar
Civilizacao Solar guestd2646d
 
2014 telpas reading_test_manual
2014 telpas reading_test_manual2014 telpas reading_test_manual
2014 telpas reading_test_manualLuis Acosta
 
Fraternizar e Fraternitas novembro 2012
Fraternizar e Fraternitas novembro 2012Fraternizar e Fraternitas novembro 2012
Fraternizar e Fraternitas novembro 2012scapolan
 
Internet Trading Service
Internet Trading ServiceInternet Trading Service
Internet Trading Servicekamal_jalal
 
ABC's of Talent Selection
ABC's of Talent Selection ABC's of Talent Selection
ABC's of Talent Selection Kyle Deweerdt
 

En vedette (20)

Grow talks presentation final
Grow talks presentation finalGrow talks presentation final
Grow talks presentation final
 
Cis 100 power point
Cis 100 power pointCis 100 power point
Cis 100 power point
 
ALLVP Annual Report 2015
ALLVP Annual Report 2015ALLVP Annual Report 2015
ALLVP Annual Report 2015
 
March Spug
March SpugMarch Spug
March Spug
 
2009年中国数学会学术年会与会者通讯录
2009年中国数学会学术年会与会者通讯录2009年中国数学会学术年会与会者通讯录
2009年中国数学会学术年会与会者通讯录
 
Spohrer darwin woi 20151119 v2
Spohrer darwin woi 20151119 v2Spohrer darwin woi 20151119 v2
Spohrer darwin woi 20151119 v2
 
SmartOwner Client Brochure - 2017
SmartOwner Client Brochure - 2017SmartOwner Client Brochure - 2017
SmartOwner Client Brochure - 2017
 
Hartstichting 2009
Hartstichting 2009Hartstichting 2009
Hartstichting 2009
 
World Heritage in Danger
World Heritage in DangerWorld Heritage in Danger
World Heritage in Danger
 
Airitibooks user guide201512
Airitibooks user guide201512Airitibooks user guide201512
Airitibooks user guide201512
 
ClearedJobs.Net Job Search
ClearedJobs.Net Job SearchClearedJobs.Net Job Search
ClearedJobs.Net Job Search
 
Civilizacao Solar
Civilizacao Solar Civilizacao Solar
Civilizacao Solar
 
2014 telpas reading_test_manual
2014 telpas reading_test_manual2014 telpas reading_test_manual
2014 telpas reading_test_manual
 
Fraternizar e Fraternitas novembro 2012
Fraternizar e Fraternitas novembro 2012Fraternizar e Fraternitas novembro 2012
Fraternizar e Fraternitas novembro 2012
 
Oriola-KD:n yritysesite 2016
Oriola-KD:n yritysesite 2016Oriola-KD:n yritysesite 2016
Oriola-KD:n yritysesite 2016
 
Internet Trading Service
Internet Trading ServiceInternet Trading Service
Internet Trading Service
 
Otaku
OtakuOtaku
Otaku
 
ABC's of Talent Selection
ABC's of Talent Selection ABC's of Talent Selection
ABC's of Talent Selection
 
Tales2 go
Tales2 goTales2 go
Tales2 go
 
ClearedJobs.Net Puzzle
ClearedJobs.Net PuzzleClearedJobs.Net Puzzle
ClearedJobs.Net Puzzle
 

Similaire à Microsoft .NET F# Implementation of A* search algorithm

Introduzione alle Self Driving Car
Introduzione alle Self Driving CarIntroduzione alle Self Driving Car
Introduzione alle Self Driving CarVincenzo Dentamaro
 
Lezione InternetWorking: il routing
Lezione InternetWorking: il routingLezione InternetWorking: il routing
Lezione InternetWorking: il routingLuca Matteo Ruberto
 
8 Routing
8 Routing8 Routing
8 Routingacapone
 
Programmazione funzionale e Stream in Java
Programmazione funzionale e Stream in JavaProgrammazione funzionale e Stream in Java
Programmazione funzionale e Stream in JavaCristina Attori
 
Routing: trattazione dei protocolli RIP, OSPF e BGP
Routing: trattazione dei protocolli RIP, OSPF e BGPRouting: trattazione dei protocolli RIP, OSPF e BGP
Routing: trattazione dei protocolli RIP, OSPF e BGPLorenzo Sfarra
 
Semi-Active Replication Protocol
Semi-Active Replication ProtocolSemi-Active Replication Protocol
Semi-Active Replication ProtocolPaolo Maresca
 
Extended Summary of Optimized Design of a Human Intranet Network
Extended Summary of Optimized Design of a Human Intranet NetworkExtended Summary of Optimized Design of a Human Intranet Network
Extended Summary of Optimized Design of a Human Intranet NetworkOlesiaRonzon
 
Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...
Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...
Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...Pieredoardo Gabutti
 
Modellazione geometrica generativa: un approccio algoritmico
Modellazione geometrica generativa: un approccio algoritmicoModellazione geometrica generativa: un approccio algoritmico
Modellazione geometrica generativa: un approccio algoritmicoAntonio Turiello
 
Simulatore Grafico Per Reti Ottiche A Pacchetto
Simulatore Grafico Per Reti Ottiche A PacchettoSimulatore Grafico Per Reti Ottiche A Pacchetto
Simulatore Grafico Per Reti Ottiche A PacchettoFedele Mantuano
 
Summary of "NebulOS: A Big Data framework for astrophysics"
Summary of "NebulOS: A Big Data framework for astrophysics"Summary of "NebulOS: A Big Data framework for astrophysics"
Summary of "NebulOS: A Big Data framework for astrophysics"MarziaPaschini
 
Un metodo di progettazione di reti locali con esigenze di qualità del servizio
Un metodo di progettazione di reti locali con esigenze di qualità del servizioUn metodo di progettazione di reti locali con esigenze di qualità del servizio
Un metodo di progettazione di reti locali con esigenze di qualità del servizioClaudio Bortone
 
Reti di computer e protocolli
Reti di computer e protocolliReti di computer e protocolli
Reti di computer e protocollifilibertodicarlo
 
Attacchi alle applicazioni basati su buffer overflow
Attacchi alle applicazioni basati su buffer overflowAttacchi alle applicazioni basati su buffer overflow
Attacchi alle applicazioni basati su buffer overflowGiacomo Antonino Fazio
 

Similaire à Microsoft .NET F# Implementation of A* search algorithm (20)

7 Sottoprogrammi
7   Sottoprogrammi7   Sottoprogrammi
7 Sottoprogrammi
 
Introduzione alle Self Driving Car
Introduzione alle Self Driving CarIntroduzione alle Self Driving Car
Introduzione alle Self Driving Car
 
Lezione InternetWorking: il routing
Lezione InternetWorking: il routingLezione InternetWorking: il routing
Lezione InternetWorking: il routing
 
Database Data Aggregator
Database Data AggregatorDatabase Data Aggregator
Database Data Aggregator
 
8 Routing
8 Routing8 Routing
8 Routing
 
Programmazione funzionale e Stream in Java
Programmazione funzionale e Stream in JavaProgrammazione funzionale e Stream in Java
Programmazione funzionale e Stream in Java
 
Tesi andrea cingolani
Tesi andrea cingolaniTesi andrea cingolani
Tesi andrea cingolani
 
Routing: trattazione dei protocolli RIP, OSPF e BGP
Routing: trattazione dei protocolli RIP, OSPF e BGPRouting: trattazione dei protocolli RIP, OSPF e BGP
Routing: trattazione dei protocolli RIP, OSPF e BGP
 
Semi-Active Replication Protocol
Semi-Active Replication ProtocolSemi-Active Replication Protocol
Semi-Active Replication Protocol
 
Extended Summary of Optimized Design of a Human Intranet Network
Extended Summary of Optimized Design of a Human Intranet NetworkExtended Summary of Optimized Design of a Human Intranet Network
Extended Summary of Optimized Design of a Human Intranet Network
 
Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...
Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...
Progetto e realizzazione di un'interfaccia web interattiva per un sistema di ...
 
Modellazione geometrica generativa: un approccio algoritmico
Modellazione geometrica generativa: un approccio algoritmicoModellazione geometrica generativa: un approccio algoritmico
Modellazione geometrica generativa: un approccio algoritmico
 
Low Level Software Security
Low Level Software SecurityLow Level Software Security
Low Level Software Security
 
Simulatore Grafico Per Reti Ottiche A Pacchetto
Simulatore Grafico Per Reti Ottiche A PacchettoSimulatore Grafico Per Reti Ottiche A Pacchetto
Simulatore Grafico Per Reti Ottiche A Pacchetto
 
Summary of "NebulOS: A Big Data framework for astrophysics"
Summary of "NebulOS: A Big Data framework for astrophysics"Summary of "NebulOS: A Big Data framework for astrophysics"
Summary of "NebulOS: A Big Data framework for astrophysics"
 
Reti
RetiReti
Reti
 
Un metodo di progettazione di reti locali con esigenze di qualità del servizio
Un metodo di progettazione di reti locali con esigenze di qualità del servizioUn metodo di progettazione di reti locali con esigenze di qualità del servizio
Un metodo di progettazione di reti locali con esigenze di qualità del servizio
 
La metodologia Top - Down - applicazione al C++
La metodologia Top - Down - applicazione al C++La metodologia Top - Down - applicazione al C++
La metodologia Top - Down - applicazione al C++
 
Reti di computer e protocolli
Reti di computer e protocolliReti di computer e protocolli
Reti di computer e protocolli
 
Attacchi alle applicazioni basati su buffer overflow
Attacchi alle applicazioni basati su buffer overflowAttacchi alle applicazioni basati su buffer overflow
Attacchi alle applicazioni basati su buffer overflow
 

Plus de Andrea Tino

Our Journey: from Waterfall to Agile to DevOps
Our Journey: from Waterfall to Agile to DevOpsOur Journey: from Waterfall to Agile to DevOps
Our Journey: from Waterfall to Agile to DevOpsAndrea Tino
 
Development & GDPR (v2)
Development & GDPR (v2)Development & GDPR (v2)
Development & GDPR (v2)Andrea Tino
 
Development & GDPR
Development & GDPRDevelopment & GDPR
Development & GDPRAndrea Tino
 
Cutting Edge on Development Methodologies in IT
Cutting Edge on Development Methodologies in ITCutting Edge on Development Methodologies in IT
Cutting Edge on Development Methodologies in ITAndrea Tino
 
An introduction to DevOps
An introduction to DevOpsAn introduction to DevOps
An introduction to DevOpsAndrea Tino
 
Continuous Everything
Continuous EverythingContinuous Everything
Continuous EverythingAndrea Tino
 
Modern Trends in UI Decoupling Design
Modern Trends in UI Decoupling DesignModern Trends in UI Decoupling Design
Modern Trends in UI Decoupling DesignAndrea Tino
 
Javascript cheatsheet
Javascript cheatsheetJavascript cheatsheet
Javascript cheatsheetAndrea Tino
 
Workshop on Cryptography - Frequency Analysis (basic)
Workshop on Cryptography - Frequency Analysis (basic)Workshop on Cryptography - Frequency Analysis (basic)
Workshop on Cryptography - Frequency Analysis (basic)Andrea Tino
 
Master Thesis - A Distributed Algorithm for Stateless Load Balancing
Master Thesis - A Distributed Algorithm for Stateless Load BalancingMaster Thesis - A Distributed Algorithm for Stateless Load Balancing
Master Thesis - A Distributed Algorithm for Stateless Load BalancingAndrea Tino
 
Modern web applications
Modern web applicationsModern web applications
Modern web applicationsAndrea Tino
 
Visual Studio Tools for Cordova
Visual Studio Tools for CordovaVisual Studio Tools for Cordova
Visual Studio Tools for CordovaAndrea Tino
 
Microsoft + Agile (light)
Microsoft + Agile (light)Microsoft + Agile (light)
Microsoft + Agile (light)Andrea Tino
 
Microsoft + Agile
Microsoft + AgileMicrosoft + Agile
Microsoft + AgileAndrea Tino
 
The World of Stylesheet Languages
The World of Stylesheet LanguagesThe World of Stylesheet Languages
The World of Stylesheet LanguagesAndrea Tino
 
How to Develop Cross-Platform Apps
How to Develop Cross-Platform AppsHow to Develop Cross-Platform Apps
How to Develop Cross-Platform AppsAndrea Tino
 
The Asynchronous Pattern (for beginners)
The Asynchronous Pattern (for beginners)The Asynchronous Pattern (for beginners)
The Asynchronous Pattern (for beginners)Andrea Tino
 
Designing an effective hybrid apps automation framework
Designing an effective hybrid apps automation frameworkDesigning an effective hybrid apps automation framework
Designing an effective hybrid apps automation frameworkAndrea Tino
 
7 tips for more effective morning SCRUM
7 tips for more effective morning SCRUM7 tips for more effective morning SCRUM
7 tips for more effective morning SCRUMAndrea Tino
 
Powerful tools for building web solutions
Powerful tools for building web solutionsPowerful tools for building web solutions
Powerful tools for building web solutionsAndrea Tino
 

Plus de Andrea Tino (20)

Our Journey: from Waterfall to Agile to DevOps
Our Journey: from Waterfall to Agile to DevOpsOur Journey: from Waterfall to Agile to DevOps
Our Journey: from Waterfall to Agile to DevOps
 
Development & GDPR (v2)
Development & GDPR (v2)Development & GDPR (v2)
Development & GDPR (v2)
 
Development & GDPR
Development & GDPRDevelopment & GDPR
Development & GDPR
 
Cutting Edge on Development Methodologies in IT
Cutting Edge on Development Methodologies in ITCutting Edge on Development Methodologies in IT
Cutting Edge on Development Methodologies in IT
 
An introduction to DevOps
An introduction to DevOpsAn introduction to DevOps
An introduction to DevOps
 
Continuous Everything
Continuous EverythingContinuous Everything
Continuous Everything
 
Modern Trends in UI Decoupling Design
Modern Trends in UI Decoupling DesignModern Trends in UI Decoupling Design
Modern Trends in UI Decoupling Design
 
Javascript cheatsheet
Javascript cheatsheetJavascript cheatsheet
Javascript cheatsheet
 
Workshop on Cryptography - Frequency Analysis (basic)
Workshop on Cryptography - Frequency Analysis (basic)Workshop on Cryptography - Frequency Analysis (basic)
Workshop on Cryptography - Frequency Analysis (basic)
 
Master Thesis - A Distributed Algorithm for Stateless Load Balancing
Master Thesis - A Distributed Algorithm for Stateless Load BalancingMaster Thesis - A Distributed Algorithm for Stateless Load Balancing
Master Thesis - A Distributed Algorithm for Stateless Load Balancing
 
Modern web applications
Modern web applicationsModern web applications
Modern web applications
 
Visual Studio Tools for Cordova
Visual Studio Tools for CordovaVisual Studio Tools for Cordova
Visual Studio Tools for Cordova
 
Microsoft + Agile (light)
Microsoft + Agile (light)Microsoft + Agile (light)
Microsoft + Agile (light)
 
Microsoft + Agile
Microsoft + AgileMicrosoft + Agile
Microsoft + Agile
 
The World of Stylesheet Languages
The World of Stylesheet LanguagesThe World of Stylesheet Languages
The World of Stylesheet Languages
 
How to Develop Cross-Platform Apps
How to Develop Cross-Platform AppsHow to Develop Cross-Platform Apps
How to Develop Cross-Platform Apps
 
The Asynchronous Pattern (for beginners)
The Asynchronous Pattern (for beginners)The Asynchronous Pattern (for beginners)
The Asynchronous Pattern (for beginners)
 
Designing an effective hybrid apps automation framework
Designing an effective hybrid apps automation frameworkDesigning an effective hybrid apps automation framework
Designing an effective hybrid apps automation framework
 
7 tips for more effective morning SCRUM
7 tips for more effective morning SCRUM7 tips for more effective morning SCRUM
7 tips for more effective morning SCRUM
 
Powerful tools for building web solutions
Powerful tools for building web solutionsPowerful tools for building web solutions
Powerful tools for building web solutions
 

Microsoft .NET F# Implementation of A* search algorithm

  • 1. RouteSharp PathFinder: Un’applicazione Microsoft F# per la ricerca di cammini minimi in una rete complessa mediante l’algoritmo di ricerca A* Davide G. Monaco, Andrea Tino Prof. Ing. A. Faro @ DIIEI UNICT Marzo 2011 1 Introduzione generale RouteSharp ` un’applicazione Microsoft .NET per l’acquisizione di reti complesse e la e ricerca di cammini minimi su esse. L’intera applicazione si divide in due grandi unit`: a 1. PathFinder Si tratta del core di sistema scritto in F# in grado di caricare reti complesse da file (tramite un microlinguaggio usato per la specifica dei nodi, delle connessioni e delle query) e fornire API per la ricerca dei cammini minimi. 2. RouteSharp Si tratta di tutta l’applicazione comprensiva del core in F# e dei futuri moduli di interfaccia scritti in C#. 1.1 Funzionalit` implementate a L’applicazione attualmente sviluppata consta del solo core in F# che mette a dispo- sizione le seguenti funzionalit`: a 1. Definizione delle reti: E’ possibile scrivere da file la rete complessa da cari- care. I file (con estensione .rs denominati in maniera pi` specifica netfile) vengono u scritti tramite un microlinguaggio line-based e permettono di definire i nodi e le connessioni della rete. E’ inoltre possibile definire le query di ricerca negli stessi netfile. 2. Caricamento e traduzione: I file vengono caricati dall’applicazione e tradotti in strutture interne che concorreranno alla generazione dei nodi e del loro vicinato. 3. Net traversal: E’ possibile, dalle query definite, effettuare la ricerca di cammini minimi all’interno della rete caricata. La ricarca avviene tramite algoritmo A*. 4. Modalit` di interazione: L’applicazione, il core in F#, dialoga tramite riga a di comando. E’ possibile specificare diverse opzioni in esecuzione tra le quali anche una modalit` interattiva mediate la quale il netfile viene esaminato, le query a ignorate, e la rete viene dunque costruita e all’utente viene infine richiesto di definire, di volta in volta, lo start point e l’end point del percorso da cercare. 1
  • 2. 2 Introduzione al paradigma funzionale La programmazione funzionale ` un paradigma di programmazione in cui la computazione e viene trattata come la valutazione di funzioni matematiche, evitando l’uso stati e dati mutabili. Le origini della programmazione funzionale possono essere ricondotte al lambda calcolo ed alla ricorsione. La programmazione funzionale pone maggior accento sulla definizione di funzioni, rispetto ai paradigmi procedurali e imperativi, che prediligono la specifica di una se- quenza di comandi da eseguire. In questi ultimi, i valori vengono calcolati cambiando lo stato del programma attraverso delle assegnazioni; un programma funzionale ` im- e mutabile: i valori non vengono trovati cambiando lo stato del programma, ma costruendo nuovi stati a partire dai precedenti. La differenza tra il concetto di funzione in un linguaggio imperativo ed in un linguag- gio funzionale consiste nel fatto che nel primo una funzione pu` avere dei side-effects (o o effetti collaterali), mentre nel secondo no. Ci` rappresenta uno dei maggiori vantaggi o nell’uso del paradigma funzionale, in quanto non solo sar` molto pi` semplice verifi- a u care la correttezza e la mancanza di bug del programma, ma sar` anche pi` efficace a u l’ottimizzazione dello stesso. Impiego Tipicamente, come tutti i linguaggi di alto livello, i linguaggi funzionali sono meno effi- cienti in merito all’uso di CPU e memoria rispetto a linguaggi imperativi di pi` basso u livello come il C. Ad ogni modo ` possibile affermare che per programmi che effettuano e computazioni numeriche intense, gestiscono grosse matrici o database multidimension- ali, alcuni linguaggi funzionali riescono a raggiungere le prestazioni di poco inferiori al C. Inoltre, l’immutabilit` dei dati, in molti casi, aumenta l’efficienza dell’esecuzione, a permettendo al compilatore di fare assunzioni che sarebbero poco sicure nel caso di lin- guaggio imperativo. Si potrebbe quindi pensare che i linguaggi funzionali possano essere impiegati in modo efficace solamente per scopi puramente accademici. Molti linguaggi funzionali, invece, sono stati utilizzati per scopi commerciali o indus- triali negli ultimi decenni, basti pensare ad Erlang che venne impiegato negli anni 80 dalla Ericsson per implementare sistemi di telecomunicazioni fault-tolerant, oppure F#, che essendo incluso nell’ambiente .NET della Microsoft sta trovando impiego in ambito commerciale. La decisione di implementare l’algoritmo A* in F# ` scaturita dalla volont` di ri- e a considerare la programmazione funzionale e proporre un’alternativa non presente in letteratura. 3 L’algoritmo A* A* (A star) ` un noto algoritmo per la ricerca su grafi che individua un percorso da e un nodo iniziale ad un nodo destinazione, descritto per la prima volta nel 1968 come 2
  • 3. estenzione dell’algoritmo di Dijkstra. 3.1 Introduzione A* utilizza ricerche di tipo best-first per trovare il percorso a costo minore; inoltre, utilizzando una funzione euristica di tipo distance-plus-cost, riesce ad essere molto performante e a determinare l’ordine in cui i nodi verranno esplorati. Tale funzione, che chiameremo f (x), viene determinata secondo la relazione: f (x) = g(x) + h(x) dove: - g(x) ` una funzione che tiene conto del costo di spostamento all’interno del grafo e dal nodo di partenza al nodo corrente. - h(x) ` una funzione di stima euristica che calcola la distanza dal nodo corrente al e nodo destinazione. Deve essere un’euristica ammissibile, ovvero una funzione che mai sovrastima il costo del raggiungimento della destinazione. Per questo motivo, in genere, soprattutto in ambito routing, la distanza considerata ` una distanza e ”in linea retta” (es. la norma in uno spazio vettoriale). 3.2 Principio A* attraversa il grafo verso la destinazione seguendo il percorso a minor costo conosciuto, mantenendo ordinata una priority queue di segmenti alternativi lungo la strada. Se, quindi, in un qualsiasi punto un possibile percorso ha un costo superiore rispetto ad un altro gi` incontrato precedentemente, l’algortmo abbandona il percorso a costo pi` a u alto in favore del segmento a costo pi` basso. Questo processo viene applicato finch´ la u e destinazione non viene raggiunta. Come accennato precedentemente, il costo preso in esame non ` costituito unicamente dal costo del singolo spostamento dal nodo corrente e al nodo vicino, bens` considera tutti gli spostamenti effettuati fino all’attuale posizione, ı g(x), e la distanza in linea retta dalla destinazione, h(x), potendo quindi valutare quanto effettivamente ci si avvicina alla destinazione con il successivo spostamento. 3.3 Funzionamento Iniziando dal nodo di partenza, come precedentemente accennato, viene mantenuta una priority queue, nota convenzionalmente come open set. Il nodo con f-score, valore di f (x), minore all’interno della coda ha maggiore priorit`. Ad ogni step, il nodo con a priorit` pi` alta viene rimosso dall’open set, vengono calcolati ed annotati gli f-score dei a u suoi vicini e gli stessi vengono aggiunti all’open set. L’algoritmo continua finch´ il nodo di destinazione ha un f-score minore di qualsiasi e altro nodo nell’open set, ritornando il percorso con successo, o altrimenti finch´ non vi e sono pi` nodi nell’open set, nel qual caso il percorso non esiste. u 3.4 Propriet` a A* ` un algoritmo completo, quindi siamo sicuri che trover` sempre una soluzione, se e a questa esiste. Inoltre, esso ` sia ammissibile che ottimo rispetto agli altri algoritmi e 3
  • 4. di ricerca ammissibili: ha una stima ottimistica del costo del percorso attraverso ogni nodo considerato. L’ottimismo consiste anche nel sapere che il vero costo del percorso attraverso ciascun nodo verso la destinazione varr` almeno quanto vale la nostra stima. a Quindi la conoscenza di A* ` cruciale. Per definizione, quando l’algoritmo ha terminato e la sua ricerca avr` trovato un percorso il cui costo attuale ` pi` basso del costo stimato a e u per ogni percorso attraverso tutti i nodi rimasti nell’open set, essendo sicuri di non aver trascurato alcun percorso dal costo minore, quindi A* ` ammissibile. e Se viene utilizzato un closed set per tener traccia dei nodi gi` esaminati, incremen- a tando le prestazioni dell’algoritmo, la funzione euristica h(x) oltre che ammissibile deve essere monotona, ovvero deve soddisfare la relazione: h(x) ≤ d(x, y) + h(y) dove d(x, y) ` la distanza tra i nodi x e y. e 3.5 Complessit` a La complessit` di A* dipende dalla funzione euristica utilizzata. a Nel caso peggiore, il numero di nodi espansi ` esponenziale rispetto alla lunghezza della e soluzione, ma ` polinomiale quando lo spazio di ricerca ` un albero, la destinazione ` un e e e singolo nodo e la funzione euristica h(x) soddisfa la seguente relazione: |h(x) − h∗ (x)| = O(log(h∗ (x))) dove h∗ (x) ` l’euristica ottima, ovvero la reale distanza che intercorre tra il nodo desti- e nazione e il nodo x; detto in altri termini, se l’errore della funzione h(x) non cresce pi` u velocemente del logaritmo dell’euristica ottima h∗ (x). 4 A* in RouteSharp In RouteSharp l’algoritmo A* ` stato inizialmente prototipato in Perl e successivamente e implementato in F#. 4.1 Implementazione Essendo F# un linguaggio multiparadigma focalizzato sulla programmazione funzionale, in RouteSharp l’implementazione dell’algoritmo differisce dalla canonica, presentata in letteratura con approccio procedurale. Sono state implementate diverse funzioni, la maggior parte delle quali ricorsive, al fine di ricoprire tutti gli aspetti implicati nel cor- retto funzionamento dell’algoritmo. Di seguito viene riportato il codice delle funzioni implementate, AStar(), Scan(), Update(), RebuildPath() e PrintPath(), accompagnato da commenti esplicativi per ciascuna funzione. type public Cost = double type public StarNode = { Node : NetworkNode ; FCost : Cost ; GCost : Cost ; 4
  • 5. HCost : Cost ; Parent : NetworkNode option ; } Sono stati definiti i tipi Cost, mappato sul tipo base double, e StarNode, che in- capsula il nodo corrente nella rete, Node di tipo NetworkNode, e associandogli i valori di f (x), g(x) e h(x) di cui l’algoritmo ha bisogno. Viene inoltre tenuta traccia del nodo di provenienza, Parent per poter ricostruire il percorso una volta trovato. La keyword option denota che il campo ` opzionale e che quindi potrebbe non essere presente. e let public AStar start goal = let norm_start = Norm ( start , goal ) let start1 = { Node = start ; FCost = norm_start ; GCost = 0.0; HCost = norm_start ; Parent = None } let goal1 = { Node = goal ; FCost = 0.0; GCost = 0.0; HCost = 0.0; Parent = None } let ol = [ start1 ] let cl = [ ] Scan ol cl goal1 Map . empty La funzione AStar() accetta in ingresso due nodi, start e goal, nel nostro caso saranno sempre NetworkNode, calcola la distanza in linea retta tra essi, tramite la fun- zione Norm(), e ne inizializza i relativi StarNode associati, start1 e goal1. Infine, dopo aver inizializzato le due liste ol e cl, rispettivamente open list e closed list, e viene invocata la funzione Scan(), passando come parametri la open list, la closed list, lo StarNode associato al nodo destinazione ed una mappa vuota. let rec public Scan ol cl goal come_from = match ol with | [] -> ( cl , come_from ) | h :: ol_tail -> if h . Node . Name = goal . Node . Name then ( cl , come_from ) else let ol2 = ol_tail let cl2 = h :: cl let starnodes = List . map ( fun ( x : N e t w o r k N o d e N e i g h b o u r ) -> { StarNode . Node = x . Node ; StarNode . FCost = h . GCost + x . Weight ; StarNode . GCost = h . GCost + x . Weight ; 5
  • 6. StarNode . HCost = 0.0; StarNode . Parent = None }) h . Node . Neighbourhood let ( ol3 , cl3 , come_from2 ) = Update ol2 cl2 starnodes h come_from Scan ol3 cl3 goal come_from2 La funzione Scan() ` una funzione ricorsiva che accetta in ingresso due liste ol e e cl, rispettivamente la open e la closed, il nodo destinazione, goal, ed una mappa che permette di stabilire i rapporti di parentela tra i nodi, come from. Se ol, ` una lista vuota, [], viene ritornata una tupla contenente la closed list e la e mappa di nodi, (cl, come from). In caso contrario, viene estratto il nodo in testa dalla open list, h, ovvero il nodo con f-score migliore, e vengono effettuati dei controlli. Se h corrisponde al nodo di destinazione, viene ritornata la tupla contenente la closed list e la mappa di nodi; in caso contrario, h viene inserito in closed list, affinch´ si abbia e memoria del fatto che tale nodo ` stato esaminato, e viene creata una lista contenente e i suoi vicini; per aggiornare open list, closed list e mappa di nodi, viene chiamata la funzione Update(), che torner` una tupla contenente i tre elementi richiesti, ed infine a viene effettuata la chiamata ricorsiva a Scan(). La funzione Update() ` un po’ pi` complessa, quindi, per semplificarne la spie- e u gazione, verr` illustrata passo passo. a let rec public Update ol cl neigh wn come_from = match neigh with | [] -> ( ol , cl , come_from ) | h :: neigh_tail -> [...] Update() ` una funzione ricorsiva che ritorna una tupla contenente la open list, la e closed list e la mappa di nodi aggiornata in base alle informazioni passate. Accetta in ingresso ol e cl, rispettivamente open e closed list, wn, il nodo corrente, neigh, la lista dei vicini del nodo corrente, e come from, la mappa di nodi gi` descritta a precedentemente. Se la lista dei vicini ` vuota, la funzione ritorna la tupla, altrimenti prosegue esaminando e il primo vicino in neigh che chiameremo h. if List . exists ( fun x -> x . Node . Name = h . Node . Name ) cl then Update ol cl neigh_tail wn come_from else *(1) Se il nodo h ` presente in closed list passa al vicino successivo chiamando ricorsiva- e mente Update(), altrimenti prosegui esaminando h. -*(1) - let testG = wn . GCost + ( wn . Node . G e t W e i g h t T o N e i g h b o u r ( h . Node . Name )) if List . exists ( fun x -> x . Node . Name = h . Node . Name ) ol then *(2) else let h2 = { Node = h . Node ; 6
  • 7. FCost = testG ; GCost = testG ; HCost = h . HCost ; Parent = Some wn . Node } let ol2 = h2 :: ol let ol3 = List . sortBy ( fun x -> x . FCost ) ol2 let come_from2 = Map . add h . Node . Name wn . Node . Name come_from Update ol3 cl neigh_tail wn come_from2 Calcola il valore di g(x), testG, sul nodo h. Se h ` presente in open list prosegui valutando ulteriori casi, altrimenti incapsula h in e uno StarNode e aggiungilo alla open list. Ordina la open list per f-score e aggiorna come from, ponendo wn come predecessore di h. Infine invoca ricorsivamente Update con la nuova open list e lista dei nodi. -*(2) - let comp_node = List . find ( fun x -> x . Node . Name = h . Node . Name ) ol if testG < comp_node . GCost then let h2 = { Node = h . Node ; FCost = testG (* h . FCost *); GCost = testG (* h . GCost *); HCost = h . HCost ; Parent = Some wn . Node } let replacer ( x ) = if x . Node . Name = h . Node . Name then h2 else x let ol2 = List . map ( fun x -> ( replacer x )) ol let ol3 = List . sortBy ( fun x -> x . FCost ) ol2 let come_from2 = Map . add h . Node . Name wn . Node . Name come_from Update ol3 cl neigh_tail wn come_from2 else let h2 = { Node = h . Node ; FCost = testG (* h . FCost *); GCost = testG (* h . GCost *); HCost = h . HCost ; Parent = h . Parent } let replacer ( x ) = if x . Node . Name = h . Node . Name then h2 else 7
  • 8. x let ol2 = List . map ( fun x -> ( replacer x )) ol let ol3 = List . sortBy ( fun x -> x . FCost ) ol2 Update ol3 cl neigh_tail wn come_from In questo caso abbiamo trovato h nella open list. Confrontiamo il valore di g(x) precedentemente annotato con testG. Se il valore di test ` minore, aggiorna i valori e in ol e come from, riordina la open list e invoca ricorsivamente Update(); altrimenti aggiorna solamente come from e invoca sempre Update(). let rec public RebuildPath finalpath nodemap node = let init lst = match lst with | [] -> [ node ] | _ -> lst try let par = Map . find node nodemap RebuildPath ( par :: ( init finalpath )) nodemap par with | :? K e y N o t F o u n d E x c e p t i o n -> finalpath La funzione RebuildPath() ` una funzione ricorsiva che ritorna il percorso. e Accetta in ingresso il percorso da ritornare, finalpath, la mappa di nodi elaborata durante il processamento di A*, nodemap, e il nodo corrente, node. La ricostruzione viene fatta risalendo dalla destinazione al nodo di partenza, valutando il nodo predecessore annotato nella nodemap. Viene sollevata un’eccezione nel caso in cui il percorso non ` stato trovato. e 5 Analisi comparativa con un’applicazione Prolog: PathFinder.exe vs Traffic.exe Per determinare una scala di performance e un ordine di efficienza di A* scritto in F#, ` stata condotta una serie di esecuzioni diagnostiche (run delle applicazioni in contesti e controllati) per accertare le attivit` svolte da PathFinder nel caricare la rete e nel cercare a il cammino minimo. Le stesse attivit` sono state condotte su Traffic, un’applicazione a scritta in Prolog avente gli stessi obiettivi di PathFinder. 5.1 Perch` questa analisi e La possibilit` di esaminare le prestazioni di PathFinder tramite un’analisi comparata a con un’applicazione Prolog permette di stabilire un’ordine di grandezza circa le perfor- mance di una’aplicazione scritta tramite un linguaggio multiparadigma (OO + Func- tional) e un’applicazione scritta tramite un linguaggio logico. Considerando inoltre che A* possiede, attualmente, pochissime implementazioni funzionali e multiparadigma in letteratura, questa analisi mette anche a fuoco potenziali sviluppi di F# nel campo delle reti complesse. 5.1.1 Limiti e considerazioni Malgrado si tratti di un’analisi comparata condotta mediante rigidi schemi e col maggior rigore possibile; ` necessario puntualizzare che i run controllati e i dati raccolti non e 8
  • 9. possono essere considerati per la definizione di una precisa tabella delle performance (dalla quale ` possibile stabilire, con certezza, quale sia l’applicazione migliore per una e certa caratteristica in esame) a causa dei seguenti motivi: 1. Numero di run: Il numero di esecuzioni controllate non ` tale da stabilire una e corretta descrizione generale del comportamento delle due applicazioni. 2. Contesto software: Il contesto software in cui le applicazioni sono state eseguite non rispecchiava un classico scenario di esame. Ovvero la presenza di processi in background di sistema e di altre applicazioni ha inquinato l’ambiente di lavoro e ha, pertanto, reso le grandezze in esame, dipendenti da parametri rumorosi. 3. Contesto hardware: La macchina su cui sono state condotte le esecuzioni non rispecchiava una vera e propria macchina per test controllati. Per questo motivo si considerino i risultati seguenti come il frutto di un’analisi volta a determinare le dinamiche generali delle due applicazioni. 5.2 Strumentazione Al fine di ottenere dati precisi per analisi in profondit` delle performance delle due a applicazioni, coerentemente al contesto software di base (sistema operativo), ` stata e utilizzata un’applicazione Microsoft per il profiling di una sessione di lavoro. Microsoft Windows Performance Analysis (WPA) ` stata scelta come tool principale per l’analisi e delle prestazioni sopratutto a fronte della sua perfetta integrazione coi sistemi Windows (il set di tool interagisce perfettamente con le funzionalit` di sistema, rendendo la suite a un componente nativo di Windows). Il risultato ` la possibilit` di ottenere informazioni e a davvero precise circa l’esecuzione di una qualsiasi applicazione. 5.2.1 Algoritmo di generazione delle reti complesse Per generare le reti complesse utilizzate nelle sessioni di run, ` stato utilizzato l’algoritmo e di Watts e Strogatz. 5.3 Sessione di esecuzione a reti dimensione-variata Questa sessione di esecuzioni ha visto i due programmi competere su reti a dimensione variabile. Lo scopo ` quello di verificare l’andamento delle prestazioni al crescere del e numero dei nodi, mantenendo costante, nei limiti del possibile, la struttura di vicinato della rete. 5.3.1 Reti caricate Le reti caricate dai programmi rispecchiano in tutto 11 configurazioni complesse con un numero di nodi variabile da 50 a 8000. Tramite l’applicazione di uno stress considirevole, al crescere del numero dei nodi, si verifica la risposta delle applicazioni all’avanzamento di tale complessit`. a 5.3.2 Risposta dei tempi di esecuzione I tempi di esecuzione hanno avuto due differenti andamenti: 9
  • 10. • PathFinder: L’applicazione ha mostrato un andamento crescente dei tempi. La dilatazione temporale evidenziata da PathFinder mette in mostra una dipendenza coerente tra numero di nodi e tempi. La crescita diviene considerevole a partire dai 1000 nodi in su, dove il salto in ordine di grandezza determina una netta spaccatura nella complessit` dele operazioni da eseguire. Tuttavia, l’applicazione a risponde bene ed in maniera controllata e predicibile. • Traffic: L’applicazione mostra un pattern fortemente irregolare nei tempi di es- ecuzione al crescere del numero dei nodi. Sono presenti inoltre forti sbalzi al crescere del numero dei nodi. La rete ad 8000 nodi non viene caricata al completo e l’applicazione sperimanta un crash non occasionale (run multipli sulla configu- razione a 8000 nodi mostrano che Traffic interrompe l’esecuzione a seguito dello stesso crash). 5.3.3 Risposta dei tempi di IO I tempi di IO (vengono presi in esame solamente i file di condifugrazione della rete per i due programmi) hanno avuto due differenti andamenti: • PathFinder: L’applicazione ha mostrato un andamento crescente a tratti dei tempi. La crescita dei tempi di IO, tuttavia, in rapporto ai tempi di computazione, avviene in maniera differente, mettendo in luce la controparte relativa ai tempi di ricerca del percorso ottimo (tempo di pura computazione). I dati mostrano, infatti, un andamento crescente delle attivit` di IO che, per`, riscontrano significativi in- a o nalzamenti solamente nella transizione da certi numeri di nodi. Dunque la crescita ` si presente, ma non costante, tuttavia regolare. Da notare, il raggiungimento dei e 7000 e 8000 nodi dove un brusco innalzamento viene rilevato. Si tratta, ed ` bene e sottolinearlo, di un pattern comunque regolare, infatti la curva dei tempi mostra variazioni d’ordine e tratti di non variazione d’ordine susseguirsi regolarmente. • Traffic: L’applicazione mostra un pattern fortemente irregolare nei tempi di IO al crescere del numero dei nodi. La crescita si mantiene piuttosto regolare fino al raggiungimento dei 1000 nodi, oltre i quali vengono sperimentati sbalzi acuti dei tempi. Tale comportamento evidenzia molte irregolarit` nell’esecuzione. a 5.3.4 Rapporto dei tempi di computazione ed IO I tempi di IO e i tempi di pura computazione determinano una percentuale sui tempi di esecizione totale per ambedue i programmi. L’esame di tali rapporti mette in luce il comportamento delle due applicazioni circa le loro attivit` con il filesystem dandoci a la possibilit` di valutare quante parte dell’esecuzione viene materialmente spesa nelle a operazioni sui file di configrazione: • PathFinder: L’applicazione ha mostrato un andamento quasi costante del rap- porto computazione/IO. Osservando l’andamento complessivo al crescere del nu- mero dei nodi, si nota come l’applicazione comunque mantenga sempre una per- centuale al di sotto del 25% dedicata alla computazione vera e propria, mentre un complementare 75% circa viene sempre dedicato alla gestione delle operazioni su file. Tale risultato mette in luce la propriet` pi` importante di PathFinder: la a u lentezza crescente, al crescere della rete, ` dovuta nettamente ai tempi necessari e affinch` la rete possa essere caricata e tradotta. e 10
  • 11. • Traffic: L’applicazione mette in luce una percentuale estremamente a favore dei tempi di computazione, segno del fatto che i tempi di accesso e modifica dei file di configurazione, constano di un parte davvero minima nell’esecuzione complessiva. 5.4 Sessione di esecuzione a reti struttura-variata Questa sessione di esecuzioni ha visto i due programmi competere su reti generate sec- ondo le seguenti distribuzioni: • Watts e Strogatz: La rete viene generata a partire da una topologia di base e il processo itera i vari nodi generando nuove connessioni con una data probabilit`. a • Barabasi: La rete viene generata a partire da una rete triangolare. Alla rete vengono dunque aggiunti i vari nodi con un dato numero di connessioni. La prob- abilit` che tali connessioni vengano agganciate ai vari nodi ` proporzionale al loro a e vicinato. • Bernoulli: La rete viene generata a partire da una rete di base con tutti i nodi. Le connessioni vengono create secondo lo schema stocastico a tentativi di Bernoulli. Lo scopo ` quello di verificare l’andamento delle prestazioni al crescere del connession- e ismo per le varie tipologie di reti. 5.4.1 Reti caricate Le reti caricate dai programmi rispecchiano in tutto 10 configurazioni complesse con un numero di nodi fissato a 30. Tramite l’applicazione di uno stress considirevole, al crescere del connessionismo delle reti, si verifica la risposta delle applicazioni all’avanzamento di tale complessit`. a 5.4.2 Risultati in generale I run hanno messo in luce un comportamento molto statico dei tempi di esecuzione da parte di Traffic, mentre una maggiore sensibilit` viene riscontrata da PathFinder. a Riguardo le percentuali di effettiva computazione ed IO, troviamo sempre una grande disparit` tra le due applicazioni, dove Traffic utilizza sempre non meno del 25% del a tempo totale, in operazioni di IO. 5.5 Sessione di esecuzione a reti heuristic-aware Questa sessione di esecuzioni ha visto i due programmi competere su reti spazialmente collocate all’interno di un sistema di riferimento bidimensionale. I nodi hanno coordi- nate e lo spazio normato in esame viene considerato per trovare, appunto nella norma euclidea, l’euristica utilizzabile da A*. In tale contesto si vuole misurare l’efficienza di A* nel raggiungere l’obiettivo quanto prima mediante l’uso delle euristiche. 5.5.1 Risultati in generale Un notevole abbattimento dei tempi di computazione da parte di A* viene riscontrato grazie all’attivazione del meccanismo ad euristica. I tempi di PathFinder si riducono notevolmente e la forbice con Traffic si allarga. Traffic rimane pi` lento, considerando il u puro tempo di computazione (eliminando l’IO complessivo), PathFinder riesce a trovare il cammino minimo in pochi millisecondi. 11