Skip to main content
IBM Quantum Platform

L'algorithme Deutsch-Jozsa

Pour ce module Qiskit en classe, les étudiants doivent disposer d'un environnement Python fonctionnel avec les paquets suivants installés :

  • qiskit v2.1.0 ou plus récent
  • qiskit-ibm-runtime v0.40.1 ou plus récent
  • qiskit-aer v0.17.0 ou plus récent
  • qiskit.visualization
  • numpy
  • pylatexenc

Pour configurer et installer les paquets ci-dessus, voir le guide d' installation de Qiskit. Afin d'exécuter des tâches sur de véritables ordinateurs quantiques, les étudiants devront créer un compte sur IBM Quantum® en suivant les étapes du guide Configurer votre compte IBM Cloud.

Ce module a été testé et a utilisé quatre secondes de temps QPU. Il s'agit uniquement d'une estimation. L'utilisation réelle peut varier.

# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'

Regardez la présentation du module par le Dr. Katie McCormick ci-dessous, ou cliquez ici pour la regarder sur YouTube.



Introduction

Au début des années 1980, les physiciens quantiques et les informaticiens avaient une vague idée que la mécanique quantique pouvait être exploitée pour effectuer des calculs bien plus puissants que ceux des ordinateurs classiques. Leur raisonnement est le suivant : il est difficile pour un ordinateur classique de simuler des systèmes quantiques, mais un ordinateur quantique devrait pouvoir le faire plus efficacement. Et si un ordinateur quantique peut simuler des systèmes quantiques plus efficacement, il peut peut-être accomplir d'autres tâches plus efficacement qu'un ordinateur classique.

La logique était bonne, mais les détails restaient à régler. Cela a commencé en 1985, lorsque David Deutsch a décrit le premier "ordinateur quantique universel" Dans ce même article, il a fourni le premier exemple de problème pour lequel un ordinateur quantique pourrait résoudre quelque chose plus efficacement qu'un ordinateur classique. Ce premier exemple de jouet est aujourd'hui connu sous le nom d'"algorithme de Deutsch" L'amélioration de l'algorithme de Deutsch était modeste, mais Deutsch a travaillé avec Richard Jozsa quelques années plus tard pour creuser davantage l'écart entre les ordinateurs classiques et les ordinateurs quantiques.

Ces algorithmes - celui de Deutsch et l'extension Deutsch-Jozsa - ne sont pas particulièrement utiles, mais ils restent très importants pour plusieurs raisons :

  1. Historiquement, ils ont été parmi les premiers algorithmes quantiques dont il a été démontré qu'ils surpassaient leurs homologues classiques. Les comprendre peut nous aider à comprendre comment la pensée de la communauté sur l'informatique quantique a évolué au fil du temps.
  2. Ils peuvent nous aider à comprendre certains aspects de la réponse à une question étonnamment subtile : Qu'est-ce qui donne à l'informatique quantique sa puissance? Les ordinateurs quantiques sont parfois comparés à des processeurs parallèles géants à échelle exponentielle. Mais ce n'est pas tout à fait exact. Si une partie de la réponse à cette question réside dans ce que l'on appelle le "parallélisme quantique", l'extraction d'un maximum d'informations en une seule fois est un art subtil. Les algorithmes de Deutsch et de Deutsch-Jozsa montrent comment cela est possible.

Dans ce module, nous découvrirons l'algorithme de Deutsch, l'algorithme de Deutsch-Jozsa et ce qu'ils nous apprennent sur la puissance de l'informatique quantique.


Le parallélisme quantique et ses limites

Une partie de la puissance de l'informatique quantique provient du "parallélisme quantique" qui est essentiellement la capacité d'effectuer des opérations sur plusieurs entrées en même temps, puisque les états d'entrée des qubits peuvent être dans une superposition de plusieurs états classiquement autorisés. CEPENDANT, bien qu'un circuit quantique puisse être capable d'évaluer plusieurs états d'entrée à la fois, il est impossible d'extraire toutes ces informations en une seule fois.

Pour comprendre ce que je veux dire, disons que nous avons un bit, xx et une fonction appliquée à ce bit, f(x)f(x). Il existe quatre fonctions binaires possibles qui transforment un bit unique en un autre bit unique :

xx
f1(x)f_1(x)
f2(x)f_2(x)
f3(x)f_3(x)
f4(x)f_4(x)
00011
10101

Nous aimerions savoir laquelle de ces fonctions (1-4) est notre f(x)f(x). Classiquement, nous devrions exécuter la fonction deux fois - une fois pour x=0x=0, une fois pour x=1x=1. Mais voyons si nous pouvons faire mieux avec un circuit quantique. Nous pouvons apprendre à connaître la fonction à l'aide de la porte suivante :

parallélisme quantique

Ici, la porte UfU_f calcule f(x)f(x), où xx est l'état du qubit 0, et l'applique au qubit 1. Ainsi, l'état résultant, xyf(x)|x\rangle|y\oplus f(x)\rangle, devient simplement xf(x)|x\rangle|f(x)\rangle lorsque y=0|y\rangle = |0\rangle. Ceci contient toutes les informations dont nous avons besoin pour connaître la fonction f(x)f(x) : le qubit 0 nous dit ce qu'est xx, et le qubit 1 nous dit ce qu'est f(x)f(x). Ainsi, si nous initialisons x=12(0+1)|x\rangle = \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle), l'état final des deux qubits sera : yx=12(f(0)0+f(1)1)|y\rangle|x\rangle = \frac{1}{\sqrt{2}}(|f(0)\rangle|0\rangle+|f(1)\rangle|1\rangle). Mais comment accéder à cette information?

