Aller au contenu principal

Encodage des données

Introduction et notation

Pour utiliser un algorithme quantique, les données classiques doivent d'une façon ou d'une autre être introduites dans un circuit quantique. On parle généralement d'encodage des données, mais aussi de chargement des données. Rappelle-toi des leçons précédentes la notion de mapping de caractéristiques (feature mapping), c'est-à-dire un mapping des caractéristiques des données d'un espace vers un autre. Le simple transfert de données classiques vers un ordinateur quantique est une sorte de mapping, et pourrait être appelé un feature mapping. En pratique, les feature mappings intégrés dans Qiskit (comme z_Feature Map et ZZ Feature Map) incluent généralement des couches de rotation et des couches d'intrication qui étendent l'état à de nombreuses dimensions dans l'espace de Hilbert. Ce processus d'encodage est une partie critique des algorithmes d'apprentissage automatique quantique et affecte directement leurs capacités computationnelles.

Certaines des techniques d'encodage présentées ci-dessous peuvent être simulées de façon efficace sur un ordinateur classique ; c'est particulièrement facile à voir dans les méthodes d'encodage qui produisent des états produits (c'est-à-dire qu'elles n'intriquent pas les qubits). Et rappelle-toi que l'utilité quantique réside très probablement là où la complexité quantique du jeu de données est bien adaptée à la méthode d'encodage. Il est donc très probable que tu finisses par écrire tes propres circuits d'encodage. Ici, nous présentons une grande variété de stratégies d'encodage possibles, simplement pour que tu puisses les comparer et les mettre en contraste, et voir ce qui est possible. On peut formuler quelques affirmations très générales sur l'utilité des techniques d'encodage. Par exemple, efficient_su2 (voir ci-dessous) avec un schéma d'intrication complet est bien plus susceptible de capturer les caractéristiques quantiques des données que les méthodes qui produisent des états produits (comme z_feature_map). Mais cela ne signifie pas qu'efficient_su2 est suffisant, ou suffisamment bien adapté à ton jeu de données, pour produire une accélération quantique. Cela nécessite une réflexion approfondie sur la structure des données à modéliser ou à classer. Il y a aussi un équilibre à trouver avec la profondeur des circuits, car de nombreux feature maps qui intriquent entièrement les qubits dans un circuit produisent des circuits très profonds, trop profonds pour obtenir des résultats utilisables sur les ordinateurs quantiques actuels.

Notation

Un jeu de données est un ensemble de MM vecteurs de données : X={x(j)j[M]}\text{X} = \{\vec{x}^{(j)}\,|\,j\in [M]\}, où chaque vecteur est de dimension NN, c'est-à-dire x(j)=(x1(j),,xN(j))RN\vec{x}^{(j)}=(\vec{x}^{(j)}_1,\ldots,\vec{x}^{(j)}_N)\in\mathbb{R}^N. Cela pourrait être étendu à des caractéristiques de données complexes. Dans cette leçon, nous pourrons utiliser occasionnellement ces notations pour l'ensemble complet (X),(\text{X}), et ses éléments spécifiques comme x(j)\vec{x}^{(j)}. Mais nous nous référerons principalement au chargement d'un seul vecteur de notre jeu de données à la fois, et nous nous référerons souvent simplement à un seul vecteur de NN caractéristiques comme x\vec{x}.

De plus, il est courant d'utiliser le symbole Φ(x)\Phi(\vec{x}) pour désigner le feature mapping Φ\Phi du vecteur de données x\vec{x}. En informatique quantique spécifiquement, il est courant de désigner les mappings quantiques par U(x),U(\vec{x}), une notation qui renforce la nature unitaire de ces opérations. On pourrait correctement utiliser le même symbole pour les deux ; ce sont tous les deux des feature mappings. Tout au long de ce cours, nous utilisons :

  • Φ(x)\Phi(\vec{x}) quand nous discutons des feature mappings en apprentissage automatique, de façon générale, et
  • U(x)U(\vec{x}) quand nous discutons des implémentations en circuits des feature mappings.

Normalisation et perte d'information

En apprentissage automatique classique, les caractéristiques des données d'entraînement sont souvent « normalisées » ou redimensionnées, ce qui améliore souvent les performances du modèle. Une façon courante de le faire est d'utiliser la normalisation min-max ou la standardisation. Dans la normalisation min-max, les colonnes de caractéristiques de la matrice de données X\text{X} (disons, la caractéristique kk) sont normalisées :

xk(i)=xk(i)min{xk(j)x(j)[X]}max{xk(j)x(j)[X]}min{xk(j)x(j)[X]}x^{'(i)}_k = \frac{x^{(i)}_k - \text{min}\{x^{(j)}_k\,|\,\vec{x}^{(j)}\in [\text{X}]\}}{\text{max}\{x^{(j)}_k\,|\,\vec{x}^{(j)}\in [\text{X}]\}-\text{min}\{x^{(j)}_k\,|\,\vec{x}^{(j)}\in [\text{X}]\}}

où min et max désignent le minimum et le maximum de la caractéristique kk sur les MM vecteurs de données du jeu de données X\text{X}. Toutes les valeurs de caractéristiques tombent alors dans l'intervalle unité : xk(i)[0,1]x^{'(i)}_k \in [0,1] pour tout i[M]i\in [M], k[N]k\in[N].

