Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Graph Coloring kata, part 2 #1804

Merged
merged 2 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions katas/content/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
"deutsch_jozsa",
"grovers_search",
"solving_sat",
"solving_graph_coloring",
"qec_shor"
]
27 changes: 27 additions & 0 deletions katas/content/solving_graph_coloring/Common.qs
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,31 @@ namespace Kata.Verification {

true
}

function IsWeakColoringValid_OneVertex_Reference (V : Int, edges: (Int, Int)[], colors: Int[], vertex : Int) : Bool {
mutable neighborCount = 0;
mutable hasDifferentNeighbor = false;

for (start, end) in edges {
if start == vertex or end == vertex {
set neighborCount += 1;
if colors[start] != colors[end] {
set hasDifferentNeighbor = true;
}
}
}

return neighborCount == 0 or hasDifferentNeighbor;
}

function IsWeakColoringValid_Reference (V : Int, edges: (Int, Int)[], colors: Int[]) : Bool {
for v in 0 .. V - 1 {
if not IsWeakColoringValid_OneVertex_Reference(V, edges, colors, v) {
return false;
}
}

return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
namespace Kata {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;

@EntryPoint()
operation SolvingGraphColoringWithGroverDemo() : Unit {
// Experiment with the parameters to explore algorithm behavior in different conditions!
let V = 3;
// The 0 -- 1 -- 2 graph from the examples
let edges = [(0, 1), (1, 2)];
let markingOracle = Oracle_VertexColoring(V, edges, _, _);
for iterations in 0 .. 9 {
mutable success = 0;
for _ in 1 .. 100 {
let res = GroversSearch(2 * V, markingOracle, iterations);
// Convert measurement results to integers
let colorPartitions = Chunks(2, res);
let colors = Mapped(bits -> BoolArrayAsInt(Reversed(bits)), colorPartitions);
if IsVertexColoringValid(V, edges, colors) {
set success += 1;
}
}
Message($"{iterations} iterations - {success}% success rate");
}
}

operation GroversSearch(
n : Int,
markingOracle : (Qubit[], Qubit) => Unit is Adj + Ctl,
iterations : Int
) : Bool[] {
use qs = Qubit[n];

// Operation that prepares the state |all⟩.
let meanStatePrep = ApplyToEachCA(H, _);

// The phase oracle.
let phaseOracle = ApplyMarkingOracleAsPhaseOracle(markingOracle, _);

// Prepare the system in the state |all⟩.
meanStatePrep(qs);

// Do Grover's iterations.
for _ in 1 .. iterations {
// Apply the phase oracle.
phaseOracle(qs);

// Apply "reflection about the mean".
ReflectionAboutState(qs, meanStatePrep);
}

// Measure to get the result.
return ResultArrayAsBoolArray(MResetEachZ(qs));
}

operation ApplyMarkingOracleAsPhaseOracle(
markingOracle : (Qubit[], Qubit) => Unit is Adj + Ctl,
qubits : Qubit[])
: Unit is Adj + Ctl {
use minus = Qubit();
within {
X(minus);
H(minus);
} apply {
markingOracle(qubits, minus);
}
}

operation ReflectionAboutState(
qs : Qubit[],
statePrep : Qubit[] => Unit is Adj + Ctl)
: Unit is Adj + Ctl {
within {
Adjoint statePrep(qs);
} apply {
ConditionalPhaseFlip(qs);
}
}

operation ConditionalPhaseFlip(qs : Qubit[]) : Unit is Adj + Ctl {
within {
ApplyToEachA(X, qs);
} apply {
Controlled Z(qs[1 ...], qs[0]);
}
R(PauliI, 2.0 * PI(), qs[0]);
}

operation Oracle_VertexColoring(V : Int, edges: (Int, Int)[], x : Qubit[], y : Qubit) : Unit is Adj + Ctl {
let edgesNumber = Length(edges);
use conflicts = Qubit[edgesNumber];
within {
for i in 0 .. edgesNumber - 1 {
let (v0, v1) = edges[i];
Oracle_ColorEquality(x[2 * v0 .. 2 * v0 + 1],
x[2 * v1 .. 2 * v1 + 1], conflicts[i]);
}
} apply {
ApplyControlledOnInt(0, X, conflicts, y);
}
}

operation Oracle_ColorEquality(x0 : Qubit[], x1 : Qubit[], y : Qubit) : Unit is Adj + Ctl {
within {
for i in 0..Length(x0) - 1 {
CNOT(x0[i], x1[i]);
}
} apply {
ApplyControlledOnInt(0, X, x1, y);
}
}

function IsVertexColoringValid(V : Int, edges: (Int, Int)[], colors: Int[]) : Bool {
for (v0, v1) in edges {
if colors[v0] == colors[v1] {
return false;
}
}
return true;
}
}
33 changes: 30 additions & 3 deletions katas/content/solving_graph_coloring/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,28 @@ In this lesson, you will implement the marking oracle for the vertex coloring pr
"title": "Weak Coloring Problem"
})