2.1. Essayez-le sur Qiskit :

À l'aide de Qiskit, nous sélectionnons au hasard l'une des quatre fonctions possibles ci-dessus et nous exécutons le circuit. Votre tâche consiste alors à utiliser les mesures du circuit quantique pour apprendre la fonction en un minimum d'essais.

Dans cette première expérience et tout au long du module, nous utiliserons un cadre pour l'informatique quantique connu sous le nom de "modèles Qiskit", qui décompose les flux de travail en plusieurs étapes :

  • Etape 1 : Tracer un problème quantique à partir d'entrées classiques
  • Étape 2 : Optimisation du problème pour l'exécution quantique
  • Étape 3 : Exécution à l'aide des primitives « IBM Quantum »
  • Étape 4 : Post-traitement et analyse classique

Commençons par installer quelques paquets indispensables, notamment les primitives de l' IBM Quantum. Nous choisirons également l'ordinateur quantique le moins sollicité parmi ceux dont nous disposons.

Le code ci-dessous vous permet de sauvegarder vos données d'identification lors de la première utilisation. Veillez à supprimer ces informations du bloc-notes après l'avoir enregistré dans votre environnement, afin que vos informations d'identification ne soient pas accidentellement partagées lorsque vous partagez le bloc-notes. Voir Configurer votre compte IBM Cloud et Initialiser le service dans un environnement non fiable pour plus d'informations.

# Load IBM Quantum Compute Service
from qiskit_ibm_runtime import QiskitRuntimeService

# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

# Syntax for first saving your token.  Delete these lines after saving your credentials.

# QiskitRuntimeService.save_account(channel='ibm_quantum_platform',
# instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR_API_KEY>', overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum_platform')

# Load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
# backend = service.least_busy(operational=True, simulator=False, min_num_qubits = 127)
backend = service.backend("ibm_brisbane")
print(backend.name)


sampler = Sampler(mode=backend)

Output:

ibm_brisbane

La cellule ci-dessous vous permettra de basculer entre l'utilisation du simulateur ou du matériel réel tout au long du carnet. Nous vous recommandons de l'exécuter maintenant :

# Load the backend sampler
from qiskit.primitives import BackendSamplerV2

# Load the Aer simulator and generate a noise model based on the currently-selected backend.
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel

# Alternatively, load a fake backend with generic properties and define a simulator.


noise_model = NoiseModel.from_backend(backend)

# Define a simulator using Aer, and use it in Sampler.
backend_sim = AerSimulator(noise_model=noise_model)
sampler_sim = BackendSamplerV2(backend=backend_sim)

# You could also define a simulator-based sampler using a generic backend:
# backend_gen = GenericBackendV2(num_qubits=18)
# sampler_gen = BackendSamplerV2(backend=backend_gen)

Maintenant que nous avons chargé les paquets nécessaires, nous pouvons procéder à la mise en œuvre des modèles Qiskit. Dans l'étape de mise en correspondance ci-dessous, nous créons d'abord une fonction qui sélectionne parmi les quatre fonctions possibles qui transforment un bit unique en un autre bit unique.

# Step 1: Map

from qiskit import QuantumCircuit

qc = QuantumCircuit(2)


def twobit_function(case: int):
    """
    Generate a valid two-bit function as a `QuantumCircuit`.
    """
    if case not in [1, 2, 3, 4]:
        raise ValueError("`case` must be 1, 2, 3, or 4.")

    f = QuantumCircuit(2)
    if case in [2, 3]:
        f.cx(0, 1)
    if case in [3, 4]:
        f.x(1)
    return f


# first, convert oracle circuit (above) to a single gate for drawing purposes. otherwise, the
# circuit is too large to display

# you may edit the number inside "twobit_function()" to select among the four valid functions:
# blackbox = twobit_function(2).to_gate()

# blackbox.label = "$U_f$"

qc.h(0)
qc.barrier()
qc.compose(twobit_function(2), inplace=True)
qc.measure_all()


qc.draw("mpl")

Output:

Output of the previous code cell

Dans le circuit ci-dessus, la porte de Hadamard "H" fait passer le qubit 0, qui est initialement dans l'état 0|0\rangle, à l'état de superposition 12(0+1)\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle). Ensuite, UfU_f évalue la fonction f(x)f(x) et l'applique au qubit 1.

Ensuite, nous devons optimiser et transpiler le circuit pour l'exécuter sur l'ordinateur quantique :

# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)

qc_isa = pm.run(qc)

Enfin, nous exécutons notre circuit transpilé sur l'ordinateur quantique et visualisons nos résultats :

# Step 3: Run the job on a real quantum computer

job = sampler.run([qc_isa], shots=1)
# job = sampler_sim.run([qc_isa],shots=1) # uncomment this line to run on simulator instead
res = job.result()
counts = res[0].data.meas.get_counts()
# Step 4: Visualize and analyze results

## Analysis
from qiskit.visualization import plot_histogram

plot_histogram(counts)

Output:

Output of the previous code cell

