This commit is contained in:
Mariia Mykhailova 2020-06-15 13:05:36 -07:00 коммит произвёл GitHub
Родитель 2cac1c9c85
Коммит 9ce81ce60e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 1161 добавлений и 1 удалений

Просмотреть файл

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<IsPackable>false</IsPackable>
<RootNamespace>Quantum.Kata.DistinguishUnitaries</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Quantum.Standard" Version="0.11.2006.403" />
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.11.2006.403" />
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.11.2006.403" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\utilities\Common\Common.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -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",
"> <details>\n",
"> <summary><u>How to install the right IQ# version</u></summary>\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",
"> </details>\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
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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)!

Просмотреть файл

@ -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;
}
}
}

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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;
}
/// <summary>
/// This driver will run all Q# tests (operations named "...Test")
/// that belong to namespace Quantum.Kata.DistinguishUnitaries.
/// </summary>
[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);
}
}
}
}

Просмотреть файл

@ -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);
}
}

Просмотреть файл

@ -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, well 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
}

Просмотреть файл

@ -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 && \

Просмотреть файл

@ -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).

Просмотреть файл

@ -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",