TODO: finish migration
Weak graph coloring is a coloring of graph vertices which labels each vertex with one of the given colors in such a way that each non-isolated vertex is connected by an edge to at least one neighbor of a different color.

In this lesson, you will implement the marking oracle for the weak graph coloring problem.

@[exercise]({
"id": "solving_graph_coloring__weak_coloring_classical",
"title": "Is Weak Coloring Valid? (Classical)",
"path": "./weak_coloring_classical/"
})

@[exercise]({
"id": "solving_graph_coloring__weak_coloring_one_vertex",
"title": "Is One-Vertex Weak Coloring Valid? (Quantum)",
"path": "./weak_coloring_one_vertex/"
})

@[exercise]({
"id": "solving_graph_coloring__weak_coloring_quantum",
"title": "Is Weak Coloring Valid? (Quantum)",
"path": "./weak_coloring/"
})



@[section]({
Expand All @@ -74,10 +95,16 @@ TODO: finish migration

In this lesson, you will experiment with using Grover's algorithm to solve graph coloring problems.

Notice that in this case, it's not as easy to know the number of solutions to the problem upfront as it was for the prefix function used in the "Grover's Search Algorithm" kata.
In this case, it's not as easy to know the number of solutions to the problem upfront as it was for the prefix function used in the "Grover's Search Algorithm" kata.
Experiment with choosing the number of iterations at random. How does this affect the success probability?

TODO: add demo
> Notice that the example used in this demo, a three-vertex graph with two edges, has the search space of size $4^3 = 64$.
> A bit over half of the colorings in the search space are valid vertex colorings. You can count them by assigning any color to vertex $1$ and then assigning any of the remaining three colors to vertices $0$ and $2$ independently, to get $4 \cdot 3 \cdot 3 = 36$ solutions to the problem.
> This example is one of the scenarios in which Grover's search behaves in a non-intuitive way: not doing any iterations
> yields a success probability of over $50\%$, so the first iteration actually reduces the probability of getting a correct answer!

@[example]({"id": "solving_graph_coloring__e2edemo", "codePath": "./examples/SolvingGraphColoringWithGroverDemo.qs"})


@[section]({
"id": "solving_graph_coloring__conclusion",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Kata {
open Microsoft.Quantum.Arrays;

operation Oracle_WeakColoring(
V : Int, edges: (Int, Int)[], x : Qubit[], y : Qubit
) : Unit is Adj + Ctl {
// Implement your solution here...

}

// You might find these helper operations from earlier tasks useful.
operation Oracle_WeakColoring_OneVertex(
V : Int, edges: (Int, Int)[], x : Qubit[], y : Qubit, vertex : Int
) : Unit is Adj + Ctl {
let neighborEdges = Filtered((a, b) -> a == vertex or b == vertex, edges);
let nNeighbors = Length(neighborEdges);
use sameColorChecks = Qubit[nNeighbors];
within {
for ((a, b), checkQubit) in Zipped(neighborEdges, sameColorChecks) {
Oracle_ColorEquality(x[a * 2 .. a * 2 + 1],
x[b * 2 .. b * 2 + 1],
checkQubit);
}
} apply {
X(y);
if nNeighbors > 0 {
Controlled X(sameColorChecks, y);
}
}
}

operation Oracle_ColorEquality(x0 : Qubit[], x1 : Qubit[], y : Qubit) : Unit is Adj + Ctl {
within {
for i in 0..Length(x0) - 1 {
CNOT(x0[i], x1[i]);
}
} apply {
ApplyControlledOnInt(0, X, x1, y);
}
}
}
47 changes: 47 additions & 0 deletions katas/content/solving_graph_coloring/weak_coloring/Solution.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Kata {
open Microsoft.Quantum.Arrays;

operation Oracle_WeakColoring(
V : Int, edges: (Int, Int)[], x : Qubit[], y : Qubit
) : Unit is Adj + Ctl {
use vertexQubits = Qubit[V];
within {
for v in 0 .. V - 1 {
Oracle_WeakColoring_OneVertex(V, edges, x, vertexQubits[v], v);
}
} apply {
Controlled X(vertexQubits, y);
}
}

// You might find these helper operations from earlier tasks useful.
operation Oracle_WeakColoring_OneVertex(
V : Int, edges: (Int, Int)[], x : Qubit[], y : Qubit, vertex : Int
) : Unit is Adj + Ctl {
let neighborEdges = Filtered((a, b) -> a == vertex or b == vertex, edges);
let nNeighbors = Length(neighborEdges);
use sameColorChecks = Qubit[nNeighbors];
within {
for ((a, b), checkQubit) in Zipped(neighborEdges, sameColorChecks) {
Oracle_ColorEquality(x[a * 2 .. a * 2 + 1],
x[b * 2 .. b * 2 + 1],
checkQubit);
}
} apply {
X(y);
if nNeighbors > 0 {
Controlled X(sameColorChecks, y);
}
}
}

operation Oracle_ColorEquality(x0 : Qubit[], x1 : Qubit[], y : Qubit) : Unit is Adj + Ctl {
within {
for i in 0..Length(x0) - 1 {
CNOT(x0[i], x1[i]);
}
} apply {
ApplyControlledOnInt(0, X, x1, y);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Kata.Verification {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Katas;

@EntryPoint()
operation CheckSolution() : Bool {
for (V, edges) in Most(ExampleGraphs()) {
if not CheckOracleRecognizesColoring(V, edges,
Kata.Oracle_WeakColoring, IsWeakColoringValid_Reference
) {
return false;
}
}

Message("Correct!");
true
}
}
15 changes: 15 additions & 0 deletions katas/content/solving_graph_coloring/weak_coloring/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
**Inputs:**

1. The number of vertices in the graph $V$ ($V \leq 6$).
2. An array of $E$ tuples of integers, representing the edges of the graph ($E \leq 12$).
Each tuple gives the indices of the start and the end vertices of the edge.
The vertices are numbered $0$ through $V - 1$.
3. An array of $2V$ qubits in an arbitrary state $\ket{x}$ representing the assigned coloring of the vertices using four colors ($nBits = 2$) in the same format as in the exercise "Read Coloring From a Qubit Array".
4. A qubit in an arbitrary state $\ket{y}$ (output/target qubit).

**Goal:**
Implement a quantum oracle which checks whether the given weak coloring is valid for the given graph,
that is, whether each vertex of the graph is either isolated or connected to at least one vertex of a different color.

Leave the qubits in the input register in the same state they started in.
Your solution should work on inputs in superposition, and not use any measurements.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
To implement this oracle, you need to check whether each vertex is colored correctly and combine the results of individual checks. You'll need to allocate an array of qubits, one per vertex, that will be flipped if the corresponding vertex is weakly colored.
This can be done easily using the `Oracle_WeakColoring_OneVertex` operation defined in the previous task. Then, you need to check if all qubits in the array is in state $\ket{1...1}$ using the Controlled $X$ gate; if they are, the coloring is valid.

As usual, remember to uncompute the changes to the auxiliary qubits after you evaluate the final result to leave them clean before their release.

@[solution]({
"id": "solving_graph_coloring__weak_coloring_quantum_solution",
"codePath": "./Solution.qs"
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Kata {
function IsWeakColoringValid(V : Int, edges: (Int, Int)[], colors: Int[]) : Bool {
// Implement your solution here...

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Kata {
function IsWeakColoringValid(V : Int, edges: (Int, Int)[], colors: Int[]) : Bool {
for vertex in 0 .. V - 1 {
mutable neighborCount = 0;
mutable hasDifferentNeighbor = false;

for (start, end) in edges {
if start == vertex or end == vertex {
set neighborCount += 1;
if colors[start] != colors[end] {
set hasDifferentNeighbor = true;
}
}
}

if neighborCount > 0 and not hasDifferentNeighbor {
return false;
}
}
return true;
}
}
Loading
Loading