Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

[Graph Coloring] Add Kata Implementation #82

Merged
merged 6 commits into from
Jul 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions GraphColoring/GraphColoring.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<IsPackable>false</IsPackable>
<RootNamespace>Quantum.Kata.GraphColoring</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Quantum.Standard" Version="0.7.1905.3109" />
<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.7.1905.3109" />
<PackageReference Include="Microsoft.Quantum.Xunit" Version="0.7.1905.3109" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>

<ItemGroup>
<None Include="README.md" />
</ItemGroup>
</Project>
25 changes: 25 additions & 0 deletions GraphColoring/GraphColoring.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.645
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphColoring", "GraphColoring.csproj", "{79F6EC37-AF54-4CBF-94B6-497703536A06}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{79F6EC37-AF54-4CBF-94B6-497703536A06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79F6EC37-AF54-4CBF-94B6-497703536A06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79F6EC37-AF54-4CBF-94B6-497703536A06}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79F6EC37-AF54-4CBF-94B6-497703536A06}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {25683E2F-6DFF-41AF-9748-477003938E13}
EndGlobalSection
EndGlobal
9 changes: 9 additions & 0 deletions GraphColoring/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Welcome!

This kata continues the exploration of the Grover's search algorithm started in the "Grover's Algorithm" kata.
It teaches writing oracles for the algorithm which describe the problem instead of the solution, using graph coloring problem as an example.
Then it takes the implementation of the Grover's search to the next level, covering solving the problems with unknown number of solutions.

* You can read more about graph coloring problems [here](https://en.wikipedia.org/wiki/Graph_coloring).
* It is strongly recommended to complete the [Grover's Algorithm kata](./../GroversAlgorithm/) before proceeding to this one. You can also refer to its [README.md](./../GroversAlgorithm/README.md) for the list of resources on Grover's algorithm.
* [SolveSATWithGrover](./../SolveSATWithGrover/) is another kata covering oracle implementation for solving constraint satisfaction problems.
183 changes: 183 additions & 0 deletions GraphColoring/ReferenceImplementation.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// 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.GraphColoring {

open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Diagnostics;


//////////////////////////////////////////////////////////////////
// Part I. Colors representation and manipulation
//////////////////////////////////////////////////////////////////

// Task 1.1. Initialize register to a color
operation InitializeColor_Reference (C : Int, register : Qubit[]) : Unit is Adj {
let N = Length(register);
// Convert C to an array of bits in little endian format
let binaryC = IntAsBoolArray(C, N);
// Value "true" corresponds to bit 1 and requires applying an X gate
ApplyPauliFromBitString(PauliX, true, binaryC, register);
}


// Task 1.2. Read color from a register
operation MeasureColor_Reference (register : Qubit[]) : Int {
return ResultArrayAsInt(MultiM(register));
}


// Task 1.3. Read coloring from a register
operation MeasureColoring_Reference (K : Int, register : Qubit[]) : Int[] {
let N = Length(register) / K;
let colorPartitions = Partitioned(ConstantArray(K - 1, N), register);
return ForEach(MeasureColor_Reference, colorPartitions);
}


// Task 1.4. 2-bit color equality oracle
operation ColorEqualityOracle_2bit_Reference (c0 : Qubit[], c1 : Qubit[], target : Qubit) : Unit is Adj+Ctl {
// Small case solution with no extra qubits allocated:
// iterate over all bit strings of length 2, and do a controlled X on the target qubit
// with control qubits c0 and c1 in states described by each of these bit strings.
// For a better solution, see task 1.4.
for (color in 0..3) {
let binaryColor = IntAsBoolArray(color, 2);
(ControlledOnBitString(binaryColor + binaryColor, X))(c0 + c1, target);
}
}


// Task 1.5. N-bit color equality oracle (no extra qubits)
operation ColorEqualityOracle_Nbit_Reference (c0 : Qubit[], c1 : Qubit[], target : Qubit) : Unit is Adj+Ctl {
for ((q0, q1) in Zip(c0, c1)) {
// compute XOR of q0 and q1 in place (storing it in q1)
CNOT(q0, q1);
}
// if all XORs are 0, the bit strings are equal
(ControlledOnInt(0, X))(c1, target);
// uncompute
for ((q0, q1) in Zip(c0, c1)) {
CNOT(q0, q1);
}
}



//////////////////////////////////////////////////////////////////
// Part II. Vertex coloring problem
//////////////////////////////////////////////////////////////////

// Task 2.1. Classical verification of vertex coloring
function IsVertexColoringValid_Reference (V : Int, edges: (Int, Int)[], colors: Int[]) : Bool {
for ((start, end) in edges) {
if (colors[start] == colors[end]) {
return false;
}
}
return true;
}


// Task 2.2. Oracle for verifying vertex coloring
operation VertexColoringOracle_Reference (V : Int, edges : (Int, Int)[], colorsRegister : Qubit[], target : Qubit) : Unit is Adj+Ctl {
let nEdges = Length(edges);
using (conflicts = Qubit[nEdges]) {
for (i in 0 .. nEdges-1) {
let (start, end) = edges[i];
// Check that endpoints of the edge have different colors:
// apply ColorEqualityOracle_Nbit_Reference oracle; if the colors are the same the result will be 1, indicating a conflict
ColorEqualityOracle_Nbit_Reference(colorsRegister[start * 2 .. start * 2 + 1],
colorsRegister[end * 2 .. end * 2 + 1], conflicts[i]);
}

// If there are no conflicts (all qubits are in 0 state), the vertex coloring is valid
(ControlledOnInt(0, X))(conflicts, target);

for (i in 0 .. nEdges-1) {
let (start, end) = edges[i];
// Check that endpoints of the edge have different colors:
// apply ColorEqualityOracle_Nbit_Reference oracle; if the colors are the same the result will be 1, indicating a conflict
Adjoint ColorEqualityOracle_Nbit_Reference(colorsRegister[start * 2 .. start * 2 + 1],
colorsRegister[end * 2 .. end * 2 + 1], conflicts[i]);
}
}
}


// Task 2.3. Using Grover's search to find vertex coloring
operation GroversAlgorithm_Reference (V : Int, oracle : ((Qubit[], Qubit) => Unit is Adj)) : Int[] {
// This task is similar to task 2.2 from SolveSATWithGrover kata, but the percentage of correct solutions is potentially higher.
mutable coloring = new Int[V];

// Note that coloring register has the number of qubits that is twice the number of vertices (2 qubits per vertex).
using ((register, output) = (Qubit[2 * V], Qubit())) {
mutable correct = false;
mutable iter = 1;
repeat {
Message($"Trying search with {iter} iterations");
GroversAlgorithm_Loop(register, oracle, iter);
let res = MultiM(register);
// to check whether the result is correct, apply the oracle to the register plus ancilla after measurement
oracle(register, output);
if (MResetZ(output) == One) {
set correct = true;
// Read off coloring
set coloring = MeasureColoring_Reference(V, register);
}
ResetAll(register);
} until (correct or iter > 10) // the fail-safe to avoid going into an infinite loop
fixup {
set iter += 1;
}
if (not correct) {
fail "Failed to find a coloring";
}
}
return coloring;
}

// Grover loop implementation taken from SolveSATWithGrover kata.
operation OracleConverterImpl (markingOracle : ((Qubit[], Qubit) => Unit is Adj), register : Qubit[]) : Unit is Adj {

using (target = Qubit()) {
// Put the target into the |-⟩ state
X(target);
H(target);

// Apply the marking oracle; since the target is in the |-⟩ state,
// flipping the target if the register satisfies the oracle condition will apply a -1 factor to the state
markingOracle(register, target);

// Put the target back into |0⟩ so we can return it
H(target);
X(target);
}
}

operation GroversAlgorithm_Loop (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit is Adj), iterations : Int) : Unit {
let phaseOracle = OracleConverterImpl(oracle, _);
ApplyToEach(H, register);

for (i in 1 .. iterations) {
phaseOracle(register);
ApplyToEach(H, register);
ApplyToEach(X, register);
Controlled Z(Most(register), Tail(register));
ApplyToEach(X, register);
ApplyToEach(H, register);
}
}
}
151 changes: 151 additions & 0 deletions GraphColoring/Tasks.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

