diff --git a/HiddenShift/.gitignore b/HiddenShift/.gitignore new file mode 100755 index 00000000000..1b2a32a9ad7 --- /dev/null +++ b/HiddenShift/.gitignore @@ -0,0 +1,357 @@ +### Backup files ### +*.swp +*~ + +### Other files ### +.DS_Store +.vscode + +# Created by https://www.gitignore.io/api/visualstudio +# Edit at https://www.gitignore.io/?templates=visualstudio + +### VisualStudio ### +## 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 +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +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/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.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 +*.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/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# 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 +*- Backup*.rdl + +# 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 personal settings +.cr/personal + +# 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/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# End of https://www.gitignore.io/api/visualstudio + diff --git a/HiddenShift/GaussianElimination.qs b/HiddenShift/GaussianElimination.qs new file mode 100755 index 00000000000..ca01cf8a87a --- /dev/null +++ b/HiddenShift/GaussianElimination.qs @@ -0,0 +1,152 @@ +namespace Quantum.Kata.HiddenShift +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + + /// + /// Returns an array of bool vectors which together form the basis of the null space. + /// The input matrix may have any dimensions >= 1. It must be rectangular (and not jagged). + /// + /// Example: + /// + /// let matrix = [ + /// [1, 0, 0, 0], + /// [0, 1, 0, 0] + /// ]; + /// let kernel = KernelMod2(matrix); + /// + /// + /// kernel is [[0, 0, 1, 0], [0, 0, 0, 1]]. + /// + function KernelMod2(matrix: Int[][]) : Int[][] { + let reduced = GaussianEliminationMod2(matrix); + let rank = QuickRank(reduced); + let nullSpaceDims = Length(matrix[0]) - rank; + + mutable resultNumber = 0; + mutable result = new Int[][nullSpaceDims]; + for (reducedCol in 0..Length(reduced[0])-1) { + // Find a column with a non-pivot row to take from + // If the element to check to see if this column is a pivot is off the end of the matrix, + // treat it as if it's a 0 + if (reducedCol-resultNumber >= Length(reduced) || reduced[reducedCol-resultNumber][reducedCol] != 1) { + + set result[resultNumber] = new Int[Length(matrix[0])]; + + mutable skippedRows = 0; + for (resultDimension in 0..Length(matrix[0])-1) { + if (resultDimension+skippedRows >= Length(reduced) || reduced[resultDimension-skippedRows][resultDimension] != 1) { + // Pull from identity matrix. Each of the skipped rows should be an element of the + // identity matrix, instead of the reduced matrix + set result[resultNumber][resultDimension] = skippedRows == resultNumber ? 1 | 0; + set skippedRows = skippedRows + 1; + } else { + set result[resultNumber][resultDimension] = reduced[resultDimension+skippedRows][reducedCol]; // Pull from result matrix + } + } + set resultNumber = resultNumber + 1; + } + } + + return result; + } + + /// + /// Computes the rank of the given matrix. + /// The input matrix may have any dimensions >= 1. It must be rectangular (and not jagged). + /// + /// Example: + /// + /// let matrix = [ + /// [1, 1, 0, 0], + /// [0, 1, 0, 0], + /// [0, 0, 0, 0] + /// ]; + /// let rank = RankMod2(matrix); + /// + /// rank is now 2. + /// + function RankMod2(matrix: Int[][]) : Int { + return QuickRank(GaussianEliminationMod2(matrix)); + } + + /// Computes the rank of the given matrix. Assumes the matrix is in row echelon form. + /// The input matrix may have any dimensions > 1. It must be rectangular (and not jagged). + function QuickRank(matrix: Int[][]) : Int { + mutable zeroRows = 0; + for (i in 0..Length(matrix)-1) { + mutable onlyZeroes = true; + for (j in 0..Length(matrix[i])-1) { + if (matrix[i][j] != 0) { + set onlyZeroes = false; + } + } + + if (onlyZeroes) { + set zeroRows = zeroRows + 1; + } + } + return Length(matrix) - zeroRows; + } + + /// + /// Returns the result of computing Gaussian elimination on the given matrix. + /// Assumes elements of the matrix are in Z2. + /// The input matrix may have any dimensions > 1. It must be rectangular (and not jagged). + /// + /// Example: + /// + /// let matrix = [ + /// [1, 0, 1, 1], + /// [0, 1, 1, 0] + /// ]; + /// let rowReduced = GaussianEliminationMod2(matrix); + /// + /// + /// rowReduced is: + /// + /// [ + /// [0, 0, 1, 0], + /// [0, 0, 0, 1] + /// ] + /// + /// + function GaussianEliminationMod2(matrix_: Int[][]) : Int[][] { + mutable matrix = matrix_; + mutable minPivotRow = 0; + for (column in 0..Length(matrix[0])-1) { + mutable pivotRow = -1; + for (row in minPivotRow..Length(matrix)-1) { + if (matrix[row][column] == 1 && pivotRow == -1) { + set pivotRow = row; + // Add the other rows when needed + for (i in 0..Length(matrix)-1) { + if (matrix[i][column] == 1 && i != row) { + set matrix = AddRowsMod2(matrix, row, i); + } + } + } + } + + if (pivotRow != -1) { + let temp = matrix[minPivotRow]; + set matrix[minPivotRow] = matrix[pivotRow]; + set matrix[pivotRow] = temp; + + set minPivotRow = minPivotRow + 1; + } + } + + return matrix; + } + + /// Helper function to add a source row into a destination row + /// matrix[destRow] += matrix[srcRow] + function AddRowsMod2(matrix_: Int[][], srcRow: Int, destRow: Int) : Int[][] { + mutable matrix = matrix_; + for (col in 0..Length(matrix[0])-1) { + set matrix[destRow][col] = matrix[destRow][col] ^^^ matrix[srcRow][col]; + } + return matrix; + } +} diff --git a/HiddenShift/GaussianEliminationTestSuite.cs b/HiddenShift/GaussianEliminationTestSuite.cs new file mode 100755 index 00000000000..9f8afeef980 --- /dev/null +++ b/HiddenShift/GaussianEliminationTestSuite.cs @@ -0,0 +1,30 @@ +using Microsoft.Quantum.Simulation.XUnit; +using Microsoft.Quantum.Simulation.Simulators; +using Xunit.Abstractions; +using System.Diagnostics; + +namespace Quantum.Kata.HiddenShift +{ + public class GaussianEliminationTestSuite + { + private readonly ITestOutputHelper output; + + public GaussianEliminationTestSuite(ITestOutputHelper output) + { + this.output = output; + } + + // Use another namespace so tests are seperated in VSTest + [OperationDriver(TestNamespace = "Quantum.Kata.HiddenShift.GaussianEliminationTests", DisplayName = "Gaussian Elimination")] + public void TestGaussianElimination(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/HiddenShift/GaussianEliminationTests.qs b/HiddenShift/GaussianEliminationTests.qs new file mode 100755 index 00000000000..1e4f85ab623 --- /dev/null +++ b/HiddenShift/GaussianEliminationTests.qs @@ -0,0 +1,225 @@ +namespace Quantum.Kata.HiddenShift.GaussianEliminationTests +{ + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Canon; + open Quantum.Kata.HiddenShift; + + operation BasicTest () : Unit + { + let result = GaussianEliminationMod2([ + [1, 0], + [1, 1] + ]); + + AssertIntMatrixEqual(result, [ + [1, 0], + [0, 1] + ], ""); + } + + operation FirstColumnHasZerosTest() : Unit + { + let result = GaussianEliminationMod2([ + [0, 1, 1], + [0, 0, 1] + ]); + + AssertIntMatrixEqual(result, [ + [0, 1, 0], + [0, 0, 1] + ], ""); + } + + operation MiddleColumnHasZerosTest() : Unit + { + let result = GaussianEliminationMod2([ + [1, 0, 1], + [0, 0, 1] + ]); + + AssertIntMatrixEqual(result, [ + [1, 0, 0], + [0, 0, 1] + ], ""); + } + + operation TwoIdenticalRowsTest() : Unit + { + let result = GaussianEliminationMod2([ + [1, 1, 0], + [1, 1, 0], + [0, 0, 0] + ]); + + AssertIntMatrixEqual(result, [ + [1, 1, 0], + [0, 0, 0], + [0, 0, 0] + ], ""); + } + + operation OnlyFlipIf1Test () : Unit + { + // In the second column, only one value is 1, so none of the rows should be modified + let result = GaussianEliminationMod2([ + [1, 0, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0] + ]); + + AssertIntMatrixEqual(result, [ + [1, 0, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0] + ], ""); + } + + operation FlipIf1Test () : Unit + { + // In the second column, both the first and third rows and added with the second + let result = GaussianEliminationMod2([ + [1, 1, 1, 0], + [0, 1, 1, 0], + [0, 1, 0, 0] + ]); + + AssertIntMatrixEqual(result, [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0] + ], ""); + } + + operation DoSwapsTest() : Unit + { + // In the second column, both the first and third rows and added with the second + let result = GaussianEliminationMod2([ + [0, 0, 1, 0], + [0, 1, 0, 0], + [1, 0, 0, 0] + ]); + + AssertIntMatrixEqual(result, [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0] + ], ""); + } + + operation EliminateRowTest() : Unit + { + let result = GaussianEliminationMod2([ + [0, 0, 1, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [1, 0, 0, 0] + ]); + + AssertIntMatrixEqual(result, [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 0] + ], ""); + } + + operation BasicKernelTest() : Unit + { + let result = KernelMod2([ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 0] + ]); + + AssertSubspaceEqual(result, [ + [0, 0, 0, 1] + ], ""); + } + + operation TrickyKernelTest() : Unit + { + let result = KernelMod2([ + [1, 0, 0, 1], + [0, 1, 1, 1] + ]); + + AssertSubspaceEqual(result, [ + [1, 1, 0, 1], + [0, 1, 1, 0] + ], ""); + } + + operation TwoIdenticalRowsKernelTest() : Unit + { + let result = KernelMod2([ + [1, 1, 0], + [1, 1, 0], + [0, 0, 0] + ]); + + AssertSubspaceEqual(result, [ + [1, 1, 0], + [0, 0, 1] + ], ""); + } + + operation NonPivotInMiddleTest() : Unit { + + let result = KernelMod2([ + [1,0,1,0,0], + [0,1,0,0,0], + [0,0,0,1,0], + [0,0,0,0,1] + ]); + + AssertSubspaceEqual(result, [ + [1, 0, 1, 0, 0] + ], ""); + } + + operation MultipleNonPivotInMiddleTest() : Unit { + + let result = KernelMod2([ + [1,0,1,0,0], + [0,1,0,0,0], + [0,0,0,0,1] + ]); + + AssertSubspaceEqual(result, [ + [1, 0, 1, 0, 0], + [0, 0, 0, 1, 0] + ], ""); + } + + function AssertIntMatrixEqual(actual: Int[][], expected: Int[][], message: String) : Unit { + AssertIntEqual(Length(actual), Length(expected), message); + for (i in 0..Length(actual)-1) { + AssertBoolEqual(IntVectorEqual(actual[i], expected[i]), true, $"Expected: {expected} Actual: {actual}. {message}"); + } + } + + function AssertSubspaceEqual(actualBasis: Int[][], expectedBasis: Int[][], message: String) : Unit { + AssertIntEqual(Length(actualBasis), Length(expectedBasis), message); + for (i in 0..Length(actualBasis)-1) { + mutable foundMatch = false; + for (j in 0..Length(expectedBasis) - 1) { + set foundMatch = foundMatch || IntVectorEqual(actualBasis[i], expectedBasis[j]); + } + AssertBoolEqual(foundMatch, true, $"Expected: {expectedBasis} Actual: {actualBasis}. {message}"); + } + } + + function IntVectorEqual(a: Int[], b: Int[]) : Bool { + if (Length(a) != Length(b)) { + return false; + } + + mutable equal = true; + for (i in 0..Length(a)-1) { + set equal = equal && a[i] == b[i]; + } + + return equal; + } +} diff --git a/HiddenShift/HiddenShift.csproj b/HiddenShift/HiddenShift.csproj new file mode 100755 index 00000000000..7921f0dca4b --- /dev/null +++ b/HiddenShift/HiddenShift.csproj @@ -0,0 +1,19 @@ + + + netcoreapp2.0 + x64 + false + false + latest + + + + + + + + + + + + diff --git a/HiddenShift/HiddenShift.sln b/HiddenShift/HiddenShift.sln new file mode 100755 index 00000000000..cc4fc24bc95 --- /dev/null +++ b/HiddenShift/HiddenShift.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.438 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HiddenShift", "HiddenShift.csproj", "{20F82642-A1E2-4159-AFC2-848AD4F4C801}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {20F82642-A1E2-4159-AFC2-848AD4F4C801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20F82642-A1E2-4159-AFC2-848AD4F4C801}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20F82642-A1E2-4159-AFC2-848AD4F4C801}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20F82642-A1E2-4159-AFC2-848AD4F4C801}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {49B39155-036B-4F43-9DE6-95DA9A5FC8CE} + EndGlobalSection +EndGlobal diff --git a/HiddenShift/Hidden_Shift_Problem.pdf b/HiddenShift/Hidden_Shift_Problem.pdf new file mode 100755 index 00000000000..d94de5cd2d3 Binary files /dev/null and b/HiddenShift/Hidden_Shift_Problem.pdf differ diff --git a/HiddenShift/README.md b/HiddenShift/README.md new file mode 100755 index 00000000000..aada2819aea --- /dev/null +++ b/HiddenShift/README.md @@ -0,0 +1,24 @@ +# Welcome! + +This kata covers the Hidden Shift Problem, which concerns determining a hidden shift s for +boolean bent functions f and g such that g(x) = f(x + s). This is another example of a +problem that can be solved exponentially faster by a quantum algorithm than any classical +algorithms. + +This Kata consists of three parts. Task 1 concerns implementing bent boolean functions for +which problem relies upon. Task 2 concerns implementing a deterministic solution to the +Hidden Shift Problem that makes O(1) oracle calls. Finally, Task 3 concerns implementing +a reduction of the Hidden Shift Problem to Simon's Algorithm and similar instances of the +Hidden Subgroup Problem, solving the Hidden Shift Problem in O(n) oracle calls with a +guaranteed probability of success. Just as in Simon's Algorithm Kata, the classical portion +of the generalized Hidden Shift Problem Algorithm is already implemented for you. However, +unlike the Simon's Algorithm Kata, we have included a Gaussian Elimination library in Q# +along with the Kata, with which users are free to make their own tests. A dedicated test +suite is provided to validate the Gaussian Elimination functions. + +#### Hidden Shift Problem +* We recommend completing the [Simon's Algorithm Kata](https://github.com/Microsoft/QuantumKatas/tree/master/SimonsAlgorithm) +before starting on this Kata. +* We recommend reading [our paper](Hidden_Shift_Problem.pdf), which goes over all the theory +and algorithms needed for completing the Hidden Shift Problem Kata. +* For more details on the Hidden Shift Problem, read [Quantum algorithms for highly non-linear Boolean functions](https://arxiv.org/abs/0811.3208) by Martin Roetteler, on which our paper is based on. diff --git a/HiddenShift/ReferenceImplementation.qs b/HiddenShift/ReferenceImplementation.qs new file mode 100755 index 00000000000..d560f2bf76f --- /dev/null +++ b/HiddenShift/ReferenceImplementation.qs @@ -0,0 +1,206 @@ +////////////////////////////////////////////////////////////////////// +// 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.HiddenShift +{ + + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Primitive; + open Microsoft.Quantum.Extensions.Diagnostics; + + //-------------------------------------------------------------------- + + // Implement the inner product oracle, which is the most basic kind + // of bent function. + // The dual of the inner product function is itself. + operation InnerProductOracle_Reference(x : Qubit[], target : Qubit) : Unit { + body (...) { + let N = Length(x); + AssertBoolEqual(((N % 2) == 0) && (N > 0), true, "The number of input qubits must be even and positive"); + for (i in 0 .. 2 .. N-1) { + CCNOT(x[i], x[i+1], target); + } + } + controlled adjoint auto; + controlled auto; + adjoint auto; + } + + // Implement a quadratic boolean function oracle. + // Q is an upper triangular matrix of 0's and 1's with 0's along the diagonal. + // L is a row vector of 0's and 1's + operation QuadraticOracle_Reference(x : Qubit[], target : Qubit, Q : Int[][], L : Int[]) : Unit { + body (...) { + let N = Length(x); + AssertIntEqual(N, Length(L), "The length of x and L must be equal"); + AssertIntEqual(N, Length(Q), "The length of x and Q must be equal"); + AssertIntEqual(Length(Q), Length(Q[0]), "Q must be a square matrix"); + for (j in 0 .. N-1) { + if (L[j] == 1) { + CNOT(x[j], target); + } + for (i in 0 .. j - 1) { + if (Q[i][j] == 1) { + CCNOT(x[i], x[j], target); + } + } + } + } + controlled adjoint auto; + controlled auto; + adjoint auto; + } + + //-------------------------------------------------------------------- + + operation ShiftedOracle_Helper_Reference(Uf : ((Qubit[], Qubit) => Unit : Adjoint, Controlled), s : Int[], x : Qubit[], target : Qubit) : Unit { + body (...) { + let N = Length(x); + using (qs = Qubit[N]) { + for (i in 0 .. N-1) { + if (s[i] == 1) { + X(qs[i]); + } + } + for (i in 0 .. N - 1) { + CNOT(x[i], qs[i]); + } + Uf(qs, target); + for (i in 0 .. N - 1) { + CNOT(x[i], qs[i]); + } + for (i in 0 .. N-1) { + if (s[i] == 1) { + X(qs[i]); + } + } + } + } + controlled adjoint auto; + controlled auto; + adjoint auto; + } + + // Returns the shifted oracle g for a marking oracle f such that g(x) = f(x + s). + function ShiftedOracle_Reference(Uf : ((Qubit[], Qubit) => Unit : Adjoint, Controlled), s : Int[]) : ((Qubit[], Qubit) => Unit : Adjoint, Controlled) { + return ShiftedOracle_Helper_Reference(Uf, s, _, _); + } + + operation PhaseFlipOracle_Helper_Reference(Uf : ((Qubit[], Qubit) => Unit : Adjoint, Controlled), x : Qubit[]) : Unit { + body (...) { + let N = Length(x); + using (b = Qubit()) { + X(b); + H(b); + Uf(x, b); + H(b); + X(b); + } + } + controlled adjoint auto; + controlled auto; + adjoint auto; + } + + // Returns the phase flip oracle corresponding to the marking oracle f. + function PhaseFlipOracle_Reference(Uf : ((Qubit[], Qubit) => Unit : Adjoint, Controlled)) : ((Qubit[]) => Unit : Adjoint, Controlled) { + return PhaseFlipOracle_Helper_Reference(Uf, _); + } + + //-------------------------------------------------------------------- + + operation WalshHadamard_Reference (x : Qubit[]) : Unit { + body (...) { + ApplyToEachA(H, x); + } + adjoint auto; + } + + // Determines the hidden shift s from the oracle for g(x) and the daul of f(x). + operation DeterministicHiddenShiftSolution_Reference (N : Int, Ug : ((Qubit[]) => Unit), Ufd : ((Qubit[]) => Unit)) : Int[] { + mutable res = new Int[N]; + using (qs = Qubit[N]) { + ApplyToEach(H, qs); + Ug(qs); + WalshHadamard_Reference(qs); + Ufd(qs); + WalshHadamard_Reference(qs); + for (i in 0 .. N-1) { + set res[i] = M(qs[i]) == One ? 1 | 0; + } + ResetAll(qs); + } + return res; + } + + //-------------------------------------------------------------------- + + operation HidingFunctionOracle_Helper_Reference (Uf : ((Qubit[]) => Unit : Adjoint, Controlled), Ug : ((Qubit[]) => Unit : Adjoint, Controlled), + b : Qubit, x : Qubit[], target : Qubit[]) : Unit { + body (...) { + ApplyToEachCA(H, target); + for (i in 0 .. Length(x) - 1) { + CNOT(x[i], target[i]); + } + + Controlled Ug([b], (target)); + X(b); + Controlled Uf([b], (target)); + X(b); + + for (i in 0 .. Length(x) - 1) { + CNOT(x[i], target[i]); + } + ApplyToEachCA(H, target); + } + controlled adjoint auto; + controlled auto; + adjoint auto; + } + + function HidingFunctionOracle_Reference (Uf : ((Qubit[]) => Unit : Adjoint, Controlled), Ug : ((Qubit[]) => Unit : Adjoint, Controlled)) : + ((Qubit, Qubit[], Qubit[]) => Unit : Adjoint, Controlled) { + return HidingFunctionOracle_Helper_Reference(Uf, Ug, _, _, _); + } + + operation HiddenShiftIteration_Reference(n: Int, Uf : ((Qubit[]) => Unit : Adjoint, Controlled), Ug : ((Qubit[]) => Unit : Adjoint, Controlled)) : Int[] { + mutable result = new Int[n+1]; + + using ((cosetReg, targetReg) = (Qubit[n+1], Qubit[n])) { + ApplyToEach(H, cosetReg); + + let h = HidingFunctionOracle_Reference(Uf, Ug); + h(cosetReg[0], cosetReg[1..Length(cosetReg)-1], targetReg); + + ApplyToEach(H, cosetReg); + + for (i in 0..n) { + set result[i] = M(cosetReg[i]) == One ? 1 | 0; + } + + ResetAll(cosetReg); + ResetAll(targetReg); + } + return result; + } + + operation GeneralizedHiddenShift_Reference(n: Int, Uf : ((Qubit[]) => Unit : Adjoint, Controlled), Ug : ((Qubit[]) => Unit : Adjoint, Controlled)) : Int[] { + mutable results = new Int[][n+1]; + for (i in 0 .. Length(results) - 1) { + set results[i] = new Int[n+1]; + } + repeat { + let newResult = HiddenShiftIteration_Reference(n, Uf, Ug); + + let currentRank = RankMod2(results); + set results[currentRank] = newResult; + } until (Length(KernelMod2(results)) == 1) + fixup {} + + return (KernelMod2(results))[0]; + } +} diff --git a/HiddenShift/Tasks.qs b/HiddenShift/Tasks.qs new file mode 100755 index 00000000000..391f3c089d2 --- /dev/null +++ b/HiddenShift/Tasks.qs @@ -0,0 +1,220 @@ +namespace Quantum.Kata.HiddenShift +{ + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Primitive; + + ////////////////////////////////////////////////////////////////// + // Welcome! + ////////////////////////////////////////////////////////////////// + + // "HiddenShiftKata" quantum kata is a series of exercises designed to + // guide you in implementing algorithms to solve the Hidden Shift Problem + // It covers the following topics: + // - bent boolean oracles, + // - a correlation based solution to the Hidden Shift Problem, + // - a Hidden Subgroup based solution to the Hidden Shift Problem. + + // 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. + + // None of the tasks in Part I require measurement. + + // NOTE: remember that addition on a Z_2 x Z_2 x ... is defined as a bitwise XOR. + + ////////////////////////////////////////////////////////////////// + // Part I. Bent Boolean Oracles + ////////////////////////////////////////////////////////////////// + + // Task 1.1: Inner Product Oracle f(x) = \sum_{x in 1..2..i-1} x_{i} x_{i+1} + // The binary inner product is the most natural kind of bent function, and + // has the property where the dual of the inner product oracle is itself. + // Inputs: + // 1) N qubits in arbitrary state |x> (input register) (N is even) + // 2) a qubit in arbitrary state |target> (output qubit) + // Goal: transform state |x>|target> into state |x>|target + f(x)> (+ is addition modulo 2). + operation InnerProductOracle(x : Qubit[], target : Qubit) : Unit { + body (...) { + // ... + } + controlled adjoint auto; + controlled auto; + adjoint auto; + } + + // Task 1.2: Quadratic Boolean Oracle f(x) = x Q x^T + L x^T + // Quadratic boolean functions are bent when the symplectic B = Q + Q^T + // has full rank. The dual of a quadratic bent function will then be + // a quadratic bent function. + // Inputs: + // 1) N qubits in arbitrary state |x> (input register) (N is even) + // 2) a qubit in arbitrary state |target> (output qubit) + // 3) an upper triangular N by N matrix Q with 0s along the diagonal + // 4) an N length vector L + // Goal: transform state |x>|target> into state |x>|target + f(x)> (+ is addition modulo 2). + operation QuadraticOracle(x : Qubit[], target : Qubit, Q : Int[][], L : Int[]) : Unit { + body (...) { + // ... + } + controlled adjoint auto; + controlled auto; + adjoint auto; + } + + // Task 1.3: Shifted Oracles + // Given a marking oracle f that takes |x>|y> to |x>|y + f(x)> + // and a shift s, return a marking oracle that takes |x>|y> to |x>|y + g(x)> + // where g(x) = f(x + s). + // + // This task is used as a setup to the Hidden Shift Problem, but is not part of + // the quantum algorithm solution. With it, you should be able to implement + // your own tests of your Hidden Shift Problem solutions. + // + // Inputs: + // 1) a marking oracle f + // 2) an Int array giving a bit string s in {0, 1}^n, where n is the dimension of the domain of f. + // Goal: return an oracle that transforms state |x>|y> into state |x>|y + g(x)> + function ShiftedOracle(f : ((Qubit[], Qubit) => Unit : Adjoint, Controlled), s : Int[]) : ((Qubit[], Qubit) => Unit : Adjoint, Controlled) { + // ... + // This task returns a NoOp so that it compiles. You'll likely + // need to return your own operation in order to get this to work. + return NoOp<(Qubit[], Qubit)>; + } + + // Task 1.4: Phase Flip Oracles + // Given a marking oracle f that takes |x>|y> to |x>|y + f(x)>, + // return a phase flip oracle that takes |x> to (-1)^f(x) |x> + // Inputs: + // 1) a marking oracle f + // Goal: return an oracle that transforms state |x> into state (-1)^f(x) |x> + function PhaseFlipOracle(f : ((Qubit[], Qubit) => Unit : Adjoint, Controlled)) : ((Qubit[]) => Unit : Adjoint, Controlled) { + // ... + // This task returns a NoOp so that it compiles. You'll likely + // need to return your own operation in order to get this to work. + return NoOp<(Qubit[])>; + } + + ////////////////////////////////////////////////////////////////// + // Part II. Deterministic Solution to the Hidden Shift Problem + ////////////////////////////////////////////////////////////////// + + // Task 2.1. Implement the Walsh-Hadamard Transform + // Inputs: + // 1) N qubits in arbitrary state |x> + // + // Goal: Transform the state according to the Walsh-Hadamard Transform. + // The matrix form of the Walsh-Hadamard transform is given by + // + // (1/Sqrt(2^n)) Sum_{x, y in {0, 1}^n} (-1)^(x, y) |x>