French
Langues
English
Japanese
German
Korean
Portuguese, Brazilian
French
Shortcuts

Note

Cette page a été générée à partir de tutorials/circuits/3_summary_of_quantum_operations.ipynb.

Exécuter en mode interactif dans le IBM Quantum lab.

Liste des opérateurs quantiques

In this section we will go into the different operations that are available in Qiskit Terra. These are:

  • Portes quantiques mono-qubit

  • Portes quantiques multi-qubits

  • Mesures

  • Réinitialisation

  • Conditionals

  • State initialization

We will also show you how to use the three different simulators:

  • unitary_simulator

  • qasm_simulator

  • statevector_simulator

[1]:
# Useful additional packages
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
from math import pi
[2]:
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, execute
from qiskit.tools.visualization import circuit_drawer
from qiskit.quantum_info import state_fidelity
from qiskit import BasicAer

backend = BasicAer.get_backend('unitary_simulator')

Etats quantiques d’un Qubit

L’état quantique d’un seul qubit peut être écrit en tant que

\[\left|\psi\right\rangle = \alpha\left|0\right\rangle + \beta \left|1\right\rangle\]

\(\alpha\) et \(\beta\) sont des nombres complexes. Dans une mesure, la probabilité que le bit soit dans \(\left|0\right\rangle est :math:\) et \(\left|1\right\rangle\) est \(|\beta|^2\). En tant que vecteur, c’est

\[\begin{split}\left|\psi\right\rangle = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}.\end{split}\]

Notez que , en raison de la conservation de la probabilité \(|\alpha|^2+ |\beta|^2 = 1\) et puisque la phase globale n’est pas détectable \(\left|\psi\right\rangle := e^{i\delta} \left|\psi\right\rangle\) nous n’avons besoin que de deux nombres réels pour décrire un seul état quantique.

Une représentation pratique est

\[\left|\psi\right\rangle = \cos(\theta/2)\left|0\right\rangle + \sin(\theta/2)e^{i\phi}\left|1\right\rangle\]

\(0\leq \phi < 2\pi\), et \(0\leq \theta \leq \pi\). À partir de là, il est clair qu’il y a une correspondance un à un entre les états du qubit (\(\mathbb{C}^2\)) et les points à la surface d’une sphère unitaire (\(\mathbb{R}^3\)). Ceci est appelé la représentation de la sphère Bloch de l’état du qubit.

Les portes/opérations quantiques sont généralement représentées par des matrices. Une porte qui agit sur un qubit est représentée par une matrice unitaire \(2\times 2\) \(U\). L’action de la porte quantique se calcule en multipliant la matrice représentant la porte avec le vecteur qui représente l’état quantique.

\[\left|\psi'\right\rangle = U\left|\psi\right\rangle\]

Un état unitaire général doit être en mesure de prendre l’angle :math:`left|0rdroitrà l’état ci-dessus. C’est à dire

\[\begin{split}U = \begin{pmatrix} \cos(\theta/2) & a \\ e^{i\phi}\sin(\theta/2) & b \end{pmatrix}\end{split}\]

\(a\) et \(b\) sont des nombres complexes contraints tels que \(U^\dagger U = I\) pour tous les \(0\leq\theta\leq\pi\) et \(0\leq \phi<2\pi\). Cela donne 3 contraintes et donc \(a\rightarrow -e^{i\lambda}\sin(\theta/2)\) et \(b\rightarrow e^{i\lambda+i\phi}\cos(\theta/2)\)\(0\leq \lambda<2\pi\) qui donnent

\[\begin{split}U = \begin{pmatrix} \cos(\theta/2) & -e^{i\lambda}\sin(\theta/2) \\ e^{i\phi}\sin(\theta/2) & e^{i\lambda+i\phi}\cos(\theta/2) \end{pmatrix}.\end{split}\]

Il s’agit de la forme la plus générale d’un qubit unitaire/unique.

Portes à un seul Qubit

Les portes (gates) mono-qubit disponibles sont: - u gates - Identity gate - Pauli gates - Clifford gates - \(C3\) gates - Standard rotation gates

Nous avons fourni un backend : unitary_simulator pour vous permettre de calculer les matrices unitaires.

[3]:
q = QuantumRegister(1)

portes U

Dans Qiskit nous vous donnons accès à l’unicité générale en utilisant la porte \(u3\)

\[u3(\theta, \phi, \lambda) = U(\theta, \phi, \lambda)\]
[4]:
qc = QuantumCircuit(q)
qc.u3(pi/2,pi/2,pi/2,q)
qc.draw()
[4]:
      ┌────────────────────┐
q0_0: ┤ U3(pi/2,pi/2,pi/2) ├
      └────────────────────┘
[5]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[5]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   +0.707j, -0.707+0.j   ]])

La porte \(u2(\phi, \lambda) =u3(\pi/2, \phi, \lambda)\) a la forme de la matrice

\[\begin{split}u2(\phi, \lambda) = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & -e^{i\lambda} \\ e^{i\phi} & e^{i(\phi + \lambda)} \end{pmatrix}.\end{split}\]

C’est une porte utile car elle nous permet de créer des superpositions.

[6]:
qc = QuantumCircuit(q)
qc.u2(pi/2,pi/2,q)
qc.draw()
[6]:
      ┌───────────────┐
q0_0: ┤ U2(pi/2,pi/2) ├
      └───────────────┘
[7]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[7]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   +0.707j, -0.707+0.j   ]])

La porte :math:` u1 (lambda) = u3 (0, 0, lambda) a la forme matricielle

\[\begin{split}u1(\lambda) = \begin{pmatrix} 1 & 0 \\ 0 & e^{i \lambda} \end{pmatrix},\end{split}\]

qui est utile car il nous permet d’appliquer une phase quantique.

[8]:
qc = QuantumCircuit(q)
qc.u1(pi/2,q)
qc.draw()
[8]:
      ┌──────────┐
q0_0: ┤ U1(pi/2) ├
      └──────────┘
[9]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[9]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

Porte identité

La porte identité est \(Id = u0(1)\).

[13]:
qc = QuantumCircuit(q)
qc.id(q)
qc.draw()
[13]:
      ┌───┐
q0_0: ┤ I ├
      └───┘
[14]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[14]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j]])

Portes de Pauli

:math:` X `: porte d’inversion (flip-bit)

La porte bit-flip \(X\) est définie comme :

\[\begin{split}X = \begin{pmatrix} 0 & 1\\ 1 & 0 \end{pmatrix}= u3(\pi,0,\pi)\end{split}\]
[15]:
qc = QuantumCircuit(q)
qc.x(q)
qc.draw()
[15]:
      ┌───┐
q0_0: ┤ X ├
      └───┘
[16]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[16]:
array([[0.+0.j, 1.-0.j],
       [1.+0.j, 0.+0.j]])

\(Y\): porte d’inversion de bits et de phase

La porte \(Y\) est définie comme :

\[\begin{split}Y = \begin{pmatrix} 0 & -i\\ i & 0 \end{pmatrix}=u3(\pi,\pi/2,\pi/2)\end{split}\]
[17]:
qc = QuantumCircuit(q)
qc.y(q)
qc.draw()
[17]:
      ┌───┐
q0_0: ┤ Y ├
      └───┘
[18]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[18]:
array([[ 0.+0.j, -0.-1.j],
       [ 0.+1.j,  0.+0.j]])

\(Z\) : porte d’inversion de phase

La porte d’inversion (flip) de phase :math:` Z ` est définie comme suit:

\[\begin{split}Z = \begin{pmatrix} 1 & 0\\ 0 & -1 \end{pmatrix}=u1(\pi)\end{split}\]
[19]:
qc = QuantumCircuit(q)
qc.z(q)
qc.draw()
[19]:
      ┌───┐
q0_0: ┤ Z ├
      └───┘
[20]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[20]:
array([[ 1.+0.j,  0.+0.j],
       [ 0.+0.j, -1.+0.j]])

Portes de Clifford

Porte d’Hadamard

\[\begin{split}H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1\\ 1 & -1 \end{pmatrix}= u2(0,\pi)\end{split}\]
[21]:
qc = QuantumCircuit(q)
qc.h(q)
qc.draw()
[21]:
      ┌───┐
q0_0: ┤ H ├
      └───┘
[22]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[22]:
array([[ 0.707+0.j,  0.707-0.j],
       [ 0.707+0.j, -0.707+0.j]])

porte \(S\) (ou, \(\sqrt{Z}\) phase)

\[\begin{split}S = \begin{pmatrix} 1 & 0\\ 0 & i \end{pmatrix}= u1(\pi/2)\end{split}\]
[23]:
qc = QuantumCircuit(q)
qc.s(q)
qc.draw()
[23]:
      ┌───┐
q0_0: ┤ S ├
      └───┘
[24]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[24]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

porte \(S^{\dagger}\) (ou, conjuguant la phase \(\sqrt{Z}\) )

\[\begin{split}S^{\dagger} = \begin{pmatrix} 1 & 0\\ 0 & -i \end{pmatrix}= u1(-\pi/2)\end{split}\]
[25]:
qc = QuantumCircuit(q)
qc.sdg(q)
qc.draw()
[25]:
      ┌─────┐
q0_0: ┤ SDG ├
      └─────┘
[26]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[26]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.-1.j]])

portes \(C3\)

porte \(T\) (ou, phase \(\sqrt{S}\))

\[\begin{split}T = \begin{pmatrix} 1 & 0\\ 0 & e^{i \pi/4} \end{pmatrix}= u1(\pi/4)\end{split}\]
[27]:
qc = QuantumCircuit(q)
qc.t(q)
qc.draw()
[27]:
      ┌───┐
q0_0: ┤ T ├
      └───┘
[28]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[28]:
array([[1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707+0.707j]])

porte \(T^{\dagger}\) (ou, conjuguant la phase \(\sqrt{S}\))

\[\begin{split}T^{\dagger} = \begin{pmatrix} 1 & 0\\ 0 & e^{-i \pi/4} \end{pmatrix}= u1(-\pi/4)\end{split}\]
[29]:
qc = QuantumCircuit(q)
qc.tdg(q)
qc.draw()
[29]:
      ┌─────┐
q0_0: ┤ TDG ├
      └─────┘
[30]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[30]:
array([[1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707-0.707j]])

