Agitateur de Neurones

Python & Informatique Quantique

Il y a une petite dizaine d’années, quand mes élèves ingénieurs me disaient tout le bien qu’ils pensaient du langage Python, je leur répondais, narquoisement, que j’avais été élevé aux vraislangages comme le C ou le Fortran. D’aucuns avaient pourtant essayé de me contaminer avec des horreurs comme le Lisp ou Java mais j’avais tenu bon à grands coups revanchards d’assembleur si nécessaire. Je n’avais trahi que pour le C++. J’avais même évité PHP, c’est dire. J’étais alors loin d’imaginer qu’en 2019, j’écrirais un article sur le Python… et encore moins, implémenté sur un ordinateur… quantique !

Nous avons commencé à travailler sur le quantique il y a quasi dix ans, principalement sur les aspects liés à la cybersécurité (cryptographie et Internet quantique). Depuis ces 3 dernières années, tout s’est accéléré : communauté, simulateurs, outils, barrières technologiques qui sautent. Même s’il reste encore énormément de travail à réaliser et de sauts technologiques à faire, toutes les parties prenantes sont confiantes dans cette dynamique positive. Comme j’aime à dire : “no pain, no gain, high pain, high gain”

Nous avons créé deux formations sur l’informatique quantique :

C’est dans le cadre de cet atelier pratique que nous avons été amenés à créer quelques scénarios de programmation quantique, dans un premier temps sur simulateur puis dans un second temps… sur un vrai ordinateur quantique !

Le quantique, c’est un peu comme le sexe chez les ados. Beaucoup en parlent mais peu ont réellement pratiqué. Nous avons donc voulu mettre les mains dans le cambouis, enfin, dans le code, afin de comprendre réellement ce changement majeur de paradigme.

La suite de cet article est le récit synthétique de cette expérience qui j’espère vous passionnera autant que moi. Je dois vous avouer que j’en suis sorti un peu « changé ».

Spoiler :Vous allez vite le découvrir – donc je préfère vous le dévoilez tout de suite : je suis un piètre développeur en Python…

Je pars du principe que tous les lecteurs connaissent Python et disposent d’un environnement de développement adhoc (mon préféré : Visual Studio Code + Jupyter + Anaconda). 

Commençons donc par quelques notions d’informatique quantique afin que vous puissiez suivre ce que nous allons faire.

Tout d’abord, pourquoi faire de l’informatique quantique ? Parce que c’est plus cool que d’instancier pour la centième fois une C5n sur AWS… Oui, mais pas seulement.

En quelques mots et avec un grosse simplification que les experts me pardonneront bien volontiers, l’informatique quantique permet de résoudre certains problèmes avec une complexité polynomiale et non exponentielle. Pour le commun des mortels, cela signifie que casser certains algorithmes de cryptographie ou calculer la structure d’une protéine complexe pourra, grâce à l’informatique quantique, prendre un temps à l’échelle humaine – quelques minutes à quelques mois et non plus à l’échelle de l’univers – quelques centaines à quelques milliards d’années. On imagine aisément les applications et les attentes de la communauté scientifique. 

Un ordinateur quantique est un système (extrêmement complexe) qui contrôle des états quantiques, les fameux qubits. Le qubit, c’est la version quantique de nos fameux bits. A la différence de nos amis binaires, un qubit peut avoir la valeur 0, la valeur 1, ou… les deux en même temps (c’est ce qu’on appelle la superposition). Pour ne pas confondre avec les bits classiques, on utilise une notation particulière : |0>ou |1>.

Si on dispose d’un ordinateur avec 4 qubits, on peut ainsi avoir une superposition simultanéede 24états logiques distincts (soit 16 états) de |0000> à  |1111>. Oui, j’ai bien écrit simultanée ! Si on a la chance de disposer d’un ordinateur avec 32 qubits, on passe à 232états logiques simultanés, soit plus de 4 milliards d’états !