L'histogramme ci-dessus représente nos résultats. Selon le nombre de tirs que vous avez choisi pour exécuter le circuit à l'étape 3 ci-dessus, vous pouvez voir une ou deux barres, représentant les états mesurés des deux qubits à chaque tir. Comme toujours avec Qiskit et dans ce carnet, nous utilisons la notation "little endian", ce qui signifie que les états des qubits 0 à n sont écrits dans l'ordre croissant de droite à gauche, de sorte que le qubit 0 est toujours le plus à droite.

Ainsi, comme le qubit 0 était dans un état de superposition, le circuit a évalué la fonction pour x=0x=0 et x=1x=1 en même temps — quelque chose que les ordinateurs classiques ne peuvent pas faire! Mais le problème se pose lorsque nous voulons en savoir plus sur la fonction f(x)f(x) - lorsque nous mesurons les qubits, leur état s'effondre. Si vous sélectionnez "shots = 1" pour n'exécuter le circuit qu'une seule fois, vous ne verrez qu'une seule barre dans l'histogramme ci-dessus et vos informations sur la fonction seront incomplètes.

Vérifiez votre compréhension

Combien de fois devons-nous exécuter l'algorithme ci-dessus pour apprendre la fonction f(x)f(x)? Cette méthode est-elle meilleure que la méthode classique? Préféreriez-vous un ordinateur classique ou quantique pour résoudre ce problème?

  • Étant donné que la mesure effondrera la superposition et ne renverra qu'une seule valeur, nous devons exécuter le circuit au moins deux fois pour renvoyer les deux sorties de la fonction f(0)f(0) et f(1)f(1). Dans le meilleur des cas, les résultats sont aussi bons que dans le cas classique, où nous calculons à la fois f(0)f(0) et f(1)f(1) dans les deux premières requêtes. Mais il est possible que nous devions l'exécuter plus de deux fois, car la mesure finale est probabiliste et peut renvoyer la même valeur f(x)f(x) les deux premières fois. Dans ce cas, je préférerais un ordinateur classique.

Ainsi, si le parallélisme quantique peut être puissant lorsqu'il est utilisé à bon escient, il n'est pas correct de dire qu'un ordinateur quantique fonctionne comme un processeur parallèle classique massif. L'acte de mesure effondre les états quantiques, de sorte que nous ne pouvons jamais accéder qu'à un seul résultat du calcul.


Algorithme de Deutsch

Bien que le parallélisme quantique ne nous donne pas à lui seul un avantage sur les ordinateurs classiques, nous pouvons l'associer à un autre phénomène quantique, l'interférence, pour obtenir une accélération. L'algorithme connu aujourd'hui sous le nom d'"algorithme de Deutsch" est le premier exemple d'algorithme permettant d'atteindre cet objectif.

Le problème

C'est là que le bât blesse :

Étant donné un bit d'entrée, x={0,1}x = \{0,1\}, et une fonction d'entrée f(x)={0,1}f(x) = \{0,1\}, déterminez si la fonction est équilibrée ou constante. En d'autres termes, si elle est équilibrée, la sortie de la fonction est 0 la moitié du temps et 1 l'autre moitié du temps. S'il est constant, la sortie de la fonction est soit toujours 0, soit toujours 1. Rappelons le tableau des quatre fonctions possibles prenant un bit unique pour un autre bit unique :

xx
f1(x)f_1(x)
f2(x)f_2(x)
f3(x)f_3(x)
f4(x)f_4(x)
00011
10101

La première et la dernière fonction, f1(x)f_1(x) et f4(x)f_4(x), sont constantes, tandis que les deux fonctions intermédiaires, f2(x)f_2(x) et f3(x)f_3(x), sont équilibrées.

L'algorithme

Deutsch a abordé ce problème par le biais du "modèle de requête" Dans le modèle d'interrogation, la fonction d'entrée ( fi(x)f_i(x) ci-dessus) est contenue dans une "boîte noire" - nous n'avons pas d'accès direct à son contenu, mais nous pouvons interroger la boîte noire et elle nous donnera la sortie de la fonction. On dit parfois qu'un "oracle" fournit ces informations. Voir la leçon 1 : Algorithmes de requête quantique du cours Fondamentaux de l'algorithmique quantique pour en savoir plus sur le modèle de requête.

Pour déterminer si un algorithme quantique est plus efficace qu'un algorithme classique dans le modèle d'interrogation, il suffit de comparer le nombre d'interrogations de la boîte noire dans chaque cas. Dans le cas classique, pour savoir si la fonction contenue dans la boîte noire est équilibrée ou constante, il faudrait interroger la boîte deux fois pour obtenir f(0)f(0) et f(1)f(1).

Dans l'algorithme quantique de Deutsch, il a trouvé un moyen d'obtenir l'information avec une seule requête! Il a apporté une modification au circuit de "parallélisme quantique" ci-dessus, afin de préparer un état de superposition sur les deux qubits, au lieu du seul qubit 0. Ensuite, les deux sorties de la fonction, f(0)f(0) et f(1)f(1), ont interféré pour renvoyer 0 si elles étaient toutes les deux 0 ou toutes les deux 1 (la fonction était constante), et ont renvoyé 1 si elles étaient différentes (la fonction était équilibrée). Deutsch pouvait ainsi faire la différence entre une fonction constante et une fonction équilibrée à l'aide d'une seule requête.

