forked from microsoft/QuantumKatas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Code.qs
217 lines (180 loc) · 8.81 KB
/
Code.qs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
//////////////////////////////////////////////////////////////////////
// This file contains pre-written code used by the tutorial notebooks.
// You should not modify anything in this file.
//////////////////////////////////////////////////////////////////////
namespace Quantum.Kata.ExploringGroversAlgorithm
{
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
//////////////////////////////////////////////////////////////////
// Part I. Quantum oracles for solving SAT problem
//////////////////////////////////////////////////////////////////
// Helper function to get the list of qubits used in the clause and the bitmask of whether they need to be flipped
function GetClauseQubits (queryRegister : Qubit[], clause : (Int, Bool)[]) : (Qubit[], Bool[]) {
mutable clauseQubits = [];
mutable flip = [];
for (index, isTrue) in clause {
// Add the variable used in the clause to the list of variables which we'll need to call the OR oracle
let qt = queryRegister[index];
set clauseQubits += [queryRegister[index]];
// If the negation of the variable is present in the formula, mark the qubit as needing a flip
set flip += [not isTrue];
}
return (clauseQubits, flip);
}
// ---------------------------------------------------------------------------------------------
// Oracle to evaluate one clause of a SAT formula
operation Oracle_SATClause (queryRegister : Qubit[],
target : Qubit,
clause : (Int, Bool)[]) : Unit is Adj {
let (clauseQubits, flip) = GetClauseQubits(queryRegister, clause);
// Actually calculate the clause (flip the necessary qubits, calculate OR, flip them back)
within {
ApplyPauliFromBitString(PauliX, true, flip, clauseQubits);
}
apply {
// First, flip target if all qubits are in |0⟩ state
(ControlledOnInt(0, X))(clauseQubits, target);
// Then flip target again to get negation
X(target);
}
}
// ---------------------------------------------------------------------------------------------
// Helper operation to evaluate all OR clauses given in the formula (independent on the number of variables in each clause)
operation EvaluateOrClauses (queryRegister : Qubit[],
ancillaRegister : Qubit[],
problem : (Int, Bool)[][]) : Unit is Adj {
for clauseIndex in 0..Length(problem)-1 {
Oracle_SATClause(queryRegister, ancillaRegister[clauseIndex], problem[clauseIndex]);
}
}
// ---------------------------------------------------------------------------------------------
// General SAT problem oracle: f(x) = ∧ᵢ (∨ₖ yᵢₖ), where yᵢₖ = either xᵢₖ or ¬xᵢₖ
operation Oracle_SAT (queryRegister : Qubit[],
target : Qubit,
problem : (Int, Bool)[][]) : Unit is Adj {
// Allocate qubits to store results of clauses evaluation
use ancillaRegister = Qubit[Length(problem)];
// Compute clauses, evaluate the overall formula as an AND oracle (can use reference depending on the implementation) and uncompute
within {
EvaluateOrClauses(queryRegister, ancillaRegister, problem);
}
apply {
Controlled X(ancillaRegister, target);
}
}
// ---------------------------------------------------------------------------------------------
function CreateOracleForSATInstance (problem : (Int, Bool)[][]) : ((Qubit[], Qubit) => Unit is Adj) {
return Oracle_SAT(_, _, problem);
}
//////////////////////////////////////////////////////////////////
// Part II. Grover's iteration
//////////////////////////////////////////////////////////////////
// Helper operation which converts marking oracle into phase oracle using an extra qubit
operation ApplyMarkingOracleAsPhaseOracle (markingOracle : ((Qubit[], Qubit) => Unit is Adj), register : Qubit[]) : Unit is Adj {
use target = Qubit();
// Put the target into the |-⟩ state and later back to |0⟩ so we can return it
within {
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
apply {
markingOracle(register, target);
}
}
// ---------------------------------------------------------------------------------------------
// Grover's algorithm loop: repeat Grover iteration the given number of times
operation GroversAlgorithm_Loop (register : Qubit[], oracle : ((Qubit[], Qubit) => Unit is Adj), iterations : Int) : Unit {
ApplyToEach(H, register);
for _ in 1 .. iterations {
// apply oracle
ApplyMarkingOracleAsPhaseOracle(oracle, register);
// apply inversion about the mean
within {
ApplyToEachA(H, register);
ApplyToEachA(X, register);
}
apply {
Controlled Z(Most(register), Tail(register));
}
}
}
//////////////////////////////////////////////////////////////////
// Part III. Helper functions for pretty printing
//////////////////////////////////////////////////////////////////
// A set of helper functions to pretty-print SAT formulas
function SATVariableAsString (var : (Int, Bool)) : String {
let (index, isTrue) = var;
return (isTrue ? "" | "¬") + $"x{index}";
}
function SATClauseAsString (clause : (Int, Bool)[]) : String {
mutable ret = SATVariableAsString(clause[0]);
for ind in 1 .. Length(clause) - 1 {
set ret = ret + " ∨ " + SATVariableAsString(clause[ind]);
}
return ret;
}
function SATInstanceAsString (instance : (Int, Bool)[][]) : String {
mutable ret = "(" + SATClauseAsString(instance[0]) + ")";
for ind in 1 .. Length(instance) - 1 {
set ret = ret + " ∧ (" + SATClauseAsString(instance[ind]) + ")";
}
return ret;
}
function VariableAssignmentAsString (variables : Bool[]) : String {
mutable ret = $"x0 = {variables[0]}";
for ind in 1 .. Length(variables) - 1 {
set ret = ret + $", x{ind} = {variables[ind]}";
}
return ret;
}
//////////////////////////////////////////////////////////////////
// Part IV. Helper functions for Python visualization notebook
//////////////////////////////////////////////////////////////////
// Helper function for computing the success probability of Grover's algorithm with the given number of iterations (and given formula)
operation SuccessProbability_SAT (N : Int, instance : (Int, Bool)[][], iter : Int) : Double {
let oracle = Oracle_SAT(_, _, instance);
mutable correct = 0;
use (register, answer) = (Qubit[N], Qubit());
for run in 1 .. 100 {
GroversAlgorithm_Loop(register, oracle, iter);
let res = MultiM(register);
oracle(register, answer);
if (MResetZ(answer) == One) {
set correct += 1;
}
ResetAll(register);
}
return IntAsDouble(correct) / 100.0;
}
// ---------------------------------------------------------------------------------------------
operation Oracle_SolutionCount (queryRegister : Qubit[], target : Qubit, nSol : Int) : Unit is Adj {
// Designate first nSol integers solutions (since we don't really care which ones are solutions)
for i in 0 .. nSol - 1 {
(ControlledOnInt(i, X))(queryRegister, target);
}
}
// Helper function for computing the success probability of Grover's algorithm with the given number of iterations (and given formula)
operation SuccessProbability_Sol (nQubit : Int, nSol : Int, iter : Int) : Double {
let oracle = Oracle_SolutionCount(_, _, nSol);
mutable correct = 0;
use (register, answer) = (Qubit[nQubit], Qubit());
for run in 1 .. 100 {
GroversAlgorithm_Loop(register, oracle, iter);
let res = MultiM(register);
oracle(register, answer);
if (MResetZ(answer) == One) {
set correct += 1;
}
ResetAll(register);
}
return IntAsDouble(correct) / 100.0;
}
}