A titre de comparaison avec le monde réel que nous connaissons, imaginons que je dispose de 4 pièces de monnaie. Si je joue à pile ou face avec les quatre pièces en même temps, j’ai 24 possibilités, mais une fois les pièces lancées et retombées, j’ai un seul ‘résultat’ visible parmi les 24 possibilités. Chaque résultat a une probabilité de 1/24 (si les pièces sont idéales et non truquées). Les 4 qubits d’un ordinateur quantique existent eux dans 16 états en même temps ce qui permet de faire des calculs de manière très efficace (ce n’est toutefois pas du parallélisme).

Une autre notion importante de l’informatique quantique est l’intrication (entanglement). Deux qubits peuvent former un système avec intrication, ce qui signifie que l’état du système peut être connu de manière indépendante de l’état des qubits qui le composent. On parle aussi de corrélation entre les deux qubits. Si on observe un des qubits, on peut savoir quel est l’état de l’autre qubit s’il était mesuré de la même manière. Même si l’autre qubit est à l’autre bout de la planète ou de l’univers.  «Par la Force des qubits intriqués, intrigué tu seras».

Afin de pouvoir réaliser des traitements sur les qubits, on dispose de sortes de portes ou d’opérateurs quantiques (quantum gates), qui permettent de construire une logique quantique, tout comme on a construit une logique binaire dans les ordinateurs classiques.

Il existe actuellement deux principaux paradigmes majeurs en informatique quantique : 

  • Quantum Annealing (QA), dont les promoteurs sont les sociétés D-Wave Systems, Google et la NASA. Cette architecture est assez particulière et limitée en terme d’algorithmes quantiques. En particulier, elle ne peut pas exécuter le fameux algorithme de Shor qui permet de factoriser un entier dans un temps polynômial et non exponentiel. Par contre, les systèmes de ce type comportent déjà plusieurs centaines de qubits.
  • Gate Model (QM), est plus généraliste et indépendant d’un fabricant particulier. IBM et Rigetti Computing sont les promoteurs de cette architecture, proposent des outils et surtout un accès (limité) à leurs ordinateurs quantiques. C’est pour cela que nous avons retenu cette architecture. Pour l’instant, ces systèmes ne dépassent pas quelques dizaines de qubits.

Rigetti et IBM mettent à notre disposition deux frameworks, Forest SDK et Qiskit, respectivement.

J’ai un petit faible pour Qiskit d’IBM qui est disponibleen Open-Source.

Si vous voulez reproduire les exemples de cet article, vous devez installerQiskit sur votre ordinateur. Les prérequis sont : Ubuntu 16.04 ou ultérieur, macOS 10.12.6 ou ultérieur, Windows 7 ou ultérieur. Python 3.5 ou ultérieur.

Je ne saurais que trop vous recommander d’utiliser Anaconda pour disposer d’un environnement simple et sain. L’installation du framework se fait alors en quelques commandes (environnement quantum dans l’exemple ci-dessous, mais nous omettrons cette précision de l’environnement dans la suite de l’article) :

conda update -n base -c defaults conda

conda create -n quantum python=3

source activate quantum

conda activate quantum

pip install qiskit[visualization] qiskit-aqua 

Maintenant que vous avez installé votre environnement de travail, roulements de tambour ! Nous allons demander à IBM de nous permettre d’accéder à ses simulateurs d’ordinateurs quantiques mais aussi à ses ordinateurs quantiques véritables, disponibles via le Cloud (IBM Quantum Cloud Services).

De manière assez classique, nous allons créer un compteet récupérer un API Token.

Une fois le token récupéré, vous devez le sauver dans un fichier nommé qiskitrcen exécutant le code suivant :

from qiskit import IBMQ

IBMQ.save_account(‘MY_API_TOKEN’)

Où ‘MY_API_TOKEN’est votre Token. Bref, tout çà est très classique et pas bien compliqué. Merci IBM.

Lorsque vous vous connectez sur votre compte, vous pouvez voir que vous avez accès (limité) à des ordinateurs quantiques d’IBM. Effet Whaouh garanti lors du prochain apéro Startups !