namespace Quantum.Kata.GraphColoring {

open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;

//////////////////////////////////////////////////////////////////
// Welcome!
//////////////////////////////////////////////////////////////////

// The "Graph coloring" quantum kata is a series of exercises designed
// to teach you the basics of using Grover search to solve constraint
// satisfaction problems, using graph coloring problem as an example.
// It covers the following topics:
// - writing oracles implementing constraints on graph coloring,
// - using Grover's algorithm to solve graph coloring problems with unknown number of solutions.

// Each task is wrapped in one operation preceded by the description of the task.
// Each task (except tasks in which you have to write a test) has a unit test associated with it,
// which initially fails. Your goal is to fill in the blank (marked with // ... comment)
// with some Q# code to make the failing test pass.

// Within each section, tasks are given in approximate order of increasing difficulty;
// harder ones are marked with asterisks.


//////////////////////////////////////////////////////////////////
// Part I. Colors representation and manipulation
//////////////////////////////////////////////////////////////////

// Task 1.1. Initialize register to a color
// Inputs:
// 1) An integer C (0 ≤ C ≤ 2ᴺ - 1).
// 2) An array of N qubits in the |0...0⟩ state.
// Goal: Prepare the array in the basis state which represents the binary notation of C.
// Use little-endian encoding (i.e., the least significant bit should be stored in the first qubit).
// Example: for N = 2 and C = 2 the state should be |01⟩.
operation InitializeColor (C : Int, register : Qubit[]) : Unit is Adj {
// ...
}


// Task 1.2. Read color from a register
// Input: An array of N qubits which are guaranteed to be in one of the 2ᴺ basis states.
// Output: An N-bit integer that represents this basis state, in little-endian encoding.
// The operation should not change the state of the qubits.
// Example: for N = 2 and the qubits in the state |01⟩ return 2 (and keep the qubits in |01⟩).
operation MeasureColor (register : Qubit[]) : Int {
// ...
return -1;
}


// Task 1.3. Read coloring from a register
// Inputs:
// 1) The number of elements in the coloring K.
// 2) An array of K * N qubits which are guaranteed to be in one of the 2ᴷᴺ basis states.
// Output: An array of K N-bit integers that represent this basis state.
// Integer i of the array is stored in qubits i * N, i * N + 1, ..., i * N + N - 1 in little-endian format.
// The operation should not change the state of the qubits.
// Example: for N = 2, K = 2 and the qubits in the state |0110⟩ return [2, 1].
operation MeasureColoring (K : Int, register : Qubit[]) : Int[] {
// ...
return new Int[0];
}


// Task 1.4. 2-bit color equality oracle
// Inputs:
// 1) an array of 2 qubits in an arbitrary state |c₀⟩ representing the first color,
// 1) an array of 2 qubits in an arbitrary state |c₁⟩ representing the second color,
// 3) a qubit in an arbitrary state |y⟩ (target qubit).
// Goal: Transform state |c₀⟩|c₁⟩|y⟩ into state |c₀⟩|c₁⟩|y ⊕ f(c₀, c₁)⟩ (⊕ is addition modulo 2),
// where f(x) = 1 if c₀ and c₁ are in the same state, and 0 otherwise.
// Leave the query register in the same state it started in.
// In this task you are allowed to allocate extra qubits.
operation ColorEqualityOracle_2bit (c0 : Qubit[], c1 : Qubit[], target : Qubit) : Unit is Adj {
// ...
}


// Task 1.5. N-bit color equality oracle (no extra qubits)
// This task is the same as task 1.3, but in this task you are NOT allowed to allocate extra qubits.
operation ColorEqualityOracle_Nbit (c0 : Qubit[], c1 : Qubit[], target : Qubit) : Unit is Adj {
// ...
}



//////////////////////////////////////////////////////////////////
// Part II. Vertex coloring problem
//////////////////////////////////////////////////////////////////

// Task 2.1. Classical verification of vertex coloring
// Inputs:
// 1) The number of vertices in the graph V (V ≤ 6).
// 2) An array of E tuples of integers, representing the edges of the graph (E ≤ 12).
// Each tuple gives the indices of the start and the end vertices of the edge.
// The vertices are indexed 0 through V - 1.
// 3) An array of V integers, representing the vertex coloring of the graph.
// i-th element of the array is the color of the vertex number i.
// Output: true if the given vertex coloring is valid
// (i.e., no pair of vertices connected by an edge have the same color),
// and false otherwise.
// Example: Graph 0 -- 1 -- 2 would have V = 3 and edges = [(0, 1), (1, 2)].
// Some of the valid colorings for it would be [0, 1, 0] and [-1, 5, 18].
function IsVertexColoringValid (V : Int, edges: (Int, Int)[], colors: Int[]) : Bool {
// 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.
Fact(Length(colors) == V, $"The vertex coloring must contain exactly {V} elements, and it contained {Length(colors)}.");

// ...

return true;
}


// Task 2.2. Oracle for verifying vertex coloring
// Inputs:
// 1) The number of vertices in the graph V (V ≤ 6).
// 2) An array of E tuples of integers, representing the edges of the graph (E ≤ 12).
// Each tuple gives the indices of the start and the end vertices of the edge.
// The vertices are indexed 0 through V - 1.
// 3) An array of 2V qubits colorsRegister that encodes the color assignments.
// 4) A qubit in an arbitrary state |y⟩ (target qubit).
//
// Goal: Transform state |x, y⟩ into state |x, y ⊕ f(x)⟩ (⊕ is addition modulo 2),
// where f(x) = 1 the given vertex coloring is valid and 0 otherwise.
// Leave the query register in the same state it started in.
//
// Each color in colorsRegister is represented as a 2-bit integer in little-endian format.
// See task 1.3 for a more detailed description of color assignments.
operation VertexColoringOracle (V : Int, edges : (Int, Int)[], colorsRegister : Qubit[], target : Qubit) : Unit is Adj+Ctl {
// ...
}


// Task 2.3. Using Grover's search to find vertex coloring
// Inputs:
// 1) The number of vertices in the graph V (V ≤ 6).
// 2) A marking oracle which implements vertex coloring verification, as implemented in task 2.2.
//
// Output: A valid vertex coloring for the graph, in a format used in task 2.1.
operation GroversAlgorithm (V : Int, oracle : ((Qubit[], Qubit) => Unit is Adj)) : Int[] {
// ...
return new Int[V];
}
}
Loading