La normalisation est aussi un concept fondamental en mécanique quantique et en informatique quantique, mais elle est légèrement différente de la normalisation min-max. La normalisation en mécanique quantique exige que la longueur (dans le contexte de l'informatique quantique, la 2-norme) d'un vecteur d'état ψ|\psi\rangle soit égale à l'unité : ψ=ψψ=1\|\psi\|=\sqrt{\langle\psi|\psi\rangle} = 1, garantissant que les probabilités de mesure somment à 1. L'état est normalisé en divisant par la 2-norme ; c'est-à-dire en redimensionnant

ψψ1ψ|\psi\rangle\rightarrow\|\psi\|^{-1}|\psi\rangle

En informatique quantique et en mécanique quantique, il ne s'agit pas d'une normalisation imposée par les gens sur les données, mais d'une propriété fondamentale des états quantiques. Selon ton schéma d'encodage, cette contrainte peut affecter la façon dont tes données sont redimensionnées. Par exemple, dans l'encodage en amplitude (voir ci-dessous), le vecteur de données est normalisé x(j)=1\vert\vec{x}^{(j)}\vert = 1 comme l'exige la mécanique quantique, et cela affecte le redimensionnement des données encodées. Dans l'encodage en phase, il est recommandé de redimensionner les valeurs des caractéristiques comme xi(j)(0,2π]\vec{x}^{(j)}_i \in (0,2\pi] afin d'éviter toute perte d'information due à l'effet modulo-2π2\pi de l'encodage vers un angle de phase de qubit[1,2].

Méthodes d'encodage

Dans les prochaines sections, nous nous référerons à un petit exemple de jeu de données classique Xex\text{X}_\text{ex} composé de M=5M=5 vecteurs de données, chacun avec N=3N=3 caractéristiques :

Xex={(4,8,5),(9,8,6),(2,9,2),(5,7,0),(3,7,5)}\text{X}_{\text{ex}}=\{(4,8,5),(9,8,6),(2,9,2),(5,7,0),(3,7,5)\}

Dans la notation introduite ci-dessus, on pourrait dire que la 1eˋre1^\text{ère} caractéristique du 4eˋme4^\text{ème} vecteur de données de notre ensemble Xex\text{X}_{\text{ex}} est x1(4)=5,\vec{x}^{(4)}_1 = 5, par exemple.

Encodage en base

L'encodage en base encode une chaîne de PP bits classique dans un état de base computationnel d'un système à PP qubits. Prenons par exemple x3(1)=5=0(23)+1(22)+0(21)+1(20).\vec{x}^{(1)}_3 = 5 = 0(2^3)+1(2^2)+0(2^1)+1(2^0). Cela peut être représenté comme une chaîne de 44 bits (0101)(0101), et par un système à 44 qubits comme l'état quantique 0101|0101\rangle. Plus généralement, pour une chaîne de PP bits : xk(j)=(b1,b2,...,bP)\vec{x}^{(j)}_k = (b_1, b_2, ... , b_P), l'état à PP qubits correspondant est xk(j)=b1,b2,...,bP|x^{(j)}_k\rangle = | b_1, b_2, ... , b_P \rangle avec bn{0,1}b_n \in \{0,1\} pour n=1,,Pn = 1 , \dots , P. Note que cela concerne une seule caractéristique.

L'encodage en base en informatique quantique représente chaque bit classique comme un qubit séparé, mappant la représentation binaire des données directement sur des états quantiques dans la base computationnelle. Lorsque plusieurs caractéristiques doivent être encodées, chaque caractéristique est d'abord convertie en sa forme binaire, puis assignée à un groupe distinct de qubits — un groupe par caractéristique — où chaque qubit reflète un bit dans la représentation binaire de cette caractéristique.

À titre d'exemple, encodons le vecteur (5, 7, 0).

Supposons que toutes les caractéristiques sont stockées sur quatre bits (plus que nécessaire, mais suffisant pour représenter tout entier à un chiffre en base 10) :

5 → binaire 0101

7 → binaire 0111

0 → binaire 0000

Ces chaînes de bits sont assignées à trois ensembles de quatre qubits, de sorte que l'état de base global à 12 qubits est :

010101110000∣0101 0111 0000⟩

Ici, les quatre premiers qubits représentent la première caractéristique, les quatre qubits suivants la deuxième caractéristique, et les quatre derniers qubits la troisième caractéristique. Le code ci-dessous convertit le vecteur de données (5,7,0) en un état quantique, et est généralisé pour le faire pour d'autres caractéristiques à un seul chiffre.

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit
from qiskit import QuantumCircuit

# Data point to encode
x = 5 # binary: 0101
y = 7 # binary: 0111
z = 0 # binary: 0000

# Convert each to 4-bit binary list
x_bits = [int(b) for b in format(x, "04b")] # [0,1,0,1]
y_bits = [int(b) for b in format(y, "04b")] # [0,1,1,1]
z_bits = [int(b) for b in format(z, "04b")] # [0,0,0,0]

# Combine all bits
all_bits = x_bits + y_bits + z_bits # [0,1,0,1,0,1,1,1,0,0,0,0]

# Initialize a 12-qubit quantum circuit
qc = QuantumCircuit(12)

# Apply x-gates where the bit is 1
for idx, bit in enumerate(all_bits):
if bit == 1:
qc.x(idx)

qc.draw("mpl")

Output of the previous code cell

Vérifie ta compréhension

Lis la question ci-dessous, réfléchis à ta réponse, puis clique sur le triangle pour révéler la solution.

Écris du code pour encoder le premier vecteur de notre exemple de jeu de données Xex\text{X}_{\text{ex}} :

x(1)=(4,8,5)\vec{x}^{(1)}=(4,8,5)

en utilisant l'encodage en base.

Réponse :

import math
from qiskit import QuantumCircuit

# Data point to encode
x = 4 # binary: 0100
y = 8 # binary: 1000
z = 5 # binary: 0101

# Convert each to 4-bit binary list
x_bits = [int(b) for b in format(x, '04b')] # [0,1,0,0]
y_bits = [int(b) for b in format(y, '04b')] # [1,0,0,0]
z_bits = [int(b) for b in format(z, '04b')] # [0,1,0,1]

# Combine all bits
all_bits = x_bits + y_bits + z_bits # [0,1,0,0,1,0,0,0,0,1,0,1]

# Initialize a 12-qubit quantum circuit
qc = QuantumCircuit(12)

# Apply x-gates where the bit is 1
for idx, bit in enumerate(all_bits):
if bit == 1:
qc.x(idx)

qc.draw('mpl')

Encodage en amplitude

L'encodage en amplitude encode les données dans les amplitudes d'un état quantique. Il représente un vecteur de données classique normalisé de dimension NN, x(j)\vec{x}^{(j)}, comme les amplitudes d'un état quantique à nn qubits, ψx|\psi_x\rangle :

ψx(j)=1αi=1Nxi(j)i|\psi^{(j)}_x\rangle = \frac{1}{\alpha}\sum_{i=1}^N x^{(j)}_i |i\rangle

NN est la même dimension des vecteurs de données qu'auparavant, xi(j)\vec{x}^{(j)}_i est le ieˋmei^\text{ème} élément de x(j)\vec{x}^{(j)} et i|i\rangle est le ieˋmei^\text{ème} état de base computationnel. Ici, α\alpha est une constante de normalisation à déterminer à partir des données encodées. C'est la condition de normalisation imposée par la mécanique quantique :

i=1Nxi(j)2=α2.\sum_{i=1}^N \left|x^{(j)}_i\right|^2 = \left|\alpha\right|^2.

En général, il s'agit d'une condition différente de la normalisation min/max utilisée pour chaque caractéristique sur l'ensemble des vecteurs de données. La façon dont cela est géré dépendra de ton problème. Mais il n'y a pas moyen de contourner la condition de normalisation de la mécanique quantique ci-dessus.

Dans l'encodage en amplitude, chaque caractéristique d'un vecteur de données est stockée comme une amplitude d'un état quantique différent. Comme un système de nn qubits fournit 2n2^n amplitudes, l'encodage en amplitude de NN caractéristiques nécessite nlog2(N)n \ge \mathrm{log}_2(N) qubits.

À titre d'exemple, encodons le premier vecteur de notre exemple de jeu de données Xex\text{X}_\text{ex}, x(1)=(4,8,5)\vec{x}^{(1)} = (4,8,5) en utilisant l'encodage en amplitude. En normalisant le vecteur résultant, on obtient :

i=1Nxi(1)2=42+82+52=105=α2α=105\sum_{i=1}^N \left|x^{(1)}_i\right|^2 = 4^2+8^2+5^2 = 105 = \left|\alpha\right|^2 \rightarrow \alpha = \sqrt{105}

et l'état quantique à 2 qubits résultant serait :

ψ(x(1))=1105(400+801+510+011)|\psi(\vec{x}^{(1)})\rangle = \frac{1}{\sqrt{105}}(4|00\rangle+8|01\rangle+5|10\rangle+0|11\rangle)

Dans l'exemple ci-dessus, le nombre de caractéristiques dans le vecteur N=3N=3 n'est pas une puissance de 2. Lorsque NN n'est pas une puissance de 2, on choisit simplement une valeur pour le nombre de qubits nn telle que 2nN2^n\geq N et on complète le vecteur d'amplitudes avec des constantes non informatives (ici, un zéro).

Comme pour l'encodage en base, une fois qu'on a calculé quel état encodera notre jeu de données, dans Qiskit on peut utiliser la fonction initialize pour le préparer :

import math

desired_state = [
1 / math.sqrt(105) * 4,
1 / math.sqrt(105) * 8,
1 / math.sqrt(105) * 5,
1 / math.sqrt(105) * 0,
]

qc = QuantumCircuit(2)
qc.initialize(desired_state, [0, 1])

qc.decompose(reps=5).draw(output="mpl")

Output of the previous code cell

Un avantage de l'encodage en amplitude est la nécessité mentionnée ci-dessus de seulement log2(N)\mathrm{log}_2(N) qubits pour encoder. Cependant, les algorithmes ultérieurs doivent opérer sur les amplitudes d'un état quantique, et les méthodes pour préparer et mesurer les états quantiques ont tendance à ne pas être efficaces.

Vérifie ta compréhension

Lis les questions ci-dessous, réfléchis à tes réponses, puis clique sur les triangles pour révéler les solutions.

Écris l'état normalisé pour encoder le vecteur suivant (composé de deux vecteurs de notre exemple de jeu de données) :

x=(9,8,6,2,9,2)\vec{x}=(9,8,6,2,9,2)

en utilisant l'encodage en amplitude.

Réponse :

Pour encoder 6 nombres, nous aurons besoin d'au moins 6 états disponibles sur les amplitudes desquels nous pouvons encoder. Cela nécessitera 3 qubits. En utilisant un facteur de normalisation inconnu α\alpha, on peut écrire cela comme :

ψ=α(9000+8001+6010+2011+9100+2101+0110+0111)|\psi\rangle = \alpha(9|000\rangle+8|001\rangle+6|010\rangle+2|011\rangle+9|100\rangle+2|101\rangle+0|110\rangle+0|111\rangle)

Note que

ψψ=α2×(92+82+62+22+92+22+02+02)=α2×(270)=1α=1270\langle \psi|\psi\rangle = |\alpha|^2\times(9^2+8^2+6^2+2^2+9^2+2^2+0^2+0^2) = |\alpha|^2\times(270)=1 \rightarrow \alpha = \frac{1}{\sqrt{270}}

Donc finalement,

ψ=1270(9000+8001+6010+2011+9100+2101+0110+0111)|\psi\rangle = \frac{1}{\sqrt{270}}(9|000\rangle+8|001\rangle+6|010\rangle+2|011\rangle+9|100\rangle+2|101\rangle+0|110\rangle+0|111\rangle)

Pour le même vecteur de données x=(9,8,6,2,9,2),\vec{x}=(9,8,6,2,9,2), écris du code pour créer un circuit qui charge ces caractéristiques de données en utilisant l'encodage en amplitude.

Réponse :

desired_state = [
9 / math.sqrt(270),
8 / math.sqrt(270),
6 / math.sqrt(270),
2 / math.sqrt(270),
9 / math.sqrt(270),
2 / math.sqrt(270),
0,
0,
]

print(desired_state)

qc = QuantumCircuit(3)
qc.initialize(desired_state, [0, 1, 2])
qc.decompose(reps=8).draw(output="mpl")

[0.5477225575051662, 0.48686449556014766, 0.36514837167011077, 0.12171612389003691, 0.5477225575051662, 0.12171612389003691, 0, 0]

"Output of the previous code cell"

Tu pourrais avoir besoin de traiter de très grands vecteurs de données. Considère le vecteur

x=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5).\vec{x}=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5).

Écris du code pour automatiser la normalisation et générer un circuit quantique pour l'encodage en amplitude.

Réponse :

Il y a de nombreuses réponses possibles. Voici du code qui affiche quelques étapes intermédiaires :

import numpy as np
from math import sqrt

init_list = [4, 8, 5, 9, 8, 6, 2, 9, 2, 5, 7, 0, 3, 7, 5]
qubits = round(np.log(len(init_list)) / np.log(2) + 0.4999999999)
need_length = 2**qubits
pad = need_length - len(init_list)
for i in range(0, pad):
init_list.append(0)