Vous pouvez même vérifier via Python à quelles machines vous avez accès. Je vous conseille de le faire dès maintenant car cela va vous permettre de vérifier que tout votre environnement est fonctionnel (dont votre Token). Pour cela, utiliser le code suivant :

from qiskit import IBMQ

IBMQ.load_accounts()

print(« Available backends: »,IBMQ.backends())

Le résultat devrait ressembler à :

Available backends:

[<IBMQBackend(‘ibmqx4’) from IBMQ()>, <IBMQBackend(‘ibmqx2’) from IBMQ()>, <IBMQBackend(‘ibmq_16_melbourne’) from IBMQ()>, <IBMQBackend(‘ibmq_qasm_simulator’) from IBMQ()>]

L’accès aux ordinateurs quantiques d’IBM s’effectue via une file d’attente. Il est possible de lancer une requête qui donne la machine la plus disponible selon le nombre de qubits qu’on souhaite. Par exemple, si notre programme a besoin de 3 qubits, le code est :

from qiskit import IBMQ

IBMQ.load_accounts()

from qiskit.providers.ibmq import least_busy

large_enough_devices = IBMQ.backends(filters=lambda x: x.configuration().n_qubits > 3 and not x.configuration().simulator)

backend = least_busy(large_enough_devices)

print(« The best backend is  » + backend.name())

Cela étant dit, IBM met aussi à notre disposition un simulateur/émulateur de 32 qubits qui s’utilise comme les vrais. Nous en reparlerons.

Maintenant que nous nous sentons tout puissants avec nos accès à de vrais ordinateurs quantiques, passons à l’écriture d’un vrai code.

C’est le moment où je vais être obligé de calmer les ardeurs des plus passionnés d’entre vous : on ne va ni casser un RSA 1024 ni plier une protéine complexe. On va… juste jouer à pile ou face.

Vous allez me dire : «Quoi ? tout çà pour un simple pile ou face ?» Oui.. mais sur un vrai ordinateur quantique, quand même ! Plus sérieusement, nous nous heurtons à deux difficultés majeures. Premièrement, la connaissance nécessaire pour implémenter de vrais algorithmes quantiques dépasse largement ces quelques lignes, et sans vouloir froisser personne, dépasse aussi les connaissances du développeur geek ‘ de base’ (no offense!). Deuxièmement, nous n’allons travailler qu’avec 3 qubits, ce qui limite pas mal notre champs d’évaluation et d’investigation.

Une pièce de monnaie pour jouer à pile ou face est un système à deux niveaux ou états : pile ou face. Cela ressemble pas mal à un qubit dont l’état quantique est une distribution probabiliste. En utilisant la notation propre à l’univers quantique (notation de Dirac), on peut écrire cet état quantique sous forme d’un vecteur colonne, appelé ket, avec a0,a1 ∈ ℂ (bref, des nombres complexes). 

Comme nous savons qu’en probabilités, la somme des probabilités est toujours égale à un, nous pouvons aussi écrire que :

Pour ceux d’entre vous qui se rappellent un peu de leurs cours de mathématiques, on peut décomposer un vecteur dans une base canonique. La base canonique quantique serait donc :

Qui sont deux qubits un peu particuliers. Et voici donc la décomposition :

Nous aboutissons alors à une superposition, qui donne un résultat de 0 avec une probabilité de |a0|2et un résultat de 1 avec une probabilité de |a1|2.

Nous allons utiliser cette analogie pour simuler le lancement de 3 pièces de monnaie sous la forme de 3 qubits et d’un circuit quantique assez simple qui mette en superposition les 3 qubits et qui permette de lire les résultats.

Nous avons besoin de 3 registres quantiques qubits, via QuantumRegister, et de 3 registres « miroirs » classiques, via ClassicalRegister, pour lire (mesurer) le résultat.

Nous allons aussi utiliser une porte quantique (gate) un peu particulière, dite de Hadamard, notée H, pour mettre chacun des 3 qubits en état de superposition (cool !). 

