1
Fork 0
mirror of https://github.com/Steffo99/unimore-bda-5.git synced 2024-11-24 17:14:24 +00:00
bda-5-steffo/README.md

291 lines
No EOL
18 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\[ Stefano Pigozzi | Grafo "creato da zero" | Tema Graph Analytics | Big Data Analytics | A.A. 2022/2023 | Unimore \]
# Analisi su grafo Neo4J relativo alle dipendenze delle crates del linguaggio Rust
> ### Graph analytics
>
> Obiettivo dellattività è analizzare il data graph di una Sandbox di Neo4j (esclusa quella vista a lezione) attraverso la definizione di almeno due research question che possano essere risolte attraverso le tecniche di graph analytics viste a lezione.
>
> Lattività consisterà nello studio delle research question attraverso la progettazione, limplementazione e lesecuzione di almeno 4 tecniche distinte e una loro interpretazione nel contesto della Sandbox scelta.
>
> Alcune precisazioni riguardo lattività richiesta:
>
> * Le Sandbox di Neo4J che possono essere usate a questo scopo sono quelle che hanno installato la Graph Data Science (GDS) Library.
> * Lattività di progettazione consisterà
> 1. nella definizione delle proiezioni che saranno memorizzate in named graph, Almeno una proiezione dovrà essere una Cypher Projection;
> 2. nella scelta degli algoritmi. In questa fase, si farà uso delle funzioni di memory estimation.
>
> * Le tecniche potranno essere implementate sia usando gli algoritmi di GDS messi a disposizione da Neo4J sia attraverso lesecuzione di query Cypher. Le tecniche implementate non dovranno essere già presenti nella Sandbox.
>
> Il risultato dellattività sarà un documento contenente
>
> * una breve descrizione della Sandbox scelta, dello schema del grafo analizzato e delle principali caratteristiche;
> * una descrizione delle research question e della soluzione proposta inclusa la progettazione delle tecniche proposte che dovrà essere adeguatamente giustificata;
> * il codice delle query eseguite sulla Sandbox, i risultati ottenuti e linterpretazione dei risultati ottenuti che rappresenteranno le risposte alle research question.
>
> Le attività verranno valutate sulla base dei seguenti criteri:
>
> * storytelling: la Sandbox è ben descritta? le research question proposte sono adeguate alle caratteristiche del grafo analizzato?
> * progettazione della graph analytics e analisi dei risultati:
> * Le proiezioni e gli algoritmi individuati sono adeguati in termini di correttezza e completezza a rispondere alle research question?
> * Linterpretazione dei risultati risponde alle research quesion?
> * complessità dellimplementazione
>
> #### Corrispondenza
>
> > \[...\] Ho installato la Graph Data Science library sul grafo che ho realizzato per la precedente attività, ed adesso sarei interessato a svolgere l'attività di Graph Analytics su di esso.
> >
> > È un'opzione prevista dalla consegna, oppure non è consentita?
>
> > Si è consentita ma la dimensione del grafo deve essere sufficiente per fare Graph Analytics. 
## Sinossi
Si sono effettuate ricerche di Graph Analytics sul database a grafo dell'indice [Crates.io], realizzato per il progetto a tema Neo4J, determinando le crate più importanti all'ecosistema attraverso gli algoritmi di *Degree Centrality*, *Betweenness Centrality*, e *PageRank*, e ricercando cluster di tag per migliorare la classificazione delle crate nell'indice attraverso gli algoritmi di *Louvain*, *Label Propagation*, e *Leiden*.
## Introduzione
> Per informazioni su cosa è una crate in Rust, come è formata, o come è stato costruito il dataset utilizzato, si veda l'[introduzione della relazione del progetto a tema Neo4J].
All'interno di questa relazione si esplorano due diverse *research questions*, marcate rispettivamente con i simboli 1⃣ e 2⃣.
### 1⃣ Quali sono le crates più importanti dell'ecosistema Rust?
Un'informazione utile da sapere per gli sviluppatori del linguaggio Rust e per i manutentori dell'indice [Crates.io] sono i nomi delle crate più importanti nell'indice.
Alcuni esempi di casi in cui il dato di importanza delle crate potrebbe essere utile sono:
- selezionare anticipatamente le crate su cui effettuare caching più aggressivo
- determinare le crate più a rischio di supply chain attack
- prioritizzare determinate crate nell'esecuzione di esperimenti con [crater]
Lo scopo di questa ricerca è quello di determinare, attraverso indagini sulla rete di dipendenze, un valore di importanza per ciascuna crate, e una classifica delle 10 crate più importanti dell'indice.
### 2⃣ Quali potrebbero essere altre *categories* utilizzabili per classificare crate?
Affinchè le crate pubblicate possano essere utilizzate, non è sufficiente che esse vengano indicizzate: è necessario anche che gli sviluppatori che potrebbero farne uso vengano al corrente della loro esistenza.
Nasce così il problema della *discoverability*, ovvero di rendere più facile possibile per gli sviluppatori le migliori crate con le funzionalità a loro necessarie.
A tale fine, [Crates.io] permette agli autori di ciascuna crate di specificare fino a 5 *keyword* (brevi stringhe arbitrarie alfanumeriche, come `logging` o `serialization`) per essa, attraverso le quali è possibile trovare la crate tramite funzionalità di ricerca del sito, e fino a 5 *category* (chiavi predefinite in un apposito [thesaurus], come `Aerospace :: Unmanned aerial vehicles`), che inseriscono la crate in raccolte tematiche sfogliabili.
Lo scopo di questa ricerca è quello di determinare, attraverso indagini sulle *keyword*, nuove possibili *category* da eventualmente introdurre nell'indice, ed eventualmente sperimentare un metodo innovativo per effettuare classificazione automatica delle crate.
## Struttura del progetto
Il progetto è organizzato nelle seguenti directory:
- `README.md`: questo stesso file
- `scripts/`: le query presenti in questa relazione come file separati, per una più facile esecuzione
## Prerequisiti
Si è scelto di utilizzare un clone del DBMS Neo4J gestito da Neo4J Desktop del progetto precedente.
### Neo4J Desktop (1.5.7)
Per effettuare il clone del DBMS, è stato sufficiente aprire il menu <kbd>···</kbd> del DBMS originale e cliccare l'opzione <kbd>Clone</kbd> presente al suo interno.
### Graph Data Science Library (2.3.3)
Per installare la [Graph Data Science Library], si è cliccato sul nome del database clonato, si ha selezionato la scheda <kbd>Plugins</kbd>, aperto la sezione <kbd>Graph Data Science Library</kbd>, e infine premuto su <kbd>Install</kbd>.
## Concetti
### Graph Catalog
La [Graph Data Science Library] non è in grado di operare direttamente sul grafo, ma opera su delle proiezioni di parti di esso immagazzinate effimeramente all'interno di uno storage denominato [Graph Catalog], al fine di permettere agli algoritmi di operare con maggiore efficienza su un sottoinsieme mirato di elementi del grafo.
Esistono vari modi per creare nuove proiezioni, ma all'interno di questa relazione ci si concentra su due di essi, ovvero le funzioni Cypher:
- [`gds.graph.project.cypher`] (anche detta Cypher projection), che crea una proiezione a partire da due query Cypher, suggerita per il solo utilizzo in fase di sviluppo in quanto relativamente lenta
- [`gds.graph.project`] (anche detta native projection), che crea una proiezione a partire dai label di nodi ed archi, operando direttamente sui dati grezzi del DBMS, ottenendo così un'efficienza significativamente maggiore e offrendo alcune funzionalità aggiuntive
Il Graph Catalog viene svuotato ad ogni nuovo avvio del DBMS Neo4J; si richiede pertanto di fare attenzione a non interrompere il processo del DBMS tra la creazione di una proiezione e l'esecuzione di un algoritmo su di essa.
### Modalità d'uso
<!-- TODO -->
## Analisi
### 1⃣ Realizzazione della *Graph Projection*
Si utilizza un approccio bottom-up per la costruzione della graph projection delle crate e delle loro dipendenze.
#### Determinazione dei nodi partecipanti
Si usa la seguente query triviale per determinare i codici identificativi dei nodi che partecipano all'algoritmo:
```cypher
MATCH (a:Crate)
RETURN id(a) AS id
```
| id |
|---:|
| 0 |
| 1 |
| 2 |
#### Determinazione degli archi partecipanti
Si costruisce invece una query più avanzata per interconnettere all'interno della proiezione i nodi in base alle dipendenze della loro versione più recente:
```cypher
// Trova tutte le versioni delle crate
MATCH (a:Crate)-[:HAS_VERSION]->(v:Version)
// Metti in ordine le versioni utilizzando l'ordine lessicografico inverso, che corrisponde all'ordine del versionamento semantico (semver) dalla versione più recente alla più vecchia
WITH a, v ORDER BY v.name DESC
// Per ogni crate, crea una lista ordinata contenente tutti i nomi delle relative versioni, ed estraine il primo, ottenendo così il nome della versione più recente
WITH a, collect(v.name)[0] AS vn
// Utilizzando il nome trovato, determina il nodo :Version corrispondente ad essa, e le crate che la contengono
MATCH (a:Crate)-[:HAS_VERSION]->(v:Version {name: vn})-[:DEPENDS_ON]->(c:Crate)
// Restituisci gli id dei nodi sorgente e destinazione
RETURN id(a) AS source, id(c) AS target
```
| source | target |
|-------:|-------:|
| 98825 | 21067 |
| 98825 | 16957 |
| 22273 | 21318 |
#### Creazione della graph projection
Si combinano le due precedenti query in una chiamata a [`gds.graph.project.cypher`]:
```cypher
CALL gds.graph.project.cypher(
"deps",
"MATCH (a:Crate) RETURN id(a) AS id",
"MATCH (a:Crate)-[:HAS_VERSION]->(v:Version) WITH a, v ORDER BY v.name DESC WITH a, collect(v.name)[0] AS vn MATCH (a:Crate)-[:HAS_VERSION]->(v:Version {name: vn})-[:DEPENDS_ON]->(c:Crate) RETURN id(a) AS source, id(c) AS target"
) YIELD
graphName,
nodeCount,
relationshipCount,
projectMillis
```
| graphName | nodeCount | relationshipCount | projectMillis |
|-----------|----------:|------------------:|--------------:|
| "deps" | 105287 | 537154 | 8272 |
### 1⃣ Degree Centrality
Come misura di importanza più basilare, si decide di analizzare la *Degree Centrality*, ovvero il numero di archi entranti che ciascun nodo possiede, utilizzando la funzione [`gds.degree`] in modalità *Stream* per semplicità di operazione.
Prima di eseguire l'algoritmo, [si stimano] le risorse computazionali richieste:
```cypher
CALL gds.degree.stream.estimate(
"deps",
{
// Di default l'algoritmo conteggia gli archi uscenti di ciascun nodo; con questo parametro, il comportamento si inverte
orientation: "REVERSE"
}
) YIELD
nodeCount,
relationshipCount,
bytesMin,
bytesMax,
requiredMemory
```
| nodeCount | relationshipCount | bytesMin | bytesMax | requiredMemory |
|----------:|------------------:|---------:|---------:|---------------:|
| 105287 | 537154 | 56 | 56 | "56 Bytes" |
Dato che la memoria richiesta stimata per l'esecuzione dell'algoritmo è pochissima, si procede immediatamente con l'esecuzione, e con il recupero delle 10 crate con più dipendenze entranti:
```cypher
CALL gds.degree.stream(
"deps",
{
// Di default l'algoritmo conteggia gli archi uscenti di ciascun nodo; con questo parametro, il comportamento si inverte
orientation: "REVERSE"
}
) YIELD
nodeId,
score
MATCH (n)
WHERE ID(n) = nodeId
RETURN n.name AS name, score, n.description AS description
ORDER BY score DESC
LIMIT 10
```
| name | score | description |
|---------------|--------:|--------------------------------------------------------------------------------------------------------------------|
| [`serde`](https://crates.io/crates/serde) | 24612.0 | "A generic serialization/deserialization framework" |
| [`serde_json`](https://crates.io/crates/serde_json) | 16365.0 | "A JSON serialization file format" |
| [`log`](https://crates.io/crates/log) | 12134.0 | "A lightweight logging facade for Rust" |
| [`tokio`](https://crates.io/crates/tokio) | 11298.0 | "An event-driven, non-blocking I/O platform for writing asynchronous I/Obacked applications." |
| [`clap`](https://crates.io/crates/clap) | 10066.0 | "A simple to use, efficient, and full-featured Command Line Argument Parser" |
| [`rand`](https://crates.io/crates/rand) | 9993.0 | "Random number generators and other randomness functionality." |
| [`thiserror`](https://crates.io/crates/thiserror) | 8615.0 | "derive(Error)" |
| [`anyhow`](https://crates.io/crates/anyhow) | 8130.0 | "Flexible concrete Error type built on std::error::Error" |
| [`futures`](https://crates.io/crates/futures) | 7398.0 | "An implementation of futures and streams featuring zero allocations,composability, and iterator-like interfaces." |
| [`lazy_static`](https://crates.io/crates/lazy_static) | 7118.0 | "A macro for declaring lazily evaluated statics in Rust." |
### 1⃣ PageRank
Per ottenere una misura di importanza più elaborata, si è scelto di utilizzare *PageRank*, algoritmo iterativo che dà maggiore rilevanza alle crate con pochi dipendenze e molti dipendenti, utilizzando la funzione [`gds.pageRank`].
Ancora, prima di eseguire l'algoritmo [si stimano] le risorse richieste:
```cypher
CALL gds.pageRank.stream.estimate(
"deps"
) YIELD
nodeCount,
relationshipCount,
bytesMin,
bytesMax,
requiredMemory
```
| nodeCount | relationshipCount | bytesMin | bytesMax | requiredMemory |
|----------:|------------------:|---------:|---------:|---------------:|
| 105287 | 537154 | 2540880 | 2540880 | "2481 KiB" |
Si osserva come la quantità di memoria richiesta sia significativamente maggiore di quella richiesta dall'algoritmo di *Degree Centrality*, ma sempre una quantità accettabile con le risorse a disposizione dei computer moderni; dunque, si procede con l'esecuzione dell'algoritmo, sempre in modalità *Stream* per semplicità di uso:
```cypher
CALL gds.pageRank.stream(
"deps",
{}
) YIELD
nodeId,
score
MATCH (n)
WHERE ID(n) = nodeId
RETURN n.name AS name, score, n.description AS description
ORDER BY score DESC
LIMIT 10
```
| name | score | description |
|--------------------------|-------------------:|--------------------------------------------------------------------------------------------------------------------------------------|
| [`serde_derive`](https://crates.io/crates/serde_derive) | 2633.874125046063 | "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]" |
| [`serde`](https://crates.io/crates/serde) | 2600.440123009119 | "A generic serialization/deserialization framework" |
| [`quote`](https://crates.io/crates/quote) | 1753.385696376074 | "Quasi-quoting macro quote!(...)" |
| [`proc-macro2`](https://crates.io/crates/proc-macro2) | 1547.702293697151 | "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case." |
| [`trybuild`](https://crates.io/crates/trybuild) | 1452.1162055975733 | "Test harness for ui tests of compiler diagnostics" |
| [`rand`](https://crates.io/crates/rand) | 1108.4777776061019 | "Random number generators and other randomness functionality." |
| [`syn`](https://crates.io/crates/syn) | 1047.3719317086066 | "Parser for Rust source code" |
| [`rustc-std-workspace-core`](https://crates.io/crates/rustc-std-workspace-core) | 997.5769831539209 | "Explicitly empty crate for rust-lang/rust integration" |
| [`serde_json`](https://crates.io/crates/serde_json) | 885.3755595284102 | "A JSON serialization file format" |
| [`criterion`](https://crates.io/crates/criterion) | 845.3984645777582 | "Statistics-driven micro-benchmarking library" |
<!-- Collegamenti -->
[Crates.io]: https://crates.io/
[introduzione della relazione del progetto a tema Neo4J]: https://github.com/Steffo99/unimore-bda-4#introduzione
[thesaurus]: https://github.com/rust-lang/crates.io/blob/master/src/boot/categories.toml
[crater]: https://github.com/rust-lang/crater
[Graph Data Science Library]: https://neo4j.com/docs/graph-data-science/current/
[Graph Catalog]: https://neo4j.com/docs/graph-data-science/current/management-ops/graph-catalog-ops/
[`gds.graph.project.cypher`]: https://neo4j.com/docs/graph-data-science/current/management-ops/projections/graph-project-cypher/
[`gds.graph.project`]: https://neo4j.com/docs/graph-data-science/current/management-ops/projections/graph-project/
[`gds.degree`]: https://neo4j.com/docs/graph-data-science/current/algorithms/degree-centrality/
[si stimano]: https://neo4j.com/docs/graph-data-science/current/common-usage/memory-estimation/
[`gds.pageRank`]: https://neo4j.com/docs/graph-data-science/current/algorithms/page-rank/