Voici un schéma de l'algorithme de Deutsch :

Schéma de l'algorithme de Deutsch

Pour comprendre le fonctionnement de cet algorithme, examinons les états quantiques des qubits aux trois points notés sur le diagramme ci-dessus. Essayez de trouver les états par vous-même avant de cliquer pour voir les réponses :

Vérifiez votre compréhension

Quel est l'état π1|\pi_1\rangle?

  • L'application d'une transformation de Hadamard transforme l'état 0|0\rangle en 12(0+1)\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) et l'état 1|1\rangle en 12(01)\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle). L'état complet devient donc π1=[012][0+12]|\pi_1\rangle = [\frac{|0\rangle-|1\rangle}{\sqrt{2}}][\frac{|0\rangle+|1\rangle}{\sqrt{2}}]

Quel est l'état π2|\pi_2\rangle?

  • Avant d'appliquer UfU_f, rappelez-vous ce qu'il fait. Il modifiera l'état du qubit 1 en fonction de l'état du qubit 0. Il est donc logique de factoriser l'état du qubit 0 : π1=12(01)0+12(01)1|\pi_1\rangle = \frac{1}{2} (|0\rangle-|1\rangle)|0\rangle+\frac{1}{2}(|0\rangle-|1\rangle)|1\rangle. Ensuite, si f(0)=f(1)f(0)=f(1), les deux termes se transformeront de la même manière et le signe relatif entre les deux termes restera positif, mais si f(0)f(1)f(0)\neq f(1), cela signifie que le deuxième terme prendra un signe négatif par rapport au premier terme, changeant l'état du qubit 0 de 12(0+1)\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) à 12(01)\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle). Donc :

    π2={±[012][0+12]iff(0)=f(1)±[012][012]iff(0)f(1)|\pi_2\rangle = \begin{cases} \pm[\frac{|0\rangle-|1\rangle}{\sqrt{2}}][\frac{|0\rangle+|1\rangle}{\sqrt{2}}] & \text{if} & f(0) = f(1) \\ \pm[\frac{|0\rangle-|1\rangle}{\sqrt{2}}][\frac{|0\rangle-|1\rangle}{\sqrt{2}}] &\text{if} & f(0) \neq f(1) \\ \end{cases}

Quel est l'état π3|\pi_3\rangle?

  • Maintenant, l'état du qubit 0 est soit 12(0+1)\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) soit 12(01)\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle), selon la fonction. L'application de la méthode de Hadamard permet d'obtenir 0|0\rangle ou 1|1\rangle, respectivement.

    π3={±[012]0iff(0)=f(1)±[012]1iff(0)f(1)|\pi_3\rangle = \begin{cases} \pm[\frac{|0\rangle-|1\rangle}{\sqrt{2}}]|0\rangle & \text{if} & f(0) = f(1) \\ \pm[\frac{|0\rangle-|1\rangle}{\sqrt{2}}]|1\rangle &\text{if} & f(0) \neq f(1) \\ \end{cases}

En examinant vos réponses aux questions ci-dessus, vous constaterez qu'il se passe quelque chose d'un peu surprenant. Bien que UfU_f ne fasse rien explicitement à l'état du qubit 0, parce qu'il modifie le qubit 1 en fonction de l'état du qubit 0, il peut arriver que cela provoque un déphasage dans le qubit 0. Ce phénomène est connu sous le nom de "phase-kickback" et est discuté plus en détail dans la leçon 1 : Algorithmes d'interrogation quantique du cours Fondamentaux de l'algorithmique quantique.

Maintenant que nous comprenons le fonctionnement de cet algorithme, mettons-le en œuvre avec Qiskit.

## Deutsch's algorithm:

## Step 1: Map the problem

# first, convert oracle circuit (above) to a single gate for drawing purposes.
# otherwise, the circuit is too large to display
blackbox = twobit_function(
    3
    # you may edit the number (1-4) inside "twobit_function()" to select among the four valid functions
).to_gate()
blackbox.label = "$U_f$"


qc_deutsch = QuantumCircuit(2, 1)

qc_deutsch.x(1)
qc_deutsch.h(range(2))

qc_deutsch.barrier()
qc_deutsch.compose(twobit_function(2), inplace=True)
qc_deutsch.barrier()

qc_deutsch.h(0)
qc_deutsch.measure(0, 0)

qc_deutsch.draw("mpl")

Output:

Output of the previous code cell
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)

qc_isa = pm.run(qc_deutsch)
# Step 3: Run the job on a real quantum computer

job = sampler.run([qc_isa], shots=1)
# job = sampler_sim.run([qc_isa],shots=1) # uncomment this line to run on simulator instead
res = job.result()
counts = res[0].data.c.get_counts()
# Step 4: Visualize and analyze results

## Analysis
print(counts)
if "1" in counts:
    print("balanced")
else:
    print("constant")

Output:

{'1': 1}
balanced

L'algorithme Deutsch-Jozsa

L'algorithme de Deutsch a constitué une première étape importante dans la démonstration de la manière dont un ordinateur quantique pourrait être plus efficace qu'un ordinateur classique, mais il ne s'agissait que d'une amélioration modeste : il ne nécessitait qu'une seule requête, contre deux dans le cas classique. En 1992, Deutsch et son collègue Richard Jozsa ont étendu l'algorithme original à deux qubits à un plus grand nombre de qubits. Le problème reste le même : déterminer si une fonction est équilibrée ou constante. Mais cette fois, la fonction passe de nn bits à un seul bit. Soit la fonction renvoie 0 et 1 un nombre égal de fois (elle est équilibrée ), soit la fonction renvoie toujours 1 ou toujours 0 (elle est constante ).

Voici un schéma de l'algorithme :

DJ_algo.png

Cet algorithme fonctionne de la même manière que l'algorithme de Deutsch : le rebond de phase permet de lire l'état du qubit 0 pour déterminer si la fonction est constante ou équilibrée. C'est un peu plus difficile à voir que dans le cas de l'algorithme de Deutsch à deux qubits, puisque les états incluront des sommes sur les qubits nn, et donc le calcul de ces états sera laissé comme un exercice optionnel pour vous à la fin du module. L'algorithme renvoie une chaîne de bits contenant tous les 0 si la fonction est constante, et une chaîne de bits contenant au moins un 1 si la fonction est équilibrée.

Pour voir comment l'algorithme fonctionne dans Qiskit, nous devons d'abord générer notre oracle : la fonction aléatoire qui est garantie comme étant soit constante, soit équilibrée. Le code ci-dessous génère une fonction équilibrée dans 50 % des cas et une fonction constante dans 50 % des cas. Ne vous inquiétez pas si vous ne suivez pas entièrement le code - il est compliqué et n'est pas nécessaire à notre compréhension de l'algorithme quantique.

from qiskit import QuantumCircuit
import numpy as np


def dj_function(num_qubits):
    """
    Create a random Deutsch-Jozsa function.
    """

    qc_dj = QuantumCircuit(num_qubits + 1)
    if np.random.randint(0, 2):
        # Flip output qubits with 50% chance
        qc_dj.x(num_qubits)
    if np.random.randint(0, 2):
        # return constant circuit with 50% chance.
        return qc_dj

    # If the "if" statement above was "TRUE" then we've returned the constant
    # function and the function is complete. If not, we proceed in creating our
    # balanced function. Everything below is to produce the balanced function:

    # select half of all possible states at random:
    on_states = np.random.choice(
        range(2**num_qubits),  # numbers to sample from
        2**num_qubits // 2,  # number of samples
        replace=False,  # makes sure states are only sampled once
    )

    def add_cx(qc_dj, bit_string):
        for qubit, bit in enumerate(reversed(bit_string)):
            if bit == "1":
                qc_dj.x(qubit)
        return qc_dj

    for state in on_states:
        # qc_dj.barrier()  # Barriers are added to help visualize how the functions are created.
        # They can safely be removed.
        qc_dj = add_cx(qc_dj, f"{state:0b}")
        qc_dj.mcx(list(range(num_qubits)), num_qubits)
        qc_dj = add_cx(qc_dj, f"{state:0b}")

    # qc_dj.barrier()

    return qc_dj


n = 3  # number of input qubits

oracle = dj_function(n)

display(oracle.draw("mpl"))

Output:

Output of the previous code cell

Il s'agit de la fonction oracle, qui est soit équilibrée, soit constante. Pouvez-vous voir en l'observant si la sortie du dernier qubit dépend des valeurs introduites pour les premiers nn qubits? Si la sortie du dernier qubit dépend des premiers nn qubits, pouvez-vous dire si cette sortie dépendante est équilibrée ou non?

Nous pouvons dire si la fonction est équilibrée ou constante en regardant le circuit ci-dessus, mais n'oubliez pas que pour ce problème, nous considérons cette fonction comme une "boîte noire" Nous ne pouvons pas jeter un coup d'œil dans la boîte pour voir le schéma du circuit. Au lieu de cela, nous devons interroger la boîte.

Pour interroger la boîte, nous utilisons l'algorithme de Deutsch-Jozsa et déterminons si la fonction est constante ou équilibrée :

blackbox = oracle.to_gate()
blackbox.label = "$U_f$"


qc_dj = QuantumCircuit(n + 1, n)
qc_dj.x(n)
qc_dj.h(range(n + 1))
qc_dj.barrier()
qc_dj.compose(blackbox, inplace=True)
qc_dj.barrier()
qc_dj.h(range(n))
qc_dj.measure(range(n), range(n))

qc_dj.decompose().decompose()


qc_dj.draw("mpl")

Output:

Output of the previous code cell
# Step 1: Map the problem

qc_dj = QuantumCircuit(n + 1, n)
qc_dj.x(n)
qc_dj.h(range(n + 1))
qc_dj.barrier()
qc_dj.compose(oracle, inplace=True)
qc_dj.barrier()
qc_dj.h(range(n))
qc_dj.measure(range(n), range(n))

qc_dj.decompose().decompose()


qc_dj.draw("mpl")

Output:

Output of the previous code cell
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)

