Note
Cette page a été générée à partir de tutorials/circuits/3_summary_of_quantum_operations.ipynb.
Liste des opérateurs quantiques¶
Dans cette section, nous aborderons les différentes opérations disponibles dans le module Terra de Qiskit. Celles-ci sont :
Portes quantiques mono-qubit
Portes quantiques multi-qubits
Mesures
Réinitialisation
Conditions
Initialisation d’état
Nous vous montrerons également comment utiliser les trois simulateurs différents:
unitary_simulator
qasm_simulator
statevector_simulator
[1]:
# Useful additional packages
import matplotlib.pyplot as plt
import numpy as np
from math import pi
[2]:
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, transpile
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
où \(\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
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
où \(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.
Un état unitaire général doit être en mesure de prendre l’angle :math:`left|0rdroitrà l’état ci-dessus. C’est à dire
où \(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)\) où \(0\leq \lambda<2\pi\) qui donnent
Il s’agit de la forme la plus générale d’un qubit unitaire/unique.
Portes à un seul Qubit¶
Les portes mono-qubit disponibles sont : - porte U - porte Identité - portes de Pauli - portes de Clifford - portes \(C3\) - portes de rotation standard
Nous avons fourni un backend : unitary_simulator
pour vous permettre de calculer les matrices unitaires.
[3]:
q = QuantumRegister(1)
Porte U¶
Dans Qiskit, nous vous donnons accès à l’unité générale en utilisant la porte \(u\), qui a la forme de la matrice suivante
[4]:
qc = QuantumCircuit(q)
qc.u(pi/2,pi/4,pi/8,q)
qc.draw()
[4]:
┌────────────────┐ q0: ┤ U(π/2,π/4,π/8) ├ └────────────────┘
[5]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[5]:
array([[ 0.707+0.j , -0.653-0.271j],
[ 0.5 +0.5j , 0.271+0.653j]])
Note sur la dépréciation de la porte U
Les méthodes QuantumCircuit \(u1\), \(u2\) et \(u3\) sont désormais obsolètes. En revanche, les remplacements suivants devraient être utilisés.
\(u1(\lambda) = p(\lambda) = u(0, 0, \lambda)\)
\(u2(\phi, \lambda) = u(\frac{\pi}{2}, \phi, \lambda) = p(\frac{\pi}{2} + \phi) \cdot sx \cdot p(\frac{\pi}{2} - \lambda)\)
\(u3(\theta, \phi, \lambda) = u(\theta, \phi, \lambda) = p(\phi + \pi) \cdot sx \cdot p(\theta + \pi) \cdot sx \cdot p(\lambda)\)
# qc.u1(lambda) is now:
qc.p(lambda)
# qc.u2(phi, lambda) is now:
qc.u(pi/2, phi, lambda)
# qc.u3(theta, phi, lambda) is now:
qc.u(theta, phi, lambda)
Porte P¶
La porte :math:` u1 (lambda) = u3 (0, 0, lambda) a la forme matricielle
qui est utile car il nous permet d’appliquer une phase quantique.
[13]:
qc = QuantumCircuit(q)
qc.p(pi/2,q)
qc.draw()
[13]:
┌────────┐ q0: ┤ P(π/2) ├ └────────┘
[14]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[14]:
array([[1.+0.j, 0.+0.j],
[0.+0.j, 0.+1.j]])
Porte identité¶
La porte d’identité est \(Id = p (0)\).
[15]:
qc = QuantumCircuit(q)
qc.id(q)
qc.draw()
[15]:
┌───┐ q0: ┤ I ├ └───┘
[16]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[16]:
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 :
[17]:
qc = QuantumCircuit(q)
qc.x(q)
qc.draw()
[17]:
┌───┐ q0: ┤ X ├ └───┘
[18]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[18]:
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 :
[19]:
qc = QuantumCircuit(q)
qc.y(q)
qc.draw()
[19]:
┌───┐ q0: ┤ Y ├ └───┘
[20]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[20]:
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:
[21]:
qc = QuantumCircuit(q)
qc.z(q)
qc.draw()
[21]:
┌───┐ q0: ┤ Z ├ └───┘
[23]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[23]:
array([[ 1.+0.j, 0.+0.j],
[ 0.+0.j, -1.+0.j]])
Portes de Clifford¶
Porte d’Hadamard¶
[22]:
qc = QuantumCircuit(q)
qc.h(q)
qc.draw()
[22]:
┌───┐ q0: ┤ H ├ └───┘
[23]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[23]:
array([[ 0.707+0.j, 0.707-0.j],
[ 0.707+0.j, -0.707+0.j]])
porte \(S\) (ou, \(\sqrt{Z}\) phase)¶
[24]:
qc = QuantumCircuit(q)
qc.s(q)
qc.draw()
[24]:
┌───┐ q0: ┤ S ├ └───┘
[29]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[29]:
array([[1.+0.j, 0.+0.j],
[0.+0.j, 0.+1.j]])
porte \(S^{\dagger}\) (ou, conjuguant la phase \(\sqrt{Z}\) )¶
[25]:
qc = QuantumCircuit(q)
qc.sdg(q)
qc.draw()
[25]:
┌─────┐ q0: ┤ Sdg ├ └─────┘
[26]:
job = backend.run(transpile(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}\))¶
[27]:
qc = QuantumCircuit(q)
qc.t(q)
qc.draw()
[27]:
┌───┐ q0: ┤ T ├ └───┘
[28]:
job = backend.run(transpile(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}\))¶
[29]:
qc = QuantumCircuit(q)
qc.tdg(q)
qc.draw()
[29]:
┌─────┐ q0: ┤ Tdg ├ └─────┘
[30]:
job = backend.run(transpile(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
Rotation autour de l’axe X¶
[31]:
qc = QuantumCircuit(q)
qc.rx(pi/2,q)
qc.draw()
[31]:
┌─────────┐ q0: ┤ Rx(π/2) ├ └─────────┘
[32]:
job = backend.run(transpile(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¶
[33]:
qc = QuantumCircuit(q)
qc.ry(pi/2,q)
qc.draw()
[33]:
┌─────────┐ q0: ┤ Ry(π/2) ├ └─────────┘
[34]:
job = backend.run(transpile(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¶
Notez qu’ici nous avons utilisé un équivalent car il est différent de \(p\) par une phase globale \(e ^ {-i \phi/2 }\).
[35]:
qc = QuantumCircuit(q)
qc.rz(pi/2,q)
qc.draw()
[35]:
┌─────────┐ q0: ┤ Rz(π/2) ├ └─────────┘
[36]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[36]:
array([[0.707-0.707j, 0. +0.j ],
[0. +0.j , 0.707+0.707j]])
Notez que ceci est différent uniquement en d’une phase globale.
Portes multi-Qubit¶
Préliminaires mathématiques¶
The space of a quantum computer grows exponentially with the number of qubits. For \(n\) qubits the complex vector space has dimension \(d=2^n\). To describe states of a multi-qubit system, the tensor product is used to « glue together » operators and basis vectors.
Let’s start by considering a 2-qubit system. Given two operators \(A\) and \(B\) that each act on one qubit, the joint operator \(A \otimes B\) acting on two qubits is
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:
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, gros-boutisme). 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
We can work out the action of \(C_{U}\) as follows. Recall that the basis vectors for a two-qubit system are ordered as \(\left|00\right\rangle, \left|01\right\rangle, \left|10\right\rangle, \left|11\right\rangle\). Suppose the control qubit is qubit 0 (which, according to Qiskit’s convention, is one the right-hand side of the tensor product). If the control qubit is in \(\left|1\right\rangle\), \(U\) should be applied to the target (qubit 1, on the left-hand side of the tensor product). Therefore, under the action of \(C_{U}\), the basis vectors are transformed according to
Sous forme de matrice, l’action de :math:` C_{U}` est
Pour trouver ces éléments de la matrice, prenons
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
qui implique que la forme matricielle de :math:` C_{U}` est
[37]:
q = QuantumRegister(2)
Portes de Pauli contrôlées¶
Porte control-X (ou control-NOT)¶
The Controlled-NOT gate flips the target
qubit when the control qubit is in the state \(\left|1\right\rangle\). If we take the MSB as the control qubit (e.g. cx(q[1],q[0])
), then the matrix would look like
However, when the LSB is the control qubit, (e.g. cx(q[0],q[1])
), this gate is equivalent to the following matrix:
[38]:
qc = QuantumCircuit(q)
qc.cx(q[0],q[1])
qc.draw()
[38]:
q60_0: ──■── ┌─┴─┐ q60_1: ┤ X ├ └───┘
[39]:
job = backend.run(transpile(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
ou lorsque le LSB est le qubit de contrôle
[40]:
qc = QuantumCircuit(q)
qc.cy(q[0],q[1])
qc.draw()
[40]:
q60_0: ──■── ┌─┴─┐ q60_1: ┤ Y ├ └───┘
[41]:
job = backend.run(transpile(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]])
Contrl- \(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:
[42]:
qc = QuantumCircuit(q)
qc.cz(q[0],q[1])
qc.draw()
[42]:
q60_0: ─■─ │ q60_1: ─■─
[43]:
job = backend.run(transpile(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.
[44]:
qc = QuantumCircuit(q)
qc.ch(q[0],q[1])
qc.draw()
[44]:
q60_0: ──■── ┌─┴─┐ q60_1: ┤ H ├ └───┘
[45]:
job = backend.run(transpile(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\).
[46]:
qc = QuantumCircuit(q)
qc.crz(pi/2,q[0],q[1])
qc.draw()
[46]:
q60_0: ─────■───── ┌────┴────┐ q60_1: ┤ Rz(π/2) ├ └─────────┘
[47]:
job = backend.run(transpile(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.
[48]:
qc = QuantumCircuit(q)
qc.cp(pi/2,q[0], q[1])
qc.draw()
[48]:
q60_0: ─■─────── │P(π/2) q60_1: ─■───────
[49]:
job = backend.run(transpile(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 contrôlée \(u3\)¶
Effectue la rotation contrôlée-\(u\) sur le qubit cible si le qubit de contrôle (ici LSB) est \(\left|1\right\rangle\).
[53]:
qc = QuantumCircuit(q)
qc.cu(pi/2, pi/2, pi/2, 0, q[0], q[1])
qc.draw()
[53]:
q60_0: ─────────■────────── ┌────────┴─────────┐ q60_1: ┤ U(π/2,π/2,π/2,0) ├ └──────────────────┘
[54]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[54]:
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
qui donne une représentation matricielle de la forme
[55]:
qc = QuantumCircuit(q)
qc.swap(q[0], q[1])
qc.draw()
[55]:
q60_0: ─X─ │ q60_1: ─X─
[56]:
job = backend.run(transpile(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, 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
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 :
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 `:
Sous forme de matrice, la porte Toffoli est
[57]:
q = QuantumRegister(3)
[58]:
qc = QuantumCircuit(q)
qc.ccx(q[0], q[1], q[2])
qc.draw()
[58]:
q105_0: ──■── │ q105_1: ──■── ┌─┴─┐ q105_2: ┤ X ├ └───┘
[59]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[59]:
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\):
Sous forme de matrice, la porte Fredkin est
[60]:
qc = QuantumCircuit(q)
qc.cswap(q[0], q[1], q[2])
qc.draw()
[60]:
q105_0: ─■─ │ q105_1: ─X─ │ q105_2: ─X─
[61]:
job = backend.run(transpile(qc, backend))
job.result().get_unitary(qc, decimals=3)
[61]:
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.
[62]:
q = QuantumRegister(1)
c = ClassicalRegister(1)
Mesures¶
We don’t have access to all the information when we make a measurement in a quantum computer. The quantum state is projected onto the standard basis. Below are two examples showing a circuit that is prepared in a basis state and the quantum computer prepared in a superposition state.
[63]:
qc = QuantumCircuit(q, c)
qc.measure(q, c)
qc.draw()
[63]:
┌─┐ q124: ┤M├ └╥┘ c0: 1/═╩═ 0
[64]:
backend = BasicAer.get_backend('qasm_simulator')
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[64]:
{'0': 1024}
Le simulateur prédit 100% du temps que le registre classique renvoie 0.
[65]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
qc.draw()
[65]:
┌───┐┌─┐ q124: ┤ H ├┤M├ └───┘└╥┘ c0: 1/══════╩═ 0
[66]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[66]:
{'1': 532, '0': 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.
[67]:
qc = QuantumCircuit(q, c)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[67]:
┌─┐ q124: ─|0>─┤M├ └╥┘ c0: 1/══════╩═ 0
[68]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[68]:
{'0': 1024}
[69]:
qc = QuantumCircuit(q, c)
qc.h(q)
qc.reset(q[0])
qc.measure(q, c)
qc.draw()
[69]:
┌───┐ ┌─┐ q124: ┤ H ├─|0>─┤M├ └───┘ └╥┘ c0: 1/═══════════╩═ 0
[70]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[70]:
{'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
[71]:
qc = QuantumCircuit(q, c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
qc.draw()
[71]:
┌───┐ ┌─┐ q124: ─┤ X ├─┤M├ └─╥─┘ └╥┘ ┌──╨──┐ ║ c0: 1/╡ 0x0 ╞═╩═ └─────┘ 0
Ici, le bit classique prend toujours la valeur 0 donc l’état du qubit est toujours inversé.
[72]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[72]:
{'1': 1024}
[73]:
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()
[73]:
┌───┐┌─┐ ┌───┐ ┌─┐ q124: ┤ H ├┤M├─┤ X ├─┤M├ └───┘└╥┘ └─╥─┘ └╥┘ ║ ┌──╨──┐ ║ c0: 1/══════╩═╡ 0x0 ╞═╩═ 0 └─────┘ 0
[74]:
job = backend.run(transpile(qc, backend))
job.result().get_counts(qc)
[74]:
{'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é:
[79]:
# 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()
[79]:
┌───────────────────────────────────────────────────────────────────┐ q233_0: ┤0 ├ │ │ q233_1: ┤1 Initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├ │ │ q233_2: ┤2 ├ └───────────────────────────────────────────────────────────────────┘
[80]:
backend = BasicAer.get_backend('statevector_simulator')
job = backend.run(transpile(qc, backend))
qc_state = job.result().get_statevector(qc)
qc_state
[80]:
array([1.52655666e-16+2.50000000e-01j, 3.53553391e-01+5.55111512e-17j,
2.50000000e-01+2.50000000e-01j, 0.00000000e+00+0.00000000e+00j,
0.00000000e+00+0.00000000e+00j, 3.53553391e-01+7.07106781e-01j,
2.50000000e-01-9.02056208e-17j, 0.00000000e+00+0.00000000e+00j])
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
La fidélité est égale à :math:` 1 si et seulement si les deux états sont égaux.
[81]:
state_fidelity(desired_vector,qc_state)
[81]:
1.0000000000000004
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
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):
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:
C’est pourquoi
U can be implemented as a « quantum multiplexor » gate, since it is a block diagonal matrix. In the quantum multiplexor formalism, a block diagonal matrix of size \(2^n \times 2^n\), and consisting of \(2^s\) blocks, is equivalent to a multiplexor with \(s\) select qubits and \(n-s\) data qubits. Depending on the state of the select qubits, the corresponding blocks are applied to the data qubits. A multiplexor of this kind can be implemented after recursive decomposition to primitive gates of cx, rz and ry.
[82]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.20.2 |
qiskit-aer | 0.10.4 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.19.1 |
qiskit | 0.36.2 |
qiskit-optimization | 0.3.2 |
qiskit-machine-learning | 0.4.0 |
System information | |
Python version | 3.10.4 |
Python compiler | MSC v.1916 64 bit (AMD64) |
Python build | main, Mar 30 2022 08:38:02 |
OS | Windows |
CPUs | 6 |
Memory (Gb) | 15.869640350341797 |
Sat May 28 10:03:32 2022 Eastern Daylight Time |
This code is a part of Qiskit
© Copyright IBM 2017, 2022.
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.
[ ]: