From 9ce81ce60e64b5230bb10153bada03165ed4445a Mon Sep 17 00:00:00 2001 From: Mariia Mykhailova Date: Mon, 15 Jun 2020 13:05:36 -0700 Subject: [PATCH] Add DistinguishUnitaries kata (#378) --- .../DistinguishUnitaries.csproj | 26 ++ .../DistinguishUnitaries.ipynb | 286 ++++++++++++ DistinguishUnitaries/DistinguishUnitaries.sln | 31 ++ DistinguishUnitaries/README.md | 5 + .../ReferenceImplementation.qs | 95 ++++ DistinguishUnitaries/Tasks.qs | 105 +++++ DistinguishUnitaries/TestSuiteRunner.cs | 43 ++ DistinguishUnitaries/Tests.qs | 142 ++++++ .../Workbook_DistinguishUnitaries.ipynb | 422 ++++++++++++++++++ Dockerfile | 1 + README.md | 4 +- index.ipynb | 2 + 12 files changed, 1161 insertions(+), 1 deletion(-) create mode 100644 DistinguishUnitaries/DistinguishUnitaries.csproj create mode 100644 DistinguishUnitaries/DistinguishUnitaries.ipynb create mode 100644 DistinguishUnitaries/DistinguishUnitaries.sln create mode 100644 DistinguishUnitaries/README.md create mode 100644 DistinguishUnitaries/ReferenceImplementation.qs create mode 100644 DistinguishUnitaries/Tasks.qs create mode 100644 DistinguishUnitaries/TestSuiteRunner.cs create mode 100644 DistinguishUnitaries/Tests.qs create mode 100644 DistinguishUnitaries/Workbook_DistinguishUnitaries.ipynb diff --git a/DistinguishUnitaries/DistinguishUnitaries.csproj b/DistinguishUnitaries/DistinguishUnitaries.csproj new file mode 100644 index 00000000..a3b7438c --- /dev/null +++ b/DistinguishUnitaries/DistinguishUnitaries.csproj @@ -0,0 +1,26 @@ + + + netcoreapp3.1 + x64 + false + Quantum.Kata.DistinguishUnitaries + + + + + + + + + + + + + + + + + + + + diff --git a/DistinguishUnitaries/DistinguishUnitaries.ipynb b/DistinguishUnitaries/DistinguishUnitaries.ipynb new file mode 100644 index 00000000..64a7d8c4 --- /dev/null +++ b/DistinguishUnitaries/DistinguishUnitaries.ipynb @@ -0,0 +1,286 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Distinguish Unitaries Kata\n", + "\n", + "The **Distinguish Unitaries** quantum kata offers tasks in which you are given a unitary and have to figure out which of the list it is by designing and performing experiments on it.\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", + "The 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 test name\" error):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%package Microsoft.Quantum.Katas::0.11.2006.403" + ] + }, + { + "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": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%workspace reload" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part I. Single-Qubit Gates" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.1. Identity or Pauli X?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the identity (**I** gate)\n", + "or the Pauli X gate (**X** gate). \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **I** gate, 1 if the given operation is the **X** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T11_DistinguishIfromX_Test \n", + "\n", + "operation DistinguishIfromX (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " // ...\n", + " return -1;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Can't come up with a solution? See the explained solution in the [Distinguish Unitaries Workbook](./Workbook_DistinguishUnitaries.ipynb#Task-1.1.-Identity-or-Pauli-X?).*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.2. Identity or Pauli Z?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the identity (**I** gate)\n", + "or the Pauli Z gate (**Z** gate). \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **I** gate, 1 if the given operation is the **Z** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T12_DistinguishIfromZ_Test \n", + "\n", + "operation DistinguishIfromZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " // ...\n", + " return -1;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Can't come up with a solution? See the explained solution in the [Distinguish Unitaries Workbook](./Workbook_DistinguishUnitaries.ipynb#Task-1.2.-Identity-or-Pauli-Z?).*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.3. Z or S?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the **Z** gate\n", + "or the **S** gate. \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **Z** gate, 1 if the given operation is the **S** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants at most **twice**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T13_DistinguishZfromS_Test \n", + "\n", + "operation DistinguishZfromS (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " // ...\n", + " return -1;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Can't come up with a solution? See the explained solution in the [Distinguish Unitaries Workbook](./Workbook_DistinguishUnitaries.ipynb#Task-1.3.-Z-or-S?).*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.4. Z or -Z?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the **Z** gate\n", + "or the minus **Z** gate (i.e., the gate $- |0\\rangle\\langle0| + |1\\rangle\\langle1|$). \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **Z** gate, 1 if the given operation is the **-Z** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T14_DistinguishZfromMinusZ_Test \n", + "\n", + "operation DistinguishZfromMinusZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " // ...\n", + " return -1;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Can't come up with a solution? See the explained solution in the [Distinguish Unitaries Workbook](./Workbook_DistinguishUnitaries.ipynb#Task-1.4.-Z-or--Z?).*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part II. Multi-Qubit Gates" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 2.1. $I \\otimes X$ or CNOT?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the $I \\otimes X$ (the X gate applied to the second qubit)\n", + "or the **CNOT** gate with the first qubit as control and the second qubit as target.\n", + "* The operation will accept an array of qubits as input, but it will fail if the array is empty or has one or more than two qubits.\n", + "* The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is $I \\otimes X$, 1 if the given operation is the **CNOT** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T21_DistinguishIXfromCNOT_Test\n", + "\n", + "operation DistinguishIXfromCNOT (unitary : (Qubit[] => Unit is Adj+Ctl)) : Int {\n", + " // ...\n", + " return -1;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Can't come up with a solution? See the explained solution in the [Distinguish Unitaries Workbook](./Workbook_DistinguishUnitaries.ipynb#Task-2.1.-$I-\\otimes-X$-or-CNOT?).*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*To be continued...*" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Q#", + "language": "qsharp", + "name": "iqsharp" + }, + "language_info": { + "file_extension": ".qs", + "mimetype": "text/x-qsharp", + "name": "qsharp", + "version": "0.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/DistinguishUnitaries/DistinguishUnitaries.sln b/DistinguishUnitaries/DistinguishUnitaries.sln new file mode 100644 index 00000000..198beb73 --- /dev/null +++ b/DistinguishUnitaries/DistinguishUnitaries.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29926.136 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DistinguishUnitaries", "DistinguishUnitaries.csproj", "{F7A0175F-4217-4343-8A3C-EA68FAAB6B7B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "..\utilities\Common\Common.csproj", "{F8640A84-E48D-4C12-BB11-94E709003CE9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F7A0175F-4217-4343-8A3C-EA68FAAB6B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7A0175F-4217-4343-8A3C-EA68FAAB6B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7A0175F-4217-4343-8A3C-EA68FAAB6B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7A0175F-4217-4343-8A3C-EA68FAAB6B7B}.Release|Any CPU.Build.0 = Release|Any CPU + {F8640A84-E48D-4C12-BB11-94E709003CE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8640A84-E48D-4C12-BB11-94E709003CE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8640A84-E48D-4C12-BB11-94E709003CE9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8640A84-E48D-4C12-BB11-94E709003CE9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4E22BDB7-FE55-4D1C-98FA-BD7612C8525D} + EndGlobalSection +EndGlobal diff --git a/DistinguishUnitaries/README.md b/DistinguishUnitaries/README.md new file mode 100644 index 00000000..b6960eab --- /dev/null +++ b/DistinguishUnitaries/README.md @@ -0,0 +1,5 @@ +# Welcome! + +The "Distinguish Unitaries" kata offers tasks in which you are given a unitary and have to figure out which of the list it is by designing and performing experiments on it. + +You can [run the DistinguishUnitaries kata as a Jupyter Notebook](https://mybinder.org/v2/gh/Microsoft/QuantumKatas/master?filepath=DistinguishUnitaries%2FDistinguishUnitaries.ipynb)! \ No newline at end of file diff --git a/DistinguishUnitaries/ReferenceImplementation.qs b/DistinguishUnitaries/ReferenceImplementation.qs new file mode 100644 index 00000000..29b2677c --- /dev/null +++ b/DistinguishUnitaries/ReferenceImplementation.qs @@ -0,0 +1,95 @@ +// 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.DistinguishUnitaries { + open Microsoft.Quantum.Arithmetic; + open Microsoft.Quantum.Characterization; + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Oracles; + + // Task 1.1. Identity or Pauli X? + // Output: 0 if the given operation is the I gate, + // 1 if the given operation is the X gate. + operation DistinguishIfromX_Reference (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // apply operation to the |0⟩ state and measure: |0⟩ means I, |1⟩ means X + using (q = Qubit()) { + unitary(q); + return MResetZ(q) == Zero ? 0 | 1; + } + } + + + // Task 1.2. Identity or Pauli Z? + // Output: 0 if the given operation is the I gate, + // 1 if the given operation is the Z gate. + operation DistinguishIfromZ_Reference (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // apply operation to the |+⟩ state and measure: |+⟩ means I, |-⟩ means Z + using (q = Qubit()) { + H(q); + unitary(q); + H(q); + return MResetZ(q) == Zero ? 0 | 1; + } + } + + + // Task 1.3. Z or S? + // Output: 0 if the given operation is the Z gate, + // 1 if the given operation is the S gate. + operation DistinguishZfromS_Reference (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // apply operation twice to |+⟩ state and measure: |+⟩ means Z, |-⟩ means S + // X will end up as XXX = X, H will end up as HXH = Z (does not change |0⟩ state) + using (q = Qubit()) { + H(q); + unitary(q); + unitary(q); + H(q); + return MResetZ(q) == Zero ? 0 | 1; + } + } + + + // Task 1.4. Z or -Z? + // Output: 0 if the given operation is the Z gate, + // 1 if the given operation is the -Z gate. + operation DistinguishZfromMinusZ_Reference (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // apply Controlled unitary to (|0⟩ + |1⟩) ⊗ |0⟩ state: Z will leave it unchanged while -Z will make it into (|0⟩ + |1⟩) ⊗ |0⟩ + using (qs = Qubit[2]) { + // prep (|0⟩ + |1⟩) ⊗ |0⟩ + H(qs[0]); + + Controlled unitary(qs[0..0], qs[1]); + + H(qs[0]); + // |0⟩ means it was Z, |1⟩ means -Z + + return MResetZ(qs[0]) == Zero ? 0 | 1; + } + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Multi-Qubit Gates + ////////////////////////////////////////////////////////////////// + + // Task 2.1. I ⊗ X or CNOT? + // Output: 0 if the given operation is I ⊗ X, + // 1 if the given operation is the CNOT gate. + operation DistinguishIXfromCNOT_Reference (unitary : (Qubit[] => Unit is Adj+Ctl)) : Int { + // apply to |00⟩ and measure 2nd qubit: CNOT will do nothing, I ⊗ X will change to |01⟩ + using (qs = Qubit[2]) { + unitary(qs); + return MResetZ(qs[1]) == One ? 0 | 1; + } + } + + +} diff --git a/DistinguishUnitaries/Tasks.qs b/DistinguishUnitaries/Tasks.qs new file mode 100644 index 00000000..6c3f611c --- /dev/null +++ b/DistinguishUnitaries/Tasks.qs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Quantum.Kata.DistinguishUnitaries { + + open Microsoft.Quantum.Intrinsic; + + + ////////////////////////////////////////////////////////////////// + // Welcome! + ////////////////////////////////////////////////////////////////// + + // "Distinguishing Unitaries" quantum kata is a series of exercises designed + // to help you learn to think about unitary transformations in a different way. + // It covers figuring out ways to distinguish several unitaries from the given list + // by performing experiments on them. + + // 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. + + // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. + + + ////////////////////////////////////////////////////////////////// + // Part I. Single-Qubit Gates + ////////////////////////////////////////////////////////////////// + + // Task 1.1. Identity or Pauli X? + // Input: An operation that implements a single-qubit unitary transformation: + // either the identity (I gate) + // or the Pauli X gate (X gate). + // The operation will have Adjoint and Controlled variants defined. + // Output: 0 if the given operation is the I gate, + // 1 if the given operation is the X gate. + // You are allowed to apply the given operation and its adjoint/controlled variants exactly once. + operation DistinguishIfromX (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // ... + return -1; + } + + + // Task 1.2. Identity or Pauli Z? + // Input: An operation that implements a single-qubit unitary transformation: + // either the identity (I gate) + // or the Pauli Z gate (Z gate). + // The operation will have Adjoint and Controlled variants defined. + // Output: 0 if the given operation is the I gate, + // 1 if the given operation is the Z gate. + // You are allowed to apply the given operation and its adjoint/controlled variants exactly once. + operation DistinguishIfromZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // ... + return -1; + } + + + // Task 1.3. Z or S? + // Input: An operation that implements a single-qubit unitary transformation: + // either the Pauli Z gate + // or the S gate. + // The operation will have Adjoint and Controlled variants defined. + // Output: 0 if the given operation is the Z gate, + // 1 if the given operation is the S gate. + // You are allowed to apply the given operation and its adjoint/controlled variants at most twice. + operation DistinguishZfromS (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // ... + return -1; + } + + + // Task 1.4. Z or -Z? + // Input: An operation that implements a single-qubit unitary transformation: + // either the Pauli Z gate + // or the minus Pauli Z gate (i.e., a gate -|0〉〈0| + |1〉〈1|). + // The operation will have Adjoint and Controlled variants defined. + // Output: 0 if the given operation is the Z gate, + // 1 if the given operation is the -Z gate. + // You are allowed to apply the given operation and its adjoint/controlled variants exactly once. + operation DistinguishZfromMinusZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int { + // ... + return -1; + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Multi-Qubit Gates + ////////////////////////////////////////////////////////////////// + + // Task 2.1. I ⊗ X or CNOT? + // Input: An operation that implements a two-qubit unitary transformation: + // either I ⊗ X (the X gate applied to the second qubit) + // or the CNOT gate with the first qubit as control and the second qubit as target. + // The operation will accept an array of qubits as input, but it will fail if the array is empty or has one or more than two qubits. + // The operation will have Adjoint and Controlled variants defined. + // Output: 0 if the given operation is I ⊗ X, + // 1 if the given operation is the CNOT gate. + // You are allowed to apply the given operation and its adjoint/controlled variants exactly once. + operation DistinguishIXfromCNOT (unitary : (Qubit[] => Unit is Adj+Ctl)) : Int { + // ... + return -1; + } + + +} diff --git a/DistinguishUnitaries/TestSuiteRunner.cs b/DistinguishUnitaries/TestSuiteRunner.cs new file mode 100644 index 00000000..6dcffe8d --- /dev/null +++ b/DistinguishUnitaries/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.DistinguishUnitaries +{ + 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.DistinguishUnitaries. + /// + [OperationDriver(TestNamespace = "Quantum.Kata.DistinguishUnitaries")] + 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/DistinguishUnitaries/Tests.qs b/DistinguishUnitaries/Tests.qs new file mode 100644 index 00000000..36f715fb --- /dev/null +++ b/DistinguishUnitaries/Tests.qs @@ -0,0 +1,142 @@ +// 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.DistinguishUnitaries { + 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 Microsoft.Quantum.Arrays; + + open Quantum.Kata.Utils; + + // "Framework" operation for testing tasks for distinguishing unitaries + // "unitaries" is the list of unitaries that can be passed to the task + // "testImpl" - the solution to be tested + // "maxCalls" - max # of calls to the unitary that are allowed (-1 means unlimited) + operation DistinguishUnitaries_Framework<'UInput> ( + unitaries : ('UInput => Unit is Adj+Ctl)[], + testImpl : (('UInput => Unit is Adj+Ctl) => Int), + maxCalls : Int) : Unit { + + let nUnitaries = Length(unitaries); + let nTotal = 100; + mutable wrongClassifications = new Int[nUnitaries * nUnitaries]; // [i * nU + j] number of times unitary i was classified as j + mutable unknownClassifications = new Int[nUnitaries]; // number of times unitary i was classified as something unknown + + for (i in 1 .. nTotal) { + // get a random integer to define the unitary used + let actualIndex = RandomInt(nUnitaries); + + ResetOracleCallsCount(); + + // get the solution's answer and verify that it's a match + let returnedIndex = testImpl(unitaries[actualIndex]); + + // check the constraint on the number of allowed calls to the unitary + // note that a unitary can be implemented as Controlled on |1⟩, so we need to count variants as well + if (maxCalls > 0) { + let actualCalls = GetOracleCallsCount(unitaries[actualIndex]) + + GetOracleCallsCount(Adjoint unitaries[actualIndex]) + + GetOracleCallsCount(Controlled unitaries[actualIndex]); + if (actualCalls > maxCalls) { + fail $"You are allowed to do at most {maxCalls} calls, and you did {actualCalls}"; + } + } + + if (returnedIndex != actualIndex) { + if (returnedIndex < 0 or returnedIndex >= nUnitaries) { + set unknownClassifications w/= actualIndex <- unknownClassifications[actualIndex] + 1; + } else { + let index = actualIndex * nUnitaries + returnedIndex; + set wrongClassifications w/= index <- wrongClassifications[index] + 1; + } + } + } + + mutable totalMisclassifications = 0; + for (i in 0 .. nUnitaries - 1) { + for (j in 0 .. nUnitaries - 1) { + let misclassifiedIasJ = wrongClassifications[(i * nUnitaries) + j]; + if (misclassifiedIasJ != 0) { + set totalMisclassifications += misclassifiedIasJ; + Message($"Misclassified {i} as {j} in {misclassifiedIasJ} test runs."); + } + } + if (unknownClassifications[i] != 0) { + set totalMisclassifications += unknownClassifications[i]; + Message($"Misclassified {i} as unknown unitary in {unknownClassifications[i]} test runs."); + } + } + // This check will tell the total number of failed classifications + Fact(totalMisclassifications == 0, $"{totalMisclassifications} test runs out of {nTotal} returned incorrect state."); + } + + + // ------------------------------------------------------ + // A pair of helper wrappers used to differentiate the unitary we pass as an argument from gates used normally + operation SingleQubitGateWrapper (unitary : (Qubit => Unit is Adj+Ctl), register : Qubit) : Unit is Adj+Ctl { + unitary(register); + } + + function SingleQubitGateAsUnitary (unitary : (Qubit => Unit is Adj+Ctl)) : (Qubit => Unit is Adj+Ctl) { + return SingleQubitGateWrapper(unitary, _); + } + + + operation T11_DistinguishIfromX_Test () : Unit { + DistinguishUnitaries_Framework(Mapped(SingleQubitGateAsUnitary, [I, X]), DistinguishIfromX, 1); + } + + + // ------------------------------------------------------ + operation T12_DistinguishIfromZ_Test () : Unit { + DistinguishUnitaries_Framework(Mapped(SingleQubitGateAsUnitary, [I, Z]), DistinguishIfromZ, 1); + } + + + // ------------------------------------------------------ + operation T13_DistinguishZfromS_Test () : Unit { + DistinguishUnitaries_Framework(Mapped(SingleQubitGateAsUnitary, [Z, S]), DistinguishZfromS, 2); + } + + + // ------------------------------------------------------ + operation MinusOne (q : Qubit) : Unit is Adj+Ctl { + within { X(q); } + apply { Z(q); } + Z(q); + } + + operation T14_DistinguishZfromMinusZ_Test () : Unit { + DistinguishUnitaries_Framework(Mapped(SingleQubitGateAsUnitary, [Z, BoundCA([Z, MinusOne])]), DistinguishZfromMinusZ, 1); + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Multi-Qubit Gates + ////////////////////////////////////////////////////////////////// + + operation IXWrapper (qs : Qubit[]) : Unit is Adj+Ctl { + Fact(Length(qs) == 2, "This unitary can only be applied to arrays of length 2."); + X(qs[1]); + } + + operation CNOTWrapper (qs : Qubit[]) : Unit is Adj+Ctl { + Fact(Length(qs) == 2, "This unitary can only be applied to arrays of length 2."); + CNOT(qs[0], qs[1]); + } + + operation T21_DistinguishIXfromCNOT_Test () : Unit { + DistinguishUnitaries_Framework([IXWrapper, CNOTWrapper], DistinguishIXfromCNOT, 1); + } + +} diff --git a/DistinguishUnitaries/Workbook_DistinguishUnitaries.ipynb b/DistinguishUnitaries/Workbook_DistinguishUnitaries.ipynb new file mode 100644 index 00000000..03cb912f --- /dev/null +++ b/DistinguishUnitaries/Workbook_DistinguishUnitaries.ipynb @@ -0,0 +1,422 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Distinguish Unitaries Kata Workbook\n", + "\n", + "**What is this workbook?**\n", + "A workbook is a collection of problems, accompanied by solutions to them. \n", + "The explanations focus on the logical steps required to solve a problem; they illustrate the concepts that need to be applied to come up with a solution to the problem, explaining the mathematical steps required. \n", + "\n", + "Note that a workbook should not be the primary source of knowledge on the subject matter; it assumes that you've already read a tutorial or a textbook and that you are now seeking to improve your problem-solving skills. You should attempt solving the tasks of the respective kata first, and turn to the workbook only if stuck. While a textbook emphasizes knowledge acquisition, a workbook emphasizes skill acquisition.\n", + "\n", + "This workbook describes the solutions to the problems offered in the [Distinguish Unitaries kata](./DistinguishUnitaries.ipynb). Since the tasks are offered as programming problems, the explanations also cover some elements of Q# that might be non-obvious for a first-time user.\n", + "\n", + "**What you should know for this workbook**\n", + "\n", + "You should be familiar with the following concepts before tackling the Distinguish Unitaries kata (and this workbook):\n", + "\n", + "1. [Basic linear algebra](./../tutorials/LinearAlgebra/LinearAlgebra.ipynb)).\n", + "2. [The concept of qubit](./../tutorials/Qubit/Qubit.ipynb) and [multi-qubit systems](./../tutorials/MultiQubitSystems/MultiQubitSystems.ipynb).\n", + "3. [Single-qubit](./../tutorials/SingleQubitGates/SingleQubitGates.ipynb) and [multi-qubit quantum gates](./../tutorials/MultiQubitGates/MultiQubitGates.ipynb) and using them to manipulate the state of the system.\n", + "4. Measurements and using them to distinguish quantum states." + ] + }, + { + "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 test name\" error):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%package Microsoft.Quantum.Katas::0.11.2006.403" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%workspace reload" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part I. Single-Qubit Gates" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.1. Identity or Pauli X?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the identity (**I** gate)\n", + "or the Pauli X gate (**X** gate). \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **I** gate, 1 if the given operation is the **X** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Solution\n", + "\n", + "The only way to extract information out of a quantum system is measurement. \n", + "Measurements give us information about the states of a system, so to get information about the gate, we need to find a way to convert it into information about a state.\n", + "If we want to distinguish two gates, we need to figure out to prepare a state and perform a measurement on it that will give us a result that we can interpret.\n", + "To do this, we’ll need to find a qubit state that, by applying to it I gate or X gate, will be transformed into states that can be distinguished using measurement, i.e., orthogonal states. \n", + "Let's find such state.\n", + "\n", + "> As a reminder, here are the matrices that correspond to the given gates:\n", + "> $$I = \\begin{bmatrix} 1 & 0 \\\\ 0 & 1 \\end{bmatrix}, X = \\begin{bmatrix} 0 & 1 \\\\ 1 & 0 \\end{bmatrix}$$\n", + "\n", + "Consider the effects of these gates on the basis states: \n", + "\n", + "$$I|0\\rangle = |0\\rangle, I|1\\rangle = |1\\rangle$$\n", + "$$X|0\\rangle = |1\\rangle, X|1\\rangle = |0\\rangle$$\n", + "\n", + "We see that the **I** gate leaves the $|0\\rangle$ state unchanged, and the **X** gate transforms it into the $|1\\rangle$ state. \n", + "So the easiest thing to do is to prepare a $|0\\rangle$ state, apply the given unitary to it and measure the resulting state in the computational basis:\n", + "* If the measurement result is `Zero`, the measured state was $|0\\rangle$, and we know the unitary applied to it was the **I** gate.\n", + "* If the measurement result is `One`, the measured state was $|1\\rangle$, and we know the unitary applied to it was the **X** gate.\n", + "\n", + "> In Q#, the freshly allocated qubits start in the $|0\\rangle$ state, so you don't need to do anything to prepare the necessary state before applying the unitary to it.\n", + "> You also have to return the qubits you allocated to the $|0\\rangle$ state before releasing them. \n", + "> You can do that by measuring the qubit using the `M` operation and applying the **X** gate if it was measured in the $|1\\rangle$ state, or you can use [`MResetZ`](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.measurement.mresetz) operation that wraps this measurement and fixup into one operation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T11_DistinguishIfromX_Test \n", + "\n", + "open Microsoft.Quantum.Measurement;\n", + "\n", + "operation DistinguishIfromX (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " using (q = Qubit()) {\n", + " unitary(q);\n", + " return MResetZ(q) == Zero ? 0 | 1;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Return to task 1.1 of the Distinguish Unitaries kata.](./DistinguishUnitaries.ipynb#Task-1.1.-Identity-or-Pauli-X?)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.2. Identity or Pauli Z?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the identity (**I** gate)\n", + "or the Pauli Z gate (**Z** gate). \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **I** gate, 1 if the given operation is the **Z** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Solution\n", + "\n", + "> As a reminder, $Z = \\begin{bmatrix} 1 & 0 \\\\ 0 & -1 \\end{bmatrix}$\n", + "\n", + "We won't be able to distinguish **I** from **Z** by applying them to the basis states, since they both leave the $|0\\rangle$ state unchanged and add a phase to the $|1\\rangle$ state: \n", + "\n", + "$$I|0\\rangle = |0\\rangle, I|1\\rangle = |1\\rangle$$\n", + "$$Z|0\\rangle = |0\\rangle, Z|1\\rangle = -|1\\rangle$$\n", + "\n", + "However, if we try applying these gates to a superposition of basis states, we'll start seeing a difference between the resulting states:\n", + "\n", + "$$I \\big(\\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle)\\big) = \\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle)$$\n", + "$$Z \\big(\\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle)\\big) = \\frac{1}{\\sqrt2}(|0\\rangle \\color{blue}{-} |1\\rangle)$$\n", + "\n", + "These two states are orthogonal and can be distinguished by measuring them in the $\\{ |+\\rangle, |-\\rangle\\}$ basis using [`MResetX`](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.measurement.mresetx) operation (which is equivalent to applying an **H** gate and measuring in the computational basis).\n", + "\n", + "> The task of distinguishing these two states is covered in more detail in the [Measurements kata](./..//Measurements/Measurements.ipynb#Task-1.3.-$|+\\rangle$-or-$|-\\rangle$?) and the corresponding workbook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T12_DistinguishIfromZ_Test \n", + "\n", + "open Microsoft.Quantum.Measurement;\n", + "\n", + "operation DistinguishIfromZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " using (q = Qubit()) {\n", + " H(q);\n", + " unitary(q);\n", + " return MResetX(q) == Zero ? 0 | 1;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Return to task 1.2 of the Distinguish Unitaries kata.](./DistinguishUnitaries.ipynb#Task-1.2.-Identity-or-Pauli-Z?)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.3. Z or S?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the **Z** gate\n", + "or the **S** gate. \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **Z** gate, 1 if the given operation is the **S** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants at most **twice**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Solution\n", + "\n", + "> As a reminder, $S = \\begin{bmatrix} 1 & 0 \\\\ 0 & i \\end{bmatrix}$\n", + "\n", + "This task differs from the previous two in that it allows you to apply the given unitary **twice**. \n", + "Let's treat this as a hint that it is, and check how the given gates looks when applied twice. \n", + "If you square the corresponding matrices (which is quite simple to do for diagonal matrices), you'll get\n", + "\n", + "$$Z^2 = \\begin{bmatrix} 1 & 0 \\\\ 0 & 1 \\end{bmatrix} = I, S^2 = \\begin{bmatrix} 1 & 0 \\\\ 0 & -1 \\end{bmatrix} = Z$$\n", + "\n", + "This means that the task of identifying the *square* of the given unitary transformation is the same as distinguishing **I** from **Z** gates - and that's exactly the [task 1.2](#Task-1.2.-Identity-or-Pauli-Z?)!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T13_DistinguishZfromS_Test \n", + "\n", + "open Microsoft.Quantum.Measurement;\n", + "\n", + "operation DistinguishZfromS (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " using (q = Qubit()) {\n", + " H(q);\n", + " unitary(q);\n", + " unitary(q);\n", + " return MResetX(q) == Zero ? 0 | 1;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Return to task 1.3 of the Distinguish Unitaries kata.](./DistinguishUnitaries.ipynb#Task-1.3.-Z-or-S?)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 1.4. Z or $-$Z?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the **Z** gate\n", + "or the minus **Z** gate (i.e., the gate $- |0\\rangle\\langle0| + |1\\rangle\\langle1|$). \n", + "The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is the **Z** gate, 1 if the given operation is the $-$**Z** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Solution\n", + "\n", + "This task is more interesting: the given gates differ by a global phase they introduce (i.e., each of them is a multiple of the other), and the results of applying them to any single-qubit state are going to be indistinguishable by any measurement you can devise.\n", + "\n", + "Fortunately, we are given not just the unitary itself, but also its controlled variant, i.e., the gate which applies the given unitary if the control qubit or qubits are in the $|1\\rangle$ state, and does nothing if they are in the $|0\\rangle$ state.\n", + "This allows us to use so called \"phase kickback\" trick, in which applying a controlled version of a gate allows us to observe the phase introduced by this gate on the control qubit. Indeed,\n", + "\n", + "| State | Controlled Z | Controlled $-$Z |\n", + "|-------|---------------|------|\n", + "| $|00\\rangle$ | $|00\\rangle$ | $|00\\rangle$ |\n", + "| $|01\\rangle$ | $|01\\rangle$ | $|01\\rangle$ |\n", + "| $|10\\rangle$ | $\\color{blue}{|10\\rangle}$ | $\\color{blue}{-|10\\rangle}$ |\n", + "| $|11\\rangle$ | $\\color{blue}{-|11\\rangle}$ | $\\color{blue}{|11\\rangle}$ |\n", + "\n", + "We see that both controlled gates don't modify the states with the control qubit in the $|0\\rangle$ state, but if the control qubit is in the $|1\\rangle$ state, they introduce a $-1$ phase to different basis states. \n", + "We can take advantage of this if we apply the controlled gate to a state in which the *control qubit* is in superposition, such as $\\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle) \\otimes |0\\rangle$:\n", + "\n", + "$$\\text{Controlled Z}\\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle) \\otimes |0\\rangle = \\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle) \\otimes |0\\rangle$$\n", + "$$\\text{Controlled }-\\text{Z}\\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle) \\otimes |0\\rangle = \\frac{1}{\\sqrt2}(|0\\rangle - |1\\rangle) \\otimes |0\\rangle$$\n", + "\n", + "After this we can measure the first qubit to distinguish $\\frac{1}{\\sqrt2}(|0\\rangle + |1\\rangle)$ from $\\frac{1}{\\sqrt2}(|0\\rangle - |1\\rangle)$, like we did in [task 1.2](#Task-1.2.-Identity-or-Pauli-Z?).\n", + "\n", + "> In Q# we can express controlled version of a gate using [Controlled functor](https://docs.microsoft.com/en-us/quantum/user-guide/using-qsharp/operations-functions#controlled-functor): the first argument of the resulting gate will be an array of control qubits, and the second one - the arguments of the original gate (in this case just the target qubit)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T14_DistinguishZfromMinusZ_Test \n", + "\n", + "open Microsoft.Quantum.Measurement;\n", + "\n", + "operation DistinguishZfromMinusZ (unitary : (Qubit => Unit is Adj+Ctl)) : Int {\n", + " using (qs = Qubit[2]) {\n", + " // prep (|0⟩ + |1⟩) ⊗ |0⟩\n", + " H(qs[0]);\n", + "\n", + " Controlled unitary(qs[0..0], qs[1]);\n", + "\n", + " return MResetX(qs[0]) == Zero ? 0 | 1;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Return to task 1.4 of the Distinguish Unitaries kata.](./DistinguishUnitaries.ipynb#Task-1.4.-Z-or--Z?)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part II. Multi-Qubit Gates" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Task 2.1. $I \\otimes X$ or CNOT?\n", + "\n", + "**Input:** An operation that implements a single-qubit unitary transformation:\n", + "either the $I \\otimes X$ (the **X** gate applied to the second qubit)\n", + "or the **CNOT** gate with the first qubit as control and the second qubit as target.\n", + "* The operation will accept an array of qubits as input, but it will fail if the array is empty or has one or more than two qubits.\n", + "* The operation will have Adjoint and Controlled variants defined.\n", + "\n", + "**Output:** 0 if the given operation is $I \\otimes X$, 1 if the given operation is the **CNOT** gate.\n", + "\n", + "You are allowed to apply the given operation and its adjoint/controlled variants exactly **once**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Solution\n", + "\n", + "Let's consider the effect of these gates on the basis states:\n", + "\n", + "| State | $I \\otimes X$ | **CNOT** |\n", + "|-------|---------------|------|\n", + "| $|00\\rangle$ | $|01\\rangle$ | $|00\\rangle$ |\n", + "| $|01\\rangle$ | $|00\\rangle$ | $|01\\rangle$ |\n", + "| $|10\\rangle$ | $|11\\rangle$ | $|11\\rangle$ |\n", + "| $|11\\rangle$ | $|10\\rangle$ | $|10\\rangle$ |\n", + "\n", + "We can see that applying these two gates to states with the first qubit in the $|1\\rangle$ state yields identical results, but applying them to states with the first qubit in the $|0\\rangle$ state produces states that differ in the second qubuit.\n", + "This makes sense, since the **CNOT** gate is defined as \"apply **X** gate to the target qubit if the control qubit is in the $|1\\rangle$ state, and do nothing if it is in the $|0\\rangle$ state\".\n", + "\n", + "Thus, the easiest solution is: allocate two qubits in the $|00\\rangle$ state and apply the unitary to them, then measure the second qubit; if it is `One`, the gate is $I \\otimes X$, otherwise it's **CNOT**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%kata T21_DistinguishIXfromCNOT_Test\n", + "\n", + "open Microsoft.Quantum.Measurement;\n", + "\n", + "operation DistinguishIXfromCNOT (unitary : (Qubit[] => Unit is Adj+Ctl)) : Int {\n", + " using (qs = Qubit[2]) {\n", + " unitary(qs);\n", + " return MResetZ(qs[1]) == One ? 0 | 1;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Return to task 2.1 of the Distinguish Unitaries kata.](./DistinguishUnitaries.ipynb#Task-2.1.-$I-\\otimes-X$-or-CNOT?)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*To be continued...*" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Q#", + "language": "qsharp", + "name": "iqsharp" + }, + "language_info": { + "file_extension": ".qs", + "mimetype": "text/x-qsharp", + "name": "qsharp", + "version": "0.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Dockerfile b/Dockerfile index a1d0858a..f8059f2f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,7 @@ RUN cd ${HOME} && \ ./scripts/prebuild-kata.sh BasicGates && \ ./scripts/prebuild-kata.sh CHSHGame && \ ./scripts/prebuild-kata.sh DeutschJozsaAlgorithm && \ + ./scripts/prebuild-kata.sh DistinguishUnitaries && \ ./scripts/prebuild-kata.sh GHZGame && \ ./scripts/prebuild-kata.sh GraphColoring && \ ./scripts/prebuild-kata.sh GroversAlgorithm && \ diff --git a/README.md b/README.md index 6d5430aa..56d80f9e 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,9 @@ Each kata is a separate project that includes: Implement the BB84 key distribution algorithm. * **[Bit-flip error correcting code](./QEC_BitFlipCode/)**. Learn about a 3-qubit error correcting code for protecting against bit-flip errors. -* **[Unitary Patterns*](./UnitaryPatterns/)**. +* **[Distinguish Unitaries](./DistinguishUnitaries/)**. + Learn to distinguish unitaries by designing and performing experiments with them. +* **[Unitary Patterns](./UnitaryPatterns/)**. Learn to implement unitaries with matrices that follow certain patterns of zero and non-zero elements. > For a Q# programming language quick reference sheet, see [Q# Language Quick Reference](./quickref/qsharp-quick-reference.pdf). diff --git a/index.ipynb b/index.ipynb index 15fa7735..e9ac3e2d 100644 --- a/index.ipynb +++ b/index.ipynb @@ -96,6 +96,8 @@ " Implement the BB84 key distribution algorithm.\n", "* **[Bit-flip error correcting code](./QEC_BitFlipCode/QEC_BitFlipCode.ipynb)**.\n", " Learn about a 3-qubit error correcting code for protecting against bit-flip errors.\n", + "* **[Distinguish Unitaries](./DistinguishUnitaries/DistinguishUnitaries.ipynb)**.\n", + " Learn to distinguish unitaries by designing and performing experiments with them.\n", "* **[Unitary Patterns](./UnitaryPatterns/UnitaryPatterns.ipynb)**.\n", " Learn to implement unitaries with matrices that follow certain patterns of zero and non-zero elements.\n", "\n",