qc_isa = pm.run(qc_dj)
# Step 3: Run the job on a real quantum computer

job = sampler.run([qc_isa], shots=1)
# job = sampler_sim.run([qc_isa],shots=1) # uncomment this line to run on simulator instead
res = job.result()
counts = res[0].data.c.get_counts()
# Step 4: Visualize and analyze results

## Analysis
print(counts)

if (
    "0" * n in counts
):  # The D-J algorithm returns all zeroes if the function was constant
    print("constant")
else:
    print("balanced")  # anything other than all zeroes means the function is balanced.

Output:

{'110': 1}
balanced

Ci-dessus, la première ligne de la sortie est la chaîne de bits des résultats des mesures. La deuxième ligne indique si la chaîne de bits implique que la fonction était équilibrée ou constante. Si la chaîne de bits contient tous les zéros, elle est constante; sinon, elle est équilibrée. Ainsi, en exécutant une seule fois le circuit quantique ci-dessus, nous pouvons déterminer si la fonction est constante ou équilibrée!

Vérifiez votre compréhension

Combien de requêtes faudrait-il à un ordinateur classique pour déterminer avec une certitude de 100 % si une fonction est constante ou équilibrée? Rappelez-vous que, classiquement, une requête unique ne vous permet d'appliquer la fonction qu'à une seule chaîne de bits.

  • Il existe 2n2^n chaînes de bits possibles à vérifier et, dans le pire des cas, vous devrez tester 2n/2+12^n/2+1 d'entre elles. Par exemple, si la fonction est constante et que vous continuez à mesurer "1" comme sortie de la fonction, vous ne pouvez pas être certain qu'elle est vraiment constante avant d'avoir vérifié plus de la moitié des résultats. Auparavant, il fallait être très malchanceux pour continuer à mesurer "1" sur une fonction équilibrée. C'est comme si l'on jouait à pile ou face et que l'on tombait à chaque fois sur pile. C'est peu probable, mais pas impossible.