Rotations standard

Les portes de rotation standard sont celles qui définissent les rotations autour des \(P=\{X,Y,Z\}\) de Pauli. Elles sont définies comme

\[R_P(\theta) = \exp(-i \theta P/2) = \cos(\theta/2)I -i \sin(\theta/2)P\]

Rotation autour de l’axe X

\[\begin{split}R_x(\theta) = \begin{pmatrix} \cos(\theta/2) & -i\sin(\theta/2)\\ -i\sin(\theta/2) & \cos(\theta/2) \end{pmatrix} = u3(\theta, -\pi/2,\pi/2)\end{split}\]
[31]:
qc = QuantumCircuit(q)
qc.rx(pi/2,q)
qc.draw()
[31]:
      ┌──────────┐
q0_0: ┤ RX(pi/2) ├
      └──────────┘
[32]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[32]:
array([[ 0.707+0.j   , -0.   -0.707j],
       [ 0.   -0.707j,  0.707+0.j   ]])

Rotation autour de l’axe des Y

\[\begin{split}R_y(\theta) = \begin{pmatrix} \cos(\theta/2) & - \sin(\theta/2)\\ \sin(\theta/2) & \cos(\theta/2). \end{pmatrix} =u3(\theta,0,0)\end{split}\]
[33]:
qc = QuantumCircuit(q)
qc.ry(pi/2,q)
qc.draw()
[33]:
      ┌──────────┐
q0_0: ┤ RY(pi/2) ├
      └──────────┘
[34]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[34]:
array([[ 0.707+0.j, -0.707+0.j],
       [ 0.707+0.j,  0.707+0.j]])

Rotation autour de l’axe Z

\[\begin{split}R_z(\phi) = \begin{pmatrix} e^{-i \phi/2} & 0 \\ 0 & e^{i \phi/2} \end{pmatrix}\equiv u1(\phi)\end{split}\]

Notez qu’ici nous avons utilisé un équivalent car il est différent de u1 par une phase globale :math:` e ^ {-i phi/2 } `.

[35]:
qc = QuantumCircuit(q)
qc.rz(pi/2,q)
qc.draw()
[35]:
      ┌──────────┐
q0_0: ┤ RZ(pi/2) ├
      └──────────┘
[36]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[36]:
array([[1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j]])

Notez que ceci est différent en raison de la phase globale.

Portes multi-Qubit

Préliminaires mathématiques

L’espace de calcul d’un ordinateur quantique croît exponentiellement avec le nombre de qubits. Pour les \(n\) qubits, l’espace vectoriel complexe correspondant a une dimension \(d=2^n\). Pour décrire les états d’un système multiqubit, le produit tensoriel est utilisé pour « coller » les opérateurs et les vecteurs de base.

Commençons par considérer un système a 2-qubit. Étant donné les deux opérateurs \(A\) et \(B\) chacun agissant sur un seul qubit, l’opérateur commun \(A\otimes B\) agissant sur les deux qubits est

\[\begin{split}\begin{equation} A\otimes B = \begin{pmatrix} A_{00} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} & A_{01} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} \\ A_{10} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} & A_{11} \begin{pmatrix} B_{00} & B_{01} \\ B_{10} & B_{11} \end{pmatrix} \end{pmatrix}, \end{equation}\end{split}\]

où :math:` A_{jk}` et :math:` B_{lm}` sont les éléments matriciels de :math:` A ` et :math:` B `, respectivement.

De façon analogue, les vecteurs de base pour le système à deux qubits sont formés à l’aide du produit tensoriel des vecteurs de base pour un seul qubit:

\[\begin{split}\begin{equation}\begin{split} \left|{00}\right\rangle &= \begin{pmatrix} 1 \begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ 0 \begin{pmatrix} 1 \\ 0 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 1 \\ 0 \\ 0 \\0 \end{pmatrix}~~~\left|{01}\right\rangle = \begin{pmatrix} 1 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ 0 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} = \begin{pmatrix}0 \\ 1 \\ 0 \\ 0 \end{pmatrix}\end{split} \end{equation}\end{split}\]
\[\begin{split}\begin{equation}\begin{split}\left|{10}\right\rangle = \begin{pmatrix} 0\begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ 1\begin{pmatrix} 1 \\ 0 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix}~~~ \left|{11}\right\rangle = \begin{pmatrix} 0 \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ 1\begin{pmatrix} 0 \\ 1 \end{pmatrix} \end{pmatrix} = \begin{pmatrix} 0 \\ 0 \\ 0 \\1 \end{pmatrix}\end{split} \end{equation}.\end{split}\]

Note we’ve introduced a shorthand for the tensor product of basis vectors, wherein \(\left|0\right\rangle \otimes \left|0\right\rangle\) is written as \(\left|00\right\rangle\). The state of an \(n\)-qubit system can be described using the \(n\)-fold tensor product of single-qubit basis vectors. Notice that the basis vectors for a 2-qubit system are 4-dimensional; in general, the basis vectors of an \(n\)-qubit system are \(2^{n}\)-dimensional, as noted earlier.

Ordre des vecteurs de base dans Qiskit

