{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Deutsch–Jozsa Algorithm Tutorial (Part I)\n", "\n", "The **Deutsch–Jozsa algorithm** is one of the most famous algorithms in quantum computing. The problem it solves has little practical value, but the algorithm itself is one of the earliest examples of a quantum algorithm that is exponentially faster than any possible deterministic algorithm for the same problem. It is also relatively simple to explain and illustrates several very important concepts (such as quantum oracles). As such, Deutsch–Jozsa algorithm is part of almost every introductory course on quantum computing.\n", "\n", "This tutorial will:\n", "* introduce you to the problems solved by the Deutsch and Deutsch–Jozsa algorithms and walk you through the classical solution to them,\n", "* give you a brief introduction to quantum oracles (for a more detailed introduction, see the [Oracles tutorial](../Oracles/Oracles.ipynb)),\n", "* describe the idea behind the Deutsch and Deutsch–Jozsa algorithms and walk you through the math for them,\n", "* teach you how to implement the algorithms in the Q# programming language,\n", "* and finally help you to run your implementation of the algorithm on several quantum oracles to see for yourself how the algorithm works!\n", "\n", "Let's go!\n", "\n", "This tutorial consists of several notebooks:\n", "1. Part I covers the problem statement and the classical algorithm for solving it.\n", "2. [Part II](./DeutschJozsaAlgorithmTutorial_P2.ipynb) introduces single-qubit quantum oracles and the Deutsch algorithm for solving the problem for one-bit input functions.\n", "3. [Part III](./DeutschJozsaAlgorithmTutorial_P3.ipynb) introduces multi-qubit quantum oracles and the Deutsch-Jozsa algorithm for solving the general case of the problem.\n", "4. [Part IV](./AQ/DeutschJozsaAlgorithmTutorial_P4.ipynb) shows how to run the algorithms in the cloud using Azure Quantum." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Part I. Problem statement and classical algorithm\n", "\n", "## The problem\n", "\n", "You are given a classical function $f(x): \\{0, 1\\}^N \\to \\{0, 1\\}$. You are guaranteed that the function $f$ is\n", "* either *constant* (has the same value for all inputs) \n", "* or *balanced* (has value 0 for half of the inputs and 1 for the other half of the inputs). \n", "\n", "The task is to figure out whether the function is constant or balanced.\n", "\n", "For the single-bit functions ($N = 1$) the problem can be formulated in a simpler way: given a classical function $f(x): \\{0, 1\\} \\to \\{0, 1\\}$, figure out whether $f(0) = f(1)$. \n", "Indeed, $f(0) = f(1)$ is an equivalent definition of a constant single-bit function, and $f(0) \\neq f(1)$ - of a balanced single-bit function.\n", "\n", "> ## Examples\n", ">\n", "> * $f(x) \\equiv 0$ or $f(x) \\equiv 1$ are examples of constant functions (and they are actually the only constant functions in existence).\n", "> * For single-bit functions, $f(x) = x$ and $f(x) = 1 - x$ are the only existing balanced functions.\n", "> * $f(x) = x \\bmod 2$ (the least significant bit of $x$) or $f(x) = 1 \\text{ if the binary notation of }x \\text{ has odd number of 1s and 0 otherwise}$ are examples of multi-bit balanced functions. \n", "> Indeed, for both these functions you can check that for every possible input $x$ for which $f(x) = 0$ there exists an input $x^\\prime$ (equal to $x$ with the least significant bit flipped) such that $f(x^\\prime) = 1$, and vice versa, which means that the function is balanced.\n", ">\n", "> There exist more complicated examples of balanced functions, but we will not need to consider them for this tutorial.\n", "\n", "## Implementing classical functions in Q#\n", "\n", "Here is the implementation of these functions in Q#; it is pretty self-descriptive, since the functions are not only very simple but also classical.\n", "\n", "> **Note**: These are just code samples, you don't have to modify the code and they are not covered by tests." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// Function 1. f(x) = 0\n", "function Function_Zero (x : Int) : Int {\n", " return 0;\n", "}\n", "\n", "\n", "// Function 2. f(x) = 1\n", "function Function_One (x : Int) : Int {\n", " return 1;\n", "}\n", "\n", "\n", "// Function 3. f(x) = x mod 2 (least significant bit of x)\n", "function Function_Xmod2 (x : Int) : Int {\n", " return x % 2;\n", "}\n", "\n", "\n", "// Function 4. f(x) = 1 if the binary notation of x has odd number of 1s, and 0 otherwise\n", "function Function_OddNumberOfOnes (x : Int) : Int {\n", " mutable nOnes = 0;\n", " mutable xBits = x;\n", " while (xBits > 0) {\n", " if xBits % 2 > 0 {\n", " set nOnes += 1;\n", " }\n", " set xBits /= 2;\n", " }\n", " return nOnes % 2;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise 1: Implement a classical function in Q#\n", "\n", "Try to implement a similar classical function in Q#!\n", "\n", "**Inputs:** \n", "1. An integer $x$.\n", "2. The number of bits in the input $N$ ($1 \\le N \\le 5$, $0 \\le x \\le 2^N-1$).\n", "\n", "**Output:** Return $f(x) = \\text{the most significant bit of }x$.\n", "\n", "> Useful documentation: [Q# Bitwise Expressions](https://docs.microsoft.com/azure/quantum/user-guide/language/expressions/bitwiseexpressions)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%kata T1_ClassicalFunction \n", "\n", "function Function_MostSignificantBit (x : Int, N : Int) : Int {\n", " // ...\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Classical algorithm\n", "\n", "If we solve this problem classically, how many calls to the given function will we need? \n", "\n", "The first call will give us no information - regardless of whether it returns 0 or 1, the function could still be constant or balanced.\n", "In the best case scenario the second call will return a different value and we'll be able to conclude that the function is balanced in just $2$ calls. \n", "However, if we get the same value for the first two calls, we'll have to keep querying the function until either we get a different value or until we do $2^{N-1}+1$ queries that will return the same value - in this case we'll know for certain that the function will be constant.\n", "\n", "## Exercise 2: Implement the classical algorithm!\n", "\n", "Q# is a domain-specific language, so it is not designed to handle arbitrary classical computations. However, this classical algorithm is so simple that you can easily implement it in Q#. Try it!\n", "\n", "**Inputs:** \n", "1. The number of bits in the input $N$ ($1 \\le N \\le 5$).\n", "2. The \"black box\" function that evaluates $f(x)$ on any given input $x \\in [0, 2^N-1]$. \n", " You are guaranteed that the function implemented by the black box is either constant or balanced.\n", "\n", "**Goal:** Return `true` if the function is constant, or `false` if it is balanced.\n", "\n", "> Useful documentation: [Q# statements](https://docs.microsoft.com/azure/quantum/user-guide/language/statements/)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%kata T2_ClassicalAlgorithm \n", "\n", "operation IsFunctionConstant_Classical (N : Int, f : (Int -> Int)) : Bool {\n", " // ...\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Continue to [part II](./DeutschJozsaAlgorithmTutorial_P2.ipynb) to learn about single-qubit quantum oracles and Deutsch algorithm for solving the problem for one-bit input functions." ] } ], "metadata": { "kernelspec": { "display_name": "Q#", "language": "qsharp", "name": "iqsharp" }, "language_info": { "file_extension": ".qs", "mimetype": "text/x-qsharp", "name": "qsharp", "version": "0.14" } }, "nbformat": 4, "nbformat_minor": 2 }