Comment votre réponse ci-dessus changerait-elle si vous deviez simplement mesurer jusqu'à ce qu'un résultat (équilibré ou constant) soit plus probable que l'autre? Combien de requêtes faut-il dans ce cas?

  • Dans ce cas, il suffit de mesurer deux fois. Si les deux mesures sont différentes, vous savez que la fonction est équilibrée. Si les deux mesures sont identiques, il peut s'agir d'un équilibre ou d'une constance. La probabilité qu'il soit équilibré avec cet ensemble de mesures est : 122n/212n1\frac{1}{2}\frac{2^n /2 - 1}{2^n-1}. Cette probabilité est inférieure à 1/2, il est donc plus probable que la fonction soit constante dans ce cas.

Ainsi, l'algorithme de Deutsch-Jozsa a démontré une accélération exponentielle par rapport à un algorithme classique déterministe (qui renvoie la réponse avec une certitude de 100 %), mais aucune accélération significative par rapport à un algorithme probabiliste (qui renvoie un résultat susceptible d' être la bonne réponse).

Le problème de Bernstein-Vazirani

En 1997, Ethan Bernstein et Umesh Vazirani ont utilisé l'algorithme de Deutsch-Jozsa pour résoudre un problème plus spécifique et plus restreint que le problème de Deutsch-Jozsa. Plutôt que d'essayer simplement de faire la distinction entre deux classes différentes de fonctions, comme dans le cas D-J, Bernstein et Vazirani ont utilisé l'algorithme Deutsch-Jozsa pour apprendre une chaîne de caractères codée dans une fonction. Voici le problème :

La fonction f:{0,1}n{0,1}f:\{0,1\}^n \rightarrow \{0,1\} prend toujours une chaîne de nn bits et produit un seul bit. Mais maintenant, au lieu de promettre que la fonction est équilibrée ou constante, on nous promet que la fonction est le produit de points entre la chaîne d'entrée xx et une chaîne secrète nn -bit ss, modulo 2. (Ce produit de points modulo 2 est appelé "produit de points binaires") Le problème est de découvrir ce qu'est la chaîne secrète, nn -bit.

En d'autres termes, on nous donne une fonction de boîte noire f:0,1n0,1f: {0,1}^n \rightarrow {0,1} qui satisfait f(x)=sxf(x) = s \cdot x pour une certaine chaîne ss, et nous voulons apprendre la chaîne ss.

Voyons comment l'algorithme D-J résout ce problème :

  1. Tout d'abord, une porte de Hadamard est appliquée aux qubits d'entrée nn, et une porte NOT plus une porte de Hadamard sont appliquées au qubit de sortie, ce qui crée l'état :
Ψ=n+n1+n2...+0|\Psi\rangle = |-\rangle_{n} \otimes |+\rangle_{n-1} \otimes |+\rangle_{n-2} \otimes ... \otimes |+\rangle_0

L'état des qubits 1 à nn peut être écrit plus simplement comme une somme sur tous 2n2^n les états de base des qubits nn 00...00,00...01,000...11,...,111...11|00...00\rangle, |00...01\rangle, |000...11\rangle, ..., |111...11\rangle. Nous appelons l'ensemble de ces états de base Σn\Sigma^n. (Voir Fundamentals of Quantum Algorithms pour plus de détails)

Ψ=12nxΣnx|\Psi\rangle = |-\rangle \otimes \frac{1}{\sqrt{2^n}}\sum\limits_{x \in \Sigma^n}{|x\rangle}
  1. Ensuite, la porte UfU_f est appliquée aux qubits. Cette porte prend les n premiers qubits en entrée (qui sont maintenant dans une superposition égale de toutes les chaînes de n bits possibles) et applique la fonction f(x)=sxf(x)=s \cdot x au qubit de sortie, de sorte que ce qubit est maintenant dans l'état : f(x) |- \oplus f(x)\rangle. Grâce au mécanisme de rebond de phase, l'état de ce qubit reste inchangé, mais certains des termes de l'état du qubit d'entrée prennent un signe moins :