Au sein de la communauté des physiciens, les qubits d’un système multiqubit sont généralement classés avec le premier qubit sur le côté gauche du produit tensoriel et le dernier qubit sur le côté droit. Par exemple, si le premier qubit est à l’état :math:` left | 0rightrangle ` et le second est dans l’état :math:` left | 1rightrangle , leur état joint est :math: left | 01rightrangle . Qiskit utilise un ordre légèrement différent des qubits, dans lequel les qubits sont représentés du bit le plus significatif (MSB) sur la gauche au bit le moins significatif (LSB) à droite (big-endian). Ceci est similaire à la représentation chaine de bits “bitstring” sur les ordinateurs classiques, et permet une conversion facile des bitstring en nombres entiers une fois les mesures effectuées. Pour l’exemple qui vient d’être donné, l’état joint est représenté sous la forme :math: left | 10rightrangle `. Fait important, * ce changement dans la représentation des états multiqubits affecte la façon dont les portes multiqubit sont représentées dans Qiskit *, comme indiqué ci-dessous.

La représentation utilisée dans Qiskit énumère les vecteurs de base dans l’ordre croissant des entiers qu’ils représentent. Par exemple, les vecteurs de base d’un système 2-qubit sont classés comme ceci :math:` left | 00rightrangle , :math: left | 01rightrangle , :math: left | 10rightrangle , and :math: left | 11rightrangle `. En pensant aux vecteurs de base comme des chaînes de bits, ils encodent les entiers 0,1,2 et 3, respectivement.

Opérations contrôlées sur des qubits

Une porte multiqubit simple implique l’application d’une porte à un qubit, conditionnée à l’état d’un autre qubit. Par exemple, nous pourrions faire basculer l’état du deuxième qubit lorsque le premier qubit est en :math:` left | 0rightrangle `. Ces portes sont connues sous le nom de « portes contrôlées » *. Les portes multi-qubits standard se composent de portes avec deux qubits et de portes avec trois qubit.s Les portes de deux qubits sont: - les portes de Pauli - les portes Hadamard contrôlées - les portes de rotation contrôlée -les portes de contrôle de phase - la porte contrôlée u3 - la poste de permutation (swap)

Les portes à trois qubits sont:- la porte de Toffoli - la porte de Fredkin

Portes à deux qubits

La plupart des portes à deux qubits sont de type contrôlé (la porte SWAP étant l’exception). En général, une porte à deux qubits contrôlée \(C_{U}\) agit pour appliquer l’unitaire a simple qubit \(U\) au second qubit lorsque l’état du premier qubit est en \(\left|1\right\rangle\). Supposons que \(U\) ait une représentation matricielle

\[\begin{split}U = \begin{pmatrix} u_{00} & u_{01} \\ u_{10} & u_{11}\end{pmatrix}.\end{split}\]

Nous pouvons définir l’action de \(C_{U}\) comme suit. Rappelez-vous que les vecteurs de base pour un système à deux qubits sont ordonnés comme ceci: \(\left|00\rdroit\r\left|01\right\rangle, \left|10\right\rangle, \left|11\right\rangle\). Supposons que le qubit de contrôle soit le qubit 0 (ce qui, selon la convention de Qiskit, est du côté droit du produit de tenseur). Si le qubit de contrôle est en \(\left|1\right\rangle\), \(U\) devrait être appliqué à la cible (qubit 1, du côté gauche du produit de tenseur). Par conséquent, sous l’action de \(C_{U}\), les vecteurs de base sont transformés en fonction de

\[\begin{split}\begin{align*} C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{U\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{U\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ \end{align*}.\end{split}\]

Sous forme de matrice, l’action de :math:` C_{U}` est

\[\begin{split}\begin{equation} C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & u_{00} & 0 & u_{01} \\ 0 & 0 & 1 & 0 \\ 0 & u_{10} &0 & u_{11} \end{pmatrix}. \end{equation}\end{split}\]

Pour trouver ces éléments de la matrice, prenons

\[C_{(jk), (lm)} = \left(\underset{\text{qubit}~1}{\left\langle j \right|} \otimes \underset{\text{qubit}~0}{\left\langle k \right|}\right) C_{U} \left(\underset{\text{qubit}~1}{\left| l \right\rangle} \otimes \underset{\text{qubit}~0}{\left| k \right\rangle}\right),\]

calcule l’action de \(C_{U}\) (donné ci-dessus) et calcule les produits intérieurs.

Comme le montrent les exemples ci-dessous, cette opération est implémentée dans Qiskit sous la forme ` ` cU (q[0], q[1]) ` `.

Si ** qubit 1 est le contrôle et qubit 0 est la cible * *, les vecteurs de base sont transformés selon

\[\begin{split}\begin{align*} C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|0\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|0\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{U\left|0\right\rangle}\\ C_{U}: \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{\left|1\right\rangle} &\rightarrow \underset{\text{qubit}~1}{\left|1\right\rangle}\otimes \underset{\text{qubit}~0}{U\left|1\right\rangle}\\ \end{align*},\end{split}\]

qui implique que la forme matricielle de :math:` C_{U}` est

\[\begin{split}\begin{equation} C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & u_{00} & u_{01} \\ 0 & 0 & u_{10} & u_{11} \end{pmatrix}. \end{equation}\end{split}\]
[37]:
q = QuantumRegister(2)

Portes de Pauli contrôlées

Porte control-X (ou, control-NOT)

La porte control-NOT inverse le qubit cible lorsque le qubit de contrôle est dans l’état \(\left |1\right\rangle\). Si nous prenons le MSB (most significant bit ou bit de poids fort) comme qubit de contrôle (par exemple cx(q[1], q[0])), alors la matrice ressemblera à

\[\begin{split}C_X = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{pmatrix}.\end{split}\]

Et lorsque c’est le bit de poids faible (LSB, less significant bit) qui est le qubit de contrôle (par exemple cx(q[0], q[1])), cette porte est équivalente à la matrice suivante :

\[\begin{split}C_X = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0 \end{pmatrix}.\end{split}\]
[38]:
qc = QuantumCircuit(q)
qc.cx(q[0],q[1])
qc.draw()
[38]:
q1_0: ──■──
      ┌─┴─┐
q1_1: ┤ X ├
      └───┘
[39]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[39]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]])