Par défaut chaque qubit est initialisé à |0>. 

Grâce à Qiskit, il est possible de visualiser le circuit avec QuantumCircuit.draw, ce qui s’avère bien pratique, surtout au début quand on ne maîtrise pas encore tout bien comme moi.

Le code est le suivant (sur simulateur pour l’instant).

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit

from qiskit import execute

from qiskit import BasicAer

import numpy as np

backend = BasicAer.get_backend(‘qasm_simulator’)

q = QuantumRegister(3)

c = ClassicalRegister(3)

circuit = QuantumCircuit(q, c)

circuit.h(q[0])

circuit.h(q[1])

circuit.h(q[2])

circuit.measure(q, c)

print(QuantumCircuit.draw(circuit, output=’text’))

job = execute(circuit, backend, shots=100)

print(job.result().get_counts(circuit))</code>

A l’exécution, on obtient le résultat suivant. Je laisse les gourous du traitement des listes sous Python peaufiner un affichage plus sympathique (histogramme ?).

{‘100’: 15, ‘110’: 10, ‘101’: 9, ‘000’: 14, ‘011’: 16, ‘111’: 12, ‘001’: 12, ‘010’: 12}

Votre résultat est peut-être diffèrent et c’est tout à fait normal. Exécutons une nouvelle fois le programme :

{‘101’: 9, ‘110’: 16, ‘100’: 14, ‘011’: 10, ‘000’: 17, ‘010’: 15, ‘111’: 11, ‘001’: 8}

Nous avons bien une distribution probabiliste sur 100 lancés de 3 pièces en pile ou face. Mathématiquement, chaque combinaison devrait avoir la même probabilité mais il faudrait, comme dans la réalité, bien plus de 100 lancés pour y arriver. 

Passons maintenant à l’exécution du code sur un vrai ordinateur quantique :

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit

from qiskit import execute

from qiskit import IBMQ

IBMQ.load_accounts()

from qiskit.providers.ibmq import least_busy

large_enough_devices = IBMQ.backends(filters=lambda x: x.configuration().n_qubits > 3 and not x.configuration().simulator)

backend = least_busy(large_enough_devices)

print(« The best backend is  » + backend.name())

print(« On lance le calcul sur un vrai ordinateur quantique ! « )

q = QuantumRegister(3)

c = ClassicalRegister(3)

circuit = QuantumCircuit(q, c)

circuit.h(q[0])

circuit.h(q[1])

circuit.h(q[2])

circuit.measure(q, c)

job = execute(circuit, backend, shots=100, max_credits=3)

print(job.result().get_counts(circuit))

Voici le résultat :

The best backend is ibmqx4

On lance le calcul sur un vrai ordinateur quantique !

{‘011’: 14, ‘101’: 12, ‘000’: 19, ‘100’: 11, ‘010’: 13, ‘111’: 5, ‘110’: 10, ‘001’: 16}

Vous avez désormais fait tourner votre premier programme sur un vrai ordinateur quantique. 

Pour ceux que cela intéresse, IBM propose aussi un simulateur HPC d’ordinateur quantique qui émule les vraies conditions (bruits, décohérence, etc…). Pour notre exemple, le code serait :

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit

from qiskit import execute

from qiskit import IBMQ

IBMQ.load_accounts()

backend = IBMQ.get_backend(‘ibmq_qasm_simulator’, hub=None)

q = QuantumRegister(3)

c = ClassicalRegister(3)

circuit = QuantumCircuit(q, c)

circuit.h(q[0])

circuit.h(q[1])

circuit.h(q[2])

circuit.measure(q, c)

print(« On lance le calcul sur le simulateur HPC quantique ! »)

job = execute(circuit, backend=backend, shots=100, max_credits=3)

print(job.result().get_counts(circuit))

Voilà qui termine cet article volontairement limité et court dont le seul but était de vous titiller les neurones et de vous donner envie d’aller plus loin.

N’hésitez pas à nous contacter à contact@voltanode.com.