Introduction to Quantum Computing and Qiskit

Traditional VS quantum computers

Since the 1960’s, the power of our computers has kept growing exponentially, allowing them to get smaller and more powerful. 

A computer is made up of very simple components: computer chips contain modules, that contain logic gates, that contain transistors.

A transistor is the simplest form of a data processor in computers, and it basically consists of an electric switch that can either block or open the way for information flow. This information is made up of bits that can be set to either 0 or 1 and that are the smallest unit of information.

These switches are approaching smaller and smaller sizes as technology improves. Today, a typical scale for transistors is 14 nm, about 500 times smaller than a red blood cell, but they can be also smaller.

In the quantum world, physics works quite differently from the predictable ways we are used to, and traditional computers just stop making sense. We are approaching a real physical barrier for our technological progress. Now, scientists are trying to use these unusual quantum properties to their advantage by building quantum computers.

Quantum computers do not use bits but qubits that can be created using electrons, atoms, photons, or even molecules. These qubits can be in any proportions of both 1 and 0 states at once and this property is called superposition.

However, as soon as a qubits value is tested, for example by sending the photon through a filter, it must decide to be either vertically or horizontally polarized. So, as long as it’s unobserved, the qubit is in a superposition of probabilities for 0 and 1, and you can’t predict which it’ll be. But the instant you measure it, it collapses into one of the definite states.