Porte control:math:Y

Applique la porte :math:` Y au qubit cible si le qubit de contrôle est le MSB

\[\begin{split}C_Y = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & -i\\ 0 & 0 & i & 0 \end{pmatrix},\end{split}\]

ou lorsque le LSB est le qubit de contrôle

\[\begin{split}C_Y = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 0 & -i\\ 0 & 0 & 1 & 0\\ 0 & i & 0 & 0 \end{pmatrix}.\end{split}\]
[40]:
qc = QuantumCircuit(q)
qc.cy(q[0],q[1])
qc.draw()
[40]:
q1_0: ──■──
      ┌─┴─┐
q1_1: ┤ Y ├
      └───┘
[41]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[41]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j]])

Porte de contrôle \(Z\) (ou, porte de contrôle de phase)

De même, la porte contrôlée Z inverse la phase du qubit cible si le qubit de contrôle est :math:` left | 1rightrangle `. La matrice est la même, que le qubit de contrôle soit le MSB ou le LSB:

\[\begin{split}C_Z = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & -1 \end{pmatrix}\end{split}\]
[42]:
qc = QuantumCircuit(q)
qc.cz(q[0],q[1])
qc.draw()
[42]:
q1_0: ─■─
       │
q1_1: ─■─
         
[43]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[43]:
array([[ 1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  0.+0.j, -1.+0.j]])

Porte de Hadamard contrôlée

Applique la porte :math:` H ` au qubit cible si le qubit de contrôle est :math:` left | 1rightrangle `. Ci-dessous se trouve le cas où le qubit de contrôle est le LSB.

\[\begin{split}C_H = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & \frac{1}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}}\\ 0 & 0 & 1 & 0\\ 0 & \frac{1}{\sqrt{2}} & 0& -\frac{1}{\sqrt{2}} \end{pmatrix}\end{split}\]
[44]:
qc = QuantumCircuit(q)
qc.ch(q[0],q[1])
qc.draw()
[44]:
q1_0: ──■──
      ┌─┴─┐
q1_1: ┤ H ├
      └───┘
[45]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[45]:
array([[ 1.   -0.j,  0.   +0.j,  0.   +0.j,  0.   +0.j],
       [ 0.   +0.j,  0.707-0.j,  0.   +0.j,  0.707-0.j],
       [ 0.   +0.j,  0.   +0.j,  1.   -0.j,  0.   +0.j],
       [ 0.   +0.j,  0.707+0.j,  0.   +0.j, -0.707+0.j]])

Portes de rotation contrôlées

Rotation contrôlée autour de l’axe Z

Effectue rotation autour de Z-axis sur le qubit cible si le qubit de contrôle (ici LSB) est \(\left|1\right\rangle\).

\[\begin{split}C_{Rz}(\lambda) = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & e^{-i\lambda/2} & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & e^{i\lambda/2} \end{pmatrix}\end{split}\]
[46]:
qc = QuantumCircuit(q)
qc.crz(pi/2,q[0],q[1])
qc.draw()
[46]:
q1_0: ─────■──────
      ┌────┴─────┐
q1_1: ┤ RZ(pi/2) ├
      └──────────┘
[47]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[47]:
array([[1.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.707-0.707j, 0.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.   +0.j   , 1.   +0.j   , 0.   +0.j   ],
       [0.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.707+0.707j]])

Rotation de phase contrôlée

Effectuez une rotation de phase si les deux bits se trouvent dans l’état :math:` left | 11rightr. La matrice est la même, que le MSB ou le LSB soit le qubit de contrôle.

\[\begin{split}C_{u1}(\lambda) = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & e^{i\lambda} \end{pmatrix}\end{split}\]
[48]:
qc = QuantumCircuit(q)
qc.cu1(pi/2,q[0], q[1])
qc.draw()
[48]:
q1_0: ─■─────
       │pi/2
q1_1: ─■─────
             
[49]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[49]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j]])

Rotation \(u3\) contrôlée

Perform controlled-\(u3\) rotation on the target qubit if the control qubit (here LSB) is \(\left|1\right\rangle\).

