From adeaa8c9b26ce40a3de5ac41bcc664c521718450 Mon Sep 17 00:00:00 2001 From: Mariia Mykhailova Date: Wed, 5 Jan 2022 11:54:18 -0800 Subject: [PATCH] [GraphColoring] Add tasks for triangle-free coloring (#719) --- GraphColoring/ReferenceImplementation.qs | 95 ++++++++++++- GraphColoring/Tasks.qs | 132 +++++++++++++++++- GraphColoring/Tests.qs | 165 ++++++++++++++++++++++- 3 files changed, 388 insertions(+), 4 deletions(-) diff --git a/GraphColoring/ReferenceImplementation.qs b/GraphColoring/ReferenceImplementation.qs index e7d491952c2..d3eafab7183 100644 --- a/GraphColoring/ReferenceImplementation.qs +++ b/GraphColoring/ReferenceImplementation.qs @@ -263,4 +263,97 @@ namespace Quantum.Kata.GraphColoring { return GroversAlgorithm_Reference(V, oracle); } -} \ No newline at end of file + + ////////////////////////////////////////////////////////////////// + // Part IV. Triangle-free coloring problem + ////////////////////////////////////////////////////////////////// + + + // Task 4.1. Convert the list of graph edges into an adjacency matrix + function EdgesListAsAdjacencyMatrix_Reference (V : Int, edges : (Int, Int)[]) : Int[][] { + mutable adjVertices = [[-1, size = V], size = V]; + for edgeInd in IndexRange(edges) { + let (v1, v2) = edges[edgeInd]; + // track both directions in the adjacency matrix + set adjVertices w/= v1 <- (adjVertices[v1] w/ v2 <- edgeInd); + set adjVertices w/= v2 <- (adjVertices[v2] w/ v1 <- edgeInd); + } + return adjVertices; + } + + + // Task 4.2. Extract a list of triangles from an adjacency matrix + function AdjacencyMatrixAsTrianglesList_Reference (V : Int, adjacencyMatrix : Int[][]) : (Int, Int, Int)[] { + mutable triangles = []; + for v1 in 0 .. V - 1 { + for v2 in v1 + 1 .. V - 1 { + for v3 in v2 + 1 .. V - 1 { + if adjacencyMatrix[v1][v2] > -1 and adjacencyMatrix[v1][v3] > -1 and adjacencyMatrix[v2][v3] > -1 { + set triangles = triangles + [(v1, v2, v3)]; + } + } + } + } + return triangles; + } + + + // Task 4.3. Classical verification of triangle-free coloring + function IsVertexColoringTriangleFree_Reference (V : Int, edges: (Int, Int)[], colors: Int[]) : Bool { + // Construct adjacency matrix of the graph + let adjacencyMatrix = EdgesListAsAdjacencyMatrix_Reference(V, edges); + // Enumerate all possible triangles of edges + let trianglesList = AdjacencyMatrixAsTrianglesList_Reference(V, adjacencyMatrix); + + for (v1, v2, v3) in trianglesList { + if (colors[adjacencyMatrix[v1][v2]] == colors[adjacencyMatrix[v1][v3]] and + colors[adjacencyMatrix[v1][v2]] == colors[adjacencyMatrix[v2][v3]]) { + return false; + } + } + + return true; + } + + + // Task 4.4. Oracle to check that three colors don't form a triangle + // (f(x) = 1 if at least two of three input bits are different) + operation ValidTriangleOracle_Reference (inputs : Qubit[], output : Qubit) : Unit is Adj+Ctl { + // We want to NOT mark only all 0s and all 1s - mark them and flip the output qubit + (ControlledOnInt(0, X))(inputs, output); + Controlled X(inputs, output); + X(output); + } + + + // Task 4.5. Oracle for verifying triangle-free edge coloring + // (f(x) = 1 if the graph edge coloring is triangle-free) + operation TriangleFreeColoringOracle_Reference ( + V : Int, + edges : (Int, Int)[], + colorsRegister : Qubit[], + target : Qubit + ) : Unit is Adj+Ctl { + // Construct adjacency matrix of the graph + let adjacencyMatrix = EdgesListAsAdjacencyMatrix_Reference(V, edges); + // Enumerate all possible triangles of edges + let trianglesList = AdjacencyMatrixAsTrianglesList_Reference(V, adjacencyMatrix); + + // Allocate one extra qubit per triangle + let nTr = Length(trianglesList); + use aux = Qubit[nTr]; + within { + for i in 0 .. nTr - 1 { + // For each triangle, form an array of qubits that holds its edge colors + let (v1, v2, v3) = trianglesList[i]; + let edgeColors = [colorsRegister[adjacencyMatrix[v1][v2]], + colorsRegister[adjacencyMatrix[v1][v3]], + colorsRegister[adjacencyMatrix[v2][v3]]]; + ValidTriangleOracle_Reference(edgeColors, aux[i]); + } + } apply { + // If all triangles are good, all aux qubits are 1 + Controlled X(aux, target); + } + } +} diff --git a/GraphColoring/Tasks.qs b/GraphColoring/Tasks.qs index 8616927d38f..0ad269e0c08 100644 --- a/GraphColoring/Tasks.qs +++ b/GraphColoring/Tasks.qs @@ -96,6 +96,10 @@ namespace Quantum.Kata.GraphColoring { // Part II. Vertex coloring problem ////////////////////////////////////////////////////////////////// + // The vertex graph coloring is a coloring of graph vertices which + // labels each vertex with one of the given colors so that + // no two vertices of the same color are connected by an edge. + // Task 2.1. Classical verification of vertex coloring // Inputs: // 1) The number of vertices in the graph V (V ≤ 6). @@ -157,6 +161,11 @@ namespace Quantum.Kata.GraphColoring { // Part III. Weak coloring problem ////////////////////////////////////////////////////////////////// + // Weak graph coloring is a coloring of graph vertices which + // labels each vertex with one of the given colors so that + // each vertex is either isolated or is connected by an edge + // to at least one neighbor of a different color. + // Task 3.1. Determine if an edge contains the vertex // Inputs: // 1) An edge denoted by a tuple of integers. @@ -265,4 +274,125 @@ namespace Quantum.Kata.GraphColoring { // ... return [0, size = V]; } -} \ No newline at end of file + + + + ////////////////////////////////////////////////////////////////// + // Part IV. Triangle-free coloring problem + ////////////////////////////////////////////////////////////////// + + // Triangle-free graph coloring is a coloring of graph edges which + // labels each edge with one of two colors so that no three edges + // of the same color form a triangle. + + // Task 4.1. Convert the list of graph edges into an adjacency matrix + // 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. + // Output: A 2D array of integers representing this graph as an adjacency matrix: + // the element [i][j] should be -1 if the vertices i and j are not connected with an edge, + // or store the index of the edge if the vertices i and j are connected with an edge. + // Elements [i][i] should be -1 unless there is an edge connecting vertex i to itself. + // Example: Consider a graph with V = 3 and edges = [(0, 1), (0, 2), (1, 2)]. + // The adjacency matrix for it would be + // [-1, 0, 1], + // [ 0, -1, 2], + // [ 1, 2, -1]. + function EdgesListAsAdjacencyMatrix (V : Int, edges : (Int, Int)[]) : Int[][] { + // ... + return []; + } + + + // Task 4.2. Extract a list of triangles from an adjacency matrix + // Inputs: + // 1) The number of vertices in the graph V (V ≤ 6). + // 2) An adjacency matrix describing the graph in the format from task 4.1. + // Output: An array of 3-tuples listing all triangles in the graph, + // that is, all triplets of vertices connected by edges. + // Each of the 3-tuples should list the triangle vertices in ascending order, + // and the 3-tuples in the array should be sorted in ascending order as well. + // Example: Consider the adjacency matrix + // [-1, 0, 1], + // [ 0, -1, 2], + // [ 1, 2, -1]. + // The list of triangles for it would be [(0, 1, 2)]. + function AdjacencyMatrixAsTrianglesList (V : Int, adjacencyMatrix : Int[][]) : (Int, Int, Int)[] { + // ... + return []; + } + + + // Task 4.3. Classical verification of triangle-free 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 E integers, representing the edge coloring of the graph. + // i-th element of the array is the color of the edge number i, and it is 0 or 1. + // The colors of edges in this array are given in the same order as the edges in the "edges" array. + // Output: true if the given coloring is triangle-free + // (i.e., no triangle of edges connecting 3 vertices has all three edges in the same color), + // and false otherwise. + // Example: Consider a graph with V = 3 and edges = [(0, 1), (0, 2), (1, 2)]. + // Some of the valid colorings for it would be [0, 1, 0] and [-1, 5, 18]. + function IsVertexColoringTriangleFree (V : Int, edges: (Int, Int)[], colors: Int[]) : Bool { + // ... + return true; + } + + + // Task 4.4. Oracle to check that three colors don't form a triangle + // (f(x) = 1 if at least two of three input bits are different) + // Inputs: + // 1) a 3-qubit array `inputs`, + // 2) a qubit `output`. + // Goal: Flip the output qubit if and only if at least two of the input qubits are different. + // For example, for the result of applying the operation to state (|001⟩ + |110⟩ + |111⟩) ⊗ |0⟩ + // will be |001⟩ ⊗ |1⟩ + |110⟩ ⊗ |1⟩ + |111⟩ ⊗ |0⟩. + operation ValidTriangleOracle (inputs : Qubit[], output : Qubit) : Unit is Adj+Ctl { + // ... + } + + + // Task 4.5. Oracle for verifying triangle-free edge coloring + // (f(x) = 1 if the graph edge coloring is triangle-free) + // Inputs: + // 1) The number of vertices in the graph V (V ≤ 6). + // 2) An array of E tuples of integers "edges", representing the edges of the graph (0 ≤ E ≤ V(V-1)/2). + // Each tuple gives the indices of the start and the end vertices of the edge. + // The vertices are indexed 0 through V - 1. + // The graph is undirected, so the order of the start and the end vertices in the edge doesn't matter. + // 3) An array of E qubits "colorsRegister" that encodes the color assignments of the edges. + // Each color will be 0 or 1 (stored in 1 qubit). + // The colors of edges in this array are given in the same order as the edges in the "edges" array. + // 4) A qubit "target" in an arbitrary state. + // + // Goal: Implement a marking oracle for function f(x) = 1 if + // the coloring of the edges of the given graph described by this colors assignment is triangle-free, + // i.e., no triangle of edges connecting 3 vertices has all three edges in the same color. + // + // Example: a graph with 3 vertices and 3 edges [(0, 1), (1, 2), (2, 0)] has one triangle. + // The result of applying the operation to state (|001⟩ + |110⟩ + |111⟩)/√3 ⊗ |0⟩ + // will be 1/√3|001⟩ ⊗ |1⟩ + 1/√3|110⟩ ⊗ |1⟩ + 1/√3|111⟩ ⊗ |0⟩. + // The first two terms describe triangle-free colorings, + // and the last term describes a coloring where all edges of the triangle have the same color. + // + // In this task you are not allowed to use quantum gates that use more qubits than the number of edges in the graph, + // unless there are 3 or less edges in the graph. For example, if the graph has 4 edges, you can only use 4-qubit gates or less. + // You are guaranteed that in tests that have 4 or more edges in the graph the number of triangles in the graph + // will be strictly less than the number of edges. + // + // Hint: Make use of functions and operations you've defined in previous tasks. + operation TriangleFreeColoringOracle ( + V : Int, + edges : (Int, Int)[], + colorsRegister : Qubit[], + target : Qubit + ) : Unit is Adj+Ctl { + // ... + } +} diff --git a/GraphColoring/Tests.qs b/GraphColoring/Tests.qs index da5871f7ce0..7fac67dc625 100644 --- a/GraphColoring/Tests.qs +++ b/GraphColoring/Tests.qs @@ -9,6 +9,8 @@ namespace Quantum.Kata.GraphColoring { + open Microsoft.Quantum.Logical; + open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Measurement; open Microsoft.Quantum.Intrinsic; @@ -16,6 +18,7 @@ namespace Quantum.Kata.GraphColoring { open Microsoft.Quantum.Convert; open Microsoft.Quantum.Math; open Microsoft.Quantum.Diagnostics; + open Quantum.Kata.Utils; @@ -229,8 +232,8 @@ namespace Quantum.Kata.GraphColoring { Message($"Testing V = {V}, edges = {edges}"); let N = 2 * V; use (coloringRegister, target) = (Qubit[N], Qubit()); - // Try all possible colorings of 4 colors on V vertices and check if they are calculated correctly. - // Hack: fix the color of the first vertex, since all colorings are agnostic to the specific colors used. + // Try all possible colorings of 4 colors on V vertices and check if they are calculated correctly. + // Hack: fix the color of the first vertex, since all colorings are agnostic to the specific colors used. for k in 0 .. (1 <<< (N - 2)) - 1 { // Prepare k-th coloring let binary = [false, false] + IntAsBoolArray(k, N); @@ -387,4 +390,162 @@ namespace Quantum.Kata.GraphColoring { Message($"Got correct coloring {coloring}"); } } + + + + ////////////////////////////////////////////////////////////////// + // Part IV. Triangle-free coloring problem + ////////////////////////////////////////////////////////////////// + + @Test("QuantumSimulator") + operation T41_EdgesListAsAdjacencyMatrix () : Unit { + for (V, edges) in ExampleGraphs() { + Message($"Running on graph V = {V}, edges = {edges}"); + let actualAdjMatrix = EdgesListAsAdjacencyMatrix(V, edges); + let expectedAdjMatrix = EdgesListAsAdjacencyMatrix_Reference(V, edges); + let equal = EqualA(EqualA(EqualI, _, _), actualAdjMatrix, expectedAdjMatrix); + Fact(equal, $"Got incorrect adjacency matrix {actualAdjMatrix}"); + Message($"Got correct adjacency matrix"); + } + } + + + function EqualTriplet(t1 : (Int, Int, Int), t2 : (Int, Int, Int)) : Bool { + let (p1, q1, r1) = t1; + let (p2, q2, r2) = t2; + return p1 == p2 and q1 == q2 and r1 == r2; + } + + + @Test("QuantumSimulator") + operation T42_AdjacencyMatrixAsTrianglesList () : Unit { + for (V, edges) in ExampleGraphs() { + Message($"Running on graph V = {V}, edges = {edges}"); + let adjMatrix = EdgesListAsAdjacencyMatrix_Reference(V, edges); + + let actualTrianglesList = AdjacencyMatrixAsTrianglesList(V, adjMatrix); + let expectedTrianglesList = AdjacencyMatrixAsTrianglesList_Reference(V, adjMatrix); + let equal = EqualA(EqualTriplet, actualTrianglesList, expectedTrianglesList); + Fact(equal, $"Got incorrect triangles list {actualTrianglesList}"); + Message($"Got correct triangles list"); + } + } + + + @Test("QuantumSimulator") + operation T43_IsVertexColoringTriangleFree () : Unit { + let testCases = (ExampleGraphs())[...2]; + + // There is only one edge coloring for a disconnected graph of 3 vertices + let coloringAndVerdicts0 = [([], true)]; + + // For the complete graph with 4 vertices: + let coloringAndVerdicts1 = [([0, 0, 1, 0, 1, 0], false), + ([0, 0, 0, 1, 1, 1], false), + ([0, 0, 1, 1, 0, 0], true)]; + + // For a graph with 5 vertices, 3 in a triangle and 2 separate: any coloring with the triangle of different colors + let coloringAndVerdicts2 = [([0, 0, 1, 0, 1], true), + ([0, 0, 0, 1, 1], true), + ([0, 1, 1, 1, 0], false)]; + + let fullTestCases = Zipped(testCases, [ + coloringAndVerdicts0, + coloringAndVerdicts1, + coloringAndVerdicts2 + ]); + + for (testCase, coloringAndVerdicts) in fullTestCases { + let (V, edges) = testCase; + for (coloring, expectedResult) in coloringAndVerdicts { + Fact(IsVertexColoringTriangleFree(V, edges, coloring) == expectedResult, + $"Coloring {coloring} judged {(not expectedResult) ? "" | " not"} triangle-free for graph V = {V}, edges = {edges}"); + } + } + } + + + // Helper operation to validate oracles for things other than vertex coloring + operation VerifySingleOutputFunction(numInputs : Int, op : ((Qubit[], Qubit) => Unit is Adj+Ctl), predicate : (Int -> Bool)) : Unit { + for assignment in 0 .. 2^numInputs - 1 { + use (inputs, output) = (Qubit[numInputs], Qubit()); + within { + ApplyXorInPlace(assignment, LittleEndian(inputs)); + } apply { + op(inputs, output); + } + + // Check that the result is expected + let actual = ResultAsBool(MResetZ(output)); + let expected = predicate(assignment); + Fact(actual == expected, + $"Oracle evaluation result {actual} does not match expected {expected} for assignment {IntAsBoolArray(assignment, numInputs)}"); + + // Check that the inputs were not modified + Fact(MeasureInteger(LittleEndian(inputs)) == 0, + $"The input states were modified for assignment {assignment}"); + } + } + + + function IsTriangleValid (input : Int) : Bool { + // the triangle is valid if it has at least two different bits (i.e., not all are the same) + return input > 0 and input < 7; + } + + + @Test("QuantumSimulator") + operation T44_ValidTriangleOracle () : Unit { + VerifySingleOutputFunction(3, ValidTriangleOracle, IsTriangleValid); + } + + + function ExampleGraphs_TriangleFreeColoring () : (Int, (Int, Int)[])[] { + return [ + // trivial graph with no edges (no triangles) + (6, []), + // "circle" graph (no triangles) + (6, [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)]), + // complete bipartite graph K_{1,5} (no triangles) + (6, [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]), + // complete bipartite graph K_{3,3} (no triangles) + (6, [(0, 1), (0, 3), (0, 5), (1, 2), (1, 4), (2, 3), (2, 5), (3, 4), (4, 5)]), + // complete graph with 3 edges (1 triangle) + (3, [(0, 1), (1, 2), (2, 0)]), + // disconnected graph consisting of two triangles 0-1-2 and 3-4-5 + (6, [(0, 1), (4, 3), (2, 1), (5, 4), (5, 0), (2, 0)]), + // square + diagonal (two triangles) + (4, [(1, 0), (3, 2), (0, 3), (2, 1), (3, 1)]), + // square + two diagonals (four triangles) + (4, [(1, 0), (3, 2), (0, 3), (2, 1), (3, 1), (0, 2)]), + // square + two diagonals + center (4 triangles) + (5, [(0, 2), (1, 2), (3, 2), (4, 2), (0, 1), (1, 3), (4, 0), (3, 4)]), + // pyramid of 4 triangles + (6, [(2, 1), (2, 3), (1, 3), (1, 0), (1, 5), (3, 5), (3, 4), (0, 5), (5, 4)]) + ]; + } + + + function BoolAsInt (a : Bool) : Int { + return a ? 1 | 0; + } + + function IsVertexColoringTriangleFree_Wrapper (V : Int, edges: (Int, Int)[], colors: Int) : Bool { + let colorBools = IntAsBoolArray(colors, Length(edges)); + let colorBits = Mapped(BoolAsInt, colorBools); + return IsVertexColoringTriangleFree_Reference(V, edges, colorBits); + } + + + @Test("QuantumSimulator") + operation T45_TriangleFreeColoringOracle () : Unit { + for (V, edges) in ExampleGraphs_TriangleFreeColoring() { + Message($"Testing {(V, edges)}"); + VerifySingleOutputFunction( + Length(edges), + TriangleFreeColoringOracle(V, edges, _, _), + IsVertexColoringTriangleFree_Wrapper(V, edges, _)); + } + } + }