init_array = np.array(init_list) # Unnormalized data vector
length = sqrt(
sum(init_array[i] ** 2 for i in range(0, len(init_array)))
) # Vector length
norm_array = init_array / length # Normalized array
print("Normalized array:")
print(norm_array)
print()

qubit_numbers = []
for i in range(0, qubits):
qubit_numbers.append(i)
print(qubit_numbers)

qc = QuantumCircuit(qubits)
qc.initialize(norm_array, qubit_numbers)
qc.decompose(reps=7).draw(output="mpl")

Normalized array: [0.17342199 0.34684399 0.21677749 0.39019949 0.34684399 0.26013299 0.086711 0.39019949 0.086711 0.21677749 0.30348849 0. 0.1300665 0.30348849 0.21677749 0. ]

[0, 1, 2, 3]

"Output of the previous code cell"

Vois-tu des avantages à l'encodage en amplitude par rapport à l'encodage en base ? Si oui, explique.

Réponse :

Il peut y avoir plusieurs réponses. Une réponse est que, étant donné l'ordre fixe des états de base, cet encodage en amplitude préserve l'ordre des nombres encodés. Il sera souvent aussi encodé de façon plus dense.

Un avantage de l'encodage en amplitude est qu'il faut seulement log2(N)\log_2(N) qubits pour un vecteur de données à NN dimensions (NN caractéristiques) xx\vec{x}\rightarrow|\vec{x}\rangle. Cependant, l'encodage en amplitude est généralement une procédure inefficace qui nécessite une préparation d'état arbitraire, qui est exponentielle dans le nombre de portes CNOT. Autrement dit, la préparation d'état a une complexité de calcul polynomiale de O(N)\mathcal O(N) en nombre de dimensions, où N=2nN = 2^n, et nn est le nombre de qubits. L'encodage en amplitude « fournit une économie exponentielle en espace au prix d'une augmentation exponentielle en temps »[3] ; cependant, des augmentations de temps de calcul à O(logN)\mathcal O(\log N) sont réalisables dans certains cas[4]. Pour une accélération quantique de bout en bout, la complexité du temps de calcul du chargement des données doit être prise en compte.

Encodage en angle

L'encodage en angle est intéressant dans de nombreux modèles QML utilisant des feature maps de Pauli comme les machines à vecteurs de support quantiques (QSVM) et les circuits quantiques variationnels (VQC), entre autres. L'encodage en angle est étroitement lié à l'encodage en phase et à l'encodage en angle dense qui sont présentés ci-dessous. Ici, nous utiliserons « encodage en angle » pour désigner une rotation en θ\theta, c'est-à-dire une rotation hors de l'axe zz accomplie par exemple par une porte RXR_X ou une porte RYR_Y[1,3]. En réalité, on peut encoder des données dans n'importe quelle rotation ou combinaison de rotations. Mais RYR_Y est courant dans la littérature, donc nous l'accentuons ici.

Lorsqu'elle est appliquée à un seul qubit, l'encodage en angle imprime une rotation autour de l'axe Y proportionnelle à la valeur des données. Considère l'encodage d'une seule caractéristique (keˋmek^\text{ème}) du jeˋmej^\text{ème} vecteur de données d'un jeu de données, xk(j)\vec{x}^{(j)}_k :

xk(j)=RY(θ=xk(j))0=cos(xk(j)2)0+sin(xk(j)2)1.|\vec{x}^{(j)}_k\rangle = R_Y(\theta=\vec{x}^{(j)}_k)|0\rangle = \textstyle\cos\left(\frac{\vec{x}^{(j)}_k}{2}\right)|0\rangle + \sin\left(\frac{\vec{x}^{(j)}_k}{2}\right)|1\rangle.

Alternativement, l'encodage en angle peut être réalisé avec des portes RX(θ)R_X(\theta), bien que l'état encodé ait une phase relative complexe par rapport à RY(θ)R_Y(\theta).

L'encodage en angle diffère des deux méthodes précédemment discutées de plusieurs façons. Dans l'encodage en angle :

  • Chaque valeur de caractéristique est mappée à un qubit correspondant, xk(j)Qk\vec{x}^{(j)}_k \rightarrow Q_k, laissant les qubits dans un état produit.
  • Une seule valeur numérique est encodée à la fois, plutôt qu'un ensemble entier de caractéristiques d'un point de données.
  • nn qubits sont nécessaires pour NN caractéristiques de données, où nNn\leq N. Souvent l'égalité tient ici. On verra comment n<Nn<N est possible dans les prochaines sections.
  • Le circuit quantique résultant a une profondeur constante (typiquement la profondeur est 1 avant la transpilation).

Le circuit quantique à profondeur constante le rend particulièrement adapté au matériel quantique actuel. Une autre caractéristique de l'encodage de nos données en utilisant θ\theta (et spécifiquement, notre choix d'utiliser l'encodage en angle sur l'axe Y) est qu'il crée des états quantiques à valeurs réelles qui peuvent être utiles pour certaines applications. Pour la rotation autour de l'axe Y, les données sont mappées avec une porte de rotation RY(θ)R_Y(\theta) par un angle réel θ(0,2π]\theta \in (0, 2\pi] (Qiskit RYGate). Comme pour l'encodage en phase (voir ci-dessous), nous recommandons de redimensionner les données de sorte que xk(j)(0,2π]\vec{x}^{(j)}_k \in (0,2\pi], évitant ainsi la perte d'information et d'autres effets indésirables.

Le code Qiskit suivant fait pivoter un seul qubit depuis un état initial 0|0\rangle pour encoder une valeur de données xk(j)=12π\vec{x}^{(j)}_k=\frac{1}{2}\pi.

from qiskit.quantum_info import Statevector
from math import pi

qc = QuantumCircuit(1)
state1 = Statevector.from_instruction(qc)
qc.ry(pi / 2, 0) # Phase gate rotates by an angle pi/2
state2 = Statevector.from_instruction(qc)
states = state1, state2

Nous allons définir une fonction pour visualiser l'action sur le vecteur d'état. Les détails de la définition de la fonction ne sont pas importants, mais la capacité à visualiser les vecteurs d'état et leurs changements est importante.

import numpy as np
from qiskit.visualization.bloch import Bloch
from qiskit.visualization.state_visualization import _bloch_multivector_data

def plot_Nstates(states, axis, plot_trace_points=True):
"""This function plots N states to 1 Bloch sphere"""
bloch_vecs = [_bloch_multivector_data(s)[0] for s in states]

if axis is None:
bloch_plot = Bloch()
else:
bloch_plot = Bloch(axes=axis)

bloch_plot.add_vectors(bloch_vecs)

if len(states) > 1:

def rgba_map(x, num):
g = (0.95 - 0.05) / (num - 1)
i = 0.95 - g * num
y = g * x + i
return (0.0, y, 0.0, 0.7)

num = len(states)
bloch_plot.vector_color = [rgba_map(x, num) for x in range(1, num + 1)]

bloch_plot.vector_width = 3
bloch_plot.vector_style = "simple"

if plot_trace_points:

def trace_points(bloch_vec1, bloch_vec2):
# bloch_vec = (x,y,z)
n_points = 15
thetas = np.arccos([bloch_vec1[2], bloch_vec2[2]])
phis = np.arctan2(
[bloch_vec1[1], bloch_vec2[1]], [bloch_vec1[0], bloch_vec2[0]]
)
if phis[1] < 0:
phis[1] = phis[1] + 2 * pi
angles0 = np.linspace(phis[0], phis[1], n_points)
angles1 = np.linspace(thetas[0], thetas[1], n_points)

xp = np.cos(angles0) * np.sin(angles1)
yp = np.sin(angles0) * np.sin(angles1)
zp = np.cos(angles1)
pnts = [xp, yp, zp]
bloch_plot.add_points(pnts)
bloch_plot.point_color = "k"
bloch_plot.point_size = [4] * len(bloch_plot.points)
bloch_plot.point_marker = ["o"]

for i in range(len(bloch_vecs) - 1):
trace_points(bloch_vecs[i], bloch_vecs[i + 1])

bloch_plot.sphere_alpha = 0.05
bloch_plot.frame_alpha = 0.15
bloch_plot.figsize = [4, 4]

bloch_plot.render()

plot_Nstates(states, axis=None, plot_trace_points=True)

Output of the previous code cell

Il s'agissait d'une seule caractéristique d'un seul vecteur de données. Lorsqu'on encode NN caractéristiques dans les angles de rotation de nn qubits, par exemple pour le jeˋmej^\text{ème} vecteur de données x(j)=(x1,...,xN),\vec{x}^{(j)} = (x_1,...,x_N), l'état produit encodé ressemble à ceci :

x(j)=k=1Ncos(xk(j))0+sin(xk(j))1|\vec{x}^{(j)}\rangle = \bigotimes^N_{k=1} \cos(\vec{x}^{(j)}_k)|0\rangle + \sin(\vec{x}^{(j)}_k)|1\rangle

On note que c'est équivalent à

x(j)=k=1NRY(2xk(j))0.|\vec{x}^{(j)}\rangle = \bigotimes^N_{k=1} R_Y(2\vec{x}^{(j)}_k)|0\rangle.

Vérifie ta compréhension

Lis les questions ci-dessous, réfléchis à tes réponses, puis clique sur les triangles pour révéler les solutions.

Encode le vecteur de données x=(0,π/4,π/2)\vec{x} = (0, \pi/4, \pi/2) en utilisant l'encodage angulaire, tel que décrit ci-dessus.