\[\begin{split}C_{u3}(\theta, \phi, \lambda) \equiv \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & e^{-i(\phi+\lambda)/2}\cos(\theta/2) & 0 & -e^{-i(\phi-\lambda)/2}\sin(\theta/2)\\ 0 & 0 & 1 & 0\\ 0 & e^{i(\phi-\lambda)/2}\sin(\theta/2) & 0 & e^{i(\phi+\lambda)/2}\cos(\theta/2) \end{pmatrix}.\end{split}\]
[50]:
qc = QuantumCircuit(q)
qc.cu3(pi/2, pi/2, pi/2, q[0], q[1])
qc.draw()
[50]:
q1_0: ──────────■───────────
      ┌─────────┴──────────┐
q1_1: ┤ U3(pi/2,pi/2,pi/2) ├
      └────────────────────┘
[51]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[51]:
array([[ 1.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ],
       [ 0.   +0.j   ,  0.707+0.j   ,  0.   +0.j   , -0.   -0.707j],
       [ 0.   +0.j   ,  0.   +0.j   ,  1.   +0.j   ,  0.   +0.j   ],
       [ 0.   +0.j   ,  0.   +0.707j,  0.   +0.j   , -0.707+0.j   ]])

Porte SWAP

La porte SWAP échange les deux qubits. Elle transforme les vecteurs de base en

\[\left|00\right\rangle \rightarrow \left|00\right\rangle~,~\left|01\right\rangle \rightarrow \left|10\right\rangle~,~\left|10\right\rangle \rightarrow \left|01\right\rangle~,~\left|11\right\rangle \rightarrow \left|11\right\rangle,\]

qui donne une représentation matricielle de la forme

\[\begin{split}\mathrm{SWAP} = \begin{pmatrix} 1 & 0 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 1 \end{pmatrix}.\end{split}\]
[52]:
qc = QuantumCircuit(q)
qc.swap(q[0], q[1])
qc.draw()
[52]:
q1_0: ─X─
       │
q1_1: ─X─
         
[53]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[53]:
array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
       [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]])

Portes à trois qubits

Il y a deux portes à trois qubits communément utilisées. Pour trois qubits, les vecteurs de base sont ordonnés comme

\[\left|000\right\rangle, \left|001\right\rangle, \left|010\right\rangle, \left|011\right\rangle, \left|100\right\rangle, \left|101\right\rangle, \left|110\right\rangle, \left|111\right\rangle,\]

qui, en tant que chaînes de bits, représentent les entiers \(0,1,2,\cdots, 7\). Encore une fois, Qiskit utilise une représentation dans laquelle le premier qubit est celui le plus à droite du produit tenseur et le troisième qubit est le plus à gauche :

\[\left|abc\right\rangle : \underset{\text{qubit 2}}{\left|a\right\rangle}\otimes \underset{\text{qubit 1}}{\left|b\right\rangle}\otimes \underset{\text{qubit 0}}{\left|c\right\rangle}.\]

Porte de Toffoli (:math:` ccx ` gate)

La  » porte de Toffoli <https://en.wikipedia.org/wiki/Quantum_logic_gate#Toffoli_(CCNOT)_gate>` __ inverse le troisième qubit si les deux premiers qubits (LSB) sont tous les deux :math:` left | 1rightrangle `:

\[\left|abc\right\rangle \rightarrow \left|bc\oplus a\right\rangle \otimes \left|b\right\rangle \otimes \left|c\right\rangle.\]

Sous forme de matrice, la porte Toffoli est

\[\begin{split}C_{CX} = \begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \end{pmatrix}.\end{split}\]
[54]:
q = QuantumRegister(3)
[55]:
qc = QuantumCircuit(q)
qc.ccx(q[0], q[1], q[2])
qc.draw()
[55]:
q2_0: ──■──
        │
q2_1: ──■──
      ┌─┴─┐
q2_2: ┤ X ├
      └───┘
