From 2c818d30b41ff847f3732b434df6c6e9454066e6 Mon Sep 17 00:00:00 2001 From: Artem Astapchuk Date: Sat, 21 Sep 2019 20:52:51 -0700 Subject: [PATCH 1/8] Added Ripple Carry Adder kata --- RippleCarryAdder/README.md | 21 + RippleCarryAdder/ReferenceImplementation.qs | 290 ++++++++ RippleCarryAdder/RippleCarryAdder.csproj | 29 + RippleCarryAdder/RippleCarryAdder.ipynb | 748 ++++++++++++++++++++ RippleCarryAdder/RippleCarryAdder.sln | 31 + RippleCarryAdder/Tasks.qs | 343 +++++++++ RippleCarryAdder/TestSuiteRunner.cs | 43 ++ RippleCarryAdder/Tests.qs | 391 ++++++++++ 8 files changed, 1896 insertions(+) create mode 100644 RippleCarryAdder/README.md create mode 100644 RippleCarryAdder/ReferenceImplementation.qs create mode 100644 RippleCarryAdder/RippleCarryAdder.csproj create mode 100644 RippleCarryAdder/RippleCarryAdder.ipynb create mode 100644 RippleCarryAdder/RippleCarryAdder.sln create mode 100644 RippleCarryAdder/Tasks.qs create mode 100644 RippleCarryAdder/TestSuiteRunner.cs create mode 100644 RippleCarryAdder/Tests.qs diff --git a/RippleCarryAdder/README.md b/RippleCarryAdder/README.md new file mode 100644 index 00000000000..7e668b41e22 --- /dev/null +++ b/RippleCarryAdder/README.md @@ -0,0 +1,21 @@ +# Welcome! + +This kata explores ripple-carry addition on a quantum computer. + +The simplest quantum adder closely mirrors its classical counterpart, +using the same basic components and the same algorithm. +A more complex version covered in part 3 of the kata uses a somewhat different algorithm +to reduce the number of ancillary qubits needed. + +#### Theory + +* [Classical binary adder on Wikipedia](https://en.wikipedia.org/wiki/Adder_(electronics)). +* [Paper on quantum binary addition](https://arxiv.org/pdf/quant-ph/0008033.pdf) by Thomas G. Draper - part 2 explains how to adapt the classical adder to a quantum environment. +* [Paper on improved ripple carry addition](https://arxiv.org/pdf/quant-ph/0410184.pdf) by Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, and David Petrie Moulton - explains the principle behind the adder in part 3 of the kata. + +#### Q# + +It is recommended to complete the [BasicGates kata](./../BasicGates/) before this one to get familiar with the basic gates used in quantum computing. +The list of basic gates available in Q# can be found at [Microsoft.Quantum.Intrinsic](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic). + +For the syntax of flow control statements in Q#, see [the Q# documentation](https://docs.microsoft.com/quantum/language/statements#control-flow). \ No newline at end of file diff --git a/RippleCarryAdder/ReferenceImplementation.qs b/RippleCarryAdder/ReferenceImplementation.qs new file mode 100644 index 00000000000..5802bc173a9 --- /dev/null +++ b/RippleCarryAdder/ReferenceImplementation.qs @@ -0,0 +1,290 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////////// +// This file contains reference solutions to all tasks. +// The tasks themselves can be found in Tasks.qs file. +// We recommend that you try to solve the tasks yourself first, +// but feel free to look up the solution if you get stuck. +////////////////////////////////////////////////////////////////////// + +namespace Quantum.Kata.RippleCarryAdder { + + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + + + ////////////////////////////////////////////////////////////////// + // Part I. Simple adder outputting to empty Qubits + ////////////////////////////////////////////////////////////////// + + // Task 1.1. Summation of two bits + operation LowestBitSum_Reference (a : Qubit, b : Qubit, sum : Qubit) : Unit is Adj { + CNOT(a, sum); + CNOT(b, sum); + } + + + // Task 1.2. Carry of two bits + operation LowestBitCarry_Reference (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { + CCNOT(a, b, carry); + } + + + // Task 1.3. One bit adder + operation OneBitAdder_Reference (a : Qubit, b : Qubit, sum : Qubit, carry : Qubit) : Unit is Adj { + LowestBitSum_Reference(a, b, sum); + LowestBitCarry_Reference(a, b, carry); + } + + + // Task 1.4. Summation of 3 bits + operation HighBitSum_Reference (a : Qubit, b : Qubit, carryin : Qubit, sum : Qubit) : Unit is Adj { + CNOT(a, sum); + CNOT(b, sum); + CNOT(carryin, sum); + } + + + // Task 1.5. Carry of 3 bits + operation HighBitCarry_Reference (a : Qubit, b : Qubit, carryin : Qubit, carryout : Qubit) : Unit is Adj { + CCNOT(a, b, carryout); + CCNOT(a, carryin, carryout); + CCNOT(b, carryin, carryout); + } + + // Task 1.6. Two bit adder + operation TwoBitAdder_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { + using (internalCarry = Qubit()) { + LowestBitSum_Reference(a[0], b[0], sum[0]); + LowestBitCarry_Reference(a[0], b[0], internalCarry); + + HighBitSum_Reference(a[1], b[1], internalCarry, sum[1]); + HighBitCarry_Reference(a[1], b[1], internalCarry, carry); + + // Clean up ancillary qubit + Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarry); + } + } + + + // Task 1.7. N-bit adder + operation ArbitraryAdder_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { + let N = Length(a); + if (N == 1) { + LowestBitSum_Reference(a[0], b[0], sum[0]); + LowestBitCarry_Reference(a[0], b[0], carry); + } + else { + using (internalCarries = Qubit[N-1]) { + LowestBitSum_Reference(a[0], b[0], sum[0]); + LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); + + for (i in 1 .. N-2) { + HighBitSum_Reference(a[i], b[i], internalCarries[i-1], sum[i]); + HighBitCarry_Reference(a[i], b[i], internalCarries[i-1], internalCarries[i]); + } + + HighBitSum_Reference(a[N-1], b[N-1], internalCarries[N-2], sum[N-1]); + HighBitCarry_Reference(a[N-1], b[N-1], internalCarries[N-2], carry); + + // Clean up the ancillary qubits + for (i in N-2 .. -1 .. 1) { + Adjoint HighBitCarry_Reference(a[i], b[i], internalCarries[i-1], internalCarries[i]); + } + Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); + } + } + } + + + // A slightly simpler solution - more uniform, but slightly slower, and requires one extra qubit + operation ArbitraryAdder_Simplified_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { + let N = Length(a); + using (internalCarries = Qubit[N]) { + let carries = internalCarries + [carry]; + for (i in 0 .. N-1) { + HighBitSum_Reference(a[i], b[i], carries[i], sum[i]); + HighBitCarry_Reference(a[i], b[i], carries[i], carries[i+1]); + } + + // Clean up the ancilla + for (i in N-2 .. -1 .. 0) { + Adjoint HighBitCarry_Reference(a[i], b[i], carries[i], carries[i+1]); + } + } + } + + + // The challenge solution - the sum qubits are used to store the carry bits, and the sum is calculated as they get cleaned up + operation ArbitraryAdder_Challenge_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { + let N = Length(a); + + // Calculate carry bits + LowestBitCarry_Reference(a[0], b[0], sum[0]); + for (i in 1 .. N-1) { + HighBitCarry_Reference(a[i], b[i], sum[i - 1], sum[i]); + } + CNOT(sum[N-1], carry); + + // Clean sum qubits and compute sum + for (i in N-1 .. -1 .. 1) { + Adjoint HighBitCarry_Reference(a[i], b[i], sum[i - 1], sum[i]); + HighBitSum_Reference(a[i], b[i], sum[i - 1], sum[i]); + } + Adjoint LowestBitCarry_Reference(a[0], b[0], sum[0]); + LowestBitSum_Reference(a[0], b[0], sum[0]); + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Simple in-place adder + ////////////////////////////////////////////////////////////////// + + // Task 2.1. In-place summation of two bits + operation LowestBitSumInPlace_Reference (a : Qubit, b : Qubit) : Unit is Adj { + CNOT(a, b); + } + + + // Task 2.2. In-place one bit adder + operation OneBitAdderInPlace_Reference (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { + LowestBitCarry_Reference(a, b, carry); + LowestBitSumInPlace_Reference(a, b); + } + + + // Task 2.3. In-place summation of three bits + operation HighBitSumInPlace_Reference (a : Qubit, b : Qubit, carryin : Qubit) : Unit is Adj { + CNOT(a, b); + CNOT(carryin, b); + } + + + // Task 2.4. In-place two bit adder + operation TwoBitAdderInPlace_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + using (internalCarry = Qubit()) { + // Set up the carry bits + LowestBitCarry_Reference(a[0], b[0], internalCarry); + HighBitCarry_Reference(a[1], b[1], internalCarry, carry); + + // Calculate sums and clean up the ancilla + HighBitSumInPlace_Reference(a[1], b[1], internalCarry); + Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarry); + LowestBitSumInPlace_Reference(a[0], b[0]); + } + } + + + // Task 2.5. In-place N-bit adder + operation ArbitraryAdderInPlace_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + let N = Length(a); + + using (internalCarries = Qubit[N]) { + // Set up the carry bits + LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); + for (i in 1 .. N-1) { + HighBitCarry_Reference(a[i], b[i], internalCarries[i - 1], internalCarries[i]); + } + CNOT(internalCarries[N-1], carry); + + // Clean up carry bits and compute sum + for (i in N-1 .. -1 .. 1) { + Adjoint HighBitCarry_Reference(a[i], b[i], internalCarries[i - 1], internalCarries[i]); + HighBitSumInPlace_Reference(a[i], b[i], internalCarries[i - 1]); + } + Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); + LowestBitSumInPlace_Reference(a[0], b[0]); + } + } + + + ////////////////////////////////////////////////////////////////// + // Part III*. Improved in-place adder + ////////////////////////////////////////////////////////////////// + + // Task 3.1. Majority gate + operation Majority_Reference (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { + CNOT(a, b); + CNOT(a, c); + CCNOT(b, c, a); + } + + + // Task 3.2. UnMajority and Add gate + operation UnMajorityAdd_Reference (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { + CCNOT(b, c, a); + CNOT(a, c); + CNOT(c, b); + } + + + // Task 3.3. One bit majority-UMA adder + operation OneBitMajUmaAdder_Reference (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { + using (tempCarry = Qubit()) { + Majority_Reference(a, b, tempCarry); + CNOT(a, carry); + UnMajorityAdd_Reference(a, b, tempCarry); + } + } + + + // Task 3.4. Two bit majority-UMA adder + operation TwoBitMajUmaAdder_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + using (tempCarry = Qubit()) { + // We only need the extra qubit so we have 3 to pass to the majority gate for the lowest bits + Majority_Reference(a[0], b[0], tempCarry); + Majority_Reference(a[1], b[1], a[0]); + + // Save last carry bit + CNOT(a[1], carry); + + // Restore inputs/ancilla and compute sum + UnMajorityAdd_Reference(a[1], b[1], a[0]); + UnMajorityAdd_Reference(a[0], b[0], tempCarry); + } + } + + + // Task 3.5. N-bit majority-UMA adder + operation ArbitraryMajUmaAdder_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + let N = Length(a); + + using (tempCarry = Qubit()) { + let carries = [tempCarry] + a; + + // Compute carry bits + for (i in 0 .. N-1) { + Majority_Reference(a[i], b[i], carries[i]); + } + + // Save last carry bit + CNOT(carries[N], carry); + + // Restore inputs and ancilla, compute sum + for (i in N-1 .. -1 .. 0) { + UnMajorityAdd_Reference(a[i], b[i], carries[i]); + } + } + } + + ////////////////////////////////////////////////////////////////// + // Part IV*. In-place subtractor + ////////////////////////////////////////////////////////////////// + + // Task 4.1. N-bit subtractor + operation Subtractor_Reference (a : Qubit[], b : Qubit[], borrow : Qubit) : Unit is Adj { + // transform b into 2ᴺ - 1 - b + ApplyToEachA(X, b); + + // compute (2ᴺ - 1 - b) + a = 2ᴺ - 1 - (b - a) using existing adder + // if this produced a carry, then (2ᴺ - 1 - (b - a)) > 2ᴺ - 1, so (b - a) < 0, and we need a borrow + // this means we can use the carry qubit from the addition as the borrow qubit + ArbitraryMajUmaAdder_Reference(a, b, borrow); + + // transform 2ᴺ - 1 - (b - a) into b - a + ApplyToEachA(X, b); + } +} diff --git a/RippleCarryAdder/RippleCarryAdder.csproj b/RippleCarryAdder/RippleCarryAdder.csproj new file mode 100644 index 00000000000..b16bd488f4f --- /dev/null +++ b/RippleCarryAdder/RippleCarryAdder.csproj @@ -0,0 +1,29 @@ + + + netcoreapp2.1 + x64 + false + Quantum.Kata.RippleCarryAdder + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/RippleCarryAdder/RippleCarryAdder.ipynb b/RippleCarryAdder/RippleCarryAdder.ipynb new file mode 100644 index 00000000000..5854bb402af --- /dev/null +++ b/RippleCarryAdder/RippleCarryAdder.ipynb @@ -0,0 +1,748 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Ripple Carry Adder Kata\n", + "\n", + "The **Ripple Carry Adder** quantum kata is a series of exercises designed\n", + "to get you familiar with ripple carry addition on a quantum computer,\n", + "walking you through the steps to build two different ripple-carry adders.\n", + "It covers the following topics:\n", + "* Adapting a classical adder to a quantum environment\n", + "* Modifying the adder to re-use qubits\n", + "* An alternate, simplified quantum adder\n", + "* A simple subtractor\n", + "\n", + "It is recommended to complete the [BasicGates kata](./../BasicGates/BasicGates.ipynb) before this one to get familiar with the basic gates used in quantum computing. The list of basic gates available in Q# can be found at [Microsoft.Quantum.Intrinsic](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic). For the syntax of flow control statements in Q#, see [the Q# documentation](https://docs.microsoft.com/quantum/language/statements#control-flow).\n", + "\n", + "Each task is wrapped in one operation preceded by the description of the task.\n", + "Your goal is to fill in the blank (marked with // ... comments)\n", + "with some Q# code that solves the task. To verify your answer, run the cell using Ctrl/⌘+Enter.\n", + "\n", + "Within each section, tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To begin, first prepare this notebook for execution (if you skip the first step, you'll get \"Syntax does not match any known patterns\" error when you try to execute Q# code in the next cells; if you skip the second step, you'll get \"Invalid kata name\" error):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%package Microsoft.Quantum.Katas::0.9.1908.2906" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.\n", + ">
\n", + "> How to install the right IQ# version\n", + "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.1.2.3, the installation steps are as follows:\n", + ">\n", + "> 1. Stop the kernel.\n", + "> 2. Uninstall the existing version of IQ#:\n", + "> dotnet tool uninstall microsoft.quantum.iqsharp -g\n", + "> 3. Install the matching version:\n", + "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.1.2.3\n", + "> 4. Reinstall the kernel:\n", + "> dotnet iqsharp install\n", + "> 5. Restart the Notebook.\n", + ">
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%workspace reload" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part I. Simple Adder Outputting to Empty Qubits\n", + "\n", + "\n", + "### Theory\n", + "\n", + "* [Classical binary adder on Wikipedia](https://en.wikipedia.org/wiki/Adder_(electronics)).\n", + "* [Paper on quantum binary addition](https://arxiv.org/pdf/quant-ph/0008033.pdf) by Thomas G. Draper - part 2 explains how to adapt the classical adder to a quantum environment." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.1. Summation of two bits\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " \n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " \n", + " 3. qubit `sum` in state $|0\\rangle$\n", + "\n", + "**Goal:** Transform the `sum` qubit into the lowest bit of the binary sum of $\\phi$ and $\\psi$.\n", + "\n", + "* $|0\\rangle + |0\\rangle \\to |0\\rangle$\n", + "* $|0\\rangle + |1\\rangle \\to |1\\rangle$\n", + "* $|1\\rangle + |0\\rangle \\to |1\\rangle$\n", + "* $|1\\rangle + |1\\rangle \\to |0\\rangle$\n", + "\n", + "Any superposition should map appropriately. For example:\n", + "\n", + "$|+\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle) \\\\\n", + "|-\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle - |1\\rangle) \\\\\n", + "|+\\rangle \\otimes |-\\rangle \\otimes |0\\rangle \\to \\frac{1}{2}(|000\\rangle + |101\\rangle - |011\\rangle - |110\\rangle)$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T11_LowestBitSum_Test\n", + "\n", + "operation LowestBitSum (a : Qubit, b : Qubit, sum : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.2. Carry of two bits\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goal:** Set the `carry` qubit to $|1\\rangle$ if the binary sum of $\\phi$ and $\\psi$ produces a carry.\n", + "\n", + "* $|0\\rangle$ and $|0\\rangle \\to |0\\rangle$\n", + "* $|0\\rangle$ and $|1\\rangle \\to |0\\rangle$\n", + "* $|1\\rangle$ and $|0\\rangle \\to |0\\rangle$\n", + "* $|1\\rangle$ and $|1\\rangle \\to |1\\rangle$\n", + "\n", + "Any superposition should map appropriately. For example:\n", + "\n", + "$|+\\rangle \\otimes |-\\rangle \\otimes |0\\rangle \\to \\frac{1}{2}(|000\\rangle + |100\\rangle - |010\\rangle - |111\\rangle)$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T12_LowestBitCarry_Test\n", + "\n", + "operation LowestBitCarry (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.3. One bit adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. two qubits `sum` and `carry` in state $|0\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform the `sum` qubit into the lowest bit of the binary sum of $\\phi$ and $\\psi$.\n", + "* Transform the `carry` qubit into the carry bit produced by that sum." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T13_OneBitAdder_Test\n", + "\n", + "operation OneBitAdder (a : Qubit, b : Qubit, sum : Qubit, carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.4. Summation of 3 bits\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carryin` in an arbitrary state $|\\omega\\rangle$\n", + " 4. qubit `carryout` in state $|0\\rangle$\n", + "\n", + "**Goal:** Transform the `sum` qubit into the lowest bit of the binary sum of $\\phi$ and $\\psi$ and $\\omega$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T14_HighBitSum_Test\n", + "\n", + "operation HighBitSum (a : Qubit, b : Qubit, carryin : Qubit, sum : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.5. Carry of 3 bits\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carryin` in an arbitrary state $|\\omega\\rangle$\n", + " 4. qubit `carryout` in state $|0\\rangle$\n", + "\n", + "**Goal:** Transform the `carryout` qubit into the carry bit produced by the sum of $\\phi$ and $\\psi$ and $\\omega$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T15_HighBitCarry_Test\n", + "\n", + "operation HighBitCarry (a : Qubit, b : Qubit, carryin : Qubit, carryout : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.6. Two bit adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. two qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. two qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. two qubit register `sum` in state $|00\\rangle$\n", + " 4. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform the `sum` register into the binary sum (little-endian) of $\\phi$ and $\\psi$.\n", + "* Transform the `carry` qubit into the carry bit produced by that sum.\n", + "\n", + "> All registers in this kata are in **little-endian** order.\n", + "> This means that they have the least significant bit first, followed by the next least significant, and so on.\n", + ">\n", + "> In this exercise, for example, $1$ would be represented as $|10\\rangle$, and $2$ as $|01\\rangle$.\n", + ">\n", + "> The sum of $|10\\rangle$ and $|11\\rangle$ would be $|001\\rangle$, with the last qubit being the carry qubit.\n", + "\n", + "
\n", + "
\n", + " Need a hint? Click here\n", + " Don't forget that you can allocate extra qubits.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T16_TwoBitAdder_Test\n", + "\n", + "operation TwoBitAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.7. N-bit adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. $N$ qubit register `sum` in state $|0...0\\rangle$\n", + " 4. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform the `sum` register into the binary sum (little-engian) of $\\phi$ and $\\psi$.\n", + "* Transform the `carry` qubit into the carry bit produced by that sum.\n", + "\n", + "**Challenge:**\n", + "\n", + "Can you do this without requesting extra qubits?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T17_ArbitraryAdder_Test\n", + "\n", + "operation ArbitraryAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part II. Simple In-place Adder\n", + "\n", + "The adder from the previous section requires empty qubits to accept the result.\n", + "This section adapts the previous adder to instead mutate the inputs.\n", + "That is, one of the inputs is re-used as the output." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 2.1. In-place summation of two bits\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform qubit `b` into the lowest bit of the sum of $\\phi$ and $\\psi$.\n", + "* Leave qubit `a` unchanged.\n", + "\n", + "> Would we be able to re-use one of the inputs to store the carry output as well? Why or why not?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T21_LowestBitSumInPlace_Test\n", + "\n", + "operation LowestBitSumInPlace (a : Qubit, b : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 2.2. In-place one bit adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform the `carry` qubit into the carry bit from the addition of $\\phi$ and $\\psi$.\n", + "* Transform qubit `b` into the lowest bit of $\\phi + \\psi$.\n", + "* Leave qubit `a` unchanged.\n", + "\n", + "
\n", + "
\n", + " Need a hint? Click here\n", + " Think very carefully about the order in which you apply the operations.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T22_OneBitAdderInPlace_Test\n", + "\n", + "operation OneBitAdderInPlace (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 2.3. In-place summation of three bits\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carryin` in an arbitrary state $|\\omega\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform qubit `b` into the lowest bit from the sum $\\phi + \\psi + \\omega$.\n", + "* Leave qubits `a` and `carryin` unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T23_HighBitSumInPlace_Test\n", + "\n", + "operation HighBitSumInPlace (a : Qubit, b : Qubit, carryin : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 2.4. In-place two bit adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. two qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. two qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform register `b` into the state $|\\phi + \\psi\\rangle$.\n", + "* Transform the `carry` qubit into the carry bit from the addition.\n", + "* Leave register `a` unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T24_TwoBitAdderInPlace_Test\n", + "\n", + "operation TwoBitAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 2.5. In-place N-bit adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goals:**\n", + "\n", + "* Transform register `b` into the state $|\\phi + \\psi\\rangle$.\n", + "* Transform the `carry` qubit into the carry bit from the addition.\n", + "* Leave register `a` unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T25_ArbitraryAdderInPlace_Test\n", + "\n", + "operation ArbitraryAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part III*. Improved In-place Adder\n", + "\n", + "The in-place adder doesn't require quite as many qubits for the inputs and outputs, but it still requires an array of extra (\"ancillary\") qubits to perform the calculation.\n", + "\n", + "A relatively recent algorithm allows you to perform the same calculation using only one additional qubit.\n", + "\n", + "### Theory\n", + "\n", + "* [Paper on improved ripple carry addition](https://arxiv.org/pdf/quant-ph/0410184.pdf) by Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, and David Petrie Moulton." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 3.1. Majority gate\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `c` in an arbitrary state $|\\omega\\rangle$\n", + "\n", + "**Goal:** Construct the \"in-place majority\" gate:\n", + "\n", + "* Transform qubit `a` into the carry bit from the sum $\\phi + \\psi + \\omega$.\n", + "* Transform qubit `b` into $|\\phi + \\psi\\rangle$.\n", + "* Transform qubit `c` into $|\\phi + \\omega\\rangle$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T31_Majority_Test\n", + "\n", + "operation Majority (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 3.2. UnMajority and Add gate\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. qubit `a` storing the carry bit from the sum $\\phi + \\psi + \\omega$\n", + " 2. qubit `b` in state $|\\phi + \\psi\\rangle$\n", + " 3. qubit `c` in state $|\\phi + \\omega\\rangle$\n", + "\n", + "**Goal:** Construct the \"un-majority and add\", or \"UMA\" gate:\n", + "\n", + "* Restore qubit `a` into state $|\\phi\\rangle$.\n", + "* Transform qubit `b` into state $|\\phi + \\psi + \\omega\\rangle$.\n", + "* Restore qubit `c` into state $|\\omega\\rangle$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T32_UnMajorityAdd_Test\n", + "\n", + "operation UnMajorityAdd (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 3.3. One bit Majority-UMA adder\n", + "\n", + "**Inputs:**\n", + "\n", + "1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", + "2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", + "3. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goal:** Construct a one bit binary adder from task 2.2 using Majority and UMA gates.\n", + "\n", + "
\n", + "
\n", + " Need a hint? Click here\n", + " Allocate an extra qubit to pass as qubit c. It's less efficient here, but it will be helpful for the next tasks.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T33_OneBitMajUmaAdder_Test\n", + "\n", + "operation OneBitMajUmaAdder (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 3.4. Two bit Majority-UMA adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. two qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. two qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goal:** Construct a two bit binary adder from task 2.4 using Majority and UMA gates.\n", + "\n", + "
\n", + "
\n", + " Need a hint? Click here\n", + " Think very carefully about which qubits you need to pass to the two gates.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T34_TwoBitMajUmaAdder_Test\n", + "\n", + "operation TwoBitMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 3.5. N-bit Majority-UMA adder\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `carry` in state $|0\\rangle$\n", + "\n", + "**Goal:** Construct an N-bit binary adder from task 2.5 using only one ancillary qubit." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T35_ArbitraryMajUmaAdder_Test\n", + "\n", + "operation ArbitraryMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part IV*. In-place Subtractor\n", + "\n", + "Subtracting a number is the same operation as adding a negative number.\n", + "As such, it's pretty easy to adapt the adder we just built to act as a subtractor." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 4.1. N-bit Subtractor\n", + "\n", + "**Inputs:**\n", + "\n", + " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", + " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", + " 3. qubit `borrow` in state $|0\\rangle$\n", + "\n", + "**Goal:** Construct an N-bit binary subtractor.\n", + "\n", + "* Transform register `b` into the state $|\\psi - \\phi\\rangle$.\n", + "* Set qubit `borrow` to $|1\\rangle$ if that subtraction requires a borrow.\n", + "* Leave register `a` unchanged.\n", + "\n", + "
\n", + "
\n", + " Need a hint? Click here\n", + " Use the adder you already built. Experiment with inverting registers before and after the addition.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T41_Subtractor_Test\n", + "\n", + "operation Subtractor (a : Qubit[], b : Qubit[], borrow : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Q#", + "language": "qsharp", + "name": "iqsharp" + }, + "language_info": { + "file_extension": ".qs", + "mimetype": "text/x-qsharp", + "name": "qsharp", + "version": "0.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/RippleCarryAdder/RippleCarryAdder.sln b/RippleCarryAdder/RippleCarryAdder.sln new file mode 100644 index 00000000000..36c559a2247 --- /dev/null +++ b/RippleCarryAdder/RippleCarryAdder.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29102.190 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RippleCarryAdder", "RippleCarryAdder.csproj", "{6CE3F3C4-4681-41C8-BB14-964B21BA287E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "..\utilities\Common\Common.csproj", "{146E58EC-CED9-4904-A4CE-2F7B70BD05BE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Release|Any CPU.Build.0 = Release|Any CPU + {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {41D1C147-2E2C-4C7C-B37D-13E80C93A0D5} + EndGlobalSection +EndGlobal diff --git a/RippleCarryAdder/Tasks.qs b/RippleCarryAdder/Tasks.qs new file mode 100644 index 00000000000..96b074586bb --- /dev/null +++ b/RippleCarryAdder/Tasks.qs @@ -0,0 +1,343 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Quantum.Kata.RippleCarryAdder { + + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + + + ////////////////////////////////////////////////////////////////// + // Welcome! + ////////////////////////////////////////////////////////////////// + + // The "Ripple Carry Adder" quantum kata is a series of exercises designed + // to get you familiar with ripple carry addition on a quantum computer, + // walking you through the steps to build two different adders. + // It covers the following topics: + // - Adapting a classical adder to a quantum environment + // - Modifying the adder to re-use input qubits + // - An alternate, simplified quantum adder + // - A simple subtractor + + // Each task is wrapped in one operation preceded by the description of the task. + // Each task (except tasks in which you have to write a test) has a unit test associated with it, + // which initially fails. Your goal is to fill in the blank (marked with // ... comment) + // with some Q# code to make the failing test pass. + + // Within each section, tasks are given in approximate order of increasing difficulty; + // harder ones are marked with asterisks. + + + ////////////////////////////////////////////////////////////////// + // Part I. Simple adder outputting to empty Qubits + ////////////////////////////////////////////////////////////////// + + // This section adapts the classical binary adder to a quantum computer. + // It starts with simple sum and carry gates, and works up to an N-bit full adder. + + // Task 1.1. Summation of two bits + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "sum" in state |0⟩ + // Goal: transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ + // |0⟩ + |0⟩ → |0⟩ + // |0⟩ + |1⟩ → |1⟩ + // |1⟩ + |0⟩ → |1⟩ + // |1⟩ + |1⟩ → |0⟩ + // any superposition should map appropriately + // for example: + // |+⟩ = (|0⟩ + |1⟩) / sqrt(2) + // |-⟩ = (|0⟩ - |1⟩) / sqrt(2) + // |+⟩ ⨂ |-⟩ ⨂ |0⟩ → (|000⟩ + |101⟩ - |011⟩ - |110⟩) / 2 + operation LowestBitSum (a : Qubit, b : Qubit, sum : Qubit) : Unit is Adj { + // ... + + } + + + // Task 1.2. Carry of two bits + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "carry" in state |0⟩ + // Goal: set the "carry" qubit to |1⟩ if the binary sum of φ and ψ produces a carry + // |0⟩ and |0⟩ → |0⟩ + // |0⟩ and |1⟩ → |0⟩ + // |1⟩ and |0⟩ → |0⟩ + // |1⟩ and |1⟩ → |1⟩ + // any superposition should map appropriately + // for example: + // |+⟩ ⨂ |-⟩ ⨂ |0⟩ → (|000⟩ + |100⟩ - |010⟩ - |111⟩) / 2 + operation LowestBitCarry (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { + // ... + + } + + + // Task 1.3. One bit adder + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) two qubits "sum" and "carry" in state |0⟩ + // Goals: + // 1) transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ + // 2) transform the "carry" qubit into the carry bit produced by that sum + operation OneBitAdder (a : Qubit, b : Qubit, sum : Qubit, carry : Qubit) : Unit is Adj { + // ... + + } + + + // Task 1.4. Summation of 3 bits + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "carryin" in an arbitrary state |ω⟩ + // 4) qubit "sum" in state |0⟩ + // Goal: transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ and ω + operation HighBitSum (a : Qubit, b : Qubit, carryin : Qubit, sum : Qubit) : Unit is Adj { + // ... + + } + + + // Task 1.5. Carry of 3 bits + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "carryin" in an arbitrary state |ω⟩ + // 4) qubit "carryout" in state |0⟩ + // Goal: transform the "carryout" qubit into the carry bit produced by the sum of φ and ψ and ω + operation HighBitCarry (a : Qubit, b : Qubit, carryin : Qubit, carryout : Qubit) : Unit is Adj { + // ... + + } + + + // Task 1.6. Two bit adder + // Inputs: + // 1) two qubit register "a" in an arbitrary state |φ⟩ + // 2) two qubit register "b" in an arbitrary state |ψ⟩ + // 3) two qubit register "sum" in state |00⟩ + // 4) qubit "carry" in state |0⟩ + // Goals: + // 1) transform the "sum" register into the binary sum of φ and ψ + // 2) transform the "carry" qubit into the carry bit produced by that sum + // Note: All qubit registers in this kata are in little-endian order. + // This means the least significant bit comes first, then the next least significant, and so on. + // In this exercise, for example, 1 would be represented as |10⟩, while 2 would be represented as |01⟩. + // The sum of |10⟩ and |11⟩ would be |001⟩, with the last qubit being the carry qubit. + operation TwoBitAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { + // Hint: don't forget that you can request extra qubits + + // ... + + } + + + // Task 1.7. N-bit adder + // Inputs: + // 1) N qubit register "a" in an arbitrary state |φ⟩ + // 2) N qubit register "b" in an arbitrary state |ψ⟩ + // 3) N qubit register "sum" in state |0...0⟩ + // 4) qubit "carry" in state |0⟩ + // Goals: + // 1) transform the "sum" register into the binary sum of φ and ψ + // 2) transform the "carry" qubit into the carry bit produced by that sum + // Challenge: can you do this without requesting extra qubits? + operation ArbitraryAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { + // ... + + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Simple in-place adder + ////////////////////////////////////////////////////////////////// + + // The adder from the previous section requires empty qubits to accept the result. + // This section adapts the previous adder to mutate the inputs. + // That is, one of the numerical inputs is re-used for the output. + + // Task 2.1. In-place summation of two bits + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // Goal: transform qubit "b" into the lowest bit of the sum of φ and ψ + // leave qubit "a" unchanged + // Something to think about: can we re-use one of the input bits for the carry as well? + operation LowestBitSumInPlace (a : Qubit, b : Qubit) : Unit is Adj { + // ... + + } + + + // Task 2.2. In-place one bit adder + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "carry" in state |0⟩ + // Goals: + // 1) transform the "carry" qubit into the carry bit from the addition of φ and ψ + // 2) transform qubit "b" into the lowest bit of φ + ψ + // leave qubit "a" unchanged + operation OneBitAdderInPlace (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { + // Hint: think carefully about the order of operations + + // ... + + } + + + // Task 2.3. In-place summation of three bits + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "carryin" in an arbitrary state |ω⟩ + // Goal: transform qubit "b" into the lowest bit from the addition of φ and ψ and ω + // leave qubits "a" and "carryin" unchanged + operation HighBitSumInPlace (a : Qubit, b : Qubit, carryin : Qubit) : Unit is Adj { + // ... + + } + + + // Task 2.4. In-place two bit adder + // Inputs: + // 1) two qubit register "a" in an arbitrary state |φ⟩ + // 2) two qubit register "b" in an arbitrary state |ψ⟩ + // 3) qubit "carry" in state |0⟩ + // Goals: + // 1) transform register "b" into the state |φ + ψ⟩ + // 2) transform the "carry" qubit into the carry bit from the addition + // leave register "a" unchanged + operation TwoBitAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + // ... + + } + + + // Task 2.5. In-place N-bit adder + // Inputs: + // 1) N qubit register "a" in an arbitrary state |φ⟩ + // 2) N qubit register "b" in an arbitrary state |ψ⟩ + // 3) qubit "carry" in state |0⟩ + // Goals: + // 1) transform register "b" into the state |φ + ψ⟩ + // 2) transform the "carry" qubit into the carry bit from the addition + // leave register "a" unchanged + operation ArbitraryAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + // ... + + } + + + ////////////////////////////////////////////////////////////////// + // Part III*. Improved in-place adder + ////////////////////////////////////////////////////////////////// + + // The in-place adder doesn't require quite as many qubits for the inputs and outputs, + // but it still requires an array of extra ("ancillary") qubits to perform the calculation. + // A relatively recent algorithm allows you to perform the same calculation + // using only one additional qubit. + + // Task 3.1. Majority gate + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "c" in an arbitrary state |ω⟩ + // Goal: construct the "in-place majority" gate + // 1) transform qubit "a" into the carry bit from the addition of φ and ψ and ω + // 2) transform qubit "b" into |φ + ψ⟩ + // 3) transform qubit "c" into |φ + ω⟩ + operation Majority (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { + // ... + + } + + + // Task 3.2. UnMajority and Add gate + // Inputs: + // 1) qubit "a" storing the carry bit from the sum φ + ψ + ω + // 2) qubit "b" in state |φ + ψ⟩ + // 3) qubit "c" in state |φ + ω⟩ + // Goal: construct the "un-majority and add", or "UMA" gate + // 1) restore qubit "a" into state |φ⟩ + // 2) transform qubit "b" into state |φ + ψ + ω⟩ + // 3) restore qubit "c" into state |ω⟩ + operation UnMajorityAdd (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { + // ... + + } + + + // Task 3.3. One bit majority-UMA adder + // Inputs: + // 1) qubit "a" in an arbitrary state |φ⟩ + // 2) qubit "b" in an arbitrary state |ψ⟩ + // 3) qubit "carry" in state |0⟩ + // Goal: construct a one bit binary adder from task 2.2 using Majority and UMA gates + operation OneBitMajUmaAdder (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { + // Hint: Allocate an extra qubit to pass as qubit "c" for the two gates. + // It's less efficient here, but it will help in the next tasks. + + // ... + + } + + + // Task 3.4. Two bit majority-UMA adder + // Inputs: + // 1) two qubit register "a" in an arbitrary state |φ⟩ + // 2) two qubit register "b" in an arbitrary state |ψ⟩ + // 3) qubit "carry" in state |0⟩ + // Goal: construct a two bit binary adder from task 2.4 using Majority and UMA gates + operation TwoBitMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + // Hint: think carefully about which qubits you need to pass to the two gates + + // ... + + } + + + // Task 3.5. N-bit majority-UMA adder + // Inputs: + // 1) N qubit register "a" in an arbitrary state |φ⟩ + // 2) N qubit register "b" in an arbitrary state |ψ⟩ + // 3) qubit "carry" in state |0⟩ + // Goal: construct an N-bit binary adder from task 2.5 using only one extra qubit + operation ArbitraryMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { + // ... + + } + + + ////////////////////////////////////////////////////////////////// + // Part IV*. In-place subtractor + ////////////////////////////////////////////////////////////////// + + // Subtracting a number is the same operation as adding a negative number. + // As such, the binary adder we just built can be easily adapted to act as a subtractor instead. + + // Task 4.1. N-bit subtractor + // Inputs: + // 1) N qubit register "a" in an arbitrary state |φ⟩ + // 2) N qubit register "b" in an arbitrary state |ψ⟩ + // 3) qubit "borrow" in state |0⟩ + // Goal: construct a binary subtractor + // 1) transform register "b" into the state |ψ - φ⟩ + // 2) set the "borrow" qubit to |1⟩ if that subtraction required a borrow + // leave register "a" unchanged + operation Subtractor (a : Qubit[], b : Qubit[], borrow : Qubit) : Unit is Adj { + // Hint: use the adder you already built + // experiment with inverting the registers before and after the addition + + // ... + + } +} \ No newline at end of file diff --git a/RippleCarryAdder/TestSuiteRunner.cs b/RippleCarryAdder/TestSuiteRunner.cs new file mode 100644 index 00000000000..7bb81f07946 --- /dev/null +++ b/RippleCarryAdder/TestSuiteRunner.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////////// +// This file contains parts of the testing harness. +// You should not modify anything in this file. +// The tasks themselves can be found in Tasks.qs file. +////////////////////////////////////////////////////////////////////// + +using Microsoft.Quantum.Simulation.XUnit; +using Xunit.Abstractions; +using System.Diagnostics; + +using Microsoft.Quantum.Katas; + +namespace Quantum.Kata.RippleCarryAdder +{ + public class TestSuiteRunner + { + private readonly ITestOutputHelper output; + + public TestSuiteRunner(ITestOutputHelper output) + { + this.output = output; + } + + /// + /// This driver will run all Q# tests (operations named "...Test") + /// that belong to namespace Quantum.Kata.RippleCarryAdder. + /// + [OperationDriver(TestNamespace = "Quantum.Kata.RippleCarryAdder")] + public void TestTarget(TestOperation op) + { + using (var sim = new CounterSimulator()) + { + // OnLog defines action(s) performed when Q# test calls function Message + sim.OnLog += (msg) => { output.WriteLine(msg); }; + sim.OnLog += (msg) => { Debug.WriteLine(msg); }; + op.TestOperationRunner(sim); + } + } + } +} diff --git a/RippleCarryAdder/Tests.qs b/RippleCarryAdder/Tests.qs new file mode 100644 index 00000000000..fcdfa7ebc36 --- /dev/null +++ b/RippleCarryAdder/Tests.qs @@ -0,0 +1,391 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////////// +// This file contains testing harness for all tasks. +// You should not modify anything in this file. +// The tasks themselves can be found in Tasks.qs file. +////////////////////////////////////////////////////////////////////// + +namespace Quantum.Kata.RippleCarryAdder { + + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Measurement; + + open Quantum.Kata.Utils; + + // ------------------------------------------------------ + function Adder (max : Int, a : Int, b : Int) : (Int, Bool) { + let sum = a + b; + return (sum % max, sum >= max); + } + + function Subtractor_F (max : Int, a : Int, b : Int) : (Int, Bool) { + let diff = b - a; + return ((diff + max) % max, diff < 0); + } + + function BinaryAdder (input : Bool[], N : Int) : Bool[] { + let max = 1 <<< N; + let bitsa = input[0 .. N-1]; + let bitsb = input[N ...]; + let a = BoolArrayAsInt(bitsa); + let b = BoolArrayAsInt(bitsb); + let (sum, carry) = Adder(max, a, b); + return IntAsBoolArray(sum, N) + [carry]; + } + + function BinarySubtractor (input : Bool[], N : Int) : Bool[] { + let max = 1 <<< N; + let bitsa = input[0 .. N-1]; + let bitsb = input[N ...]; + let a = BoolArrayAsInt(bitsa); + let b = BoolArrayAsInt(bitsb); + let (diff, borrow) = Subtractor_F(max, a, b); + return IntAsBoolArray(diff, N) + [borrow]; + } + + function BinaryXor (bits : Bool[]) : Bool { + let N = Length(bits); + mutable ans = false; + for (i in 0 .. N-1) { + if (bits[i]) { + set ans = not ans; + } + } + return ans; + } + + // ------------------------------------------------------ + // Wrapper operations to make the tasks usable with AssertOperationsEqualReferenced + operation QubitArrayOperationWrapper2 (op : ((Qubit, Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { + op(Head(arr), Tail(arr)); + } + + operation QubitArrayOperationWrapper3 (op : ((Qubit, Qubit, Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { + op(arr[0], arr[1], arr[2]); + } + + operation QubitArrayOperationWrapper4 (op : ((Qubit, Qubit, Qubit, Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { + op(arr[0], arr[1], arr[2], arr[3]); + } + + operation QubitArrayAdderWrapper (N : Int, op : ((Qubit[], Qubit[], Qubit[], Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { + let splits = Partitioned([N, N, N, 1], arr); + op(splits[0], splits[1], splits[2], Tail(arr)); + } + + operation QubitArrayInPlaceAdderWrapper (N : Int, op : ((Qubit[], Qubit[], Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { + let splits = Partitioned([N, N, 1], arr); + op(splits[0], splits[1], Tail(arr)); + } + + // ------------------------------------------------------ + // Helper operations to prepare qubits from an input and compare them to the output + operation PrepareRegister (register : Qubit[], state : Int) : Bool[] { + let bits = IntAsBoolArray(state, Length(register)); + ApplyPauliFromBitString(PauliX, true, bits, register); + return bits; + } + + operation VerifyRegister (register : Qubit[], state : Bool[], errorPrefix : String) : Unit { + let results = MultiM(register); + let resultsb = ResultArrayAsBoolArray(results); + AllEqualityFactB(resultsb, state, errorPrefix + $"expected {BoolArrayAsResultArray(state)}, but got {results}"); + } + + // ------------------------------------------------------ + // Function to generate more readable error messages + function GenerateErrorPrefix (bits : Bool[]) : String { + let N = Length(bits); + let astr = $"Inputs a:{BoolArrayAsResultArray(bits[0 .. (N/2)-1])}"; + let postfix = " produce unexpected output: "; + if (N % 2 == 0) { + // Only a and b inputs + return astr + $" and b:{BoolArrayAsResultArray(bits[N/2 ...])}" + postfix; + } + else { + // 3 inputs - a, b, and carry + return astr + $", b:{BoolArrayAsResultArray(bits[N/2 .. N-2])}, and c:{BoolArrayAsResultArray(bits[N-1 ...])}" + postfix; + } + } + + // ------------------------------------------------------ + // Assertion to compare a binary function with its quantum counterpart + operation AssertOperationImplementsBinaryFunction (op : ((Qubit[]) => Unit is Adj), fun : ((Bool[]) -> Bool[]), Nbits : Int, Nverify : Int) : Unit { + let max = 1 <<< Nbits; + + using ((qinput, qoutput) = (Qubit[Nbits], Qubit[Nverify])) { + let qs = qinput + qoutput; + for (i in 0 .. max-1) { + let inbits = PrepareRegister(qinput, i); + let outbits = fun(inbits); + + op(qs); + + VerifyRegister(qinput, inbits, "Inputs should not be modified: "); + VerifyRegister(qoutput, outbits, GenerateErrorPrefix(inbits)); + ResetAll(qs); + } + } + } + + // Assertion to compare a binary function with its in-place quantum counterpart + operation AssertInPlaceOperationImplementsBinaryFunction (op : ((Qubit[]) => Unit is Adj), fun : ((Bool[]) -> Bool[]), N : Int, targetStart : Int, targetEnd : Int, extra : Int) : Unit { + let max = 1 <<< N; + using ((qinput, qextra) = (Qubit[N], Qubit[extra])) { + let qs = qinput + qextra; + let target = qinput[targetStart .. targetEnd] + qextra; + let beforeTarget = qinput[0 .. targetStart-1]; + let afterTarget = qinput[targetEnd+1 ...]; + for (i in 0 .. max-1) { + let inbits = PrepareRegister(qinput, i); + let outbits = fun(inbits); + + op(qs); + + VerifyRegister(beforeTarget, inbits[0 .. targetStart-1], "Input a should not be modified: "); + VerifyRegister(afterTarget, inbits[targetEnd+1 ...], "The carry input should not be modified: "); + VerifyRegister(target, outbits, GenerateErrorPrefix(inbits)); + ResetAll(qs); + } + } + } + + ////////////////////////////////////////////////////////////////// + // Part I. Simple adder outputting to empty Qubits + ////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------ + function LowestBitSum_F (bits : Bool[]) : Bool[] { + return [BinaryXor(bits)]; + } + + operation T11_LowestBitSum_Test () : Unit { + let testOp = QubitArrayOperationWrapper3(LowestBitSum, _); + let refOp = QubitArrayOperationWrapper3(LowestBitSum_Reference, _); + + AssertOperationImplementsBinaryFunction(testOp, LowestBitSum_F, 2, 1); + AssertOperationsEqualReferenced(3, testOp, refOp); + } + + // ------------------------------------------------------ + function LowestBitCarry_F (bits : Bool[]) : Bool[] { + return [bits[0] and bits[1]]; + } + + operation T12_LowestBitCarry_Test () : Unit { + let testOp = QubitArrayOperationWrapper3(LowestBitCarry, _); + let refOp = QubitArrayOperationWrapper3(LowestBitCarry_Reference, _); + + AssertOperationImplementsBinaryFunction(testOp, LowestBitCarry_F, 2, 1); + AssertOperationsEqualReferenced(3, testOp, refOp); + } + + // ------------------------------------------------------ + operation T13_OneBitAdder_Test () : Unit { + let testOp = QubitArrayOperationWrapper4(OneBitAdder, _); + let refOp = QubitArrayOperationWrapper4(OneBitAdder_Reference, _); + + AssertOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 1), 2, 2); + AssertOperationsEqualReferenced(4, testOp, refOp); + } + + // ------------------------------------------------------ + function HighBitSum_F (bits : Bool[]) : Bool[] { + return [BinaryXor(bits)]; + } + + operation T14_HighBitSum_Test () : Unit { + let testOp = QubitArrayOperationWrapper4(HighBitSum, _); + let refOp = QubitArrayOperationWrapper4(HighBitSum_Reference, _); + + AssertOperationImplementsBinaryFunction(testOp, HighBitSum_F, 3, 1); + AssertOperationsEqualReferenced(4, testOp, refOp); + } + + // ------------------------------------------------------ + function HighBitCarry_F (bits : Bool[]) : Bool[] { + return [(bits[0] and bits[1]) or (bits[2] and (bits[0] or bits[1]))]; + } + + operation T15_HighBitCarry_Test () : Unit { + let testOp = QubitArrayOperationWrapper4(HighBitCarry, _); + let refOp = QubitArrayOperationWrapper4(HighBitCarry_Reference, _); + + AssertOperationImplementsBinaryFunction(testOp, HighBitCarry_F, 3, 1); + AssertOperationsEqualReferenced(4, testOp, refOp); + } + + // ------------------------------------------------------ + operation T16_TwoBitAdder_Test () : Unit { + let testOp = QubitArrayAdderWrapper(2, TwoBitAdder, _); + let refOp = QubitArrayAdderWrapper(2, TwoBitAdder_Reference, _); + + AssertOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 2), 4, 3); + AssertOperationsEqualReferenced(7, testOp, refOp); + } + + // ------------------------------------------------------ + operation T17_ArbitraryAdder_Test () : Unit { + // 4 bits seems reasonable - any more than that will take forever + for (i in 1 .. 4){ + let testOp = QubitArrayAdderWrapper(i, ArbitraryAdder, _); + AssertOperationImplementsBinaryFunction(testOp, BinaryAdder(_, i), 2 * i, i + 1); + } + // Can't compare to the reference operation, because then the challenge solution doesn't pass the test + } + + ////////////////////////////////////////////////////////////////// + // Part II. Simple in-place adder + ////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------ + operation T21_LowestBitSumInPlace_Test () : Unit { + let testOp = QubitArrayOperationWrapper2(LowestBitSumInPlace, _); + let refOp = QubitArrayOperationWrapper2(LowestBitSumInPlace_Reference, _); + + AssertInPlaceOperationImplementsBinaryFunction(testOp, LowestBitSum_F, 2, 1, 1, 0); + AssertOperationsEqualReferenced(2, testOp, refOp); + } + + // ------------------------------------------------------ + operation T22_OneBitAdderInPlace_Test () : Unit { + let testOp = QubitArrayOperationWrapper3(OneBitAdderInPlace, _); + let refOp = QubitArrayOperationWrapper3(OneBitAdderInPlace_Reference, _); + + AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 1), 2, 1, 1, 1); + AssertOperationsEqualReferenced(3, testOp, refOp); + } + + // ------------------------------------------------------ + operation T23_HighBitSumInPlace_Test () : Unit { + let testOp = QubitArrayOperationWrapper3(HighBitSumInPlace, _); + let refOp = QubitArrayOperationWrapper3(HighBitSumInPlace_Reference, _); + + AssertInPlaceOperationImplementsBinaryFunction(testOp, HighBitSum_F, 3, 1, 1, 0); + AssertOperationsEqualReferenced(3, testOp, refOp); + } + + // ------------------------------------------------------ + operation T24_TwoBitAdderInPlace_Test () : Unit { + let testOp = QubitArrayInPlaceAdderWrapper(2, TwoBitAdderInPlace, _); + let refOp = QubitArrayInPlaceAdderWrapper(2, TwoBitAdderInPlace_Reference, _); + + AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 2), 4, 2, 3, 1); + AssertOperationsEqualReferenced(5, testOp, refOp); + } + + // ------------------------------------------------------ + operation T25_ArbitraryAdderInPlace_Test () : Unit { + for (i in 1 .. 4) { + let testOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryAdderInPlace, _); + let refOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryAdderInPlace_Reference, _); + AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, i), 2 * i, i, (2 * i) - 1, 1); + AssertOperationsEqualReferenced((2 * i) + 1, testOp, refOp); + } + } + + ////////////////////////////////////////////////////////////////// + // Part III*. Improved in-place adder + ////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------ + function Majority_F (bits : Bool[]) : Bool[] { + let a = bits[0]; + let b = bits[1]; + let c = bits[2]; + let ab = XOR(a, b); + let ac = XOR(a, c); + let cout = XOR(ab and ac, a); + return [cout, ab, ac]; + } + + operation T31_Majority_Test () : Unit { + let testOp = QubitArrayOperationWrapper3(Majority, _); + let refOp = QubitArrayOperationWrapper3(Majority_Reference, _); + + AssertInPlaceOperationImplementsBinaryFunction(testOp, Majority_F, 3, 0, 2, 0); + AssertOperationsEqualReferenced(3, testOp, refOp); + } + + // ------------------------------------------------------ + function UnMajorityAdd_F (bits : Bool[]) : Bool[] { + let a = bits[0]; + let b = bits[1]; + let c = bits[2]; + let a2 = XOR(b and c, a); + let c2 = XOR(a2, c); + let b2 = XOR(c2, b); + return [a2, b2, c2]; + } + + operation T32_UnMajorityAdd_Test () : Unit { + let testOp = QubitArrayOperationWrapper3(UnMajorityAdd, _); + let refOp = QubitArrayOperationWrapper3(UnMajorityAdd_Reference, _); + + AssertInPlaceOperationImplementsBinaryFunction(testOp, UnMajorityAdd_F, 3, 0, 2, 0); + AssertOperationsEqualReferenced(3, testOp, refOp); + } + + // ------------------------------------------------------ + operation T33_OneBitMajUmaAdder_Test () : Unit { + let testOp = QubitArrayOperationWrapper3(OneBitMajUmaAdder, _); + let refOp = QubitArrayOperationWrapper3(OneBitMajUmaAdder_Reference, _); + AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 1), 2, 1, 1, 1); + AssertOperationsEqualReferenced(3, testOp, refOp); + } + + // ------------------------------------------------------ + operation T34_TwoBitMajUmaAdder_Test () : Unit { + //Reverted to old test, as opration call counts don't work for counting task operations in notebooks + //ResetOracleCallsCount(); + let testOp = QubitArrayInPlaceAdderWrapper(2, TwoBitMajUmaAdder, _); + let refOp = QubitArrayInPlaceAdderWrapper(2, TwoBitMajUmaAdder_Reference, _); + AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 2), 4, 2, 3, 1); + //let sumCalls = GetOracleCallsCount(HighBitSumInPlace); + //let carryCalls = GetOracleCallsCount(HighBitCarry); + //let majCalls = GetOracleCallsCount(Majority); + //let umaCalls = GetOracleCallsCount(UnMajorityAdd); + //Fact((sumCalls == 0) and (carryCalls == 0), "You shouldn't be calling the old sum/carry operations for this task."); + //Fact((majCalls > 0) and (umaCalls > 0), "Are you sure you're using the Majority and UMA gates?"); + + + AssertOperationsEqualReferenced(5, testOp, refOp); + } + + // ------------------------------------------------------ + operation T35_ArbitraryMajUmaAdder_Test () : Unit { + // This algorithm is much faster, so a 5 qubit test is feasible + for (i in 1 .. 5) { + let testOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryMajUmaAdder, _); + let refOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryMajUmaAdder_Reference, _); + + ResetQubitCount(); + AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, i), 2 * i, i, (2 * i) - 1, 1); + let used = GetMaxQubitCount(); + Fact(used <= (2 * (i + 1)), "Too many qubits used"); + + AssertOperationsEqualReferenced((2 * i) + 1, testOp, refOp); + } + } + + ////////////////////////////////////////////////////////////////// + // Part IV*. In-place subtractor + ////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------ + operation T41_Subtractor_Test () : Unit { + for (i in 1 .. 5) { + let testOp = QubitArrayInPlaceAdderWrapper(i, Subtractor, _); + let refOp = QubitArrayInPlaceAdderWrapper(i, Subtractor_Reference, _); + AssertInPlaceOperationImplementsBinaryFunction(testOp, BinarySubtractor(_, i), 2 * i, i, (2 * i) - 1, 1); + AssertOperationsEqualReferenced((2 * i) + 1, testOp, refOp); + } + } +} From 4e537e718aabecae8a12548da7da7f12ce5d3872 Mon Sep 17 00:00:00 2001 From: Artem Astapchuk Date: Sat, 21 Sep 2019 21:06:02 -0700 Subject: [PATCH 2/8] Added links to new kata in README/index notebook. --- README.md | 2 ++ index.ipynb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index daf842b5307..b7675582a52 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ Currently covered topics are: This kata covers phase estimation algorithms. * **[Bit-flip error correcting code](./QEC_BitFlipCode/)**. This kata introduces a 3-qubit error correcting code for protecting against bit-flip errors. +* **[Ripple Carry Adder](./RippleCarryAdder/)**. + This kata walks you through building a ripple-carry adder on a quantum computer. * **[Unitary Patterns*](./UnitaryPatterns/)**. This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of zero and non-zero elements. diff --git a/index.ipynb b/index.ipynb index 158fdb977fb..64d7c227b3b 100644 --- a/index.ipynb +++ b/index.ipynb @@ -55,6 +55,8 @@ "This kata covers phase estimation algorithms.\n", "* **[Bit-flip error correcting code](./QEC_BitFlipCode/QEC_BitFlipCode.ipynb)**.\n", " This kata introduces a 3-qubit error correcting code for protecting against bit-flip errors.\n", + "* **[Ripple Carry Adder](./RippleCarryAdder/RippleCarryAdder.ipynb)**.\n", + " This kata walks you through building a ripple-carry adder on a quantum computer.\n", "* **[Unitary Patterns](./UnitaryPatterns/UnitaryPatterns.ipynb)**.\n", "This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of \n", "zero and non-zero elements.\n", From 73b8b77cec3f22bfa5789341a4c25d8466638d95 Mon Sep 17 00:00:00 2001 From: Artem Astapchuk Date: Fri, 4 Oct 2019 18:09:29 -0700 Subject: [PATCH 3/8] Added Single Qubit Gates tutorial --- .gitignore | 2 + .../ReferenceImplementation.qs | 56 ++ .../SingleQubitGates/SingleQubitGates.csproj | 19 + .../SingleQubitGates/SingleQubitGates.ipynb | 874 ++++++++++++++++++ .../SingleQubitGates/SingleQubitGates.sln | 25 + tutorials/SingleQubitGates/Tasks.qs | 54 ++ tutorials/SingleQubitGates/TestSuiteRunner.cs | 44 + tutorials/SingleQubitGates/Tests.qs | 72 ++ 8 files changed, 1146 insertions(+) create mode 100644 tutorials/SingleQubitGates/ReferenceImplementation.qs create mode 100644 tutorials/SingleQubitGates/SingleQubitGates.csproj create mode 100644 tutorials/SingleQubitGates/SingleQubitGates.ipynb create mode 100644 tutorials/SingleQubitGates/SingleQubitGates.sln create mode 100644 tutorials/SingleQubitGates/Tasks.qs create mode 100644 tutorials/SingleQubitGates/TestSuiteRunner.cs create mode 100644 tutorials/SingleQubitGates/Tests.qs diff --git a/.gitignore b/.gitignore index 57ff868dddf..195807679b3 100644 --- a/.gitignore +++ b/.gitignore @@ -283,3 +283,5 @@ TSWLatexianTemp* # standalone packages *.sta +/tutorials/ComplexArithmetic/__pycache__ +/tutorials/LinearAlgebra/__pycache__ diff --git a/tutorials/SingleQubitGates/ReferenceImplementation.qs b/tutorials/SingleQubitGates/ReferenceImplementation.qs new file mode 100644 index 00000000000..e7d8ac18d26 --- /dev/null +++ b/tutorials/SingleQubitGates/ReferenceImplementation.qs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////////// +// This file contains reference solutions to all tasks. +// You should not modify anything in this file. +// We recommend that you try to solve the tasks yourself first, +// but feel free to look up the solution if you get stuck. +////////////////////////////////////////////////////////////////////// + +namespace Quantum.Kata.SingleQubitGates { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Math; + + // Exercise 1. + operation ApplyY_Reference (q : Qubit) : Unit is Adj { + Y(q); + } + + // Exercise 2. + operation ApplyZ_Reference (q : Qubit) : Unit is Adj { + Z(q); + } + + // Exercise 3. + operation ZeroFlip_Reference (q : Qubit) : Unit is Adj+Ctl { + X(q); + Z(q); + X(q); + } + + // Exercise 4. + operation PrepareMinus_Reference (q : Qubit) : Unit is Adj { + X(q); + H(q); + } + + // Exercise 5. + operation ThreePiPhase_Reference (q : Qubit) : Unit is Adj+Ctl { + S(q); + T(q); + } + + // Exercise 6. + operation RotatedState_Reference (alpha : Double, beta : Double, q : Qubit) : Unit is Adj { + let phi = ArcTan2(beta, alpha); + Rx(2.0 * phi, q); + } + + // Exercise 7. + operation ArbitraryState_Reference (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj { + let phi = ArcTan2(beta, alpha); + Ry(2.0 * phi, q); + R1(theta, q); + } +} \ No newline at end of file diff --git a/tutorials/SingleQubitGates/SingleQubitGates.csproj b/tutorials/SingleQubitGates/SingleQubitGates.csproj new file mode 100644 index 00000000000..1c29cca2b6b --- /dev/null +++ b/tutorials/SingleQubitGates/SingleQubitGates.csproj @@ -0,0 +1,19 @@ + + + netcoreapp2.0 + x64 + false + Kata.SingleQubitGates + SingleQubitGates + + + + + + + + + + + + diff --git a/tutorials/SingleQubitGates/SingleQubitGates.ipynb b/tutorials/SingleQubitGates/SingleQubitGates.ipynb new file mode 100644 index 00000000000..41ea628aa99 --- /dev/null +++ b/tutorials/SingleQubitGates/SingleQubitGates.ipynb @@ -0,0 +1,874 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Single Qubit Gates\n", + "\n", + "This tutorial introduces you to single qubit gates. Quantum gates are the quantum counterpart to classical logic gates, acting as the building blocks of quantum computing. Quantum gates transform qubit states in various ways, and can be applied sequentially to perform complex quantum calculations. Single qubit gates, as their name implies, act on single qubits. You can learn more at [Wikipedia](https://en.wikipedia.org/wiki/Quantum_logic_gate).\n", + "\n", + "You can use a tool called [Quirk](https://algasert.com/quirk) to visualize how these gates interact with various qubit states.\n", + "\n", + "It is highly recommended to go through the [single qubit systems tutorial](../Qubit/Qubit.ipynb) before attempting this one.\n", + "\n", + "This tutorial covers the following topics:\n", + "\n", + "* Matrix representation\n", + "* Ket-bra representation\n", + "* Pauli gates\n", + "* Hadamard gate\n", + "* Phase gates\n", + "* Rotation gates" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To begin, first prepare this notebook for execution (if you skip this step, you'll get \"Syntax does not match any known patterns\" error when you try to execute Q# code in the exercises):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%package Microsoft.Quantum.Katas::0.9.1908.2906" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.\n", + ">
\n", + "> How to install the right IQ# version\n", + "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.1.2.3, the installation steps are as follows:\n", + ">\n", + "> 1. Stop the kernel.\n", + "> 2. Uninstall the existing version of IQ#:\n", + "> dotnet tool uninstall microsoft.quantum.iqsharp -g\n", + "> 3. Install the matching version:\n", + "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.1.2.3\n", + "> 4. Reinstall the kernel:\n", + "> dotnet iqsharp install\n", + "> 5. Restart the Notebook.\n", + ">
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Basics\n", + "\n", + "There are certain properties common to all quantum gates. This section will introduce those properties, using the quantum $X$ gate as an example." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Matrix Representation\n", + "\n", + "Quantum gates are represented as $2^N \\times 2^N$ [unitary matrices](../LinearAlgebra/LinearAlgebra.ipynb#Unary-Operations), where $N$ is the number of qubits the gate operates on. Single qubit gates are represented by $2 \\times 2$ matrices.\n", + "As a quick reminder, a unitary matrix is a square matrix whose inverse is its adjoint. Our example for this section, the $X$ gate, is represented by the following matrix:\n", + "\n", + "$$\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}$$\n", + "\n", + "You may recall that the state of a qubit is represented by a vector of size $2$. You can apply a gate to a qubit by [multiplying](../LinearAlgebra/LinearAlgebra.ipynb#Matrix-Multiplication) the gate's matrix by the qubit's state vector. The result will be another vector, representing the new state of the qubit. For example, applying the $X$ gate to the computational basis states looks like this:\n", + "\n", + "$$X|0\\rangle =\n", + "\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}\n", + "\\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix} =\n", + "\\begin{bmatrix} 0 \\cdot 1 + 1 \\cdot 0 \\\\ 1 \\cdot 1 + 0 \\cdot 0 \\end{bmatrix} =\n", + "\\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix} \\\\\n", + "X|1\\rangle =\n", + "\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}\n", + "\\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix} =\n", + "\\begin{bmatrix} 0 \\cdot 0 + 1 \\cdot 1 \\\\ 1 \\cdot 0 + 0 \\cdot 1 \\end{bmatrix} =\n", + "\\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix}$$\n", + "\n", + "The general case:\n", + "\n", + "$$|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle \\\\\n", + "X|\\psi\\rangle =\n", + "\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}\n", + "\\begin{bmatrix} \\alpha \\\\ \\beta \\end{bmatrix} =\n", + "\\begin{bmatrix} 0 \\cdot \\alpha + 1 \\cdot \\beta \\\\ 1 \\cdot \\alpha + 0 \\cdot \\beta \\end{bmatrix} =\n", + "\\begin{bmatrix} \\beta \\\\ \\alpha \\end{bmatrix}$$\n", + "\n", + "> If you don't remember what $|0\\rangle$, $|1\\rangle$, and $|\\psi\\rangle$ mean, you should review the section on [Dirac notation](../SingleQubits/SingleQubits.ipynb#Dirac-Notation).\n", + "\n", + "Because this is the most common way to represent quantum gates, the terms \"gate\" and \"gate matrix\" will be used interchangeably in this tutorial.\n", + "\n", + "Applying several quantum gates in sequence is equivalent to performing several of these multiplications. For example, if you have gates $A$ and $B$ and a qubit in state $|\\psi\\rangle$, the result of applying $A$ followed by $B$ to that qubit would be $B\\big(A|\\psi\\rangle\\big)$ (gates closest to the qubit state get applied first). Because matrix multiplication is associative, however, this is equivalent to multiplying the $B$ matrix by the $A$ matrix, producing a compound gate of the two, and then applying that to the qubit: $\\big(BA\\big)|\\psi\\rangle$.\n", + "\n", + "All quantum gates are reversible - there is another gate which will undo any given gate's transformation, returning the qubit to its original state. This means that when dealing with quantum gates, information about qubit states is never lost, as opposed to classical logic gates, some of which destroy information. This is why measurements are not considered gates - measurements destroy information about a quantum state. For more information, see the [measurements tutorial](../Measurements/Measurements.ipynb).\n", + "\n", + "Because quantum gates are represented by unitary matrices, the inverse of a gate is its adjoint." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Effects on Basis States.\n", + "\n", + "Doing matrix multiplication on long chains of gates is tedious and time consuming. Fortunately, there is an easier way to do quantum calculations.\n", + "\n", + "There is a simple way to find out what a gate does to the two computational basis states ($|0\\rangle$ and $|1\\rangle$) from looking at its matrix. Consider an arbitrary gate:\n", + "\n", + "$$A = \\begin{bmatrix} \\epsilon & \\zeta \\\\ \\eta & \\mu \\end{bmatrix}$$\n", + "\n", + "Watch what happens when we apply it to these states:\n", + "\n", + "$$A|0\\rangle =\n", + "\\begin{bmatrix} \\epsilon & \\zeta \\\\ \\eta & \\mu \\end{bmatrix}\n", + "\\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix} =\n", + "\\begin{bmatrix} \\epsilon \\cdot 1 + \\zeta \\cdot 0 \\\\ \\eta \\cdot 1 + \\mu \\cdot 0 \\end{bmatrix} =\n", + "\\begin{bmatrix} \\epsilon \\\\ \\eta \\end{bmatrix} = \\epsilon|0\\rangle + \\eta|1\\rangle \\\\\n", + "A|1\\rangle =\n", + "\\begin{bmatrix} \\epsilon & \\zeta \\\\ \\eta & \\mu \\end{bmatrix}\n", + "\\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix} =\n", + "\\begin{bmatrix} \\epsilon \\cdot 0 + \\zeta \\cdot 1 \\\\ \\eta \\cdot 0 + \\mu \\cdot 1 \\end{bmatrix} =\n", + "\\begin{bmatrix} \\zeta \\\\ \\mu \\end{bmatrix} = \\zeta|0\\rangle + \\mu|1\\rangle$$\n", + "\n", + "Notice that applying the gate to the $|0\\rangle$ state outputs the first column of the gate's matrix. Likewise, applying the gate to the $|1\\rangle$ state outputs the second column. This holds true for any quantum gate, including, of course, the $X$ gate:\n", + "\n", + "$$X = \\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix} \\\\\n", + "X|0\\rangle = \\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix} = |1\\rangle \\\\\n", + "X|1\\rangle = \\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix} = |0\\rangle$$\n", + "\n", + "Once you understand how a gate affects the computational basis states, you can easily find how it affects any state.\n", + "Recall that any qubit state vector can be re-written as a sum of the basis states:\n", + "\n", + "$$|\\psi\\rangle = \\begin{bmatrix} \\alpha \\\\ \\beta \\end{bmatrix} = \\alpha|0\\rangle + \\beta|1\\rangle$$\n", + "\n", + "Because matrix multiplication distributes over addition, once you know how a gate affects those two basis states, you can calculate how it affects any state:\n", + "\n", + "$$X|\\psi\\rangle = X\\big(\\alpha|0\\rangle + \\beta|1\\rangle\\big) = X\\big(\\alpha|0\\rangle\\big) + X\\big(\\beta|1\\rangle\\big) = \\alpha X|0\\rangle + \\beta X|1\\rangle = \\alpha|1\\rangle + \\beta|0\\rangle$$\n", + "\n", + "That is, applying a gate to a qubit in superposition is equivalent to applying that gate to both states that make up that superposition." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ket-bra Representation\n", + "\n", + "There is another way to represent quantum gates, this time using Dirac notation. However, the kets we've been using aren't enough to represent arbitrary matrices. We need to introduce another piece of notation: the **bra** (this is why Dirac notation is sometimes called **bra-ket notation**).\n", + "Since kets represent column vectors, a bra is a ket's row vector counterpart. For any ket $|\\psi\\rangle$, its bra is $\\langle\\psi| = |\\psi\\rangle^\\dagger$.\n", + "\n", + "> As a quick reminder, the [adjoint](../LinearAlgebra/LinearAlgebra.ipynb#Unary-Operations), also known as the conjugate transpose of a matrix, well, the conjugate of that matrix's transpose.\n", + "\n", + "Some examples:\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
KetBra
$|0\\rangle = \\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix}$$\\langle0| = \\begin{bmatrix} 1 & 0 \\end{bmatrix}$
$|1\\rangle = \\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix}$$\\langle1| = \\begin{bmatrix} 0 & 1 \\end{bmatrix}$
$|i\\rangle = \\begin{bmatrix} \\frac{1}{\\sqrt{2}} \\\\ \\frac{i}{\\sqrt{2}} \\end{bmatrix}$$\\langle i| = \\begin{bmatrix} \\frac{1}{\\sqrt{2}} & -\\frac{i}{\\sqrt{2}} \\end{bmatrix}$
$|\\psi\\rangle = \\begin{bmatrix} \\alpha \\\\ \\beta \\end{bmatrix}$$\\langle\\psi| = \\begin{bmatrix} \\overline{\\alpha} & \\overline{\\beta} \\end{bmatrix}$
$|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$$\\langle\\psi| = \\overline{\\alpha}\\langle0| + \\overline{\\beta}\\langle1|$
\n", + "\n", + "Kets and bras give us a neat way to express [inner](../LinearAlgebra/LinearAlgebra.ipynb#Inner-Product) and [outer](../LinearAlgebra/LinearAlgebra.ipynb#Outer-Product) products. The inner product of $|\\phi\\rangle$ and $|\\psi\\rangle$ is $\\langle\\phi|\\psi\\rangle$, and their outer product is $|\\phi\\rangle\\langle\\psi|$. Notice that the norm of $|\\psi\\rangle$ is $\\sqrt{\\langle\\psi|\\psi\\rangle}$.\n", + "\n", + "This brings us to representing matrices. Recall that the outer product of two vectors of the same size produces a square matrix. We can use the sum of several outer products of simple vectors (with scalar factors) to express any matrix. For example, the $X$ gate can be expressed as follows:\n", + "\n", + "$$X = |0\\rangle\\langle1| + |1\\rangle\\langle0| \\\\\n", + "|0\\rangle\\langle1| + |1\\rangle\\langle0| =\n", + "\\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix}\\begin{bmatrix} 0 & 1 \\end{bmatrix} +\n", + "\\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix}\\begin{bmatrix} 1 & 0 \\end{bmatrix} =\n", + "\\begin{bmatrix} 0 & 1 \\\\ 0 & 0 \\end{bmatrix} + \\begin{bmatrix} 0 & 0 \\\\ 1 & 0 \\end{bmatrix} =\n", + "\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}$$\n", + "\n", + "This can also be used for calculations:\n", + "\n", + "$$X|0\\rangle = \\big(|0\\rangle\\langle1| + |1\\rangle\\langle0|\\big)|0\\rangle = |0\\rangle\\langle1|0\\rangle + |1\\rangle\\langle0|0\\rangle = |0\\rangle\\big(\\langle1|0\\rangle\\big) + |1\\rangle\\big(\\langle0|0\\rangle\\big) = |0\\rangle(0) + |1\\rangle(1) = |1\\rangle$$\n", + "\n", + "> That last step may seem a bit confusing. Recall that $|0\\rangle$ and $|1\\rangle$ form an **orthonormal basis**. That is, they are both normalized, and they are orthogonal to each other.\n", + ">\n", + "> A vector is normalized if its norm is equal to $1$, which only happens if its inner product with itself is equal to $1$. This means that $\\langle0|0\\rangle = \\langle1|1\\rangle = 1$\n", + ">\n", + "> Two vectors are orthogonal to each other if their inner product equals $0$. This means that $\\langle0|1\\rangle = \\langle 1|0\\rangle = 0$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> ## Ket-bra decomposition\n", + ">\n", + "> This section deals with finding the ket-bra decompositions of quantum gates. This section is not necessary to start working with quantum gates, so feel free to skip it for now, and come back to it later.\n", + ">\n", + "> You can use the properties of [eigenvalues and eigenvectors](../LinearAlgebra/LinearAlgebra.ipynb#Eigenvalues-and-Eigenvectors) to find the ket-bra decomposition of any gate. Given a gate $A$, and its eigenvectors $|\\phi\\rangle$ and $|\\psi\\rangle$, if:\n", + ">\n", + "> $$A|\\phi\\rangle = \\epsilon|\\phi\\rangle \\\\\n", + "A|\\psi\\rangle = \\zeta|\\psi\\rangle$$\n", + ">\n", + "> Then:\n", + ">\n", + "> $$A = \\epsilon|\\phi\\rangle\\langle\\phi| + \\zeta|\\psi\\rangle\\langle\\psi|$$\n", + ">\n", + "> Let's use our $X$ gate as a simple example. The $X$ gate has two eigenvectors: $|+\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + |1\\rangle\\big)$ and $|-\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - |1\\rangle\\big)$. Their eigenvalues are $1$ and $-1$ respectively:\n", + ">\n", + "> $$X|+\\rangle = |+\\rangle \\\\\n", + "X|-\\rangle = -|-\\rangle$$\n", + ">\n", + "> Here's what the decomposition looks like:\n", + ">\n", + "> $$X = |+\\rangle\\langle+| - |-\\rangle\\langle-| =\n", + "\\frac{1}{\\sqrt{2}}\\big(|0\\rangle + |1\\rangle\\big)\\frac{1}{\\sqrt{2}}\\big(\\langle0| + \\langle1|\\big) - |-\\rangle\\langle-| = \\\\\n", + "= \\frac{1}{2}\\big[\\big(|0\\rangle + |1\\rangle\\big)\\big(\\langle0| + \\langle1\\big) - \\big(|0\\rangle - |1\\rangle\\big)\\big(\\langle0| - \\langle1|\\big)\\big] = \\\\\n", + "= \\frac{1}{2}\\big(|0\\rangle\\langle0| + |0\\rangle\\langle1| + |1\\rangle\\langle0| + |1\\rangle\\langle1| - |0\\rangle\\langle0| + |0\\rangle\\langle1| + |1\\rangle\\langle0| - |1\\rangle\\langle1|\\big) = \\\\\n", + "= \\frac{1}{2}\\big(|0\\rangle\\langle0| - |0\\rangle\\langle0| + |0\\rangle\\langle1| + |0\\rangle\\langle1| + |1\\rangle\\langle0| + |1\\rangle\\langle0| + |1\\rangle\\langle1| - |1\\rangle\\langle1|\\big) =\n", + "\\frac{1}{2}\\big(2|0\\rangle\\langle1| + 2|1\\rangle\\langle0|\\big) = |0\\rangle\\langle1| + |1\\rangle\\langle0|$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Important Gates\n", + "\n", + "This section introduces some of the common single qubit gates, including their matrix form, their ket-bra decomposition, and a brief summary of what they do to various qubit states.\n", + "\n", + "This section relies on the following notation:\n", + "\n", + "$$|+\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + |1\\rangle\\big) \\\\\n", + "|-\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - |1\\rangle\\big) \\\\\n", + "|i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + i|1\\rangle\\big) \\\\\n", + "|-i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - i|1\\rangle\\big) \\\\\n", + "|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pauli Gates\n", + "\n", + "The Pauli gates, named after Wolfgang Pauli ([Wikipedia](https://en.wikipedia.org/wiki/Wolfgang_Pauli)), are based on the so-called **Pauli matrices**, which also have some special significance to the topic of measurement. All three Pauli gates are **self-adjoint**, meaning that each one is its own inverse.\n", + "\n", + "### Pauli $X$ Gate\n", + "\n", + "The $X$ gate is probably the most common quantum gate. It has the effect of \"inverting\" the state of the qubit it's applied to, swapping the amplitudes of the $|0\\rangle$ and $|1\\rangle$ states. It is sometimes referred to as the **bit flip** gate, or the **NOT** gate, because it acts like the classical NOT gate on the computational basis.\n", + "\n", + "### Pauli $Y$ Gate\n", + "\n", + "The $Y$ gate is somewhat similar to the $X$ gate, but it introduces a complex component into the mix.\n", + "\n", + "### Pauli $Z$ Gate\n", + "\n", + "The final Pauli gate is the $Z$ gate, sometimes known as the **phase flip** gate. It doesn't affect state $|0\\rangle$, and applies a phase of $-1$ to state $|1\\rangle$.\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GateMatrixKet-BraApplying to $|\\psi\\rangle$Applying to basis statesQ# Documentation
$X$$\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}$$|0\\rangle\\langle1| + |1\\rangle\\langle0|$$X|\\psi\\rangle = \\alpha|1\\rangle + \\beta|0\\rangle$$X|0\\rangle = |1\\rangle \\\\\n", + "X|1\\rangle = |0\\rangle \\\\\n", + "X|+\\rangle = |+\\rangle \\\\\n", + "X|-\\rangle = -|-\\rangle \\\\\n", + "X|i\\rangle = i|-i\\rangle \\\\\n", + "X|-i\\rangle = -i|i\\rangle$ X
$Y$$\\begin{bmatrix} 0 & -i \\\\ i & 0 \\end{bmatrix}$$i(|1\\rangle\\langle0| - |0\\rangle\\langle1|)$$Y|\\psi\\rangle = i\\big(\\alpha|1\\rangle - \\beta|0\\rangle\\big)$$Y|0\\rangle = i|1\\rangle \\\\\n", + "Y|1\\rangle = -i|0\\rangle \\\\\n", + "Y|+\\rangle = -i|-\\rangle \\\\\n", + "Y|-\\rangle = i|+\\rangle \\\\\n", + "Y|i\\rangle = |i\\rangle \\\\\n", + "Y|-i\\rangle = -|-i\\rangle$Y
$Z$$\\begin{bmatrix} 1 & 0 \\\\ 0 & -1 \\end{bmatrix}$$|0\\rangle\\langle0| - |1\\rangle\\langle1|$$Z|\\psi\\rangle = \\alpha|0\\rangle - \\beta|1\\rangle$$Z|0\\rangle = |0\\rangle \\\\\n", + "Z|1\\rangle = -|1\\rangle \\\\\n", + "Z|+\\rangle = |-\\rangle \\\\\n", + "Z|-\\rangle = |+\\rangle \\\\\n", + "Z|i\\rangle = |-i\\rangle \\\\\n", + "Z|-i\\rangle = |i\\rangle$ Z
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Demo! Pauli X Gate\n", + "\n", + "The following cell contains code demonstrating how to apply gates in Q#, using the Pauli $X$ gate as an example. It sets up a series of quantum states, and then shows the result of applying the $X$ gate to each one. To run the demo, run the next cell using `Ctrl+Enter` (`⌘+Enter` on a Mac), then run the cell after it to see the output.\n", + "\n", + "Because qubits cannot be directly assigned or accessed, applying a gate doesn't return the resulting state of the qubit, but rather modifies the qubit's state in-place. This is why we never assign the output of these gates to anything in this demo - they don't produce any output.\n", + "\n", + "All the basic gates we will be covering in this tutorial are part of the [Intrinsic](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.intrinsic?view=qsharp-preview) namespace. We're also using the function [DumpMachine](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.diagnostics.dumpmachine?view=qsharp-preview) from the [Diagnostics](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.diagnostics?view=qsharp-preview) namespace to print the state of the quantum simulator." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Run this cell using Ctrl+Enter (⌘+Enter on Mac)\n", + "// Run the next cell to see the output\n", + "\n", + "// To use a namespace, you need to use the `open` keyword to access it\n", + "open Microsoft.Quantum.Diagnostics;\n", + "\n", + "// The Intrinsic namespace is open by default\n", + "// We can open it explicitly, although this produces a warning\n", + "open Microsoft.Quantum.Intrinsic;\n", + "\n", + "operation PauliX_Demo () : Unit {\n", + " // This allocates a qubit for us to work with\n", + " using (q = Qubit()) {\n", + " let divider = \"--------------------------------------------------------------------------------------------------\";\n", + " \n", + " // This prints out the state of the simulator\n", + " // A newly allocated qubit starts in state |0❭\n", + " Message(\"Qubit in state |0❭:\");\n", + " DumpMachine();\n", + " Message(divider);\n", + " \n", + " // Let's apply the X gate\n", + " X(q);\n", + " Message(\"Qubit in state X|0❭ = |1❭:\");\n", + " DumpMachine();\n", + " Message(divider);\n", + " \n", + " // Applying the X gate again reverses the transformation\n", + " X(q);\n", + " Message(\"Qubit in state XX|0❭ = X|1❭ = |0❭:\");\n", + " DumpMachine();\n", + " Message(divider);\n", + " \n", + " // Now, let's use the Hadamard gate to put the qubit into a superposition\n", + " // You'll learn about the Hadamard gate later\n", + " H(q);\n", + " Message(\"Qubit in state |+❭:\");\n", + " DumpMachine();\n", + " Message(divider);\n", + " \n", + " // Here's what happens when we apply the X gate to that superposition state\n", + " X(q);\n", + " Message(\"Qubit in state X|+❭ = |+❭:\");\n", + " DumpMachine();\n", + " Message(divider);\n", + " \n", + " // Now, let's change the qubit to state |-❭ and see what happens then\n", + " Reset(q);\n", + " X(q);\n", + " H(q);\n", + " Message(\"Qubit in state |-❭:\");\n", + " DumpMachine();\n", + " Message(divider);\n", + " \n", + " X(q);\n", + " Message(\"Qubit in state X|-❭ = -|-❭:\");\n", + " DumpMachine();\n", + " Message(divider);\n", + " \n", + " // You can also invert any gate using the Adjoint functor\n", + " // This will apply the inverse of the X gate\n", + " Adjoint X(q);\n", + " Message(\"Qubit in state X'X|-❭ = |-❭:\");\n", + " DumpMachine();\n", + " \n", + " // This returns the qubit into state |0❭\n", + " Reset(q);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "%simulate PauliX_Demo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 1: The $Y$ gate\n", + "\n", + "This is an exercise designed to test your understanding of the concepts thus far. Your task is to write an operation that applies a particular transformation to a qubit. Unlike the demos you have seen so far, you don't have to allocate the qubit - the operation you're writing takes the qubit as its input.\n", + "\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "\n", + "**Goal:** Apply the $Y$ gate to the qubit." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T1_ApplyY_Test\n", + "\n", + "operation ApplyY (q : Qubit) : Unit is Adj {\n", + " // Fill in your code here\n", + " // Then run the cell to test your work\n", + " // For this exercise, just apply the Y gate, without doing anything else\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 2: The $Z$ gate\n", + "\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "\n", + "**Goal:** Apply the $Z$ gate to the qubit." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T2_ApplyZ_Test\n", + "\n", + "operation ApplyZ (q : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 3*: Phasing the $|0\\rangle$ state\n", + "\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "\n", + "**Goal:** Use several basic gates to apply the transformation represented by the following matrix to the qubit:\n", + "\n", + "$$\\begin{bmatrix} -1 & 0 \\\\ 0 & 1 \\end{bmatrix}$$\n", + "\n", + "> Note that applying a gate doesn't return anything, so you can't assign its output to anything, and you can't use its output as the input to another function or operation. That is, something like `q2 = X(q);` or `Y(X(q));` doesn't work. This is because qubits are a special data type in Q# which can't be accessed and assigned directly. In order to apply several gates to the same qubit, you need to call them in sequence:\n", + ">\n", + "> ```C#\n", + "> X(q);\n", + "> Y(q);\n", + "> ```\n", + "\n", + "
\n", + "
\n", + " Need a hint? Click here\n", + " Don't forget, there's a way you can tell what a matrix does to the basis states by looking at its matrix.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T3_ZeroFlip_Test\n", + "\n", + "operation ZeroFlip (q : Qubit) : Unit is Adj+Ctl {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Identity\n", + "\n", + "The identity gate is mostly here for completeness, at least for now. It will come in handy when dealing with multi-qubit systems and multi-qubit gates. It is represented by the identity matrix, and does not affect the state of the qubit.\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GateMatrixKet-BraApplying to $|\\psi\\rangle$Q# Documentation
$I$$\\begin{bmatrix} 1 & 0 \\\\ 0 & 1 \\end{bmatrix}$$|0\\rangle\\langle0| + |1\\rangle\\langle1|$$I|\\psi\\rangle = |\\psi\\rangle$I
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Hadamard\n", + "\n", + "The **Hadamard** gate is an extremely important quantum gate. Unlike the previous gates, applying the Hadamard gate to a qubit in a computational basis state puts that qubit into a superposition.\n", + "Like the Pauli gates, the Hadamard gate is self-adjoint.\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GateMatrixKet-BraApplying to $|\\psi\\rangle$Applying to basis statesQ# Documentation
$H$$\\begin{bmatrix} \\frac{1}{\\sqrt{2}} & \\frac{1}{\\sqrt{2}} \\\\ \\frac{1}{\\sqrt{2}} & -\\frac{1}{\\sqrt{2}} \\end{bmatrix} = \\frac{1}{\\sqrt{2}}\\begin{bmatrix} 1 & 1 \\\\ 1 & -1 \\end{bmatrix}$$|0\\rangle\\langle+| + |1\\rangle\\langle-|$$H|\\psi\\rangle = \\alpha|+\\rangle + \\beta|-\\rangle = \\frac{\\alpha + \\beta}{\\sqrt{2}}|0\\rangle + \\frac{\\alpha - \\beta}{\\sqrt{2}}|1\\rangle$$H|0\\rangle = |+\\rangle \\\\\n", + "H|1\\rangle = |-\\rangle \\\\\n", + "H|+\\rangle = |0\\rangle \\\\\n", + "H|-\\rangle = |1\\rangle \\\\\n", + "H|i\\rangle = e^{i\\pi/4}|-i\\rangle \\\\\n", + "H|-i\\rangle = e^{-i\\pi/4}|i\\rangle$H
\n", + "\n", + "> If you need a refresher on what $e^{i\\theta}$ means, you should review the section on [complex exponentiation](../ComplexArithmetic/ComplexArithmetic.ipynb#Imaginary-Exponents)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 4: Preparing a $|-\\rangle$ state\n", + "\n", + "**Input:** A qubit in state $|0\\rangle$.\n", + "\n", + "**Goal:** Transform the qubit into state $|-\\rangle$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T4_PrepareMinus_Test\n", + "\n", + "operation PrepareMinus (q : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Phase Shift Gates\n", + "\n", + "The next two gates are known as phase shift gates. They apply a phase to the $|1\\rangle$ state, and leave the $|0\\rangle$ state untouched.\n", + "\n", + "#### The $S$ Gate\n", + "\n", + "The $S$ gate applies a phase of $i$ to the $|1\\rangle$ state. Applying the $S$ gate twice is equivalent to applying the $Z$ gate: $S^2 = Z$.\n", + "\n", + "\n", + "#### The $T$ Gate\n", + "\n", + "The $T$ gate applies a phase of $e^{i\\pi/4}$ to the $|1\\rangle$ state, \"rotating\" or \"phasing\" it by $\\frac{\\pi}{4}$. Applying it twice is equivalent to applying the $S$ gate: $T^2 = S$.\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GateMatrixKet-BraApplying to $|\\psi\\rangle$Applying to basis statesQ# Documentation
$S$$\\begin{bmatrix} 1 & 0 \\\\ 0 & i \\end{bmatrix}$$|0\\rangle\\langle0| + i|1\\rangle\\langle1|$$S|\\psi\\rangle = \\alpha|0\\rangle + i\\beta|1\\rangle$$S|0\\rangle = |0\\rangle \\\\\n", + "S|1\\rangle = i|1\\rangle \\\\\n", + "S|+\\rangle = |i\\rangle \\\\\n", + "S|-\\rangle = |-i\\rangle \\\\\n", + "S|i\\rangle = |-\\rangle \\\\\n", + "S|-i\\rangle = |+\\rangle$S
$T$$\\begin{bmatrix} 1 & 0 \\\\ 0 & e^{i\\pi/4} \\end{bmatrix}$$|0\\rangle\\langle0| + e^{i\\pi/4}|1\\rangle$$\\langle1|$$T|\\psi\\rangle = \\alpha|0\\rangle + \\beta e^{i\\pi/4}|1\\rangle$$T|0\\rangle = |0\\rangle \\\\\n", + "T|1\\rangle = e^{i\\pi/4}|1\\rangle$T
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 5: Three-fourths phase\n", + "\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "\n", + "**Goal:** Use several basic gates to apply the transformation represented by the following matrix to the given qubit:\n", + "\n", + "$$\\begin{bmatrix} 1 & 0 \\\\ 0 & e^{3i\\pi/4} \\end{bmatrix}$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T5_ThreePiPhase_Test\n", + "\n", + "operation ThreePiPhase (q : Qubit) : Unit is Adj+Ctl {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Rotation Gates\n", + "\n", + "The next few gates are parametrized - their exact behavior depends on a numeric parameter - an angle $\\theta$, given in radians. By convention, the parameter is multiplied by $\\frac{1}{2}$ within the gate's matrix. These gates are the $X$ rotation gate $R_x(\\theta)$, $Y$ rotation gate $R_y(\\theta)$, $Z$ rotation gate $R_z(\\theta)$, and the arbitrary phase gate $R_1(\\theta)$.\n", + "\n", + "> These gates are known as rotation gates, because they represent rotations around various axes on the Bloch sphere. The Bloch sphere is a way of representing qubits visually, using the unobservable phase to map qubit states onto the surface of a sphere. Unfortunately, this visualization isn't very useful beyond single qubits, which is why we have opted not to include it in this tutorial series. If you are curious about it, you can learn more at the [VCPC](http://www.vcpc.univie.ac.at/~ian/hotlist/qc/talks/bloch-sphere.pdf) or [Wikipedia](https://en.wikipedia.org/wiki/Bloch_sphere).\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GateMatrixApplying to $|\\psi\\rangle$Applying to basis statesQ# Documentation
$R_x(\\theta)$$\\begin{bmatrix} \\cos\\frac{\\theta}{2} & -i\\sin\\frac{\\theta}{2} \\\\ -i\\sin\\frac{\\theta}{2} & \\cos\\frac{\\theta}{2} \\end{bmatrix}$$R_x(\\theta)|\\psi\\rangle = (\\alpha\\cos\\frac{\\theta}{2} - i\\beta\\sin\\frac{\\theta}{2})|0\\rangle + (\\beta\\cos\\frac{\\theta}{2} - i\\alpha\\sin\\frac{\\theta}{2})|1\\rangle$$R_x(\\theta)|0\\rangle = \\cos\\frac{\\theta}{2}|0\\rangle - i\\sin\\frac{\\theta}{2}|1\\rangle \\\\\n", + "R_x(\\theta)|1\\rangle = \\cos\\frac{\\theta}{2}|1\\rangle - i\\sin\\frac{\\theta}{2}|0\\rangle$Rx
$R_y(\\theta)$$\\begin{bmatrix} \\cos\\frac{\\theta}{2} & -\\sin\\frac{\\theta}{2} \\\\ \\sin\\frac{\\theta}{2} & \\cos\\frac{\\theta}{2} \\end{bmatrix}$$R_y(\\theta)|\\psi\\rangle = (\\alpha\\cos\\frac{\\theta}{2} - \\beta\\sin\\frac{\\theta}{2})|0\\rangle + (\\beta\\cos\\frac{\\theta}{2} + \\alpha\\sin\\frac{\\theta}{2})|1\\rangle$$R_y(\\theta)|0\\rangle = \\cos\\frac{\\theta}{2}|0\\rangle + \\sin\\frac{\\theta}{2}|1\\rangle \\\\\n", + "R_y(\\theta)|1\\rangle = \\cos\\frac{\\theta}{2}|1\\rangle - \\sin\\frac{\\theta}{2}|0\\rangle$Ry
$R_z(\\theta)$$\\begin{bmatrix} e^{-i\\theta/2} & 0 \\\\ 0 & e^{i\\theta/2} \\end{bmatrix}$$R_z(\\theta)|\\psi\\rangle = \\alpha e^{-i\\theta/2}|0\\rangle + \\beta e^{i\\theta/2}|1\\rangle$$R_z(\\theta)|0\\rangle = e^{-i\\theta/2}|0\\rangle \\\\\n", + "R_z(\\theta)|1\\rangle = e^{i\\theta/2}|1\\rangle$Rz
$R_1(\\theta)$$\\begin{bmatrix} 1 & 0 \\\\ 0 & e^{i\\theta} \\end{bmatrix}$$R_1(\\theta)|\\psi\\rangle = \\alpha|0\\rangle + \\beta e^{i\\theta}|1\\rangle$$R_1(\\theta)|0\\rangle = |0\\rangle \\\\\n", + "R_1(\\theta)|1\\rangle = e^{i\\theta}|1\\rangle$R1
\n", + "\n", + "You have already encountered some special cases of the phase gate. $T = R_1(\\frac{\\pi}{4})$, $S = R_1(\\frac{\\pi}{2})$, and $Z = R_1(\\pi)$. In addition, the phase gate is very closely related to the $Z$ rotation gate. Applying a phase gate is equivalent to applying the $Z$ rotation gate, and then applying a **global phase** to the entire qubit: $R_1(\\theta) = e^{i\\theta/2}R_z(\\theta)$.\n", + "\n", + "In addition, the rotation gates are very closely related to their respective Pauli gates. Notice: $X = iR_x(\\pi)$, $Y = iR_y(\\pi)$, and $Z = iR_z(\\pi)$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 6: Preparing a rotated state\n", + "\n", + "**Inputs:**\n", + "\n", + "1. A non-negative real number $\\alpha$.\n", + "2. A non-negative real number $\\beta = \\sqrt{1 - \\alpha^2}$.\n", + "3. A qubit in state $|0\\rangle$.\n", + "\n", + "**Goal:** Use a rotation gate to transform the qubit into state $\\alpha|0\\rangle -i\\beta|1\\rangle$\n", + "\n", + "> You will probably need functions from the [Math](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.math?view=qsharp-preview) namespace, specifically [ArcTan2](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.math.arctan2?view=qsharp-preview).\n", + "> \n", + "> You can assign variables in Q# by using the `let` keyword: `let num = 3;` or `let result = Function(input);`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T6_RotatedState_Test\n", + "\n", + "open Microsoft.Quantum.Math;\n", + "\n", + "operation RotatedState (alpha : Double, beta : Double, q : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 7**: Preparing an arbitrary state\n", + "\n", + "**Inputs:**\n", + "\n", + "1. A non-negative real number $\\alpha$.\n", + "2. A non-negative real number $\\beta = \\sqrt{1 - \\alpha^2}$.\n", + "3. A real number $\\theta$.\n", + "4. A qubit in state $|0\\rangle$.\n", + "\n", + "**Goal:** Transform the qubit into state $\\alpha|0\\rangle + e^{i\\theta}\\beta|1\\rangle$.\n", + "\n", + "> Because only the relative amplitudes and relative phase have any physical meaning, this allows us to prepare any quantum state we want to." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T7_ArbitraryState_Test\n", + "\n", + "open Microsoft.Quantum.Math;\n", + "\n", + "operation ArbitraryState (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj {\n", + " // ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "You should now be familiar enough with quantum computing to complete the first part of the [Basic Gates Kata](../../BasicGates/BasicGates.ipynb). When you are ready, you can proceed to the [Multi-qubit Systems](../MultiQubitSystems/MultiQubitSystems.ipynb) tutorial, and then the [Multi-qubit Gates](../MultiQubitGates/MultiQubitGates.ipynb) tutorial." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Q#", + "language": "qsharp", + "name": "iqsharp" + }, + "language_info": { + "file_extension": ".qs", + "mimetype": "text/x-qsharp", + "name": "qsharp", + "version": "0.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/SingleQubitGates/SingleQubitGates.sln b/tutorials/SingleQubitGates/SingleQubitGates.sln new file mode 100644 index 00000000000..bf68cab1b1d --- /dev/null +++ b/tutorials/SingleQubitGates/SingleQubitGates.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29306.81 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SingleQubitGates", "SingleQubitGates.csproj", "{D00121A5-6A8E-474B-A0CA-BC01C4488F43}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D00121A5-6A8E-474B-A0CA-BC01C4488F43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D00121A5-6A8E-474B-A0CA-BC01C4488F43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D00121A5-6A8E-474B-A0CA-BC01C4488F43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D00121A5-6A8E-474B-A0CA-BC01C4488F43}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E500D02C-E57E-4624-A303-D368FB4A259F} + EndGlobalSection +EndGlobal diff --git a/tutorials/SingleQubitGates/Tasks.qs b/tutorials/SingleQubitGates/Tasks.qs new file mode 100644 index 00000000000..cf7e4bbebe6 --- /dev/null +++ b/tutorials/SingleQubitGates/Tasks.qs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////// +// This file is a back end for the tasks in Basic Gates tutorial. +// We strongly recommend to use the Notebook version of the tutorial +// to enjoy the full experience. +////////////////////////////////////////////////////////////////// + +namespace Quantum.Kata.SingleQubitGates { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Math; + + // Exercise 1. + operation ApplyY (q : Qubit) : Unit is Adj { + // ... + + } + + // Exercise 2. + operation ApplyZ (q : Qubit) : Unit is Adj { + // ... + + } + + // Exercise 3. + operation ZeroFlip (q : Qubit) : Unit is Adj+Ctl { + // ... + + } + + // Exercise 4. + operation PrepareMinus (q : Qubit) : Unit is Adj { + // ... + + } + + // Exercise 5. + operation ThreePiPhase (q : Qubit) : Unit is Adj+Ctl { + // ... + + } + + operation RotatedState (alpha : Double, beta : Double, q : Qubit) : Unit is Adj { + // ... + + } + + // Exercise 7. + operation ArbitraryState (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj { + // ... + + } +} \ No newline at end of file diff --git a/tutorials/SingleQubitGates/TestSuiteRunner.cs b/tutorials/SingleQubitGates/TestSuiteRunner.cs new file mode 100644 index 00000000000..7170ed7620a --- /dev/null +++ b/tutorials/SingleQubitGates/TestSuiteRunner.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////////// +// This file contains parts of the testing harness. +// You should not modify anything in this file. +// The tasks themselves can be found in Tasks.qs file. +////////////////////////////////////////////////////////////////////// + +using System.Diagnostics; + +using Microsoft.Quantum.Simulation.XUnit; +using Microsoft.Quantum.Simulation.Simulators; + +using Xunit.Abstractions; + +namespace Quantum.Kata.SingleQubitGates +{ + public class TestSuiteRunner + { + private readonly ITestOutputHelper output; + + public TestSuiteRunner(ITestOutputHelper output) + { + this.output = output; + } + + /// + /// This driver will run all Q# tests (operations named "...Test") + /// that belong to namespace Quantum.Kata.SingleQubitGates. + /// + [OperationDriver(TestNamespace = "Quantum.Kata.SingleQubitGates")] + public void TestTarget(TestOperation op) + { + using (var sim = new QuantumSimulator()) + { + // OnLog defines action(s) performed when Q# test calls function Message + sim.OnLog += (msg) => { output.WriteLine(msg); }; + sim.OnLog += (msg) => { Debug.WriteLine(msg); }; + op.TestOperationRunner(sim); + } + } + } +} diff --git a/tutorials/SingleQubitGates/Tests.qs b/tutorials/SingleQubitGates/Tests.qs new file mode 100644 index 00000000000..fde6dd43632 --- /dev/null +++ b/tutorials/SingleQubitGates/Tests.qs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////////// +// This file contains testing harness for all tasks. +// You should not modify anything in this file. +////////////////////////////////////////////////////////////////////// + +namespace Quantum.Kata.SingleQubitGates { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Math; + open Microsoft.Quantum.Convert; + + // The tests are written to test controlled versions of operations instead of plain ones. + // This is done to verify that the tasks don't add a global phase to the implementations. + // Global phase is not relevant physically, but it can be very confusing for a beginner to consider R1 vs Rz, + // so the tests use controlled version of the operations which converts the global phase into a relative phase + // and makes it possible to detect. + + // ------------------------------------------------------ + // Helper wrapper to represent controlled variant of operation on one qubit + // as an operation on an array of two qubits + operation ControlledArrayWrapperOperation (op : (Qubit => Unit is Adj+Ctl), qs : Qubit[]) : Unit is Adj+Ctl { + Controlled op([qs[0]], qs[1]); + } + + operation ArrayWrapperOperation (op : (Qubit => Unit is Adj), qs : Qubit[]) : Unit is Adj { + op(qs[0]); + } + + // Exercise 1. + operation T1_ApplyY_Test () : Unit { + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(ApplyY, _), ArrayWrapperOperation(Y, _)); + } + + // Exercise 2. + operation T2_ApplyZ_Test () : Unit { + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(ApplyZ, _), ArrayWrapperOperation(Z, _)); + } + + // Exercise 3. + operation T3_ZeroFlip_Test () : Unit { + AssertOperationsEqualReferenced(2, ControlledArrayWrapperOperation(ZeroFlip, _), ControlledArrayWrapperOperation(ZeroFlip_Reference, _)); + } + + // Exercise 4. + operation T4_PrepareMinus_Test () : Unit { + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(PrepareMinus, _), ArrayWrapperOperation(PrepareMinus_Reference, _)); + } + + // Exercise 5. + operation T5_ThreePiPhase_Test () : Unit { + AssertOperationsEqualReferenced(2, ArrayWrapperOperation(ThreePiPhase, _), ArrayWrapperOperation(ThreePiPhase_Reference, _)); + } + + operation T6_RotatedState_Test () : Unit { + for (i in 0 .. 10) { + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(RotatedState(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), _), _), ArrayWrapperOperation(RotatedState_Reference(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), _), _)); + } + } + + // Exercise 7. + operation T7_ArbitraryState_Test () : Unit { + for (i in 0 .. 10) { + for (j in 0 .. 10) { + AssertOperationsEqualReferenced(1, ArrayWrapperOperation(ArbitraryState(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), IntAsDouble(j), _), _), ArrayWrapperOperation(ArbitraryState_Reference(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), IntAsDouble(j), _), _)); + } + } + } +} \ No newline at end of file From 3f8207d9526b670e39dd2e78b49aab7084060b9f Mon Sep 17 00:00:00 2001 From: Artem Astapchuk Date: Fri, 4 Oct 2019 18:15:38 -0700 Subject: [PATCH 4/8] Removed RCA Kata --- RippleCarryAdder/README.md | 21 - RippleCarryAdder/ReferenceImplementation.qs | 290 -------- RippleCarryAdder/RippleCarryAdder.csproj | 29 - RippleCarryAdder/RippleCarryAdder.ipynb | 748 -------------------- RippleCarryAdder/RippleCarryAdder.sln | 31 - RippleCarryAdder/Tasks.qs | 343 --------- RippleCarryAdder/TestSuiteRunner.cs | 43 -- RippleCarryAdder/Tests.qs | 391 ---------- 8 files changed, 1896 deletions(-) delete mode 100644 RippleCarryAdder/README.md delete mode 100644 RippleCarryAdder/ReferenceImplementation.qs delete mode 100644 RippleCarryAdder/RippleCarryAdder.csproj delete mode 100644 RippleCarryAdder/RippleCarryAdder.ipynb delete mode 100644 RippleCarryAdder/RippleCarryAdder.sln delete mode 100644 RippleCarryAdder/Tasks.qs delete mode 100644 RippleCarryAdder/TestSuiteRunner.cs delete mode 100644 RippleCarryAdder/Tests.qs diff --git a/RippleCarryAdder/README.md b/RippleCarryAdder/README.md deleted file mode 100644 index 7e668b41e22..00000000000 --- a/RippleCarryAdder/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Welcome! - -This kata explores ripple-carry addition on a quantum computer. - -The simplest quantum adder closely mirrors its classical counterpart, -using the same basic components and the same algorithm. -A more complex version covered in part 3 of the kata uses a somewhat different algorithm -to reduce the number of ancillary qubits needed. - -#### Theory - -* [Classical binary adder on Wikipedia](https://en.wikipedia.org/wiki/Adder_(electronics)). -* [Paper on quantum binary addition](https://arxiv.org/pdf/quant-ph/0008033.pdf) by Thomas G. Draper - part 2 explains how to adapt the classical adder to a quantum environment. -* [Paper on improved ripple carry addition](https://arxiv.org/pdf/quant-ph/0410184.pdf) by Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, and David Petrie Moulton - explains the principle behind the adder in part 3 of the kata. - -#### Q# - -It is recommended to complete the [BasicGates kata](./../BasicGates/) before this one to get familiar with the basic gates used in quantum computing. -The list of basic gates available in Q# can be found at [Microsoft.Quantum.Intrinsic](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic). - -For the syntax of flow control statements in Q#, see [the Q# documentation](https://docs.microsoft.com/quantum/language/statements#control-flow). \ No newline at end of file diff --git a/RippleCarryAdder/ReferenceImplementation.qs b/RippleCarryAdder/ReferenceImplementation.qs deleted file mode 100644 index 5802bc173a9..00000000000 --- a/RippleCarryAdder/ReferenceImplementation.qs +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -////////////////////////////////////////////////////////////////////// -// This file contains reference solutions to all tasks. -// The tasks themselves can be found in Tasks.qs file. -// We recommend that you try to solve the tasks yourself first, -// but feel free to look up the solution if you get stuck. -////////////////////////////////////////////////////////////////////// - -namespace Quantum.Kata.RippleCarryAdder { - - open Microsoft.Quantum.Measurement; - open Microsoft.Quantum.Arrays; - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Canon; - - - ////////////////////////////////////////////////////////////////// - // Part I. Simple adder outputting to empty Qubits - ////////////////////////////////////////////////////////////////// - - // Task 1.1. Summation of two bits - operation LowestBitSum_Reference (a : Qubit, b : Qubit, sum : Qubit) : Unit is Adj { - CNOT(a, sum); - CNOT(b, sum); - } - - - // Task 1.2. Carry of two bits - operation LowestBitCarry_Reference (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { - CCNOT(a, b, carry); - } - - - // Task 1.3. One bit adder - operation OneBitAdder_Reference (a : Qubit, b : Qubit, sum : Qubit, carry : Qubit) : Unit is Adj { - LowestBitSum_Reference(a, b, sum); - LowestBitCarry_Reference(a, b, carry); - } - - - // Task 1.4. Summation of 3 bits - operation HighBitSum_Reference (a : Qubit, b : Qubit, carryin : Qubit, sum : Qubit) : Unit is Adj { - CNOT(a, sum); - CNOT(b, sum); - CNOT(carryin, sum); - } - - - // Task 1.5. Carry of 3 bits - operation HighBitCarry_Reference (a : Qubit, b : Qubit, carryin : Qubit, carryout : Qubit) : Unit is Adj { - CCNOT(a, b, carryout); - CCNOT(a, carryin, carryout); - CCNOT(b, carryin, carryout); - } - - // Task 1.6. Two bit adder - operation TwoBitAdder_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { - using (internalCarry = Qubit()) { - LowestBitSum_Reference(a[0], b[0], sum[0]); - LowestBitCarry_Reference(a[0], b[0], internalCarry); - - HighBitSum_Reference(a[1], b[1], internalCarry, sum[1]); - HighBitCarry_Reference(a[1], b[1], internalCarry, carry); - - // Clean up ancillary qubit - Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarry); - } - } - - - // Task 1.7. N-bit adder - operation ArbitraryAdder_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { - let N = Length(a); - if (N == 1) { - LowestBitSum_Reference(a[0], b[0], sum[0]); - LowestBitCarry_Reference(a[0], b[0], carry); - } - else { - using (internalCarries = Qubit[N-1]) { - LowestBitSum_Reference(a[0], b[0], sum[0]); - LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); - - for (i in 1 .. N-2) { - HighBitSum_Reference(a[i], b[i], internalCarries[i-1], sum[i]); - HighBitCarry_Reference(a[i], b[i], internalCarries[i-1], internalCarries[i]); - } - - HighBitSum_Reference(a[N-1], b[N-1], internalCarries[N-2], sum[N-1]); - HighBitCarry_Reference(a[N-1], b[N-1], internalCarries[N-2], carry); - - // Clean up the ancillary qubits - for (i in N-2 .. -1 .. 1) { - Adjoint HighBitCarry_Reference(a[i], b[i], internalCarries[i-1], internalCarries[i]); - } - Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); - } - } - } - - - // A slightly simpler solution - more uniform, but slightly slower, and requires one extra qubit - operation ArbitraryAdder_Simplified_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { - let N = Length(a); - using (internalCarries = Qubit[N]) { - let carries = internalCarries + [carry]; - for (i in 0 .. N-1) { - HighBitSum_Reference(a[i], b[i], carries[i], sum[i]); - HighBitCarry_Reference(a[i], b[i], carries[i], carries[i+1]); - } - - // Clean up the ancilla - for (i in N-2 .. -1 .. 0) { - Adjoint HighBitCarry_Reference(a[i], b[i], carries[i], carries[i+1]); - } - } - } - - - // The challenge solution - the sum qubits are used to store the carry bits, and the sum is calculated as they get cleaned up - operation ArbitraryAdder_Challenge_Reference (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { - let N = Length(a); - - // Calculate carry bits - LowestBitCarry_Reference(a[0], b[0], sum[0]); - for (i in 1 .. N-1) { - HighBitCarry_Reference(a[i], b[i], sum[i - 1], sum[i]); - } - CNOT(sum[N-1], carry); - - // Clean sum qubits and compute sum - for (i in N-1 .. -1 .. 1) { - Adjoint HighBitCarry_Reference(a[i], b[i], sum[i - 1], sum[i]); - HighBitSum_Reference(a[i], b[i], sum[i - 1], sum[i]); - } - Adjoint LowestBitCarry_Reference(a[0], b[0], sum[0]); - LowestBitSum_Reference(a[0], b[0], sum[0]); - } - - - ////////////////////////////////////////////////////////////////// - // Part II. Simple in-place adder - ////////////////////////////////////////////////////////////////// - - // Task 2.1. In-place summation of two bits - operation LowestBitSumInPlace_Reference (a : Qubit, b : Qubit) : Unit is Adj { - CNOT(a, b); - } - - - // Task 2.2. In-place one bit adder - operation OneBitAdderInPlace_Reference (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { - LowestBitCarry_Reference(a, b, carry); - LowestBitSumInPlace_Reference(a, b); - } - - - // Task 2.3. In-place summation of three bits - operation HighBitSumInPlace_Reference (a : Qubit, b : Qubit, carryin : Qubit) : Unit is Adj { - CNOT(a, b); - CNOT(carryin, b); - } - - - // Task 2.4. In-place two bit adder - operation TwoBitAdderInPlace_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - using (internalCarry = Qubit()) { - // Set up the carry bits - LowestBitCarry_Reference(a[0], b[0], internalCarry); - HighBitCarry_Reference(a[1], b[1], internalCarry, carry); - - // Calculate sums and clean up the ancilla - HighBitSumInPlace_Reference(a[1], b[1], internalCarry); - Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarry); - LowestBitSumInPlace_Reference(a[0], b[0]); - } - } - - - // Task 2.5. In-place N-bit adder - operation ArbitraryAdderInPlace_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - let N = Length(a); - - using (internalCarries = Qubit[N]) { - // Set up the carry bits - LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); - for (i in 1 .. N-1) { - HighBitCarry_Reference(a[i], b[i], internalCarries[i - 1], internalCarries[i]); - } - CNOT(internalCarries[N-1], carry); - - // Clean up carry bits and compute sum - for (i in N-1 .. -1 .. 1) { - Adjoint HighBitCarry_Reference(a[i], b[i], internalCarries[i - 1], internalCarries[i]); - HighBitSumInPlace_Reference(a[i], b[i], internalCarries[i - 1]); - } - Adjoint LowestBitCarry_Reference(a[0], b[0], internalCarries[0]); - LowestBitSumInPlace_Reference(a[0], b[0]); - } - } - - - ////////////////////////////////////////////////////////////////// - // Part III*. Improved in-place adder - ////////////////////////////////////////////////////////////////// - - // Task 3.1. Majority gate - operation Majority_Reference (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { - CNOT(a, b); - CNOT(a, c); - CCNOT(b, c, a); - } - - - // Task 3.2. UnMajority and Add gate - operation UnMajorityAdd_Reference (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { - CCNOT(b, c, a); - CNOT(a, c); - CNOT(c, b); - } - - - // Task 3.3. One bit majority-UMA adder - operation OneBitMajUmaAdder_Reference (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { - using (tempCarry = Qubit()) { - Majority_Reference(a, b, tempCarry); - CNOT(a, carry); - UnMajorityAdd_Reference(a, b, tempCarry); - } - } - - - // Task 3.4. Two bit majority-UMA adder - operation TwoBitMajUmaAdder_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - using (tempCarry = Qubit()) { - // We only need the extra qubit so we have 3 to pass to the majority gate for the lowest bits - Majority_Reference(a[0], b[0], tempCarry); - Majority_Reference(a[1], b[1], a[0]); - - // Save last carry bit - CNOT(a[1], carry); - - // Restore inputs/ancilla and compute sum - UnMajorityAdd_Reference(a[1], b[1], a[0]); - UnMajorityAdd_Reference(a[0], b[0], tempCarry); - } - } - - - // Task 3.5. N-bit majority-UMA adder - operation ArbitraryMajUmaAdder_Reference (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - let N = Length(a); - - using (tempCarry = Qubit()) { - let carries = [tempCarry] + a; - - // Compute carry bits - for (i in 0 .. N-1) { - Majority_Reference(a[i], b[i], carries[i]); - } - - // Save last carry bit - CNOT(carries[N], carry); - - // Restore inputs and ancilla, compute sum - for (i in N-1 .. -1 .. 0) { - UnMajorityAdd_Reference(a[i], b[i], carries[i]); - } - } - } - - ////////////////////////////////////////////////////////////////// - // Part IV*. In-place subtractor - ////////////////////////////////////////////////////////////////// - - // Task 4.1. N-bit subtractor - operation Subtractor_Reference (a : Qubit[], b : Qubit[], borrow : Qubit) : Unit is Adj { - // transform b into 2ᴺ - 1 - b - ApplyToEachA(X, b); - - // compute (2ᴺ - 1 - b) + a = 2ᴺ - 1 - (b - a) using existing adder - // if this produced a carry, then (2ᴺ - 1 - (b - a)) > 2ᴺ - 1, so (b - a) < 0, and we need a borrow - // this means we can use the carry qubit from the addition as the borrow qubit - ArbitraryMajUmaAdder_Reference(a, b, borrow); - - // transform 2ᴺ - 1 - (b - a) into b - a - ApplyToEachA(X, b); - } -} diff --git a/RippleCarryAdder/RippleCarryAdder.csproj b/RippleCarryAdder/RippleCarryAdder.csproj deleted file mode 100644 index b16bd488f4f..00000000000 --- a/RippleCarryAdder/RippleCarryAdder.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - netcoreapp2.1 - x64 - false - Quantum.Kata.RippleCarryAdder - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - diff --git a/RippleCarryAdder/RippleCarryAdder.ipynb b/RippleCarryAdder/RippleCarryAdder.ipynb deleted file mode 100644 index 5854bb402af..00000000000 --- a/RippleCarryAdder/RippleCarryAdder.ipynb +++ /dev/null @@ -1,748 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Ripple Carry Adder Kata\n", - "\n", - "The **Ripple Carry Adder** quantum kata is a series of exercises designed\n", - "to get you familiar with ripple carry addition on a quantum computer,\n", - "walking you through the steps to build two different ripple-carry adders.\n", - "It covers the following topics:\n", - "* Adapting a classical adder to a quantum environment\n", - "* Modifying the adder to re-use qubits\n", - "* An alternate, simplified quantum adder\n", - "* A simple subtractor\n", - "\n", - "It is recommended to complete the [BasicGates kata](./../BasicGates/BasicGates.ipynb) before this one to get familiar with the basic gates used in quantum computing. The list of basic gates available in Q# can be found at [Microsoft.Quantum.Intrinsic](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic). For the syntax of flow control statements in Q#, see [the Q# documentation](https://docs.microsoft.com/quantum/language/statements#control-flow).\n", - "\n", - "Each task is wrapped in one operation preceded by the description of the task.\n", - "Your goal is to fill in the blank (marked with // ... comments)\n", - "with some Q# code that solves the task. To verify your answer, run the cell using Ctrl/⌘+Enter.\n", - "\n", - "Within each section, tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To begin, first prepare this notebook for execution (if you skip the first step, you'll get \"Syntax does not match any known patterns\" error when you try to execute Q# code in the next cells; if you skip the second step, you'll get \"Invalid kata name\" error):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%package Microsoft.Quantum.Katas::0.9.1908.2906" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "> The package versions in the output of the cell above should always match. If you are running the Notebooks locally and the versions do not match, please install the IQ# version that matches the version of the `Microsoft.Quantum.Katas` package.\n", - ">
\n", - "> How to install the right IQ# version\n", - "> For example, if the version of `Microsoft.Quantum.Katas` package above is 0.1.2.3, the installation steps are as follows:\n", - ">\n", - "> 1. Stop the kernel.\n", - "> 2. Uninstall the existing version of IQ#:\n", - "> dotnet tool uninstall microsoft.quantum.iqsharp -g\n", - "> 3. Install the matching version:\n", - "> dotnet tool install microsoft.quantum.iqsharp -g --version 0.1.2.3\n", - "> 4. Reinstall the kernel:\n", - "> dotnet iqsharp install\n", - "> 5. Restart the Notebook.\n", - ">
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%workspace reload" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part I. Simple Adder Outputting to Empty Qubits\n", - "\n", - "\n", - "### Theory\n", - "\n", - "* [Classical binary adder on Wikipedia](https://en.wikipedia.org/wiki/Adder_(electronics)).\n", - "* [Paper on quantum binary addition](https://arxiv.org/pdf/quant-ph/0008033.pdf) by Thomas G. Draper - part 2 explains how to adapt the classical adder to a quantum environment." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 1.1. Summation of two bits\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " \n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " \n", - " 3. qubit `sum` in state $|0\\rangle$\n", - "\n", - "**Goal:** Transform the `sum` qubit into the lowest bit of the binary sum of $\\phi$ and $\\psi$.\n", - "\n", - "* $|0\\rangle + |0\\rangle \\to |0\\rangle$\n", - "* $|0\\rangle + |1\\rangle \\to |1\\rangle$\n", - "* $|1\\rangle + |0\\rangle \\to |1\\rangle$\n", - "* $|1\\rangle + |1\\rangle \\to |0\\rangle$\n", - "\n", - "Any superposition should map appropriately. For example:\n", - "\n", - "$|+\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle) \\\\\n", - "|-\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle - |1\\rangle) \\\\\n", - "|+\\rangle \\otimes |-\\rangle \\otimes |0\\rangle \\to \\frac{1}{2}(|000\\rangle + |101\\rangle - |011\\rangle - |110\\rangle)$" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T11_LowestBitSum_Test\n", - "\n", - "operation LowestBitSum (a : Qubit, b : Qubit, sum : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 1.2. Carry of two bits\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goal:** Set the `carry` qubit to $|1\\rangle$ if the binary sum of $\\phi$ and $\\psi$ produces a carry.\n", - "\n", - "* $|0\\rangle$ and $|0\\rangle \\to |0\\rangle$\n", - "* $|0\\rangle$ and $|1\\rangle \\to |0\\rangle$\n", - "* $|1\\rangle$ and $|0\\rangle \\to |0\\rangle$\n", - "* $|1\\rangle$ and $|1\\rangle \\to |1\\rangle$\n", - "\n", - "Any superposition should map appropriately. For example:\n", - "\n", - "$|+\\rangle \\otimes |-\\rangle \\otimes |0\\rangle \\to \\frac{1}{2}(|000\\rangle + |100\\rangle - |010\\rangle - |111\\rangle)$" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T12_LowestBitCarry_Test\n", - "\n", - "operation LowestBitCarry (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 1.3. One bit adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. two qubits `sum` and `carry` in state $|0\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform the `sum` qubit into the lowest bit of the binary sum of $\\phi$ and $\\psi$.\n", - "* Transform the `carry` qubit into the carry bit produced by that sum." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T13_OneBitAdder_Test\n", - "\n", - "operation OneBitAdder (a : Qubit, b : Qubit, sum : Qubit, carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 1.4. Summation of 3 bits\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carryin` in an arbitrary state $|\\omega\\rangle$\n", - " 4. qubit `carryout` in state $|0\\rangle$\n", - "\n", - "**Goal:** Transform the `sum` qubit into the lowest bit of the binary sum of $\\phi$ and $\\psi$ and $\\omega$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T14_HighBitSum_Test\n", - "\n", - "operation HighBitSum (a : Qubit, b : Qubit, carryin : Qubit, sum : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 1.5. Carry of 3 bits\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carryin` in an arbitrary state $|\\omega\\rangle$\n", - " 4. qubit `carryout` in state $|0\\rangle$\n", - "\n", - "**Goal:** Transform the `carryout` qubit into the carry bit produced by the sum of $\\phi$ and $\\psi$ and $\\omega$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T15_HighBitCarry_Test\n", - "\n", - "operation HighBitCarry (a : Qubit, b : Qubit, carryin : Qubit, carryout : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 1.6. Two bit adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. two qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. two qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. two qubit register `sum` in state $|00\\rangle$\n", - " 4. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform the `sum` register into the binary sum (little-endian) of $\\phi$ and $\\psi$.\n", - "* Transform the `carry` qubit into the carry bit produced by that sum.\n", - "\n", - "> All registers in this kata are in **little-endian** order.\n", - "> This means that they have the least significant bit first, followed by the next least significant, and so on.\n", - ">\n", - "> In this exercise, for example, $1$ would be represented as $|10\\rangle$, and $2$ as $|01\\rangle$.\n", - ">\n", - "> The sum of $|10\\rangle$ and $|11\\rangle$ would be $|001\\rangle$, with the last qubit being the carry qubit.\n", - "\n", - "
\n", - "
\n", - " Need a hint? Click here\n", - " Don't forget that you can allocate extra qubits.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T16_TwoBitAdder_Test\n", - "\n", - "operation TwoBitAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 1.7. N-bit adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. $N$ qubit register `sum` in state $|0...0\\rangle$\n", - " 4. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform the `sum` register into the binary sum (little-engian) of $\\phi$ and $\\psi$.\n", - "* Transform the `carry` qubit into the carry bit produced by that sum.\n", - "\n", - "**Challenge:**\n", - "\n", - "Can you do this without requesting extra qubits?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T17_ArbitraryAdder_Test\n", - "\n", - "operation ArbitraryAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part II. Simple In-place Adder\n", - "\n", - "The adder from the previous section requires empty qubits to accept the result.\n", - "This section adapts the previous adder to instead mutate the inputs.\n", - "That is, one of the inputs is re-used as the output." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 2.1. In-place summation of two bits\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform qubit `b` into the lowest bit of the sum of $\\phi$ and $\\psi$.\n", - "* Leave qubit `a` unchanged.\n", - "\n", - "> Would we be able to re-use one of the inputs to store the carry output as well? Why or why not?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T21_LowestBitSumInPlace_Test\n", - "\n", - "operation LowestBitSumInPlace (a : Qubit, b : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 2.2. In-place one bit adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform the `carry` qubit into the carry bit from the addition of $\\phi$ and $\\psi$.\n", - "* Transform qubit `b` into the lowest bit of $\\phi + \\psi$.\n", - "* Leave qubit `a` unchanged.\n", - "\n", - "
\n", - "
\n", - " Need a hint? Click here\n", - " Think very carefully about the order in which you apply the operations.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T22_OneBitAdderInPlace_Test\n", - "\n", - "operation OneBitAdderInPlace (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 2.3. In-place summation of three bits\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carryin` in an arbitrary state $|\\omega\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform qubit `b` into the lowest bit from the sum $\\phi + \\psi + \\omega$.\n", - "* Leave qubits `a` and `carryin` unchanged." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T23_HighBitSumInPlace_Test\n", - "\n", - "operation HighBitSumInPlace (a : Qubit, b : Qubit, carryin : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 2.4. In-place two bit adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. two qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. two qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform register `b` into the state $|\\phi + \\psi\\rangle$.\n", - "* Transform the `carry` qubit into the carry bit from the addition.\n", - "* Leave register `a` unchanged." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T24_TwoBitAdderInPlace_Test\n", - "\n", - "operation TwoBitAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 2.5. In-place N-bit adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goals:**\n", - "\n", - "* Transform register `b` into the state $|\\phi + \\psi\\rangle$.\n", - "* Transform the `carry` qubit into the carry bit from the addition.\n", - "* Leave register `a` unchanged." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T25_ArbitraryAdderInPlace_Test\n", - "\n", - "operation ArbitraryAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part III*. Improved In-place Adder\n", - "\n", - "The in-place adder doesn't require quite as many qubits for the inputs and outputs, but it still requires an array of extra (\"ancillary\") qubits to perform the calculation.\n", - "\n", - "A relatively recent algorithm allows you to perform the same calculation using only one additional qubit.\n", - "\n", - "### Theory\n", - "\n", - "* [Paper on improved ripple carry addition](https://arxiv.org/pdf/quant-ph/0410184.pdf) by Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, and David Petrie Moulton." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 3.1. Majority gate\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `c` in an arbitrary state $|\\omega\\rangle$\n", - "\n", - "**Goal:** Construct the \"in-place majority\" gate:\n", - "\n", - "* Transform qubit `a` into the carry bit from the sum $\\phi + \\psi + \\omega$.\n", - "* Transform qubit `b` into $|\\phi + \\psi\\rangle$.\n", - "* Transform qubit `c` into $|\\phi + \\omega\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T31_Majority_Test\n", - "\n", - "operation Majority (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 3.2. UnMajority and Add gate\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. qubit `a` storing the carry bit from the sum $\\phi + \\psi + \\omega$\n", - " 2. qubit `b` in state $|\\phi + \\psi\\rangle$\n", - " 3. qubit `c` in state $|\\phi + \\omega\\rangle$\n", - "\n", - "**Goal:** Construct the \"un-majority and add\", or \"UMA\" gate:\n", - "\n", - "* Restore qubit `a` into state $|\\phi\\rangle$.\n", - "* Transform qubit `b` into state $|\\phi + \\psi + \\omega\\rangle$.\n", - "* Restore qubit `c` into state $|\\omega\\rangle$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T32_UnMajorityAdd_Test\n", - "\n", - "operation UnMajorityAdd (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 3.3. One bit Majority-UMA adder\n", - "\n", - "**Inputs:**\n", - "\n", - "1. qubit `a` in an arbitrary state $|\\phi\\rangle$\n", - "2. qubit `b` in an arbitrary state $|\\psi\\rangle$\n", - "3. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goal:** Construct a one bit binary adder from task 2.2 using Majority and UMA gates.\n", - "\n", - "
\n", - "
\n", - " Need a hint? Click here\n", - " Allocate an extra qubit to pass as qubit c. It's less efficient here, but it will be helpful for the next tasks.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T33_OneBitMajUmaAdder_Test\n", - "\n", - "operation OneBitMajUmaAdder (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 3.4. Two bit Majority-UMA adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. two qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. two qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goal:** Construct a two bit binary adder from task 2.4 using Majority and UMA gates.\n", - "\n", - "
\n", - "
\n", - " Need a hint? Click here\n", - " Think very carefully about which qubits you need to pass to the two gates.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T34_TwoBitMajUmaAdder_Test\n", - "\n", - "operation TwoBitMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 3.5. N-bit Majority-UMA adder\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `carry` in state $|0\\rangle$\n", - "\n", - "**Goal:** Construct an N-bit binary adder from task 2.5 using only one ancillary qubit." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T35_ArbitraryMajUmaAdder_Test\n", - "\n", - "operation ArbitraryMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Part IV*. In-place Subtractor\n", - "\n", - "Subtracting a number is the same operation as adding a negative number.\n", - "As such, it's pretty easy to adapt the adder we just built to act as a subtractor." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Task 4.1. N-bit Subtractor\n", - "\n", - "**Inputs:**\n", - "\n", - " 1. $N$ qubit register `a` in an arbitrary state $|\\phi\\rangle$\n", - " 2. $N$ qubit register `b` in an arbitrary state $|\\psi\\rangle$\n", - " 3. qubit `borrow` in state $|0\\rangle$\n", - "\n", - "**Goal:** Construct an N-bit binary subtractor.\n", - "\n", - "* Transform register `b` into the state $|\\psi - \\phi\\rangle$.\n", - "* Set qubit `borrow` to $|1\\rangle$ if that subtraction requires a borrow.\n", - "* Leave register `a` unchanged.\n", - "\n", - "
\n", - "
\n", - " Need a hint? Click here\n", - " Use the adder you already built. Experiment with inverting registers before and after the addition.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%kata T41_Subtractor_Test\n", - "\n", - "operation Subtractor (a : Qubit[], b : Qubit[], borrow : Qubit) : Unit is Adj {\n", - " // ...\n", - "}" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Q#", - "language": "qsharp", - "name": "iqsharp" - }, - "language_info": { - "file_extension": ".qs", - "mimetype": "text/x-qsharp", - "name": "qsharp", - "version": "0.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/RippleCarryAdder/RippleCarryAdder.sln b/RippleCarryAdder/RippleCarryAdder.sln deleted file mode 100644 index 36c559a2247..00000000000 --- a/RippleCarryAdder/RippleCarryAdder.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RippleCarryAdder", "RippleCarryAdder.csproj", "{6CE3F3C4-4681-41C8-BB14-964B21BA287E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "..\utilities\Common\Common.csproj", "{146E58EC-CED9-4904-A4CE-2F7B70BD05BE}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6CE3F3C4-4681-41C8-BB14-964B21BA287E}.Release|Any CPU.Build.0 = Release|Any CPU - {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {146E58EC-CED9-4904-A4CE-2F7B70BD05BE}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {41D1C147-2E2C-4C7C-B37D-13E80C93A0D5} - EndGlobalSection -EndGlobal diff --git a/RippleCarryAdder/Tasks.qs b/RippleCarryAdder/Tasks.qs deleted file mode 100644 index 96b074586bb..00000000000 --- a/RippleCarryAdder/Tasks.qs +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -namespace Quantum.Kata.RippleCarryAdder { - - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Diagnostics; - open Microsoft.Quantum.Convert; - open Microsoft.Quantum.Math; - - - ////////////////////////////////////////////////////////////////// - // Welcome! - ////////////////////////////////////////////////////////////////// - - // The "Ripple Carry Adder" quantum kata is a series of exercises designed - // to get you familiar with ripple carry addition on a quantum computer, - // walking you through the steps to build two different adders. - // It covers the following topics: - // - Adapting a classical adder to a quantum environment - // - Modifying the adder to re-use input qubits - // - An alternate, simplified quantum adder - // - A simple subtractor - - // Each task is wrapped in one operation preceded by the description of the task. - // Each task (except tasks in which you have to write a test) has a unit test associated with it, - // which initially fails. Your goal is to fill in the blank (marked with // ... comment) - // with some Q# code to make the failing test pass. - - // Within each section, tasks are given in approximate order of increasing difficulty; - // harder ones are marked with asterisks. - - - ////////////////////////////////////////////////////////////////// - // Part I. Simple adder outputting to empty Qubits - ////////////////////////////////////////////////////////////////// - - // This section adapts the classical binary adder to a quantum computer. - // It starts with simple sum and carry gates, and works up to an N-bit full adder. - - // Task 1.1. Summation of two bits - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "sum" in state |0⟩ - // Goal: transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ - // |0⟩ + |0⟩ → |0⟩ - // |0⟩ + |1⟩ → |1⟩ - // |1⟩ + |0⟩ → |1⟩ - // |1⟩ + |1⟩ → |0⟩ - // any superposition should map appropriately - // for example: - // |+⟩ = (|0⟩ + |1⟩) / sqrt(2) - // |-⟩ = (|0⟩ - |1⟩) / sqrt(2) - // |+⟩ ⨂ |-⟩ ⨂ |0⟩ → (|000⟩ + |101⟩ - |011⟩ - |110⟩) / 2 - operation LowestBitSum (a : Qubit, b : Qubit, sum : Qubit) : Unit is Adj { - // ... - - } - - - // Task 1.2. Carry of two bits - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "carry" in state |0⟩ - // Goal: set the "carry" qubit to |1⟩ if the binary sum of φ and ψ produces a carry - // |0⟩ and |0⟩ → |0⟩ - // |0⟩ and |1⟩ → |0⟩ - // |1⟩ and |0⟩ → |0⟩ - // |1⟩ and |1⟩ → |1⟩ - // any superposition should map appropriately - // for example: - // |+⟩ ⨂ |-⟩ ⨂ |0⟩ → (|000⟩ + |100⟩ - |010⟩ - |111⟩) / 2 - operation LowestBitCarry (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { - // ... - - } - - - // Task 1.3. One bit adder - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) two qubits "sum" and "carry" in state |0⟩ - // Goals: - // 1) transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ - // 2) transform the "carry" qubit into the carry bit produced by that sum - operation OneBitAdder (a : Qubit, b : Qubit, sum : Qubit, carry : Qubit) : Unit is Adj { - // ... - - } - - - // Task 1.4. Summation of 3 bits - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "carryin" in an arbitrary state |ω⟩ - // 4) qubit "sum" in state |0⟩ - // Goal: transform the "sum" qubit into the lowest bit of the binary sum of φ and ψ and ω - operation HighBitSum (a : Qubit, b : Qubit, carryin : Qubit, sum : Qubit) : Unit is Adj { - // ... - - } - - - // Task 1.5. Carry of 3 bits - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "carryin" in an arbitrary state |ω⟩ - // 4) qubit "carryout" in state |0⟩ - // Goal: transform the "carryout" qubit into the carry bit produced by the sum of φ and ψ and ω - operation HighBitCarry (a : Qubit, b : Qubit, carryin : Qubit, carryout : Qubit) : Unit is Adj { - // ... - - } - - - // Task 1.6. Two bit adder - // Inputs: - // 1) two qubit register "a" in an arbitrary state |φ⟩ - // 2) two qubit register "b" in an arbitrary state |ψ⟩ - // 3) two qubit register "sum" in state |00⟩ - // 4) qubit "carry" in state |0⟩ - // Goals: - // 1) transform the "sum" register into the binary sum of φ and ψ - // 2) transform the "carry" qubit into the carry bit produced by that sum - // Note: All qubit registers in this kata are in little-endian order. - // This means the least significant bit comes first, then the next least significant, and so on. - // In this exercise, for example, 1 would be represented as |10⟩, while 2 would be represented as |01⟩. - // The sum of |10⟩ and |11⟩ would be |001⟩, with the last qubit being the carry qubit. - operation TwoBitAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { - // Hint: don't forget that you can request extra qubits - - // ... - - } - - - // Task 1.7. N-bit adder - // Inputs: - // 1) N qubit register "a" in an arbitrary state |φ⟩ - // 2) N qubit register "b" in an arbitrary state |ψ⟩ - // 3) N qubit register "sum" in state |0...0⟩ - // 4) qubit "carry" in state |0⟩ - // Goals: - // 1) transform the "sum" register into the binary sum of φ and ψ - // 2) transform the "carry" qubit into the carry bit produced by that sum - // Challenge: can you do this without requesting extra qubits? - operation ArbitraryAdder (a : Qubit[], b : Qubit[], sum : Qubit[], carry : Qubit) : Unit is Adj { - // ... - - } - - - ////////////////////////////////////////////////////////////////// - // Part II. Simple in-place adder - ////////////////////////////////////////////////////////////////// - - // The adder from the previous section requires empty qubits to accept the result. - // This section adapts the previous adder to mutate the inputs. - // That is, one of the numerical inputs is re-used for the output. - - // Task 2.1. In-place summation of two bits - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // Goal: transform qubit "b" into the lowest bit of the sum of φ and ψ - // leave qubit "a" unchanged - // Something to think about: can we re-use one of the input bits for the carry as well? - operation LowestBitSumInPlace (a : Qubit, b : Qubit) : Unit is Adj { - // ... - - } - - - // Task 2.2. In-place one bit adder - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "carry" in state |0⟩ - // Goals: - // 1) transform the "carry" qubit into the carry bit from the addition of φ and ψ - // 2) transform qubit "b" into the lowest bit of φ + ψ - // leave qubit "a" unchanged - operation OneBitAdderInPlace (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { - // Hint: think carefully about the order of operations - - // ... - - } - - - // Task 2.3. In-place summation of three bits - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "carryin" in an arbitrary state |ω⟩ - // Goal: transform qubit "b" into the lowest bit from the addition of φ and ψ and ω - // leave qubits "a" and "carryin" unchanged - operation HighBitSumInPlace (a : Qubit, b : Qubit, carryin : Qubit) : Unit is Adj { - // ... - - } - - - // Task 2.4. In-place two bit adder - // Inputs: - // 1) two qubit register "a" in an arbitrary state |φ⟩ - // 2) two qubit register "b" in an arbitrary state |ψ⟩ - // 3) qubit "carry" in state |0⟩ - // Goals: - // 1) transform register "b" into the state |φ + ψ⟩ - // 2) transform the "carry" qubit into the carry bit from the addition - // leave register "a" unchanged - operation TwoBitAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - // ... - - } - - - // Task 2.5. In-place N-bit adder - // Inputs: - // 1) N qubit register "a" in an arbitrary state |φ⟩ - // 2) N qubit register "b" in an arbitrary state |ψ⟩ - // 3) qubit "carry" in state |0⟩ - // Goals: - // 1) transform register "b" into the state |φ + ψ⟩ - // 2) transform the "carry" qubit into the carry bit from the addition - // leave register "a" unchanged - operation ArbitraryAdderInPlace (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - // ... - - } - - - ////////////////////////////////////////////////////////////////// - // Part III*. Improved in-place adder - ////////////////////////////////////////////////////////////////// - - // The in-place adder doesn't require quite as many qubits for the inputs and outputs, - // but it still requires an array of extra ("ancillary") qubits to perform the calculation. - // A relatively recent algorithm allows you to perform the same calculation - // using only one additional qubit. - - // Task 3.1. Majority gate - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "c" in an arbitrary state |ω⟩ - // Goal: construct the "in-place majority" gate - // 1) transform qubit "a" into the carry bit from the addition of φ and ψ and ω - // 2) transform qubit "b" into |φ + ψ⟩ - // 3) transform qubit "c" into |φ + ω⟩ - operation Majority (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { - // ... - - } - - - // Task 3.2. UnMajority and Add gate - // Inputs: - // 1) qubit "a" storing the carry bit from the sum φ + ψ + ω - // 2) qubit "b" in state |φ + ψ⟩ - // 3) qubit "c" in state |φ + ω⟩ - // Goal: construct the "un-majority and add", or "UMA" gate - // 1) restore qubit "a" into state |φ⟩ - // 2) transform qubit "b" into state |φ + ψ + ω⟩ - // 3) restore qubit "c" into state |ω⟩ - operation UnMajorityAdd (a : Qubit, b : Qubit, c : Qubit) : Unit is Adj { - // ... - - } - - - // Task 3.3. One bit majority-UMA adder - // Inputs: - // 1) qubit "a" in an arbitrary state |φ⟩ - // 2) qubit "b" in an arbitrary state |ψ⟩ - // 3) qubit "carry" in state |0⟩ - // Goal: construct a one bit binary adder from task 2.2 using Majority and UMA gates - operation OneBitMajUmaAdder (a : Qubit, b : Qubit, carry : Qubit) : Unit is Adj { - // Hint: Allocate an extra qubit to pass as qubit "c" for the two gates. - // It's less efficient here, but it will help in the next tasks. - - // ... - - } - - - // Task 3.4. Two bit majority-UMA adder - // Inputs: - // 1) two qubit register "a" in an arbitrary state |φ⟩ - // 2) two qubit register "b" in an arbitrary state |ψ⟩ - // 3) qubit "carry" in state |0⟩ - // Goal: construct a two bit binary adder from task 2.4 using Majority and UMA gates - operation TwoBitMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - // Hint: think carefully about which qubits you need to pass to the two gates - - // ... - - } - - - // Task 3.5. N-bit majority-UMA adder - // Inputs: - // 1) N qubit register "a" in an arbitrary state |φ⟩ - // 2) N qubit register "b" in an arbitrary state |ψ⟩ - // 3) qubit "carry" in state |0⟩ - // Goal: construct an N-bit binary adder from task 2.5 using only one extra qubit - operation ArbitraryMajUmaAdder (a : Qubit[], b : Qubit[], carry : Qubit) : Unit is Adj { - // ... - - } - - - ////////////////////////////////////////////////////////////////// - // Part IV*. In-place subtractor - ////////////////////////////////////////////////////////////////// - - // Subtracting a number is the same operation as adding a negative number. - // As such, the binary adder we just built can be easily adapted to act as a subtractor instead. - - // Task 4.1. N-bit subtractor - // Inputs: - // 1) N qubit register "a" in an arbitrary state |φ⟩ - // 2) N qubit register "b" in an arbitrary state |ψ⟩ - // 3) qubit "borrow" in state |0⟩ - // Goal: construct a binary subtractor - // 1) transform register "b" into the state |ψ - φ⟩ - // 2) set the "borrow" qubit to |1⟩ if that subtraction required a borrow - // leave register "a" unchanged - operation Subtractor (a : Qubit[], b : Qubit[], borrow : Qubit) : Unit is Adj { - // Hint: use the adder you already built - // experiment with inverting the registers before and after the addition - - // ... - - } -} \ No newline at end of file diff --git a/RippleCarryAdder/TestSuiteRunner.cs b/RippleCarryAdder/TestSuiteRunner.cs deleted file mode 100644 index 7bb81f07946..00000000000 --- a/RippleCarryAdder/TestSuiteRunner.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -////////////////////////////////////////////////////////////////////// -// This file contains parts of the testing harness. -// You should not modify anything in this file. -// The tasks themselves can be found in Tasks.qs file. -////////////////////////////////////////////////////////////////////// - -using Microsoft.Quantum.Simulation.XUnit; -using Xunit.Abstractions; -using System.Diagnostics; - -using Microsoft.Quantum.Katas; - -namespace Quantum.Kata.RippleCarryAdder -{ - public class TestSuiteRunner - { - private readonly ITestOutputHelper output; - - public TestSuiteRunner(ITestOutputHelper output) - { - this.output = output; - } - - /// - /// This driver will run all Q# tests (operations named "...Test") - /// that belong to namespace Quantum.Kata.RippleCarryAdder. - /// - [OperationDriver(TestNamespace = "Quantum.Kata.RippleCarryAdder")] - public void TestTarget(TestOperation op) - { - using (var sim = new CounterSimulator()) - { - // OnLog defines action(s) performed when Q# test calls function Message - sim.OnLog += (msg) => { output.WriteLine(msg); }; - sim.OnLog += (msg) => { Debug.WriteLine(msg); }; - op.TestOperationRunner(sim); - } - } - } -} diff --git a/RippleCarryAdder/Tests.qs b/RippleCarryAdder/Tests.qs deleted file mode 100644 index fcdfa7ebc36..00000000000 --- a/RippleCarryAdder/Tests.qs +++ /dev/null @@ -1,391 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -////////////////////////////////////////////////////////////////////// -// This file contains testing harness for all tasks. -// You should not modify anything in this file. -// The tasks themselves can be found in Tasks.qs file. -////////////////////////////////////////////////////////////////////// - -namespace Quantum.Kata.RippleCarryAdder { - - open Microsoft.Quantum.Arrays; - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Diagnostics; - open Microsoft.Quantum.Convert; - open Microsoft.Quantum.Math; - open Microsoft.Quantum.Measurement; - - open Quantum.Kata.Utils; - - // ------------------------------------------------------ - function Adder (max : Int, a : Int, b : Int) : (Int, Bool) { - let sum = a + b; - return (sum % max, sum >= max); - } - - function Subtractor_F (max : Int, a : Int, b : Int) : (Int, Bool) { - let diff = b - a; - return ((diff + max) % max, diff < 0); - } - - function BinaryAdder (input : Bool[], N : Int) : Bool[] { - let max = 1 <<< N; - let bitsa = input[0 .. N-1]; - let bitsb = input[N ...]; - let a = BoolArrayAsInt(bitsa); - let b = BoolArrayAsInt(bitsb); - let (sum, carry) = Adder(max, a, b); - return IntAsBoolArray(sum, N) + [carry]; - } - - function BinarySubtractor (input : Bool[], N : Int) : Bool[] { - let max = 1 <<< N; - let bitsa = input[0 .. N-1]; - let bitsb = input[N ...]; - let a = BoolArrayAsInt(bitsa); - let b = BoolArrayAsInt(bitsb); - let (diff, borrow) = Subtractor_F(max, a, b); - return IntAsBoolArray(diff, N) + [borrow]; - } - - function BinaryXor (bits : Bool[]) : Bool { - let N = Length(bits); - mutable ans = false; - for (i in 0 .. N-1) { - if (bits[i]) { - set ans = not ans; - } - } - return ans; - } - - // ------------------------------------------------------ - // Wrapper operations to make the tasks usable with AssertOperationsEqualReferenced - operation QubitArrayOperationWrapper2 (op : ((Qubit, Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { - op(Head(arr), Tail(arr)); - } - - operation QubitArrayOperationWrapper3 (op : ((Qubit, Qubit, Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { - op(arr[0], arr[1], arr[2]); - } - - operation QubitArrayOperationWrapper4 (op : ((Qubit, Qubit, Qubit, Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { - op(arr[0], arr[1], arr[2], arr[3]); - } - - operation QubitArrayAdderWrapper (N : Int, op : ((Qubit[], Qubit[], Qubit[], Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { - let splits = Partitioned([N, N, N, 1], arr); - op(splits[0], splits[1], splits[2], Tail(arr)); - } - - operation QubitArrayInPlaceAdderWrapper (N : Int, op : ((Qubit[], Qubit[], Qubit) => Unit is Adj), arr : Qubit[]) : Unit is Adj { - let splits = Partitioned([N, N, 1], arr); - op(splits[0], splits[1], Tail(arr)); - } - - // ------------------------------------------------------ - // Helper operations to prepare qubits from an input and compare them to the output - operation PrepareRegister (register : Qubit[], state : Int) : Bool[] { - let bits = IntAsBoolArray(state, Length(register)); - ApplyPauliFromBitString(PauliX, true, bits, register); - return bits; - } - - operation VerifyRegister (register : Qubit[], state : Bool[], errorPrefix : String) : Unit { - let results = MultiM(register); - let resultsb = ResultArrayAsBoolArray(results); - AllEqualityFactB(resultsb, state, errorPrefix + $"expected {BoolArrayAsResultArray(state)}, but got {results}"); - } - - // ------------------------------------------------------ - // Function to generate more readable error messages - function GenerateErrorPrefix (bits : Bool[]) : String { - let N = Length(bits); - let astr = $"Inputs a:{BoolArrayAsResultArray(bits[0 .. (N/2)-1])}"; - let postfix = " produce unexpected output: "; - if (N % 2 == 0) { - // Only a and b inputs - return astr + $" and b:{BoolArrayAsResultArray(bits[N/2 ...])}" + postfix; - } - else { - // 3 inputs - a, b, and carry - return astr + $", b:{BoolArrayAsResultArray(bits[N/2 .. N-2])}, and c:{BoolArrayAsResultArray(bits[N-1 ...])}" + postfix; - } - } - - // ------------------------------------------------------ - // Assertion to compare a binary function with its quantum counterpart - operation AssertOperationImplementsBinaryFunction (op : ((Qubit[]) => Unit is Adj), fun : ((Bool[]) -> Bool[]), Nbits : Int, Nverify : Int) : Unit { - let max = 1 <<< Nbits; - - using ((qinput, qoutput) = (Qubit[Nbits], Qubit[Nverify])) { - let qs = qinput + qoutput; - for (i in 0 .. max-1) { - let inbits = PrepareRegister(qinput, i); - let outbits = fun(inbits); - - op(qs); - - VerifyRegister(qinput, inbits, "Inputs should not be modified: "); - VerifyRegister(qoutput, outbits, GenerateErrorPrefix(inbits)); - ResetAll(qs); - } - } - } - - // Assertion to compare a binary function with its in-place quantum counterpart - operation AssertInPlaceOperationImplementsBinaryFunction (op : ((Qubit[]) => Unit is Adj), fun : ((Bool[]) -> Bool[]), N : Int, targetStart : Int, targetEnd : Int, extra : Int) : Unit { - let max = 1 <<< N; - using ((qinput, qextra) = (Qubit[N], Qubit[extra])) { - let qs = qinput + qextra; - let target = qinput[targetStart .. targetEnd] + qextra; - let beforeTarget = qinput[0 .. targetStart-1]; - let afterTarget = qinput[targetEnd+1 ...]; - for (i in 0 .. max-1) { - let inbits = PrepareRegister(qinput, i); - let outbits = fun(inbits); - - op(qs); - - VerifyRegister(beforeTarget, inbits[0 .. targetStart-1], "Input a should not be modified: "); - VerifyRegister(afterTarget, inbits[targetEnd+1 ...], "The carry input should not be modified: "); - VerifyRegister(target, outbits, GenerateErrorPrefix(inbits)); - ResetAll(qs); - } - } - } - - ////////////////////////////////////////////////////////////////// - // Part I. Simple adder outputting to empty Qubits - ////////////////////////////////////////////////////////////////// - - // ------------------------------------------------------ - function LowestBitSum_F (bits : Bool[]) : Bool[] { - return [BinaryXor(bits)]; - } - - operation T11_LowestBitSum_Test () : Unit { - let testOp = QubitArrayOperationWrapper3(LowestBitSum, _); - let refOp = QubitArrayOperationWrapper3(LowestBitSum_Reference, _); - - AssertOperationImplementsBinaryFunction(testOp, LowestBitSum_F, 2, 1); - AssertOperationsEqualReferenced(3, testOp, refOp); - } - - // ------------------------------------------------------ - function LowestBitCarry_F (bits : Bool[]) : Bool[] { - return [bits[0] and bits[1]]; - } - - operation T12_LowestBitCarry_Test () : Unit { - let testOp = QubitArrayOperationWrapper3(LowestBitCarry, _); - let refOp = QubitArrayOperationWrapper3(LowestBitCarry_Reference, _); - - AssertOperationImplementsBinaryFunction(testOp, LowestBitCarry_F, 2, 1); - AssertOperationsEqualReferenced(3, testOp, refOp); - } - - // ------------------------------------------------------ - operation T13_OneBitAdder_Test () : Unit { - let testOp = QubitArrayOperationWrapper4(OneBitAdder, _); - let refOp = QubitArrayOperationWrapper4(OneBitAdder_Reference, _); - - AssertOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 1), 2, 2); - AssertOperationsEqualReferenced(4, testOp, refOp); - } - - // ------------------------------------------------------ - function HighBitSum_F (bits : Bool[]) : Bool[] { - return [BinaryXor(bits)]; - } - - operation T14_HighBitSum_Test () : Unit { - let testOp = QubitArrayOperationWrapper4(HighBitSum, _); - let refOp = QubitArrayOperationWrapper4(HighBitSum_Reference, _); - - AssertOperationImplementsBinaryFunction(testOp, HighBitSum_F, 3, 1); - AssertOperationsEqualReferenced(4, testOp, refOp); - } - - // ------------------------------------------------------ - function HighBitCarry_F (bits : Bool[]) : Bool[] { - return [(bits[0] and bits[1]) or (bits[2] and (bits[0] or bits[1]))]; - } - - operation T15_HighBitCarry_Test () : Unit { - let testOp = QubitArrayOperationWrapper4(HighBitCarry, _); - let refOp = QubitArrayOperationWrapper4(HighBitCarry_Reference, _); - - AssertOperationImplementsBinaryFunction(testOp, HighBitCarry_F, 3, 1); - AssertOperationsEqualReferenced(4, testOp, refOp); - } - - // ------------------------------------------------------ - operation T16_TwoBitAdder_Test () : Unit { - let testOp = QubitArrayAdderWrapper(2, TwoBitAdder, _); - let refOp = QubitArrayAdderWrapper(2, TwoBitAdder_Reference, _); - - AssertOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 2), 4, 3); - AssertOperationsEqualReferenced(7, testOp, refOp); - } - - // ------------------------------------------------------ - operation T17_ArbitraryAdder_Test () : Unit { - // 4 bits seems reasonable - any more than that will take forever - for (i in 1 .. 4){ - let testOp = QubitArrayAdderWrapper(i, ArbitraryAdder, _); - AssertOperationImplementsBinaryFunction(testOp, BinaryAdder(_, i), 2 * i, i + 1); - } - // Can't compare to the reference operation, because then the challenge solution doesn't pass the test - } - - ////////////////////////////////////////////////////////////////// - // Part II. Simple in-place adder - ////////////////////////////////////////////////////////////////// - - // ------------------------------------------------------ - operation T21_LowestBitSumInPlace_Test () : Unit { - let testOp = QubitArrayOperationWrapper2(LowestBitSumInPlace, _); - let refOp = QubitArrayOperationWrapper2(LowestBitSumInPlace_Reference, _); - - AssertInPlaceOperationImplementsBinaryFunction(testOp, LowestBitSum_F, 2, 1, 1, 0); - AssertOperationsEqualReferenced(2, testOp, refOp); - } - - // ------------------------------------------------------ - operation T22_OneBitAdderInPlace_Test () : Unit { - let testOp = QubitArrayOperationWrapper3(OneBitAdderInPlace, _); - let refOp = QubitArrayOperationWrapper3(OneBitAdderInPlace_Reference, _); - - AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 1), 2, 1, 1, 1); - AssertOperationsEqualReferenced(3, testOp, refOp); - } - - // ------------------------------------------------------ - operation T23_HighBitSumInPlace_Test () : Unit { - let testOp = QubitArrayOperationWrapper3(HighBitSumInPlace, _); - let refOp = QubitArrayOperationWrapper3(HighBitSumInPlace_Reference, _); - - AssertInPlaceOperationImplementsBinaryFunction(testOp, HighBitSum_F, 3, 1, 1, 0); - AssertOperationsEqualReferenced(3, testOp, refOp); - } - - // ------------------------------------------------------ - operation T24_TwoBitAdderInPlace_Test () : Unit { - let testOp = QubitArrayInPlaceAdderWrapper(2, TwoBitAdderInPlace, _); - let refOp = QubitArrayInPlaceAdderWrapper(2, TwoBitAdderInPlace_Reference, _); - - AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 2), 4, 2, 3, 1); - AssertOperationsEqualReferenced(5, testOp, refOp); - } - - // ------------------------------------------------------ - operation T25_ArbitraryAdderInPlace_Test () : Unit { - for (i in 1 .. 4) { - let testOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryAdderInPlace, _); - let refOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryAdderInPlace_Reference, _); - AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, i), 2 * i, i, (2 * i) - 1, 1); - AssertOperationsEqualReferenced((2 * i) + 1, testOp, refOp); - } - } - - ////////////////////////////////////////////////////////////////// - // Part III*. Improved in-place adder - ////////////////////////////////////////////////////////////////// - - // ------------------------------------------------------ - function Majority_F (bits : Bool[]) : Bool[] { - let a = bits[0]; - let b = bits[1]; - let c = bits[2]; - let ab = XOR(a, b); - let ac = XOR(a, c); - let cout = XOR(ab and ac, a); - return [cout, ab, ac]; - } - - operation T31_Majority_Test () : Unit { - let testOp = QubitArrayOperationWrapper3(Majority, _); - let refOp = QubitArrayOperationWrapper3(Majority_Reference, _); - - AssertInPlaceOperationImplementsBinaryFunction(testOp, Majority_F, 3, 0, 2, 0); - AssertOperationsEqualReferenced(3, testOp, refOp); - } - - // ------------------------------------------------------ - function UnMajorityAdd_F (bits : Bool[]) : Bool[] { - let a = bits[0]; - let b = bits[1]; - let c = bits[2]; - let a2 = XOR(b and c, a); - let c2 = XOR(a2, c); - let b2 = XOR(c2, b); - return [a2, b2, c2]; - } - - operation T32_UnMajorityAdd_Test () : Unit { - let testOp = QubitArrayOperationWrapper3(UnMajorityAdd, _); - let refOp = QubitArrayOperationWrapper3(UnMajorityAdd_Reference, _); - - AssertInPlaceOperationImplementsBinaryFunction(testOp, UnMajorityAdd_F, 3, 0, 2, 0); - AssertOperationsEqualReferenced(3, testOp, refOp); - } - - // ------------------------------------------------------ - operation T33_OneBitMajUmaAdder_Test () : Unit { - let testOp = QubitArrayOperationWrapper3(OneBitMajUmaAdder, _); - let refOp = QubitArrayOperationWrapper3(OneBitMajUmaAdder_Reference, _); - AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 1), 2, 1, 1, 1); - AssertOperationsEqualReferenced(3, testOp, refOp); - } - - // ------------------------------------------------------ - operation T34_TwoBitMajUmaAdder_Test () : Unit { - //Reverted to old test, as opration call counts don't work for counting task operations in notebooks - //ResetOracleCallsCount(); - let testOp = QubitArrayInPlaceAdderWrapper(2, TwoBitMajUmaAdder, _); - let refOp = QubitArrayInPlaceAdderWrapper(2, TwoBitMajUmaAdder_Reference, _); - AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, 2), 4, 2, 3, 1); - //let sumCalls = GetOracleCallsCount(HighBitSumInPlace); - //let carryCalls = GetOracleCallsCount(HighBitCarry); - //let majCalls = GetOracleCallsCount(Majority); - //let umaCalls = GetOracleCallsCount(UnMajorityAdd); - //Fact((sumCalls == 0) and (carryCalls == 0), "You shouldn't be calling the old sum/carry operations for this task."); - //Fact((majCalls > 0) and (umaCalls > 0), "Are you sure you're using the Majority and UMA gates?"); - - - AssertOperationsEqualReferenced(5, testOp, refOp); - } - - // ------------------------------------------------------ - operation T35_ArbitraryMajUmaAdder_Test () : Unit { - // This algorithm is much faster, so a 5 qubit test is feasible - for (i in 1 .. 5) { - let testOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryMajUmaAdder, _); - let refOp = QubitArrayInPlaceAdderWrapper(i, ArbitraryMajUmaAdder_Reference, _); - - ResetQubitCount(); - AssertInPlaceOperationImplementsBinaryFunction(testOp, BinaryAdder(_, i), 2 * i, i, (2 * i) - 1, 1); - let used = GetMaxQubitCount(); - Fact(used <= (2 * (i + 1)), "Too many qubits used"); - - AssertOperationsEqualReferenced((2 * i) + 1, testOp, refOp); - } - } - - ////////////////////////////////////////////////////////////////// - // Part IV*. In-place subtractor - ////////////////////////////////////////////////////////////////// - - // ------------------------------------------------------ - operation T41_Subtractor_Test () : Unit { - for (i in 1 .. 5) { - let testOp = QubitArrayInPlaceAdderWrapper(i, Subtractor, _); - let refOp = QubitArrayInPlaceAdderWrapper(i, Subtractor_Reference, _); - AssertInPlaceOperationImplementsBinaryFunction(testOp, BinarySubtractor(_, i), 2 * i, i, (2 * i) - 1, 1); - AssertOperationsEqualReferenced((2 * i) + 1, testOp, refOp); - } - } -} From a34cbdfa8409e8ce38d3202bd24c9cb6e17152b9 Mon Sep 17 00:00:00 2001 From: Mariia Mykhailova Date: Thu, 31 Oct 2019 07:52:27 -0700 Subject: [PATCH 5/8] Remove files that cause merge conflicts --- .gitignore | 5 +++-- README.md | 15 +++++++++++---- index.ipynb | 19 ++++++++++++++----- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 195807679b3..98bce651c0a 100644 --- a/.gitignore +++ b/.gitignore @@ -283,5 +283,6 @@ TSWLatexianTemp* # standalone packages *.sta -/tutorials/ComplexArithmetic/__pycache__ -/tutorials/LinearAlgebra/__pycache__ + +# Python build files +__pycache__ diff --git a/README.md b/README.md index b7675582a52..12664fea265 100644 --- a/README.md +++ b/README.md @@ -32,15 +32,20 @@ Each kata is a separate project which includes: ## List of Notebook tutorials ## +* **[Complex Arithmetic](./tutorials/ComplexArithmetic/)**. + This tutorial introduces you to complex numbers, + explaining the mathematics required to work with quantum computing. +* **[Linear Algebra](./tutorials/LinearAlgebra/)**. + This tutorial walks you through working with matrices and vectors, + which are used to represent quantum states and quantum operations. * **[Deutsch–Jozsa algorithm](./tutorials/DeutschJozsaAlgorithm/)**. This tutorial teaches you to implement classical functions and equivalent quantum oracles, discusses the classical solution to the Deutsch–Jozsa problem, and introduces the Deutsch and Deutsch–Jozsa algorithms. +* **[Exploring Grover's search algorithm](./tutorials/ExploringGroversAlgorithm/)**. + This tutorial offers you a high-level exploration of Grover's search algorithm, picking up where the [Grover's algorithm kata](./GroversAlgorithm/) left off. ## List of Katas ## -Each kata covers one topic. -Currently covered topics are: - #### Quantum computing concepts * **[Basic quantum computing gates](./BasicGates/)**. This kata focuses on main single-qubit and multi-qubit gates used in quantum computing. @@ -75,11 +80,13 @@ Currently covered topics are: * **[Mermin-Peres magic square game](./MagicSquareGame)**. #### Miscellaneous +* **[BB84 protocol](./KeyDistribution_BB84/)**. + This kata covers BB84 key distribution algorithm. * **[Phase estimation](./PhaseEstimation/)**. This kata covers phase estimation algorithms. * **[Bit-flip error correcting code](./QEC_BitFlipCode/)**. This kata introduces a 3-qubit error correcting code for protecting against bit-flip errors. -* **[Ripple Carry Adder](./RippleCarryAdder/)**. +* **[Ripple-carry adder](./RippleCarryAdder/)**. This kata walks you through building a ripple-carry adder on a quantum computer. * **[Unitary Patterns*](./UnitaryPatterns/)**. This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of zero and non-zero elements. diff --git a/index.ipynb b/index.ipynb index 64d7c227b3b..c4b8035584f 100644 --- a/index.ipynb +++ b/index.ipynb @@ -9,10 +9,17 @@ "\n", "## List of Notebook Tutorials\n", "\n", + "* **[Complex Arithmetic](./tutorials/ComplexArithmetic/ComplexArithmetic.ipynb)**.\n", + " This tutorial teaches you how to work with complex numbers,\n", + " explaining the mathematics required to work with quantum computing.\n", + "* **[Linear Algebra](./tutorials/LinearAlgebra/LinearAlgebra.ipynb)**.\n", + " This tutorial walks you through working with matrices and vectors,\n", + " which are used to represent quantum states and quantum operations.\n", "* **[Deutsch–Jozsa algorithm](./tutorials/DeutschJozsaAlgorithm/DeutschJozsaAlgorithmTutorial.ipynb)**.\n", " This tutorial teaches you to implement classical functions and equivalent quantum oracles, \n", " discusses the classical solution to the Deutsch–Jozsa problem and introduces Deutsch and Deutsch–Jozsa algorithms.\n", - "\n", + "* **[Exploring Grover's search algorithm](./tutorials/ExploringGroversAlgorithm/ExploringGroversAlgorithmTutorial.ipynb)**.\n", + " This tutorial offers you a high-level exploration of Grover's search algorithm, picking up where the [Grover's algorithm kata](./GroversAlgorithm/GroversAlgorithm.ipynb) left off.\n", "\n", "## List of Katas\n", "\n", @@ -41,7 +48,7 @@ "SAT problems as an example. It covers implementing quantum oracles based on the problem description instead of a \n", "hard-coded answer and using Grover's algorithm to solve problems with unknown number of solutions.\n", "* **[Solving graph coloring problems using Grover's algorithm](./GraphColoring/GraphColoring.ipynb)**.\n", - "This kata continues the exploration of Grover's search algorithm, using graph coloring problems as an example.\n", + " This kata continues the exploration of Grover's search algorithm, using graph coloring problems as an example.\n", "\n", "#### Entanglement games\n", "\n", @@ -51,14 +58,16 @@ "\n", "#### Miscellaneous\n", "\n", + "* **[BB84 protocol](./KeyDistribution_BB84/KeyDistribution_BB84.ipynb)**.\n", + " This kata covers BB84 key distribution algorithm.\n", "* **[Phase estimation](./PhaseEstimation/PhaseEstimation.ipynb)**.\n", - "This kata covers phase estimation algorithms.\n", + " This kata covers phase estimation algorithms.\n", "* **[Bit-flip error correcting code](./QEC_BitFlipCode/QEC_BitFlipCode.ipynb)**.\n", " This kata introduces a 3-qubit error correcting code for protecting against bit-flip errors.\n", - "* **[Ripple Carry Adder](./RippleCarryAdder/RippleCarryAdder.ipynb)**.\n", + "* **[Ripple-carry adder](./RippleCarryAdder/RippleCarryAdder.ipynb)**.\n", " This kata walks you through building a ripple-carry adder on a quantum computer.\n", "* **[Unitary Patterns](./UnitaryPatterns/UnitaryPatterns.ipynb)**.\n", - "This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of \n", + " This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of \n", "zero and non-zero elements.\n", "\n", "\n", From 7366fdae428927575bd439a426291bda1ba1affb Mon Sep 17 00:00:00 2001 From: Mariia Mykhailova Date: Thu, 31 Oct 2019 07:54:39 -0700 Subject: [PATCH 6/8] Take lsit files from old commit --- .gitignore | 3 --- README.md | 15 +++------------ index.ipynb | 19 ++++--------------- 3 files changed, 7 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 98bce651c0a..57ff868dddf 100644 --- a/.gitignore +++ b/.gitignore @@ -283,6 +283,3 @@ TSWLatexianTemp* # standalone packages *.sta - -# Python build files -__pycache__ diff --git a/README.md b/README.md index 12664fea265..daf842b5307 100644 --- a/README.md +++ b/README.md @@ -32,20 +32,15 @@ Each kata is a separate project which includes: ## List of Notebook tutorials ## -* **[Complex Arithmetic](./tutorials/ComplexArithmetic/)**. - This tutorial introduces you to complex numbers, - explaining the mathematics required to work with quantum computing. -* **[Linear Algebra](./tutorials/LinearAlgebra/)**. - This tutorial walks you through working with matrices and vectors, - which are used to represent quantum states and quantum operations. * **[Deutsch–Jozsa algorithm](./tutorials/DeutschJozsaAlgorithm/)**. This tutorial teaches you to implement classical functions and equivalent quantum oracles, discusses the classical solution to the Deutsch–Jozsa problem, and introduces the Deutsch and Deutsch–Jozsa algorithms. -* **[Exploring Grover's search algorithm](./tutorials/ExploringGroversAlgorithm/)**. - This tutorial offers you a high-level exploration of Grover's search algorithm, picking up where the [Grover's algorithm kata](./GroversAlgorithm/) left off. ## List of Katas ## +Each kata covers one topic. +Currently covered topics are: + #### Quantum computing concepts * **[Basic quantum computing gates](./BasicGates/)**. This kata focuses on main single-qubit and multi-qubit gates used in quantum computing. @@ -80,14 +75,10 @@ Each kata is a separate project which includes: * **[Mermin-Peres magic square game](./MagicSquareGame)**. #### Miscellaneous -* **[BB84 protocol](./KeyDistribution_BB84/)**. - This kata covers BB84 key distribution algorithm. * **[Phase estimation](./PhaseEstimation/)**. This kata covers phase estimation algorithms. * **[Bit-flip error correcting code](./QEC_BitFlipCode/)**. This kata introduces a 3-qubit error correcting code for protecting against bit-flip errors. -* **[Ripple-carry adder](./RippleCarryAdder/)**. - This kata walks you through building a ripple-carry adder on a quantum computer. * **[Unitary Patterns*](./UnitaryPatterns/)**. This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of zero and non-zero elements. diff --git a/index.ipynb b/index.ipynb index c4b8035584f..158fdb977fb 100644 --- a/index.ipynb +++ b/index.ipynb @@ -9,17 +9,10 @@ "\n", "## List of Notebook Tutorials\n", "\n", - "* **[Complex Arithmetic](./tutorials/ComplexArithmetic/ComplexArithmetic.ipynb)**.\n", - " This tutorial teaches you how to work with complex numbers,\n", - " explaining the mathematics required to work with quantum computing.\n", - "* **[Linear Algebra](./tutorials/LinearAlgebra/LinearAlgebra.ipynb)**.\n", - " This tutorial walks you through working with matrices and vectors,\n", - " which are used to represent quantum states and quantum operations.\n", "* **[Deutsch–Jozsa algorithm](./tutorials/DeutschJozsaAlgorithm/DeutschJozsaAlgorithmTutorial.ipynb)**.\n", " This tutorial teaches you to implement classical functions and equivalent quantum oracles, \n", " discusses the classical solution to the Deutsch–Jozsa problem and introduces Deutsch and Deutsch–Jozsa algorithms.\n", - "* **[Exploring Grover's search algorithm](./tutorials/ExploringGroversAlgorithm/ExploringGroversAlgorithmTutorial.ipynb)**.\n", - " This tutorial offers you a high-level exploration of Grover's search algorithm, picking up where the [Grover's algorithm kata](./GroversAlgorithm/GroversAlgorithm.ipynb) left off.\n", + "\n", "\n", "## List of Katas\n", "\n", @@ -48,7 +41,7 @@ "SAT problems as an example. It covers implementing quantum oracles based on the problem description instead of a \n", "hard-coded answer and using Grover's algorithm to solve problems with unknown number of solutions.\n", "* **[Solving graph coloring problems using Grover's algorithm](./GraphColoring/GraphColoring.ipynb)**.\n", - " This kata continues the exploration of Grover's search algorithm, using graph coloring problems as an example.\n", + "This kata continues the exploration of Grover's search algorithm, using graph coloring problems as an example.\n", "\n", "#### Entanglement games\n", "\n", @@ -58,16 +51,12 @@ "\n", "#### Miscellaneous\n", "\n", - "* **[BB84 protocol](./KeyDistribution_BB84/KeyDistribution_BB84.ipynb)**.\n", - " This kata covers BB84 key distribution algorithm.\n", "* **[Phase estimation](./PhaseEstimation/PhaseEstimation.ipynb)**.\n", - " This kata covers phase estimation algorithms.\n", + "This kata covers phase estimation algorithms.\n", "* **[Bit-flip error correcting code](./QEC_BitFlipCode/QEC_BitFlipCode.ipynb)**.\n", " This kata introduces a 3-qubit error correcting code for protecting against bit-flip errors.\n", - "* **[Ripple-carry adder](./RippleCarryAdder/RippleCarryAdder.ipynb)**.\n", - " This kata walks you through building a ripple-carry adder on a quantum computer.\n", "* **[Unitary Patterns](./UnitaryPatterns/UnitaryPatterns.ipynb)**.\n", - " This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of \n", + "This unusual kata offers tasks on implementing unitaries with matrices that follow certain patterns of \n", "zero and non-zero elements.\n", "\n", "\n", From 89b6a012b04164f8d4010051f8b4d5a044c184c5 Mon Sep 17 00:00:00 2001 From: Mariia Mykhailova Date: Thu, 31 Oct 2019 11:40:24 -0700 Subject: [PATCH 7/8] Start addressing review comments --- .../SingleQubitGates/SingleQubitGates.csproj | 14 +-- .../SingleQubitGates/SingleQubitGates.ipynb | 117 +++++++++++------- 2 files changed, 78 insertions(+), 53 deletions(-) diff --git a/tutorials/SingleQubitGates/SingleQubitGates.csproj b/tutorials/SingleQubitGates/SingleQubitGates.csproj index 1c29cca2b6b..d667c0ce906 100644 --- a/tutorials/SingleQubitGates/SingleQubitGates.csproj +++ b/tutorials/SingleQubitGates/SingleQubitGates.csproj @@ -1,17 +1,15 @@  - netcoreapp2.0 + netcoreapp2.1 x64 - false - Kata.SingleQubitGates - SingleQubitGates + Quantum.Kata.SingleQubitGates - - - - + + + + diff --git a/tutorials/SingleQubitGates/SingleQubitGates.ipynb b/tutorials/SingleQubitGates/SingleQubitGates.ipynb index 41ea628aa99..14149607516 100644 --- a/tutorials/SingleQubitGates/SingleQubitGates.ipynb +++ b/tutorials/SingleQubitGates/SingleQubitGates.ipynb @@ -4,22 +4,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Single Qubit Gates\n", + "# Single-Qubit Gates\n", "\n", - "This tutorial introduces you to single qubit gates. Quantum gates are the quantum counterpart to classical logic gates, acting as the building blocks of quantum computing. Quantum gates transform qubit states in various ways, and can be applied sequentially to perform complex quantum calculations. Single qubit gates, as their name implies, act on single qubits. You can learn more at [Wikipedia](https://en.wikipedia.org/wiki/Quantum_logic_gate).\n", + "This tutorial introduces you to single-qubit gates. Quantum gates are the quantum counterpart to classical logic gates, acting as the building blocks of quantum algorithms. Quantum gates transform qubit states in various ways, and can be applied sequentially to perform complex quantum calculations. Single-qubit gates, as their name implies, act on individual qubits. You can learn more at [Wikipedia](https://en.wikipedia.org/wiki/Quantum_logic_gate).\n", "\n", - "You can use a tool called [Quirk](https://algasert.com/quirk) to visualize how these gates interact with various qubit states.\n", - "\n", - "It is highly recommended to go through the [single qubit systems tutorial](../Qubit/Qubit.ipynb) before attempting this one.\n", + "We recommend to go through the [tutorial that introduces the concept of qubit](../Qubit/Qubit.ipynb) before starting this one.\n", "\n", "This tutorial covers the following topics:\n", "\n", "* Matrix representation\n", "* Ket-bra representation\n", - "* Pauli gates\n", - "* Hadamard gate\n", - "* Phase gates\n", - "* Rotation gates" + "* The most important single-qubit gates" ] }, { @@ -31,11 +26,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/json": "[\"Microsoft.Quantum.Standard::0.9.1909.3002\",\"Microsoft.Quantum.Katas::0.9.1909.3002\"]", + "text/html": [ + "
  • Microsoft.Quantum.Standard::0.9.1909.3002
  • Microsoft.Quantum.Katas::0.9.1909.3002
" + ], + "text/plain": [ + "Microsoft.Quantum.Standard::0.9.1909.3002, Microsoft.Quantum.Katas::0.9.1909.3002" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "%package Microsoft.Quantum.Katas::0.9.1908.2906" + "%package Microsoft.Quantum.Katas::0.9.1909.3002" ] }, { @@ -73,8 +83,10 @@ "source": [ "## Matrix Representation\n", "\n", - "Quantum gates are represented as $2^N \\times 2^N$ [unitary matrices](../LinearAlgebra/LinearAlgebra.ipynb#Unary-Operations), where $N$ is the number of qubits the gate operates on. Single qubit gates are represented by $2 \\times 2$ matrices.\n", - "As a quick reminder, a unitary matrix is a square matrix whose inverse is its adjoint. Our example for this section, the $X$ gate, is represented by the following matrix:\n", + "Quantum gates are represented as $2^N \\times 2^N$ [unitary matrices](../LinearAlgebra/LinearAlgebra.ipynb#Unitary-Matrices), where $N$ is the number of qubits the gate operates on. \n", + "As a quick reminder, a unitary matrix is a square matrix whose inverse is its adjoint. \n", + "Single qubit gates are represented by $2 \\times 2$ matrices.\n", + "Our example for this section, the $X$ gate, is represented by the following matrix:\n", "\n", "$$\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}$$\n", "\n", @@ -100,26 +112,26 @@ "\\begin{bmatrix} 0 \\cdot \\alpha + 1 \\cdot \\beta \\\\ 1 \\cdot \\alpha + 0 \\cdot \\beta \\end{bmatrix} =\n", "\\begin{bmatrix} \\beta \\\\ \\alpha \\end{bmatrix}$$\n", "\n", - "> If you don't remember what $|0\\rangle$, $|1\\rangle$, and $|\\psi\\rangle$ mean, you should review the section on [Dirac notation](../SingleQubits/SingleQubits.ipynb#Dirac-Notation).\n", + "> If you need a reminder of what $|0\\rangle$, $|1\\rangle$, and $|\\psi\\rangle$ mean, you can review the section on [Dirac notation](../Qubit/Qubit.ipynb#Dirac-Notation) in the previous tutorial.\n", "\n", "Because this is the most common way to represent quantum gates, the terms \"gate\" and \"gate matrix\" will be used interchangeably in this tutorial.\n", "\n", - "Applying several quantum gates in sequence is equivalent to performing several of these multiplications. For example, if you have gates $A$ and $B$ and a qubit in state $|\\psi\\rangle$, the result of applying $A$ followed by $B$ to that qubit would be $B\\big(A|\\psi\\rangle\\big)$ (gates closest to the qubit state get applied first). Because matrix multiplication is associative, however, this is equivalent to multiplying the $B$ matrix by the $A$ matrix, producing a compound gate of the two, and then applying that to the qubit: $\\big(BA\\big)|\\psi\\rangle$.\n", - "\n", - "All quantum gates are reversible - there is another gate which will undo any given gate's transformation, returning the qubit to its original state. This means that when dealing with quantum gates, information about qubit states is never lost, as opposed to classical logic gates, some of which destroy information. This is why measurements are not considered gates - measurements destroy information about a quantum state. For more information, see the [measurements tutorial](../Measurements/Measurements.ipynb).\n", + "Applying several quantum gates in sequence is equivalent to performing several of these multiplications. \n", + "For example, if you have gates $A$ and $B$ and a qubit in state $|\\psi\\rangle$, the result of applying $A$ followed by $B$ to that qubit would be $B\\big(A|\\psi\\rangle\\big)$ (the gates closest to the qubit state get applied first). \n", + "Matrix multiplication is associative, so this is equivalent to multiplying the $B$ matrix by the $A$ matrix, producing a compound gate of the two, and then applying that to the qubit: $\\big(BA\\big)|\\psi\\rangle$.\n", "\n", - "Because quantum gates are represented by unitary matrices, the inverse of a gate is its adjoint." + "All quantum gates are reversible - there is another gate which will undo any given gate's transformation, returning the qubit to its original state. \n", + "This means that when dealing with quantum gates, information about qubit states is never lost, as opposed to classical logic gates, some of which destroy information. \n", + "Quantum gates are represented by unitary matrices, so the inverse of a gate is its adjoint; these terms are also used interchangeably in quantum computing." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Effects on Basis States.\n", - "\n", - "Doing matrix multiplication on long chains of gates is tedious and time consuming. Fortunately, there is an easier way to do quantum calculations.\n", + "## Effects on Basis States (Dirac Notation, Continued)\n", "\n", - "There is a simple way to find out what a gate does to the two computational basis states ($|0\\rangle$ and $|1\\rangle$) from looking at its matrix. Consider an arbitrary gate:\n", + "There is a simple way to find out what a gate does to the two computational basis states ($|0\\rangle$ and $|1\\rangle$) from looking at its matrix that comes in handy when you want to work with states in Dirac notation. Consider an arbitrary gate:\n", "\n", "$$A = \\begin{bmatrix} \\epsilon & \\zeta \\\\ \\eta & \\mu \\end{bmatrix}$$\n", "\n", @@ -136,14 +148,14 @@ "\\begin{bmatrix} \\epsilon \\cdot 0 + \\zeta \\cdot 1 \\\\ \\eta \\cdot 0 + \\mu \\cdot 1 \\end{bmatrix} =\n", "\\begin{bmatrix} \\zeta \\\\ \\mu \\end{bmatrix} = \\zeta|0\\rangle + \\mu|1\\rangle$$\n", "\n", - "Notice that applying the gate to the $|0\\rangle$ state outputs the first column of the gate's matrix. Likewise, applying the gate to the $|1\\rangle$ state outputs the second column. This holds true for any quantum gate, including, of course, the $X$ gate:\n", + "Notice that applying the gate to the $|0\\rangle$ state transforms it into the state written as the first column of the gate's matrix. Likewise, applying the gate to the $|1\\rangle$ state transforms it into the state written as the second column. This holds true for any quantum gate, including, of course, the $X$ gate:\n", "\n", "$$X = \\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix} \\\\\n", "X|0\\rangle = \\begin{bmatrix} 0 \\\\ 1 \\end{bmatrix} = |1\\rangle \\\\\n", "X|1\\rangle = \\begin{bmatrix} 1 \\\\ 0 \\end{bmatrix} = |0\\rangle$$\n", "\n", "Once you understand how a gate affects the computational basis states, you can easily find how it affects any state.\n", - "Recall that any qubit state vector can be re-written as a sum of the basis states:\n", + "Recall that any qubit state vector can be written as a linear combination of the basis states:\n", "\n", "$$|\\psi\\rangle = \\begin{bmatrix} \\alpha \\\\ \\beta \\end{bmatrix} = \\alpha|0\\rangle + \\beta|1\\rangle$$\n", "\n", @@ -151,7 +163,7 @@ "\n", "$$X|\\psi\\rangle = X\\big(\\alpha|0\\rangle + \\beta|1\\rangle\\big) = X\\big(\\alpha|0\\rangle\\big) + X\\big(\\beta|1\\rangle\\big) = \\alpha X|0\\rangle + \\beta X|1\\rangle = \\alpha|1\\rangle + \\beta|0\\rangle$$\n", "\n", - "That is, applying a gate to a qubit in superposition is equivalent to applying that gate to both states that make up that superposition." + "That is, applying a gate to a qubit in superposition is equivalent to applying that gate to the basis states that make up that superposition and adding the results with appropriate weights." ] }, { @@ -161,7 +173,8 @@ "## Ket-bra Representation\n", "\n", "There is another way to represent quantum gates, this time using Dirac notation. However, the kets we've been using aren't enough to represent arbitrary matrices. We need to introduce another piece of notation: the **bra** (this is why Dirac notation is sometimes called **bra-ket notation**).\n", - "Since kets represent column vectors, a bra is a ket's row vector counterpart. For any ket $|\\psi\\rangle$, its bra is $\\langle\\psi| = |\\psi\\rangle^\\dagger$.\n", + "\n", + "Recall that kets represent column vectors; a bra is a ket's row vector counterpart. For any ket $|\\psi\\rangle$, the corresponding bra is its adjoint (conjugate transpose): $\\langle\\psi| = |\\psi\\rangle^\\dagger$.\n", "\n", "> As a quick reminder, the [adjoint](../LinearAlgebra/LinearAlgebra.ipynb#Unary-Operations), also known as the conjugate transpose of a matrix, well, the conjugate of that matrix's transpose.\n", "\n", @@ -194,11 +207,16 @@ " $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$\n", " $\\langle\\psi| = \\overline{\\alpha}\\langle0| + \\overline{\\beta}\\langle1|$\n", " \n", - "\n", - "\n", - "Kets and bras give us a neat way to express [inner](../LinearAlgebra/LinearAlgebra.ipynb#Inner-Product) and [outer](../LinearAlgebra/LinearAlgebra.ipynb#Outer-Product) products. The inner product of $|\\phi\\rangle$ and $|\\psi\\rangle$ is $\\langle\\phi|\\psi\\rangle$, and their outer product is $|\\phi\\rangle\\langle\\psi|$. Notice that the norm of $|\\psi\\rangle$ is $\\sqrt{\\langle\\psi|\\psi\\rangle}$.\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Kets and bras give us a neat way to express [inner](../LinearAlgebra/LinearAlgebra.ipynb#Inner-Product) and [outer](../LinearAlgebra/LinearAlgebra.ipynb#Outer-Product) products. The inner product of $|\\phi\\rangle$ and $|\\psi\\rangle$ is the matrix product of $\\langle\\phi|$ and $|\\psi\\rangle$, denoted as $\\langle\\phi|\\psi\\rangle$, and their outer product is the matrix product of $|\\phi\\rangle$ and $\\langle\\psi|$, denoted as $|\\phi\\rangle\\langle\\psi|$. Notice that the norm of $|\\psi\\rangle$ is $\\sqrt{\\langle\\psi|\\psi\\rangle}$.\n", "\n", - "This brings us to representing matrices. Recall that the outer product of two vectors of the same size produces a square matrix. We can use the sum of several outer products of simple vectors (with scalar factors) to express any matrix. For example, the $X$ gate can be expressed as follows:\n", + "This brings us to representing matrices. Recall that the outer product of two vectors of the same size produces a square matrix. We can use a linear combination of several outer products of simple vectors (such as basis vectors) to express any square matrix. For example, the $X$ gate can be expressed as follows:\n", "\n", "$$X = |0\\rangle\\langle1| + |1\\rangle\\langle0| \\\\\n", "|0\\rangle\\langle1| + |1\\rangle\\langle0| =\n", @@ -207,7 +225,7 @@ "\\begin{bmatrix} 0 & 1 \\\\ 0 & 0 \\end{bmatrix} + \\begin{bmatrix} 0 & 0 \\\\ 1 & 0 \\end{bmatrix} =\n", "\\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}$$\n", "\n", - "This can also be used for calculations:\n", + "This representation can be used to carry out calculations in Dirac notation without ever switching back to matrix representation:\n", "\n", "$$X|0\\rangle = \\big(|0\\rangle\\langle1| + |1\\rangle\\langle0|\\big)|0\\rangle = |0\\rangle\\langle1|0\\rangle + |1\\rangle\\langle0|0\\rangle = |0\\rangle\\big(\\langle1|0\\rangle\\big) + |1\\rangle\\big(\\langle0|0\\rangle\\big) = |0\\rangle(0) + |1\\rangle(1) = |1\\rangle$$\n", "\n", @@ -242,12 +260,10 @@ ">\n", "> Here's what the decomposition looks like:\n", ">\n", - "> $$X = |+\\rangle\\langle+| - |-\\rangle\\langle-| =\n", - "\\frac{1}{\\sqrt{2}}\\big(|0\\rangle + |1\\rangle\\big)\\frac{1}{\\sqrt{2}}\\big(\\langle0| + \\langle1|\\big) - |-\\rangle\\langle-| = \\\\\n", - "= \\frac{1}{2}\\big[\\big(|0\\rangle + |1\\rangle\\big)\\big(\\langle0| + \\langle1\\big) - \\big(|0\\rangle - |1\\rangle\\big)\\big(\\langle0| - \\langle1|\\big)\\big] = \\\\\n", - "= \\frac{1}{2}\\big(|0\\rangle\\langle0| + |0\\rangle\\langle1| + |1\\rangle\\langle0| + |1\\rangle\\langle1| - |0\\rangle\\langle0| + |0\\rangle\\langle1| + |1\\rangle\\langle0| - |1\\rangle\\langle1|\\big) = \\\\\n", - "= \\frac{1}{2}\\big(|0\\rangle\\langle0| - |0\\rangle\\langle0| + |0\\rangle\\langle1| + |0\\rangle\\langle1| + |1\\rangle\\langle0| + |1\\rangle\\langle0| + |1\\rangle\\langle1| - |1\\rangle\\langle1|\\big) =\n", - "\\frac{1}{2}\\big(2|0\\rangle\\langle1| + 2|1\\rangle\\langle0|\\big) = |0\\rangle\\langle1| + |1\\rangle\\langle0|$$" + "> $$X = |+\\rangle\\langle+| - |-\\rangle\\langle-| = \\\\\n", + "= \\frac{1}{2}\\big[\\big(|0\\rangle + |1\\rangle\\big)\\big(\\langle0| + \\langle1|\\big) - \\big(|0\\rangle - |1\\rangle\\big)\\big(\\langle0| - \\langle1|\\big)\\big] = \\\\\n", + "= \\frac{1}{2}\\big(\\color{red}{|0\\rangle\\langle0|} + |0\\rangle\\langle1| + |1\\rangle\\langle0| + \\color{red}{|1\\rangle\\langle1|} - \\color{red}{|0\\rangle\\langle0|} + |0\\rangle\\langle1| + |1\\rangle\\langle0| - \\color{red}{|1\\rangle\\langle1|}\\big) = \\\\\n", + "= \\frac{1}{2}\\big(2|0\\rangle\\langle1| + 2|1\\rangle\\langle0|\\big) = |0\\rangle\\langle1| + |1\\rangle\\langle0|$$" ] }, { @@ -256,15 +272,26 @@ "source": [ "# Important Gates\n", "\n", - "This section introduces some of the common single qubit gates, including their matrix form, their ket-bra decomposition, and a brief summary of what they do to various qubit states.\n", + "This section introduces some of the common single-qubit gates, including their matrix form, their ket-bra decomposition, and a brief \"cheatsheet\" listing their effect on some common qubit states.\n", + "\n", + "You can use a tool called [Quirk](https://algasert.com/quirk) to visualize how these gates interact with various qubit states.\n", "\n", "This section relies on the following notation:\n", "\n", - "$$|+\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + |1\\rangle\\big) \\\\\n", - "|-\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - |1\\rangle\\big) \\\\\n", - "|i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + i|1\\rangle\\big) \\\\\n", - "|-i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - i|1\\rangle\\big) \\\\\n", - "|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$$" + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$|+\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + |1\\rangle\\big)$$|-\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - |1\\rangle\\big)$
$|i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + i|1\\rangle\\big)$$|-i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - i|1\\rangle\\big)$
\n", + "\n", + "$$|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$$" ] }, { @@ -273,7 +300,7 @@ "source": [ "## Pauli Gates\n", "\n", - "The Pauli gates, named after Wolfgang Pauli ([Wikipedia](https://en.wikipedia.org/wiki/Wolfgang_Pauli)), are based on the so-called **Pauli matrices**, which also have some special significance to the topic of measurement. All three Pauli gates are **self-adjoint**, meaning that each one is its own inverse.\n", + "The Pauli gates, named after [Wolfgang Pauli](https://en.wikipedia.org/wiki/Wolfgang_Pauli), are based on the so-called **Pauli matrices**. All three Pauli gates are **self-adjoint**, meaning that each one is its own inverse.\n", "\n", "### Pauli $X$ Gate\n", "\n", From 4d33bbdea9f81f6869e867845600205bba9e1f08 Mon Sep 17 00:00:00 2001 From: Mariia Mykhailova Date: Thu, 31 Oct 2019 17:18:25 -0700 Subject: [PATCH 8/8] Finish addressing comments, add new tutorial to lists --- Dockerfile | 1 + README.md | 2 + index.ipynb | 2 + tutorials/LinearAlgebra/LinearAlgebra.ipynb | 12 +- .../ReferenceImplementation.qs | 16 +- .../SingleQubitGates/SingleQubitGates.ipynb | 291 ++++++++---------- tutorials/SingleQubitGates/Tasks.qs | 15 +- tutorials/SingleQubitGates/Tests.qs | 47 ++- 8 files changed, 186 insertions(+), 200 deletions(-) diff --git a/Dockerfile b/Dockerfile index ed8cfbf8546..3ff00fc0ee5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,3 +44,4 @@ RUN ${HOME}/scripts/prebuild-kata.sh tutorials/DeutschJozsaAlgorithm DeutschJozs RUN ${HOME}/scripts/prebuild-kata.sh tutorials/ExploringGroversAlgorithm ExploringGroversAlgorithmTutorial.ipynb RUN ${HOME}/scripts/prebuild-kata.sh tutorials/LinearAlgebra LinearAlgebra.ipynb RUN ${HOME}/scripts/prebuild-kata.sh tutorials/Qubit Qubit.ipynb +RUN ${HOME}/scripts/prebuild-kata.sh tutorials/SingleQubitGates SingleQubitGates.ipynb diff --git a/README.md b/README.md index e49a8112fdd..5086f11a9ff 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Each kata is a separate project which includes: which are used to represent quantum states and quantum operations. * **[The Qubit](./tutorials/Qubit/)**. This tutorial introduces you to the concept of a qubit. +* **[Single-Qubit Gates](./tutorials/SingleQubitGates/)**. + This tutorial introduces the concept of a quantum gate and walks you through the most common single-qubit gates. * **[Deutsch–Jozsa algorithm](./tutorials/DeutschJozsaAlgorithm/)**. This tutorial teaches you to implement classical functions and equivalent quantum oracles, discusses the classical solution to the Deutsch–Jozsa problem, and introduces the Deutsch and Deutsch–Jozsa algorithms. diff --git a/index.ipynb b/index.ipynb index 3279a98978c..6d3cd70e250 100644 --- a/index.ipynb +++ b/index.ipynb @@ -17,6 +17,8 @@ " which are used to represent quantum states and quantum operations.\n", "* **[The Qubit](./tutorials/Qubit/Qubit.ipynb)**.\n", " This tutorial introduces you to the concept of a qubit.\n", + "* **[Single-Qubit Gates](./tutorials/SingleQubitGates/SingleQubitGates.ipynb)**.\n", + " This tutorial introduces the concept of a quantum gate and walks you through the most common single-qubit gates.\n", "* **[Deutsch–Jozsa algorithm](./tutorials/DeutschJozsaAlgorithm/DeutschJozsaAlgorithmTutorial.ipynb)**.\n", " This tutorial teaches you to implement classical functions and equivalent quantum oracles, \n", " discusses the classical solution to the Deutsch–Jozsa problem and introduces Deutsch and Deutsch–Jozsa algorithms.\n", diff --git a/tutorials/LinearAlgebra/LinearAlgebra.ipynb b/tutorials/LinearAlgebra/LinearAlgebra.ipynb index a4c5c01083a..ff153985725 100644 --- a/tutorials/LinearAlgebra/LinearAlgebra.ipynb +++ b/tutorials/LinearAlgebra/LinearAlgebra.ipynb @@ -962,17 +962,9 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Your function must return a value!\n" - ] - } - ], + "outputs": [], "source": [ "@exercise\n", "def find_eigenvalue(a : Matrix, v : Matrix) -> float:\n", diff --git a/tutorials/SingleQubitGates/ReferenceImplementation.qs b/tutorials/SingleQubitGates/ReferenceImplementation.qs index e7d8ac18d26..39cbc9a79cf 100644 --- a/tutorials/SingleQubitGates/ReferenceImplementation.qs +++ b/tutorials/SingleQubitGates/ReferenceImplementation.qs @@ -13,42 +13,44 @@ namespace Quantum.Kata.SingleQubitGates { open Microsoft.Quantum.Math; // Exercise 1. - operation ApplyY_Reference (q : Qubit) : Unit is Adj { + operation ApplyY_Reference (q : Qubit) : Unit is Adj+Ctl { Y(q); } // Exercise 2. - operation ApplyZ_Reference (q : Qubit) : Unit is Adj { + operation GlobalPhaseI_Reference (q : Qubit) : Unit is Adj+Ctl { + X(q); Z(q); + Y(q); } // Exercise 3. - operation ZeroFlip_Reference (q : Qubit) : Unit is Adj+Ctl { + operation SignFlipOnZero_Reference (q : Qubit) : Unit is Adj+Ctl { X(q); Z(q); X(q); } // Exercise 4. - operation PrepareMinus_Reference (q : Qubit) : Unit is Adj { + operation PrepareMinus_Reference (q : Qubit) : Unit is Adj+Ctl { X(q); H(q); } // Exercise 5. - operation ThreePiPhase_Reference (q : Qubit) : Unit is Adj+Ctl { + operation ThreeQuatersPiPhase_Reference (q : Qubit) : Unit is Adj+Ctl { S(q); T(q); } // Exercise 6. - operation RotatedState_Reference (alpha : Double, beta : Double, q : Qubit) : Unit is Adj { + operation PrepareRotatedState_Reference (alpha : Double, beta : Double, q : Qubit) : Unit is Adj+Ctl { let phi = ArcTan2(beta, alpha); Rx(2.0 * phi, q); } // Exercise 7. - operation ArbitraryState_Reference (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj { + operation PrepareArbitraryState_Reference (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj+Ctl { let phi = ArcTan2(beta, alpha); Ry(2.0 * phi, q); R1(theta, q); diff --git a/tutorials/SingleQubitGates/SingleQubitGates.ipynb b/tutorials/SingleQubitGates/SingleQubitGates.ipynb index 14149607516..80c7ab89ff5 100644 --- a/tutorials/SingleQubitGates/SingleQubitGates.ipynb +++ b/tutorials/SingleQubitGates/SingleQubitGates.ipynb @@ -26,24 +26,9 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "application/json": "[\"Microsoft.Quantum.Standard::0.9.1909.3002\",\"Microsoft.Quantum.Katas::0.9.1909.3002\"]", - "text/html": [ - "
  • Microsoft.Quantum.Standard::0.9.1909.3002
  • Microsoft.Quantum.Katas::0.9.1909.3002
" - ], - "text/plain": [ - "Microsoft.Quantum.Standard::0.9.1909.3002, Microsoft.Quantum.Katas::0.9.1909.3002" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "%package Microsoft.Quantum.Katas::0.9.1909.3002" ] @@ -74,7 +59,7 @@ "source": [ "# The Basics\n", "\n", - "There are certain properties common to all quantum gates. This section will introduce those properties, using the quantum $X$ gate as an example." + "There are certain properties common to all quantum gates. This section will introduce those properties, using the $X$ gate as an example." ] }, { @@ -244,14 +229,14 @@ ">\n", "> This section deals with finding the ket-bra decompositions of quantum gates. This section is not necessary to start working with quantum gates, so feel free to skip it for now, and come back to it later.\n", ">\n", - "> You can use the properties of [eigenvalues and eigenvectors](../LinearAlgebra/LinearAlgebra.ipynb#Eigenvalues-and-Eigenvectors) to find the ket-bra decomposition of any gate. Given a gate $A$, and its eigenvectors $|\\phi\\rangle$ and $|\\psi\\rangle$, if:\n", + "> You can use the properties of [eigenvalues and eigenvectors](../LinearAlgebra/LinearAlgebra.ipynb#Part-III:-Eigenvalues-and-Eigenvectors) to find the ket-bra decomposition of any gate. Given a gate $A$, and its orthogonal eigenvectors $|\\phi\\rangle$ and $|\\psi\\rangle$, if:\n", ">\n", - "> $$A|\\phi\\rangle = \\epsilon|\\phi\\rangle \\\\\n", - "A|\\psi\\rangle = \\zeta|\\psi\\rangle$$\n", + "> $$A|\\phi\\rangle = x_\\phi|\\phi\\rangle \\\\\n", + "A|\\psi\\rangle = x_\\psi|\\psi\\rangle$$\n", ">\n", "> Then:\n", ">\n", - "> $$A = \\epsilon|\\phi\\rangle\\langle\\phi| + \\zeta|\\psi\\rangle\\langle\\psi|$$\n", + "> $$A = x_\\phi|\\phi\\rangle\\langle\\phi| + x_\\psi|\\psi\\rangle\\langle\\psi|$$\n", ">\n", "> Let's use our $X$ gate as a simple example. The $X$ gate has two eigenvectors: $|+\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + |1\\rangle\\big)$ and $|-\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - |1\\rangle\\big)$. Their eigenvalues are $1$ and $-1$ respectively:\n", ">\n", @@ -289,9 +274,7 @@ " $|i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle + i|1\\rangle\\big)$\n", " $|-i\\rangle = \\frac{1}{\\sqrt{2}}\\big(|0\\rangle - i|1\\rangle\\big)$\n", " \n", - "\n", - "\n", - "$$|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$$" + "" ] }, { @@ -302,18 +285,6 @@ "\n", "The Pauli gates, named after [Wolfgang Pauli](https://en.wikipedia.org/wiki/Wolfgang_Pauli), are based on the so-called **Pauli matrices**. All three Pauli gates are **self-adjoint**, meaning that each one is its own inverse.\n", "\n", - "### Pauli $X$ Gate\n", - "\n", - "The $X$ gate is probably the most common quantum gate. It has the effect of \"inverting\" the state of the qubit it's applied to, swapping the amplitudes of the $|0\\rangle$ and $|1\\rangle$ states. It is sometimes referred to as the **bit flip** gate, or the **NOT** gate, because it acts like the classical NOT gate on the computational basis.\n", - "\n", - "### Pauli $Y$ Gate\n", - "\n", - "The $Y$ gate is somewhat similar to the $X$ gate, but it introduces a complex component into the mix.\n", - "\n", - "### Pauli $Z$ Gate\n", - "\n", - "The final Pauli gate is the $Z$ gate, sometimes known as the **phase flip** gate. It doesn't affect state $|0\\rangle$, and applies a phase of $-1$ to state $|1\\rangle$.\n", - "\n", "\n", " \n", " \n", @@ -325,7 +296,7 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -340,7 +311,7 @@ "X|-\\rangle = -|-\\rangle \\\\\n", "X|i\\rangle = i|-i\\rangle \\\\\n", "X|-i\\rangle = -i|i\\rangle$ \n", - " \n", + " \n", " \n", " \n", " \n", @@ -353,7 +324,7 @@ "Y|-\\rangle = i|+\\rangle \\\\\n", "Y|i\\rangle = |i\\rangle \\\\\n", "Y|-i\\rangle = -|-i\\rangle$\n", - " \n", + " \n", " \n", " \n", " \n", @@ -366,22 +337,33 @@ "Z|-\\rangle = |+\\rangle \\\\\n", "Z|i\\rangle = |-i\\rangle \\\\\n", "Z|-i\\rangle = |i\\rangle$ \n", - " \n", + " \n", " \n", - "
GateMatrixKet-BraApplying to $|\\psi\\rangle$Applying to $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$Applying to basis statesQ# Documentation
XX
$Y$YY
$Z$ZZ
" + "\n", + "\n", + "> The $X$ gate is sometimes referred to as the **bit flip** gate, or the **NOT** gate, because it acts like the classical NOT gate on the computational basis.\n", + ">\n", + "> The $Z$ gate is sometimes referred to as the **phase flip** gate." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Demo! Pauli X Gate\n", + "### Demo: Pauli Gates\n", "\n", "The following cell contains code demonstrating how to apply gates in Q#, using the Pauli $X$ gate as an example. It sets up a series of quantum states, and then shows the result of applying the $X$ gate to each one. To run the demo, run the next cell using `Ctrl+Enter` (`⌘+Enter` on a Mac), then run the cell after it to see the output.\n", "\n", - "Because qubits cannot be directly assigned or accessed, applying a gate doesn't return the resulting state of the qubit, but rather modifies the qubit's state in-place. This is why we never assign the output of these gates to anything in this demo - they don't produce any output.\n", + "In the previous tutorial we discussed that the qubit state in Q# cannot be directly assigned or accessed. The same logic is extended to the quantum gates: applying a gate to a qubit modifies the internal state of that qubit but doesn't return the resulting state of the qubit. This is why we never assign the output of these gates to any variables in this demo - they don't produce any output.\n", + "\n", + "Applying several gates in a row follows the same principle. In the mathematical notation applying an $X$ gate followed by a $Z$ gate to a state $|\\psi\\rangle$ is denoted as $Z(X(|\\psi\\rangle))$, because the result of applying a gate to a state is another state. In Q#, applying a gate doesn't return anything, so you can't use its output as an input to another gate - something like `Z(X(q))` will not produce expected result. Instead, to apply several gates to the same qubit, you need to call them separately in the order in which they are applied:\n", "\n", - "All the basic gates we will be covering in this tutorial are part of the [Intrinsic](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.intrinsic?view=qsharp-preview) namespace. We're also using the function [DumpMachine](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.diagnostics.dumpmachine?view=qsharp-preview) from the [Diagnostics](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.diagnostics?view=qsharp-preview) namespace to print the state of the quantum simulator." + "```\n", + "X(q);\n", + "Z(q);\n", + "```\n", + "\n", + "All the basic gates we will be covering in this tutorial are part of the [Intrinsic](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic) namespace. We're also using the function [DumpMachine](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.diagnostics.dumpmachine) to print the state of the quantum simulator." ] }, { @@ -396,63 +378,34 @@ "// To use a namespace, you need to use the `open` keyword to access it\n", "open Microsoft.Quantum.Diagnostics;\n", "\n", - "// The Intrinsic namespace is open by default\n", - "// We can open it explicitly, although this produces a warning\n", - "open Microsoft.Quantum.Intrinsic;\n", - "\n", - "operation PauliX_Demo () : Unit {\n", + "operation PauliGates_Demo () : Unit {\n", + " let divider = \"--------------------------------------------------------------------------------------------------\";\n", " // This allocates a qubit for us to work with\n", " using (q = Qubit()) {\n", - " let divider = \"--------------------------------------------------------------------------------------------------\";\n", - " \n", - " // This prints out the state of the simulator\n", - " // A newly allocated qubit starts in state |0❭\n", - " Message(\"Qubit in state |0❭:\");\n", - " DumpMachine();\n", - " Message(divider);\n", - " \n", - " // Let's apply the X gate\n", - " X(q);\n", - " Message(\"Qubit in state X|0❭ = |1❭:\");\n", - " DumpMachine();\n", - " Message(divider);\n", - " \n", - " // Applying the X gate again reverses the transformation\n", - " X(q);\n", - " Message(\"Qubit in state XX|0❭ = X|1❭ = |0❭:\");\n", - " DumpMachine();\n", - " Message(divider);\n", - " \n", - " // Now, let's use the Hadamard gate to put the qubit into a superposition\n", - " // You'll learn about the Hadamard gate later\n", - " H(q);\n", - " Message(\"Qubit in state |+❭:\");\n", - " DumpMachine();\n", - " Message(divider);\n", " \n", - " // Here's what happens when we apply the X gate to that superposition state\n", - " X(q);\n", - " Message(\"Qubit in state X|+❭ = |+❭:\");\n", + " // This will put the qubit into an uneven superposition |𝜓❭,\n", + " // where the amplitudes of |0⟩ and |1⟩ have different moduli\n", + " Ry(1.0, q);\n", + "\n", + " Message(\"Qubit in state |𝜓❭:\");\n", " DumpMachine();\n", " Message(divider);\n", " \n", - " // Now, let's change the qubit to state |-❭ and see what happens then\n", - " Reset(q);\n", + " // Let's apply the X gate; notice how it swaps the amplitudes of the |0❭ and |1❭ basis states\n", " X(q);\n", - " H(q);\n", - " Message(\"Qubit in state |-❭:\");\n", + " Message(\"Qubit in state X|𝜓❭:\");\n", " DumpMachine();\n", " Message(divider);\n", " \n", - " X(q);\n", - " Message(\"Qubit in state X|-❭ = -|-❭:\");\n", + " // Applying the Z gate adds -1 relative phase to the |1❭ basis states\n", + " Z(q);\n", + " Message(\"Qubit in state ZX|𝜓❭:\");\n", " DumpMachine();\n", " Message(divider);\n", " \n", - " // You can also invert any gate using the Adjoint functor\n", - " // This will apply the inverse of the X gate\n", - " Adjoint X(q);\n", - " Message(\"Qubit in state X'X|-❭ = |-❭:\");\n", + " // Finally, applying the Y gate returns the qubit to its original state |𝜓❭, with an extra global phase of i\n", + " Y(q);\n", + " Message(\"Qubit in state YZX|𝜓❭:\");\n", " DumpMachine();\n", " \n", " // This returns the qubit into state |0❭\n", @@ -469,20 +422,24 @@ }, "outputs": [], "source": [ - "%simulate PauliX_Demo" + "%simulate PauliGates_Demo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Exercise 1: The $Y$ gate\n", + "## Exercises\n", + "\n", + "The following exercises are designed to test your understanding of the concepts you've learned so far. \n", + "In each exercise your task is to implement an operation that applies a particular transformation to a qubit. \n", + "Unlike the demos you have seen so far, you don't have to allocate the qubit or to put it into a certain initial state - the qubit is already allocated, prepared in some state and provided to you as an input to the operation.\n", "\n", - "This is an exercise designed to test your understanding of the concepts thus far. Your task is to write an operation that applies a particular transformation to a qubit. Unlike the demos you have seen so far, you don't have to allocate the qubit - the operation you're writing takes the qubit as its input.\n", + "### Exercise 1: The $Y$ gate\n", "\n", - "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$.\n", "\n", - "**Goal:** Apply the $Y$ gate to the qubit." + "**Goal:** Apply the $Y$ gate to the qubit, i.e., transform the given state into $i\\alpha|1\\rangle - i\\beta|0\\rangle$." ] }, { @@ -493,10 +450,9 @@ "source": [ "%kata T1_ApplyY_Test\n", "\n", - "operation ApplyY (q : Qubit) : Unit is Adj {\n", - " // Fill in your code here\n", - " // Then run the cell to test your work\n", - " // For this exercise, just apply the Y gate, without doing anything else\n", + "operation ApplyY (q : Qubit) : Unit is Adj+Ctl {\n", + " // Fill in your code here, then run the cell to test your work.\n", + " // For this exercise, just apply the Y gate.\n", " // ...\n", "}" ] @@ -505,11 +461,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Exercise 2: The $Z$ gate\n", + "### Exercise 2: Applying a global phase $i$\n", "\n", - "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$.\n", "\n", - "**Goal:** Apply the $Z$ gate to the qubit." + "**Goal:** Use several Pauli gates to change the qubit state to $i|\\psi\\rangle = i\\alpha|0\\rangle + i\\beta|1\\rangle$." ] }, { @@ -518,9 +474,9 @@ "metadata": {}, "outputs": [], "source": [ - "%kata T2_ApplyZ_Test\n", + "%kata T2_GlobalPhaseI_Test\n", "\n", - "operation ApplyZ (q : Qubit) : Unit is Adj {\n", + "operation GlobalPhaseI (q : Qubit) : Unit is Adj+Ctl {\n", " // ...\n", "}" ] @@ -529,25 +485,18 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Exercise 3*: Phasing the $|0\\rangle$ state\n", + "### Exercise 3*: Applying a $-1$ phase to $|0\\rangle$ state\n", "\n", - "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$.\n", "\n", - "**Goal:** Use several basic gates to apply the transformation represented by the following matrix to the qubit:\n", + "**Goal:** Use several Pauli gates to change the qubit state to $- \\alpha|0\\rangle + \\beta|1\\rangle$, i.e., apply the transformation represented by the following matrix::\n", "\n", "$$\\begin{bmatrix} -1 & 0 \\\\ 0 & 1 \\end{bmatrix}$$\n", "\n", - "> Note that applying a gate doesn't return anything, so you can't assign its output to anything, and you can't use its output as the input to another function or operation. That is, something like `q2 = X(q);` or `Y(X(q));` doesn't work. This is because qubits are a special data type in Q# which can't be accessed and assigned directly. In order to apply several gates to the same qubit, you need to call them in sequence:\n", - ">\n", - "> ```C#\n", - "> X(q);\n", - "> Y(q);\n", - "> ```\n", - "\n", "
\n", "
\n", " Need a hint? Click here\n", - " Don't forget, there's a way you can tell what a matrix does to the basis states by looking at its matrix.\n", + " Experiment with different sequences of Pauli gates and observe their effect on the state.\n", "
" ] }, @@ -557,9 +506,9 @@ "metadata": {}, "outputs": [], "source": [ - "%kata T3_ZeroFlip_Test\n", + "%kata T3_SignFlipOnZero_Test\n", "\n", - "operation ZeroFlip (q : Qubit) : Unit is Adj+Ctl {\n", + "operation SignFlipOnZero (q : Qubit) : Unit is Adj+Ctl {\n", " // ...\n", "}" ] @@ -576,13 +525,13 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " Gate\n", " Matrix\n", " Ket-Bra\n", - " Applying to $|\\psi\\rangle$\n", + " Applying to $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$\n", " Q# Documentation\n", " \n", " \n", @@ -590,7 +539,7 @@ " $\\begin{bmatrix} 1 & 0 \\\\ 0 & 1 \\end{bmatrix}$\n", " $|0\\rangle\\langle0| + |1\\rangle\\langle1|$\n", " $I|\\psi\\rangle = |\\psi\\rangle$\n", - "
I\n", + " I\n", " \n", "" ] @@ -615,7 +564,7 @@ " Gate\n", " Matrix\n", " Ket-Bra\n", - " Applying to $|\\psi\\rangle$\n", + " Applying to $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$\n", " Applying to basis states\n", " Q# Documentation\n", " \n", @@ -630,11 +579,12 @@ "H|-\\rangle = |1\\rangle \\\\\n", "H|i\\rangle = e^{i\\pi/4}|-i\\rangle \\\\\n", "H|-i\\rangle = e^{-i\\pi/4}|i\\rangle$\n", - " H\n", + " H\n", " \n", "\n", "\n", - "> If you need a refresher on what $e^{i\\theta}$ means, you should review the section on [complex exponentiation](../ComplexArithmetic/ComplexArithmetic.ipynb#Imaginary-Exponents)." + "> As a reminder, $e^{i\\pi/4} = \\frac{1}{\\sqrt2} (1 + i)$ and $e^{-i\\pi/4} = \\frac{1}{\\sqrt2} (1 - i)$. \n", + "> If you need a refresher on calculating expressions like $e^{i\\theta}$, you should review the section on [complex exponentiation](../ComplexArithmetic/ComplexArithmetic.ipynb#Imaginary-Exponents)." ] }, { @@ -656,7 +606,7 @@ "source": [ "%kata T4_PrepareMinus_Test\n", "\n", - "operation PrepareMinus (q : Qubit) : Unit is Adj {\n", + "operation PrepareMinus (q : Qubit) : Unit is Adj+Ctl {\n", " // ...\n", "}" ] @@ -667,16 +617,7 @@ "source": [ "## Phase Shift Gates\n", "\n", - "The next two gates are known as phase shift gates. They apply a phase to the $|1\\rangle$ state, and leave the $|0\\rangle$ state untouched.\n", - "\n", - "#### The $S$ Gate\n", - "\n", - "The $S$ gate applies a phase of $i$ to the $|1\\rangle$ state. Applying the $S$ gate twice is equivalent to applying the $Z$ gate: $S^2 = Z$.\n", - "\n", - "\n", - "#### The $T$ Gate\n", - "\n", - "The $T$ gate applies a phase of $e^{i\\pi/4}$ to the $|1\\rangle$ state, \"rotating\" or \"phasing\" it by $\\frac{\\pi}{4}$. Applying it twice is equivalent to applying the $S$ gate: $T^2 = S$.\n", + "The next two gates are known as phase shift gates. They apply a phase to the $|1\\rangle$ state, and leave the $|0\\rangle$ state unchanged.\n", "\n", "\n", " \n", @@ -689,7 +630,7 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -704,18 +645,21 @@ "S|-\\rangle = |-i\\rangle \\\\\n", "S|i\\rangle = |-\\rangle \\\\\n", "S|-i\\rangle = |+\\rangle$\n", - " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", - " \n", + " \n", " \n", - "
GateMatrixKet-BraApplying to $|\\psi\\rangle$Applying to $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$Applying to basis statesQ# Documentation
SS
$T$$\\begin{bmatrix} 1 & 0 \\\\ 0 & e^{i\\pi/4} \\end{bmatrix}$$|0\\rangle\\langle0| + e^{i\\pi/4}|1\\rangle$$\\langle1|$$T|\\psi\\rangle = \\alpha|0\\rangle + \\beta e^{i\\pi/4}|1\\rangle$$T|\\psi\\rangle = \\alpha|0\\rangle + e^{i\\pi/4} \\beta |1\\rangle$$T|0\\rangle = |0\\rangle \\\\\n", "T|1\\rangle = e^{i\\pi/4}|1\\rangle$TT
" + "\n", + "\n", + "> Notice that applying the $T$ gate twice is equivalent to applying the $S$ gate, and applying the $S$ gate twice is equivalent to applying the $Z$ gate: \n", + "$$T^2 = S, S^2 = Z$$" ] }, { @@ -724,11 +668,17 @@ "source": [ "### Exercise 5: Three-fourths phase\n", "\n", - "**Input:** A qubit in an arbitrary state $|\\psi\\rangle$.\n", + "**Input:** A qubit in an arbitrary state $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$.\n", + "\n", + "**Goal:** Use several phase shift gates to apply the transformation represented by the following matrix to the given qubit:\n", "\n", - "**Goal:** Use several basic gates to apply the transformation represented by the following matrix to the given qubit:\n", + "$$\\begin{bmatrix} 1 & 0 \\\\ 0 & e^{3i\\pi/4} \\end{bmatrix}$$\n", "\n", - "$$\\begin{bmatrix} 1 & 0 \\\\ 0 & e^{3i\\pi/4} \\end{bmatrix}$$" + "
\n", + "
\n", + " Need a hint? Click here\n", + " As a reminder, $i = e^{i\\pi/2}$.\n", + "
" ] }, { @@ -737,9 +687,9 @@ "metadata": {}, "outputs": [], "source": [ - "%kata T5_ThreePiPhase_Test\n", + "%kata T5_ThreeQuatersPiPhase_Test\n", "\n", - "operation ThreePiPhase (q : Qubit) : Unit is Adj+Ctl {\n", + "operation ThreeQuatersPiPhase (q : Qubit) : Unit is Adj+Ctl {\n", " // ...\n", "}" ] @@ -750,9 +700,13 @@ "source": [ "## Rotation Gates\n", "\n", - "The next few gates are parametrized - their exact behavior depends on a numeric parameter - an angle $\\theta$, given in radians. By convention, the parameter is multiplied by $\\frac{1}{2}$ within the gate's matrix. These gates are the $X$ rotation gate $R_x(\\theta)$, $Y$ rotation gate $R_y(\\theta)$, $Z$ rotation gate $R_z(\\theta)$, and the arbitrary phase gate $R_1(\\theta)$.\n", + "The next few gates are parametrized: their exact behavior depends on a numeric parameter - an angle $\\theta$, given in radians. \n", + "These gates are the $X$ rotation gate $R_x(\\theta)$, $Y$ rotation gate $R_y(\\theta)$, $Z$ rotation gate $R_z(\\theta)$, and the arbitrary phase gate $R_1(\\theta)$. \n", + "Note that for the first three gates the parameter $\\theta$ is multiplied by $\\frac{1}{2}$ within the gate's matrix.\n", "\n", - "> These gates are known as rotation gates, because they represent rotations around various axes on the Bloch sphere. The Bloch sphere is a way of representing qubits visually, using the unobservable phase to map qubit states onto the surface of a sphere. Unfortunately, this visualization isn't very useful beyond single qubits, which is why we have opted not to include it in this tutorial series. If you are curious about it, you can learn more at the [VCPC](http://www.vcpc.univie.ac.at/~ian/hotlist/qc/talks/bloch-sphere.pdf) or [Wikipedia](https://en.wikipedia.org/wiki/Bloch_sphere).\n", + "> These gates are known as rotation gates, because they represent rotations around various axes on the Bloch sphere. The Bloch sphere is a way of representing the qubit states visually, mapping them onto the surface of a sphere. \n", + "> Unfortunately, this visualization isn't very useful beyond single-qubit states, which is why we have opted not to go into details in this tutorial series. \n", + "> If you are curious about it, you can learn more in [this slide deck](http://www.vcpc.univie.ac.at/~ian/hotlist/qc/talks/bloch-sphere.pdf) or in [Wikipedia article](https://en.wikipedia.org/wiki/Bloch_sphere).\n", "\n", "\n", " \n", @@ -763,7 +717,7 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -773,7 +727,7 @@ " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -781,7 +735,7 @@ " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -789,7 +743,7 @@ " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -797,13 +751,21 @@ " \n", " \n", - " \n", + " \n", " \n", "
GateMatrixApplying to $|\\psi\\rangle$Applying to $|\\psi\\rangle = \\alpha|0\\rangle + \\beta|1\\rangle$Applying to basis statesQ# Documentation
$R_x(\\theta)|\\psi\\rangle = (\\alpha\\cos\\frac{\\theta}{2} - i\\beta\\sin\\frac{\\theta}{2})|0\\rangle + (\\beta\\cos\\frac{\\theta}{2} - i\\alpha\\sin\\frac{\\theta}{2})|1\\rangle$$R_x(\\theta)|0\\rangle = \\cos\\frac{\\theta}{2}|0\\rangle - i\\sin\\frac{\\theta}{2}|1\\rangle \\\\\n", "R_x(\\theta)|1\\rangle = \\cos\\frac{\\theta}{2}|1\\rangle - i\\sin\\frac{\\theta}{2}|0\\rangle$RxRx
$R_y(\\theta)$$R_y(\\theta)|\\psi\\rangle = (\\alpha\\cos\\frac{\\theta}{2} - \\beta\\sin\\frac{\\theta}{2})|0\\rangle + (\\beta\\cos\\frac{\\theta}{2} + \\alpha\\sin\\frac{\\theta}{2})|1\\rangle$$R_y(\\theta)|0\\rangle = \\cos\\frac{\\theta}{2}|0\\rangle + \\sin\\frac{\\theta}{2}|1\\rangle \\\\\n", "R_y(\\theta)|1\\rangle = \\cos\\frac{\\theta}{2}|1\\rangle - \\sin\\frac{\\theta}{2}|0\\rangle$RyRy
$R_z(\\theta)$$R_z(\\theta)|\\psi\\rangle = \\alpha e^{-i\\theta/2}|0\\rangle + \\beta e^{i\\theta/2}|1\\rangle$$R_z(\\theta)|0\\rangle = e^{-i\\theta/2}|0\\rangle \\\\\n", "R_z(\\theta)|1\\rangle = e^{i\\theta/2}|1\\rangle$RzRz
$R_1(\\theta)$$R_1(\\theta)|\\psi\\rangle = \\alpha|0\\rangle + \\beta e^{i\\theta}|1\\rangle$$R_1(\\theta)|0\\rangle = |0\\rangle \\\\\n", "R_1(\\theta)|1\\rangle = e^{i\\theta}|1\\rangle$R1R1
\n", "\n", - "You have already encountered some special cases of the phase gate. $T = R_1(\\frac{\\pi}{4})$, $S = R_1(\\frac{\\pi}{2})$, and $Z = R_1(\\pi)$. In addition, the phase gate is very closely related to the $Z$ rotation gate. Applying a phase gate is equivalent to applying the $Z$ rotation gate, and then applying a **global phase** to the entire qubit: $R_1(\\theta) = e^{i\\theta/2}R_z(\\theta)$.\n", + "You have already encountered some special cases of the $R_1$ gate: \n", + "\n", + "$$T = R_1(\\frac{\\pi}{4}), S = R_1(\\frac{\\pi}{2}), Z = R_1(\\pi)$$\n", "\n", - "In addition, the rotation gates are very closely related to their respective Pauli gates. Notice: $X = iR_x(\\pi)$, $Y = iR_y(\\pi)$, and $Z = iR_z(\\pi)$." + "In addition, this gate is closely related to the $R_z$ gate: applying $R_1$ gate is equivalent to applying the $R_z$ gate, and then applying a global phase: \n", + "\n", + "$$R_1(\\theta) = e^{i\\theta/2}R_z(\\theta)$$\n", + "\n", + "In addition, the rotation gates are very closely related to their respective Pauli gates: \n", + "\n", + "$$X = iR_x(\\pi), Y = iR_y(\\pi), Z = iR_z(\\pi)$$" ] }, { @@ -814,15 +776,19 @@ "\n", "**Inputs:**\n", "\n", - "1. A non-negative real number $\\alpha$.\n", - "2. A non-negative real number $\\beta = \\sqrt{1 - \\alpha^2}$.\n", + "1. Real numbers $\\alpha$ and $\\beta$ such that $\\alpha^2 + \\beta^2 = 1$.\n", "3. A qubit in state $|0\\rangle$.\n", "\n", "**Goal:** Use a rotation gate to transform the qubit into state $\\alpha|0\\rangle -i\\beta|1\\rangle$\n", "\n", "> You will probably need functions from the [Math](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.math?view=qsharp-preview) namespace, specifically [ArcTan2](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.math.arctan2?view=qsharp-preview).\n", "> \n", - "> You can assign variables in Q# by using the `let` keyword: `let num = 3;` or `let result = Function(input);`" + "> You can assign variables in Q# by using the `let` keyword: `let num = 3;` or `let result = Function(input);`\n", + "\n", + "
\n", + " Need a hint? Click here\n", + " Don't forget, you can tell what a matrix does to the basis states by looking at its matrix: the first column of the matrix is the state into which it will transform the $|0\\rangle$ state.\n", + "
" ] }, { @@ -831,11 +797,11 @@ "metadata": {}, "outputs": [], "source": [ - "%kata T6_RotatedState_Test\n", + "%kata T6_PrepareRotatedState_Test\n", "\n", "open Microsoft.Quantum.Math;\n", "\n", - "operation RotatedState (alpha : Double, beta : Double, q : Qubit) : Unit is Adj {\n", + "operation PrepareRotatedState (alpha : Double, beta : Double, q : Qubit) : Unit is Adj+Ctl {\n", " // ...\n", "}" ] @@ -855,7 +821,7 @@ "\n", "**Goal:** Transform the qubit into state $\\alpha|0\\rangle + e^{i\\theta}\\beta|1\\rangle$.\n", "\n", - "> Because only the relative amplitudes and relative phase have any physical meaning, this allows us to prepare any quantum state we want to." + "> Since only the relative amplitudes and relative phase have any physical meaning, this allows us to prepare any single-qubit quantum state we want to." ] }, { @@ -864,11 +830,11 @@ "metadata": {}, "outputs": [], "source": [ - "%kata T7_ArbitraryState_Test\n", + "%kata T7_PrepareArbitraryState_Test\n", "\n", "open Microsoft.Quantum.Math;\n", "\n", - "operation ArbitraryState (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj {\n", + "operation PrepareArbitraryState (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj+Ctl {\n", " // ...\n", "}" ] @@ -879,7 +845,8 @@ "source": [ "## Conclusion\n", "\n", - "You should now be familiar enough with quantum computing to complete the first part of the [Basic Gates Kata](../../BasicGates/BasicGates.ipynb). When you are ready, you can proceed to the [Multi-qubit Systems](../MultiQubitSystems/MultiQubitSystems.ipynb) tutorial, and then the [Multi-qubit Gates](../MultiQubitGates/MultiQubitGates.ipynb) tutorial." + "Congratulations! You have learned enough to try solving the first part of the [Basic Gates kata](../../BasicGates/BasicGates.ipynb). \n", + "When you are done with that, proceed to learn about the multi-qubit systems and the multi-qubit gates." ] } ], diff --git a/tutorials/SingleQubitGates/Tasks.qs b/tutorials/SingleQubitGates/Tasks.qs index cf7e4bbebe6..e15d2325c9d 100644 --- a/tutorials/SingleQubitGates/Tasks.qs +++ b/tutorials/SingleQubitGates/Tasks.qs @@ -12,42 +12,43 @@ namespace Quantum.Kata.SingleQubitGates { open Microsoft.Quantum.Math; // Exercise 1. - operation ApplyY (q : Qubit) : Unit is Adj { + operation ApplyY (q : Qubit) : Unit is Adj+Ctl { // ... } // Exercise 2. - operation ApplyZ (q : Qubit) : Unit is Adj { + operation GlobalPhaseI (q : Qubit) : Unit is Adj+Ctl { // ... } // Exercise 3. - operation ZeroFlip (q : Qubit) : Unit is Adj+Ctl { + operation SignFlipOnZero (q : Qubit) : Unit is Adj+Ctl { // ... } // Exercise 4. - operation PrepareMinus (q : Qubit) : Unit is Adj { + operation PrepareMinus (q : Qubit) : Unit is Adj+Ctl { // ... } // Exercise 5. - operation ThreePiPhase (q : Qubit) : Unit is Adj+Ctl { + operation ThreeQuatersPiPhase (q : Qubit) : Unit is Adj+Ctl { // ... } - operation RotatedState (alpha : Double, beta : Double, q : Qubit) : Unit is Adj { + // Exercise 5. + operation PrepareRotatedState (alpha : Double, beta : Double, q : Qubit) : Unit is Adj+Ctl { // ... } // Exercise 7. - operation ArbitraryState (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj { + operation PrepareArbitraryState (alpha : Double, beta : Double, theta : Double, q : Qubit) : Unit is Adj+Ctl { // ... } diff --git a/tutorials/SingleQubitGates/Tests.qs b/tutorials/SingleQubitGates/Tests.qs index fde6dd43632..b186cfc401f 100644 --- a/tutorials/SingleQubitGates/Tests.qs +++ b/tutorials/SingleQubitGates/Tests.qs @@ -26,46 +26,65 @@ namespace Quantum.Kata.SingleQubitGates { Controlled op([qs[0]], qs[1]); } - operation ArrayWrapperOperation (op : (Qubit => Unit is Adj), qs : Qubit[]) : Unit is Adj { - op(qs[0]); + + // ------------------------------------------------------ + operation AssertEqualOnZeroState (testImpl : (Qubit => Unit is Ctl), refImpl : (Qubit => Unit is Adj+Ctl)) : Unit { + using (qs = Qubit[2]) { + within { + H(qs[0]); + } + apply { + // apply operation that needs to be tested + Controlled testImpl([qs[0]], qs[1]); + + // apply adjoint reference operation + Adjoint Controlled refImpl([qs[0]], qs[1]); + } + + // assert that all qubits end up in |0⟩ state + AssertAllZero(qs); + } } // Exercise 1. operation T1_ApplyY_Test () : Unit { - AssertOperationsEqualReferenced(1, ArrayWrapperOperation(ApplyY, _), ArrayWrapperOperation(Y, _)); + AssertOperationsEqualReferenced(2, ControlledArrayWrapperOperation(ApplyY, _), ControlledArrayWrapperOperation(Y, _)); } // Exercise 2. - operation T2_ApplyZ_Test () : Unit { - AssertOperationsEqualReferenced(1, ArrayWrapperOperation(ApplyZ, _), ArrayWrapperOperation(Z, _)); + operation T2_GlobalPhaseI_Test () : Unit { + AssertOperationsEqualReferenced(2, ControlledArrayWrapperOperation(GlobalPhaseI, _), ControlledArrayWrapperOperation(GlobalPhaseI_Reference, _)); } // Exercise 3. - operation T3_ZeroFlip_Test () : Unit { - AssertOperationsEqualReferenced(2, ControlledArrayWrapperOperation(ZeroFlip, _), ControlledArrayWrapperOperation(ZeroFlip_Reference, _)); + operation T3_SignFlipOnZero_Test () : Unit { + AssertOperationsEqualReferenced(2, ControlledArrayWrapperOperation(SignFlipOnZero, _), ControlledArrayWrapperOperation(SignFlipOnZero_Reference, _)); } // Exercise 4. operation T4_PrepareMinus_Test () : Unit { - AssertOperationsEqualReferenced(1, ArrayWrapperOperation(PrepareMinus, _), ArrayWrapperOperation(PrepareMinus_Reference, _)); + AssertEqualOnZeroState(PrepareMinus, PrepareMinus_Reference); } // Exercise 5. - operation T5_ThreePiPhase_Test () : Unit { - AssertOperationsEqualReferenced(2, ArrayWrapperOperation(ThreePiPhase, _), ArrayWrapperOperation(ThreePiPhase_Reference, _)); + operation T5_ThreeQuatersPiPhase_Test () : Unit { + AssertOperationsEqualReferenced(2, ControlledArrayWrapperOperation(ThreeQuatersPiPhase, _), ControlledArrayWrapperOperation(ThreeQuatersPiPhase_Reference, _)); } - operation T6_RotatedState_Test () : Unit { + // Exercise 6. + operation T6_PrepareRotatedState_Test () : Unit { for (i in 0 .. 10) { - AssertOperationsEqualReferenced(1, ArrayWrapperOperation(RotatedState(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), _), _), ArrayWrapperOperation(RotatedState_Reference(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), _), _)); + AssertEqualOnZeroState(PrepareRotatedState(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), _), + PrepareRotatedState_Reference(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), _)); } } // Exercise 7. - operation T7_ArbitraryState_Test () : Unit { + operation T7_PrepareArbitraryState_Test () : Unit { for (i in 0 .. 10) { for (j in 0 .. 10) { - AssertOperationsEqualReferenced(1, ArrayWrapperOperation(ArbitraryState(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), IntAsDouble(j), _), _), ArrayWrapperOperation(ArbitraryState_Reference(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), IntAsDouble(j), _), _)); + AssertEqualOnZeroState(PrepareArbitraryState(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), IntAsDouble(j), _), + PrepareArbitraryState_Reference(Cos(IntAsDouble(i)), Sin(IntAsDouble(i)), IntAsDouble(j), _)); } } }