{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "7e5d320e",
      "metadata": {},
      "source": [
        "---\n",
        "title: Deutsch's algorithm\n",
        "description: A free IBM course on quantum information and computation\n",
        "---\n",
        "\n",
        "# Deutsch's algorithm\n",
        "\n",
        "Deutsch's algorithm solves the parity problem for the special case that $n = 1.$\n",
        "In the context of quantum computing this problem is sometimes referred to as *Deutsch's problem*, and we'll follow that nomenclature in this lesson.\n",
        "\n",
        "To be precise, the input is represented by a function $f:\\Sigma \\rightarrow \\Sigma$ from one bit to one bit.\n",
        "There are four such functions:\n",
        "\n",
        "$$\n",
        "\\rule[-10mm]{0mm}{10mm}\n",
        "\\begin{array}{c|c}\n",
        "  a & f_1(a)\\\\\n",
        "  \\hline\n",
        "  0 & 0\\\\\n",
        "  1 & 0\n",
        "\\end{array}\n",
        "\\qquad\n",
        "\\begin{array}{c|c}\n",
        "  a & f_2(a)\\\\\n",
        "  \\hline\n",
        "  0 & 0\\\\\n",
        "  1 & 1\n",
        "\\end{array}\n",
        "\\qquad\n",
        "\\begin{array}{c|c}\n",
        "  a & f_3(a)\\\\\n",
        "  \\hline\n",
        "  0 & 1\\\\\n",
        "  1 & 0\n",
        "\\end{array}\n",
        "\\qquad\n",
        "\\begin{array}{c|c}\n",
        "  a & f_4(a)\\\\\n",
        "  \\hline\n",
        "  0 & 1\\\\\n",
        "  1 & 1\n",
        "\\end{array}\n",
        "$$\n",
        "\n",
        "The first and last of these functions are *constant* and the middle two are *balanced*, meaning that the two possible output values for the function occur the same number of times as we range over the inputs.\n",
        "Deutsch's problem is to determine which of these two categories the input function belongs to: constant or balanced.\n",
        "\n",
        "<Figure title=\"Deutsch's problem\">\n",
        "  Input:  a function $f:\\{0,1\\}\\rightarrow\\{0,1\\}$ \\\n",
        "  Output: $0$ if $f$ is constant, $1$ if $f$ is balanced\n",
        "</Figure>\n",
        "\n",
        "If we view the input function $f$ in Deutsch's problem as representing random access to a string, we're thinking about a two-bit string: $f(0)f(1).$\n",
        "\n",
        "$$\n",
        "\\begin{array}{cc}\n",
        "\\mathsf{function} & \\mathsf{string}\\\\\n",
        "\\hline\n",
        "f_1 & 00 \\\\\n",
        "f_2 & 01 \\\\\n",
        "f_3 & 10 \\\\\n",
        "f_4 & 11\n",
        "\\end{array}\n",
        "$$\n",
        "\n",
        "When viewed in this way, Deutsch's problem is to compute the parity (or, equivalently, the exclusive-OR) of the two bits.\n",
        "\n",
        "Every classical query algorithm that correctly solves this problem must query both bits: $f(0)$ and $f(1).$\n",
        "If we learn that $f(1) = 1,$ for instance, the answer could still be $0$ or $1,$ depending on whether $f(0) = 1$ or $f(0) = 0,$ respectively.\n",
        "Every other case is similar; knowing just one of two bits doesn't provide any information at all about their parity.\n",
        "So, the Boolean circuit described in the previous section is the best we can do in terms of the number of queries required to solve this problem.\n",
        "\n",
        "## Quantum circuit description\n",
        "\n",
        "Deutsch's algorithm solves Deutsch's problem using a single query, therefore providing a quantifiable advantage of quantum over classical computations.\n",
        "This may be a modest advantage — one query as opposed to two — but we have to start somewhere.\n",
        "Scientific advances sometimes have seemingly humble origins.\n",
        "\n",
        "Here is a quantum circuit that describes Deutsch's algorithm:\n",
        "\n",
        "![Deutsch's algorithm](https://quantum.cloud.ibm.com/learning/images/courses/fundamentals-of-quantum-algorithms/quantum-query-algorithms/Deutsch-circuit.svg)\n",
        "\n",
        "## Analysis\n",
        "\n",
        "To analyze Deutsch's algorithm, we will trace through the action of the circuit above and identify the states of the qubits at the times suggested by this figure:\n",
        "\n",
        "![States during Deutsch's algorithm](https://quantum.cloud.ibm.com/learning/images/courses/fundamentals-of-quantum-algorithms/quantum-query-algorithms/Deutsch-circuit-states.svg)\n",
        "\n",
        "The initial state is $\\vert 1\\rangle \\vert 0 \\rangle,$ and the two Hadamard operations on the left-hand side of the circuit transform this state to\n",
        "\n",
        "$$\n",
        "\\vert \\pi_1 \\rangle = \\vert - \\rangle \\vert + \\rangle\n",
        "= \\frac{1}{2} \\bigl( \\vert 0\\rangle - \\vert 1\\rangle \\bigr) \\vert 0\\rangle\n",
        "+ \\frac{1}{2} \\bigl( \\vert 0\\rangle - \\vert 1\\rangle \\bigr) \\vert 1\\rangle.\n",
        "$$\n",
        "\n",
        "(As always, we're following Qiskit's qubit ordering convention, which puts the top qubit to the right and the bottom qubit to the left.) It may feel unintuitive to write this product state partly distributed (leaving the states of qubit 1 factored out), but this will make our later expressions more compact.\n",
        "\n",
        "Next, the $U_f$ gate is performed.\n",
        "According to the definition of the $U_f$ gate, the value of the function $f$ for the classical state of the top/rightmost qubit is XORed onto the bottom/leftmost qubit, which transforms $\\vert \\pi_1\\rangle$ into the state\n",
        "\n",
        "$$\n",
        "\\vert \\pi_2 \\rangle\n",
        "= \\frac{1}{2} \\bigl( \\vert 0 \\oplus f(0) \\rangle - \\vert 1 \\oplus f(0) \\rangle \\bigr) \\vert 0 \\rangle\n",
        "+ \\frac{1}{2} \\bigl( \\vert 0 \\oplus f(1) \\rangle - \\vert 1 \\oplus f(1) \\rangle \\bigr) \\vert 1 \\rangle.\n",
        "$$\n",
        "\n",
        "We can simplify this expression by observing that the formula\n",
        "\n",
        "$$\n",
        "\\vert 0 \\oplus a\\rangle - \\vert 1 \\oplus a\\rangle = (-1)^a \\bigl( \\vert 0\\rangle - \\vert 1\\rangle \\bigr)\n",
        "$$\n",
        "\n",
        "works for both possible values $a\\in\\Sigma.$\n",
        "More explicitly, the two cases are as follows.\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\vert 0 \\oplus 0\\rangle - \\vert 1 \\oplus 0\\rangle\n",
        "& = \\vert 0 \\rangle - \\vert 1 \\rangle\n",
        "= (-1)^0 \\bigl( \\vert 0\\rangle - \\vert 1\\rangle \\bigr)\\\\\n",
        "\\vert 0 \\oplus 1\\rangle - \\vert 1 \\oplus 1\\rangle & = \\vert 1 \\rangle - \\vert 0\\rangle\n",
        "= (-1)^1 \\bigl( \\vert 0\\rangle - \\vert 1\\rangle \\bigr)\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Thus, we can alternatively express $\\vert\\pi_2\\rangle$ like this:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "  \\vert\\pi_2\\rangle\n",
        "  & = \\frac{1}{2} (-1)^{f(0)} \\bigl( \\vert 0 \\rangle - \\vert 1 \\rangle \\bigr) \\vert 0 \\rangle\n",
        "  + \\frac{1}{2} (-1)^{f(1)} \\bigl( \\vert 0 \\rangle - \\vert 1 \\rangle \\bigr) \\vert 1 \\rangle \\\\\n",
        "  & = \\vert - \\rangle \\biggl( \\frac{(-1)^{f(0)} \\vert 0\\rangle + (-1)^{f(1)} \\vert 1\\rangle}{\\sqrt{2}}\\biggr).\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Something interesting just happened!\n",
        "Although the action of the $U_f$ gate on standard basis states leaves the top/rightmost qubit alone and XORs the function value onto the bottom/leftmost qubit, here we see that the state of the top/rightmost qubit has changed (in general) while the state of the bottom/leftmost qubit remains the same — specifically being in the $\\vert - \\rangle$ state before and after the $U_f$ gate is performed.\n",
        "This phenomenon is known as the *phase kickback*, and we will have more to say about it shortly.\n",
        "\n",
        "With one final simplification, which is to pull the factor of $(-1)^{f(0)}$ outside of the sum, we obtain this expression of the state $\\vert\\pi_2\\rangle$:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "  \\vert\\pi_2\\rangle\n",
        "  & = (-1)^{f(0)} \\vert - \\rangle\n",
        "      \\biggl( \\frac{\\vert 0\\rangle + (-1)^{f(0) \\oplus f(1)} \\vert 1\\rangle}{\\sqrt{2}}\\biggr) \\\\\n",
        "  & = \\begin{cases}\n",
        "        (-1)^{f(0)} \\vert - \\rangle \\vert + \\rangle & \\text{if $f(0) \\oplus f(1) = 0$}\\\\[1mm]\n",
        "        (-1)^{f(0)} \\vert - \\rangle \\vert - \\rangle & \\text{if $f(0) \\oplus f(1) = 1$}.\n",
        "      \\end{cases}\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Notice that in this expression, we have $f(0) \\oplus f(1)$ in the exponent of $-1$ as opposed to $f(1) - f(0),$ which is what we might expect from a purely algebraic viewpoint, but we obtain the same result either way.\n",
        "This is because the value $(-1)^k$ for any integer $k$ depends only on whether $k$ is even or odd.\n",
        "\n",
        "Applying the final Hadamard gate to the top qubit leaves us with the state\n",
        "\n",
        "$$\n",
        "\\vert \\pi_3 \\rangle =\n",
        "\\begin{cases}\n",
        "  (-1)^{f(0)} \\vert - \\rangle \\vert 0 \\rangle & \\text{if $f(0) \\oplus f(1) = 0$}\\\\[1mm]\n",
        "  (-1)^{f(0)} \\vert - \\rangle \\vert 1 \\rangle & \\text{if $f(0) \\oplus f(1) = 1$},\n",
        "\\end{cases}\n",
        "$$\n",
        "\n",
        "which leads to the correct outcome with probability $1$ when the right/topmost qubit is measured.\n",
        "\n",
        "## Further remarks on the phase kickback\n",
        "\n",
        "Before moving on, let's look at the analysis above from a slightly different angle that may shed some light on the phase kickback phenomenon.\n",
        "\n",
        "First, notice that the following formula works for all choices of bits $b,c\\in\\Sigma.$\n",
        "\n",
        "$$\n",
        "\\vert b \\oplus c\\rangle = X^c \\vert b \\rangle\n",
        "$$\n",
        "\n",
        "This can be verified by checking it for the two possible values $c = 0$ and $c = 1$:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "\\vert b \\oplus 0 \\rangle & = \\vert b\\rangle = \\mathbb{I} \\vert b \\rangle = X^0 \\vert b \\rangle\\\\\n",
        "\\vert b \\oplus 1 \\rangle & = \\vert \\neg b\\rangle = X \\vert b \\rangle = X^1 \\vert b \\rangle.\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "Using this formula, we see that\n",
        "\n",
        "$$\n",
        "U_f \\bigl(\\vert b\\rangle \\vert a \\rangle\\bigr)\n",
        "= \\vert b \\oplus f(a) \\rangle \\vert a \\rangle\n",
        "= \\bigl(X^{f(a)}\\vert b \\rangle\\bigr) \\vert a \\rangle\n",
        "$$\n",
        "\n",
        "for every choice of bits $a,b\\in\\Sigma.$\n",
        "Because this formula is true for $b=0$ and $b=1,$ we see by linearity that\n",
        "\n",
        "$$\n",
        "U_f \\bigl( \\vert \\psi \\rangle \\vert a \\rangle \\bigr) = \\bigl(X^{f(a)}\\vert \\psi \\rangle\\bigr) \\vert a \\rangle\n",
        "$$\n",
        "\n",
        "for all qubit state vectors $\\vert \\psi\\rangle,$ and therefore\n",
        "\n",
        "$$\n",
        "U_f \\bigl( \\vert - \\rangle \\vert a \\rangle \\bigr) = \\bigl(X^{f(a)} \\vert - \\rangle \\bigr) \\vert a \\rangle\n",
        "= (-1)^{f(a)} \\vert - \\rangle \\vert a \\rangle.\n",
        "$$\n",
        "\n",
        "The key that makes this work is that $X\\vert - \\rangle = - \\vert - \\rangle.$\n",
        "In mathematical terms, the vector $\\vert - \\rangle$ is an *eigenvector* of the matrix $X$ having *eigenvalue* $-1.$\n",
        "\n",
        "We'll discuss eigenvectors and eigenvalues in greater detail in the upcoming lesson on *Phase estimation and factoring,* where the phase kickback phenomenon is generalized to other unitary operations.\n",
        "\n",
        "Keeping in mind that scalars float freely through tensor products, we find an alternative way of reasoning how the operation $U_f$ transforms $\\vert \\pi_1\\rangle$ into $\\vert \\pi_2\\rangle$ in the analysis above:\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        "  \\vert \\pi_2 \\rangle\n",
        "  & = U_f \\bigl( \\vert - \\rangle \\vert + \\rangle \\bigr)\\\\\n",
        "  & = \\frac{1}{\\sqrt{2}} U_f \\bigl(\\vert - \\rangle \\vert 0\\rangle \\bigr)\n",
        "    + \\frac{1}{\\sqrt{2}} U_f \\bigl(\\vert - \\rangle \\vert 1\\rangle \\bigr)\\\\\n",
        "  & = \\vert - \\rangle \\biggl( \\frac{(-1)^{f(0)} \\vert 0\\rangle + (-1)^{f(1)} \\vert 1\\rangle}{\\sqrt{2}}\\biggr).\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d0cff57e",
      "metadata": {},
      "source": [
        "## Implementation in Qiskit\n",
        "\n",
        "Now let's see how we can implement Deutsch's algorithm in Qiskit. We'll start with a version check and then perform the imports required solely for this implementation.\n",
        "For the implementations of other algorithms that follows, we'll perform the required imports separately for the sake of greater modularity.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "baa9bc2c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2.1.1\n"
          ]
        }
      ],
      "source": [
        "from qiskit import __version__\n",
        "\n",
        "print(__version__)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "0a706449",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit_aer import AerSimulator"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0af39df9",
      "metadata": {},
      "source": [
        "First we'll define a quantum circuit that implements a query gate for one of the four functions $f_1,$ $f_2,$ $f_3,$ or $f_4$ from one bit to one bit described previously. As we already mentioned, the implementation of query gates is not really a part of Deutsch's algorithm itself;\n",
        "here we're essentially just showing one way to prepare the input, in the form of a circuit implementation of a query gate.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "ba5a708f",
      "metadata": {},
      "outputs": [],
      "source": [
        "def deutsch_function(case: int):\n",
        "    # This function generates a quantum circuit for one of the 4 functions\n",
        "    # from one bit to one bit\n",
        "\n",
        "    if case not in [1, 2, 3, 4]:\n",
        "        raise ValueError(\"`case` must be 1, 2, 3, or 4.\")\n",
        "\n",
        "    f = QuantumCircuit(2)\n",
        "    if case in [2, 3]:\n",
        "        f.cx(0, 1)\n",
        "    if case in [3, 4]:\n",
        "        f.x(1)\n",
        "    return f"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46eccf09",
      "metadata": {},
      "source": [
        "We can see what each circuit looks like using the `draw` method. Here's the circuit for the function $f_3.$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "787e3c81",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/fundamentals-of-quantum-algorithms/quantum-query-algorithms/deutsch-algorithm/extracted-outputs/787e3c81-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "display(deutsch_function(3).draw(output=\"mpl\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b6fa5b7b",
      "metadata": {},
      "source": [
        "Next we will create the actual quantum circuit for Deutsch's algorithm, substituting the query gate with a quantum circuit implementation given as an argument. Shortly we'll plug in one of the four circuits defined by the function `deutsch_function` we defined earlier.\n",
        "Barriers are included to show the visual separation between the query gate implementation and the rest of the circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "379aac92",
      "metadata": {},
      "outputs": [],
      "source": [
        "def compile_circuit(function: QuantumCircuit):\n",
        "    # Compiles a circuit for use in Deutsch's algorithm.\n",
        "\n",
        "    n = function.num_qubits - 1\n",
        "    qc = QuantumCircuit(n + 1, n)\n",
        "\n",
        "    qc.x(n)\n",
        "    qc.h(range(n + 1))\n",
        "\n",
        "    qc.barrier()\n",
        "    qc.compose(function, inplace=True)\n",
        "    qc.barrier()\n",
        "\n",
        "    qc.h(range(n))\n",
        "    qc.measure(range(n), range(n))\n",
        "\n",
        "    return qc"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e5112614",
      "metadata": {},
      "source": [
        "Again we can see what the circuit looks like using the `draw` method.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "27b41067",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/fundamentals-of-quantum-algorithms/quantum-query-algorithms/deutsch-algorithm/extracted-outputs/27b41067-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "display(compile_circuit(deutsch_function(3)).draw(output=\"mpl\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d24d14eb",
      "metadata": {},
      "source": [
        "Finally, we'll create a function that runs the circuit previously defined one time and outputs the appropriate result: \"constant\" or \"balanced.\"\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "5b31e311",
      "metadata": {},
      "outputs": [],
      "source": [
        "def deutsch_algorithm(function: QuantumCircuit):\n",
        "    # Determine if a one-bit function is constant or balanced.\n",
        "\n",
        "    qc = compile_circuit(function)\n",
        "\n",
        "    result = AerSimulator().run(qc, shots=1, memory=True).result()\n",
        "    measurements = result.get_memory()\n",
        "    if measurements[0] == \"0\":\n",
        "        return \"constant\"\n",
        "    return \"balanced\""
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3d0868ed",
      "metadata": {},
      "source": [
        "We can now run Deutsch's algorithm on any one of the four functions defined above.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "33b8355d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'balanced'"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "f = deutsch_function(3)\n",
        "display(deutsch_algorithm(f))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}