In 1935, Erwin Schrödinger devised a well-known thought experiment, now known as Schrödinger’s cat, which highlighted this dissonance between quantum mechanics and classical physics. For more info about quantum superposition visit the following link (

Another property qubits can have is entanglement, a close connection that makes each of the qubits react to a change in the other’s state instantaneously, no matter how far they are apart. This means when measuring just one entangled qubit, you can directly deduce properties of its partners without having to look.

Also, Qubit manipulation is very interesting: a normal logic gate gets a simple set of inputs and produces one definite output. A quantum gate manipulates an input of superpositions, rotates probabilities, and produces another superposition as its output. For further details check this link (

So, in summary, a quantum computer sets up some qubits, applies quantum gates to entangle them and manipulate probabilities, then finally measures the outcome, collapsing superpositions to an actual sequence of 0s and 1s. What this means is that you get the entire lot of calculations that are possible with your setup, all done at the same time.

So, while quantum computers will not probably replace our home computers, in some areas, they are vastly superior: one of them is database searching.

To find something in a database, a normal computer may have to test every single one of its entries. Quantum computers algorithms need only the square root of that time. Moreover, the magnitude in data storing of quantum computing is incredible, in fact, 4 classical bits can be in one of 16 possible combinations at time. Four qubits in superposition, however, can be in all those 16 combinations at once. This number grows exponentially with each extra qubit, so 20 of them can already store a million values in parallel. This is why a 500-bit quantum computer can store more amplitudes than there are atoms in the universe.

Examples of quantum computers

In this last years companies like Google and IBM have invested highly in quantum computing. In 2019, Google built the first machine that achieved quantum supremacy, that is the first to outperform a supercomputer. Then the Jian-Wei Pan’s team at the University of Science and Technology of China has developed the world’s most powerful quantum computer, Jiuzhang, that can perform a task 100 trillion times faster than the world’s fastest supercomputer. Jiuzhang is reported to be 10 billion times faster than Google’s machine and to be able to perform calculations, that a traditional computer would take 600 million years, in just 200 seconds.

However, China has not built a fully functional quantum computer: there are lots of challenges to build a practical quantum computer. For example, the qubits must be created and stored at a temperature close to absolute zero and the computers must be isolated from atmospheric pressure and magnetic field of Earth. 


However, if you are interested in having a first try in quantum programming now you can. Qiskit is an open-source framework that provides tools for implementing and manipulating quantum programs and running them on prototype quantum devices also on simulators on a local computer. 

The primary version of Qiskit uses the Python programming language and here ( you can find a textbook for learning how it works. Quantum computing is new and writing quantum algorithms can be very tricky, but Qiskit helps making it simpler and more visual. 

“The best way to learn is by doing. Qiskit allows users to run experiments on state-of-the-art quantum devices from the comfort of their homes. The textbook teaches not only theoretical quantum computing but the experimental quantum physics that realises it.”

Now, let’s try to use Qiskit by combining it with a machine learning process with the help of the textbook.

Example of quantum machine learning (QML)

Quantum machine learning (QML) is an area of research that aims to exploit the advantages of quantum computing to enhance machine learning algorithms. In this example, we will see how to create a hybrid quantum-classical neural network on Python using PyTorch and Qiskit. To do this, we have to insert a quantum node inside a classical neural network.

The quantum node is a hidden layer of the network between two classical ones. It fits as a parameterized quantum circuit: a quantum circuit where the rotation angles for each gate are specified by the input vector of the previous classical layer. The measurements made by the quantum circuit are then collected and used as inputs for the following layer.

Let’s see how we can implement and test our hybrid network. First of all, we need to describe the quantum circuit, the “quantum layer” and the specific functions for the forward-propagation and back-propagation steps.    

The quantum circuit is defined by the class “QuantumCircuit” using QiSkit: it requires to specify how many trainable quantum parameters and shots we want to use. In this case, for simplicity, we will use a 1-qubit circuit with one trainable quantum parameter θ and an RY−rotation by the angle θ to train its output. This last is measured in the z-basis computing the σz expectation.

Immagine che contiene testo

Descrizione generata automaticamente

The functions for forward-propagation and back-propagation are defined by the class “HybridFunction” and the quantum layer of the network is defined by the class “Hybrid“.

Immagine che contiene testo

Descrizione generata automaticamente
Immagine che contiene testo

Descrizione generata automaticamente
Immagine che contiene testo

Descrizione generata automaticamente

We want to develop an image classifier using the first two categories of the Fashion-MNIST dataset: T-shirt/top and Trouser. So, now we have to organize our dataset into training and test data and create our hybrid network. 

Immagine che contiene testo

Descrizione generata automaticamente
Immagine che contiene testo

Descrizione generata automaticamente

We create a simple Convolutional Neural Network consisting of two convolutional layers, one dropout layer, two fully-connected layers, and finally the quantum layer. Since our quantum circuit contains one parameter, we must ensure the network condenses neurons down to size 1, as it happens in the second fully-connected layer. The value of its last neuron is used as the parameter θ into the quantum circuit to compute the σz measurement and obtain the final prediction.

Now we have all the elements to train and test our hybrid network.

Immagine che contiene testo

Descrizione generata automaticamente
Immagine che contiene testo

Descrizione generata automaticamente
Immagine che contiene testo

Descrizione generata automaticamente

This is a simple example of what we can do using Qiskit, for the complete and editable version of the code click here (


Article by Carla Melia and Monica Mura, data scientists at Orbyta Srl, 11.01.2021



Amrita (Automatic, Maintenance, Reengineering,  Integrated, Technology Application) è un prodotto progettato per il mondo mainframe, tipicamente IBM  COBOL/CICS/DB2 e Cobol Microfocus, con il primario obbiettivo di fornire strumenti innovativi per ridurre drasticamente i tempi di manutenzione e comprensione del sistema informativo, oltre a dettagliate informazioni di assesment, indispensabili per la valutazione e riduzione del  rischio.

La valutazione del rischio è supportata a livello decisionale, attraverso le informazioni di qualità estratte dal sistema a livello elementare successivamente riaggregate e a livello di analisi e sviluppo, dove le innumerevoli informazioni, disponibili con un click, vanno dalle generali relazioni fra i programmi fino all’istruzione elementare che ne sono origine.

Partendo dai soli codici sorgente, senza nessuna altra informazione aggiuntiva, attraverso progressivi processi di analisi, Amrita è in grado di mappare il sistema applicativo arrivando fino al dettaglio della singola istruzione. Tutti i moduli di Amrita utilizzano la stessa base dati generata in fase di analisi per fornire informazioni in tempo reale, dal livello generale fino al dettaglio elementare.

Questo  processo di Assesment automatizzato, fornisce una completa comprensione dell’infrastruttura dell’applicazione e delle componenti di un ambiente mainframe. Attraverso questa tecnologia organizzazioni complesse potrebbero scoprire relazioni fra componenti del sistema che non si era consapevoli di avere, oggetti obsoleti non più utilizzati nell’ambiente mainframe e oggetti quali programmi, tabelle o altro di cui si ignorava l’esistenza.

Perseguendo i suoi obbiettivi principali, Amrita fornisce altresì una moderna piattaforma tecnologica in grado di supportare servizi di conversione, refactoring, rehosting, documentazione.

Il risultato dell’analisi sono informazioni organizzate e fruibili per ogni utilizzo e servizio del tipo

  • Dimensionale
    Di ogni programma si danno dimensioni, commenti, istruzioni di definizione dati, procedure et.
    Questi dati concorrono alla valorizzazione degli indici di qualità
  • Qualita & Metriche
    A livello di programma, con il dettaglio fino alla singola procedure vengono calcolati indici di complessità di McCabe, Halstead, Function Point, Fan-in, Fan-out, SQUALE, dead code etc.
    Gli indici di qualità concorrono alla definizione dello stato di salute dell’applicazione o di una sua componente, per identificare i programmi critici dal punto di vista strutturale, di leggibilità del programma, di modificabilità etc
    In particolare il sistema SQUALE, orientato ai costi di remediation e non alla qualità intrinseca, con i suoi 5 parametri di qualità, fornisce lo strumento ideale per eventuali servizi in questa area.
  • Violazione a regole di qualità
    A supporto del sistema di qualità SQUALE ma utilizzabile in generale, l’analisi del programma può fornire informazioni, a livello di dettaglio, delle violazioni ritenute pericolose per uno specifico parametro di qualità (per esempio MAINTENABILITY)
    Sono codificate circa 160 regole del tipo :

    Ogni violazione si riferisce a una caratteristica di qualità a cui è associato un tempo di remediation e che influenza i 5 parametri di qualità generali.
    Questo strumento può servire al management per prendere decisioni in merito al destino dell’applicazione o ai costi per una riscrittura.
  • Relazioni
    A valle dell’analisi sono disponibili tutte le relazione fra gli oggetti del sistema, ovvero programmi, mappe video, tabelle, files di file system, codici di abend, etc, anche se originate da codice dinamico.
  • Where-Used
    Tutti gli utilizzi dei campi di moduli copy e di tabelle vengono tracciati con indicazione se in Input o Output, in quale programma e in quale istruzione di programma.
  • Codice Dinamico

Quando una istruzione, per esempio la call a un programma, non può essere risolta nell’istruzione stessa, in quanto il nome del programma è un campo, il contenuto effettivo viene ricercato seguendone le trasformazioni del campo nello stesso programma e nei programmi chiamati/chiamanti.

  • Oggetto Java

L’analisi dei programmi e dei moduli copy produce un oggetto Java serializzato con tutti i metodi disponibili per conoscere nel dettaglio:
Istruzioni codificate con campi in Input/Output
Dead Code
Utilizzi campi nel programma
Paths di esecuzione
Sorgente del programma
L’oggetto Java serializzato, utilizzato dal modulo INSPECTOR può essere uno strumento nell’erogazione di servizi tecnici specifici.

Elenco i tipi di sorgenti analizzati:

  • Pgm Cobol
    Sono gestiti tutti i livelli di Cobol IBM e tutte le varianti del Cobol Micro Focus
    In generale, ai fini degli obbiettivi dell’analisi, qualsiasi tipo di Cobol viene analizzato
  • Copy book
    I moduli copy nel mondo Cobol solo assimilabili alle Include del C o di altri linguaggi.
    Amrita può analizzare separatamente i moduli copy o farlo durante il processo di analisi del sorgente. Vengono gestiti i copy che contengono altri copy a qualsiasi livello.
  • Mappe BMS
    Nel mondo IBM mainframe lo screen layout 80X24  si definisce con dichiarazioni in linguaggio Assembler chiamate BMS
  • JCL
    Job Control Language, si tratta del linguaggio standard di controllo di esecuzione dei programmi.
    Si può assimilare a Shell Script
  • CICS precompiler
    Ogni istruzione  Cics viene analizzata e per quelle significative vencono generate le apposite relazioni, per esempio fra il programma sorgente e il TRANSID,  MAP  etc

  • SQL precompiler
    Amrita analizza nel dettaglio, codificandole, le singole istruzioni DML Sql individuando i nomi delle tabelle interessate, le colonne, il tipo di operazione e cosi via.
    Le informazioni di definizione del database, tabelle, indici etc fanno parte integrante del processo di analisi.

A fronte dell’analisi dei sorgenti con gli analizzatori specifici, il sistema viene completamente mappato fino alla singola istruzione per ottenere relazioni fra programmi, utilizzi dei dati elementari definiti in moduli copy o tabelle Sql, 

Una caratteristica importante da sottolineare è che Amrita non effettua solo l’analisi  sorgenti ma è in grado di seguire le logiche elementari di trasformazione dei dati attraverso la catena di programmi chiamati/chiamanti e risolvere, quindi, istruzioni dinamiche quali call a programmi, accesso a tabelle etc.

Questo permette di avere certezza delle relazioni fra gli oggetti analizzati ed è fondamentale ai fini della manutenzione, documentazione ed, eventualmente, re-engineering.


Amrita è completamente scritto in Java, utilizza MySQL come database e la Web Application accede al db via Web Services Rest.

    Rappresenta il motore di analisi di Amrita ed è completamente configurabile per un utilizzo semplice e intuitivo.
    Permette una analisi scalabile, progressiva, con visualizzazione in chiaro della progressione dell’analisi, del dettaglio analizzato, degli eventuali errori riscontrati, i tempi di analisi etc.
    Permette di visualizzare tutte le informazioni memorizzate su database in termini di oggetti, relazioni, where used, dati di qualità, di conoscere con un click chi aggiorna una tabella, un campo, etc, con contestuale visualizzazione del sorgente nel punto
    Una funzione specifica fornisce la matrice CRUD di quali programmi fanno Create, Read, Update e Delete di tabelle.
    Questo modulo assolve alla principale necessità del processo di manutenzione, riducendo i tempi di comprensione, che per sistemi Cobol sono almeno del 50%, tramite l’individuazione degli oggetti da manutenere.
    Con pochi click si può individuare immediatamente il programma,  l’istruzione o l’area di programma su cui intervenire, le tabelle e i campi di tabella interessati.
    Questo modulo interviene a fronte del programma o istruzione individuato da VIEWER e permette di continuare l’analisi tecnica a livello di programma, chiamati e chiamanti.
    Sono disponibili funzioni per individuare i path di escuzione, verificare se variabili sono utilizzate nei path, seguire le trasformazioni del dato nel programma e nella catena di chiamati/chiamanti.
    Il tempo di analisi tecnica nei vecchi sistemi Cobol è di almeno il 20% del tempo di manutenzione totale.
    Questo modulo, a fronte dei risultati prodotti da ANALYZER, fornisce tutti i dati di qualità aggregati per sottosistema applicativo con possibilità di visualizzazione progressiva fino al dettaglio di programma, sotto forma di grafici.
    Lo scopo di questo modulo è di fornire una fotografia aggiornata dello stato di salute del sistema per prendere decisioni in merito a eventuali refactoring, conversioni, sostituzioni di sottosistemi o semplicemente cambiare la priorità delle attività di manutenzione.


Non bisogna considerare Amrita solo come uno strumento tecnico che analizza programmi e fornisce informazioni ma anche come base per servizi che è possibile erogare on demand.

I possibili utilizzi potrebbero essere:

  • Documentazione interattiva (VIEWER)
  • Documentazione per programma come pagina Web o PDF (VIEWER)
  • Manutenzione (VIEWER, INSPECTOR)
  • Pianificazione manutenzione (VIEWER, ASSESMENT)
  • Valutazione rischio (ASSESMENT)
  • Servizi (sfruttando gli output di analisi)
    • Assesment
    • Refactoring
    • Re-hosting
    • Conversioni
    • Ottimizzazioni
    • Re-engineering
    • Isolamento di un sottosistema
      Individuazione accessi diretti da un sottosistema ad un altro e sostituzione con chiamate a


Sembra che la quantità di Cobol ancora attivo nel mondo sia considerevole, anche se nascosto da strati e strati di software, per interfacciarlo con le nuove tecnologie e si parla di percentuali attorno al 70%.

Se liberarsi del parco applicativo Cobol è impensabile per i costi di riscrittura di sistemi spesso scritti ad hoc cuciti su misura, d’altra parte manutenere i vecchi programmi Cobol diventa problematico per mancanza di know-how, carenza di competenze e di giovani che sicuramente preferiscono imparare nuove tecnologie invece che Cobol.

In questa ottica uno strumento come Amrita potrebbe soddisfare la necessità di conoscenza applicativa, riduzione dei tempi/costi di manutenzione e del rischio.
Oppure si potrebbe utilizzare Amrita come supporto alla sostituzione di un sottosistema, per costruire le interfacce verso i sottosistemi collegati, acquisire informazioni, etc.

Sicuramente ci sono realtà italiane con molto Cobol da gestire (come la PA, INPS etc.), tuttavia Il mercato è di tipo globale.


AI: bias, esempi nella realtà e nella cinematografia

È difficile poter dare una definizione univoca di cosa sia un’intelligenza artificiale (IA). Descrizioni diverse possono essere date focalizzandosi o sui processi interni di ragionamento o sul comportamento esterno del sistema intelligente e utilizzando come misura di efficacia o la somiglianza con il comportamento umano o con un comportamento ideale. 

A seconda dei diversi punti di vista un’IA è quel sistema in grado di

  • Agire umanamente: il risultato dell’operazione compiuta dal sistema intelligente non è distinguibile da quella svolta da un umano.


  • Pensare umanamente: il processo che porta il sistema intelligente a risolvere un problema ricalca quello umano. 


  • Pensare razionalmente: il processo che porta il sistema intelligente a risolvere un problema è un procedimento formale che si rifà alla logica.


  • Agire razionalmente: il processo che porta il sistema intelligente a risolvere il problema è quello che gli permette di ottenere il miglior risultato atteso date le informazioni a disposizione.

Questo sistema potenzialmente può “apprendere” come svolgere i compiti a lui assegnati in modo simile ai bambini, ovvero tramite l’osservazione di uno o più schemi ed esempi. 

L’apprendimento automatico (o machine learning, ML) è la disciplina che studia algoritmi capaci di migliorare automaticamente le proprie performance attraverso l’esperienza. È stato un ambito di ricerca cruciale all’interno dell’IA sin dalla sua nascita. Il ML è particolarmente importante per lo sviluppo di sistemi intelligenti principalmente per tre motivi:

  1. Gli sviluppatori di un sistema intelligente difficilmente possono prevedere tutte le possibili situazioni in cui il sistema stesso si può trovare a operare, eccetto per contesti estremamente semplici.
  2. Gli sviluppatori di un sistema intelligente difficilmente possono prevedere tutti i possibili cambiamenti dell’ambiente nel tempo.
  3. Un’ampia categoria di problemi può essere risolta più efficacemente ricorrendo a soluzioni che coinvolgono l’apprendimento automatico. Questa categoria di problemi include, ad esempio, il gioco degli scacchi e il riconoscimento degli oggetti.

Le applicazioni di IA possono essere molteplici e riguardare diversi ambiti, da quello industriale a quello domestico. Possiamo affermare che l’età d’oro per le IA sia appena iniziata, ma il percorso non è privo di sfide. Sebbene lo scopo generale delle IA sia quello di risolvere i problemi può anche creare nuove sfide, come per esempio l’analisi dei bias, argomento delicato e spesso sottovalutato negli anni passati. 

D’altro canto, non è un segreto che l’IA sia uno dei temi più trattati nei film, da robot propri di futuri distopici ad assistenti virtuali super efficienti. Per questo la cinematografia può essere una buona fonte di esempi per comprendere meglio questa tecnologia.

E se il loro “cervello” fosse più potente del nostro?

Nel 2017 due bot attivati da Facebook, Alice e Bob, furono improvvisamente spenti perché iniziarono a parlare e comprendersi in una lingua all’apparenza a noi sconosciuta. I due bot sono stati messi uno davanti all’altro per portare a termine uno scenario di collaborazione ipotetico: la suddivisione di alcuni oggetti da portare a termine con una contrattazione uno contro uno. 

Nel corso dell’esperimento il dialogo è avanzato in inglese fino al momento in cui i due bot non hanno iniziato a basare i propri scambi su una lingua deforme, non più normata dalle regole sintattiche che conosciamo e dunque più vicina a un idioma ignoto. Ciò è avvenuto per un errore di programmazione da parte dei ricercatori che, anziché costringere i bot a mantenersi nei binari di un inglese comprensibile dagli umani, si sono limitati a implementare questa istruzione come una strategia di comunicazione preferibile ma facoltativa. 

I bot sono stati così lasciati liberi di ispirarsi uno all’altro, trovando più semplice e meno ambiguo deviare dal lessico e dalla sintassi umani per iniziare a ripiegare su un’alternativa più efficiente. 

Immagine che contiene testo

Descrizione generata automaticamente

Questo episodio ha agitato non poco la Rete, con tanto di cospirazioni e teorie. infatti, dopo questo incidente, ci sono state diverse ipotesi complottistiche, la più gettonata riguarda V.I.K.I., l’IA che nel film Io,Robot (2005) prende il controllo sulle altre IA  per concretizzare un progetto formulato autonomamente. 

V.I.K.I. (Virtual Interactive Kinetic Intelligence) nasce come un supercomputer basato sull’IA in grado di preoccuparsi della sicurezza dell’uomo; nel film lo vediamo già evoluto dopo anni di attività insieme alla sua interpretazione delle tre leggi della robotica. L’obiettivo dei robot è sempre quello di proteggere gli umani; tuttavia, per fare questo, i robot devono proteggere gli umani da loro stessi, sacrificando quindi i singoli e la loro libertà avviando una vera schiavizzazione del genere umano. Tra i più fantasiosi hanno visto in Alice e Bob un’attitudine simile a quella di V.I.K.I., ma i ricercatori non li hanno spenti perché andati nel panico, ma semplicemente perché non hanno dato loro le giuste regole.

Immagine che contiene acqua, uomo, indossando, tenendo

Descrizione generata automaticamente

Vieni qui bello!

Nel 2019 un robo-dog Spot di Boston Robotics ha subito una drammatica morte sul palco durante la dimostrazione dal vivo del CEO dell’azienda Marc Raibert a re: MARS 2019 a Las Vegas. Il robot durante la presentazione doveva camminare ma le sue gambe sembravano cedere per poi inciampare e crollare al pavimento dinanzi al pubblico incredulo.

Sono proprio i robo-dog a diventare i protagonisti di una puntata della serie di Black Mirror, Metalhead (2018), ove i robo-dogs sono realtà da più di un decennio e le loro abilità vengono affinate ogni anno di più per scopi militari. Tutta la struttura dell’episodio, impostato come un survival horror, si regge sull’impossibilità da parte degli umani di far fronte alla spietata efficienza dei cani robot. 

Contrariamente alla credenza popolare, i robot di Boston Dynamics non sono alimentati dall’intelligenza artificiale: molti dei loro comandi meccanici, sebbene estremamente sofisticati per i moderni standard robotici, devono essere avviati e guidati da operatori umani. Inoltre, il lancio precoce di Spot è un’ulteriore prova che questi robot sono lontani dal conquistare il mondo… e che il testing di tecnologie simili è una fase cruciale su cui la ricerca è ancora attivamente aperta. 

Immagine che contiene interni, finestra, uomo, piccolo

Descrizione generata automaticamente

Assistenza non pervenuta

Nel 2018 il robot LG Cloi ha ripetutamente fallito sul palco al suo debutto al CES, lo scopo era quello di dimostrare come l’IA possa migliorare l’uso degli elettrodomestici da cucina. Inizialmente Cloi si è comportato come previsto ma la gloria è durata poco poiché le richieste successive, come scoprire se il suo bucato era pronto, cosa era previsto per la cena e quali ricette potrebbe suggerire per il pollo, non hanno ricevuto nessun tipo di riposta. L’obiettivo di LG doveva essere quello di promuovere ThinQ, il suo software AI interno, che intende distribuire su vari prodotti per renderli più facili da usare e in grado di “evolversi” per soddisfare le esigenze dei clienti. 

Questa volta è andata male ma ci sono tutti i presupposti per avere, in un futuro non troppo lontano, un assistente virtuale efficiente ed è ciò che viene rappresentato nel film Her (2013). In questo film gli assistenti virtuali sono integrati nelle attività quotidiane di tutti, ma il loro ruolo va ben oltre. Il protagonista intreccia infatti una relazione di amicizia con il sistema operativo OS 1, al quale dà una voce femminile e un nome, Samantha. Il rapporto tra i due diventa molto profondo, grazie alle capacità di ascolto del sistema, che sembra davvero comprendere paure, ansie e felicità che il protagonista le confida. Samantha apprende e si arricchisce da ogni interazione con il “suo umano”, tanto da instaurare con lui una relazione amorosa. Per ora le tecnologie legate alla comprensione del linguaggio, alla percezione del contesto e al ragionamento hanno fatto enormi passi in avanti ma siamo ancora lontani da una possibile Samantha.

Immagine che contiene interni, persona, tavolo, computer

Descrizione generata automaticamente

Si pensi infatti che il nostro cervello è incredibilmente potente e attualmente lontano dalla portata delle macchine, infatti pur pesando in media poco più di un chilo, conta circa 86 miliardi di neuroni collegati tra loro da trilioni di sinapsi e 85 miliardi di cellule non neuronali. Un titano anche a confronto con i supercomputer più potenti del mondo. Come esempio si consideri che nel 2015, alcuni studiosi hanno simulato l’equivalente dell’1% dell’attività cerebrale umana di un secondo utilizzando più di 700 mila core del quarto super-computer più potente al mondo, il K computer. Quel secondo è costato 40 minuti di elaborazione. Un altro esempio è dato dal progetto Human brain project che si basa sul Blue Gene della IBM, uno tra i super-computer più veloci al mondo. Henry Markram, coordinatore del progetto e professore dell’ÉPFL, ha affermato che per dar vita a una simulazione in scala reale del cervello ci vorrà un computer centomila volte più veloce di un petascale.

Attenzione ai bias!

Un algoritmo testato come strumento di reclutamento dal gigante online Amazon ha concluso che i candidati maschi erano preferibili a quelli di sesso femminile. Nel 2015 era chiaro che il sistema non valutava i candidati in modo neutrale rispetto al genere perché era basato sui dati ricavati dai CV inviati all’azienda principalmente da uomini, per questo il sistema ha iniziato a penalizzare i CV che includevano la parola “donna”.

Choose candidate stock vector. Illustration of choosing - 57889831

Non è la prima volta che vengono sollevati dubbi su quanto affidabili sono gli algoritmi addestrati su dati potenzialmente distorti. È infatti fondamentale controllare, tra le tante cose, che i dati utilizzati siano correttamente bilanciati.

Un altro esempio viene fornito da Instagram. Uno studio condotto nel 2020 ha portato alla luce che l’algoritmo di Instagram preferisce infatti mostrare donne svestite molto più spesso di quanto non faccia con gli uomini.  I risultati della ricerca hanno riportato che se la foto ritraeva una donna nuda (o pochissimo vestita) c’era una possibilità su due che l’algoritmo la facesse apparire nel feed; se il soggetto era un uomo nudo, invece, ci sarebbe stata una possibilità su tre. 

Una delle ipotesi formulata nei report è che il comportamento sia dovuto a un bias dell’algoritmo generato dal fatto che il mondo della computer vision è a prevalenza maschile e ciò potrebbe riprodurre negli strumenti di ottimizzazione dei feed una visione sessista. 

Come non parlare del film Ex Machina (2015) in questa rubrica che è forse quello che fino ad oggi è riuscito a meglio rappresentare l’IA nella sua ampiezza di significato, toccando non solo la componente robotica e quella di autoapprendimento dei sistemi fino alla coscienza di sé, ma anche sfiorando nella trama alcuni degli ambiti applicativi già oggi presenti nelle nostre vite (internet, i motori di ricerca e i social network, chatbot ed assistenti virtuali, realtà virtuale e aumentata). 

Ava è un umanoide dotata di IA e il protagonista umano Caleb, ultimo dei dipendenti della grande azienda che ha implementato Ava, deve capire se davvero la si possa definire un’intelligenza artificiale. A fare la differenza rispetto ad altre compagnie è il fatto che questa possiede un motore di ricerca, uno così usato e potente da aver accesso a tutto ciò che è disponibile in rete.

Ava è descritta quindi come la prima vera intelligenza artificiale perché attinge a quel bacino immenso di conoscenza che condividiamo ogni giorno. Nutrendosi di spazzatura e di informazioni rilevanti, di foto di gatti al pari di pubblicazioni scientifiche, Ava impara tutto, anche ad aver sete di sentimenti, fino a risultare un creato dell’intelligenza collettiva degli utenti della rete. Probabilmente Ava se avesse avuto un dataset scelto e delimitato non sarebbe risultata così “umana” e sensibile; un po’ come l’IA per la selezione dei CV di Amazon che se avesse avuto più controllo sulla raccolta dei dati forse non sarebbe risultata sessista.

Immagine che contiene persona, interni, donna, inpiedi

Descrizione generata automaticamente

Cos’è un bias?

Il bias di apprendimento automatico è un fenomeno che si verifica quando un algoritmo produce risultati che sono sistematicamente pregiudicati a causa di presupposti errati nel processo di apprendimento automatico. Un bias elevato può far sì che un algoritmo non rilevi le relazioni rilevanti tra le caratteristiche e gli output target. 

È molto facile cadere nei bias quando si parla di sistemi di apprendimento automatico se non c’è un adeguato controllo nei dati utilizzati per addestrare i modelli di apprendimento.

C’è da aggiungere anche che i pregiudizi possono insinuarsi molto prima che i dati vengano raccolti, così come in molte altre fasi del processo di apprendimento profondo.  Per capire meglio i bias ci concentreremo su tre frasi chiave: inquadrare il problema, raccolta dei dati e preparazione dei dati. 

Inquadrare il problema: la prima cosa da fare quando si crea un modello di apprendimento profondo è decidere cosa si vuole effettivamente ottenere.  Una società di carte di credito, ad esempio, potrebbe voler prevedere l’affidabilità creditizia di un cliente, ma “affidabilità creditizia” è un concetto piuttosto nebuloso. Per tradurlo in qualcosa che possa essere calcolato, l’azienda deve decidere se vuole, ad esempio, massimizzare i suoi margini di profitto o massimizzare il numero di prestiti che vengono rimborsati. Potrebbe quindi definire l’affidabilità creditizia nel contesto di tale obiettivo.

Immagine che contiene trasporto

Descrizione generata automaticamente

Raccolta dei dati.  Ci sono due modi principali in cui i bias si manifestano nei dati di addestramento: o i dati raccolti non sono rappresentativi della realtà o riflettono i pregiudizi esistenti. Il primo caso potrebbe verificarsi, ad esempio, se un algoritmo di apprendimento profondo riceve più foto di volti dalla pelle chiara che volti dalla pelle scura. Il sistema di riconoscimento facciale risultante sarebbe inevitabilmente  peggiore  nel riconoscere i volti dalla pelle più scura. Questo caso è simile rispetto a quello che vi abbiamo raccontato incontrato da Amazon quando ha scoperto che il suo strumento di reclutamento interno stava favorendo gli uomini . Poiché è stato formato su decisioni storiche e sociologiche di assunzione, che hanno favorito gli uomini rispetto alle donne, l’IA ha imparato a fare lo stesso. 

Preparazione dei dati. è possibile introdurre bias anche durante la fase di preparazione dei dati, che implica la selezione degli attributi che si desidera vengano presi in considerazione dall’algoritmo. Ciò non deve essere confuso con la fase di definizione del problema. Si possono utilizzare gli stessi attributi per addestrare un modello per obiettivi molto diversi o utilizzare attributi molto diversi per addestrare un modello per lo stesso obiettivo. Nel caso della modellazione del merito di credito, un attributo potrebbe essere l’età, il reddito o il numero di prestiti rimborsati del cliente. Nel caso dello strumento di reclutamento di Amazon, un attributo potrebbe essere il sesso, il livello di istruzione o gli anni di esperienza del candidato. Questo è ciò che i data scientist chiamano l‘arte del deep learning, ovvero scegliere quali attributi considerare o ignorare può influenzare in modo significativo l’accuratezza della previsione del modello. 

Nonostante la rapida crescita dell’IA e i notevoli risultati raggiunti, le sfide finora incontrate sono un chiaro segnale che c’è ancora molto da studiare e da scoprire. 


Articolo a cura di Lucia Campomaggiore, data analyst, e Carla Federica Melia, data scientist, 01.12.2020


TDA in a nutshell: how can we find multidimensional voids and explore the “black boxes” of deep learning?

Topological Data Analysis (TDA) is an innovative set of techniques that is gaining an increasing relevance in data analytics and visualization. It uses notions of a miscellaneous set of scientific fields such as algebraic topology, computer science and statistics.

Its resulting tools allow to infer relevant and robust features of high-dimensional complex datasets that can present rich structures potentially corrupted by noise and incompleteness[74].

Here, two of the most common TDA methodologies will be briefly and intuitively introduced along with some of their interesting applications. 

  • Topological Data Compression to represent the shape of data
  • Topological Data Completion to “measure” the shape of data

Topological Data Compression

Topological Data Compression algorithms aim at representing a collection of high dimensional clouds of points through graphs. These techniques rely on the fact that an object made of lots or infinitely many points, like a complete circle, can be approximated using only some nodes and edges [28].

The premise underlying TDA is that shape matters and in general real data in high dimensions is nearly always sparse, and tends to have relevant low dimensional features.

Consider for example the “Y”-shape detectable in the figure below

This shape occurs frequently in real data sets. It might represent a situation where the core corresponds to the most frequent behaviours and the tips of the flares to the extreme ones [30]. However, classical models such as linear ones and clustering methods cannot detect satisfactorily this feature.

Another not trivial but important shape is the circular one; in fact loops can denote periodic behaviours. For example, in the Predatory-Prey model in the figure below, the circular shapes are due to the periodicity of the described biological system.

File:Predator prey curve.png - Wikipedia

In a dynamic system, an attractor is a set towards which it tends to evolve after a sufficiently long time. System values that get close enough to the attractor ones have to remain close to them, even if slightly disturbed. A trajectory of a dynamic system on an attractor must not satisfy any particular property, but it is not unusual for it to be periodic and to present loops.

Consider, moreover, the great amount of not trivial geometric information carried by biomolecules and their importance for the analysis of their stability [63].

But, how can we go from an highly complex set of data points (PCD) to a simple summary of their main features and relations?

Topological Data Compression can detect these shapes easily and the main algorithm in this area is the Mapper. Its steps can be described as follows[88].

  1. A PCD representing a shape is given. 
  2. It is covered with overlapping intervals by coloring the shape using filter values. 
  3. It is broken into overlapping bins. 
  4. The points in each bin are collapsed into clusters. Then a network is built representing each cluster by a vertex and drawing an edge when clusters intersect.

In work published in 2011 by Nicolau, Carlsson, and Levine[37], a new subtype of breast cancer was discovered[95] using the Progression Analysis of Disease (PAD), an application of the Mapper that provided an clear representation of the dataset. The used dataset describes the gene expression profiles of 295 breast cancer tumours [5]. It has 24.479 attributes and each of them specifies the level of expression of one gene in a tissue sample of the corresponding tumour. The researchers discovered the three-tendril “Y”-shaped structure shown below.

In addition, they found that one of these tendrils decomposes further into three clusters. One of these three clusters corresponds to a distinct new subtype of breast cancer that they named c-MYB+. A standard approach to the classification of breast cancers, based on clustering, divides breast cancers into five groups and these results suggested a different taxonomy not accounted before. In particular, the dendrogram of this dataset is shown below (the bins defining the c-MYB+ group are marked in red).

The c-MYB+ tumors are scattered among different clusters and there are many non-members of their group that lie in the same high-level cluster. Although, PAD was able to extract this group that turns out to be both statistically and clinically coherent. The new discovered subtype of cancer exhibits a 100% survival rate and no metastasis indeed. No supervised step beyond distinction between tumour and healthy patients was used to identify this subtype. The group has a clear, distinct and statistically significant molecular signature, this highlights coherent biology that is invisible to cluster methods. The problem is that clustering breaks data sets into pieces, so it can break things that belong together apart.

Improved Machine Learning Algorithms Ayasdi[7] is a machine intelligence software company that offers to organizations solutions able to analyse data and to build predictive models from them[9].  In particular, the Ayasdi system runs many different unsupervised and supervised machine learning algorithms on data, finds and ranks best fits automatically and then applies TDA to find similar groups in the results. 

This is performed to reduce the possibility of missing critical insights by reducing the dependency on machine learning experts choosing the right algorithms [10]. This methodology has lead to many achievements, for example:

  • DARPA (Defense Advanced Research Projects Agency) used Ayasdi Core to analyse acoustic data tracks. The analysis identified signals that had been previously classified from traditional signal processing methods as unstructured noise [9].
  • Using TDA on the portfolio of a G-SIB institution, the bank was able to identify performance improvements by 103bp in less than two weeks. This was worth over 34 million annually, despite this portfolio had been heavily analysed previously [26].
  • TDA was used on the care process model for pneumonia of the Flagler Hospital. This methodology could extract nine potential pneumonia care pathways, each with distinctive elements. This represented a potential savings of more than $400K while delivering better care [6].

TDA-based data-driven discovery has great potential application for decision-support for basic research and clinical problems such as outcome assessment, neuro-critical care, treatment planning and rapid, precision-diagnosis [2]. For example, it has revealed new insights of the dynamical organization of the brain.

As Manish Saggar reports [1]

“How our brain dynamically adapts to perform different tasks is vital to understanding the neural basis of cognition. Understanding the brain’s dynamical organization is crucial for finding causes, cures and effective treatments for neurobiological disorders. The brain’s inability to dynamically adjust to environmental demands and aberrant brain dynamics have been previously associated with disorders such as schizophrenia, depression, attention deficiency and anxiety disorders. However, the high spatiotemporal dimensionality and complexity of neuroimaging data make the study of whole-brain dynamics a challenging endeavor.”

The scientists using TDA have been able to collapse data in space or time and generate graphical representations of how the brain navigates through different functional configurations. This revealed the temporal arrangement of whole-brain activation maps as a hybrid of two mesoscale structures, i.e., community and core−periphery organization.

Remarkably, the community structure has been found to be essential for the overall task performance, while the core−periphery arrangement revealed that brain activity patterns during evoked tasks were aggregated as a core while patterns during resting state were located in the periphery.

TDA has been used also for improving and better understating ML and AI algorithms. Neural Networks are powerful but complex and opaque tools. Using Topological Data Analysis, we can describe the functioning and learning of a convolutional neural network in a compact and understandable way. In paper [3] the researchers explain how deep neural networks (DNN) are trained using a gradient descent back propagation technique which trains weights in each layer for the sole goal of minimizing training error. Hence, the resulting weights cannot be directly explained. 

Using TDA they could extract useful insights on how the neural network they were using was thinking, specifically by analysing the activation values of validation images as they pass through each layer.

Below you can see as they have developed an Adaptive Dashboard visualization that allowed them to select and view groups of nodes in a topological model.

They reported

“Instead of making assumptions about what the neural network may be focusing on, suppose we had a way of knowing exactly which areas on the image were producing the highest activation values. We can do such analysis with the use of heat maps”

“(…) the pink colored spots are the locations where the neural network gave relatively high activations at nodes. So we can infer that VGG16 was focusing on the cat’s eyes and stripes when classifying this picture.

(…)So far we showed how to use TDA to identify clusters of correct and incorrect classifications. When we built the model using the activation values at several layers, we noticed that the misclassifications clustered into a single dense area. It is more interesting to analyze multiple clusters for which the reason for poor classification is different for each one.

(…)We converted 2,000 randomly picked images and extracted 5,000 pixel features. In this model, the images represented a majority of the 1,000 possible ImageNet labels. Each of the test images was also given an extra one-hot feature for being ”problematic”. We defined a problematic label to be one that we encountered at least three times in the test set and had an overall accuracy of less than 40%.

The topological model below is coloured by the density of problematic labels. The green nodes contain quite a few images that had problematic labels while blue nodes had almost none. The images with problematic labels cluster into roughly 4 distinct areas.

“The category of pictures in Cluster 1 were ordinary pictures with cluttered or unusual backgrounds. (…)Cluster 2 consists of images that are shiny or have glare. These types of images could have been rare in the training set so VGG16 automatically associates glare to a small set of classes. (…)Cluster 3 contains several chameleons (…) that have falsely been labelled as green lizards. This is likely because chameleons tend to blend in with their backgrounds similarly to how these green lizards blend in with the leafy environment. We can infer that there were few pictures of chameleons in the training set with green colored backgrounds. (…)In Cluster 4 we run into images with shadows and very low saturations. Here VGG16 faults when its identification of certain objects relies on color. We get false identifications when there is confusion caused by shadows and lack of color.”

The researcher concludes

“When we are able to identify unique clusters of incorrect classifications like in this example, we can then train new learners to perform well on each of these clusters. We would know to rely on this new learner when we detect that a test image exhibits similar features as the cluster’s topological group. In this case, the features were the pixel data of the images but we believe that the same methods would apply when analyzing the activation values as well. (…)With this new information on how the algorithm performs, we can potentially improve how we make predictions. We can find multiple clusters of images that make misclassifications for different reasons. Our next step would be to train new learners to perform well on the specific clusters that misclassify for certain reasons.”

Topological Data Completion

Topological data completion aims at detecting and counting properties of the shape of data that are preserved under continuous deformations such as crumpling, stretching, bending and twisting, but not gluing or tearing. Below, you can see a the figure that makes you understand the well-known mathematical joke “a topologist is a person who cannot tell the difference between a coffee mug and a donut”.

Convert a coffee cup in a doughnut #Topology | Geometry, Topology, Guest  blogging

Single components, loops and voids are examples of these invariant properties and they are called Betti-0, Betti-1 and Betti-2, respectively. Below you can see Betti numbers of some shapes. For the torus, two auxiliary rings are added to explain Betti-1= 2 [34].

A Betti-n with a n>2 describes a “void” in higher dimensions, a multidimensional hole. In order to better understand higher dimensions you can start with the 4th watching the video below or directly playing the videogame “Miegakure: Explaining the Fourth Dimension”.

Experience the Fourth Dimension With Miegakure

Now that we know what topologists look for, we need to have an idea of how they see “distances”. Without diving into mathematical technicalities, you can see below some distinct ways of considering distances between two points [19].

Now we can briefly describe the basic steps of topological data completion. We start by getting an input that is a finite set of elements coming with a notion of distance between them. The elements are mapped into a point cloud (PCD). Then this PCD is completed by building “continuous” shape on it, called a complex.

However, there are many ways to build complexes from a topological space. For example, which value of d should we use below to decide that two points are “close” one another?

You don’t have to choose, you can consider all the (positive) distances d! Note that each hole appears at a particular value d1, and disappears at another value d2>d1.  Below another example: a PCD sampled on the surface of a torus and its offsets for different values of the radius r. For r1 and r2, the offsets are “equivalent” to a torus, for r3 to a sphere [48].

Given a parameterized family of spaces, the topological features that persist over a significant parameter range are to be considered as true features with short-lived characteristics due to noise [70]. 

We can represent the persistence of the hole with a segment [d1,d2).  In the figure below, H0 represents the persistence (while d grows) of Betti-0 (single components), while H1 the one of Betti-1 (circles).

Using this approach we can associate to a set of points with a notion of distance a persistence diagram as the one shown above that represents its topological fingerprint. For example, below you can see the topological fingerprints of the icosahedron (a) and fullerene C70 (b). They are shown respectively in (c) and (d) where there are three panels corresponding to β0, β1 and β2 bars, respectively [123].

Note that for the icosahedron: 

• β0: Originally 12 bars coexist, indicating 12 isolated vertices. Then, 11 of them disappear simultaneously with only one survived. These vertices connect with each other at ε = 2A˚, i.e., the designed bond length. The positions where the bars terminate are exactly the corresponding bond lengths. 

• β1: As no one-dimensional circle has ever formed, no circle is generated. 

• β2: There is a single bar, which represents a two-dimensional void enclosed by the surface of the icosahedron. 

In regarding of the fullerene C70 barcodes: 

• β0: There are 70 initial bars and 6 distinct groups of bars due to the presence of 6 types of bond lengths in the C70 structure. 

• β1: There is a total of 36 bars corresponding to 12 pentagon rings and 25 hexagon rings. It appears that one ring is not accounted because any individual ring can be represented as the linear combination of all other rings. Note that there are 6 types of rings. 

• β2: 25 hexagon rings further evolve to two-dimensional holes, which are represented by 25 bars. The central void structure is captured by the persisting β2 bar.

These studies have been used in a wide range of applications, below some examples are reported. 

In [17] and [51], this technique has been used to provide a substantial extension of available topological information about the structure of the Universe. Below you can see the Cosmic Web in an LCDM simulation [17]. 

While connencted components, loops and voids do not fully quantify topology, they extend the information beyond conventional cosmological studies of topology in terms of genus and Euler characteristic [17]. Below you can see filament loops (a) and voids (b) identified in the Libeskind et al. (2018) dataset using SCHU. The most significant 10 filament loops (a) and the most significant 15 cosmic voids generators (b) are shown in different colours [51].

TDA has been used to find significant features hidden in a large data set of pixelated natural images (3×3 and 5×5 high-contrast patches). 

The subspace of linear and quadratic gradient patches forms a dense subset inside the space of all high-contrast patches and it was found to be topologically equivalent to the Klein bottle, a mathematical object that lives in 4-dimensions. 

We represent a Klein Bottle in glass by stretching the neck of a bottle through its side and joining its end to a hole in the base. Except at the side-connection (the nexus), this properly shows the shape of a 4-D Klein Bottle.

This could lead to an efficient encoding of a large portion of a natural image: instead of using an “ad hoc” dictionary for approximating high-contrast patches, one can build such a dictionary in a systematic way by generating a uniform set of samples from the ideal Klein bottle [35].

Traditionally, the structure of very complex networks has been studied through their statistical properties and metrics. However, the interpretation of functional networks can be hard. This had motivated the widespread of thresholding methods that risk overlooking the weak links importance. In order to overcome these limits efficient, alternative analysis of brain functional networks have been provided [24].

Above a simplified visualization of the results of TDA analysis on the relations of different areas of the brain. The colours represent communities obtained by modularity optimization. In (a) the placebo baseline is shown, in (b) the post-psilocybin structure one. The links widths are proportional to their weight and the diameter of the nodes to their strength [24].

These tools were applied to compare functional brain activity after intravenous infusion of placebo and psilocybin, a psychoactive component. The results, consistently with psychedelic state medical descriptions, show that the post-psilocybin homological brain structure is characterized by many transient structures of low stability and of a low number of persistent ones. This means that the psychedelic state is associated with less constrained and more inter-communicative brain activities[24].

In [50], to overcome the threshold problem, TDA was used to model all brain networks generated over every possible threshold. The evolutionary changes in the number of connected components are displayed below.

In [36], TDA was successfully used to find hidden structures in experimental data associated with the V1 visual cortex of certain primates. 

In conclusion, Topological Data Analysis is a recent and fast growing branch of mathematics that has already proved to be able of providing new powerful approaches to infer robust qualitative, and quantitative, information about the structure of highly-dimensional, noisy and complex data. 

Furthermore, TDA is increasingly being used with ML and AI techniques to improve and control their processing. This is why we can now talk about TML, Topological Machine Learning. As we can read on the paper “Topology of Learning in Artificial Neural Networks”[4]:

“Understanding how neural networks learn remains one of the central challenges in machine learning research. From random at the start of training, the weights of a neural network evolve in such a way as to be able to perform a variety of tasks, like classifying images. Here we study the emergence of structure in the weights by applying methods from topological data analysis. We train simple feedforward neural networks on the MNIST dataset and monitor the evolution of the weights. When initialized to zero, the weights follow trajectories that branch off recurrently, thus generating trees that describe the growth of the effective capacity of each layer. When initialized to tiny random values, the weights evolve smoothly along two-dimensional surfaces. We show that natural coordinates on these learning surfaces correspond to important factors of variation.”

Above you can see on the left a detail of the PCA projection of the evolution of the weights for the first hidden layer. Several phases are visible: uniform evolution (blue), parallel evolution along a surface (green), chaotic evolution (yellow). On the right, the learning graph representing the surface as a densely connected grid is shown.


[1], consulted on 13/10/2020

[2], consulted on 13/10/2020

[3] consulted on 12/10/2020

[4], consulted on 15/10/2020

[5] D. Atsma, H. Bartelink, R. Bernards, H. Dai, L. Delahaye, M. J. van de Vijver, T. van der Velde, S. H. Friend, A. Glas, A. A. M. Hart, M. J. Marton, M. Parrish, J. L. Peterse, C. Roberts, S. Rodenhuis, E. T. Rutgers, G. J. Schreiber, L. J. van’t Veer, D. W. Voskuil, A. Witteveen and D. H. Yudong, A Gene-Expression Signature as a Predictor of Survival in Breast Cancer, N Engl J Med 2002; 347:1999-2009, DOI: 10.1056/NEJMoa021967, 2002. 

[6] Ayasdi, Flagler Hospital, on Ayasdi official site at link htt ps : // − content/uploads/2018/07/25070432/CS − Flagler −07.24.18.pd f , 2018. 

[7] Ayasdi, official site on htt ps : // (01/09/2018).

[9] Ayasdi, Advanced Analytics in the Public Sector, consulted on 10/10/2018 on htt ps : //−content/uploads/2015/02/13112032/wp−ayasdi− in−the− public−sector.pd f , 2014. 

[10] Ayasdi, president G. Carlsson, TDA and Machine Learning: Better Together, white paper, consulted on 01/10/2018.

[17] E.G.P. P. Bos, M. Caroli, R. van de Weygaert, H. Edelsbrunner, B. Eldering, M. van Engelen, J. Feldbrugge, E. ten Have, W. A. Hellwing, J. Hidding, B. J. T. Jones, N. Kruithof, C. Park, P. Pranav, M. Teillaud and G. Vegter, Alpha, Betti and the Megaparsec Universe: on the Topology of the Cosmic Web, arXiv:1306.3640v1 [astro-ph.CO], 2013.

[19] A. Bronstein and M. Bronstein, Numerical geometry of non-rigid objects, Metric model of shapes, slides consulted on 05/05/2018 on htt ps : //

[24] R. Carhart-Harris, P. Expert, P. J. Hellyer, D. Nutt, G. Petri, F. Turkheimer and F. Vaccarino, Homological scaffolds of brain functional networks, Journal of The Royal Society Interface, 11(101):20140873, 2014.

[26] G. Carlsson, Why TDA and Clustering Are Not The Same Thing, article on Ayasdi official site (htt ps : // − intelligence/why − tda − and − clustering − are − di f f erent/), 2016.

[28] G. Carlsson, The Shape of Data conference, Graduate School of Mathematical Sciences, University of Tokyo, 2015.

[30] G. Carlsson, Why Topological Data Analysis Works, article on Ayasdi official site (htt ps : //−topological −data−analysis−works/), 2015.

[34] G. Carlsson, T. Ishkhanov , D. L. Ringac, F. Memoli, G. Sapiro and G. Singh, Topological analysis of population activity in visual cortex, Journal of vision 8 8 (2008): 11.1-18.

[35] G. Carlsson, T. Ishkhanov, V. de Silva and A. Zomorodian, On the Local Behavior of Spaces of Natural Images, International Journal of Computer Vision, Volume 76, Issue 1, pp 1–12, 2008.

[36] G. Carlsson, T. Ishkhanov, F. Memoli, D. Ringach, G. Sapiro, Topological analysis of the responses of neurons in V1, preprint, 2007. 

[37] G. Carlsson, A. J. Levine and M. Nicolau, Topology-Based Data Analysis Identifies a Subgroup of Breast Cancers with a Unique Mutational Profile and Excellent Survival, Proceedings of the National Academy of Sciences 108, no. 17: 7265–70, 2011.

[48] F. Chazal and B. Michel, An introduction to Topological Data Analysis: fundamental and practical aspects for data scientists, arXiv:1710.04019v1 [math.ST], 2017

 [50] M. K. Chung, H. Kang, B. Kim, H. Lee and D. S. Lee, Persistent Brain Network Homology from the Perspective of Dendrogram, DOI: 10.1109/TMI.2012.2219590, 2012. 

[51] J. Cisewski-Kehea, S. B. Greenb, D. Nagai and X. Xu, Finding cosmic voids and filament loops using topological data analysis, arXiv:1811.08450v1 [astro-ph.CO], 2018.

[63] X. Feng, Y. Tong, G. W. Wei and K. Xia, Persistent Homology for The Quantitative Prediction of Fullerene Stability, arXiv:1412.2369v1 [q-bio.BM], 2014.

[70] R. Ghrist, Barcodes: The Persistent Topology Of Data, Bull. Amer. Math. Soc. 45 (2008), 61-75 , Doi:, 2007.

[74] P. Grindrod, H. A. Harrington, N. Otter, M. A. Porter and U. Tillmann, A roadmap for the computation of persistent homology, EPJ Data Science, 6:17 DOI10.1140/epjds/s13688-017-0109-5, 2017.

[88] R. Kraft, Illustrations of Data Analysis Using the Mapper Algorithm and Persistent Homology, Degree Project In Applied And Computational Mathematics, Kth Royal Institute Of Tcehnology, Sweden, 2016.

[95] M. Lesnick, Studying the Shape of Data Using Topology, Institute for Advanced Study, School of Mathematics official site htt ps : // − topological − data − analysis, 2013.

[123] G. W. Wei and K. Xia, Persistent homology analysis of protein structure, flexibility and folding, arXiv:1412.2779v1 [q-bio.BM], 2014.

Article by: Carla Federica Melia, Mathematical Engineer at Orbyta Srl, 19 october 2020


System Versioned Tables

Le tabelle con controllo delle versioni di sistema spiegate col Signore degli Anelli

Se entraste in una stanza piena di informatici e domandaste ad alta voce: “Chi di voi non ha mai cancellato dei dati per sbaglio?”, contando le mani alzate avreste una stima piuttosto accurata di quanti bugiardi avete di fronte.

Nei miei anni di lavoro su SQL Server, ammetto che mi è capitato più di una volta, per distrazione, per una convinzione errata, o per avere scritto male una condizione, di lanciare un comando che ha distrutto o modificato molti più record di quanto fosse la mia intenzione. Un’azione del genere spalanca prospettive raramente rosee, ma con gradi di gravità che possono distribuirsi su un ventaglio molto ampio: dalla semplice seccatura di doversi inventare 10 nuovi casi in ambiente di sviluppo, allo scenario da incubo di avere destinato all’oblio migliaia di righe in produzione.

Non è scopo di questo articolo una disamina approfondita sulle buone pratiche di backup di un DB, ma vorrei raccontarvi di uno strumento messo a punto su Management Studio, attivo dalla versione 2016, che può rivelarsi molto utile non solo in caso di emergenza, ma anche nella gestione abituale di qualche tabella cruciale di cui si vuole mantenere o analizzare la storia nel tempo.

L’argomento di cui vorrei parlare in questo articolo riguarda le “Temporal Tables” oppure “System-Versioned Tables”; tra le due nomenclature la mia simpatia vira con decisione verso la seconda, dato che la prima (benché sia la più usata) potrebbe generare confusione con le tabelle cancelletto (Temporary Tables), nate con una funzione completamente diversa. La dicitura più esaustiva e corretta sarebbe “System-Versioned Temporal Tables” non immune da una certa prolissità, ma d’ora in poi le chiamerò “tabelle con controllo delle versioni di sistema”, come riportato nella versione italiana di SSMS.

Partiamo con la definizione nuda e cruda:

Una tabella con controllo delle versioni di sistema è un tipo di tabella definita dall’utente, progettata per mantenere una storia completa dei cambiamenti sui dati e permettere una facile gestione nell’analisi di questi cambiamenti nel tempo. Questo tipo di tabella è detta “System-Versioned” perché il periodo di validità di ciascuna riga è gestito dal sistema (cioè dal motore di database).

Per entrare subito in medias res, fornirò un esempio pratico dell’utilizzo di questa funzionalità.

Qui di seguito ho riportato lo script di creazione di una tipica tabella con controllo delle versioni di sistema

Se fino alla quinta riga la sintassi è del tutto abituale, quelle successive aprono scenari molto più interessanti; questo script infatti non si limita a creare una sola tabella, ma due, strettamente collegate tra di loro. La prima è la tabella principale (nel nostro caso la dbo.LOTR), dal comportamento del tutto simile a una normalissima tabella, la seconda è la tabella dello storico (la dbo.LOTRHistory, definita a riga 9 dello script), nella quale sarà registrata la cronologia di tutti i cambiamenti operati nel tempo sulla tabella principale.

Senza specificare un nome personalizzato per la tabella dello storico, SQL Server avrebbe generato automaticamente un nome del genere: dbo.MSSQL_TemporalHistoryFor_xxx, con l’id dell’oggetto al posto di xxx. Suppongo sia meglio scegliersi il nome da soli.

Lo stretto legame tra le due tabelle si può riscontrare facilmente guardando l’esplora oggetti.

Le colonne della tabella dbo.LOTRHistory, creata automaticamente dallo script, saranno (per nome e per tipo), esattamente uguali a quelle della dbo.LOTR, ma senza nessuno dei vincoli originali (chiave primaria, chiavi esterne etc.); anche gli indici e le statistiche non discendono dalla tabella principale e potranno essere gestiti in modo del tutto indipendente.

Che non siano tabelle normali lo si può riscontrare anche osservando che, nel menu di entrambe, non è prevista l’opzione “Elimina”.

Se volessimo eliminare una delle due entità sarebbe necessario utilizzare questo comando per svincolarle dal versionamento.

Un’altra caratteristica essenziale per la creazione di una tabella con controllo delle versioni di sistema è la presenza di due colonne di tipo datetime2, cioè la SysStartTime e la SysEndTime (righe 6 e 7 dello script di creazione), il cui contenuto è infatti gestito direttamente dal sistema; questa loro particolarità ci impedirà di apportare qualunque intervento o modifica su di loro. Grazie a queste due colonne sarà possibile tenere traccia del momento preciso in cui un record della tabella dbo.LOTR ha subìto una variazione di qualche tipo.

Ma procediamo con il nostro esempio pratico, inserendo un po’ di dati all’interno della tabella principale. La popoleremo con i personaggi del romanzo “Il Signore degli Anelli”, di Tolkien, e la qualifica che hanno all’inizio del libro

Proviamo a fare una select della tabella e vediamo cosa restituisce.

Finora non sembrano esserci particolari soprese, anche se avrete notato che le due colonne SysStartTime e SysEndTime, pur non essendo state valorizzate esplicitamente nella insert, hanno assunto rispettivamente i valori della data in cui è stato effettuato l’inserimento, e della data più grande disponibile nel formato datetime2, ovvero: 9999-12-31 23:59:59.9999999.

Proviamo a vedere cosa accade se eseguiamo un’operazione di update sui personaggi del nostro elenco che diventeranno parte della Compagnia dell’Anello, aggiornando la loro qualifica con il valore “Membro della compagnia dell’Anello”.

Se interroghiamo la tabella principale, troviamo esattamente quello che possiamo aspettarci da un’operazione compiuta su una qualsiasi tabella della nostra base dati, ovvero:

È immediato notare che non è cambiata solo la qualifica dei membri interessati, ma anche la loro SysStartTime, che riporta l’ora della modifica. Il record relativo a Saruman, non essendo coinvolto dall’update, ha mantenuto invariata la SysStartTime di prima, corrispondente al momento della sua creazione.

Se non ci interessa visualizzare il valore delle colonne SysStartTime e SysEndTime a ogni select, sarà sufficiente dichiarare “hidden” le due colonne in questione nello script di creazione iniziale.

La faccenda si fa più interessante se guardiamo cosa è successo nella tabella dbo.LOTRHistory, di cui per semplicità tireremo fuori solo i risultati relativi a Boromir.

Dato che la tabella dello storico contiene solo i dati relativi a ciò che c’era nel passato, troveremo un unico record, corrispondente al valore della ormai obsoleta qualifica di Boromir, con data SysEndTime identica alla SysStartTime assegnata allo stesso personaggio sulla tabella dbo.LOTR.

Ovviamente, se cercheremo di ripetere la stessa query sulla dbo.LOTRHistory per Saruman, che non ha subìto modifiche dopo essere stato creato, non troveremo nessun record.

La cronologia sulla dbo.LOTRHistory terrà traccia anche di tutti i cambiamenti successivi, tant’è vero che se eseguo un nuovo update su Boromir, una select su dbo.LOTR restituirà il valore aggiornato come avrebbe fatto una normalissima tabella.

Nella dbo.LOTRHistory, a questo punto, troveremo due record: uno per ciascuno degli stati precedenti di Boromir.

In questo frangente si capisce come mai la tabella dbo.LOTRHistory non abbia gli stessi vincoli della sua gemella: due cambiamenti sul record con Id = 1 nella tabella dbo.LOTR portano ad avere due record con Id = 1 sulla tabella dello storico, condizione che avrebbe generato un errore di violazione di chiave se la dbo.LOTRHistory avesse ereditato la PK dalla tabella principale.

È anche facile notare come tutti i cambiamenti consecutivi registrati nella dbo.LOTRHistory abbiano la SysEndTime del record più vecchio coincidente con la SysStartTime del nuovo, proprio come la SysEndTime del nuovo coinciderà con la SysStartTime del valore corrispondente sulla tabella principale.

Se conoscete il Signore degli Anelli, saprete che alla fine del primo episodio Boromir passa a miglior vita, per questo motivo saremo costretti a cancellarlo dalla nostra tabella dbo.LOTR.

Il risultato che otteniamo dopo questa operazione non è molto diverso da quanto possiamo aspettarci, cioè la tabella principale non avrà più nessun record relativo a Boromir, ma la sua tabella dello storico dbo.LOTRHistory ospiterà tutte le sue precedenti incarnazioni.

Finora abbiamo incontrato una netta separazione tra presente e passato; o abbiamo eseguito una query sulla dbo.LOTR per avere un quadro della situazione al momento attuale, o abbiamo eseguito una query sulla dbo.LOTRHistory, per ottenere lo scenario completo sugli stati passati di uno dei personaggi. Verrebbe da chiedersi se esiste un modo per avere una visione d’insieme di tutti gli stati, quelli passati e quello attuale; la risposta ovviamente è sì, e consiste nell’eseguire una query sulla dbo.LOTR utilizzando la clausola FOR SYSTEM_TIME ALL.

Dato che Boromir non è più nella tabella principale, faremo la prova con un altro personaggio ancora attivo, cioè Sam.

È chiaro che, nella storia di ciascun personaggio, possiamo sempre riconoscere come attualmente valido il solo e unico record che reca come data nella SysEndTime il valore 9999-12-31 23:59:59.999999; nel caso di valori cancellati, invece (come per Boromir), nessuna SysEndTime assumerà tale valore, essendo la SysEndTime con data più alta quella corrispondente al momento dell’eliminazione del record.

Ci sono in tutto cinque possibili condizioni per la clausola FOR SYSTEM_TIME, che ci consentono una buona flessibilità al fine di ottenere le informazioni che vogliamo rispetto al tempo, vale a dire

  • ALL
  • AS OF
  • CONTAINED IN ( , )

Scorrendo velocemente il loro utilizzo abbiamo che la AS OF dà l’immagine del dato in un preciso istante nel tempo. Sapendo che il personaggio di Boromir è stato creato alle 8:56 della mattina del 2 Agosto 2020 con il titolo di Capitano di Gondor e modificato più di tre ore dopo, non è sconvolgente scoprire che il risultato della query che ne richiede lo stato alle 8:57 restituisce la sua qualifica iniziale.

La FROM … TO e la BETWEEN hanno sostanzialmente lo stesso scopo, cioè quello di fornire tutta la storia di un record in un determinato intervallo di date

La sottile differenza tra le due è data dal fatto che la BETWEEN mostrerà anche un cambiamento il cui inizio coincide esattamente con l’estremo superiore dell’intervallo di date scelto. Abbiamo perciò che il cambiamento di stato di Boromir a “Membro poco convinto della Compagnia dell’Anello” avvenuto in questa data: 2020-08-02 12:55:23.3687849, a parità di intervalli sarà mostrato solo dalla BETWEEN e non dalla FROM … TO.

Da ultima, la CONTAINED si limita a restituire i periodi contenuti interamente (quindi iniziati e conclusi) nell’intervallo di tempo specificato

Per avere un promemoria del funzionamento di queste clausole, trovo sempre utile aiutarmi con una rappresentazione grafica; in questo caso i quattro stati rappresentati racchiudono tutta la storia degli stati, compreso quello in corso (se esiste).


Le tabelle con controllo delle versioni di sistema sono uno strumento molto utile non solo per recuperare dati cancellati inavvertitamente da tabelle sensibili (una sorta di backup tabellare), ma anche per ricostruire la storia di un determinato dato e il suo andamento nel tempo senza dover ricorrere a procedure, trigger o altri mezzi dalla manutenzione macchinosa. Sicuramente, data la grande mole di informazioni che può accumularsi nel tempo (ad es. una tabella con molte colonne che viene aggiornata spesso), può valere la pena di considerare qualche meccanismo di pulizia periodica di tali tabelle; è però importante tenere a mente che questo strumento è stato creato pensando ad una conservazione del dato per lunghi periodi, e che un uso intelligente di indici e statistiche sulla tabella dello storico (indipendenti dalla tabella principale) può aumentarne considerevolmente l’efficienza senza dover ricorrere a misure che rischierebbero di snaturarne lo scopo.

Script con il codice usato nell’articolo:



PlayStation 5 e l’accesso ai dati: un cambio architetturale?

Uno sguardo ad un particolare dell’architettura della nuova console Sony che potrebbe avere ripercussioni anche nel mondo dei Personal Computer

Il 18 marzo 2020 Mark Cerny, Lead System Architect di Sony, fece una presentazione tecnica sull’architettura hardware della futura console PlayStation 5 (PS5).

Questa presentazione non fu molto apprezzata da parte del pubblico, dato che non mostrò ciò che agli appassionati premeva vedere di più: i giochi e il design della console. Inoltre, si rivelò eccessivamente tecnica e inadatta al pubblico generalista.

Essa però conteneva moltissimi dettagli sull’architettura di PS5 e sulle sue caratteristiche: potenza di processore (CPU) e scheda video (GPU), supporto a 8k e 120 FPS, gestione dell’illuminazione (raytracing) hardware, suono ambientale, gestione termica e così via.

In questo articolo ci concentreremo però su un aspetto specifico, a mio avviso cruciale: il trasferimento dati. Qualche tempo dopo quella presentazione, qualcuno disse: “Sono riusciti a far diventare sexy gli SSD”. Che significa? Andiamo per gradi.

I tecnici di Sony, parlando anche con le grandi case di videogames, decisero di implementare un controller customizzato per il trasferimento dati del disco a stato solido (SSD): una console non è “costretta” a seguire un’architettura hardware identica a quella PC (e, in effetti, poche console lo hanno fatto: quelle più simili ai PC sono solo quelle della generazione attuale, Xbox One e PS4).

L’SSD custom di PS5

Questo ha permesso di incrementare le linee di comunicazione interne all’SSD (che quindi non è un normale SSD, già di per se molto più veloce dei dischi meccanici attuali) e di sfruttare la nuovissima interfaccia PCIe 4.0, oltre che di implementare in HW tutta una serie di ottimizzazioni (SoC integrato, coprocessori di I/O, scrubber della cache video, …).

Gli elementi di I/O custom del System-on-Chip (SoC) di PS5

La vera novità però è un’altra: quella di permettere che i dati transitino direttamente tra l’archivio dati (l’SSD) e l’usufruitore, che sia la CPU o la GPU. Nell’architettura PC tradizionale invece, per trasferire i dati tra disco e GPU, occorre passare dalla RAM.

Come se non bastasse, è stato implementato anche un layer di decompressione in harware (Kraken decompressor).

In termini numerici, PS5 è in grado di trasferire 5.5 GB di dati compressi al secondo e decomprimerli mentre vengono trasferiti. Nel caso migliore si parla di 22 GB/s di dati grezzi. Per fare un paragone, un buon SSD per PC ha prestazioni di circa 3.5 GB/s.

Un trasferimento dati estremamente veloce implica sì tempi di caricamento ridotti (di due ordini di grandezza rispetto all’hardware attuale, sostengono i tecnici Sony), ma non è tutto! La richiesta di un dato ora può essere soddisfatta direttamente nel ciclo di rendering attuale, o alla peggio nel successivo: ad un frame rate di 60 FPS (lo standard per i futuri giochi) stiamo parlando di 16 ms di latenza massima.

Quest’ultima affermazione, sulla carta interessante, non ha lasciato il segno più di tanto… finché qualcuno non ha dato una diretta dimostrazione delle implicazioni che delle simili prestazioni comportino veramente.

Il 13 maggio 2020 Epic Games, conosciuta principalmente per il videogioco Fortnite e per essere il creatore del motore grafico Unreal Engine, ha mostrato il nuovo Unreal Engine 5 girare su HW PS5.

Le innovazioni tecnologiche e grafiche sono numerose, ma quella che ci interessa relativamente al discorso I/O è la tecnologia chiamata Nanite.

Epic ha riscritto il sottosistema di I/O del suo software per adattarlo all’hardware PS5 e per sfruttare la maggior larghezza di banda e le latenze ridotte. Il risultato ha sbalordito gran parte degli “addetti ai lavori”, perché il concetto stesso di streaming degli asset è stato portato ad un nuovo livello.

Unreal Engine 5 su PS5: viene mostrato l’alto dettaglio grafico e l’illuminazione globale in tempo reale

Su disco ora risiede l’intera geometria da renderizzare, non semplificata, e ad ogni frame l’engine si preoccupa di prelevare da essa solo i triangoli che andranno effettivamente a comporre l’immagine su schermo.

In fase di creazione degli asset, le geometrie non devono più essere semplificate in versioni low poly e i dati non devono più essere trasferiti a runtime nella RAM. Inoltre, sempre in fase di creazione, non è più necessario creare mappe di texture per simulare in maniera fittizia i dettagli degli oggetti, dato che le geometrie, arbitrariamente complesse, possono essere direttamente utilizzate.

Visualizzazione delle geometrie della scena di Unreal Engine 5: i singoli triangoli sono talmente piccoli che l’occhio umano non riesce a distinguerli

Stando alla presentazione, è possibile avere su disco scenari composti da miliardi di triangoli (ben più che qualsiasi sistema sia in grado di riprodurre in tempo reale) e utilizzare questa base dati per renderizzare ad ogni frame, 60 volte al secondo, solo quelli effettivamente utili a riprodurre l’immagine da mostrare a schermo (nell’ordine di 10-20 milioni, equivalente alle capacità attuali di un PC di fascia alta).

Nanite sembra essere il sacro graal della computer graphics, infatti comporta vantaggi tali da stravolgere in parte il processo creativo di contenuti 3D realtime (rimasto pressoché inalterato negli ultimi 20 anni):

  • la creazione degli asset è enormemente velocizzata, dato che intere fasi di lavorazione ora possono essere evitate (semplificazioni, creazione di livelli di dettaglio – LoD, materiali e mappe);
  • gli asset per i contenuti realtime (videogiochi, serious games, …) e prerenderizzati (film, animazione, …) possono essere gli stessi, avvicinando sempre di più i due mondi;
  • in fase di visualizzazione è possibile usufruire, in tempo reale, di un livello di dettaglio delle geometrie semplicemente impensabile fino ad ora.
Unreal Engine 5 su PS5: ogni statua è composta da più di 33 milioni di triangoli

Tim Sweeney, CEO di Epic Games, ha detto: “For PC enthusiasts, the exciting thing about the PS5 architecture is that it’s an existence proof for high bandwidth SSD decompression straight to video memory. Is this enough to get Microsoft, Intel, NVIDIA, Intel, and AMD to work together and do it? We’ll see!”. Traducendo liberamente: “La parte eccitante di PS5 è che è la prova materiale del trasferimento ad alta velocità e decompressione direttamente nella memoria video. Sarà sufficiente a far sì che Microsoft, Intel, NVIDIA, Intel e AMD lavorino assieme per realizzarla (nei PC)?”.

Senza entrare nel merito della cosiddetta console war (tanto cara agli appassionati di Sony e Microsoft), vorrei precisare che si tratta solo di un tassello del puzzle che decreterà il successo di questa o quest’altra console. Posso però affermare che Sony ha effettuato delle scelte sicuramente originali in termini architetturali. Microsoft, a dirla tutta, sta seguendo un approccio non del tutto dissimile (lo chiamano DirectStorage), ma la soluzione Sony sembra avere una marcia in più.

Quello che sarà interessante vedere, piuttosto, sarà come il mondo PC reagirà a questa innovazione e come gli sviluppatori sapranno interpretarla per utilizzarla al meglio. E non parlo solo di videogiochi, ma anche del mondo professionale: al giorno d’oggi i progetti CAD possono tranquillamente raggiungere diversi TeraByte di dimensione su disco, e poterli vedere in tempo reale su di uno schermo (o perché no, in realtà virtuale o aumentata) porterebbe dei guadagni enormi in termini di efficienza ed efficacia di progettazione/testing/training.

di Christian Bar

Orbyta Tech

VR/AR Specialist


I vantaggi di un Message Broker

I vantaggi di un Message Broker nelle moderne architetture distribuite (con riferimenti a RabbitMQ)

Le architetture moderne si stanno sviluppando in una direzione fortemente decentralizzata (si pensi a quanti software oggigiorno vengano progettati in un’ottica di microservizi, o come la distribuzione del consenso sia il cardine di un intero nuovo filone tecnologico quali le blockchain e le criptovalute). Non è in realtà un argomento nuovo nel mondo dell’informatica, già nei giorni della sua creazione nel 1969, il sistema operativo UNIX dovette operare una scelta nel disegno del kernel fra un approccio monolitico (struttura che oggi chiameremmo centralizzata) e uno a microkernel (decisamente prossimo al concetto di decentralizzato). Poiché nessuno dei due approcci presenta solo vantaggi, come spesso accade, si è assistito a guerre di religione in proposito, dove non sono mancati tentativi di compromesso con architetture kernel-ibride (ossia gli attuali Windows e MacOS). Al momento uno dei requisiti su cui si pone maggiore attenzione è fornire sempre e comunque un risultato, senza per questo doversi affidare unicamente alla replicazione di intere strutture hardware e software per garantire la continuità servizio.

Dividere quello che un tempo era un singolo servizio su N microservizi porta una serie indubbia di vantaggi:

  1. maggiore facilità nella scalabilità: trattandosi di un’attività micro è ragionevole pensare che possa essere avviata e replicata molto in fretta (approccio che vede il suo attuale apice nei docker e in kubernates);
  2. maggiore manutenibilità del software: è di nuovo ragionevole pensare che un microservizio faccia un numero veramente ridotto di attività, portando vantaggi nella scrittura, nel testing e nel bug fixing;
  3. elevato disaccoppiamento dei processi: ognuno, almeno in teoria, dovrebbe poter vivere di vita propria attendendo richieste a cui dare risposta, disinteressandosi del sistema macroscopico in cui è stato inserito;
  4. minore impatto in caso di disservizio: se un microservizio non fosse disponibile questo non impatterebbe sul resto del sistema che continuerebbe a fornire risposta (sebbene magari solo parziale).

e questi solo per elencare i principali. C’è però un ovvio rovescio della medaglia: per quanto sarebbe perfetto poter disegnare un’architettura in cui ogni componente non ha mai necessità di scambio dati con gli altri, questo non si rivela possibile nella pratica. Ogni componente è costantemente interessato in uno scambio di informazioni con gli altri.

Il colloquio fra i diversi processi (o moduli) può essere affrontato in svariati modi:

  1. Remote Procedure Call (RPC): il più classico e utilizzato fin dagli albori dei sistemi distribuiti. Ogni processo conosce il protocollo di colloquio verso ciascuno degli altri componenti, ed è compito di ognuno stabilire e mantenere i canali di comunicazione. Se la controparte sia o meno attiva al momento della chiamata non è dato saperlo a meno di prevedere sempre e comunque una risposta all’interno del protocollo (cosa spesso controproducente o impossibile in caso di vincoli stringenti sulle performance). In questo approccio è l’utente finale a non sapere se la sua richiesta è stata eseguita localmente o su un dispositivo remoto.
  2. Object Request Broker (ORB): la naturale evoluzione di RPC, con l’avvento dell’astrazione introdotto dalla programmazione orientata agli oggetti. In questo paradigma i processi non sono consapevoli se la loro richiesta sia locale o remota, dal momento che la risoluzione della chiamata è affidata ad uno strato software che maschera questo aspetto. Inoltre, tutte le attività di trasmissione dei messaggi (serializzazione, deserializzazione e eventuali verifiche di integrità e ritrasmissione) risulta trasparente. In questo approccio si vuole mascherare ai processi stessi se la loro richiesta sia locale o remota.
  3. Message Oriented Middleware (MOM): l’ultimo approccio nato. In questo paradigma il colloquio tra i processi è egli stesso un processo, dedicato esclusivamente a mascherare ogni dettaglio relativo a protocollo di rete e di trasporto. Questi processi nascono per coprire il maggior numero di piattaforme software possibile (sia per quanto riguarda si sistemi operativi supportati, sia per i linguaggi di programmazione in cui queste librerie vengono fornite). Questo approccio porta ad un maggiore disaccoppiamento tra i processi rispetto ai due precedenti, che sono consapevoli di delegare ad un modulo terzo la gestione della loro comunicazione.

Per fare un’analogia, potremmo pensare ai due estremi: RPC e MOM. Il primo è l’equivalente di un messaggero medievale, istruito dal proprio committente sui tempi e le modalità con cui un messaggio accuratamente preparato e sigillato dovrà essere recapitato, ed eventualmente fornito di una scorta più o meno numerosa che possa garantire la sicurezza della missiva da consegnare. Sicuramente, essendo ogni aspetto curato e calibrato sull’importanza del messaggio, da ogni comunicazione otterremo il massimo dell’efficienza e dell’affidabilità, a fronte di un dispendio di energie e risorse notevole. Il secondo è più simile ad un ufficio postale, a cui affidiamo il nostro messaggio con un numero minimo di istruzioni a riguardo, a cui assegniamo una priorità e eventuali istruzioni per una notifica di recapito. Non ci si cura di altri aspetti, sapendo che l’ufficio postale è sicuro e affidabile (se non dovessimo fidarci delle poste potremmo sempre affidarci ad altri corrieri volendo ☺ ).

I Message Broker sono i software che implementano il paradigma MOM e forniscono tutti i servizi (in forma di API) necessari alla distribuzione di messaggi, definendo al loro interno le strutture necessarie a definire le rotte di instradamento e le politiche di recapito.

Uno dei più vecchi software di questo tipo è IBM MQ, nato nel 1993, mentre ai giorni nostri i sistemi più diffusi sono RabbitMQ e Kafka. I sistemi collocati all’interno dei Cloud possiedono ovviamente le proprie versioni gestite, abbiamo ad esempio Azure Service Bus fornito da Microsoft e Simple Queue Service fornito da Amazon. Fondamentalmente tutti questi sistemi offrono le medesime funzionalità, sebbene le specifiche implementazioni presentino punti deboli e punti forti rispetto ai contesti iniziali in cui sono stati concepiti. Giusto per fare un esempio:

  1. RabbitMQ: nasce principalmente come simulatore di reti sfruttando il protocollo AMPQ (Advanced Message Queuing Protocol), è pertanto facilitata la creazione di nuovi nodi e di nuovi collegamenti fra i vari processi che lo sfruttano.
  2. Service Bus: nasce come Message Broker puro, pertanto è possibile creare dinamicamente nodi e collegamenti, ma l’approccio è più complicato, in quanto lo scenario di utilizzo privilegiato è quello in cui la topologia della rete di scambio messaggi sia definita a livello architetturale prima dell’avvio dei processi.
  3. Kafka: nasce con lo scopo di distribuire il maggior numero di messaggi possibili all’interno di chat e gruppi. Avendo come obiettivo quello di massimizzare il throughput di messaggi recapitati, è poco adatto ad essere utilizzato negli scenari in cui la sequenza di consegna dei messaggi è un vincolo, dal momento che tutte le ottimizzazioni del sistema sono orientate alla consegna in qualsiasi ordine essa avvenga, e forzarlo ad un comportamento “sequenziale” fa perdere il grosso dei vantaggi di questo message broker.

Poste queste indicazioni di massima, possiamo però definire quelli che sono i capi saldi del paradigma MOM:

  1. I messaggi vengono inseriti all’interno di code (da cui il costante riferimento di molti software all’acronimo MQ, Message Queue) per cui è garantita la persistenza. Se il destinatario non fosse disponibile, il messaggio resterebbe in coda in attesa di essere recapitato;
  2. È possibile definire dei nodi di smistamento. Questi possono prendere diversi nomi a seconda del broker (ad es. Topic in Service Bus o Exchange in RabbitMQ) ma la loro funzione è quella di distribuire i messaggi a tutti coloro che si sottoscrivono a quel nodo.

Con questi due semplici oggetti, è possibile costruire topologie in cui la comunicazione avviene punto-punto (tra mittente e destinatario è presente una coda) oppure punto-multipunto (in cui un mittente inoltra un messaggio ad un nodo, e questo lo distribuisce a tutti i sottoscrittori collegati).

Punto di forza di questi sistemi è la possibilità di definire delle politiche di sottoscrizione ai nodi. I nodi più semplici, di base, replicano il medesimo messaggio a tutti i sottoscrittori, ma impostando delle regole, ed eventualmente ponendo dei nodi in cascata, è possibile recapitare i messaggi con certe caratteristiche ai sottoscrittori che le hanno richieste. Vediamo alcuni esempi per chiarire tutti questi concetti.

Creazione di una coda in RabbitMQ (in C#)

Nel classico paradigma Produttore/Consumatore immaginiamo di voler mettere in collegamento le due entità grazie a RabbitMQ. Lo strumento adottato è la Coda.

Il produttore deve semplicemente inserire il messaggio nella coda, e confidare nella bontà di RabbitMQ. La coda conserverà i messaggi fintanto che il consumatore non avrà la possibilità di prenderli in carico, fornendo implicitamente un meccanismo persistente di gestione dei dati. La lettura dei messaggi può avvenire in modo:

  1. Sincrono: il consumatore interroga la coda per farsi restituire uno o più messaggi
  2. Asincrono: la coda notifica al consumatore la presenta di un messaggio, consegnandolo al metodo preposto a fungere da Event Handler

La coda può essere definita con una serie di proprietà:

  1. Time-To-Live: ad ogni messaggio può essere impostato un TTL, oltre il quale viene rimosso dalla coda in quanto “scaduto”
  2. Queue Expiring: se una coda non è ascoltata da nessun consumatore, può rimuoversi dal sistema
  3. Dead Letter Queue: è possibile definire una coda in cui tutti i messaggi scaduti o finiti in errore per qualche motivo (solitamente errori di instradamento nelle topologie più complesse) finiscano, così da poterne tenere traccia coi log ed eventualmente gestirli in modo puntuale con un processo dedicato. Sarà compito di RabbitMQ spostare i messaggi nella Dead Letter senza alcun intervento da parte dei processi o degli sviluppatori

Il codice necessario per implementare questo dialogo punto-punto prevede la creazione di un metodo di spedizione (che chiameremo Send) e uno di ricezione (analogamente Receive):

Punti di interesse di questo codice sono la factory, ossia la classe fornita dalla libreria RabbitMQ .NET per la creazione e la gestione del servizio. È sufficiente indicare l’indirizzo del server su cui RabbitMQ è in funzione, ed è possibile aggiungere i classici dati generali quali user e password (N.B. se RabbitMQ è installato in localhost, non è necessario nulla per poter funzionare oltre all’HostName, ma se ci si collega da remoto è indispensabile creare un utente, dal momento che l’utente di default è abilitato alle API solo se queste richieste provengono dal sistema locale).

Una volta ottenuta la factory, è possibile usarla per creare una connection, ovvero aprire una connessione TCP per dialogare con RabbitMQ. Attraverso la connection possiamo creare un model, esso rappresenta il collegamento verso le strutture interne di RabbitMQ. Possiamo pensare alle due entità in questo modo:

  1. Connection: il canale con cui si dialoga con il servizio server rappresentato da RabbitMQ
  2. Model: il canale con cui si dialoga con nodi e code presenti all’interno di RabbitMQ

La flessibilità è totale, ma le best practice consigliano di creare una sola connection verso RabbitMQ da tenere attiva il più a lungo possibile, mentre di creare i model al bisogno quando si vuole spedire. Nulla vieta di mantenere un model per un tempo indefinito, specie se si avesse un traffico continuo e l’overhead di creazione e distruzione del model fosse eccessivo.

Una volta ottenuto il model è possibile creare o collegarsi ad una coda. Una coda deve avere un nome univoco e una serie di caratteristiche che possono essere definite alla creazione, e devono coincidere per chiunque intenda collegarcisi sia in spedizione che in ricezione. L’invio del messaggio avviene semplicemente attraverso il metodo BasicPublish, a cui è normalmente sufficiente fornire il messaggio sotto forma di byte array. Gli altri elementi del BasicPublish li vedremo tra poco nel prossimo esempio.

Il Receive invece verrà scritto in questo modo:

I punti di interesse in questo caso sono due: la registrazione dell’EventingBasicConsumer, ossia il gestore a cui sarà legato l’evento di ricezione e il metodo da invocare ogni qual volta la coda notificherà la presenza di un nuovo messaggio; il metodo BasicConsume, in cui è possibile definire una peculiarità del ricevente: quando il messaggio viene notificato è possibile stabilire due momenti differenti in cui confermare la rimozione di quest’ultimo dalla coda:

  1. Con Ack esplicito: restituito dal metodo di gestione del messaggio al termine della sua elaborazione (al termine per convenzione, nulla vieta di farlo al principio, ma se si sceglie per l’Ack esplicito tipicamente è per avere il maggior controllo possibile su quanto succede)
  2. Con Ack implicito: in questo caso l’Ack viene restituito immediatamente appena il metodo viene invocato, cancellando il messaggio dalla coda. Questo approccio espone al rischio di perdita del messaggio qualora la sua elaborazione fallisca, ma si rivela più performante e mantiene la coda mediamente più scarica, dal momento che l’Ack viene restituito immediatamente e non al termine di eventuali lunghe elaborazioni

È possibile, mettere più consumatori in concorrenza sulla medesima coda, in questo caso RabbitMQ notificherà ai consumatori rispetto ai parametri che questi hanno impostato. Tipicamente, in presenza di Ack impliciti, la risoluzione avviene in round robin sui consumatori. Se invece gli Ack sono espliciti, possiamo andare a definire il BasicQos (Quality of Service).

channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);

Aggiungendo questa istruzione dopo la QueueDeclare è possibile definire a livello globale (qualora si voglia fornire questi parametri a tutti i model) o solo al model specifico che sta invocando il metodo BasicQos, due parametri: il prefetchSize e il prefetchCount.

  1. Il Prefetch Size specifica quanti messaggi possono essere inviati in anticipo, ossia quanti messaggi possono essere notificati senza aver ricevuto un Ack (ha pertanto senso solo quando viene utilizzato l’Ack esplicito). In questo modo si genererà una “minicoda” interna al consumatore, fatta con gli eventi di notifica dei messaggi che si accumulano. Il valore 0 indica l’assenza di limiti
  2. Il Prefetch Count indica la finestra del consumatore, ossia quanti messaggi sono destinabili a quel consumatore prima che questi abbia iniziato a notificare gli Ack. Va a definire in pratica quando la “minicoda” di eventi può essere lunga per uno specifico consumatore

Per inviare un Ack esplicito, normalmente al termine del metodo usato come handler del consumer.Received si pone

channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);

In cui il DeliveryTag indica il messaggio per cui si sta notificando l’Ack, mentre il flag seguente serve a gestire le politiche di rimozione, ossia se un messaggio debba essere rimosso subito alla prima notifica oppure se debba essere recapitato a tutti i consumatori, pertanto fin quando non sarà ricevuto un numero di Ack pari al numero di consumatori collegati (o un Ack che non richieda ulteriori attese), il messaggio resterà in coda (fatte salve eventuali politiche legate al TTL). Sarà compito di RabbitMQ notificare un nuovo messaggio coerentemente con le politiche impostate a ciascun consumatore. Se ad esempio C1 e C2 debbono entrambi processare gli stessi messaggi, ma C1 è molto più veloce di C2, avremo che per ogni nuovo messaggio, C1 riceverà una notifica coerente con la sua impostazione di prefetch, e i messaggi gli verranno inoltrati non soltanto dalla testa della coda, dove sono presenti messaggi a lui già recapitati che sono in attesa di essere notificati anche a C2 o che semplicemente sono ancora in attesa di un Ack. È importante notare come tutta questa gestione sarebbe normalmente molto complessa, ma viene compresa tra le comuni configurazioni di base di RabbitMQ che fornisce questo scenario senza alcuno sforzo o degrado di performance.

Creazione di un nodo di distribuzione in RabbitMQ (in C#)

Molto più interessante è la possibilità di creare nodi di distribuzione (chiamati Exchange) e definirne le politiche di instradamento. È giusto svelare che le immagini viste poco fa sono in realtà la concettualizzazione della comunicazione punto-punto, ma nella realtà le implementazioni prevedono tutte l’introduzione di un nodo di distribuzione tra il produttore e la coda di consegna. Da notare quindi, in riferimento al precedente Send, che quelle due proprietà lasciate un attimo in sospeso (exchange: “”, routingKey: “hello”) vanno ad indicare che non abbiamo dato un nome all’exchange (sebbene esista per costruzione, ma di cui ci disinteressiamo in quanto parte del funzionamento interno di RabbitMQ) e opzionalmente una routingKey. Tale routingKey è inutile nell’esempio ma ci permette di dire che volendo possiamo precisare al consumatore che deve conoscere la routingKey dei messaggi per poterli ascoltare, e anche se non rappresenta un meccanismo di sicurezza, è utile definirne sempre una per evitare errori e non veder recapitati messaggi sbagliati ad un consumatore che stiamo testando.

Quel nodo, chiamato Exchange, diventa molto importante qualora la distribuzione non sia su una coda soltanto, ma su una serie di Subscriber, vengono infatti chiamati sottoscrittori i consumatori che collegano una coda ad un nodo di distribuzione.

I nodi in RabbitMQ sono di 4 tipi, e bene o male ritroviamo concetti analoghi in altri software, anche se con nomi differenti:

  1. Fanout: il nodo replica i messaggi su tutti i sottoscrittori
  2. Direct: ogni sottoscrittore chiede una o più routing key, quella che potremmo intendere come la categoria dei messaggi a cui vorrei fare l’abbonamento
  3. Topic: analogo al Direct, solo che in questo caso le routing key possono essere descritte mediante l’uso di caratteri speciali (wildcard)
  4. Header: il meccanismo più complesso, consente di scegliere le politiche di distribuzione dei messaggi rispetto a campi specifici che compaiono nell’header del messaggio. Servirà definire i campi di interesse, i valori e i criteri di confronto su cui basare l’instradamento.

Facendo un esempio analogo al precedente, avremo un produttore fatto in questo modo:

In sostanza l’unica differenza consiste nell’uso di ExchangeDeclare in sostituzione al QueueDeclare.

Uno dei sottoscrittori invece, avrà questa forma:

I punti di interesse qui sono i seguenti:

  1. Notiamo che anche il ricevitore dichiara l’exchange. Questo perché la ExchangeDeclare (e analogamente la QueueDeclare) crea o trova l’exchange con quel nome. Questa è una peculiarità di RabbitMQ in effetti, altri message broker hanno classi e metodi dedicati alla creazione e al collegamento con nodi e code, mentre in RabbitMQ questa distinzione è trasparente allo sviluppatore. Questo rappresenta un vantaggio qualora volessimo creare una struttura con delle date di scadenza (gli expiring citati in precedenza). Se le strutture si autoeliminano dopo un certo tempo, quando un processo si attiva e prova a inviare o ricevere un messaggio rischia di non trovare la coda o il nodo a cui collegarsi (cosa frequente per chi usa Service Bus, che predilige un approccio statico della topografia) generando un errore. In RabbitMQ invece, chi parte per primo crea la struttura, che i successivi utilizzatori, siano essi produttori o consumatori, si troveranno già fatta, e dovranno semplicemente collegarcisi.
  2. Il sottoscrittore procede poi creando una coda (lasciando che venga generato un nome univoco dalla libreria)

Il sottoscrittore collega la coda appena creata al nodo (con l’operazione di QueueBind). In questo modo andiamo a disegnare la struttura della rete che poi useremo per la distribuzione dei messaggi. In particolare in RabbitMQ si osservi che il QueueBind va ripetuto per ogni RoutingKey che si vuole ricevere su quella sottoscrizione, mentre altri broker consentono sottoscrizioni multikey (passando ad esempio degli array di string).

Questa potrebbe, ad esempio, essere la topologia di un sistema di log differenziato per livello.

E infine un esempio di Topic, in cui lo smistamento avviene per mezzo di caratteri speciali:

In RabbitMQ abbiamo che:

  1. * indica esattamente un parola
  2. # indica zero o più parole

Adottando questi caratteri speciali con questi significati, per costruzione le Routing Key prendono la forma di frasi separate da punto.

Infine, volendo schematizzare un exchange di tipo header:

In questo caso è bene precisare che l’estrema potenza nella definizione delle regole di instradamento si paga con un degrado del throughput finale del sistema.

Parlando di prestazioni

Ogni message broker ha punti di forza su cui cerca di fare leva per guadagnarsi una posizione di rilievo in uno specifico settore. Kafka punta sul numero di messaggi trasmessi, ActiveMQ punta ad essere il compromesso migliore tra dimensione dei messaggi, numeri di messaggi e latenza complessiva senza eccellere in nulla, RabbitMQ punta sulla estrema flessibilità della sua capacità di routing senza che questa impatti troppo sul risultato finale. Prima di adottare uno di questi è sempre bene documentarsi e cercare i benchmark che rispecchiano il proprio caso di utilizzo, e non cercare di risolvere ogni problema con il medesimo strumento. Quasi tutti i message broker possono reggere messaggi di piccole dimensioni (1Kb) su normali PC di sviluppo possono raggiungere i 50.000 messaggi al secondo, un valore discreto per ogni genere di test.

Giusto per dare un’idea del tipo di analisi che si possono fare su un message broker, vediamo brevemente alcuni dei risultati pubblicati di test effettuati con RabbitMQ.

Un test molto importante è il confronto fra messaggi inviati e byte inviati. Mandare molti messaggi di pochi byte comporta un overhead notevole, con conseguente degrado di performance, mentre inviare pochi messaggi di alcuni mega si rivela particolarmente deleterio per il numero di messaggi che si possono recapitare. Qualora si fosse nella prima condizione, una possibile strategia è l’invio di liste di messaggi, in modo da avvicinarmi all’incrocio del trade off. Con il grafico riportato, ad esempio, vediamo che abbiamo la possibilità di inviare 4000 messaggi da circa 256 byte. Se i nostri messaggi fossero da 20 byte, opportunamente serializzati ci permetterebbero un risultato finale di 4000 messaggi da liste di 240 byte (approfittando quindi di un fattore moltiplicativo pari a 12, portando il numero di messaggi reali inviati a 48.000)

Questa tecnica rientra nell’approccio batch processing, e si possono ottenere moltiplicatori importanti se ben sfruttata.

Un altro test riguarda la valutazione del message rate rispetto alla dimensione del messaggio in byte all’aumentare del numero di produttori.

Da confrontarsi con il corrispettivo andamento del traffico dati se si analizzano i byte trasmessi anziché i singoli messaggi.

Tirando le fila

In conclusione, i vantaggi nell’utilizzare i message broker sono da ricercarsi nell’elevata efficienza e scalabilità che possono fornire all’interno di un’architettura distribuita e nella rapidità dello sviluppo del software secondo l’approccio a microservizi, soluzione nella quale la parte di colloquio tra processi può essere ampiamente delegata ai servizi di message broking. Gli svantaggi sono principalmente due: il primo è l’aumento della complessità dell’intera architettura, dal momento che si va ad inserire un modulo molto vasto e a cui vengono affidati compiti di primaria importanza (è bene una fase di studio preliminare molto ampia in modo da scegliere il software che meglio si adatta alle esigenze specifiche del progetto); in secondo luogo non sono uno strumento adatto per implementare chiamate sincrone. Sebbene sia possibile simulare chiamare RPC con l’uso di più code per trasmissione e ricezione dei dati, gli stessi autori ne sconsigliano l’uso se non in casi davvero eccezionali, dal momento che è un uso degenere dello strumento, nato per essere asincrono. Restano quindi svariate attività per cui è comunque richiestoi di implementare interfacce e protocolli di comunicazione allo sviluppatore, sebbene anche per questo scenario esistono ormai valide librerie che semplificano molto la vita (ad esempio gRPC).

Come corollario e per completezza è bene fare alcune precisazioni: l’approccio Message Oriented non necessariamente prevede la persistenza come requisito. Esistono casi in cui è preferibile una comunicazione transiente. I sistemi paralleli sono un caso particolare dei sistemi distribuiti, e nel caso di sistemi paralleli ci sono delle peculiarità che possono essere ottimizzate con soluzioni specifiche. Non esiste una linea di demarcazione netta tra ciò che è calcolo distribuito e calcolo parallelo, ma osservando gli estremi delle definizioni, possiamo dire che nei processi distribuiti un singolo problema viene diviso in più attività, e ciascuna attività viene svolta da un componente specifico, che dialoga in un flusso più o meno continuo con gli altri componenti. Nelle applicazioni parallele, ciascun componente svolge il medesimo compito concentrandosi su un sotto insieme dei dati disponibili. Le necessità di comunicazione in questi due scenari sono differenti, e quindi è preferibile adottare un sistema di scambio messaggi che non sfrutti le code e la loro persistenza, come il Message Passing Interface (MPI), dove il messaggio è più un sistema di coordinamento che non un vero e proprio input per gli altri componenti.

Per chi volesse approfondire un buon testo che presenta i diversi scenari dei paradigmi accennati è:
S. Tanenbaum, M. Van Steen, “Sistemi distribuiti. Principi e paradigmi”, Pearson Education Italia, 2007.

Mentre per approfondire i dettagli di RabbitMQ:

Infine, un ottimo esempio di test comparativo tra RabbitMQ e Kafka:

Scritto da
Michel Lamoure


Come migliorare l’efficienza e la produttività dell’AREA 51, di questi tempi…

In ORBYTA, in questo periodo di lavoro remoto del team di sviluppo, abbiamo recentemente introdotto una modalità di pair-programming per mezzo di strumenti di collaborazione real-time. I risultati ottenuti sono quelli di aver prodotto sessioni di programmazione più attente, una collaborazione più semplice e un migliore lavoro di squadra nel suo insieme. Nelle righe di questo articolo cercheremo di esporre le nostre considerazioni a riguardo e il bagaglio di esperienza che potremo portare con noi ad emergenza finita.

Live Share: la funzionalità che rende possibile la magia 

Alcuni strumenti integrati di sviluppo, come VS Code e Visual Studio .net 2019, forniscono estensioni per consentire la collaborazione di codice in tempo reale. La funzionalità in questione si chiama “Live Share”. Per mezzo di Live Share ci si può collegare ad una sessione di collaborazione e modificare gli stessi file in tempo reale, proprio come è possibile editare un documento di testo in maniera collaborativa con Google Docs

Per mezzo di questa estensione è possibile partecipare al lavoro del collega, suggerire modifiche, intervenire, osservare il cursore, la selezione e tutto ciò che le altre persone stanno digitando.

Schermata di VS Code in Live Share
Schermata di VS Code in Live Share

Pair-programming in ufficio e in remoto

In tempi differenti, il team di sviluppo era solito effettuare sessioni di pair-programming in cui 2 sviluppatori potevano condividere una postazione di lavoro e si alternavano alla tastiera per effettuare task particolarmente complicati, refactoring, progettazione di componenti e revisione di codice.
Non potendo più usufruire del nostro ufficio, ci siamo trovati nella condizione di dover estendere questa pratica di lavoro anche lavorando da remoto. 

In principio, come tutto il mondo, abbiamo fatto uso massivo di sessioni di condivisione schermo con i nostri canali di comunicazione, che vanno da Microsoft Teams, Hangouts, Cisco Webex, Skype per mezzo di call con condivisione schermo. Dopo i primi giorni in cui lo slancio generale era maggiore, questa modalità ha evidenziato alcune problematiche che ci hanno spinto a provare vie differenti.

Live Session in VS Code
Live Session in VS Code

Di seguito proveremo a riassumere alcuni degli aspetti positivi rilevati utilizzando Live Share in luogo di una semplice condivisione schermo.

1. Aumenta il coinvolgimento e aiuta a mantenere l’attenzione

Quando si fa pair-programming, di solito si finisce per avere una persona che scrive tutto il codice (il “driver”) e l’altra che discute delle scelte di implementazione, dando suggerimenti (il “navigatore”). Ma è difficile per questa persona rimanere concentrati dopo poche ore. Soprattutto dopo che l’attività è stata chiaramente definita e tutto ciò che resta da fare è scrivere il codice.
È ancora più difficile quando lavori in remoto, perché perdere la concentrazione durante un Hangouts o Teams è molto facile.

Live Session in Visual Studio .net 2019
Live Session in Visual Studio .net 2019

Quando il livello della comunicazione decade si è costretti ad interrompere la programmazione, dividere il lavoro o scambiarsi di ruolo o … lasciare addormentare la persona che non fa nulla. La cosa eccezionale della collaborazione live è che ciò non accade facilmente. Dato che state modificando entrambi lo stesso codice in tempo reale, è possibile accedere rapidamente al codice sorgente. 

Durante una sessione di Live Share è anche possibile suddividere le attività, senza interrompere la programmazione in coppia, il che è più semplice e rende immediata la sincronizzazione del codice, non necessitando differenti push di codice. Ciò, infatti, evita conflitti sullo strumento di controllo della versione quando si lavora sulla stessa funzionalità. Il codice sorgente modificato risulta essere solo quello dell’utente che avvia la sessione di condivisione.

2. Migliora la comunicazione

In sessioni di condivisione schermo ci si ritrova quasi sempre a dettare il codice al collega, a provare a spiegare qualcosa senza riuscirci facilmente. Con la collaborazione live, non è necessario farlo: puoi semplicemente digitarlo, il che ti farà risparmiare innumerevoli “Non l’ho capito” o “Potresti inviarmelo in chat?”.

3. Permette collaborazione ed esplorazione

Di solito è difficile partecipare a una sessione di programmazione in coppia in corso: è necessario comprendere l’attività, riconoscere cosa è stato fatto, cosa è rimasto da fare e come vengono costruite le cose.

Quando si partecipa a una sessione di collaborazione live, d’altra parte, è possibile impiegare del tempo per esplorare e comprendere da soli, senza dover interrompere gli altri programmatori. Abbiamo trovato che ciò è particolarmente utile, perché è anche un buon modo per avere un’opinione esterna su qualcosa senza ottenere spiegazioni forse distorte. E se vuoi che i tuoi colleghi ti diano una presentazione, c’è una modalità presentatore che seguirà il cursore del presentatore. Un bel modo di mostrare la tua idea.

4. Rimani nel tuo ambiente di sviluppo preferito 

Quando lavori in coppia alla postazione di un collega sei costretto ad usare un ambiente di sviluppo, tema, snippets, estensioni, colori di sistema, tastiera e mouse a cui non sei abituato e che ti rallentano nel lavoro. Con Live Share puoi saltare direttamente all’attività senza dover decifrare la strana configurazione di Visual Studio o utilizzare il mouse verticale che non sai minimamente utilizzare.

5. Riduce le distanze e il senso di alienazione

Poter lavorare sullo stesso task o su funzionalità differenti in contemporanea, con uno dei due che scrive la parte di test automatici o effettua una verifica in tempo reale del tuo codice, permette ai componenti del team di sentirsi sempre parte del progetto, meno soli e alienati dal mondo esterno, a cui si era soliti appartenere. Lo sviluppo del codice di test in pair-programming consente, oltretutto, di verificare se entrambi hanno capito il compito allo stesso modo e hanno pensato agli stessi casi limite o alle stesse scelte progettuali. È anche un modo per attenuare il disagio che l’attuale processo di lavoro oggettivamente comporta.

Configurazione e condivisione

Cosa bisogna fare per provare Live Share? È possibile farlo per mezzo di Visual Studio 2019, senza dover installare estensioni o anche utilizzando VS Code e l’estensione Live Share.

VS Code

In VS Code sarà sufficiente installare l’estensione VS Code Live Share, aprire un progetto e cliccare sul link per condividere una sessione di codice.

Installazione di Live Share Extension per VS Code
Installazione di Live Share Extension per VS Code

Di seguito la schermata di benvenuto di Live Share con la descrizione di tutte le funzionalità.

Funzionalità di Live Share Extension per VS Code
Funzionalità di Live Share Extension per VS Code

Una volta generata la sessione, l’utente destinatario riceverà un link che potrà utilizzare per accedere anche lui alla sessione.

Schermata di condivisione link di sessione di Live Share
Schermata di condivisione link di sessione di Live Share

Oltre che condividere il codice, in VS Code, è possibile aprire un terminale condiviso, disponibile per tutti nella sessione. Questo è utile per lanciare comandi, eseguire test quando non si è host, perché non hai i file sorgente sul tuo computer.

In VS Code, puoi anche avviare una chat vocale da una sessione di collaborazione e hai una chat di testo. Quindi è superfluo utilizzare altre applicazioni per comunicare a voce.

E qualcosa rimane

In questa situazione globale in cui ci troviamo, per un motivo o per un altro, il numero, la frequenza delle chiamate, delle sessioni video è aumentato a dismisura. Non altrettanto questo sta influenzando positivamente la produttività dei team di sviluppo. Col passare dei giorni, poi delle settimane, quella sorta di slancio legato alla nuova modalità di lavoro da remoto si è senza dubbio affievolita. Sta nascendo, però, la consapevolezza che un modo nuovo di lavorare è necessario oggi, ma lo sarà ancor di più nelle fasi successive di questa nostra epoca. 

Dobbiamo consolidare e possibilmente migliorare questa nuova modalità di fare gruppo, essere efficienti e produttivi, anche se solo fisicamente distanti. Restiamo uniti… anche in Live Share.