Ψ=12nxΣn(1)f(x)x|\Psi\rangle = |-\rangle \otimes \frac{1}{\sqrt{2^n}}\sum\limits_{x \in \Sigma^n}{(-1)^{f(x)}|x\rangle}
  1. La série suivante de Hadamards est appliquée aux qubits 0 à n1n-1. Dans ce cas, il peut s'avérer difficile de garder la trace des signes moins. Il est utile de savoir que l'application d'une couche de Hadamards à nn qubits dans un état de base standard x|x\rangle peut s'écrire comme suit :
Hnx=12nyΣn(1)xyyH^{\otimes n} |x\rangle = \frac{1}{\sqrt{2^n}}\sum\limits_{y \in \Sigma^n}{(-1)^{x \cdot y}|y\rangle}

L'état devient donc :

Ψ=12nxΣnyΣn(1)(sx)+(xy)y|\Psi\rangle = |-\rangle \otimes \frac{1}{2^n}\sum\limits_{x \in \Sigma^n}\sum\limits_{y \in \Sigma^n}{(-1)^{(s \cdot x) + (x \cdot y)}|y\rangle}
  1. L'étape suivante consiste à mesurer les premiers nn bits. Mais qu'allons-nous mesurer? Il s'avère que l'état ci-dessus se simplifie en : Ψ=s|\Psi\rangle = |-\rangle \otimes |s\rangle mais c'est loin d'être évident. Si vous souhaitez vous familiariser avec les mathématiques, consultez le cours Fundamentals of Quantum Algorithms de John Watrous. Le fait est que le mécanisme de rebond de phase conduit à ce que les qubits d'entrée soient dans l'état s|s\rangle. Ainsi, pour découvrir quelle était la chaîne secrète ss, il suffit de mesurer les qubits!

Vérifiez votre compréhension

Vérifier que l'état de l'étape 3 ci-dessus est bien l'état s|s\rangle pour le cas particulier de n=1n=1.

  • Lorsque vous écrivez explicitement les deux sommations, vous devriez obtenir un état à quatre termes (omettons pour cela l'état de sortie |-\rangle ) :

    Ψ=12[0+(1)s0+1+(1)(s+1)1]|\Psi\rangle = \frac{1}{2}[|0\rangle + (-1)^s |0\rangle + |1\rangle + (-1)^{(s+1)} |1\rangle]

    Si s=0s=0, les deux premiers termes s'additionnent de manière constructive et les deux derniers s'annulent, ce qui nous donne Ψ=0|\Psi\rangle = |0\rangle. Si s=1s=1, les deux derniers termes s'additionnent de manière constructive et les deux premiers s'annulent, ce qui nous donne Ψ=1|\Psi\rangle = |1\rangle. Donc, dans les deux cas, Ψ=s|\Psi\rangle = |s\rangle. Nous espérons que ce cas le plus simple vous donne une idée de la manière dont fonctionne le cas général avec nn qubits : tous les termes qui ne sont pas s|s\rangle interfèrent, laissant seulement l'état s|s\rangle.

Comment le même algorithme peut-il résoudre les problèmes de Bernstein-Vazirani et de Deutsch-Jozsa? Pour comprendre cela, pensez aux fonctions de Bernstein-Vazirani, qui sont de la forme f(x)=sxf(x) = s \cdot x. Ces fonctions sont-elles également des fonctions de Deutsch-Jozsa? Il s'agit de déterminer si les fonctions de cette forme satisfont à la promesse du problème de Deutsch-Jozsa, à savoir qu'elles sont soit constantes, soit équilibrées. Comment cela nous aide-t-il à comprendre comment un même algorithme résout deux problèmes différents?

  • Toute fonction de Bernstein-Vazirani de la forme f(x)=sxf(x) = s \cdot x satisfait également la promesse du problème de Deutsch-Jozsa : si s=00...00, alors la fonction est constante (elle renvoie toujours 0 pour chaque chaîne de caractères x). Si s est une autre chaîne, la fonction est équilibrée. Ainsi, l'application de l'algorithme de Deutsch-Jozsa à l'une de ces fonctions résout simultanément les deux problèmes! Il renvoie la chaîne, et si cette chaîne est 00...00, nous savons qu'elle est constante; s'il y a au moins un "1" dans la chaîne, nous savons qu'elle est équilibrée.

Nous pouvons également vérifier que cet algorithme résout avec succès le problème de Bernstein-Vazirani en le testant expérimentalement. Tout d'abord, nous créons la fonction B-V qui vit à l'intérieur de la boîte noire :

# Step 1: Map the problem


def bv_function(s):
    """
    Create a Bernstein-Vazirani function from a string of 1s and 0s.
    """
    qc = QuantumCircuit(len(s) + 1)
    for index, bit in enumerate(reversed(s)):
        if bit == "1":
            qc.cx(index, len(s))
    return qc


display(bv_function("1000").draw("mpl"))

Output:

Output of the previous code cell
string = "1000"  # secret string that we'll pretend we don't know or have access to
n = len(string)

qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.barrier()
# qc.compose(oracle, inplace = True)
qc.compose(bv_function(string), inplace=True)
qc.barrier()
qc.h(range(n))
qc.measure(range(n), range(n))

qc.draw("mpl")

Output:

Output of the previous code cell
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)