Réponse :

qc = QuantumCircuit(3)
qc.ry(0, 0)
qc.ry(2 * math.pi / 4, 1)
qc.ry(2 * math.pi / 2, 2)
qc.draw(output="mpl")

&quot;Output of the previous code cell&quot;

En utilisant l'encodage angulaire tel que décrit ci-dessus, combien de qubits sont nécessaires pour encoder 5 caractéristiques ?

Réponse : 5

Encodage par phase

L'encodage par phase est très similaire à l'encodage angulaire décrit ci-dessus. L'angle de phase d'un qubit est un angle réel ϕ\phi autour de l'axe zz à partir de l'axe +x+x. Les données sont mappées avec une rotation de phase, P(ϕ)=eiϕ/2RZ(ϕ)P(\phi) = e^{i\phi/2}R_Z(\phi), où ϕ(0,2π]\phi \in (0,2\pi] (voir Qiskit PhaseGate pour plus d'informations). Il est recommandé de redimensionner les données de façon que xk(j)(0,2π]\vec{x}^{(j)}_k \in (0,2\pi]. Cela évite la perte d'information et d'autres effets potentiellement indésirables[1,2].

Un qubit est souvent initialisé dans l'état 0|0\rangle, qui est un état propre de l'opérateur de rotation de phase, ce qui signifie que l'état du qubit doit d'abord être tourné pour que l'encodage par phase puisse être mis en œuvre. Il est donc judicieux d'initialiser l'état avec une porte Hadamard : H0=+=12(0+1)H|0\rangle = |+\rangle = \textstyle\frac{1}{\sqrt{2}}(|0\rangle + |1\rangle). L'encodage par phase sur un seul qubit consiste à conférer une phase relative proportionnelle à la valeur de la donnée :

xk(j)=P(ϕ=xk(j))+=12(0+eixk(j)1).|\vec{x}^{(j)}_k\rangle = P(\phi=\vec{x}^{(j)}_k)|+\rangle = \textstyle\frac{1}{\sqrt{2}}\big(|0\rangle + e^{i\vec{x}^{(j)}_k}|1\rangle\big).

La procédure d'encodage par phase mappe chaque valeur de caractéristique sur la phase d'un qubit correspondant, xk(j)Qk\vec{x}^{(j)}_k \rightarrow Q_k. Au total, l'encodage par phase a une profondeur de circuit de 2, en incluant la couche Hadamard, ce qui en fait un schéma d'encodage efficace. L'état multi-qubit encodé par phase (nn qubits pour N=nN=n caractéristiques) est un état produit :

x(j)=k=1NPk(ϕ=xk(j))+N=12Nk=1N(0+eixk(j)1).|\vec{x}^{(j)}\rangle = \bigotimes_{k=1}^{N} P_k(\phi = \vec{x}^{(j)}_k)|+\rangle^{\otimes N} = {\textstyle\frac{1}{\sqrt{2^N}}} \bigotimes_{k=1}^{N}\big(|0\rangle + e^{i\vec{x}^{(j)}_k}|1\rangle\big).

Le code Qiskit suivant prépare d'abord l'état initial d'un seul qubit en le faisant tourner avec une porte Hadamard, puis le fait tourner à nouveau à l'aide d'une porte de phase pour encoder une caractéristique de données xk(j)=12π\vec{x}^{(j)}_k=\frac{1}{2}\pi.

qc = QuantumCircuit(1)
qc.h(0) # Hadamard gate rotates state down to Bloch equator
state1 = Statevector.from_instruction(qc)

qc.p(pi / 2, 0) # Phase gate rotates by an angle pi/2
state2 = Statevector.from_instruction(qc)

states = state1, state2

qc.draw("mpl", scale=1)

Output of the previous code cell

On peut visualiser la rotation dans ϕ\phi en utilisant la fonction plot_Nstates que nous avons définie.

plot_Nstates(states, axis=None, plot_trace_points=True)

Output of the previous code cell

Le tracé de la sphère de Bloch montre la rotation selon l'axe Z +P(12π)+|+\rangle \rightarrow P(\frac{1}{2}\pi)|+\ranglexk(j)=12π\vec{x}^{(j)}_k=\frac{1}{2}\pi. La flèche vert clair indique l'état final.

L'encodage par phase est utilisé dans de nombreuses feature maps quantiques, notamment les feature maps ZZ et ZZZZ, ainsi que les feature maps de Pauli générales, entre autres.

Vérifie ta compréhension

Lis les questions ci-dessous, réfléchis à tes réponses, puis clique sur les triangles pour révéler les solutions.

Combien de qubits sont nécessaires pour utiliser l'encodage par phase tel que décrit ci-dessus afin de stocker 8 caractéristiques ?

Réponse : 8

Écris du code pour encoder le vecteur x(1)=(4,8,5,9,8,6,2,9,2,5,7,0)\vec{x}^{(1)}=(4,8,5,9,8,6,2,9,2,5,7,0) en utilisant l'encodage par phase.

Réponse :

Il peut y avoir plusieurs réponses. Voici un exemple :

phase_data = [4, 8, 5, 9, 8, 6, 2, 9, 2, 5, 7, 0]
qc = QuantumCircuit(len(phase_data))
for i in range(0, len(phase_data)):
qc.h(i)
qc.rz(phase_data[i] * 2 * math.pi / float(max(phase_data)), i)
qc.draw(output="mpl")

&quot;Output of the previous code cell&quot;

Encodage angulaire dense

L'encodage angulaire dense (DAE) est une combinaison de l'encodage angulaire et de l'encodage par phase. Le DAE permet d'encoder deux valeurs de caractéristiques dans un seul qubit : un angle avec un angle de rotation autour de l'axe Y, et l'autre avec un angle de rotation autour de l'axe zz : xk(j),\vec{x}^{(j)}_k, x(j)θ,ϕ\vec{x}^{(j)}_\ell \rightarrow \theta, \phi. Il encode deux caractéristiques de la manière suivante :

xk(j),x(j)=RZ(ϕ=x(j))RY(θ=xk(j))0=cos(xk(j)2)0+eix(j)sin(xk(j)2)1.|\vec{x}^{(j)}_k,\vec{x}^{(j)}_\ell\rangle = R_Z(\phi=\vec{x}^{(j)}_\ell) R_Y(\theta=\vec{x}^{(j)}_k)|0\rangle = \cos\left(\frac{\vec{x}^{(j)}_k}{2}\right)|0\rangle + e^{i\vec{x}^{(j)}_\ell} \sin\left(\frac{\vec{x}^{(j)}_k}{2}\right)|1\rangle.

L'encodage de deux caractéristiques de données dans un seul qubit permet de réduire de 2×2\times le nombre de qubits nécessaires pour l'encodage. En étendant cela à davantage de caractéristiques, le vecteur de données x=(x1,...,xN)\vec{x} = (x_1,...,x_N) peut être encodé comme suit :

x=k=1N/2cos(x2k1)0+eix2ksin(x2k1)1|\vec{x}\rangle = \bigotimes_{k=1}^{N/2} \cos(x_{2k-1})|0\rangle + e^{i x_{2k}}\sin(x_{2k-1})|1\rangle

Le DAE peut être généralisé à des fonctions arbitraires des deux caractéristiques au lieu des fonctions sinusoïdales utilisées ici. On appelle cela l'encodage général de qubit[7].

À titre d'exemple de DAE, le code ci-dessous encode et visualise l'encodage des caractéristiques x1=θ=3π/8x_1=\theta = 3\pi/8 et x2=ϕ=7π/4x_2=\phi = 7\pi/4.

qc = QuantumCircuit(1)
state1 = Statevector.from_instruction(qc)
qc.ry(3 * pi / 8, 0)
state2 = Statevector.from_instruction(qc)
qc.rz(7 * pi / 4, 0)
state3 = Statevector.from_instruction(qc)
states = state1, state2, state3

plot_Nstates(states, axis=None, plot_trace_points=True)

Output of the previous code cell

Vérifie ta compréhension

Lis les questions ci-dessous, réfléchis à tes réponses, puis clique sur les triangles pour révéler les solutions.

D'après le traitement ci-dessus, combien de qubits sont nécessaires pour encoder 6 caractéristiques avec l'encodage dense ?

Réponse : 3

Écris du code pour charger le vecteur x(1)=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5)\vec{x}^{(1)}=(4,8,5,9,8,6,2,9,2,5,7,0,3,7,5) en utilisant l'encodage angulaire dense.

Réponse :

Note que nous avons complété la liste avec un « 0 » pour éviter le problème d'un seul paramètre inutilisé dans notre schéma d'encodage.

dense_data = [4, 8, 5, 9, 8, 6, 2, 9, 2, 5, 7, 0, 3, 7, 5, 0]
qc = QuantumCircuit(int(len(dense_data) / 2))
entry = 0
for i in range(0, int(len(dense_data) / 2)):
qc.ry(dense_data[entry] * 2 * math.pi / float(max(dense_data)), i)
entry = entry + 1
qc.rz(dense_data[entry] * 2 * math.pi / float(max(dense_data)), i)
entry = entry + 1
qc.draw(output="mpl")

&quot;Output of the previous code cell&quot;

Encodage avec des feature maps intégrées

Encodage à des points arbitraires

L'encodage angulaire, l'encodage par phase et l'encodage dense préparent des états produits avec une feature encodée sur chaque qubit (ou deux features par qubit). C'est différent de l'encodage en base et de l'encodage en amplitude, car ces méthodes utilisent des états intriqués. Il n'y a pas de correspondance 1:1 entre feature de la donnée et qubit. Dans l'encodage en amplitude, par exemple, une feature peut être l'amplitude de l'état 01|01\rangle et une autre feature l'amplitude de 10|10\rangle. En général, les méthodes qui encodent dans des états produits donnent des circuits moins profonds et peuvent stocker 1 ou 2 features par qubit. Les méthodes qui utilisent l'intrication et associent une feature à un état plutôt qu'à un qubit produisent des circuits plus profonds, et peuvent stocker plus de features par qubit en moyenne.

Mais l'encodage n'a pas besoin d'être entièrement dans des états produits ou entièrement dans des états intriqués comme dans l'encodage en amplitude. En effet, de nombreux schémas d'encodage intégrés dans Qiskit permettent d'encoder aussi bien avant qu'après une couche d'intrication, plutôt qu'uniquement au début. C'est ce qu'on appelle le « data reuploading ». Pour des travaux connexes, voir les références [5] et [6].

Dans cette section, nous allons utiliser et visualiser quelques-uns des schémas d'encodage intégrés. Toutes les méthodes de cette section encodent NN features sous forme de rotations sur NN portes paramétrées sur nn qubits, où nNn \leq N. Note que maximiser le chargement des données pour un nombre donné de qubits n'est pas la seule considération. Dans de nombreux cas, la profondeur du circuit peut être une considération encore plus importante que le nombre de qubits.

Efficient SU2

Un exemple courant et utile d'encodage avec intrication est le circuit efficient_su2 de Qiskit. Remarquablement, ce circuit peut, par exemple, encoder 8 features sur seulement 2 qubits. Voyons cela, puis essayons de comprendre comment c'est possible.

from qiskit.circuit.library import efficient_su2

circuit = efficient_su2(num_qubits=2, reps=1, insert_barriers=True)
circuit.decompose().draw(output="mpl")

Output of the previous code cell

Lorsque nous écrivons notre état, nous utiliserons la convention Qiskit selon laquelle les qubits les moins significatifs sont ordonnés à l'extrême droite, comme dans q2,q1,q0|q_2,q_1,q_0\rangle ou q2q1q0.|q_2\rangle\otimes|q_1\rangle\otimes|q_0\rangle. Ces états peuvent devenir très compliqués très rapidement, et ce rare exemple peut aider à expliquer pourquoi ces états sont rarement écrits explicitement.

Notre système commence dans l'état 00.|00\rangle. Jusqu'à la première barrière (un point que nous notons b1b1), nos états sont :

ψb1=(cos(θ12)0+sin(θ12)eiθ31)(cos(θ02)0+sin(θ02)eiθ21)|\psi\rangle_{b1} = \left(\cos\left(\frac{\theta_1}{2}\right)|0\rangle+\sin\left(\frac{\theta_1}{2}\right)e^{i\theta_3}|1\rangle\right)\otimes\left(\cos\left(\frac{\theta_0}{2}\right)|0\rangle+\sin\left(\frac{\theta_0}{2}\right)e^{i\theta_2}|1\rangle\right)

C'est simplement de l'encodage dense, que nous avons déjà vu. Maintenant, après la porte CNOT, à la deuxième barrière (b2b2), notre état est

ψb2=cos(θ12)cos(θ02)00+cos(θ12)sin(θ02)eiθ211+sin(θ12)cos(θ02)eiθ310+sin(θ12)sin(θ02)eiθ2eiθ301\begin{aligned} |\psi\rangle_{b2} = & \cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_0}{2}\right)|00\rangle+\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_0}{2}\right)e^{i\theta_2}|11\rangle\\ + & \sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_0}{2}\right)e^{i\theta_3}|10\rangle+\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_0}{2}\right)e^{i\theta_2}e^{i\theta_3}|01\rangle \end{aligned}

Nous appliquons maintenant le dernier ensemble de rotations sur qubit unique et regroupons les états similaires pour obtenir :

ψfinal=[cos(θ02)(cos(θ12)cos(θ52)sin(θ12)sin(θ52)eiθ3)cos(θ42)+sin(θ02)(cos(θ12)sin(θ52)sin(θ12)cos(θ52)eiθ3)sin(θ42)eiθ2]00+[cos(θ02)(cos(θ12)cos(θ52)sin(θ12)sin(θ52)eiθ3)sin(θ42)+sin(θ02)(cos(θ12)sin(θ52)+sin(θ12)cos(θ52)eiθ3)cos(θ42)eiθ2]eiθ601+[cos(θ02)(cos(θ12)sin(θ52)+sin(θ12)cos(θ52)eiθ3)cos(θ42)sin(θ02)(cos(θ12)cos(θ52)+sin(θ12)sin(θ52)eiθ3)sin(θ42)eiθ2]eiθ710+[cos(θ02)(cos(θ12)sin(θ52)+sin(θ12)cos(θ52)eiθ3)sin(θ42)+sin(θ02)(cos(θ12)cos(θ52)+sin(θ12)sin(θ52)eiθ3)cos(θ42)eiθ2]eiθ6eiθ711\begin{align*} |\psi\rangle_{\text{final}} = & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)-\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)\right.\\ + & \left.\sin\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)-\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] |00\rangle\\ + & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)-\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)\right.\\ + & \left.\sin\left(\frac{\theta_0}{2}\right)\left(-\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] e^{i\theta_6}|01\rangle\\ + & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)\right.\\ - & \left.\sin\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] e^{i\theta_7}|10\rangle\\ + & \left[\cos\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\sin\left(\frac{\theta_4}{2}\right)\right.\\ + & \left.\sin\left(\frac{\theta_0}{2}\right)\left(\cos\left(\frac{\theta_1}{2}\right)\cos\left(\frac{\theta_5}{2}\right)+\sin\left(\frac{\theta_1}{2}\right)\sin\left(\frac{\theta_5}{2}\right)e^{i\theta_3}\right)\cos\left(\frac{\theta_4}{2}\right)e^{i\theta_2}\right] e^{i\theta_6}e^{i\theta_7}|11\rangle \end{align*}

C'est probablement trop compliqué à analyser. Prends plutôt du recul et réfléchis au nombre de paramètres que nous avons chargés sur l'état : huit. Mais nous n'avons que quatre états de base computationnels. À première vue, il peut sembler que nous ayons chargé plus de paramètres qu'il n'est raisonnable, puisque l'état final peut s'écrire ψfinal=c000+c101+c210+c311\psi_\text{final} = c_0|00\rangle+c_1|01\rangle+c_2|10\rangle+c_3|11\rangle. Note cependant que chaque préfacteur est complexe ! Écrit ainsi :

ψfinal=(a0+ib0)00+(a1+ib1)01+(a2+ib2)10+(a3+ib3)11\psi_\text{final} = (a_0+ib_0)|00\rangle+(a_1+ib_1)|01\rangle+(a_2+ib_2)|10\rangle+(a_3+ib_3)|11\rangle

On peut voir que nous avons bien, en effet, huit paramètres sur l'état sur lesquels encoder nos huit features.

En augmentant le nombre de qubits et en augmentant le nombre de répétitions des couches d'intrication et de rotation, on peut encoder beaucoup plus de données. Écrire les fonctions d'onde devient rapidement intraitable. Mais on peut toujours voir l'encodage en action. Ici nous encodons le vecteur de données x\vec{x} avec 12 features, sur un circuit efficient_su2 à 3 qubits, en utilisant chacune des portes paramétrées pour encoder une feature différente.

x=(0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2)\vec{x} = (0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2)

Dans ce vecteur de données, les features sont présentées dans un ordre particulier. Isolément, peu importe si elles sont encodées dans cet ordre ou dans l'ordre inverse. Ce qui est important, c'est de garder une trace de cet ordre et d'être cohérent. Note dans le diagramme du circuit que efficient_su2 suppose un certain ordre d'encodage, à savoir remplir la première couche de portes paramétrées du qubit 0 au qubit 2, puis passer à la couche suivante. Cela n'est ni cohérent ni incohérent avec la notation little-endian, puisqu'ici les features de données ne peuvent pas être ordonnées par qubit a priori, avant qu'un circuit d'encodage ait été spécifié.

x = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2]
circuit = efficient_su2(num_qubits=3, reps=1, insert_barriers=True)
encode = circuit.assign_parameters(x)
encode.decompose().draw(output="mpl")

Output of the previous code cell

Au lieu d'augmenter le nombre de qubits, tu peux choisir d'augmenter le nombre de répétitions des couches d'intrication et de rotation. Mais il y a des limites au nombre de répétitions utiles. Comme dit précédemment, il y a un compromis : les circuits avec plus de qubits ou plus de répétitions des couches d'intrication et de rotation peuvent stocker plus de paramètres, mais le font avec une plus grande profondeur de circuit. Nous reviendrons sur les profondeurs de quelques feature maps intégrées ci-dessous. Les prochaines méthodes d'encodage intégrées dans Qiskit ont « feature map » dans leur nom. Rappelons qu'encoder des données dans un circuit quantique est un feature mapping, dans le sens où il amène les données dans un nouvel espace : l'espace de Hilbert des qubits concernés. La relation entre la dimensionnalité de l'espace de features original et celle de l'espace de Hilbert dépendra du circuit utilisé pour l'encodage.

Feature map ZZ

La feature map ZZ (ZFM) peut être interprétée comme une extension naturelle de l'encodage par phase. La ZFM est constituée de couches alternées de portes à qubit unique : des couches de portes Hadamard et des couches de portes de phase. Soit le vecteur de données x\vec{x} avec NN features. Le circuit quantique qui réalise le feature mapping est représenté par un opérateur unitaire qui agit sur l'état initial :

UZFM(x)0N=ϕ(x)\mathscr{U}_{\text{ZFM}}(\vec{x})|0\rangle^{\otimes N}=|\phi(\vec{x})\rangle

0N|0\rangle^{\otimes N} est l'état fondamental à NN qubits. Cette notation est utilisée par cohérence avec la référence [4] Havlicek et al. Les features xix_i des données sont associées une à une aux qubits correspondants. Par exemple, si tu as 8 features dans un vecteur de données, tu utiliserais 8 qubits. Le circuit ZFM est composé de rr répétitions d'un sous-circuit comprenant des couches de portes Hadamard et des couches de portes de phase. Une couche Hadamard est composée d'une porte Hadamard agissant sur chaque qubit d'un registre à nn qubits, HHH=HnH \otimes H \otimes \dots \otimes H = H^{\otimes n}, à la même étape de l'algorithme. Cette description s'applique également à une couche de portes de phase dans laquelle le ieˋmei^\text{ème} qubit est actionné par P(xi)P(\vec{x}_i). Chaque porte PP prend une feature comme argument, mais la couche de portes de phase (P(x1)P(xk)P(xN)P(\vec{x}_1)\otimes\ldots P(\vec{x}_k)\otimes\ldots P(\vec{x}_N) est une fonction du vecteur de données. L'unitaire complet du circuit ZFM avec une seule répétition est :

UZFM=(P(x1)P(xk)P(xN)HN)=(k=1NP(xk))HN\mathscr{U}_{\text{ZFM}}=\big(P(\vec{x}_1)\otimes\ldots P(\vec{x}_k)\otimes\ldots P(\vec{x}_N)H^{\otimes N}\big)=\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N}

Alors rr répétitions de cet unitaire donneraient

UZFM(r)(x)=s=1r[(k=1NP(xk))HN]\mathscr{U}^{(r)}_{\text{ZFM}}\left(\vec{x}\right)=\prod_{s=1}^{r}\left[\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N}\right]

Les features xkx_k des données sont associées aux portes de phase de la même façon dans toutes les rr répétitions. L'état de la feature map ZFM est un état produit et est efficacement simulable classiquement[4].

Pour commencer par un petit exemple, un circuit ZFM à deux qubits est codé avec Qiskit et dessiné pour afficher la structure simple du circuit. Dans l'exemple, une seule répétition, r=1r=1, est implémentée avec le vecteur de données x=(12π,13π)\vec{x} = \left(\textstyle\frac{1}{2}\pi, \textstyle\frac{1}{3}\pi\right). Note que ceci est écrit dans l'ordre standard d'un vecteur en Python, ce qui signifie que l'élément d'indice 00 est 12π.\textstyle\frac{1}{2}\pi. Nous sommes libres d'encoder cette feature d'indice 00 sur notre qubit d'indice 00, ou sur notre qubit d'indice NN. Encore une fois, il ne peut pas toujours y avoir un seul mapping 1:1 de l'ordre des features vers l'ordre des qubits, car différentes feature maps encodent différents nombres de features sur chaque qubit. Ce qui est important, encore une fois, c'est que nous sachions où chaque feature est encodée. Lorsqu'on fournit une liste de paramètres à la feature map ZZ, elle encodera la feature 0 de la liste sur le qubit le moins significatif disposant d'une porte paramétrée, c'est-à-dire le qubit 0. Nous suivrons donc cette convention lorsque nous le faisons à la main. Nous encoderons 12π\textstyle\frac{1}{2}\pi sur le qubit d'indice 00, et 13π\textstyle\frac{1}{3}\pi sur le qubit d'indice 11.

L'opérateur unitaire du circuit ZFM agit sur l'état initial de la façon suivante :

UZFM(xˉ)00=P(xˉ)2H200=(P(13π)H0)(P(12π)H0).\mathscr{U}_{\text{ZFM}}(\bar{x})|00\rangle = P(\bar{x})^{\otimes 2} H^{\otimes 2}|00\rangle = \left( P\left(\textstyle\frac{1}{3}\pi\right)H|0\rangle \right) \otimes \left(P\left(\textstyle\frac{1}{2}\pi\right)H|0\rangle\right).

La formule a été réorganisée autour du produit tensoriel pour mettre en évidence les opérations sur chaque qubit. Le code Qiskit suivant utilise explicitement des portes Hadamard et de phase pour montrer la structure de la ZFM :

qc0 = QuantumCircuit(1)
qc1 = QuantumCircuit(1)

qc0.h(0)
qc0.p(pi / 2, 0)

qc1.h(0)
qc1.p(pi / 3, 0)

# Combine circuits qc0 and qc1 into 1 circuit
qc = QuantumCircuit(2)
qc.compose(qc0, [0], inplace=True)
qc.compose(qc1, [1], inplace=True)

qc.draw("mpl", scale=1)

Output of the previous code cell

Nous encodons maintenant le même vecteur de données x=(12π,13π)\vec{x} = \left(\textstyle\frac{1}{2}\pi, \textstyle\frac{1}{3}\pi\right) dans un circuit ZFM avec trois répétitions, r=3r=3, en utilisant la classe Qiskit z_feature_map, ce qui nous donne au total la feature map quantique UZFM(x)\mathscr{U}_{\text{ZFM}}(\vec{x}). Par défaut dans la classe z_feature_map, les paramètres β\beta sont multipliés par 2 avant d'être associés à la porte de phase βP(θ=2β)\beta \rightarrow P(\theta = 2\beta). Pour reproduire les mêmes encodages qu'above, nous divisons par 2.

from qiskit.circuit.library import z_feature_map

zfeature_map = z_feature_map(feature_dimension=2, reps=3)
zfeature_map = zfeature_map.assign_parameters([(1 / 2) * pi / 2, (1 / 2) * pi / 3])
zfeature_map.decompose().draw("mpl")

Output of the previous code cell

C'est clairement un mapping différent de celui fait à la main plus haut, mais note la cohérence dans l'ordre des paramètres : 12π\textstyle\frac{1}{2}\pi a de nouveau été encodé sur le qubit d'indice 00.

Tu peux utiliser la ZFM via la classe ZFM de Qiskit ; tu peux aussi utiliser cette structure comme inspiration pour construire ta propre feature map.

Feature map ZZZZ

La feature map ZZZZ (ZZFM) étend la ZFM avec l'inclusion de portes d'intrication à deux qubits, plus précisément la porte de rotation ZZZZ, RZZ(θ)R_{ZZ}(\theta). La ZZFM est supposée être généralement coûteuse à calculer sur un ordinateur classique, contrairement à la ZFM.

RZZ(θ)R_{ZZ}(\theta) implémente une interaction ZZZZ et est maximalement intriquante pour θ=12π\theta = \textstyle{\frac{1}{2}}\pi. RZZ(θ)R_{ZZ}(\theta) peut être décomposée en une série de portes sur deux qubits, comme montré dans le code Qiskit suivant en utilisant la porte RZZ et la méthode decompose de la classe QuantumCircuit. Nous encodons une seule feature du vecteur de données x\vec{x} : xk=π.\vec{x}_k=\pi.

qc = QuantumCircuit(2)
qc.rzz(pi, 0, 1)
qc.draw("mpl", scale=1)

Output of the previous code cell

Comme c'est souvent le cas, nous voyons cela représenté comme une unité de type porte unique, jusqu'à ce que nous utilisions .decompose() pour voir toutes les portes constitutives.

qc.decompose().draw("mpl", scale=1)

Output of the previous code cell

Les données sont associées à une rotation de phase P(θ)=eiθ/2RZ(θ)P(\theta) = e^{i\theta/2}R_Z(\theta) sur le second qubit. La porte RZZ(θ)R_{ZZ}(\theta) intrigue les deux qubits sur lesquels elle opère, avec un degré d'intrication déterminé par la valeur de la feature encodée.

Le circuit ZZFM complet est constitué d'une porte Hadamard et d'une porte de phase, comme dans la ZFM, suivies de l'intrication décrite ci-dessus. Une seule répétition du circuit ZZFM est :

UZZFM(x)=UZZ(x)(P(x1)P(xk)P(xN)HN)=UZZ(x)(k=1NP(xk))HN,\mathscr{U}_{\text{ZZFM}}(\vec{x}) = U_{ZZ}(\vec{x})\big(P(\vec{x}_1)\otimes\ldots P(\vec{x}_k)\otimes\ldots P(\vec{x}_N)H^{\otimes N}\big)=U_{ZZ}(\vec{x})\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N},

UZZ(x)U_{ZZ}(\vec{x}) contient une couche de portes ZZ structurée par un schéma d'intrication. Plusieurs schémas d'intrication sont présentés dans les blocs de code ci-dessous. La structure de UZZ(x)U_{ZZ}(\vec{x}) inclut également une fonction qui combine les features de données des qubits intrigués de la façon suivante. Disons que la porte RZZR_{ZZ} doit être appliquée aux qubits pp et qq. Dans la couche de phase, ces qubits ont des portes de phase qui encodent xp\vec{x}_p et xq\vec{x}_q sur eux, respectivement. L'argument θq,p\theta_{q,p} de RZZ,q,p(θq,p)R_{ZZ,q,p}(\theta_{q,p}) ne sera pas simplement l'une de ces features ou l'autre, mais une fonction souvent notée ϕ\phi (à ne pas confondre avec l'angle azimutal) :

θq,pϕ(xq,xp)=2(πxq)(πxp).\theta_{q,p} \rightarrow \phi(\vec{x}_q, \vec{x}_p) = 2(\pi-\vec{x}_q)(\pi-\vec{x}_p).

Nous verrons cela dans plusieurs exemples ci-dessous. L'extension à plusieurs répétitions est la même que dans le cas de la z_feature_map :

UZZFM(r)(x)=s=1r[UZZ(x)(k=1NP(xk))HN].\mathscr{U}^{(r)}_{\text{ZZFM}}\left(\vec{x}\right)=\prod_{s=1}^{r}\left[U_{ZZ}(\vec{x})\left(\bigotimes_{k = 1}^N P(\vec{x}_k)\right)H^{\otimes N}\right].

Les opérateurs ayant gagné en complexité, encodons d'abord un vecteur de données x=(x0,x1)\vec{x} = (x_0, x_1) avec une ZZFM à deux qubits et une répétition avec le code suivant :

from qiskit.circuit.library import zz_feature_map

feature_dim = 2
zzfeature_map = zz_feature_map(
feature_dimension=feature_dim, entanglement="linear", reps=1
)
zzfeature_map.decompose(reps=1).draw("mpl", scale=1)

Output of the previous code cell

Par défaut dans Qiskit, les features (x1,x2)(\vec{x}_1, \vec{x}_2) sont combinées dans RZZ(θ)R_{ZZ}(\theta) par cette fonction de mapping θ1,2=ϕ(x1,x2)=2(πx1)(πx2)\theta_{1,2} = \phi(\vec{x}_1, \vec{x}_2) = 2(\pi-\vec{x}_1)(\pi-\vec{x}_2). Qiskit permet à l'utilisateur de personnaliser la fonction ϕ\phi (ou ϕS\phi_SSS est l'ensemble des paires de qubits couplés via les portes RZZR_{ZZ}) comme étape de prétraitement.

En passant à un vecteur de données à quatre dimensions x=(x1,x2,x3,x4)\vec{x} = (\vec{x}_1, \vec{x}_2, \vec{x}_3, \vec{x}_4) et en l'associant à une ZZFM à quatre qubits avec une répétition, nous pouvons commencer à voir le mapping ϕ\phi pour diverses paires de qubits. Nous pouvons également voir la signification de l'intrication « linéaire » :

feature_dim = 4
zzfeature_map = zz_feature_map(
feature_dimension=feature_dim, entanglement="linear", reps=1
)
zzfeature_map.decompose().draw("mpl", scale=1)

Output of the previous code cell

Dans le schéma d'intrication linéaire, les paires de qubits voisins (numérotés) de ce circuit sont intriqués. Il existe d'autres schémas d'intrication intégrés dans Qiskit, notamment circular et full.

Feature map de Pauli

La feature map de Pauli (PFM) est la généralisation de la ZFM et de la ZZFM à l'utilisation de portes de Pauli arbitraires. La PFM prend une forme très similaire aux deux feature maps précédentes. Pour rr répétitions de l'encodage des NN features du vecteur x,\vec{x},

UPFM(x)=s=1rU(x)Hn.\mathscr{U}_{\text{PFM}}(\vec{x}) = \prod_{s=1}^{r} U(\vec{x}) H^{\otimes n}.

Pour la PFM, U(x)U(\vec{x}) est généralisé en un opérateur unitaire d'expansion de Pauli. Nous présentons ici une forme plus générale des feature maps étudiées jusqu'à présent :

U(x)=exp(iSIϕS(x)iSσi),U(\vec{x}) = \exp\left(i \sum_{S \in\mathcal{I}} \phi_S(\vec{x}) \prod_{i \in S} \sigma_i \right),

σi\sigma_i est un opérateur de Pauli, σiI,X,Y,Z\sigma_i \in {I,X,Y,Z}. Ici I\mathcal{I} est l'ensemble de toutes les connectivités de qubits telles que déterminées par la feature map, incluant l'ensemble des qubits actionnés par des portes à qubit unique. C'est-à-dire que pour une feature map dans laquelle le qubit 0 est actionné par une porte de phase, et les qubits 2 et 3 par une porte RZZR_{ZZ}, l'ensemble I\mathcal{I} inclurait {{0},{2,3}}\{\{0\},\{2,3\}\}. SS parcourt tous les éléments de cet ensemble. Dans les feature maps précédentes, la fonction ϕS(x)\phi_S(\vec{x}) était impliquée soit exclusivement avec des portes à qubit unique, soit exclusivement avec des portes à deux qubits. Ici, nous la définissons de façon générale :

ϕS(x)={xiif S={i} (single-qubit)jS(πxj)if S2 (multi-qubit)\phi_S(\vec{x})= \begin{cases} x_i & \text{if } S= \{i\} \text{ (single-qubit)}\\ \prod_{j\in{S}}(\pi-x_j) & \text{if } |S|\ge2 \text{ (multi-qubit)}\\ \end{cases}

Pour la documentation, voir la documentation de la classe Qiskit Pauli feature map). Dans la ZZFM, l'opérateur σi\sigma_i est restreint à ZiZ_i.

Une façon de comprendre l'unitaire ci-dessus est par analogie avec le propagateur dans un système physique. L'unitaire ci-dessus est un opérateur d'évolution unitaire, exp(itH)\exp(it\mathcal{H}), pour un Hamiltonien, H\mathcal{H}, similaire au modèle d'Ising, où le paramètre de temps, tt, est remplacé par des valeurs de données pour piloter l'évolution. Le développement de cet opérateur unitaire donne le circuit PFM. Les connectivités d'intrication dans SS peuvent être interprétées comme des couplages d'Ising dans un réseau de spins. Considérons un exemple avec les opérateurs de Pauli YY et XXXX représentant ces interactions de type Ising. Qiskit fournit une classe pauli_feature_map pour instancier une PFM avec un choix de portes à qubit unique et à nn qubits, qui dans cet exemple seront passées sous forme de chaînes de Pauli 'Y' et 'XX'. En général, nn vaut 1 ou 2 pour des interactions à un ou deux qubits, respectivement. Le schéma d'intrication est « linéaire », ce qui signifie que seuls les qubits voisins dans le circuit quantique sont couplés. Note que cela ne correspond pas aux qubits voisins sur l'ordinateur quantique lui-même, car ce circuit quantique est une couche d'abstraction.

from qiskit.circuit.library import pauli_feature_map

feature_dim = 3
pfmap = pauli_feature_map(
feature_dimension=feature_dim, entanglement="linear", reps=1, paulis=["Y", "XX"]
)

pfmap.decompose().draw("mpl", scale=1.5)

Output of the previous code cell

Qiskit fournit un paramètre, α\alpha, dans les feature maps de Pauli pour contrôler l'échelle des rotations de Pauli.

U(xˉ)=exp(iαS[n]ϕS(xˉ)iSσi)U(\bar{x}) = \exp\left(i \alpha \sum_{S\subseteq[n]} \phi_S(\bar{x}) \prod_{i \in S} \sigma_i \right)

La valeur par défaut de α\alpha est 22. En optimisant sa valeur dans l'intervalle, par exemple, [0,4],[0,4], on peut mieux aligner un noyau quantique aux données.

Ici nous visualisons diverses feature maps de Pauli pour des circuits à deux qubits, afin d'avoir une meilleure idée de l'éventail des possibilités.

from qiskit.visualization import circuit_drawer
import matplotlib.pyplot as plt

feature_dim = 2
fig, axs = plt.subplots(9, 2)
i_plot = 0
for paulis in [
["I"],
["X"],
["Y"],
["Z"],
["XX"],
["XY"],
["XZ"],
["YY"],
["YZ"],
["ZZ"],
["X", "ZZ"],
["Y", "ZZ"],
["Z", "ZZ"],
["X", "YZ"],
["Y", "YZ"],
["Z", "YZ"],
["YY", "ZZ"],
["XY", "ZZ"],
]:
pfmap = pauli_feature_map(feature_dimension=feature_dim, paulis=paulis, reps=1)
circuit_drawer(
pfmap.decompose(),
output="mpl",
style={"backgroundcolor": "#EEEEEE"},
ax=axs[int((i_plot - i_plot % 2) / 2), i_plot % 2],
)
axs[int((i_plot - i_plot % 2) / 2), i_plot % 2].title.set_text(paulis)
i_plot += 1

fig.set_figheight(16)
fig.set_figwidth(16)

Output of the previous code cell

Ce qui précède peut bien sûr être étendu pour inclure d'autres permutations et répétitions de matrices de Pauli. Les apprenants sont encouragés à expérimenter avec ces options.

Récapitulatif des feature maps intégrées

Tu as vu plusieurs schémas pour encoder des données dans un circuit quantique :

  • Encodage en base
  • Encodage en amplitude
  • Encodage angulaire
  • Encodage par phase
  • Encodage dense

Tu as vu comment construire tes propres feature maps en utilisant ces schémas d'encodage, et tu as vu quatre feature maps intégrées qui tirent parti de l'encodage angulaire et de l'encodage par phase :

  • Efficient SU2
  • Feature map Z
  • Feature map ZZ
  • Feature map de Pauli

Ces feature maps intégrées diffèrent les unes des autres de plusieurs façons :

  • La profondeur pour un nombre donné de features encodées
  • Le nombre de qubits nécessaires pour un nombre donné de features
  • Le degré d'intrication (évidemment lié aux autres différences)

Le code ci-dessous applique ces quatre feature maps intégrées à l'encodage d'un ensemble de features, et trace la profondeur à deux qubits du circuit résultant. Étant donné que les taux d'erreur à deux qubits sont bien plus élevés que les taux d'erreur des portes à qubit unique, on pourrait raisonnablement s'intéresser en priorité à la profondeur des portes à deux qubits. Dans le code ci-dessous, nous obtenons le nombre de toutes les portes d'un circuit en décomposant d'abord le circuit puis en utilisant count_ops(), comme indiqué. Ici, les portes à deux qubits qui nous intéressent sont les portes 'cx' :

# Initializing parameters and empty lists for depths
x = [0.1, 0.2]
n_data = []
zz2gates = []
su22gates = []
z2gates = []
p2gates = []

# Generating feature maps
for n in range(3, 10):
x.append(n / 10)
zzcircuit = zz_feature_map(n, reps=1, insert_barriers=True)
zcircuit = z_feature_map(n, reps=1, insert_barriers=True)
su2circuit = efficient_su2(n, reps=1, insert_barriers=True)
pcircuit = pauli_feature_map(n, reps=1, paulis=["XX"], insert_barriers=True)
# Getting the cx depths
zzcx = zzcircuit.decompose().count_ops().get("cx")
zcx = zcircuit.decompose().count_ops().get("cx")
su2cx = su2circuit.decompose().count_ops().get("cx")
pcx = pcircuit.decompose().count_ops().get("cx")

# Appending the cx gate counts to the lists. We shift the zz and pauli data points, because they overlap.
n_data.append(n)
zz2gates.append(zzcx - 0.5)
z2gates.append(0)
su22gates.append(su2cx)
p2gates.append(pcx + 0.5)

# Plot the output
plt.plot(n_data, p2gates, "bo")
plt.plot(n_data, zz2gates, "ro")
plt.plot(n_data, su22gates, "yo")
plt.plot(n_data, z2gates, "go")
plt.ylabel("CX Gates")
plt.xlabel("Data elements")
plt.legend(["Pauli", "ZZ", "SU2", "Z"])
# plt.suptitle('zz_feature_map(n)')
plt.show()

En général, les feature maps Pauli et ZZ entraîneront une profondeur de circuit plus grande et un plus grand nombre de portes à 2 qubits que les feature maps efficient_su2 et Z.

Étant donné que les feature maps intégrées dans Qiskit sont largement applicables, nous n'aurons souvent pas besoin de concevoir les nôtres, surtout en phase d'apprentissage. Cependant, les experts en apprentissage automatique quantique reviendront probablement sur la conception de leur propre feature mapping, lorsqu'ils s'attaqueront à deux défis complexes :

  1. Le matériel moderne : la présence du bruit et le surcoût important des codes correcteurs d'erreurs signifient que les applications actuelles devront tenir compte de choses comme l'efficacité matérielle et la minimisation de la profondeur des portes à deux qubits.

  2. Des mappings adaptés au problème en question : c'est une chose de dire que la zz_feature_map, par exemple, est difficile à simuler classiquement, et donc intéressante. C'en est une autre que la zz_feature_map soit idéalement adaptée à ta tâche d'apprentissage automatique ou à ton jeu de données. La performance des différents circuits quantiques paramétrés sur différents types de données est un domaine de recherche actif.

Nous concluons avec une note sur l'efficacité matérielle.

Feature mapping efficace pour le matériel

Un feature mapping efficace pour le matériel est un mapping qui tient compte des contraintes des vrais ordinateurs quantiques, dans le but de réduire le bruit et les erreurs dans le calcul. Lors de l'exécution de circuits quantiques sur des ordinateurs quantiques à court terme, il existe de nombreuses stratégies pour atténuer le bruit inhérent au matériel. L'une des principales stratégies pour l'efficacité matérielle est la minimisation de la profondeur du circuit quantique afin que le bruit et la décohérence aient moins de temps pour corrompre le calcul. La profondeur d'un circuit quantique est le nombre d'étapes de portes alignées temporellement nécessaires pour compléter l'ensemble du calcul (après optimisation du circuit)[5]. Rappelle-toi que la profondeur du circuit abstrait et logique peut être bien inférieure à la profondeur une fois le circuit transpilé pour un vrai ordinateur quantique.

La transpilation est le processus de conversion du circuit quantique d'une abstraction de haut niveau vers un circuit prêt à s'exécuter sur un vrai ordinateur quantique, en tenant compte des contraintes du matériel. Un ordinateur quantique possède un ensemble natif de portes à un et deux qubits. Cela signifie que toutes les portes du code Qiskit doivent être transpilées vers l'ensemble des portes matérielles natives. Par exemple, dans ibm_torino, un QPU doté d'un processeur Heron r1 et mis en service en 2023, les portes natives ou de base sont \{CZ, ID, RZ, SX, X\}. Ce sont respectivement la porte à deux qubits CZ contrôlée, et les portes à qubit unique appelées identité, rotation-ZZ, racine carrée de NOT, et NOT, fournissant un ensemble universel. Lors de l'implémentation de portes multi-qubits sous forme de sous-circuit équivalent, des portes physiques CZCZ à deux qubits sont nécessaires, ainsi que d'autres portes à qubit unique disponibles dans le matériel. De plus, pour effectuer une porte à deux qubits sur une paire de qubits qui ne sont pas physiquement couplés, des portes SWAP sont ajoutées pour déplacer les états des qubits entre les qubits afin de permettre le couplage, ce qui entraîne une extension inévitable du circuit. En utilisant l'argument optimization qui peut être réglé de 0 jusqu'à un niveau maximum de 3. Pour plus de contrôle et de personnalisation, le pipeline du transpileur peut être géré avec le Qiskit Pass Manager. Réfère-toi à la documentation du transpileur Qiskit pour plus d'informations sur la transpilation.

Dans Havlicek et al. 2019 [2], une façon dont les auteurs obtiennent l'efficacité matérielle est d'utiliser la feature map ZZZZ car c'est un développement du second ordre (voir la section « Feature map ZZZZ » ci-dessus). Un développement d'ordre NN comporte des portes à NN qubits. Les ordinateurs quantiques IBM® n'ont pas de portes natives à NN qubits pour N>2N>2, donc pour les implémenter il faudrait les décomposer en portes CNOT à deux qubits disponibles dans le matériel. Une deuxième façon dont les auteurs minimisent la profondeur est en choisissant une topologie de couplage ZZZZ qui se mappe directement sur les couplages de l'architecture. Une optimisation supplémentaire qu'ils effectuent est de cibler un sous-circuit matériel plus performant et convenablement connecté. D'autres éléments à considérer sont la minimisation du nombre de répétitions de la feature map et le choix d'un schéma d'intrication personnalisé à faible profondeur ou « linéaire » plutôt que le schéma « full » qui intrigue tous les qubits.

Data encoding image

Le graphique ci-dessus montre un réseau de nœuds et d'arêtes représentant respectivement des qubits physiques et des couplages matériels. La carte de couplage et les performances d'ibm_torino sont affichées avec toutes les portes de couplage CZ à deux qubits possibles. Les qubits sont codés par couleur sur une échelle basée sur le temps de relaxation T1 en microsecondes (μs), où des temps T1 plus longs sont meilleurs et apparaissent dans une teinte plus claire. Les arêtes de couplage sont codées par couleur en fonction de l'erreur CZ, où les teintes plus foncées sont meilleures. Les informations sur la spécification du matériel sont accessibles dans le schéma de configuration du backend matériel IBMQBackend.configuration().

Références

  1. Maria Schuld and Francesco Petruccione, Supervised Learning with Quantum Computers, Springer 2018, doi:10.1007/978-3-319-96424-9.
  2. Vojtech Havlicek et al., "Supervised Learning with Quantum Enhanced Feature Spaces." Nature, vol. 567 (2019): 209–212. https://arxiv.org/abs/1804.11326.
  3. Ryan LaRose and Brian Coyle, "Robust data encodings for quantum classifiers", Physical Review A 102, 032420 (2020), doi:10.1103/PhysRevA.102.032420, arXiv:2003.01695.
  4. Lou Grover and Terry Rudolph. "Creating Superpositions That Correspond to Efficiently Integrable Probability Distributions." arXiv:quant-ph/0208112, August 15, 2002, https://arxiv.org/abs/quant-ph/0208112.
  5. Adrián Pérez-Salinas, Alba Cervera-Lierta, Elies Gil-Fuster, José I. Latorre, "Data re-uploading for a universal quantum classifier", Quantum 4, 226 (2020), ArXiv.org/abs/1907.02085.
  6. Maria Schuld, Ryan Sweke, Johannes Jakob Meyer, "The effect of data encoding on the expressive power of variational quantum machine learning models", Phys. Rev. A 103, 032430 (2021), arxiv.org/abs/2008.08605
import qiskit

qiskit.version.get_version_info()