[56]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[56]:
array([[1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])

Porte d’échange (swap) contrôlée (Porte de Fredkin)

La porte de Fredkin, ou la porte d’échange contrôlée, échange les deuxième et troisième qubits si le premier qubit (LSB) est \(\left|1\right\rangle\):

\[\left|abc\right\rangle \rightarrow \begin{cases} \left|bac\right\rangle~~\text{if}~c=1 \cr \left|abc\right\rangle~~\text{if}~c=0 \end{cases}.\]

Sous forme de matrice, la porte Fredkin est

\[\begin{split}C_{\mathrm{SWAP}} = \begin{pmatrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 \end{pmatrix}.\end{split}\]
[57]:
qc = QuantumCircuit(q)
qc.cswap(q[0], q[1], q[2])
qc.draw()
[57]:
q2_0: ─■─
       │
q2_1: ─X─
       │
q2_2: ─X─
         
[58]:
job = execute(qc, backend)
job.result().get_unitary(qc, decimals=3)
[58]:
array([[1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],
       [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j]])

Opérations non unitaires

En plus de toutes les opérations unitaires dans les circuits quantiques, nous avons aussi accès à des opérations non unitaires. Il s’agit notamment des mesures, de la réinitialisation des qubits et des opérations conditionnelles classiques.

[59]:
q = QuantumRegister(1)
c = ClassicalRegister(1)

Mesures

Nous n’avons pas accès à toutes les informations lorsque nous faisons une mesure dans un ordinateur quantique. L’état quantique est projeté sur la base standard. Ci-dessous sont deux exemples montrant d’une part un circuit qui est préparé dans un état de base et d’autre part un état de superposition.

[60]:
qc = QuantumCircuit(q, c)
qc.measure(q, c)
qc.draw()
[60]:
      ┌─┐
q3_0: ┤M├
      └╥┘
c0_0: ═╩═
         
[61]:
backend = BasicAer.get_backend('qasm_simulator')
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[61]:
{'0': 1024}

Le simulateur prédit 100% du temps que le registre classique renvoie 0.

[62]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
qc.draw()
[62]:
      ┌───┐┌─┐
q3_0: ┤ H ├┤M├
      └───┘└╥┘
c0_0: ══════╩═
              
[63]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[63]:
{'0': 532, '1': 492}

Le simulateur prédit que la valeur du registre classique sera 1 ou 0 à 50% du temps.

Réinitialisation

Il est également possible de ` ` réinitialiser ` ` les qubits vers l’état :math:` left | 0rpositionrangle ` au milieu du calcul. Notez que ` ` reset ` ` n’est pas une porte logique, puisqu’elle est irréversible.

[64]:
qc = QuantumCircuit(q, c)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[64]:
           ┌─┐
q3_0: ─|0>─┤M├
           └╥┘
c0_0: ══════╩═
              
[65]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[65]:
{'0': 1024}
[66]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[66]:
      ┌───┐     ┌─┐
q3_0: ┤ H ├─|0>─┤M├
      └───┘     └╥┘
c0_0: ═══════════╩═
                   
[67]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[67]:
{'0': 1024}

Ici, nous voyons que pour ces deux circuits, le simulateur prévoit toujours que la sortie est à 100% dans l’état 0.

Opérations conditionnelles

Il est également possible d’effectuer des opérations conditionnées par l’état du registre classique

[68]:
qc = QuantumCircuit(q, c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[68]:
       ┌───┐ ┌─┐
q3_0: ─┤ X ├─┤M├
       └─┬─┘ └╥┘
      ┌──┴──┐ ║
c0_0: ╡ = 0 ╞═╩═
      └─────┘   

Ici, le bit classique prend toujours la valeur 0 donc l’état du qubit est toujours inversé.

[69]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[69]:
{'1': 1024}
[70]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q,c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[70]:
      ┌───┐┌─┐ ┌───┐ ┌─┐
q3_0: ┤ H ├┤M├─┤ X ├─┤M├
      └───┘└╥┘ └─┬─┘ └╥┘
            ║ ┌──┴──┐ ║
c0_0: ══════╩═╡ = 0 ╞═╩═
              └─────┘   
[71]:
job = execute(qc, backend, shots=1024)
job.result().get_counts(qc)
[71]:
{'1': 1024}

Ici, le bit classique de la première mesure est aléatoire, mais l’opération conditionnelle résulte en un qubit déterministiquement mis en :math:` left | 1rightrangle `.

Initialisation arbitraire

Et si nous voulons initialiser un registre de qubit à un état arbitraire? Un état arbitraire pour :math:` n ` qubits peut être spécifié par un vecteur d’amplitude :math:` 2 ^ n ` , où la somme des amplitudes au carré est égale à 1. Par exemple, l’état tri-qubit suivant peut être préparé:

\[\left|\psi\right\rangle = \frac{i}{4}\left|000\right\rangle + \frac{1}{\sqrt{8}}\left|001\right\rangle + \frac{1+i}{4}\left|010\right\rangle + \frac{1+2i}{\sqrt{8}}\left|101\right\rangle + \frac{1}{4}\left|110\right\rangle\]
[77]:
# Initializing a three-qubit quantum state
import math
desired_vector = [
    1 / math.sqrt(16) * complex(0, 1),
    1 / math.sqrt(8) * complex(1, 0),
    1 / math.sqrt(16) * complex(1, 1),
    0,
    0,
    1 / math.sqrt(8) * complex(1, 2),
    1 / math.sqrt(16) * complex(1, 0),
    0]


q = QuantumRegister(3)

qc = QuantumCircuit(q)

qc.initialize(desired_vector, [q[0],q[1],q[2]])
qc.draw()
[77]:
       ┌───────────────────────────────────────────────────────────────────┐
q28_0: ┤0                                                                  ├
       │                                                                   │
q28_1: ┤1 initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├
       │                                                                   │
q28_2: ┤2                                                                  ├
       └───────────────────────────────────────────────────────────────────┘
[74]:
backend = BasicAer.get_backend('statevector_simulator')
job = execute(qc, backend)
qc_state = job.result().get_statevector(qc)
qc_state
[74]:
array([2.50000000e-01+0.j        , 5.55111512e-17-0.35355339j,
       2.50000000e-01-0.25j      , 0.00000000e+00+0.j        ,
       0.00000000e+00+0.j        , 7.07106781e-01-0.35355339j,
       8.67361738e-17-0.25j      , 0.00000000e+00+0.j        ])

La fidelité <https://en.wikipedia.org/wiki/Fidelity_of_quantum_states> __ est utile pour vérifier si deux états sont identiques ou non. Pour les états quantiques (purs) :math:` left | psi_1rightrangle ` et :math:` left | psi_2rightrangle `, la fidélité est

\[F\left(\left|\psi_1\right\rangle,\left|\psi_2\right\rangle\right) = \left|\left\langle\psi_1\middle|\psi_2\right\rangle\right|^2.\]

La fidélité est égale à :math:` 1 si et seulement si les deux états sont égaux.

[75]:
state_fidelity(desired_vector,qc_state)
[75]:
1.0

Plus de détails:

Comment l’état souhaité est-il généré ? Il existe plusieurs méthodes pour ce faire. Qiskit utilise une méthode proposée par Shende et al <https://arxiv.org/abs/quant-ph/0406176>` __. Ici, l’idée est de supposer que le registre quantique a commencé à partir de notre état souhaité, et de construire un circuit qui l’emmène à l’état :math:` left | 00 .. 0rightrangle ` . Le circuit d’initialisation est alors l’inverse de ce circuit.

Pour obtenir un état quantique arbitraire zéro dans la base de calcul, nous effectuons une procédure itérative qui démêle les qubits du registre un par un. Nous savons que tout état arbitraire à un seul qubit :math:` left |rhorightrangle ` peut être modifié vers l’état :math:` left | 0rightrangle ` en utilisant une rotation autout de l’axe Z de :math:` phi -degrès suivie d’une rotation autour de l’axe Y de :math:theta `-degrés

\[R_y(-\theta)R_z(-\phi)\left|\rho\right\rangle = re^{it}\left|0\right\rangle\]

Puisque maintenant nous avons affaire avec :math:` n ` qubits au lieu de seulement 1, nous devons factoriser le vecteur d’état pour extraire le bit le moins significatif (LSB least significant bit):

\[\begin{split}\begin{align*} \left|\psi\right\rangle =& \alpha_{0_0}\left|00..00\right\rangle + \alpha_{0_1}\left|00..01\right\rangle + \alpha_{1_0}\left|00..10\right\rangle + \alpha_{1_1}\left|00..11\right\rangle + ... \\&+ \alpha_{(2^{n-1}-1)_0}\left|11..10\right\rangle + \alpha_{(2^{n-1}-1)_1}\left|11..11\right\rangle \\ =& \left|00..0\right\rangle (\alpha_{0_0}\left|0\right\rangle + \alpha_{0_1}\left|1\right\rangle) + \left|00..1\right\rangle (\alpha_{1_0}\left|0\right\rangle + \alpha_{1_1}\left|1\right\rangle) + ... \\&+ \left|11..1\right\rangle (\alpha_{(2^{n-1}-1)_0}(\left|0\right\rangle + \alpha_{(2^{n-1}-1)_1}\left|1\right\rangle) \\ =& \left|00..0\right\rangle\left|\rho_0\right\rangle + \left|00..1\right\rangle\left|\rho_1\right\rangle + ... + \left|11..1\right\rangle\left|\rho_{2^{n-1}-1}\right\rangle \end{align*}\end{split}\]

Maintenant chacun des états à un seul qubit :math:` left |rho_0rightrangle, …, left |rho_ { 2 ^{n-1}-1 }rightrangle ` peut être transformé vers :math:` left | 0rightrangle ` en trouvant les angles :math:` phi ` et :math: `theta ` appropriés en suivant l’équation ci-dessus. Faire cela simultanément sur tous les états revient à l’unité suivante, qui démêle la LSB:

\[\begin{split}U = \begin{pmatrix} R_{y}(-\theta_0)R_{z}(-\phi_0) & & & &\\ & R_{y}(-\theta_1)R_{z}(-\phi_1) & & &\\ & . & & &\\ & & . & &\\ & & & & R_y(-\theta_{2^{n-1}-1})R_z(-\phi_{2^{n-1}-1}) \end{pmatrix}\end{split}\]

C’est pourquoi

\[\begin{split}U\left|\psi\right\rangle = \begin{pmatrix} r_0e^{it_0}\\ r_1e^{it_1}\\ . \\ . \\ r_{2^{n-1}-1}e^{it_{2^{n-1}-1}} \end{pmatrix}\otimes\left|0\right\rangle\end{split}\]

U peut être implémenté comme un « multiplexeur quantique », puisqu’il s’agit d’une matrice diagonale par bloc. Dans le formalisme quantique de multiplexage, une matrice diagonale par bloc de dimensions \(2^n \times 2^n\), et comprenant \(2^s\) blocs est équivalente à un multiplexeur avec \(s\) qubits de selection et \(n - s\) qubits de données. En fonction de l’état des bits de sélection, les blocs correspondants sont appliqués aux bits de données. Un multiplexeur de ce type peut être mis en œuvre après une décomposition récursive vers les portes primitives cx, rz et ry.

[76]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
QiskitNone
Terra0.14.0
Aer0.6.0
Ignis0.3.0
AquaNone
IBM Q Provider0.6.1
System information
Python3.7.7 (default, Mar 26 2020, 10:32:53) [Clang 4.0.1 (tags/RELEASE_401/final)]
OSDarwin
CPUs4
Memory (Gb)16.0
Tue Apr 28 22:09:33 2020 EDT

This code is a part of Qiskit

© Copyright IBM 2017, 2020.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

[ ]: