diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..766b88338de --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto +*.sh text eol=lf + diff --git a/.gitignore b/.gitignore index 3e759b75bf4..bce367f11c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,330 +1,281 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ -**/Properties/launchSettings.json - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_i.h -*.ilk -*.meta +# Git ignore file for the Solid project + +# Build artifacts +bin/ +obj/ +Documentation/Help/ +packages/ +src/simulation/Runtime/x64/ *.obj -*.iobj -*.pch +*.dll *.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc +*.exe +*.chm +*.html +Tests/build + +# test outputs +TestResults/ +*.qxe + +# Random VS files +*.suo *.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages +*.vspscc +UpgradeLog.htm +.vs +*.user + +# Random non-solution files +*.user *.nupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ +StyleCop.Cache +*.sublime-workspace +build_log.txt +.DS_store + +# Q# code-behind files: +*.g.cs + +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# makeidx +*.idx +*.ilg +*.ind +*.ist + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# easy-todo +*.lod + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# Kile +*.backup + +# KBibTeX +*~[0-9]* -# MSBuild Binary and Structured Log -*.binlog +# auto folder when using emacs and auctex +./auto/* +*.el -# NVidia Nsight GPU debugger configuration file -*.nvuser +# expex forward references with \gathertags +*-tags.tex -# MFractors (Xamarin productivity tool) working folder -.mfractor/ +# standalone packages +*.sta diff --git a/BasicGates/.vscode/extensions.json b/BasicGates/.vscode/extensions.json new file mode 100644 index 00000000000..14d152e069a --- /dev/null +++ b/BasicGates/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "quantum.quantum-devkit-vscode" + ] +} \ No newline at end of file diff --git a/BasicGates/.vscode/tasks.json b/BasicGates/.vscode/tasks.json new file mode 100644 index 00000000000..bae788317e9 --- /dev/null +++ b/BasicGates/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "args": [ + "build" + ], + "type": "process", + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "test", + "command": "dotnet", + "args": [ + "test" + ], + "type": "process", + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/BasicGates/BasicGates.csproj b/BasicGates/BasicGates.csproj new file mode 100644 index 00000000000..9035bc552c2 --- /dev/null +++ b/BasicGates/BasicGates.csproj @@ -0,0 +1,22 @@ + + + netcoreapp2.0 + x64 + false + Quantum.Kata.BasicGates + + + + + + + + + + + + + + + + diff --git a/BasicGates/BasicGates.sln b/BasicGates/BasicGates.sln new file mode 100644 index 00000000000..af20280bffa --- /dev/null +++ b/BasicGates/BasicGates.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicGates", "BasicGates.csproj", "{4BAAB76A-ABFA-4F5C-9E5B-788BB3E8647E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4BAAB76A-ABFA-4F5C-9E5B-788BB3E8647E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BAAB76A-ABFA-4F5C-9E5B-788BB3E8647E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BAAB76A-ABFA-4F5C-9E5B-788BB3E8647E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BAAB76A-ABFA-4F5C-9E5B-788BB3E8647E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7CB0806C-1BBF-4E0C-BDFC-04A9A068F41C} + EndGlobalSection +EndGlobal diff --git a/BasicGates/README.md b/BasicGates/README.md new file mode 100644 index 00000000000..9994a868864 --- /dev/null +++ b/BasicGates/README.md @@ -0,0 +1,14 @@ +# Welcome! + +The basic gates kata covers the basic operations (a.k.a. "gates") used in quantum computing, as well as the concept of controlled and adjoint versions of gates. + +#### Theory + +* A list of most common gates can be found in [this Wikipedia article](https://en.wikipedia.org/wiki/Quantum_logic_gate). +* [Quirk](http://algassert.com/quirk) is a convenient tool for visualizing the effect of gates on qubit states. + +#### Q# materials + +* Basic gates provided in Q# belong to the `Microsoft.Quantum.Primitive` namespace and are listed [here](https://docs.microsoft.com/qsharp/api/prelude/microsoft.quantum.primitive). +* Using controlled and adjoint versions of gates is covered in the Q# documentation on [operation types](https://docs.microsoft.com/en-us/quantum/quantum-qr-typemodel#operation-and-function-types). +* Defining controlled and adjoint versions of gates is covered in the Q# documentation on [operation definitions](https://docs.microsoft.com/en-us/quantum/quantum-qr-filestructure#operation-definitions). \ No newline at end of file diff --git a/BasicGates/ReferenceImplementation.qs b/BasicGates/ReferenceImplementation.qs new file mode 100644 index 00000000000..de87de43cdb --- /dev/null +++ b/BasicGates/ReferenceImplementation.qs @@ -0,0 +1,233 @@ +// 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.BasicGates +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + + ////////////////////////////////////////////////////////////////// + // Part I. Single-Qubit Gates + ////////////////////////////////////////////////////////////////// + + // Task 1.1. State flip + // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the state of the qubit to α |1⟩ + β |0⟩. + // Example: + // If the qubit is in state |0〉, change its state to |1〉. + // If the qubit is in state |1〉, change its state to |0〉. + operation StateFlip_Reference (q : Qubit) : () + { + body + { + X(q); + } + adjoint self; + } + + // Task 1.2. Basis change: |0⟩ to |+⟩ and |1⟩ to |-⟩ (and vice versa) + // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the state of the qubit as follows: + // If the qubit is in state |0〉, change its state to |+〉 = (|0⟩ + |1⟩) / sqrt(2). + // If the qubit is in state |1〉, change its state to |-〉 = (|0⟩ - |1⟩) / sqrt(2). + // If the qubit is in superposition, change its state according to the effect on basis vectors. + // Note: |+〉 and |-〉 form a different basis for single-qubit states, called X basis. + operation BasisChange_Reference (q : Qubit) : () + { + body + { + H(q); + } + adjoint self; + } + + // Task 1.3. Sign flip: |+〉 to |-〉 and vice versa. + // Inputs: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the qubit state to α |0⟩ - β |1⟩ (flip the sign of |1⟩ component of the superposition). + operation SignFlip_Reference (q : Qubit) : () + { + body + { + Z(q); + } + adjoint self; + } + + // Task 1.4*. Amplitude change (|0⟩ to cos(alpha)*|0〉 + sin(alpha)*|1〉). + // Inputs: + // 1) A qubit in state β|0⟩ + γ|1⟩. + // 2) Angle alpha, in radians, represented as Double + // Goal: Change the state of the qubit as follows: + // If the qubit is in state |0〉, change its state to cos(alpha)*|0〉 + sin(alpha)*|1〉. + // If the qubit is in state |1〉, change its state to -sin(alpha)*|0〉 + cos(alpha)*|1〉. + // If the qubit is in superposition, change its state according to the effect on basis vectors. + operation AmplitudeChange_Reference (q : Qubit, alpha : Double) : () + { + body + { + Ry(2.0 * alpha, q); + } + adjoint auto; + } + + // Task 1.5. Phase flip + // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the qubit state to α |0⟩ + iβ |1⟩ (flip the phase of |1⟩ component of the superposition). + operation PhaseFlip_Reference (q : Qubit) : () + { + body + { + S(q); + // alternatively Rz(0.5 * PI(), q); + } + adjoint auto; + } + + // Task 1.6*. Phase change + // Inputs: + // 1) A qubit in state β|0⟩ + γ|1⟩. + // 2) Angle alpha, in radians, represented as Double + // Goal: Change the state of the qubit as follows: + // If the qubit is in state |0〉, don't change its state. + // If the qubit is in state |1〉, change its state to exp(i*alpha)|1〉. + // If the qubit is in superposition, change its state according to the effect on basis vectors. + operation PhaseChange_Reference (q : Qubit, alpha : Double) : () + { + body + { + Rz(alpha, q); + } + adjoint auto; + } + + // Task 1.7. Bell state change - 1 + // Input: Two entangled qubits in Bell state |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2). + // Goal: Change the two-qubit state to |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2). + operation BellStateChange1_Reference (qs : Qubit[]) : () + { + body + { + Z(qs[0]); + // alternatively Z(qs[1]); + } + adjoint auto; + } + + // Task 1.8. Bell state change - 2 + // Input: Two entangled qubits in Bell state |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2). + // Goal: Change the two-qubit state to |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2). + operation BellStateChange2_Reference (qs : Qubit[]) : () + { + body + { + X(qs[0]); + // alternatively X(qs[1]); + } + adjoint auto; + } + + // Task 1.9. Bell state change - 3 + // Input: Two entangled qubits in Bell state |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2). + // Goal: Change the two-qubit state to |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2). + operation BellStateChange3_Reference (qs : Qubit[]) : () + { + body + { + Y(qs[0]); + } + adjoint auto; + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Multi-Qubit Gates + ////////////////////////////////////////////////////////////////// + + // Task 2.1. Two-qubit gate - 1 + // Input: Two unentangled qubits (stored in an array of length 2). + // The first qubit will be in state |ψ⟩ = α |0⟩ + β |1⟩, the second - in state |0〉 + // (this can be written as two-qubit state (α|0⟩ + β|1⟩) ⊕ |0〉). + // Goal: Change the two-qubit state to α |00⟩ + β |11⟩. + // Note that unless the starting state of the first qubit was |0〉 or |1〉, + // the resulting two-qubit state can not be represented as a tensor product + // of the states of individual qubits any longer; thus the qubits become entangled. + operation TwoQubitGate1_Reference (qs : Qubit[]) : () + { + body + { + CNOT(qs[0], qs[1]); + } + adjoint self; + } + + // Task 2.2. Two-qubit gate - 2 + // Input: Two qubits (stored in an array of length 2) + // in state |+⟩ ⊕ |+⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. + // Goal: Change the two-qubit state to (|00⟩ + |01⟩ + |10⟩ - |11⟩) / 2. + // Note that while the starting state can be represented as a tensor product of single-qubit states, + // the resulting two-qubit state can not be represented in such a way. + operation TwoQubitGate2_Reference (qs : Qubit[]) : () + { + body + { + (Controlled Z)([qs[0]], qs[1]); + } + adjoint self; + } + + // Task 2.3. Two-qubit gate - 3 + // Input: Two qubits (stored in an array of length 2) in an arbitrary + // two-qubit state α|00⟩ + β|01⟩ + γ|10⟩ + δ|11⟩. + // Goal: Change the two-qubit state to α|00⟩ + γ|01⟩ + β|10⟩ + δ|11⟩. + operation TwoQubitGate3_Reference (qs : Qubit[]) : () + { + body + { + // Hint: this task can be solved using one primitive gate; + // as an exercise, try to express the solution using several controlled Pauli gates. + + CNOT(qs[0], qs[1]); + CNOT(qs[1], qs[0]); + CNOT(qs[0], qs[1]); + } + adjoint self; + } + + // Task 2.4. Toffoli gate + // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. + // Goal: Flip the state of the third qubit if the state of the first two is |11⟩: + // i.e., change the three-qubit state to + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + θ|110⟩ + η|111⟩. + operation ToffoliGate_Reference (qs : Qubit[]) : () + { + body + { + CCNOT(qs[0], qs[1], qs[2]); + // alternatively (Controlled X)(qs[0..1], qs[2]); + } + adjoint self; + } + + // Task 2.5. Fredkin gate + // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. + // Goal: Swap the states of second and third qubit if and only if the state of the first qubit is |1⟩: + // i.e., change the three-qubit state to + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + η|101⟩ + ζ|110⟩ + θ|111⟩. + operation FredkinGate_Reference (qs : Qubit[]) : () + { + body + { + (Controlled SWAP)([qs[0]], (qs[1], qs[2])); + } + adjoint self; + } +} diff --git a/BasicGates/Tasks.qs b/BasicGates/Tasks.qs new file mode 100644 index 00000000000..1f02dc7e483 --- /dev/null +++ b/BasicGates/Tasks.qs @@ -0,0 +1,249 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Quantum.Kata.BasicGates +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + + ////////////////////////////////////////////////////////////////// + // Welcome! + ////////////////////////////////////////////////////////////////// + + // "Basic Gates" quantum kata is a series of exercises designed + // to get you familiar with the basic quantum gates in Q#. + // It covers the following topics: + // - basic single-qubit and multi-qubit gates, + // - adjoint and controlled gates, + // - using gates to modify the state of a qubit. + // + // 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. + // + // Most tasks can be done using exactly one gate. + // None of the tasks require measurement, and the tests are written so as to fail if qubit state is measured. + // + // The tasks are given in approximate order of increasing difficulty; harder ones are marked with asterisks. + + + ////////////////////////////////////////////////////////////////// + // Part I. Single-Qubit Gates + ////////////////////////////////////////////////////////////////// + + // Task 1.1. State flip: |0⟩ to |1⟩ and vice versa + // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the state of the qubit to α |1⟩ + β |0⟩. + // Example: + // If the qubit is in state |0〉, change its state to |1〉. + // If the qubit is in state |1〉, change its state to |0〉. + // Note that this operation is self-adjoint: applying it for a second time + // returns the qubit to the original state. + operation StateFlip (q : Qubit) : () + { + body + { + // The Pauli X gate will change the |0〉 state to the |1〉 state and vice versa. + // Type X(q); + // Then rebuild the project and rerun the tests - T11_StateFlip_Test should now pass! + + // ... + } + adjoint self; + } + + // Task 1.2. Basis change: |0⟩ to |+⟩ and |1⟩ to |-⟩ (and vice versa) + // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the state of the qubit as follows: + // If the qubit is in state |0〉, change its state to |+〉 = (|0⟩ + |1⟩) / sqrt(2). + // If the qubit is in state |1〉, change its state to |-〉 = (|0⟩ - |1⟩) / sqrt(2). + // If the qubit is in superposition, change its state according to the effect on basis vectors. + // Note: |+〉 and |-〉 form a different basis for single-qubit states, called X basis. + // |0〉 and |1〉 are called Z basis. + operation BasisChange (q : Qubit) : () + { + body + { + // ... + } + adjoint self; + } + + // Task 1.3. Sign flip: |+〉 to |-〉 and vice versa. + // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the qubit state to α |0⟩ - β |1⟩ (flip the sign of |1⟩ component of the superposition). + operation SignFlip (q : Qubit) : () + { + body + { + // ... + } + adjoint self; + } + + // Task 1.4*. Amplitude change: |0⟩ to cos(alpha)*|0〉 + sin(alpha)*|1〉. + // Inputs: + // 1) A qubit in state β|0⟩ + γ|1⟩. + // 2) Angle alpha, in radians, represented as Double + // Goal: Change the state of the qubit as follows: + // If the qubit is in state |0〉, change its state to cos(alpha)*|0〉 + sin(alpha)*|1〉. + // If the qubit is in state |1〉, change its state to -sin(alpha)*|0〉 + cos(alpha)*|1〉. + // If the qubit is in superposition, change its state according to the effect on basis vectors. + operation AmplitudeChange (q : Qubit, alpha : Double) : () + { + body + { + // ... + } + adjoint auto; + } + + // Task 1.5. Phase flip + // Input: A qubit in state |ψ⟩ = α |0⟩ + β |1⟩. + // Goal: Change the qubit state to α |0⟩ + iβ |1⟩ (flip the phase of |1⟩ component of the superposition). + operation PhaseFlip (q : Qubit) : () + { + body + { + // ... + } + adjoint auto; + } + + // Task 1.6*. Phase change + // Inputs: + // 1) A qubit in state β|0⟩ + γ|1⟩. + // 2) Angle alpha, in radians, represented as Double + // Goal: Change the state of the qubit as follows: + // If the qubit is in state |0〉, don't change its state. + // If the qubit is in state |1〉, change its state to exp(i*alpha)|1〉. + // If the qubit is in superposition, change its state according to the effect on basis vectors. + operation PhaseChange (q : Qubit, alpha : Double) : () + { + body + { + // ... + } + adjoint auto; + } + + // Task 1.7. Bell state change - 1 + // Input: Two entangled qubits in Bell state |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2). + // Goal: Change the two-qubit state to |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2). + operation BellStateChange1 (qs : Qubit[]) : () + { + body + { + // ... + } + adjoint auto; + } + + // Task 1.8. Bell state change - 2 + // Input: Two entangled qubits in Bell state |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2). + // Goal: Change the two-qubit state to |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2). + operation BellStateChange2 (qs : Qubit[]) : () + { + body + { + // ... + } + adjoint auto; + } + + // Task 1.9. Bell state change - 3 + // Input: Two entangled qubits in Bell state |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2). + // Goal: Change the two-qubit state to |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2). + operation BellStateChange3 (qs : Qubit[]) : () + { + body + { + // ... + } + adjoint auto; + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Multi-Qubit Gates + ////////////////////////////////////////////////////////////////// + + // Task 2.1. Two-qubit gate - 1 + // Input: Two unentangled qubits (stored in an array of length 2). + // The first qubit will be in state |ψ⟩ = α |0⟩ + β |1⟩, the second - in state |0〉 + // (this can be written as two-qubit state (α|0⟩ + β|1⟩) ⊕ |0〉). + // Goal: Change the two-qubit state to α |00⟩ + β |11⟩. + // Note that unless the starting state of the first qubit was |0〉 or |1〉, + // the resulting two-qubit state can not be represented as a tensor product + // of the states of individual qubits any longer; thus the qubits become entangled. + operation TwoQubitGate1 (qs : Qubit[]) : () + { + body + { + // ... + } + adjoint self; + } + + // Task 2.2. Two-qubit gate - 2 + // Input: Two qubits (stored in an array of length 2) + // in state |+⟩ ⊕ |+⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. + // Goal: Change the two-qubit state to (|00⟩ + |01⟩ + |10⟩ - |11⟩) / 2. + // Note that while the starting state can be represented as a tensor product of single-qubit states, + // the resulting two-qubit state can not be represented in such a way. + operation TwoQubitGate2 (qs : Qubit[]) : () + { + body + { + // ... + } + adjoint self; + } + + // Task 2.3. Two-qubit gate - 3 + // Input: Two qubits (stored in an array of length 2) in an arbitrary + // two-qubit state α|00⟩ + β|01⟩ + γ|10⟩ + δ|11⟩. + // Goal: Change the two-qubit state to α|00⟩ + γ|01⟩ + β|10⟩ + δ|11⟩. + operation TwoQubitGate3 (qs : Qubit[]) : () + { + body + { + // Hint: this task can be solved using one primitive gate; + // as an exercise, try to express the solution using several + // (possibly controlled) Pauli gates. + + // ... + } + adjoint self; + } + + // Task 2.4. Toffoli gate + // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. + // Goal: Flip the state of the third qubit if the state of the first two is |11⟩: + // i.e., change the three-qubit state to + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + θ|110⟩ + η|111⟩. + operation ToffoliGate (qs : Qubit[]) : () + { + body + { + // ... + } + adjoint self; + } + + // Task 2.5. Fredkin gate + // Input: Three qubits (stored in an array of length 3) in an arbitrary three-qubit state + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + ζ|101⟩ + η|110⟩ + θ|111⟩. + // Goal: Swap the states of second and third qubit if and only if the state of the first qubit is |1⟩: + // α|000⟩ + β|001⟩ + γ|010⟩ + δ|011⟩ + ε|100⟩ + η|101⟩ + ζ|110⟩ + θ|111⟩. + operation FredkinGate (qs : Qubit[]) : () + { + body + { + // ... + } + adjoint self; + } +} diff --git a/BasicGates/TestSuiteRunner.cs b/BasicGates/TestSuiteRunner.cs new file mode 100644 index 00000000000..225b4222376 --- /dev/null +++ b/BasicGates/TestSuiteRunner.cs @@ -0,0 +1,42 @@ +// 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 Microsoft.Quantum.Simulation.Simulators; +using Xunit.Abstractions; +using System.Diagnostics; + +namespace Quantum.Kata.BasicGates +{ + 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.BasicGates. + /// + [OperationDriver(TestNamespace = "Quantum.Kata.BasicGates")] + public void TestTarget(TestOperation op) + { + using (var sim = new QuantumSimulator()) + { + // OnLog defines action(s) performed when Q# test calls function Message + sim.OnLog += (msg) => { output.WriteLine(msg); }; + sim.OnLog += (msg) => { Debug.WriteLine(msg); }; + op.TestOperationRunner(sim); + } + } + } +} diff --git a/BasicGates/Tests.qs b/BasicGates/Tests.qs new file mode 100644 index 00000000000..8228b443ed7 --- /dev/null +++ b/BasicGates/Tests.qs @@ -0,0 +1,268 @@ +// 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.BasicGates +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Convert; + open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Extensions.Testing; + + // ------------------------------------------------------ + // helper wrapper to represent operation on one qubit as an operation on an array of qubits + operation ArrayWrapperOperation (op : ((Qubit) => () : Adjoint), qs : Qubit[]) : () + { + body + { + op(qs[0]); + } + adjoint + { + (Adjoint op)(qs[0]); + } + } + + // ------------------------------------------------------ + operation T11_StateFlip_Test () : () + { + body + { + AssertOperationsEqualReferenced(ArrayWrapperOperation(StateFlip, _), ArrayWrapperOperation(StateFlip_Reference, _), 1); + } + } + + // ------------------------------------------------------ + operation T12_BasisChange_Test () : () + { + body + { + AssertOperationsEqualReferenced(ArrayWrapperOperation(BasisChange, _), ArrayWrapperOperation(BasisChange_Reference, _), 1); + } + } + + // ------------------------------------------------------ + operation T13_SignFlip_Test () : () + { + body + { + AssertOperationsEqualReferenced(ArrayWrapperOperation(SignFlip, _), ArrayWrapperOperation(SignFlip_Reference, _), 1); + } + } + + // ------------------------------------------------------ + operation T14_AmplitudeChange_Test () : () + { + body + { + for (i in 0..36) { + let alpha = 2.0 * PI() * ToDouble(i) / 36.0; + AssertOperationsEqualReferenced(ArrayWrapperOperation(AmplitudeChange(_, alpha), _), ArrayWrapperOperation(AmplitudeChange_Reference(_, alpha), _), 1); + } + } + } + + // ------------------------------------------------------ + operation T15_PhaseFlip_Test () : () + { + body + { + AssertOperationsEqualReferenced(ArrayWrapperOperation(PhaseFlip, _), ArrayWrapperOperation(PhaseFlip_Reference, _), 1); + } + } + + // ------------------------------------------------------ + operation T16_PhaseChange_Test () : () + { + body + { + for (i in 0..36) { + let alpha = 2.0 * PI() * ToDouble(i) / 36.0; + AssertOperationsEqualReferenced(ArrayWrapperOperation(PhaseChange(_, alpha), _), ArrayWrapperOperation(PhaseChange_Reference(_, alpha), _), 1); + } + } + } + + // ------------------------------------------------------ + // 0 - |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2) + // 1 - |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2) + // 2 - |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2) + // 3 - |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2) + operation StatePrep_BellState (qs : Qubit[], state : Int) : () { + body { + H(qs[0]); + CNOT(qs[0], qs[1]); + // now we have |00〉 + |11〉 - modify it based on state arg + if (state % 2 == 1) { + // negative phase + Z(qs[1]); + } + if (state / 2 == 1) { + X(qs[1]); + } + } + adjoint auto; + } + + // ------------------------------------------------------ + operation VerifyBellStateConversion( + testOp : ((Qubit[]) => ()), + startState : Int, + targetState : Int) : () { + body { + using (qs = Qubit[2]) + { + // prepare Bell state startState + StatePrep_BellState(qs, startState); + + // apply operation that needs to be tested + testOp(qs); + + // verify the result by applying adjoint of state prep for target state + (Adjoint StatePrep_BellState)(qs, targetState); + + // assert that all qubits end up in |0〉 state + AssertAllZero(qs); + } + } + } + // ------------------------------------------------------ + operation T17_BellStateChange1_Test () : () + { + body + { + VerifyBellStateConversion(BellStateChange1, 0, 1); + } + } + + // ------------------------------------------------------ + operation T18_BellStateChange2_Test () : () + { + body + { + VerifyBellStateConversion(BellStateChange2, 0, 2); + } + } + + // ------------------------------------------------------ + operation T19_BellStateChange3_Test () : () + { + body + { + VerifyBellStateConversion(BellStateChange3, 0, 3); + } + } + + // ------------------------------------------------------ + // prepare state |A〉 = cos(α) * |0〉 + sin(α) * |1〉 + operation StatePrep_A (alpha : Double, q : Qubit) : () { + body { + Ry(2.0 * alpha, q); + } + adjoint auto; + } + + // ------------------------------------------------------ + operation T21_TwoQubitGate1_Test () : () + { + body + { + // Note that the way the problem is formulated, we can't just compare two unitaries, + // we need to create an input state |A〉 and check that the output state is correct + using (qs = Qubit[2]) + { + for (i in 0..36) { + let alpha = 2.0 * PI() * ToDouble(i) / 36.0; + + // prepare A state + StatePrep_A(alpha, qs[0]); + + // apply operation that needs to be tested + TwoQubitGate1(qs); + + // apply adjoint reference operation and adjoint of state prep + (Adjoint TwoQubitGate1_Reference)(qs); + (Adjoint StatePrep_A)(alpha, qs[0]); + + // assert that all qubits end up in |0〉 state + AssertAllZero(qs); + } + } + } + } + + // ------------------------------------------------------ + // prepare state |+⟩ ⊕ |+⟩ = (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2. + operation StatePrep_PlusPlus (qs : Qubit[]) : () { + body { + ApplyToEachA(H, qs); + } + adjoint auto; + } + + // ------------------------------------------------------ + operation T22_TwoQubitGate2_Test () : () + { + body + { + using (qs = Qubit[2]) + { + // prepare |+⟩ ⊕ |+⟩ state + StatePrep_PlusPlus(qs); + + // apply operation that needs to be tested + TwoQubitGate2(qs); + + // apply adjoint reference operation and adjoint of state prep + (Adjoint TwoQubitGate2_Reference)(qs); + (Adjoint StatePrep_PlusPlus)(qs); + + // assert that all qubits end up in |0〉 state + AssertAllZero(qs); + } + } + } + + // ------------------------------------------------------ + operation SwapWrapper (qs : Qubit[]) : () + { + body + { + SWAP(qs[0], qs[1]); + } + adjoint self; + } + + operation T23_TwoQubitGate3_Test () : () + { + body + { + AssertOperationsEqualReferenced(SwapWrapper, TwoQubitGate3_Reference, 2); + AssertOperationsEqualReferenced(TwoQubitGate3, TwoQubitGate3_Reference, 2); + } + } + + // ------------------------------------------------------ + operation T24_ToffoliGate_Test () : () + { + body + { + AssertOperationsEqualReferenced(ToffoliGate, ToffoliGate_Reference, 3); + } + } + + // ------------------------------------------------------ + operation T25_FredkinGate_Test () : () + { + body + { + AssertOperationsEqualReferenced(FredkinGate, FredkinGate_Reference, 3); + } + } +} \ No newline at end of file diff --git a/DeutschJozsaAlgorithm/.vscode/extensions.json b/DeutschJozsaAlgorithm/.vscode/extensions.json new file mode 100644 index 00000000000..14d152e069a --- /dev/null +++ b/DeutschJozsaAlgorithm/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "quantum.quantum-devkit-vscode" + ] +} \ No newline at end of file diff --git a/DeutschJozsaAlgorithm/.vscode/tasks.json b/DeutschJozsaAlgorithm/.vscode/tasks.json new file mode 100644 index 00000000000..bae788317e9 --- /dev/null +++ b/DeutschJozsaAlgorithm/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "args": [ + "build" + ], + "type": "process", + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "test", + "command": "dotnet", + "args": [ + "test" + ], + "type": "process", + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj new file mode 100644 index 00000000000..7c3e1ac9ed5 --- /dev/null +++ b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.csproj @@ -0,0 +1,22 @@ + + + netcoreapp2.0 + x64 + false + Quantum.Kata.DeutschJozsaAlgorithm + + + + + + + + + + + + + + + + diff --git a/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.sln b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.sln new file mode 100644 index 00000000000..cbb31e50ad5 --- /dev/null +++ b/DeutschJozsaAlgorithm/DeutschJozsaAlgorithm.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeutschJozsaAlgorithm", "DeutschJozsaAlgorithm.csproj", "{B4477C91-FBD6-4DCE-8B4C-1A5E48669790}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B4477C91-FBD6-4DCE-8B4C-1A5E48669790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4477C91-FBD6-4DCE-8B4C-1A5E48669790}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4477C91-FBD6-4DCE-8B4C-1A5E48669790}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4477C91-FBD6-4DCE-8B4C-1A5E48669790}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F23513ED-2103-49D7-9F64-7BA3199FC689} + EndGlobalSection +EndGlobal diff --git a/DeutschJozsaAlgorithm/OracleCounterSimulator.cs b/DeutschJozsaAlgorithm/OracleCounterSimulator.cs new file mode 100644 index 00000000000..02c2b5a7d5d --- /dev/null +++ b/DeutschJozsaAlgorithm/OracleCounterSimulator.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +////////////////////////////////////////////////////////////////////// +// This file contains parts of the testing harness. +// You should not modify anything in this file. +// The tasks themselves can be found in Tasks.qs file. +////////////////////////////////////////////////////////////////////// + +using System; +using System.Collections.Generic; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +using Xunit; + +namespace Quantum.Kata.DeutschJozsaAlgorithm +{ + /// + /// This custom quantum simulator keeps track of the number of times + /// each operation is executed, by providing a custom native operation: + /// `AssertOracleCallsCount` which asserts the number of times + /// the given oracle (operation) has been called. + /// + public class OracleCounterSimulator : QuantumSimulator + { + private Dictionary _operationsCount = new Dictionary(); + + public OracleCounterSimulator( + bool throwOnReleasingQubitsNotInZeroState = true, + uint? randomNumberGeneratorSeed = null, + bool disableBorrowing = false) : + base(throwOnReleasingQubitsNotInZeroState, randomNumberGeneratorSeed, disableBorrowing) + { + this.OnOperationStart += CountOperationCalls; + } + + /// + /// Callback method for the OnOperationStart event. + /// + public void CountOperationCalls(ICallable op, IApplyData data) + { + if (_operationsCount.ContainsKey(op)) + { + _operationsCount[op]++; + } + else + { + _operationsCount[op] = 1; + } + } + + // Custom Native operation to reset the oracle counts back to 0. + public class ResetOracleCallsImpl : ResetOracleCallsCount + { + OracleCounterSimulator _sim; + + public ResetOracleCallsImpl(OracleCounterSimulator m) : base(m) + { + _sim = m; + } + + public override Func Body => (__in) => + { + _sim._operationsCount.Clear(); + return QVoid.Instance; + }; + } + + // Custom Native operation to Assert the number of calls for an Operation. + public class AssertOracleCallsImpl : AssertOracleCallsCount + { + OracleCounterSimulator _sim; + + public AssertOracleCallsImpl(OracleCounterSimulator m) : base(m) + { + _sim = m; + } + + public override Func<(Int64, T), QVoid> Body => (__in) => + { + var (expected, oracle) = __in; + + var op = oracle as ICallable; + Assert.NotNull(op); + + var actual = _sim._operationsCount.ContainsKey(op) ? _sim._operationsCount[op] : 0; + Assert.True(expected >= actual, $"Oracle should be called at most {expected} time(s), your solution called it {actual} time(s)."); + + return QVoid.Instance; + }; + } + } +} diff --git a/DeutschJozsaAlgorithm/README.md b/DeutschJozsaAlgorithm/README.md new file mode 100644 index 00000000000..5523e257c4c --- /dev/null +++ b/DeutschJozsaAlgorithm/README.md @@ -0,0 +1,15 @@ +# Welcome! + +This kata covers several well-studied algorithms and concepts. + +#### Deutsch-Jozsa algorithm +* A good place to start is [Wikipedia](https://en.wikipedia.org/wiki/Deutsch%E2%80%93Jozsa_algorithm). +* Nielsen, M. A. & Chuang, I. L. (2010). Quantum Computation and Quantum Information. pp. 34-36 +* [Lecture 5: A simple searching algorithm; the Deutsch-Jozsa algorithm](https://cs.uwaterloo.ca/~watrous/CPSC519/LectureNotes/05.pdf) + +#### Bernstein-Vazirani algorithm + +* Bernstein, E. & Vazirani, U. (1997). Quantum complexity theory. SIAM J. Comput. 26, 5, pp. 1411-1473. +* ["Quantum Algorithm Implementations for Beginners"](https://arxiv.org/pdf/1804.03719.pdf), section III. +* ["A Generalization of Bernstein-Vazirani Algorithm to Qudit Systems"](https://arxiv.org/pdf/1609.03185.pdf). + \ No newline at end of file diff --git a/DeutschJozsaAlgorithm/ReferenceImplementation.qs b/DeutschJozsaAlgorithm/ReferenceImplementation.qs new file mode 100644 index 00000000000..6e842a07fae --- /dev/null +++ b/DeutschJozsaAlgorithm/ReferenceImplementation.qs @@ -0,0 +1,373 @@ +// 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.DeutschJozsaAlgorithm +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + + ////////////////////////////////////////////////////////////////// + // Part I. Oracles + ////////////////////////////////////////////////////////////////// + + // Task 1.1. f(x) = 0 + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_Zero_Reference (x : Qubit[], y : Qubit) : () + { + body + { + // Since f(x) = 0 for all values of x, |y ⊕ f(x)〉 = |y〉. + // This means that the operation doesn't need to do any transformation to the inputs. + // Build the project and run the tests to see that T01_Oracle_Zero_Test test passes. + } + adjoint auto; + } + + // Task 1.2. f(x) = 1 + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_One_Reference (x : Qubit[], y : Qubit) : () + { + body + { + // Since f(x) = 1 for all values of x, |y ⊕ f(x)〉 = |y ⊕ 1〉 = |NOT y〉. + // This means that the operation needs to flip qubit y (i.e. transform |0〉 to |1〉 and vice versa). + X(y); + } + adjoint auto; + } + + // Task 1.3. f(x) = xₖ (the value of k-th qubit) + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) 0-based index of the qubit from input register (0 <= k < N) + // Goal: transform state |x, y〉 into state |x, y ⊕ xₖ〉 (⊕ is addition modulo 2). + operation Oracle_Kth_Qubit_Reference (x : Qubit[], y : Qubit, k : Int) : () + { + body + { + AssertBoolEqual(0 <= k && k < Length(x), true, "k should be between 0 and N-1, inclusive"); + CNOT(x[k], y); + } + adjoint auto; + } + + // Task 1.4. f(x) = 1 if x has odd number of 1s, and 0 otherwise + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_OddNumberOfOnes_Reference (x : Qubit[], y : Qubit) : () + { + body + { + // Hint: f(x) can be represented as x_0 ⊕ x_1 ⊕ ... ⊕ x_(N-1) + for (i in 0..Length(x)-1) { + CNOT(x[i], y); + } + // alternative solution: ApplyToEachA(CNOT(_, y), x); + } + adjoint auto; + } + + // Task 1.5. f(x) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for a given bit vector r (scalar product function) + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) a bit vector of length N represented as Int[] + // You are guaranteed that the qubit array and the bit vector have the same length. + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + // + // Note: the functions featured in tasks 1.1, 1.3 and 1.4 are special cases of this function. + operation Oracle_ProductFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : () + { + body + { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); + + for (i in 0..Length(x)-1) { + if (r[i] == 1) { + CNOT(x[i], y); + } + } + } + adjoint auto; + } + + // Task 1.6. f(x) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) modulo 2 for a given bit vector r + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) a bit vector of length N represented as Int[] + // You are guaranteed that the qubit array and the bit vector have the same length. + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_ProductWithNegationFunction_Reference (x : Qubit[], y : Qubit, r : Int[]) : () + { + body + { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); + + for (i in 0..Length(x)-1) { + if (r[i] == 1) { + CNOT(x[i], y); + } else { + // do a 0-controlled NOT + X(x[i]); + CNOT(x[i], y); + X(x[i]); + } + } + } + adjoint auto; + } + + // Task 1.7. f(x) = Σᵢ 𝑥ᵢ + (1 if prefix of x is equal to the given bit vector, and 0 otherwise) modulo 2 + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) a bit vector of length P represented as Int[] (1 <= P <= N) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + // + // A prefix of length k of a state |x〉 = |x₁, ..., xₙ〉 is the state of its first k qubits |x₁, ..., xₖ〉. + // For example, a prefix of length 2 of a state |0110〉 is 01. + operation Oracle_HammingWithPrefix_Reference (x : Qubit[], y : Qubit, prefix : Int[]) : () + { + body + { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + let P = Length(prefix); + AssertBoolEqual(1 <= P && P <= Length(x), true, "P should be between 1 and N, inclusive"); + + // Hint: the first part of the function is the same as in task 1.4 + for (i in 0..Length(x)-1) { + CNOT(x[i], y); + } + + // add check for prefix as a multicontrolled NOT + // true bits of r correspond to 1-controls, false bits - to 0-controls + for (i in 0..P-1) { + if (prefix[i] == 0) { + X(x[i]); + } + } + (Controlled X)(x[0..P-1], y); + // uncompute changes done to input register + for (i in 0..P-1) { + if (prefix[i] == 0) { + X(x[i]); + } + } + } + adjoint auto; + } + + // Task 1.8*. f(x) = 1 if x has two or three bits (out of three) set to 1, and 0 otherwise (majority function) + // Inputs: + // 1) 3 qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_MajorityFunction_Reference (x : Qubit[], y : Qubit) : () + { + body + { + // The following line enforces the constraint on the input array. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertBoolEqual(3 == Length(x), true, "x should have exactly 3 qubits"); + + // Hint: represent f(x) in terms of AND and ⊕ operations + CCNOT(x[0], x[1], y); + CCNOT(x[0], x[2], y); + CCNOT(x[1], x[2], y); + } + adjoint auto; + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Bernstein-Vazirani Algorithm + ////////////////////////////////////////////////////////////////// + + // Task 2.1. State preparation for Bernstein-Vazirani algorithm + // Inputs: + // 1) N qubits in |0〉 state (query register) + // 2) a qubit in |0〉 state (answer register) + // Goal: + // 1) create an equal superposition of all basis vectors from |0...0〉 to |1...1〉 on query register + // (i.e. state (|0...0〉 + ... + |1...1〉) / sqrt(2^N) ) + // 2) create |-〉 state (|-〉 = (|0〉 - |1〉) / sqrt(2)) on answer register + operation BV_StatePrep_Reference (query : Qubit[], answer : Qubit) : () + { + body + { + ApplyToEachA(H, query); + X(answer); + H(answer); + } + adjoint auto; + } + + // Task 2.2. Bernstein-Vazirani algorithm implementation + // Inputs: + // 1) the number of qubits in the input register N for the function f + // 2) a quantum operation which implements the oracle |x〉|y〉 -> |x〉|y ⊕ f(x)〉, where + // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function + // You are guaranteed that the function f implemented by the oracle is a scalar product function + // (can be represented as f(𝑥₀, …, 𝑥ₙ₋₁) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for some bit vector r = (𝑟₀, …, 𝑟ₙ₋₁)). + // You have implemented the oracle implementing the scalar product function in task 1.5. + // Output: + // A bit vector r reconstructed from the function + // + // Note: a trivial approach is to call the oracle N times: + // |10...0〉|0〉 = |10...0〉|r₀〉, |010...0〉|0〉 = |010...0〉|r₁〉 and so on. + // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. + operation BV_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] + { + body + { + mutable r = new Int[N]; + + // allocate N+1 qubits + using (qs = Qubit[N+1]) { + // split allocated qubits into input register and answer register + let x = qs[0..N-1]; + let y = qs[N]; + + // prepare qubits in the right state + BV_StatePrep_Reference(x, y); + + // apply oracle + Uf(x, y); + + // apply Hadamard to each qubit of the input register + ApplyToEach(H, x); + + // measure all qubits of the input register; + // the result of each measurement is converted to a Bool + for (i in 0..N-1) { + if (M(x[i]) != Zero) { + set r[i] = 1; + } + } + + // before releasing the qubits make sure they are all in |0〉 state + ResetAll(qs); + } + return r; + } + } + + + ////////////////////////////////////////////////////////////////// + // Part III. Deutsch-Jozsa Algorithm + ////////////////////////////////////////////////////////////////// + + // Task 3.1. Deutsch-Jozsa algorithm implementation + // Inputs: + // 1) the number of qubits in the input register N for the function f + // 2) a quantum operation which implements the oracle |x〉|y〉 -> |x〉|y ⊕ f(x)〉, where + // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function + // You are guaranteed that the function f implemented by the oracle is either + // constant (returns 0 on all inputs or 1 on all inputs) or + // balanced (returns 0 on exactly one half of the input domain and 1 on the other half). + // Output: + // true if the function f is constant + // false if the function f is balanced + // + // Note: a trivial approach is to call the oracle multiple times: + // if the values for more than half of the possible inputs are the same, the function is constant. + // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. + operation DJ_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => ())) : Bool + { + body + { + // Declare variable in which the result will be accumulated; + // this variable has to be mutable to allow updating it. + mutable isConstantFunction = true; + + // Hint: even though Deutsch-Jozsa algorithm operates on a wider class of functions + // than Bernstein-Vazirani (i.e. functions which can not be represented as a scalar product, such as f(x) = 1), + // it can be expressed as running Bernstein-Vazirani algorithm + // and then post-processing the return value classically: + // the function is constant if and only if all elements of the returned array are false + + let r = BV_Algorithm_Reference(N, Uf); + for (i in 0..N-1) { + set isConstantFunction = isConstantFunction && (r[i] == 0); + } + + return isConstantFunction; + } + } + + + ////////////////////////////////////////////////////////////////// + // Part IV. Come up with your own algorithm! + ////////////////////////////////////////////////////////////////// + + // Task 4.1. Reconstruct the oracle from task 1.6 + // Inputs: + // 1) the number of qubits in the input register N for the function f + // 2) a quantum operation which implements the oracle |x〉|y〉 -> |x〉|y ⊕ f(x)〉, where + // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function + // You are guaranteed that the function f implemented by the oracle can be represented as + // f(𝑥₀, …, 𝑥ₙ₋₁) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) modulo 2 for some bit vector r = (𝑟₀, …, 𝑟ₙ₋₁). + // You have implemented the oracle implementing this function in task 1.6. + // Output: + // A bit vector r which generates the same oracle as the one you are given + operation Noname_Algorithm_Reference (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] + { + body + { + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable r = new Int[N]; + + using (qs = Qubit[N+1]) { + // split allocated qubits into input register and answer register + let x = qs[0..N-1]; + let y = qs[N]; + + // apply oracle to qubits in all 0 state + Uf(x, y); + + // f(x) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) = 2 Σᵢ 𝑟ᵢ 𝑥ᵢ + Σᵢ 𝑟ᵢ + Σᵢ 𝑥ᵢ + N = Σᵢ 𝑟ᵢ + N + // remove the N from the expression + if (N % 2 == 1) { + X(y); + } + + // now y = Σᵢ 𝑟ᵢ + + // measure the output register + let m = M(y); + if (m == One) { + // adjust parity of bit vector r + set r[0] = 1; + } + + // before releasing the qubits make sure they are all in |0〉 state + ResetAll(qs); + } + + return r; + } + } +} diff --git a/DeutschJozsaAlgorithm/Tasks.qs b/DeutschJozsaAlgorithm/Tasks.qs new file mode 100644 index 00000000000..0a374d48ca0 --- /dev/null +++ b/DeutschJozsaAlgorithm/Tasks.qs @@ -0,0 +1,351 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Quantum.Kata.DeutschJozsaAlgorithm +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + + ////////////////////////////////////////////////////////////////// + // Welcome! + ////////////////////////////////////////////////////////////////// + + // "Deutsch-Jozsa algorithm" quantum kata is a series of exercises designed + // to get you familiar with programming in Q#. + // It covers the following topics: + // - writing oracles (quantum operations which implement certain classical functions), + // - Bernstein-Vazirani algorithm for recovering the parameters of a scalar product function, + // - Deutsch-Jozsa algorithm for recognizing a function as constant or balanced, and + // - writing tests in Q#. + // + // 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. + + ////////////////////////////////////////////////////////////////// + // Part I. Oracles + ////////////////////////////////////////////////////////////////// + + // In this section you will implement oracles defined by classical functions using the following rules: + // - a function f(𝑥₀, …, 𝑥ₙ₋₁) with N bits of input x = (𝑥₀, …, 𝑥ₙ₋₁) and 1 bit of output y + // defines an oracle which acts on N input qubits and 1 output qubit. + // - the oracle effect on qubits in computational basis states is defined as follows: + // |x〉 |y〉 -> |x〉 |y ⊕ f(x)〉 (⊕ is addition modulo 2) + // - the oracle effect on qubits in superposition is defined following the linearity of quantum operations. + // - the oracle must act properly on qubits in all possible input states. + + // Task 1.1. f(x) = 0 + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_Zero (x : Qubit[], y : Qubit) : () + { + body + { + // Since f(x) = 0 for all values of x, |y ⊕ f(x)〉 = |y〉. + // This means that the operation doesn't need to do any transformation to the inputs. + // Build the project and run the tests to see that T01_Oracle_Zero_Test test passes. + } + } + + // Task 1.2. f(x) = 1 + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_One (x : Qubit[], y : Qubit) : () + { + body + { + // Since f(x) = 1 for all values of x, |y ⊕ f(x)〉 = |y ⊕ 1〉 = |NOT y〉. + // This means that the operation needs to flip qubit y (i.e. transform |0〉 to |1〉 and vice versa). + + // ... + } + } + + // Task 1.3. f(x) = xₖ (the value of k-th qubit) + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) 0-based index of the qubit from input register (0 <= k < N) + // Goal: transform state |x, y〉 into state |x, y ⊕ xₖ〉 (⊕ is addition modulo 2). + operation Oracle_Kth_Qubit (x : Qubit[], y : Qubit, k : Int) : () + { + body + { + // The following line enforces the constraints on the value of k that you are given. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertBoolEqual(0 <= k && k < Length(x), true, "k should be between 0 and N-1, inclusive"); + + // ... + } + } + + // Task 1.4. f(x) = 1 if x has odd number of 1s, and 0 otherwise + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_OddNumberOfOnes (x : Qubit[], y : Qubit) : () + { + body + { + // Hint: f(x) can be represented as x_0 ⊕ x_1 ⊕ ... ⊕ x_(N-1) + + // ... + } + } + + // Task 1.5. f(x) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for a given bit vector r (scalar product function) + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) a bit vector of length N represented as Int[] + // You are guaranteed that the qubit array and the bit vector have the same length. + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + // + // Note: the functions featured in tasks 1.1, 1.3 and 1.4 are special cases of this function. + operation Oracle_ProductFunction (x : Qubit[], y : Qubit, r : Int[]) : () + { + body + { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); + + // ... + } + } + + // Task 1.6. f(x) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) modulo 2 for a given bit vector r + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) a bit vector of length N represented as Int[] + // You are guaranteed that the qubit array and the bit vector have the same length. + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_ProductWithNegationFunction (x : Qubit[], y : Qubit, r : Int[]) : () + { + body + { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertIntEqual(Length(x), Length(r), "Arrays should have the same length"); + + // ... + } + } + + // Task 1.7. f(x) = Σᵢ 𝑥ᵢ + (1 if prefix of x is equal to the given bit vector, and 0 otherwise) modulo 2 + // Inputs: + // 1) N qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // 3) a bit vector of length P represented as Int[] (1 <= P <= N) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + // + // A prefix of length k of a state |x〉 = |x₁, ..., xₙ〉 is the state of its first k qubits |x₁, ..., xₖ〉. + // For example, a prefix of length 2 of a state |0110〉 is 01. + operation Oracle_HammingWithPrefix (x : Qubit[], y : Qubit, prefix : Int[]) : () + { + body + { + // The following line enforces the constraint on the input arrays. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + let P = Length(prefix); + AssertBoolEqual(1 <= P && P <= Length(x), true, "P should be between 1 and N, inclusive"); + + // Hint: the first part of the function is the same as in task 1.4 + + // ... + + // Hint: you can use Controlled functor to perform multicontrolled gates + // (gates with multiple control qubits). + + // ... + } + } + + // Task 1.8*. f(x) = 1 if x has two or three bits (out of three) set to 1, and 0 otherwise (majority function) + // Inputs: + // 1) 3 qubits in arbitrary state |x〉 (input register) + // 2) a qubit in arbitrary state |y〉 (output qubit) + // Goal: transform state |x, y〉 into state |x, y ⊕ f(x)〉 (⊕ is addition modulo 2). + operation Oracle_MajorityFunction (x : Qubit[], y : Qubit) : () + { + body + { + // The following line enforces the constraint on the input array. + // You don't need to modify it. Feel free to remove it, this won't cause your code to fail. + AssertBoolEqual(3 == Length(x), true, "x should have exactly 3 qubits"); + + // Hint: represent f(x) in terms of AND and ⊕ operations + + // ... + } + } + + + ////////////////////////////////////////////////////////////////// + // Part II. Bernstein-Vazirani Algorithm + ////////////////////////////////////////////////////////////////// + + // Task 2.1. State preparation for Bernstein-Vazirani algorithm + // Inputs: + // 1) N qubits in |0〉 state (query register) + // 2) a qubit in |0〉 state (answer register) + // Goal: + // 1) create an equal superposition of all basis vectors from |0...0〉 to |1...1〉 on query register + // (i.e. state (|0...0〉 + ... + |1...1〉) / sqrt(2^N) ) + // 2) create |-〉 state (|-〉 = (|0〉 - |1〉) / sqrt(2)) on answer register + operation BV_StatePrep (query : Qubit[], answer : Qubit) : () + { + body + { + // ... + } + adjoint auto; + } + + // Task 2.2. Bernstein-Vazirani algorithm implementation + // Inputs: + // 1) the number of qubits in the input register N for the function f + // 2) a quantum operation which implements the oracle |x〉|y〉 -> |x〉|y ⊕ f(x)〉, where + // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function + // You are guaranteed that the function f implemented by the oracle is a scalar product function + // (can be represented as f(𝑥₀, …, 𝑥ₙ₋₁) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for some bit vector r = (𝑟₀, …, 𝑟ₙ₋₁)). + // You have implemented the oracle implementing the scalar product function in task 1.5. + // Output: + // A bit vector r reconstructed from the function + // + // Note: a trivial approach is to call the oracle N times: + // |10...0〉|0〉 = |10...0〉|r₀〉, |010...0〉|0〉 = |010...0〉|r₁〉 and so on. + // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. + operation BV_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] + { + body + { + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable r = new Int[N]; + + // ... + + return r; + } + } + + // Task 2.3. Testing Bernstein-Vazirani algorithm + // Goal: use your implementation of Bernstein-Vazirani algorithm from task 2.2 to figure out + // what bit vector the scalar product function oracle from task 1.5 was using. + // As a reminder, this oracle creates an operation f(x) = Σᵢ 𝑟ᵢ 𝑥ᵢ modulo 2 for a given bit vector r, + // and Bernstein-Vazirani algorithm recovers that bit vector given the operation. + operation BV_Test () : () + { + body + { + // Hint: use Oracle_ProductFunction to implement the scalar product function oracle passed to BV_Algorithm. + // Since Oracle_ProductFunction takes three arguments (Qubit[], Qubit and Int[]), + // and the operation passed to BV_Algorithm must take two arguments (Qubit[] and Qubit), + // you need to use partial application to fix the third argument (a specific value of a bit vector). + // + // You might want to use something like the following: + // let oracle = Oracle_ProductFunction(_, _, [...your bit vector here...]); + + // Hint: use AssertIntArrayEqual function to assert that the return value of BV_Algorithm operation + // matches the expected value (i.e. the bit vector passed to Oracle_ProductFunction). + + // BV_Test appears in the list of unit tests for the solution; run it to verify your code. + + // ... + } + } + + + ////////////////////////////////////////////////////////////////// + // Part III. Deutsch-Jozsa Algorithm + ////////////////////////////////////////////////////////////////// + + // Task 3.1. Deutsch-Jozsa algorithm implementation + // Inputs: + // 1) the number of qubits in the input register N for the function f + // 2) a quantum operation which implements the oracle |x〉|y〉 -> |x〉|y ⊕ f(x)〉, where + // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function + // You are guaranteed that the function f implemented by the oracle is either + // constant (returns 0 on all inputs or 1 on all inputs) or + // balanced (returns 0 on exactly one half of the input domain and 1 on the other half). + // Output: + // true if the function f is constant + // false if the function f is balanced + // + // Note: a trivial approach is to call the oracle multiple times: + // if the values for more than half of the possible inputs are the same, the function is constant. + // Quantum computing allows to perform this task in just one call to the oracle; try to implement this algorithm. + operation DJ_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => ())) : Bool + { + body + { + // Declare Bool variable in which the result will be accumulated; + // this variable has to be mutable to allow updating it. + mutable isConstantFunction = true; + + // Hint: even though Deutsch-Jozsa algorithm operates on a wider class of functions + // than Bernstein-Vazirani (i.e. functions which can not be represented as a scalar product, such as f(x) = 1), + // it can be expressed as running Bernstein-Vazirani algorithm + // and then post-processing the return value classically + + // ... + + return isConstantFunction; + } + } + + // Task 3.2. Testing Deutsch-Jozsa algorithm + // Goal: use your implementation of Deutsch-Jozsa algorithm from task 3.1 to test + // each of the oracles you've implemented in part I for being constant or balanced. + operation DJ_Test () : () + { + body + { + // Hint: you will need to use partial application to test Oracle_Kth_Qubit and Oracle_ParityFunction; + // see task 2.3 for a description of how to do that. + + // Hint: use AssertBoolEqual function to assert that the return value of DJ_Algorithm operation matches the expected value + + // DJ_Test appears in the list of unit tests for the solution; run it to verify your code. + + // ... + } + } + + + ////////////////////////////////////////////////////////////////// + // Part IV. Come up with your own algorithm! + ////////////////////////////////////////////////////////////////// + + // Task 4.1. Reconstruct the oracle from task 1.6 + // Inputs: + // 1) the number of qubits in the input register N for the function f + // 2) a quantum operation which implements the oracle |x〉|y〉 -> |x〉|y ⊕ f(x)〉, where + // x is N-qubit input register, y is 1-qubit answer register, and f is a Boolean function + // You are guaranteed that the function f implemented by the oracle can be represented as + // f(𝑥₀, …, 𝑥ₙ₋₁) = Σᵢ (𝑟ᵢ 𝑥ᵢ + (1 - 𝑟ᵢ)(1 - 𝑥ᵢ)) modulo 2 for some bit vector r = (𝑟₀, …, 𝑟ₙ₋₁). + // You have implemented the oracle implementing this function in task 1.6. + // Output: + // A bit vector r which generates the same oracle as the one you are given + operation Noname_Algorithm (N : Int, Uf : ((Qubit[], Qubit) => ())) : Int[] + { + body + { + // Declare a Bool array in which the result will be stored; + // the array has to be mutable to allow updating its elements. + mutable r = new Int[N]; + + // ... + + return r; + } + } +} diff --git a/DeutschJozsaAlgorithm/TestSuiteRunner.cs b/DeutschJozsaAlgorithm/TestSuiteRunner.cs new file mode 100644 index 00000000000..2eed269d5bb --- /dev/null +++ b/DeutschJozsaAlgorithm/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 System.Diagnostics; + +using Microsoft.Quantum.Simulation.XUnit; +using Xunit.Abstractions; + + +namespace Quantum.Kata.DeutschJozsaAlgorithm +{ + 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.DeutschJozsaAlgorithm. + /// + [OperationDriver(TestNamespace = "Quantum.Kata.DeutschJozsaAlgorithm")] + public void TestTarget(TestOperation op) + { + using (var sim = new OracleCounterSimulator()) + { + // 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/DeutschJozsaAlgorithm/Tests.qs b/DeutschJozsaAlgorithm/Tests.qs new file mode 100644 index 00000000000..2c26e834a0a --- /dev/null +++ b/DeutschJozsaAlgorithm/Tests.qs @@ -0,0 +1,348 @@ +// 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.DeutschJozsaAlgorithm +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Testing; + + // ------------------------------------------------------ + operation ApplyOracle (qs : Qubit[], oracle : ((Qubit[], Qubit) => ())) : () + { + body + { + let N = Length(qs); + oracle(qs[0..N-2], qs[N-1]); + } + } + + // ------------------------------------------------------ + operation ApplyOracleA (qs : Qubit[], oracle : ((Qubit[], Qubit) => () : Adjoint)) : () + { + body + { + let N = Length(qs); + oracle(qs[0..N-2], qs[N-1]); + } + adjoint auto; + } + + // ------------------------------------------------------ + operation AssertTwoOraclesAreEqual (nQubits : Range, + oracle1 : ((Qubit[], Qubit) => ()), + oracle2 : ((Qubit[], Qubit) => () : Adjoint)) : () + { + body + { + let sol = ApplyOracle(_, oracle1); + let refSol = ApplyOracleA(_, oracle2); + for (i in nQubits) { + AssertOperationsEqualReferenced(sol, refSol, i+1); + } + } + } + + // ------------------------------------------------------ + operation T11_Oracle_Zero_Test () : () + { + body + { + AssertTwoOraclesAreEqual(1..10, Oracle_Zero, Oracle_Zero_Reference); + } + } + + // ------------------------------------------------------ + operation T12_Oracle_One_Test () : () + { + body + { + AssertTwoOraclesAreEqual(1..10, Oracle_One, Oracle_One_Reference); + } + } + + // ------------------------------------------------------ + operation T13_Oracle_Kth_Qubit_Test () : () + { + body + { + let maxQ = 6; + // loop over index of the qubit to be used + for (k in 0..maxQ-1) { + // number of qubits to try is from k+1 to 6 + AssertTwoOraclesAreEqual(k+1..maxQ, Oracle_Kth_Qubit(_, _, k), Oracle_Kth_Qubit_Reference(_, _, k)); + } + } + } + + // ------------------------------------------------------ + operation T14_Oracle_OddNumberOfOnes_Test () : () + { + body + { + // cross-test: for 1 qubit it's the same as Kth_Qubit for k = 0 + AssertTwoOraclesAreEqual(1..1, Oracle_OddNumberOfOnes, Oracle_Kth_Qubit_Reference(_, _, 0)); + + AssertTwoOraclesAreEqual(1..10, Oracle_OddNumberOfOnes, Oracle_OddNumberOfOnes_Reference); + } + } + + // ------------------------------------------------------ + operation AssertTwoOraclesWithIntAreEqual (r : Int[], + oracle1 : ((Qubit[], Qubit, Int[]) => ()), + oracle2 : ((Qubit[], Qubit, Int[]) => () : Adjoint)) : () + { + body + { + AssertTwoOraclesAreEqual(Length(r)..Length(r), oracle1(_, _, r), oracle2(_, _, r)); + } + } + + operation T15_Oracle_ProductFunction_Test () : () + { + body + { + // cross-tests + // the mask for all 1's corresponds to Oracle_OddNumberOfOnes + mutable r = [1; 1; 1; 1; 1; 1; 1; 1; 1; 1]; + let L = Length(r); + for (i in 2..L) { + AssertTwoOraclesAreEqual(i..i, Oracle_ProductFunction(_, _, r[0..i-1]), Oracle_OddNumberOfOnes_Reference); + } + + // the mask with all 0's corresponds to Oracle_Zero + for (i in 0..L-1) { + set r[i] = 0; + } + for (i in 2..L) { + AssertTwoOraclesAreEqual(i..i, Oracle_ProductFunction(_, _, r[0..i-1]), Oracle_Zero_Reference); + } + + // the mask with only the K-th element set to 1 corresponds to Oracle_Kth_Qubit + for (i in 0..L-1) { + set r[i] = 1; + AssertTwoOraclesAreEqual(L..L, Oracle_ProductFunction(_, _, r), Oracle_Kth_Qubit_Reference(_, _, i)); + set r[i] = 0; + } + + set r = [1; 0; 1; 0; 1; 0]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); + + set r = [1; 0; 0; 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); + + set r = [0; 0; 1; 1; 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductFunction, Oracle_ProductFunction_Reference); + } + } + + operation T16_Oracle_ProductWithNegationFunction_Test () : () + { + body + { + // cross-tests + // the mask for all 1's corresponds to Oracle_OddNumberOfOnes + mutable r = [1; 1; 1; 1; 1; 1; 1; 1; 1; 1]; + let L = Length(r); + for (i in 2..L) { + AssertTwoOraclesAreEqual(i..i, Oracle_ProductWithNegationFunction(_, _, r[0..i-1]), Oracle_OddNumberOfOnes_Reference); + } + + set r = [1; 0; 1; 0; 1; 0]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); + + set r = [1; 0; 0; 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); + + set r = [0; 0; 1; 1; 1]; + AssertTwoOraclesWithIntAreEqual(r, Oracle_ProductWithNegationFunction, Oracle_ProductWithNegationFunction_Reference); + } + } + + operation T17_Oracle_HammingWithPrefix_Test () : () + { + body + { + mutable prefix = [1]; + AssertTwoOraclesAreEqual(1..10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); + + set prefix = [1; 0]; + AssertTwoOraclesAreEqual(2..10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); + + set prefix = [0; 0; 0]; + AssertTwoOraclesAreEqual(3..10, Oracle_HammingWithPrefix(_, _, prefix), Oracle_HammingWithPrefix_Reference(_, _, prefix)); + } + } + + operation T18_Oracle_MajorityFunction_Test () : () + { + body + { + AssertTwoOraclesAreEqual(3..3, Oracle_MajorityFunction, Oracle_MajorityFunction_Reference); + } + } + + // ------------------------------------------------------ + operation T21_BV_StatePrep_Test () : () + { + body + { + for (N in 1..10) { + using (qs = Qubit[N+1]) + { + // apply operation that needs to be tested + BV_StatePrep(qs[0..N-1], qs[N]); + + // apply adjoint reference operation + (Adjoint BV_StatePrep_Reference)(qs[0..N-1], qs[N]); + + // assert that all qubits end up in |0〉 state + AssertAllZero(qs); + } + } + } + } + + // ------------------------------------------------------ + function AssertOracleCallsCount<'T>(count: Int, oracle: 'T) : () { } + + // ------------------------------------------------------ + function ResetOracleCallsCount() : () { } + + // ------------------------------------------------------ + function AssertIntArrayEqual (actual : Int[], expected : Int[], message : String) : () { + let n = Length(actual); + if (n != Length(expected)) { + fail message; + } + for (idx in 0..(n-1)) { + if( actual[idx] != expected[idx] ) { + fail message; + } + } + } + + // ------------------------------------------------------ + function IntArrFromPositiveInt (n : Int, bits : Int) : Int[] { + let rbool = BoolArrFromPositiveInt(n, bits); + mutable r = new Int[bits]; + for (i in 0..bits-1) { + if (rbool[i]) { + set r[i] = 1; + } + } + return r; + } + + // ------------------------------------------------------ + operation AssertBVAlgorithmWorks (r : Int[]) : () + { + body + { + let oracle = Oracle_ProductFunction_Reference(_, _, r); + AssertIntArrayEqual(BV_Algorithm(Length(r), oracle), r, "Bernstein-Vazirani algorithm failed"); + AssertOracleCallsCount(1, oracle); + } + } + + operation T22_BV_Algorithm_Test () : () + { + body + { + ResetOracleCallsCount(); + + // test BV the way we suggest the learner to test it: + // apply the algorithm to reference oracles and check that the output is as expected + for (bits in 1..4) { + for (n in 0..2^bits-1) { + let r = IntArrFromPositiveInt(n, bits); + AssertBVAlgorithmWorks(r); + } + } + AssertBVAlgorithmWorks([1; 1; 1; 0; 0]); + AssertBVAlgorithmWorks([1; 0; 1; 0; 1; 0]); + } + } + + // ------------------------------------------------------ + operation AssertDJAlgorithmWorks(oracle: ((Qubit[], Qubit) => ()), expected : Bool, msg: String) : () + { + body + { + AssertBoolEqual(DJ_Algorithm(4, oracle), expected, msg); + AssertOracleCallsCount(1, oracle); + } + } + + operation T31_DJ_Algorithm_Test () : () + { + body + { + ResetOracleCallsCount(); + + // test DJ the way we suggest the learner to test it: + // apply the algorithm to reference oracles and check that the output is as expected + AssertBoolEqual(DJ_Algorithm(4, Oracle_Zero_Reference), true, "f(x) = 0 not identified as constant"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_One_Reference), true, "f(x) = 1 not identified as constant"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_Kth_Qubit_Reference(_, _, 1)), false, "f(x) = x_k not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_OddNumberOfOnes_Reference), false, "f(x) = sum of x_i not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_ProductFunction_Reference(_, _, [1; 0; 1; 1])), false, "f(x) = sum of r_i x_i not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_ProductWithNegationFunction_Reference(_, _, [1; 0; 1; 1])), false, "f(x) = sum of r_i x_i + (1 - r_i)(1 - x_i) not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(4, Oracle_HammingWithPrefix_Reference(_, _, [0; 1])), false, "f(x) = sum of x_i + 1 if prefix equals given not identified as balanced"); + AssertBoolEqual(DJ_Algorithm(3, Oracle_MajorityFunction_Reference), false, "f(x) = majority function not identified as balanced"); + } + } + + // ------------------------------------------------------ + operation AssertNonameAlgorithmWorks (r : Int[]) : () + { + body + { + let givenOracle = Oracle_ProductWithNegationFunction_Reference(_, _, r); + let res = Noname_Algorithm(Length(r), givenOracle); + + // check that the oracle was called once (later it will be called again by test harness) + AssertOracleCallsCount(1, givenOracle); + + // check that the oracle obtained from r + // is equivalent to the oracle obtained from return value + AssertIntEqual(Length(res), Length(r), "Returned bit vector must have the same length as the oracle input."); + let resOracle = Oracle_ProductWithNegationFunction_Reference(_, _, res); + AssertTwoOraclesAreEqual(Length(r)..Length(r), givenOracle, resOracle); + } + } + + operation CallNonameAlgoOnInt (n : Int, bits : Int) : () + { + body + { + let r = IntArrFromPositiveInt(n, bits); + AssertNonameAlgorithmWorks(r); + } + } + + operation T41_Noname_Algorithm_Test () : () + { + body + { + ResetOracleCallsCount(); + + // apply the algorithm to reference oracles and check that the output is as expected + // test all bit vectors of length 1..4 + for (bits in 1..4) { + for (n in 0..2^bits-1) { + CallNonameAlgoOnInt(n, bits); + } + } + // and a couple of random ones + AssertNonameAlgorithmWorks([1; 1; 1; 0; 0]); + AssertNonameAlgorithmWorks([1; 0; 1; 0; 1; 0]); + } + } +} \ No newline at end of file diff --git a/Measurements/.vscode/extensions.json b/Measurements/.vscode/extensions.json new file mode 100644 index 00000000000..14d152e069a --- /dev/null +++ b/Measurements/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "quantum.quantum-devkit-vscode" + ] +} \ No newline at end of file diff --git a/Measurements/.vscode/tasks.json b/Measurements/.vscode/tasks.json new file mode 100644 index 00000000000..bae788317e9 --- /dev/null +++ b/Measurements/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "args": [ + "build" + ], + "type": "process", + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "test", + "command": "dotnet", + "args": [ + "test" + ], + "type": "process", + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Measurements/Measurements.csproj b/Measurements/Measurements.csproj new file mode 100644 index 00000000000..beddaeaf7fd --- /dev/null +++ b/Measurements/Measurements.csproj @@ -0,0 +1,22 @@ + + + netcoreapp2.0 + x64 + false + Quantum.Kata.Measurements + + + + + + + + + + + + + + + + diff --git a/Measurements/Measurements.sln b/Measurements/Measurements.sln new file mode 100644 index 00000000000..2768c3ab468 --- /dev/null +++ b/Measurements/Measurements.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Measurements", "Measurements.csproj", "{F7A0175F-4217-4343-8A3C-EA68FAAB6B7B}" +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 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4E22BDB7-FE55-4D1C-98FA-BD7612C8525D} + EndGlobalSection +EndGlobal diff --git a/Measurements/README.md b/Measurements/README.md new file mode 100644 index 00000000000..6416bc925f9 --- /dev/null +++ b/Measurements/README.md @@ -0,0 +1,11 @@ +# Welcome! + +The measurements kata covers the following topics: +- single-qubit measurements +- joint measurements +- quantum state discrimination for both orthogonal and non-orthogonal states + +Variations of quantum state discrimination tasks are covered in the paper ["Quantum State Discrimination"](https://arxiv.org/pdf/quant-ph/0010114.pdf). +* Task 2.1 is an example of hypothesis testing for two pure states. +* Task 2.2 is an example of unambiguous state discrimination. See also the paper ["Unambiguous quantum measurement of nonorthogonal states"](https://www.researchgate.net/publication/13375059_Unambiguous_quantum_measurement_of_nonorthogonal_states) + for further information and hints about how to implement the unambiguous measurements required for this task. diff --git a/Measurements/ReferenceImplementation.qs b/Measurements/ReferenceImplementation.qs new file mode 100644 index 00000000000..ba787d507e1 --- /dev/null +++ b/Measurements/ReferenceImplementation.qs @@ -0,0 +1,414 @@ +// 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.Measurements +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Convert; + open Microsoft.Quantum.Extensions.Math; + + ////////////////////////////////////////////////////////////////// + // Part I. Single-Qubit Measurements + ////////////////////////////////////////////////////////////////// + + // Task 1.1. |0〉 or |1〉 ? + // Input: a qubit which is guaranteed to be in |0〉 or |1〉 state. + // Output: true if qubit was in |1〉 state, or false if it was in |0〉 state. + // The state of the qubit at the end of the operation does not matter. + operation IsQubitOne_Reference (q : Qubit) : Bool + { + body + { + let res = M(q); + return res == One; + } + } + + // Task 1.2. |+〉 or |-〉 ? + // Input: a qubit which is guaranteed to be in |+〉 or |-〉 state + // (|+〉 = (|0〉 + |1〉) / sqrt(2), |-〉 = (|0〉 - |1〉) / sqrt(2)). + // Output: true if qubit was in |+〉 state, or false if it was in |-〉 state. + // The state of the qubit at the end of the operation does not matter. + operation IsQubitPlus_Reference (q : Qubit) : Bool + { + body + { + H(q); + let res = M(q); + return res == Zero; + } + } + + // Task 1.3. |A〉 or |B〉 ? + // Inputs: + // 1) angle alpha, in radians, represented as Double + // 2) a qubit which is guaranteed to be in |A〉 or |B〉 state + // |A〉 = cos(alpha) * |0〉 + sin(alpha) * |1〉, + // |B〉 = - sin(alpha) * |0〉 + cos(alpha) * |1〉. + // Output: true if qubit was in |A〉 state, or false if it was in |B〉 state. + // The state of the qubit at the end of the operation does not matter. + operation IsQubitA_Reference (alpha : Double, q : Qubit) : Bool + { + body + { + // |0〉 is converted into |A〉 and |1〉 into |B〉 by Ry(2.0 * alpha) + // so |A〉 is converted into |0〉 by the opposite rotation + Ry(- 2.0 * alpha, q); + let res = M(q); + return res == Zero; + } + } + + // Task 1.4. |00〉 or |11〉 ? + // Input: two qubits (stored in an array) which are guaranteed to be in |00〉 or |11〉 state. + // Output: 0 if qubits were in |00〉 state, + // 1 if they were in |11〉 state. + // The state of the qubits at the end of the operation does not matter. + operation ZeroZeroOrOneOne_Reference (qs : Qubit[]) : Int + { + body + { + // it's enough to do one measurement on any qubit + let res = M(qs[0]); + if (res == Zero) { + return 0; + } else { + return 1; + } + } + } + + // Task 1.5. Distinguish four basis states + // Input: two qubits (stored in an array) which are guaranteed to be + // in one of the four basis states (|00〉, |01〉, |10〉 or |11〉). + // Output: 0 if qubits were in |00〉 state, + // 1 if they were in |01〉 state, + // 2 if they were in |10〉 state, + // 3 if they were in |11〉 state. + // The state of the qubits at the end of the operation does not matter. + operation BasisStateMeasurement_Reference (qs : Qubit[]) : Int + { + body + { + // measurement on the first qubit gives the higher bit of the answer, on the second - the lower + mutable m1 = 0; + if (M(qs[0]) == One) { + set m1 = 1; + } + mutable m2 = 0; + if (M(qs[1]) == One) { + set m2 = 1; + } + return m1 * 2 + m2; + } + } + + // Task 1.6. Distinguish two basis states given by bit strings + // Inputs: + // 1) N qubits (stored in an array) which are guaranteed to be + // in one of the two basis states described by the given bit strings. + // 2) two bit string represented as Bool[]s. + // Output: 0 if qubits were in the basis state described by the first bit string, + // 1 if they were in the basis state described by the second bit string. + // Bit values false and true correspond to |0〉 and |1〉 states. + // The state of the qubits at the end of the operation does not matter. + // You are guaranteed that the both bit strings have the same length as the qubit array, + // and that the bit strings will differ in at least one bit. + // You can use exactly one measurement. + // Example: for bit strings [false; true; false] and [false; false; true] + // return 0 corresponds to state |010〉, and return 1 corresponds to state |001〉. + + function FindFirstDiff_Reference (bits1 : Bool[], bits2 : Bool[]) : Int + { + mutable firstDiff = -1; + for (i in 0 .. Length(bits1)-1) { + if (bits1[i] != bits2[i] && firstDiff == -1) { + set firstDiff = i; + } + } + return firstDiff; + } + + operation TwoBitstringsMeasurement_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Int + { + body + { + // find the first index at which the bit strings are different and measure it + let firstDiff = FindFirstDiff_Reference(bits1, bits2); + let res = (M(qs[firstDiff]) == One); + if (res == bits1[firstDiff]) { + return 0; + } else { + return 1; + } + } + } + + // Task 1.7. |0...0〉 state or W state ? + // Input: N qubits (stored in an array) which are guaranteed to be + // either in |0...0〉 state + // or in W state (https://en.wikipedia.org/wiki/W_state). + // Output: 0 if qubits were in |0...0〉 state, + // 1 if they were in W state. + // The state of the qubits at the end of the operation does not matter. + operation AllZerosOrWState_Reference (qs : Qubit[]) : Int + { + body + { + // measure all qubits; if there is exactly one One, it's W state, if there are no Ones, it's |0...0〉 + // (and there should never be two or more Ones) + mutable countOnes = 0; + for (i in 0..Length(qs)-1) { + if (M(qs[i]) == One) { + set countOnes = countOnes + 1; + } + } + if (countOnes > 1) { + fail "Impossible to get multiple Ones when measuring W state"; + } + if (countOnes == 0) { + return 0; + } + return 1; + } + } + + // Task 1.8. GHZ state or W state ? + // Input: N qubits (stored in an array) which are guaranteed to be + // either in GHZ state (https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) + // or in W state (https://en.wikipedia.org/wiki/W_state). + // Output: 0 if qubits were in GHZ state, + // 1 if they were in W state. + // The state of the qubits at the end of the operation does not matter. + operation GHZOrWState_Reference (qs : Qubit[]) : Int + { + body + { + // measure all qubits; if there is exactly one One, it's W state, + // if there are no Ones or all are Ones, it's GHZ + // (and there should never be a different number of Ones) + let N = Length(qs); + mutable countOnes = 0; + for (i in 0..N-1) { + if (M(qs[i]) == One) { + set countOnes = countOnes + 1; + } + } + if (countOnes > 1 && countOnes < Length(qs)) { + fail $"Impossible to get {countOnes} Ones when measuring W state or GHZ state on {N} qubits"; + } + if (countOnes == 1) { + return 1; + } + return 0; + } + } + + // Task 1.9. Distinguish four Bell states + // Input: two qubits (stored in an array) which are guaranteed to be in one of the four Bell states: + // |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2) + // |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2) + // |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2) + // |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2) + // Output: 0 if qubits were in |Φ⁺〉 state, + // 1 if they were in |Φ⁻〉 state, + // 2 if they were in |Ψ⁺〉 state, + // 3 if they were in |Ψ⁻〉 state. + // The state of the qubits at the end of the operation does not matter. + operation BellState_Reference (qs : Qubit[]) : Int + { + body + { + H(qs[0]); + H(qs[1]); + CNOT(qs[1], qs[0]); + H(qs[1]); + mutable m1 = 0; + if (M(qs[0]) == One) { + set m1 = 1; + } + mutable m2 = 0; + if (M(qs[1]) == One) { + set m2 = 1; + } + return m2 * 2 + m1; + } + } + + // Task 1.10*. Distinguish four orthogonal 2-qubit states + // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: + // |S0〉 = (|00〉 + |01〉 + |10〉 + |11〉) / 2 + // |S1〉 = (|00〉 - |01〉 + |10〉 - |11〉) / 2 + // |S2〉 = (|00〉 + |01〉 - |10〉 - |11〉) / 2 + // |S3〉 = (|00〉 - |01〉 - |10〉 + |11〉) / 2 + // Output: 0 if qubits were in |S0〉 state, + // 1 if they were in |S1〉 state, + // 2 if they were in |S2〉 state, + // 3 if they were in |S3〉 state. + // The state of the qubits at the end of the operation does not matter. + operation TwoQubitState_Reference (qs : Qubit[]) : Int + { + body + { + // These states are produced by H ⊗ H, applied to four basis states. + // To measure them, apply H ⊗ H followed by basis state measurement + // implemented in BasisStateMeasurement_Reference. + H(qs[0]); + H(qs[1]); + return BasisStateMeasurement_Reference(qs); + } + } + + // Task 1.11**. Distinguish four orthogonal 2-qubit states, part two + // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: + // |S0〉 = ( |00〉 - |01〉 - |10〉 - |11〉) / 2 + // |S1〉 = (-|00〉 + |01〉 - |10〉 - |11〉) / 2 + // |S2〉 = (-|00〉 - |01〉 + |10〉 - |11〉) / 2 + // |S3〉 = (-|00〉 - |01〉 - |10〉 + |11〉) / 2 + // Output: 0 if qubits were in |S0〉 state, + // 1 if they were in |S1〉 state, + // 2 if they were in |S2〉 state, + // 3 if they were in |S3〉 state. + // The state of the qubits at the end of the operation does not matter. + + // Helper function to implement diag(-1, 1, 1, 1) + operation ApplyDiag (qs : Qubit[]) : () + { + body + { + ApplyToEach(X, qs); + (Controlled Z)([qs[0]], qs[1]); + ApplyToEach(X, qs); + } + adjoint self + } + + // The actual reference implementation for Task 1.11 + operation TwoQubitStatePartTwo_Reference (qs : Qubit[]) : Int + { + body + { + // Observe that the unitary matrix A formed by the columns |S0〉, ..., |S3〉 + // is up to permutations matrices and diagonal +1/-1 matrices equal to the + // tensor product H ⊗ H when multiplied from the left and the right. + // Specifically, A = diag(-1, 1, 1, 1) (H ⊗ H) diag(-1, 1, 1, 1) pi, + // where pi is the permutation (1,2) corresponding to a swap of 2 qubits. + SWAP(qs[0], qs[1]); // pi + With(ApplyDiag, ApplyToEach(H, _), qs); // diag(..) (H ⊗ H) diag(..) + return BasisStateMeasurement_Reference(qs); + } + } + + + ////////////////////////////////////////////////////////////////// + // Part II*. Discriminating Nonorthogonal States + ////////////////////////////////////////////////////////////////// + + // Task 2.1*. |0〉 or |+〉 ? + // (quantum hypothesis testing or state discrimination with minimum error) + // Input: a qubit which is guaranteed to be in |0〉 or |+〉 state with equal probability. + // Output: true if qubit was in |0〉 state, or false if it was in |+〉 state. + // The state of the qubit at the end of the operation does not matter. + // Note: in this task you have to get accuracy of at least 80%. + operation IsQubitPlusOrZero_Reference (q : Qubit) : Bool + { + body + { + // Let {E_a, E_b} be a measurement with two outcomes a and b, which we identify with + // the answers, i.e., "a" = "state was |0〉" and "b = state was |+〉". Then we define + // P(a|0) = probability to observe first outcome given that the state was |0〉 + // P(b|0) = probability to observe second outcome given that the state was |0〉 + // P(a|+) = probability to observe first outcome given that the state was |+〉 + // P(b|+) = probability to observe second outcome given that the state was |+〉 + // the task is to maximize the probability to be correct on a single shot experiment + // which is the same as to minimize the probability to be wrong (on a single shot). + // Assuming uniform prior, i.e., P(+) = P(0) = 1/2, we get + // P_correct = P(0) P(a|0) + P(+) P(b|+). Assuming a von Neumann measurement of the + // form E_a = Ry(2*alpha) * (1,0) = (cos(alpha), sin(alpha)) and + // E_b = Ry(2*alpha) * (0,1) = (sin(alpha), -cos(alpha)), we get that + // P_correct = 1/2 + cos²(alpha) + cos(alpha) sin(alpha). Maximizing this for alpha, + // we get max P_success = 1/2 (1 + 1/sqrt(2)) = 0.8535.., which is attained for alpha = π/8. + + // Rotate the input state by π/8 means to apply Ry with angle 2π/8. + Ry(0.25*PI(), q); + return (M(q) == Zero); + } + } + + // Task 2.2**. |0〉, |+〉 or inconclusive? + // (unambiguous state discrimination) + // Input: a qubit which is guaranteed to be in |0〉 or |+〉 state with equal probability. + // Output: 0 if qubit was in |0〉 state, + // 1 if it was in |+〉 state, + // -1 if you can't decide, i.e., an "inconclusive" result. + // Your solution: + // - can never give 0 or 1 answer incorrectly (i.e., identify |0〉 as 1 or |+〉 as 0). + // - must give inconclusive (-1) answer at most 80% of the times. + // - must correctly identify |0〉 state as 0 at least 10% of the times. + // - must correctly identify |1〉 state as 1 at least 10% of the times. + // + // The state of the qubit at the end of the operation does not matter. + // You are allowed to use ancilla qubit(s). + operation IsQubitPlusZeroOrInconclusiveSimpleUSD_Reference (q : Qubit) : Int + { + body + { + // A simple strategy that gives an inconclusive result with probability 0.75 + // and never errs in case it yields a conclusive result can be obtained from + // randomizing the choice of measurement basis between the computational basis (std) + // and the Hadamard basis (had). Observe that when measured in the standard basis, + // the state |0〉 will always lead to the outcome "0", whereas the state |+〉 + // will lead to outcomes "0" respectively "1" with probability 1/2. This means + // that upon measuring "1" we can with certainty conclude that the state was |+〉. + // A similar argument applies to the scenario where we measure in the Hadamard + // basis, where |0〉 can lead to both outcomes, whereas |+〉 always leads to "0". + // Then upon measuring "1" we can with certainty conclude that the state was |0〉. + // + // This leads to the following scenarios (shown are the conditional probabilities + // of the above scenarios and resulting answers). + // state | basis | output 0 | output 1 | output -1 + // ----------------------------------------------- + // |0〉 | std | 0 | 0 | 1 + // |+〉 | std | 0 | 1/2 | 1/2 + // |0〉 | had | 1/2 | 0 | 1/2 + // |+〉 | had | 0 | 0 | 1 + + mutable output = 0; + let basis = RandomInt(2); + // randomize over std and had + + if (basis == 0) { + // use standard basis + let result = M(q); + if (result == One) { + // this can only arise if the state was |+〉 + set output = 1; + } + else { + set output = -1; + } + } + else { + // use Hadamard basis + H(q); + let result = M(q); + if (result == One) { + // this can only arise if the state was |0〉 + set output = 0; + } + else { + set output = -1; + } + } + return output; + } + } +} diff --git a/Measurements/Tasks.qs b/Measurements/Tasks.qs new file mode 100644 index 00000000000..117adb2c5c6 --- /dev/null +++ b/Measurements/Tasks.qs @@ -0,0 +1,276 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Quantum.Kata.Measurements +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Convert; + open Microsoft.Quantum.Extensions.Math; + + ////////////////////////////////////////////////////////////////// + // Welcome! + ////////////////////////////////////////////////////////////////// + + // "Measurements" quantum kata is a series of exercises designed + // to get you familiar with programming in Q#. + // It covers the following topics: + // - single-qubit measurements, + // - joint measurements, + // - discriminating orthogonal and nonorthogonal states. + // + // 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 Measurements + ////////////////////////////////////////////////////////////////// + + // Task 1.1. |0〉 or |1〉 ? + // Input: a qubit which is guaranteed to be in |0〉 or |1〉 state. + // Output: true if qubit was in |1〉 state, or false if it was in |0〉 state. + // The state of the qubit at the end of the operation does not matter. + operation IsQubitOne (q : Qubit) : Bool + { + body + { + // ... + return false; + } + } + + // Task 1.2. |+〉 or |-〉 ? + // Input: a qubit which is guaranteed to be in |+〉 or |-〉 state + // (|+〉 = (|0〉 + |1〉) / sqrt(2), |-〉 = (|0〉 - |1〉) / sqrt(2)). + // Output: true if qubit was in |+〉 state, or false if it was in |-〉 state. + // The state of the qubit at the end of the operation does not matter. + operation IsQubitPlus (q : Qubit) : Bool + { + body + { + // ... + return false; + } + } + + // Task 1.3. |A〉 or |B〉 ? + // Inputs: + // 1) angle alpha, in radians, represented as Double + // 2) a qubit which is guaranteed to be in |A〉 or |B〉 state + // |A〉 = cos(alpha) * |0〉 + sin(alpha) * |1〉, + // |B〉 = - sin(alpha) * |0〉 + cos(alpha) * |1〉. + // Output: true if qubit was in |A〉 state, or false if it was in |B〉 state. + // The state of the qubit at the end of the operation does not matter. + operation IsQubitA (alpha : Double, q : Qubit) : Bool + { + body + { + // ... + return false; + } + } + + // Task 1.4. |00〉 or |11〉 ? + // Input: two qubits (stored in an array) which are guaranteed to be in |00〉 or |11〉 state. + // Output: 0 if qubits were in |00〉 state, + // 1 if they were in |11〉 state. + // The state of the qubits at the end of the operation does not matter. + operation ZeroZeroOrOneOne (qs : Qubit[]) : Int + { + body + { + // ... + return -1; + } + } + + // Task 1.5. Distinguish four basis states + // Input: two qubits (stored in an array) which are guaranteed to be + // in one of the four basis states (|00〉, |01〉, |10〉 or |11〉). + // Output: 0 if qubits were in |00〉 state, + // 1 if they were in |01〉 state, + // 2 if they were in |10〉 state, + // 3 if they were in |11〉 state. + // In this task and the subsequent ones the order of qubit states + // in task description matches the order of qubits in the array + // (i.e., |10〉 state corresponds to qs[0] in state |1〉 and qs[1] in state |0〉). + // The state of the qubits at the end of the operation does not matter. + operation BasisStateMeasurement (qs : Qubit[]) : Int + { + body + { + // ... + return -1; + } + } + + // Task 1.6. Distinguish two basis states given by bit strings + // Inputs: + // 1) N qubits (stored in an array) which are guaranteed to be + // in one of the two basis states described by the given bit strings. + // 2) two bit string represented as Bool[]s. + // Output: 0 if qubits were in the basis state described by the first bit string, + // 1 if they were in the basis state described by the second bit string. + // Bit values false and true correspond to |0〉 and |1〉 states. + // The state of the qubits at the end of the operation does not matter. + // You are guaranteed that the both bit strings have the same length as the qubit array, + // and that the bit strings will differ in at least one bit. + // You can use exactly one measurement. + // Example: for bit strings [false; true; false] and [false; false; true] + // return 0 corresponds to state |010〉, and return 1 corresponds to state |001〉. + operation TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Int + { + body + { + // ... + return -1; + } + } + + // Task 1.7. |0...0〉 state or W state ? + // Input: N qubits (stored in an array) which are guaranteed to be + // either in |0...0〉 state + // or in W state (https://en.wikipedia.org/wiki/W_state). + // Output: 0 if qubits were in |0...0〉 state, + // 1 if they were in W state. + // The state of the qubits at the end of the operation does not matter. + operation AllZerosOrWState (qs : Qubit[]) : Int + { + body + { + // ... + return -1; + } + } + + // Task 1.8. GHZ state or W state ? + // Input: N qubits (stored in an array) which are guaranteed to be + // either in GHZ state (https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) + // or in W state (https://en.wikipedia.org/wiki/W_state). + // Output: 0 if qubits were in GHZ state, + // 1 if they were in W state. + // The state of the qubits at the end of the operation does not matter. + operation GHZOrWState (qs : Qubit[]) : Int + { + body + { + // ... + return -1; + } + } + + // Task 1.9. Distinguish four Bell states + // Input: two qubits (stored in an array) which are guaranteed to be in one of the four Bell states: + // |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2) + // |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2) + // |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2) + // |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2) + // Output: 0 if qubits were in |Φ⁺〉 state, + // 1 if they were in |Φ⁻〉 state, + // 2 if they were in |Ψ⁺〉 state, + // 3 if they were in |Ψ⁻〉 state. + // The state of the qubits at the end of the operation does not matter. + operation BellState (qs : Qubit[]) : Int + { + body + { + // Hint: you need to use 2-qubit gates to solve this task + + // ... + return -1; + } + } + + // Task 1.10*. Distinguish four orthogonal 2-qubit states + // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: + // |S0〉 = (|00〉 + |01〉 + |10〉 + |11〉) / 2 + // |S1〉 = (|00〉 - |01〉 + |10〉 - |11〉) / 2 + // |S2〉 = (|00〉 + |01〉 - |10〉 - |11〉) / 2 + // |S3〉 = (|00〉 - |01〉 - |10〉 + |11〉) / 2 + // Output: 0 if qubits were in |S0〉 state, + // 1 if they were in |S1〉 state, + // 2 if they were in |S2〉 state, + // 3 if they were in |S3〉 state. + // The state of the qubits at the end of the operation does not matter. + operation TwoQubitState (qs : Qubit[]) : Int + { + body + { + // ... + return -1; + } + } + + // Task 1.11**. Distinguish four orthogonal 2-qubit states, part two + // Input: two qubits (stored in an array) which are guaranteed to be in one of the four orthogonal states: + // |S0〉 = ( |00〉 - |01〉 - |10〉 - |11〉) / 2 + // |S1〉 = (-|00〉 + |01〉 - |10〉 - |11〉) / 2 + // |S2〉 = (-|00〉 - |01〉 + |10〉 - |11〉) / 2 + // |S3〉 = (-|00〉 - |01〉 - |10〉 + |11〉) / 2 + // Output: 0 if qubits were in |S0〉 state, + // 1 if they were in |S1〉 state, + // 2 if they were in |S2〉 state, + // 3 if they were in |S3〉 state. + // The state of the qubits at the end of the operation does not matter. + operation TwoQubitStatePartTwo (qs : Qubit[]) : Int + { + body + { + // ... + return -1; + } + } + + + ////////////////////////////////////////////////////////////////// + // Part II*. Discriminating Nonorthogonal States + ////////////////////////////////////////////////////////////////// + + // The solutions for tasks in this section are validated using the following method. + // The solution is called on N input states, each of which is picked randomly, + // with all possible input states equally likely to be generated. + // The accuracy of state discrimination is estimated as an average of + // discrimination correctness over all input states. + + // Task 2.1*. |0〉 or |+〉 ? + // (quantum hypothesis testing or state discrimination with minimum error) + // Input: a qubit which is guaranteed to be in |0〉 or |+〉 state with equal probability. + // Output: true if qubit was in |0〉 state, or false if it was in |+〉 state. + // The state of the qubit at the end of the operation does not matter. + // Note: in this task you have to get accuracy of at least 80%. + operation IsQubitPlusOrZero (q : Qubit) : Bool + { + body + { + // ... + return true; + } + } + + // Task 2.2**. |0〉, |+〉 or inconclusive? + // (unambiguous state discrimination) + // Input: a qubit which is guaranteed to be in |0〉 or |+〉 state with equal probability. + // Output: 0 if qubit was in |0〉 state, + // 1 if it was in |+〉 state, + // -1 if you can't decide, i.e., an "inconclusive" result. + // Your solution: + // - can never give 0 or 1 answer incorrectly (i.e., identify |0〉 as 1 or |+〉 as 0). + // - must give inconclusive (-1) answer at most 80% of the times. + // - must correctly identify |0〉 state as 0 at least 10% of the times. + // - must correctly identify |1〉 state as 1 at least 10% of the times. + // + // The state of the qubit at the end of the operation does not matter. + // You are allowed to use ancilla qubit(s). + operation IsQubitPlusZeroOrInconclusiveSimpleUSD (q : Qubit) : Int + { + body + { + // ... + return -2; + } + } +} \ No newline at end of file diff --git a/Measurements/TestSuiteRunner.cs b/Measurements/TestSuiteRunner.cs new file mode 100644 index 00000000000..3372c46f245 --- /dev/null +++ b/Measurements/TestSuiteRunner.cs @@ -0,0 +1,42 @@ +// 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 Microsoft.Quantum.Simulation.Simulators; +using Xunit.Abstractions; +using System.Diagnostics; + +namespace Quantum.Kata.Measurements +{ + 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.Measurements. + /// + [OperationDriver(TestNamespace = "Quantum.Kata.Measurements")] + public void TestTarget(TestOperation op) + { + using (var sim = new QuantumSimulator()) + { + // OnLog defines action(s) performed when Q# test calls function Message + sim.OnLog += (msg) => { output.WriteLine(msg); }; + sim.OnLog += (msg) => { Debug.WriteLine(msg); }; + op.TestOperationRunner(sim); + } + } + } +} diff --git a/Measurements/Tests.qs b/Measurements/Tests.qs new file mode 100644 index 00000000000..ac02b343ff9 --- /dev/null +++ b/Measurements/Tests.qs @@ -0,0 +1,533 @@ +// 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.Measurements +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Convert; + open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Extensions.Testing; + + ////////////////////////////////////////////////////////////////// + + // "Framework" operation for testing single-qubit tasks for distinguishing states of one qubit + // with Bool return + operation DistinguishTwoStates_OneQubit ( + statePrep : ((Qubit, Int) => ()), + testImpl : (Qubit => Bool) + ) : () + { + body + { + let nTotal = 100; + mutable nOk = 0; + using (qs = Qubit[1]) + { + for (i in 1..nTotal) + { + // get a random bit to define whether qubit will be in a state corresponding to true return (1) or to false one (0) + // state = 0 false return + // state = 1 true return + let state = RandomIntPow2(1); + + // do state prep: convert |0〉 to outcome with false return or to outcome with true return depending on state + statePrep(qs[0], state); + + // get the solution's answer and verify that it's a match + let ans = testImpl(qs[0]); + if (ans == (state == 1)) { + set nOk = nOk + 1; + } + + // we're not checking the state of the qubit after the operation + Reset(qs[0]); + } + } + AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); + } + } + + // ------------------------------------------------------ + operation StatePrep_IsQubitOne (q : Qubit, state : Int) : () { + body { + if (state == 0) { + // convert |0〉 to |0〉 + } else { + // convert |0〉 to |1〉 + X(q); + } + } + } + + operation T101_IsQubitOne_Test () : () + { + body + { + DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitOne); + } + } + + // ------------------------------------------------------ + operation StatePrep_IsQubitPlus (q : Qubit, state : Int) : () { + body { + if (state == 0) { + // convert |0〉 to |-〉 + X(q); + H(q); + } else { + // convert |0〉 to |+〉 + H(q); + } + } + } + + operation T102_IsQubitPlus_Test () : () + { + body + { + DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitPlus); + } + } + + // ------------------------------------------------------ + // |A〉 = cos(alpha) * |0〉 + sin(alpha) * |1〉, + // |B〉 = - sin(alpha) * |0〉 + cos(alpha) * |1〉. + operation StatePrep_IsQubitA (alpha : Double, q : Qubit, state : Int) : () { + body { + if (state == 0) { + // convert |0〉 to |B〉 + X(q); + Ry(2.0 * alpha, q); + } else { + // convert |0〉 to |A〉 + Ry(2.0 * alpha, q); + } + } + } + + operation T103_IsQubitA_Test () : () + { + body + { + // cross-test + // alpha = 0.0 or PI() => !isQubitOne + // alpha = PI() / 2.0 => isQubitOne + DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitA(PI() / 2.0, _)); + // alpha = PI() / 4.0 => isQubitPlus + DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitA(PI() / 4.0, _)); + + for (i in 0..10) { + let alpha = PI() * ToDouble(i) / 10.0; + DistinguishTwoStates_OneQubit(StatePrep_IsQubitA(alpha, _, _), IsQubitA(alpha, _)); + } + } + } + + // ------------------------------------------------------ + + // "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits + // with Int return + operation DistinguishStates_MultiQubit ( + Nqubit : Int, + Nstate : Int, + statePrep : ((Qubit[], Int) => ()), + testImpl : (Qubit[] => Int) + ) : () + { + body + { + let nTotal = 100; + mutable nOk = 0; + using (qs = Qubit[Nqubit]) + { + for (i in 1..nTotal) + { + // get a random integer to define the state of the qubits + let state = RandomInt(Nstate); + + // do state prep: convert |0...0〉 to outcome with return equal to state + statePrep(qs, state); + + // get the solution's answer and verify that it's a match + let ans = testImpl(qs); + if (ans == state) { + set nOk = nOk + 1; + } + + // we're not checking the state of the qubit after the operation + ResetAll(qs); + } + } + AssertIntEqual(nOk, nTotal, $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state."); + } + } + + // ------------------------------------------------------ + operation StatePrep_ZeroZeroOrOneOne (qs : Qubit[], state : Int) : () { + body { + if (state == 1) { + // |11〉 + X(qs[0]); + X(qs[1]); + } + } + } + + operation T104_ZeroZeroOrOneOne_Test () : () { + body { + DistinguishStates_MultiQubit(2, 2, StatePrep_ZeroZeroOrOneOne, ZeroZeroOrOneOne); + } + } + + // ------------------------------------------------------ + operation StatePrep_BasisStateMeasurement (qs : Qubit[], state : Int) : () { + body { + if (state / 2 == 1) { + // |10〉 or |11〉 + X(qs[0]); + } + if (state % 2 == 1) { + // |01〉 or |11〉 + X(qs[1]); + } + } + } + + operation T105_BasisStateMeasurement_Test () : () { + body { + DistinguishStates_MultiQubit(2, 4, StatePrep_BasisStateMeasurement, BasisStateMeasurement); + } + } + + // ------------------------------------------------------ + operation StatePrep_Bitstring (qs : Qubit[], bits : Bool[]) : () { + body { + for (i in 0..Length(qs)-1) { + if (bits[i]) { + X(qs[i]); + } + } + } + } + + operation StatePrep_TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[], state : Int) : () { + body { + if (state == 0) { + StatePrep_Bitstring(qs, bits1); + } else { + StatePrep_Bitstring(qs, bits2); + } + } + } + + operation T106_TwoBitstringsMeasurement_Test () : () { + body { + for (i in 1..1) { + let b1 = [false; true]; + let b2 = [true; false]; + DistinguishStates_MultiQubit(2, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); + } + for (i in 1..1) { + let b1 = [true; true; false]; + let b2 = [false; true; true]; + DistinguishStates_MultiQubit(3, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); + } + for (i in 1..1) { + let b1 = [false; true; true; false]; + let b2 = [false; true; true; true]; + DistinguishStates_MultiQubit(4, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); + } + for (i in 1..1) { + let b1 = [true; false; false; false]; + let b2 = [true; false; true; true]; + DistinguishStates_MultiQubit(4, 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _), TwoBitstringsMeasurement(_, b1, b2)); + } + } + } + + // ------------------------------------------------------ + operation WState_Arbitrary_Reference (qs : Qubit[]) : () + { + body + { + let N = Length(qs); + if (N == 1) { + // base case of recursion: |1〉 + X(qs[0]); + } else { + // |W_N> = |0〉|W_(N-1)> + |1〉|0...0〉 + // do a rotation on the first qubit to split it into |0〉 and |1〉 with proper weights + // |0〉 -> sqrt((N-1)/N) |0〉 + 1/sqrt(N) |1〉 + let theta = ArcSin(1.0 / Sqrt(ToDouble(N))); + Ry(2.0 * theta, qs[0]); + // do a zero-controlled W-state generation for qubits 1..N-1 + X(qs[0]); + (Controlled WState_Arbitrary_Reference)(qs[0..0], qs[1..N-1]); + X(qs[0]); + } + } + adjoint auto; + controlled auto; + adjoint controlled auto; + } + + operation StatePrep_AllZerosOrWState (qs : Qubit[], state : Int) : () { + body { + if (state == 1) { + // prep W state + WState_Arbitrary_Reference(qs); + } + } + } + + operation T107_AllZerosOrWState_Test () : () { + body { + for (i in 2..6) { + DistinguishStates_MultiQubit(i, 2, StatePrep_AllZerosOrWState, AllZerosOrWState); + } + } + } + + // ------------------------------------------------------ + operation GHZ_State_Reference (qs : Qubit[]) : () + { + body + { + H(qs[0]); + for (i in 1 .. Length(qs)-1) { + CNOT(qs[0], qs[i]); + } + } + adjoint auto; + } + + operation StatePrep_GHZOrWState (qs : Qubit[], state : Int) : () { + body { + if (state == 0) { + // prep GHZ state + GHZ_State_Reference(qs); + } else { + // prep W state + WState_Arbitrary_Reference(qs); + } + } + } + + operation T108_GHZOrWState_Test () : () { + body { + for (i in 2..6) { + DistinguishStates_MultiQubit(i, 2, StatePrep_GHZOrWState, GHZOrWState); + } + } + } + + // ------------------------------------------------------ + // 0 - |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2) + // 1 - |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2) + // 2 - |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2) + // 3 - |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2) + operation StatePrep_BellState (qs : Qubit[], state : Int) : () { + body { + H(qs[0]); + CNOT(qs[0], qs[1]); + // now we have |00〉 + |11〉 - modify it based on state arg + if (state % 2 == 1) { + // negative phase + Z(qs[1]); + } + if (state / 2 == 1) { + X(qs[1]); + } + } + } + + operation T109_BellState_Test () : () { + body { + DistinguishStates_MultiQubit(2, 4, StatePrep_BellState, BellState); + } + } + + // ------------------------------------------------------ + // 0 - (|00〉 + |01〉 + |10〉 + |11〉) / 2 + // 1 - (|00〉 - |01〉 + |10〉 - |11〉) / 2 + // 2 - (|00〉 + |01〉 - |10〉 - |11〉) / 2 + // 3 - (|00〉 - |01〉 - |10〉 + |11〉) / 2 + operation StatePrep_TwoQubitState (qs : Qubit[], state : Int) : () { + body { + // start with state prep of basis vectors + StatePrep_BasisStateMeasurement(qs, state); + H(qs[0]); + H(qs[1]); + } + } + + // ------------------------------------------------------ + // 0 - ( |00〉 - |01〉 - |10〉 - |11〉) / 2 + // 1 - (-|00〉 + |01〉 - |10〉 - |11〉) / 2 + // 2 - (-|00〉 - |01〉 + |10〉 - |11〉) / 2 + // 3 - (-|00〉 - |01〉 - |10〉 + |11〉) / 2 + operation StatePrep_TwoQubitStatePartTwo (qs : Qubit[], state : Int) : () { + body { + // start with state prep of basis vectors + StatePrep_BasisStateMeasurement(qs, state); + // now apply all gates for unitary in reference impl (in reverse + adjoint) + ApplyToEach(X, qs); + (Controlled Z)([qs[0]], qs[1]); + ApplyToEach(X, qs); + ApplyToEach(H, qs); + ApplyToEach(X, qs); + (Controlled Z)([qs[0]], qs[1]); + ApplyToEach(X, qs); + SWAP(qs[0], qs[1]); + } + } + + operation T110_TwoQubitState_Test () : () { + body { + DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitState, TwoQubitState); + } + } + + operation T111_TwoQubitStatePartTwo_Test () : () { + body { + DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitStatePartTwo, TwoQubitStatePartTwo); + } + } + + ////////////////////////////////////////////////////////////////// + + operation StatePrep_IsQubitZeroOrPlus (q : Qubit, state : Int) : () { + body { + if (state == 0) { + // convert |0〉 to |0〉 + } else { + // convert |0〉 to |+〉 + H(q); + } + } + } + + + // "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits + // with Int return. Framework tests against a threshold parameter for the fraction of runs that must succeed. + operation DistinguishStates_MultiQubit_Threshold ( + Nqubit : Int, + Nstate : Int, + threshold : Double, + statePrep : ((Qubit, Int) => ()), + testImpl : (Qubit => Bool) + ) : () + { + body + { + let nTotal = 1000; + mutable nOk = 0; + using (qs = Qubit[Nqubit]) + { + for (i in 1..nTotal) + { + // get a random integer to define the state of the qubits + let state = RandomInt(Nstate); + + // do state prep: convert |0〉 to outcome with return equal to state + statePrep(qs[0], state); + // get the solution's answer and verify that it's a match + let ans = testImpl(qs[0]); + if (ans == (state == 0)) { + set nOk = nOk + 1; + } + + // we're not checking the state of the qubit after the operation + ResetAll(qs); + } + } + if (ToDouble(nOk) < threshold * ToDouble(nTotal)) { + fail $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state which does not meet the required threshold of at least {threshold*100}%."; + } + } + } + + + // "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits + // with Int return. Framework tests against a threshold parameter for the fraction of runs that must succeed. + // Framework tests in the USD scenario, i.e., it is allowed to respond "inconclusive" (with some probability) + // up to given threshold, but it is never allowed to err if an actual conclusive response is given. + operation USD_DistinguishStates_MultiQubit_Threshold ( + Nqubit : Int, + Nstate : Int, + thresholdInconcl : Double, + thresholdConcl : Double, + statePrep : ((Qubit, Int) => ()), + testImpl : (Qubit => Int) + ) : () + { + body + { + let nTotal = 10000; + mutable nInconc = 0; // counts total inconclusive answers + mutable nConclOne = 0; // counts total conclusive |0〉 state identifications + mutable nConclPlus = 0; // counts total conclusive |+> state identifications + + using (qs = Qubit[Nqubit]) + { + for (i in 1..nTotal) + { + // get a random integer to define the state of the qubits + let state = RandomInt(Nstate); + // do state prep: convert |0〉 to outcome with return equal to state + statePrep(qs[0], state); + // get the solution's answer and verify that it's a match + let ans = testImpl(qs[0]); + // check that the answer is actually in allowed range + if (ans < -1 || ans > 1) { + fail $"state {state} led to invalid response {ans}."; + } + // keep track of the number of inconclusive answers given + if (ans == -1) { + set nInconc = nInconc + 1; + } + if (ans == 0 && state == 0) { + set nConclOne = nConclOne + 1; + } + if (ans == 1 && state == 1) { + set nConclPlus = nConclPlus + 1; + } + // check if upon conclusive result the answer is actually correct + if ((ans == 0) && (state == 1) || (ans == 1) && (state == 0)) { + fail $"state {state} led to incorrect conclusive response {ans}."; + } + // we're not checking the state of the qubit after the operation + ResetAll(qs); + } + } + if (ToDouble(nInconc) > thresholdInconcl * ToDouble(nTotal)) { + fail $"{nInconc} test runs out of {nTotal} returned inconclusive which does not meet the required threshold of at most {thresholdInconcl*100}%."; + } + if (ToDouble(nConclOne) < thresholdConcl * ToDouble(nTotal)) { + fail $"Only {nConclOne} test runs out of {nTotal} returned conclusive |0〉 which does not meet the required threshold of at least {thresholdConcl*100}%."; + } + if (ToDouble(nConclPlus) < thresholdConcl * ToDouble(nTotal)) { + fail $"Only {nConclPlus} test runs out of {nTotal} returned conclusive |+> which does not meet the required threshold of at least {thresholdConcl*100}%."; + } + } + } + + operation T201_IsQubitZeroOrPlus_Test () : () { + body { + DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, StatePrep_IsQubitZeroOrPlus, IsQubitPlusOrZero); + } + } + + operation T202_IsQubitZeroOrPlusSimpleUSD_Test () : () { + body { + USD_DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, 0.1, StatePrep_IsQubitZeroOrPlus, IsQubitPlusZeroOrInconclusiveSimpleUSD); + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 72f1506a93b..f9f06738dd2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,105 @@ +# Introduction + +The Quantum Katas are a series of self-paced tutorials aimed at teaching you elements of quantum computing and Q# programming at the same time. + +Each kata covers one topic. +Currently covered topics are: + +* **[Basic quantum computing gates](./BasicGates/)**. + Tasks which focus on main single-qubit and multi-qubit gates used in quantum computing. +* **[Superposition](./Superposition/)**. + Tasks which focus on preparing a certain superposition state on one or multiple qubits. +* **[Measurements](./Measurements/)**. + Tasks which focus on distinguishing quantum states using measurements. +* **[Deutsch–Jozsa algorithm](./DeutschJozsaAlgorithm/)**. + Tasks which focus on writing quantum oracles which implement classical functions, and the Bernstein–Vazirani and Deutsch–Jozsa algorithms. + +Each kata is a separate project which includes: + +* A sequence of tasks on the topic progressing from trivial to challenging. + Each task requires you to fill in some code; the first task might require just one line, and the last one might require a sizable fragment of code. +* A testing framework that sets up, runs and validates your solutions. + Each task is covered by a [*unit test*](https://docs.microsoft.com/en-us/visualstudio/test/getting-started-with-unit-testing) which initially fails; once the test passes, you can move on to the next task! +* Pointers to reference materials you might need to solve the tasks, both on quantum computing and on Q#. +* Reference solutions, for when all else fails. + +# Installing and Getting Started # + +To get started with the Quantum Katas, you'll first need to install the [Quantum Development Kit](https://docs.microsoft.com/quantum), available for Windows 10, macOS, and for Linux. +Please see the [install guide for the Quantum Development Kit](https://docs.microsoft.com/en-us/quantum/quantum-installconfig) if you do not already have the Quantum Development Kit installed. + +A quick reference sheet for Q# programming language is available [here](./quickref/qsharp-quick-reference.pdf). + +### Downloading the Quantum Katas ### + +If you have Git installed, go on and clone the Microsoft/QuantumKatas repository. +From your favorite command line: + +```bash +$ git clone https://github.com/Microsoft/QuantumKatas.git +``` + +> **TIP**: Both Visual Studio 2017 and Visual Studio Code make it easy to clone repositories from within your development environment. +> See the [Visual Studio 2017](https://docs.microsoft.com/en-us/vsts/git/tutorial/clone?view=vsts&tabs=visual-studio#clone-from-another-git-provider) and [Visual Studio Code](https://code.visualstudio.com/docs/editor/versioncontrol#_cloning-a-repository) documentation for details. + +Alternatively, if you don't have Git installed, you can manually download a standalone copy of the katas from https://github.com/Microsoft/QuantumKatas/archive/master.zip. + +### Opening a Tutorial ### + +Each individual kata is placed in its own directory as a self-contained Q# solution and project pair. +For instance, the **BasicGates** kata is laid out as below. + +``` +QuantumKatas/ + BasicGates/ + README.md # Instructions specific to this kata. + .vscode/ # Metadata used by Visual Studio Code. + BasicGates.sln # Visual Studio 2017 solution file. + BasicGates.csproj # Project file used to build both classical and quantum code. + + Tasks.qs # Q# source code that you will fill as you solve each task. + Tests.qs # Q# tests that verify your solutions. + TestSuiteRunner.cs # C# source code used to run the Q# tests. + ReferenceImplementation.qs # Q# source code containing solutions to the tasks. +``` + +To open the **BasicGates** kata in Visual Studio 2017, open the `QuantumKatas/BasicGates.sln` solution file. + +To open the **BasicGates** kata in Visual Studio Code, open the `QuantumKatas/BasicGates/` folder. +Press Ctrl + Shift + P / ⌘ + Shift + P to open the Command Palette and type "Open Folder" on Windows 10 or Linux or "Open" on macOS. + +> **TIP**: Almost all commands available in Visual Studio Code can be found in the Command Palette. +> If you ever get stuck, press Ctrl + Shfit + P / ⌘ + Shift + P and type some letters to search through all available commands. + +> **TIP**: You can also launch Visual Studio Code from the command line if you prefer: +> ```bash +> $ code QuantumKatas/BasicGates/ +> ``` + +### Running Kata Tests ### + +Once you have a kata open, it's time to run the tests using the instructions below. +Initially all tests will fail; do not panic! +Open the `Tasks.qs` file and start filling in the code to complete the tasks. Each task is covered by a unit test; once you fill in the correct code for a task, rebuild the project and re-run the tests, and the corresponding unit test will pass. + +#### Visual Studio 2017 + +1. Build solution. +2. Open Test Explorer (found in `Test` > `Windows` menu) and select "Run All" to run all unit tests at once. +3. Work on the tasks in the `Tasks.qs` file. +4. To test your code changes for a task, rebuild solution and re-run all unit tests using "Run All" or the unit test which covers that task by right-clicking on that test and selecting "Run Selected Tests". + +#### Visual Studio Code + +1. Press Ctrl + \` / ⌘ + \` to open the integrated terminal. + The terminal should already start in the kata directory, but if not, use `cd` to navigate to the folder containing the `*.csproj` file for the kata. +2. Run `dotnet test` in the integrated terminal. + This should automatically build the kata project and run all unit tests; initially, all unit tests should fail. +3. Work on the tasks in the `Tasks.qs` file. +4. To test your code changes for a task, run `dotnet test` again. + +For convenience, we also provide a `tasks.json` configuration for each kata that allows Visual Studio Code to run the build and test steps from the Command Palette. +Press Ctrl + Shift + P / ⌘ + Shift + P to open the Palette and type "Run Build Task" or "Run Test Task," then press Enter. # Contributing diff --git a/Superposition/.vscode/extensions.json b/Superposition/.vscode/extensions.json new file mode 100644 index 00000000000..14d152e069a --- /dev/null +++ b/Superposition/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "quantum.quantum-devkit-vscode" + ] +} \ No newline at end of file diff --git a/Superposition/.vscode/tasks.json b/Superposition/.vscode/tasks.json new file mode 100644 index 00000000000..bae788317e9 --- /dev/null +++ b/Superposition/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "args": [ + "build" + ], + "type": "process", + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "test", + "command": "dotnet", + "args": [ + "test" + ], + "type": "process", + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Superposition/README.md b/Superposition/README.md new file mode 100644 index 00000000000..c2e8821b15b --- /dev/null +++ b/Superposition/README.md @@ -0,0 +1,12 @@ +# Welcome! + +The superposition kata covers the following topics: + - basic single-qubit and multi-qubit gates + - superposition + - flow control and recursion in Q# + +It is recommended to complete the basic gates kata before this one to get familiar with the basic gates used in quantum computing. The list of basic gates available in Q# can be found at [Microsoft.Quantum.Primitive](https://docs.microsoft.com/en-us/qsharp/api/prelude/microsoft.quantum.primitive). + +You can find detailed coverage of Bell states and their creation [here](https://blogs.msdn.microsoft.com/uk_faculty_connection/2018/02/06/a-beginners-guide-to-quantum-computing-and-q/). + +For the syntax of flow control statements in Q#, see [the Q# documentation](https://docs.microsoft.com/en-us/quantum/quantum-qr-statements#control-flow). diff --git a/Superposition/ReferenceImplementation.qs b/Superposition/ReferenceImplementation.qs new file mode 100644 index 00000000000..f6435225ff5 --- /dev/null +++ b/Superposition/ReferenceImplementation.qs @@ -0,0 +1,320 @@ +// 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.Superposition +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Convert; + open Microsoft.Quantum.Extensions.Math; + + // Task 1. Plus state + // Input: a qubit in |0〉 state (stored in an array of length 1). + // Goal: create a |+〉 state on this qubit (|+〉 = (|0〉 + |1〉) / sqrt(2)). + operation PlusState_Reference (qs : Qubit[]) : () + { + body + { + H(qs[0]); + } + adjoint auto; + } + + // Task 2. Minus state + // Input: a qubit in |0〉 state (stored in an array of length 1). + // Goal: create a |-〉 state on this qubit (|-〉 = (|0〉 - |1〉) / sqrt(2)). + operation MinusState_Reference (qs : Qubit[]) : () + { + body + { + X(qs[0]); + H(qs[0]); + } + adjoint auto; + } + + // Task 3. Unequal superposition + // Inputs: + // 1) a qubit in |0〉 state (stored in an array of length 1). + // 2) angle alpha, in radians, represented as Double + // Goal: create a cos(alpha) * |0〉 + sin(alpha) * |1〉 state on this qubit. + operation UnequalSuperposition_Reference (qs : Qubit[], alpha : Double) : () + { + body + { + // Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive + Ry(2.0 * alpha, qs[0]); + } + adjoint auto; + } + + // Task 4. Bell state + // Input: two qubits in |00〉 state (stored in an array of length 2). + // Goal: create a Bell state |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2) on these qubits. + operation BellState_Reference (qs : Qubit[]) : () + { + body + { + H(qs[0]); + CNOT(qs[0], qs[1]); + } + adjoint auto; + } + + // Task 5. All Bell states + // Inputs: + // 1) two qubits in |00〉 state (stored in an array of length 2) + // 2) an integer index + // Goal: create one of the Bell states based on the value of index: + // 0: |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2) + // 1: |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2) + // 2: |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2) + // 3: |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2) + operation AllBellStates_Reference (qs : Qubit[], index : Int) : () + { + body + { + H(qs[0]); + CNOT(qs[0], qs[1]); + // now we have |00〉 + |11〉 - modify it based on index arg + if (index % 2 == 1) { + // negative phase + Z(qs[1]); + } + if (index / 2 == 1) { + X(qs[1]); + } + } + adjoint auto; + } + + // Task 6. Greenberger–Horne–Zeilinger state + // Input: N qubits in |0...0〉 state. + // Goal: create a GHZ state (|0...0〉 + |1...1〉) / sqrt(2) on these qubits. + operation GHZ_State_Reference (qs : Qubit[]) : () + { + body + { + H(qs[0]); + for (i in 1 .. Length(qs)-1) { + CNOT(qs[0], qs[i]); + } + } + adjoint auto; + } + + // Task 7. Superposition of all basis vectors + // Input: N qubits in |0...0〉 state. + // Goal: create an equal superposition of all basis vectors from |0...0〉 to |1...1〉 + // (i.e. state (|0...0〉 + ... + |1...1〉) / sqrt(2^N) ). + operation AllBasisVectorsSuperposition_Reference (qs : Qubit[]) : () + { + body + { + for (i in 0 .. Length(qs)-1) { + H(qs[i]); + } + } + adjoint auto; + } + + // Task 8. Superposition of |0...0〉 and given bit string + // Inputs: + // 1) N qubits in |0...0〉 state + // 2) bit string represented as Bool[] + // Goal: create an equal superposition of |0...0〉 and basis state given by the second bit string. + // Bit values false and true correspond to |0〉 and |1〉 states. + // You are guaranteed that the qubit array and the bit string have the same length. + // You are guaranteed that the first bit of the bit string is true. + // Example: for bit string = [true; false] the qubit state required is (|00〉 + |10〉) / sqrt(2). + operation ZeroAndBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[]) : () + { + body + { + AssertIntEqual(Length(bits), Length(qs), "Arrays should have the same length"); + AssertBoolEqual(bits[0], true, "First bit of the input bit string should be set to true"); + + // Hadamard first qubit + H(qs[0]); + + // iterate through the bit string and CNOT to qubits corresponding to true bits + for (i in 1..Length(qs)-1) { + if (bits[i]) { + CNOT(qs[0], qs[i]); + } + } + } + adjoint auto; + } + + // Task 9. Superposition of two bit strings + // Inputs: + // 1) N qubits in |0...0〉 state + // 2) two bit string represented as Bool[]s + // Goal: create an equal superposition of two basis states given by the bit strings. + // Bit values false and true correspond to |0〉 and |1〉 states. + // Example: for bit strings [false; true; false] and [false; false; true] + // the qubit state required is (|010〉 + |001〉) / sqrt(2). + // You are guaranteed that the two bit strings will be different. + + // helper function for TwoBitstringSuperposition_Reference + function FindFirstDiff_Reference (bits1 : Bool[], bits2 : Bool[]) : Int + { + mutable firstDiff = -1; + for (i in 0 .. Length(bits1)-1) { + if (bits1[i] != bits2[i] && firstDiff == -1) { + set firstDiff = i; + } + } + return firstDiff; + } + + operation TwoBitstringSuperposition_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : () + { + body + { + // find the index of the first bit at which the bit strings are different + let firstDiff = FindFirstDiff_Reference(bits1, bits2); + + // Hadamard corresponding qubit to create superposition + H(qs[firstDiff]); + + // iterate through the bit strings again setting the final state of qubits + for (i in 0 .. Length(qs)-1) { + if (bits1[i] == bits2[i]) { + // if two bits are the same apply X or nothing + if (bits1[i]) { + X(qs[i]); + } + } else { + // if two bits are different, set their difference using CNOT + if (i > firstDiff) { + CNOT(qs[firstDiff], qs[i]); + if (bits1[i] != bits1[firstDiff]) { + X(qs[i]); + } + } + } + } + } + adjoint auto; + } + + // Task 10. W state on 2^k qubits + // Input: N = 2^k qubits in |0...0〉 state. + // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. + // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. + // Example: for N = 4, W state is (|1000〉 + |0100〉 + |0010〉 + |0001〉) / 2. + operation WState_PowerOfTwo_Reference (qs : Qubit[]) : () + { + body + { + let N = Length(qs); + if (N == 1) { + // base of recursion: |1〉 + X(qs[0]); + } else { + let K = N / 2; + // create W state on the first K qubits + WState_PowerOfTwo_Reference(qs[0..K-1]); + + // the next K qubits are in |0...0〉 state + // allocate ancilla in |+〉 state + using (anc = Qubit[1]) { + H(anc[0]); + for (i in 0..K-1) { + (Controlled SWAP)(anc, (qs[i], qs[i+K])); + } + for (i in K..N-1) { + CNOT(qs[i], anc[0]); + } + } + } + } + adjoint auto; + } + + // Task 11. W state on arbitrary number of qubits + // Input: N qubits in |0...0〉 state (N is not necessarily a power of 2). + // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. + // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. + // Example: for N = 3, W state is (|100〉 + |010〉 + |001〉) / sqrt(3). + + // general solution based on rotations and recursive application of controlled generation routine + operation WState_Arbitrary_Reference (qs : Qubit[]) : () + { + body + { + let N = Length(qs); + if (N == 1) { + // base case of recursion: |1〉 + X(qs[0]); + } else { + // |W_N> = |0〉|W_(N-1)> + |1〉|0...0〉 + // do a rotation on the first qubit to split it into |0〉 and |1〉 with proper weights + // |0〉 -> sqrt((N-1)/N) |0〉 + 1/sqrt(N) |1〉 + let theta = ArcSin(1.0 / Sqrt(ToDouble(N))); + Ry(2.0 * theta, qs[0]); + // do a zero-controlled W-state generation for qubits 1..N-1 + X(qs[0]); + (Controlled WState_Arbitrary_Reference)(qs[0..0], qs[1..N-1]); + X(qs[0]); + } + } + adjoint auto; + controlled auto; + adjoint controlled auto; + } + + // solution based on generation for 2^k and post-selection using measurements + operation WState_Arbitrary_Postselect (qs : Qubit[]) : () + { + body + { + let N = Length(qs); + if (N == 1) { + // base case of recursion: |1〉 + X(qs[0]); + } else { + // find the smallest power of 2 which is greater than or equal to N + // as a hack, we know we're not doing it on more than 64 qubits + mutable P = 1; + for (i in 1..6) { + if (P < N) { + set P = P * 2; + } + } + + if (P == N) { + // prepare as a power of 2 (previous task) + WState_PowerOfTwo_Reference(qs); + } else { + // allocate extra qubits + using (ans = Qubit[P-N]) { + let all_qubits = qs + ans; + repeat { + // prepare state W_P on original + ancilla qubits + WState_PowerOfTwo_Reference(all_qubits); + + // measure ancilla qubits; if all of the results are Zero, we get the right state on main qubits + mutable allZeros = true; + for (i in 0..P-N-1) { + set allZeros = allZeros && IsResultZero(M(ans[i])); + } + } until allZeros + fixup { + ResetAll(ans); + } + } + } + } + } + } +} diff --git a/Superposition/Superposition.csproj b/Superposition/Superposition.csproj new file mode 100644 index 00000000000..64a5599bc0e --- /dev/null +++ b/Superposition/Superposition.csproj @@ -0,0 +1,22 @@ + + + netcoreapp2.0 + x64 + false + Quantum.Kata.Superposition + + + + + + + + + + + + + + + + diff --git a/Superposition/Superposition.sln b/Superposition/Superposition.sln new file mode 100644 index 00000000000..ec25fd63f70 --- /dev/null +++ b/Superposition/Superposition.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Superposition", "Superposition.csproj", "{1FD1C660-6646-44B0-B252-32378CBF682B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1FD1C660-6646-44B0-B252-32378CBF682B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FD1C660-6646-44B0-B252-32378CBF682B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FD1C660-6646-44B0-B252-32378CBF682B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FD1C660-6646-44B0-B252-32378CBF682B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0DE89BFF-6EB5-4848-AB40-A01C1CD406FB} + EndGlobalSection +EndGlobal diff --git a/Superposition/Tasks.qs b/Superposition/Tasks.qs new file mode 100644 index 00000000000..6a0e0e5ab06 --- /dev/null +++ b/Superposition/Tasks.qs @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Quantum.Kata.Superposition +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Convert; + open Microsoft.Quantum.Extensions.Math; + + ////////////////////////////////////////////////////////////////// + // Welcome! + ////////////////////////////////////////////////////////////////// + + // "Superposition" quantum kata is a series of exercises designed + // to get you familiar with programming in Q#. + // It covers the following topics: + // - basic single-qubit and multi-qubit gates, + // - superposition, + // - flow control and recursion in Q#. + // + // 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. + + // Task 1. Plus state + // Input: a qubit in |0⟩ state (stored in an array of length 1). + // Goal: create a |+⟩ state on this qubit (|+⟩ = (|0⟩ + |1⟩) / sqrt(2)). + operation PlusState (qs : Qubit[]) : () + { + body + { + // Hadamard gate H will convert |0⟩ state to |+⟩ state. + // The first qubit of the array can be accessed as qs[0]. + // Type H(qs[0]); + // Then rebuild the project and rerun the tests - T01_PlusState_Test should now pass! + + // ... + } + } + + // Task 2. Minus state + // Input: a qubit in |0⟩ state (stored in an array of length 1). + // Goal: create a |-⟩ state on this qubit (|-⟩ = (|0⟩ - |1⟩) / sqrt(2)). + operation MinusState (qs : Qubit[]) : () + { + body + { + // In this task, as well as in all subsequent ones, you have to come up with the solution yourself. + + // ... + } + } + + // Task 3*. Unequal superposition + // Inputs: + // 1) a qubit in |0⟩ state (stored in an array of length 1). + // 2) angle alpha, in radians, represented as Double + // Goal: create a cos(alpha) * |0⟩ + sin(alpha) * |1⟩ state on this qubit. + operation UnequalSuperposition (qs : Qubit[], alpha : Double) : () + { + body + { + // Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive namespace. + // Note that all rotation operators rotate the state by _half_ of its angle argument. + + // ... + } + } + + // Task 4. Bell state + // Input: two qubits in |00⟩ state (stored in an array of length 2). + // Goal: create a Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) on these qubits. + operation BellState (qs : Qubit[]) : () + { + body + { + // ... + } + } + + // Task 5. All Bell states + // Inputs: + // 1) two qubits in |00〉 state (stored in an array of length 2) + // 2) an integer index + // Goal: create one of the Bell states based on the value of index: + // 0: |Φ⁺〉 = (|00〉 + |11〉) / sqrt(2) + // 1: |Φ⁻〉 = (|00〉 - |11〉) / sqrt(2) + // 2: |Ψ⁺〉 = (|01〉 + |10〉) / sqrt(2) + // 3: |Ψ⁻〉 = (|01〉 - |10〉) / sqrt(2) + operation AllBellStates (qs : Qubit[], index : Int) : () + { + body + { + // ... + } + } + + // Task 6. Greenberger–Horne–Zeilinger state + // Input: N qubits in |0...0⟩ state. + // Goal: create a GHZ state (|0...0⟩ + |1...1⟩) / sqrt(2) on these qubits. + operation GHZ_State (qs : Qubit[]) : () + { + body + { + // Hint: N can be found as Length(qs). + + // ... + } + } + + // Task 7. Superposition of all basis vectors + // Input: N qubits in |0...0⟩ state. + // Goal: create an equal superposition of all basis vectors from |0...0⟩ to |1...1⟩ + // (i.e. state (|0...0⟩ + ... + |1...1⟩) / sqrt(2^N) ). + operation AllBasisVectorsSuperposition (qs : Qubit[]) : () + { + body + { + // ... + } + } + + // Task 8. Superposition of |0...0⟩ and given bit string + // Inputs: + // 1) N qubits in |0...0⟩ state + // 2) bit string represented as Bool[] + // Goal: create an equal superposition of |0...0⟩ and basis state given by the bit string. + // Bit values false and true correspond to |0⟩ and |1⟩ states. + // You are guaranteed that the qubit array and the bit string have the same length. + // You are guaranteed that the first bit of the bit string is true. + // Example: for bit string = [true; false] the qubit state required is (|00⟩ + |10⟩) / sqrt(2). + operation ZeroAndBitstringSuperposition (qs : Qubit[], bits : Bool[]) : () + { + body + { + // The following lines enforce the constraints on the input that you are given. + // You don't need to modify them. Feel free to remove them, this won't cause your code to fail. + AssertIntEqual(Length(bits), Length(qs), "Arrays should have the same length"); + AssertBoolEqual(bits[0], true, "First bit of the input bit string should be set to true"); + + // ... + } + } + + // Task 9. Superposition of two bit strings + // Inputs: + // 1) N qubits in |0...0⟩ state + // 2) two bit string represented as Bool[]s + // Goal: create an equal superposition of two basis states given by the bit strings. + // Bit values false and true correspond to |0⟩ and |1⟩ states. + // Example: for bit strings [false; true; false] and [false; false; true] + // the qubit state required is (|010⟩ + |001⟩) / sqrt(2). + // You are guaranteed that the both bit strings have the same length as the qubit array, + // and that the bit strings will differ in at least one bit. + operation TwoBitstringSuperposition (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : () + { + body + { + // ... + } + } + + // Task 10**. W state on 2^k qubits + // Input: N = 2^k qubits in |0...0⟩ state. + // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. + // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. + // Example: for N = 4, W state is (|1000⟩ + |0100⟩ + |0010⟩ + |0001⟩) / 2. + operation WState_PowerOfTwo (qs : Qubit[]) : () + { + body + { + // Hint: you can use Controlled functor to perform arbitrary controlled gates. + + // ... + } + } + + // Task 11**. W state on arbitrary number of qubits + // Input: N qubits in |0...0⟩ state (N is not necessarily a power of 2). + // Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits. + // W state is an equal superposition of all basis states on N qubits of Hamming weight 1. + // Example: for N = 3, W state is (|100⟩ + |010⟩ + |001⟩) / sqrt(3). + operation WState_Arbitrary (qs : Qubit[]) : () + { + body + { + // ... + } + } +} diff --git a/Superposition/TestSuiteRunner.cs b/Superposition/TestSuiteRunner.cs new file mode 100644 index 00000000000..e31251f2cb9 --- /dev/null +++ b/Superposition/TestSuiteRunner.cs @@ -0,0 +1,42 @@ +// 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 Microsoft.Quantum.Simulation.Simulators; +using Xunit.Abstractions; +using System.Diagnostics; + +namespace Quantum.Kata.Superposition +{ + 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.Superposition. + /// + [OperationDriver(TestNamespace = "Quantum.Kata.Superposition")] + public void TestTarget(TestOperation op) + { + using (var sim = new QuantumSimulator()) + { + // OnLog defines action(s) performed when Q# test calls function Message + sim.OnLog += (msg) => { output.WriteLine(msg); }; + sim.OnLog += (msg) => { Debug.WriteLine(msg); }; + op.TestOperationRunner(sim); + } + } + } +} diff --git a/Superposition/Tests.qs b/Superposition/Tests.qs new file mode 100644 index 00000000000..38b505e319b --- /dev/null +++ b/Superposition/Tests.qs @@ -0,0 +1,240 @@ +// 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.Superposition +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Extensions.Convert; + open Microsoft.Quantum.Extensions.Math; + open Microsoft.Quantum.Extensions.Testing; + + // ------------------------------------------------------ + operation AssertEqualOnZeroState (N : Int, testImpl : (Qubit[] => ()), refImpl : (Qubit[] => () : Adjoint)) : () + { + body + { + using (qs = Qubit[N]) + { + // apply operation that needs to be tested + testImpl(qs); + + // apply adjoint reference operation and check that the result is |0〉 + (Adjoint refImpl)(qs); + + // assert that all qubits end up in |0〉 state + AssertAllZero(qs); + } + } + } + + // ------------------------------------------------------ + operation T01_PlusState_Test () : () + { + body + { + AssertEqualOnZeroState(1, PlusState, PlusState_Reference); + } + } + + // ------------------------------------------------------ + operation T02_MinusState_Test () : () + { + body + { + AssertEqualOnZeroState(1, MinusState, MinusState_Reference); + } + } + + // ------------------------------------------------------ + operation T03_UnequalSuperposition_Test () : () + { + body + { + // cross-test + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.0), ApplyToEachA(I, _)); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.5 * PI()), ApplyToEachA(X, _)); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.5 * PI()), ApplyToEachA(Y, _)); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.25 * PI()), PlusState_Reference); + AssertEqualOnZeroState(1, UnequalSuperposition(_, 0.75 * PI()), MinusState_Reference); + + for (i in 1..36) { + let alpha = 2.0 * PI() * ToDouble(i) / 36.0; + AssertEqualOnZeroState(1, UnequalSuperposition(_, alpha), UnequalSuperposition_Reference(_, alpha)); + } + } + } + + // ------------------------------------------------------ + operation T04_BellState_Test () : () + { + body + { + AssertEqualOnZeroState(2, BellState, BellState_Reference); + } + } + + // ------------------------------------------------------ + operation T05_AllBellStates_Test () : () + { + body + { + for (i in 0..3) { + AssertEqualOnZeroState(2, AllBellStates(_, i), AllBellStates_Reference(_, i)); + } + } + } + + // ------------------------------------------------------ + operation T06_GHZ_State_Test () : () + { + body + { + // for N = 1 it's just |+〉 + AssertEqualOnZeroState(1, GHZ_State, PlusState_Reference); + // for N = 2 it's Bell state + AssertEqualOnZeroState(2, GHZ_State, BellState_Reference); + for (n in 3..9) { + AssertEqualOnZeroState(n, GHZ_State, GHZ_State_Reference); + } + } + } + + // ------------------------------------------------------ + operation T07_AllBasisVectorsSuperposition_Test () : () + { + body + { + // for N = 1 it's just |+〉 + AssertEqualOnZeroState(1, AllBasisVectorsSuperposition, PlusState_Reference); + + for (n in 2..9) { + AssertEqualOnZeroState(n, AllBasisVectorsSuperposition, AllBasisVectorsSuperposition_Reference); + } + } + } + + // ------------------------------------------------------ + operation T08_ZeroAndBitstringSuperposition_Test () : () + { + body + { + // compare with results of previous operations + AssertEqualOnZeroState(1, ZeroAndBitstringSuperposition(_, [true]), PlusState_Reference); + AssertEqualOnZeroState(2, ZeroAndBitstringSuperposition(_, [true; true]), BellState_Reference); + AssertEqualOnZeroState(3, ZeroAndBitstringSuperposition(_, [true; true; true]), GHZ_State_Reference); + + if (true) { + let b = [true; false]; + AssertEqualOnZeroState(2, ZeroAndBitstringSuperposition(_, b), + ZeroAndBitstringSuperposition_Reference(_, b)); + } + if (true) { + let b = [true; false; true]; + AssertEqualOnZeroState(3, ZeroAndBitstringSuperposition(_, b), + ZeroAndBitstringSuperposition_Reference(_, b)); + } + if (true) { + let b = [true; false; true; true; false; false]; + AssertEqualOnZeroState(6, ZeroAndBitstringSuperposition(_, b), + ZeroAndBitstringSuperposition_Reference(_, b)); + } + } + } + + // ------------------------------------------------------ + operation T09_TwoBitstringSuperposition_Test () : () + { + body + { + // compare with results of previous operations + AssertEqualOnZeroState(1, TwoBitstringSuperposition(_, [true], [false]), PlusState_Reference); + AssertEqualOnZeroState(2, TwoBitstringSuperposition(_, [false; false], [true; true]), BellState_Reference); + AssertEqualOnZeroState(3, TwoBitstringSuperposition(_, [true; true; true], [false; false; false]), GHZ_State_Reference); + + // compare with reference implementation + // diff in first position + for (i in 1..1) + { + let b1 = [false; true]; + let b2 = [true; false]; + AssertEqualOnZeroState(2, TwoBitstringSuperposition(_, b1, b2), + TwoBitstringSuperposition_Reference(_, b1, b2)); + } + for (i in 1..1) + { + let b1 = [true; true; false]; + let b2 = [false; true; true]; + AssertEqualOnZeroState(3, TwoBitstringSuperposition(_, b1, b2), + TwoBitstringSuperposition_Reference(_, b1, b2)); + } + // diff in last position + for (i in 1..1) + { + let b1 = [false; true; true; false]; + let b2 = [false; true; true; true]; + AssertEqualOnZeroState(4, TwoBitstringSuperposition(_, b1, b2), + TwoBitstringSuperposition_Reference(_, b1, b2)); + } + // diff in the middle + for (i in 1..1) + { + let b1 = [true; false; false; false]; + let b2 = [true; false; true; true]; + AssertEqualOnZeroState(4, TwoBitstringSuperposition(_, b1, b2), + TwoBitstringSuperposition_Reference(_, b1, b2)); + } + } + } + + // ------------------------------------------------------ + operation T10_WState_PowerOfTwo_Test () : () + { + body + { + // separate check for N = 1 (return must be |1〉) + using (qs = Qubit[1]) { + WState_PowerOfTwo(qs); + Assert([PauliZ], qs, One, ""); + X(qs[0]); + } + + AssertEqualOnZeroState(2, WState_PowerOfTwo, TwoBitstringSuperposition_Reference(_, [false; true], [true; false])); + AssertEqualOnZeroState(4, WState_PowerOfTwo, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(8, WState_PowerOfTwo, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(16, WState_PowerOfTwo, WState_PowerOfTwo_Reference); + } + } + + // ------------------------------------------------------ + operation T11_WState_Arbitrary_Test () : () + { + body + { + // separate check for N = 1 (return must be |1〉) + using (qs = Qubit[1]) { + WState_Arbitrary_Reference(qs); + Assert([PauliZ], qs, One, ""); + X(qs[0]); + } + + // cross-tests + AssertEqualOnZeroState(2, WState_Arbitrary, TwoBitstringSuperposition_Reference(_, [false; true], [true; false])); + + AssertEqualOnZeroState(2, WState_Arbitrary, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(4, WState_Arbitrary, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(8, WState_Arbitrary, WState_PowerOfTwo_Reference); + AssertEqualOnZeroState(16, WState_Arbitrary, WState_PowerOfTwo_Reference); + + for (i in 2..16) { + AssertEqualOnZeroState(i, WState_Arbitrary, WState_Arbitrary_Reference); + } + } + } +} \ No newline at end of file diff --git a/quickref/qsharp-quick-reference.pdf b/quickref/qsharp-quick-reference.pdf new file mode 100644 index 00000000000..6e06b8a1d0d Binary files /dev/null and b/quickref/qsharp-quick-reference.pdf differ diff --git a/quickref/qsharp-quick-reference.tex b/quickref/qsharp-quick-reference.tex new file mode 100644 index 00000000000..e0afa775907 --- /dev/null +++ b/quickref/qsharp-quick-reference.tex @@ -0,0 +1,262 @@ +% Copyright (c) Microsoft Corporation. All rights reserved. +% Licensed under the MIT License. + +%!TEX program=xelatex +% This cheatsheet is based on the template +% provided at https://gist.github.com/alexander-yakushev/c773543bf9a957749f79. +\documentclass[10pt,english,landscape]{article} +\usepackage{multicol} +\usepackage{calc} +\usepackage[landscape]{geometry} +\usepackage{color,graphicx,overpic} + +% https://tex.stackexchange.com/a/192067 +\usepackage{fontawesome} + \newfontfamily{\FA}{[FontAwesome.otf]} + +% Known issue in {fontspec}: https://github.com/wspr/fontspec/issues/312#issuecomment-342125206 +% Since {fontspec} is included only when compiling with XeTeX, we guard +% our fix accordingly. +\usepackage{ifxetex} + \ifxetex + \let\latinencoding\relax + \usepackage{fontspec} + \setmainfont{Segoe UI} + \setmonofont{Consolas} + \fi + +% \usepackage[T1]{fontenc} +% \usepackage[bitstream-charter]{mathdesign} +\usepackage[utf8]{inputenc} +\usepackage{url} +\usepackage{amsfonts} +\usepackage{array,booktabs} +\usepackage{textcomp} +\usepackage[usenames,dvipsnames,table]{xcolor} +\usepackage[most]{tcolorbox} +\usepackage{menukeys} +\usepackage{tabularx} +\usepackage{multirow} +\usepackage{colortbl} +\usepackage{tikz} +\usepackage{environ} +\usepackage{braket} + +\usetikzlibrary{calc} +\pgfdeclarelayer{background} +\pgfdeclarelayer{foreground} +\pgfsetlayers{background,main,foreground} + +\geometry{top=-0.5cm,left=1cm,right=1cm,bottom=1cm} + +\pagestyle{empty} % Turn off header and footer + +% \renewcommand\rmdefault{phv} % Arial +% \renewcommand\sfdefault{phv} % Arial + +% Redefine section commands to use less space +\makeatletter +\renewcommand{\section}{\@startsection{section}{1}{0mm}% + {-1ex plus -.5ex minus -.2ex}% + {0.5ex plus .2ex}%x + {\normalfont\large\bfseries}} +\renewcommand{\subsection}{\@startsection{subsection}{2}{0mm}% + {-1explus -.5ex minus -.2ex}% + {0.5ex plus .2ex}% + {\normalfont\normalsize\bfseries}} +\renewcommand{\subsubsection}{\@startsection{subsubsection}{3}{0mm}% + {-1ex plus -.5ex minus -.2ex}% + {1ex plus .2ex}% + {\normalfont\small\bfseries}} +\makeatother + +\setcounter{secnumdepth}{0} % Don't print section numbers +\setlength{\parindent}{0pt} +\setlength{\parskip}{0pt plus 0.5ex} + +\definecolor{TableHead}{rgb}{0.353, 0.329, 0.667} +\definecolor{TableRow}{rgb}{0.816, 0.812, 0.902} + +\NewEnviron{keysref}[1]{ + % \begin{center} + \smallskip + \begin{tikzpicture} + \rowcolors{1}{}{TableRow} + + \node (tbl) [inner sep=0pt] { + \begin{tabular}{p{2.5cm}p{5.05cm}} + \rowcolor{TableHead} + \multicolumn{2}{l}{\normalsize\textbf{\color{white}{#1}}}\parbox{0pt}{\rule{0pt}{0.3ex+\baselineskip}}\\ + \BODY + \arrayrulecolor{TableHead}\specialrule{.17em}{0em}{.2em} + \end{tabular}}; + \begin{pgfonlayer}{background} + \draw[rounded corners=2pt,top color=TableHead,bottom color=TableHead, draw=white] + ($(tbl.north west)-(0,-0.05)$) rectangle ($(tbl.north east)-(0.0,0.15)$); + \draw[rounded corners=2pt,top color=TableHead,bottom color=TableHead, draw=white] + ($(tbl.south west)-(0.0,-0.11)$) rectangle ($(tbl.south east)-(-0.0,-0.02)$); + \end{pgfonlayer} + \end{tikzpicture} + % \end{center} +} + +% https://tex.stackexchange.com/a/102523 +\newcommand{\forceindent}[1]{\leavevmode{\parindent=#1\indent}} + +%% CUSTOM NOTATION %% + +\newcommand{\qs}{Q\#} +\newcommand{\unixlike}{\hfill\faApple\faLinux} +\newcommand{\ctrllike}{\hfill\faWindows\faLinux} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\begin{document} + +\raggedright\ + +% \begin{center} + \Large{\qs~Language Quick Reference} +% \end{center} + +\footnotesize +\begin{multicols}{3} + + \begin{keysref}{Primitive Types} + 64-bit integers & \texttt{Int} \\ + Double-precision floats & \texttt{Double} \\ + Booleans & \texttt{Bool} \newline + e.g.: \texttt{true} or \texttt{false} \\ + Qubits & \texttt{Qubit} \\ + Pauli basis & \texttt{Pauli} \newline + e.g.: \texttt{PauliI}, \texttt{PauliX}, \texttt{PauliY}, or \texttt{PauliZ} \\ + Measurement \newline results & \texttt{Result} \newline + e.g.: \texttt{Zero} or \texttt{One} \\ + Sequences of \newline integers & \texttt{Range} \newline + e.g.: 1..10 or 5..-1..0 \\ + Strings & \texttt{String} \\ + \end{keysref} + + \begin{keysref}{Derived Types} + Arrays & \texttt{\emph{elementType}[]} \\ + Tuples & \texttt{(\emph{type0}, \emph{type1}, ...)} \newline + e.g.: \texttt{(Int, Qubit)} \\ + Functions & \texttt{\emph{input} -> \emph{output}} \newline + e.g.: \texttt{ArcTan2 : (Double, Double) -> Double} \\ + Operations & \texttt{\emph{input} => \emph{output} : \emph{variants}} \newline + e.g.: \texttt{H : (Qubit => () : Adjoint, Controlled)} \\ + \end{keysref} + + \begin{keysref}{Functions, Operations and Types} + Define function \newline (classical routine) + & \texttt{function \emph{Name}(\emph{in0} : \emph{type0}, ...) : \emph{returnType} \{} \newline + \texttt{\hphantom{....}// \emph{function body}} \newline + \texttt{\}} \\ + Define operation \newline (quantum routine) + & \texttt{operation \emph{Name}(\emph{in0} : \emph{type0}, ...) : \emph{returnType} \{} \newline + \texttt{\hphantom{....}body \{ ... \}} \newline + \texttt{\hphantom{....}adjoint \{ ... \}} \newline + \texttt{\hphantom{....}controlled \{ ... \}} \newline + \texttt{\hphantom{....}adjoint controlled \{ ... \}} \newline + \texttt{\}} \\ + Define \newline user-defined type & \texttt{newtype \emph{TypeName} = \emph{BaseType}} \newline + \texttt{newtype TermList = (Int, Int -> (Double, Double))} \\ + Call adjoint \newline operation & \texttt{(Adjoint \emph{Name})(\emph{parameters})} \\ + Call controlled \newline operation & \texttt{(Controlled \emph{Name})(\emph{controlQubits}, \emph{parameters})} \\ + \end{keysref} + + \begin{keysref}{Symbols and Variables} + Declare immutable \newline symbol & \texttt{let \emph{name} = \emph{value}} \\ + Declare mutable \newline symbol (variable) & \texttt{mutable \emph{name} = \emph{value}} \\ + Update mutable \newline symbol (variable) & \texttt{set \emph{name} = \emph{value}} \\ + \end{keysref} + + \columnbreak%\ + + \begin{keysref}{Arrays} + Allocation & \texttt{mutable \emph{name} = new \emph{Type}[\emph{length}]} \\ + Length & \texttt{Length(\emph{name})} \\ + k-th element & \texttt{\emph{name}[k]} \newline NB: indices are 0-based \\ + Array literal & \texttt{[\emph{value0}; \emph{value1}; ...]} \newline + e.g.: \texttt{[true; false; true]} \\ + Slicing (subarray) & \texttt{let \emph{name} = \emph{name}[\emph{start}..\emph{end}]} \\ + \end{keysref} + + \begin{keysref}{Control Flow} + For loop & \texttt{for (\emph{ind} in \emph{range}) \{ ... \}} \newline + e.g.: \texttt{for (i in 0..N-1) \{ ... \}} \\ + Repeat-until-success loop & \texttt{repeat \{ ... \} \newline until \emph{condition} \newline fixup \{ ... \}} \\ + Conditional \newline statement & \texttt{if \emph{cond1} \{ ... \} \newline elif \emph{cond2} \{ ... \} \newline else \{ ... \}}\\ + Return a value & \texttt{return \emph{value}} \\ + Stop with an error & \texttt{fail "\emph{Error message}"} \\ + \end{keysref} + + \begin{keysref}{Debugging} + Print a string & \texttt{Message("Hello Quantum!")} \\ + Print an \newline interpolated string + & \texttt{Message(\$"Value = \{\emph{val}\}")} \\ + Assert that qubit is in $\ket{0}$ or $\ket{1}$ & \texttt{AssertQubit (expected : Result, q : Qubit)}\\ + Print amplitudes \newline of wave function & \texttt{DumpMachine()} \\ + \end{keysref} + + \begin{keysref}{Qubits and Operations on Qubits} + Allocate qubits & \texttt{using (\emph{name} = Qubit[\emph{length}]) \{} \newline + \texttt{\hphantom{....}//} Qubits in \texttt{\emph{name}} start in $\ket{0}$. \newline + \texttt{\hphantom{....}...} \newline + \texttt{\hphantom{....}//} Qubits must be returned to $\ket{0}$. \newline + \texttt{\}} \\ + Pauli gates & \texttt{X} : + $\ket{0} \mapsto \ket{1}$, $\ket{1} \mapsto \ket{0}$ \newline + \texttt{Y} : + $\ket{0} \mapsto i \ket{1}$, $\ket{1} \mapsto -i \ket{0}$ \newline + \texttt{Z} : + $\ket{0} \mapsto \ket{0}$, $\ket{1} \mapsto -\ket{1}$ \\ + Hadamard & \texttt{H} : + $\ket{0} \mapsto \ket{+} = \frac{1}{\sqrt{2}} ( \ket{0} + \ket{1} )$, \newline + $\ket{1} \mapsto \ket{-} = \frac{1}{\sqrt{2}} ( \ket{0} - \ket{1} )$ \\ + Controlled-NOT & \texttt{CNOT : ((control : Qubit, \newline target : Qubit) => ())} \newline + $\ket{00} \mapsto \ket{00}$, $\ket{01} \mapsto \ket{01}$, \newline + $\ket{10} \mapsto \ket{11}$, $\ket{11} \mapsto \ket{10}$ \\ + Measure qubit in Pauli $Z$ basis & \texttt{M : Qubit => Result} \\ + Perform joint measurement of qubits in given Pauli bases & \texttt{Measure : (Pauli[], Qubit[]) => Result} \\ + Rotate about given Pauli axis & \texttt{R : (Pauli, Double, Qubit) => ()} \\ + Rotate about Pauli $X$, $Y$, $Z$ axis & \texttt{Rx : (Double, Qubit) => ()} \newline + \texttt{Ry : (Double, Qubit) => ()} \newline + \texttt{Rz : (Double, Qubit) => ()} \\ + Reset qubit to $\ket{0}$ & \texttt{Reset : Qubit => ()} \\ + Reset qubits to $\ket{0..0}$ & \texttt{ResetAll : Qubit[] => ()} \\ + \end{keysref} + + \columnbreak%\ + + \section{Resources} + + \begin{keysref}{Documentation} + Quantum \newline Development Kit & \url{https://docs.microsoft.com/quantum} \\ + \qs~Language \newline Reference & \url{https://docs.microsoft.com/quantum/quantum-qr-intro} \\ + \qs~Library \newline Reference & \url{https://docs.microsoft.com/qsharp/api} \\ + \end{keysref} + + \begin{keysref}{\qs~Code Repositories} + QDK Samples and Libraries & \url{https://github.com/Microsoft/Quantum} \\ + Quantum Katas & \url{https://github.com/Microsoft/QuantumKatas} \\ + \end{keysref} + + \begin{keysref}{Command Line Basics} + Change directory & \texttt{cd \emph{dirname}} \\ + Go to home & \texttt{cd \textasciitilde} \\ + Go up one directory & \texttt{cd ..} \\ + Make new directory & \texttt{mkdir \emph{dirname}} \\ + Open current \newline directory in VS Code & \texttt{code .} \\ + \end{keysref} + + \begin{keysref}{Working with \qs~Projects} + Create new project & \texttt{dotnet new console -lang Q\# --output \emph{project-dir}} \\ + Change directory to \newline project directory & \texttt{cd \emph{project-dir}} \\ + Build project & \texttt{dotnet build} \\ + Run all unit tests & \texttt{dotnet test} \\ + \end{keysref} + +\end{multicols} + +\end{document} \ No newline at end of file