qc_isa = pm.run(qc)
# Step 3: Run the job on a real quantum computer

job = sampler.run([qc_isa], shots=1)
# job = sampler_sim.run([qc_isa],shots=1) # uncomment this line to run on simulator instead
res = job.result()
counts = res[0].data.c.get_counts()
# Step 4: Visualize and analyze results

## Analysis
print(counts)

Output:

{'0000': 1}

Ainsi, avec une seule requête, l'algorithme de Deutsch-Jozsa renverra la chaîne ss utilisée dans la fonction : f(x)=xsf(x)=x \cdot s lorsque nous l'appliquerons au problème de Bernstein-Vazirani. Avec un algorithme classique, il faudrait nn requêtes pour résoudre le même problème.


Conclusion

Nous espérons qu'en examinant ces exemples simples, nous vous avons donné une meilleure idée de la manière dont les ordinateurs quantiques sont capables d'exploiter la superposition, l'intrication et l'interférence pour atteindre leur puissance par rapport aux ordinateurs classiques.

L'algorithme de Deutsch-Jozsa revêt une importance historique considérable, car il a été le premier à démontrer une accélération par rapport à un algorithme classique, mais il ne s'agissait que d'une accélération polynomiale. L'algorithme Deutsch-Jozsa n'est que le début de l'histoire.

Après avoir utilisé l'algorithme pour résoudre leur problème, Bernstein et Vazirani s'en sont servis comme base pour un problème récursif plus compliqué, appelé problème récursif d'échantillonnage de Fourier. Leur solution offre une accélération super-polynomiale par rapport aux algorithmes classiques. Avant même Bernstein et Vazirani, Peter Shor avait déjà mis au point son célèbre algorithme qui permettait aux ordinateurs quantiques de factoriser de grands nombres exponentiellement plus vite que n'importe quel algorithme classique. Ces résultats, pris dans leur ensemble, ont montré la promesse excitante d'un futur ordinateur quantique et ont incité les physiciens et les ingénieurs à faire de cet avenir une réalité.


Questions

Les enseignants peuvent demander des versions de ces cahiers avec les corrigés et des conseils sur le placement dans les programmes d'études communs en répondant à cette enquête rapide sur la façon dont les cahiers sont utilisés.

Concepts essentiels

  • les algorithmes Deutsch et Deutsch-Jozsa utilisent le parallélisme quantique combiné à l'interférence pour trouver une réponse à un problème plus rapidement qu'un ordinateur classique.
  • le mécanisme de rebond de phase est un phénomène quantique contre-intuitif qui transfère des opérations sur un qubit à la phase d'un autre qubit. Les algorithmes de Deutsch et de Deutsch-Jozsa utilisent ce mécanisme.
  • L'algorithme de Deutsch-Jozsa offre une accélération polynomiale par rapport à n'importe quel algorithme classique déterministe.
  • L'algorithme de Deutsch-Jozsa peut être appliqué à un autre problème, appelé problème de Bernstein-Vazirani, qui consiste à trouver une chaîne cachée codée dans une fonction.

true/false

  1. T/F L'algorithme de Deutsch est un cas particulier de l'algorithme de Deutsch-Jozsa où l'entrée est un seul qubit.
  2. T/F Les algorithmes de Deutsch et de Deutsch-Jozsa utilisent la superposition et l'interférence quantiques pour atteindre leur efficacité.
  3. T/F L'algorithme de Deutsch-Jozsa nécessite plusieurs évaluations de fonctions pour déterminer si une fonction est constante ou équilibrée.
  4. T/F L'"algorithme de Bernstein-Vazirani" est en fait le même que l'algorithme de Deutsch-Jozsa, appliqué à un problème différent.
  5. T/F L'algorithme de Bernstein-Vazirani peut trouver plusieurs chaînes secrètes simultanément.

Réponse courte

  1. Combien de temps faudrait-il à un algorithme classique pour résoudre le problème Deutsch-Jozsa dans le pire des cas?

  2. Combien de temps faudrait-il à un algorithme classique pour résoudre le problème de Bernstein-Vazirani? Quel est le gain de vitesse offert par l'algorithme DJ dans ce cas?

  3. Décrire le mécanisme de rétroaction en phase et son fonctionnement pour résoudre les problèmes de Deutsch-Jozsa et de Bernstein-Vazirani.

Problème difficile

  1. L'algorithme de Deutsch-Jozsa : Rappelez-vous que vous aviez une question ci-dessus vous demandant de calculer les états intermédiaires des qubits π1\pi_1, et π2\pi_2 de l'algorithme de Deutsch. Faites de même pour les états intermédiaires n+1n+1 -qubit π1\pi_1, et π2\pi_2 de l'algorithme de Deutsch-Jozsa, pour le cas spécifique où n=2n=2. Ensuite, vérifiez que π3=x0...xn(1)f(x0...xn)x0...xn\pi_3 = |-\rangle \otimes \sum\limits_{x_0...x_n}(-1)^{f(x_0...x_n)}|x_0 ... x_n\rangle, à nouveau, pour le cas spécifique où n=2n=2.
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.