forked from microsoft/Quantum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Shor.qs
278 lines (234 loc) · 13.5 KB
/
Shor.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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Samples.IntegerFactorization {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Oracles;
open Microsoft.Quantum.Characterization;
open Microsoft.Quantum.Diagnostics;
///////////////////////////////////////////////////////////////////////////////////////////////
// Introduction ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// This sample contains Q# code implementing Shor's quantum algorithm for
// factoring integers. The underlying modular arithmetic is implemented
// in phase encoding, based on a paper by Stephane Beauregard who gave a
// quantum circuit for factoring n-bit numbers that needs 2n+3 qubits and
// O(n³log(n)) elementary quantum gates.
/// # Summary
/// Uses Shor's algorithm to factor the parameter `number`
///
/// # Input
/// ## number
/// An integer to be factored
/// ## useRobustPhaseEstimation
/// If set to true, we use Microsoft.Quantum.Characterization.RobustPhaseEstimation and
/// Microsoft.Quantum.Characterization.QuantumPhaseEstimation otherwise
///
/// # Output
/// Pair of numbers p > 1 and q > 1 such that p⋅q = `number`
operation FactorInteger(number : Int, useRobustPhaseEstimation : Bool) : (Int, Int) {
// First check the most trivial case, if the provided number is even
if (number % 2 == 0) {
Message("An even number has been passed; 2 is the factor.");
return (number / 2, 2);
}
// Next try to guess a number co-prime to `number`
// Get a random integer in the interval [1,number-1]
let coprimeCandidate = RandomInt(number - 2) + 1;
// Check if the random integer indeed co-prime using
// Microsoft.Quantum.Math.IsCoprimeI.
// If true use Quantum algorithm for Period finding.
if (IsCoprimeI(coprimeCandidate, number)) {
// Print a message using Microsoft.Quantum.Intrinsic.Message
// indicating that we are doing something quantum.
Message($"Estimating period of {coprimeCandidate}");
// Call Quantum Period finding algorithm for
// `coprimeCandidate` mod `number`.
// Here we have a choice which Phase Estimation algorithm to use.
let period = EstimatePeriod(coprimeCandidate, number, useRobustPhaseEstimation);
// Period finding reduces to factoring only if period is even
if (period % 2 == 0) {
// Compute `coprimeCandidate` ^ `period/2` mod `number`
// using Microsoft.Quantum.Math.ExpModI.
let halfPower = ExpModI(coprimeCandidate, period / 2, number);
// If we are unlucky, halfPower is just -1 mod N,
// This is a trivial case not useful for factoring
if (halfPower != number - 1) {
// When the halfPower is not -1 mod N
// halfPower-1 or halfPower+1 share non-trivial divisor with `number`.
// We find a divisor Microsoft.Quantum.Math.GreatestCommonDivisorI.
let factor = MaxI(GreatestCommonDivisorI(halfPower - 1, number), GreatestCommonDivisorI(halfPower + 1, number));
// Return computed non-trivial factors.
return (factor, number / factor);
} else {
// Report the failure of hitting a trivial case.
// We have to start over again.
fail "Residue xᵃ = -1 (mod N) where a is a period.";
}
} else {
// When period is odd we have to pick another number to estimate
// period of and start over.
fail "Period is odd.";
}
}
// In this case we guessed a divisor by accident
else {
// Find a divisor using Microsoft.Quantum.Math.GreatestCommonDivisorI
let gcd = GreatestCommonDivisorI(number, coprimeCandidate);
// And do not forget to tell the user that we were lucky and didn't do anything
// quantum using Microsoft.Quantum.Intrinsic.Message
Message($"We have guessed a divisor of {number} to be {gcd} by accident.");
// Return the factorization
return (gcd, number / gcd);
}
}
/// # Summary
/// Interprets `target` as encoding unsigned little-endian integer k
/// and performs transformation |k⟩ ↦ |gᵖ⋅k mod N ⟩ where
/// p is `power`, g is `generator` and N is `modulus`.
///
/// # Input
/// ## generator
/// The unsigned integer multiplicative order ( period )
/// of which is being estimated. Must be co-prime to `modulus`.
/// ## modulus
/// The modulus which defines the residue ring Z mod `modulus`
/// in which the multiplicative order of `generator` is being estimated.
/// ## power
/// Power of `generator` by which `target` is multiplied.
/// ## target
/// Register interpreted as LittleEndian which is multiplied by
/// given power of the generator. The multiplication is performed modulo
/// `modulus`.
operation ApplyOrderFindingOracle(generator : Int, modulus : Int, power : Int, target : Qubit[])
: Unit
is Adj + Ctl {
// Check that the parameters satisfy the requirements.
Fact(IsCoprimeI(generator, modulus), "`generator` and `modulus` must be co-prime");
// The oracle we use for order finding essentially wraps
// Microsoft.Quantum.Arithmetic.MultiplyByModularInteger operation
// that implements |x⟩ ↦ |x⋅a mod N ⟩.
// We also use Microsoft.Quantum.Math.ExpModI to compute a by which
// x must be multiplied.
// Also note that we interpret target as unsigned integer
// in little-endian encoding by using Microsoft.Quantum.Arithmetic.LittleEndian
// type.
MultiplyByModularInteger(ExpModI(generator, power, modulus), modulus, LittleEndian(target));
}
/// # Summary
/// Finds a multiplicative order of the generator
/// in the residue ring Z mod `modulus`.
///
/// # Input
/// ## generator
/// The unsigned integer multiplicative order ( period )
/// of which is being estimated. Must be co-prime to `modulus`.
/// ## modulus
/// The modulus which defines the residue ring Z mod `modulus`
/// in which the multiplicative order of `generator` is being estimated.
/// ## useRobustPhaseEstimation
/// If set to true, we use Microsoft.Quantum.Characterization.RobustPhaseEstimation and
/// Microsoft.Quantum.Characterization.QuantumPhaseEstimation
///
/// # Output
/// The period ( multiplicative order ) of the generator mod `modulus`
operation EstimatePeriod(generator : Int, modulus : Int, useRobustPhaseEstimation : Bool) : Int {
// Here we check that the inputs to the EstimatePeriod operation are valid.
EqualityFactB(IsCoprimeI(generator, modulus), true, "`generator` and `modulus` must be co-prime");
// The variable that stores the divisor of the generator period found so far.
mutable result = 1;
// Number of bits in the modulus with respect to which we are estimating the period.
let bitsize = BitSizeI(modulus);
// The EstimatePeriod operation estimates the period r by finding an
// approximation k/2^bitsPrecision to a fraction s/r where s is some integer.
// Note that if s and r have common divisors we will end up recovering a divisor of r
// and not r itself. However, if we recover enough divisors of r
// we recover r itself pretty soon.
// Number of bits of precision with which we need to estimate s/r to recover period r.
// using continued fractions algorithm.
let bitsPrecision = 2 * bitsize + 1;
repeat {
// The variable that stores numerator of dyadic fraction k/2^bitsPrecision
// approximating s/r
mutable dyadicFractionNum = 0;
// Allocate qubits for the superposition of eigenstates of
// the oracle that is used in period finding
using (eigenstateRegister = Qubit[bitsize]) {
// Initialize eigenstateRegister to 1 which is a superposition of
// the eigenstates we are estimating the phases of.
// We first interpret the register as encoding unsigned integer
// in little endian encoding.
let eigenstateRegisterLE = LittleEndian(eigenstateRegister);
ApplyXorInPlace(1, eigenstateRegisterLE);
// An oracle of type Microsoft.Quantum.Oracles.DiscreteOracle
// that we are going to use with phase estimation methods below.
let oracle = DiscreteOracle(ApplyOrderFindingOracle(generator, modulus, _, _));
// Find the numerator of a dyadic fraction that approximates
// s/r where r is the multiplicative order ( period ) of g
if (useRobustPhaseEstimation) {
// Use Microsoft.Quantum.Characterization.RobustPhaseEstimation to estimate s/r.
// RobustPhaseEstimation needs only one extra qubit, but requires
// several calls to the oracle
let phase = RobustPhaseEstimation(bitsPrecision, oracle, eigenstateRegisterLE!);
// Compute the numerator k of dyadic fraction k/2^bitsPrecision
// approximating s/r. Note that phase estimation project on the eigenstate
// corresponding to random s.
set dyadicFractionNum = Round(((phase * IntAsDouble(2 ^ bitsPrecision)) / 2.0) / PI());
} else {
// Use Microsoft.Quantum.Characterization.QuantumPhaseEstimation to estimate s/r.
// When using QuantumPhaseEstimation we will need extra `bitsPrecision`
// qubits
using (register = Qubit[bitsPrecision]) {
let dyadicFractionNumerator = LittleEndian(register);
// The register that will contain the numerator k of
// dyadic fraction k/2^bitsPrecision. The numerator is unsigned
// integer encoded in big-endian format. This is indicated by
// use of Microsoft.Quantum.Arithmetic.BigEndian type.
QuantumPhaseEstimation(oracle, eigenstateRegisterLE!, LittleEndianAsBigEndian(dyadicFractionNumerator));
// Directly measure the numerator k of dyadic fraction k/2^bitsPrecision
// approximating s/r. Note that phase estimation project on
// the eigenstate corresponding to random s.
set dyadicFractionNum = MeasureInteger(dyadicFractionNumerator);
}
}
// Return all the qubits used for oracle's eigenstate back to 0 state
// using Microsoft.Quantum.Intrinsic.ResetAll
ResetAll(eigenstateRegister);
}
// Sometimes we might measure all zeros state in Phase Estimation.
// This is a failure and we need to start all over.
if (dyadicFractionNum == 0) {
fail "We measured 0 for the numerator";
}
// This will print our estimate of s/r to the standard output
// using Microsoft.Quantum.Intrinsic.Message
Message($"Estimated eigenvalue is {dyadicFractionNum}/2^{bitsPrecision}.");
// Now we use Microsoft.Quantum.Math.ContinuedFractionConvergentI
// function to recover s/r from dyadic fraction k/2^bitsPrecision.
let (numerator, period) = (ContinuedFractionConvergentI(Fraction(dyadicFractionNum, 2 ^ bitsPrecision), modulus))!;
// ContinuedFractionConvergentI does not guarantee the signs of the numerator
// and denominator. Here we make sure that both are positive using
// AbsI.
let (numeratorAbs, periodAbs) = (AbsI(numerator), AbsI(period));
// Use Microsoft.Quantum.Intrinsic.Message to output the
// period divisor and the eigenstate number
Message($"Estimated divisor of period is {periodAbs}, " + $" we have projected on eigenstate marked by {numeratorAbs}.");
// Update the result variable by including newly found divisor.
// Uses Microsoft.Quantum.Math.GreatestCommonDivisorI function from Microsoft.Quantum.Math.
set result = (periodAbs * result) / GreatestCommonDivisorI(result, periodAbs);
}
until (ExpModI(generator, result, modulus) == 1)
fixup {
// Above we checked if we have found actual period, or only the divisor of it.
// If the period was found, loop terminates.
// If we have not found the period, output message about it to
// standard output and try again.
Message("It looks like the period has divisors and we have " + "found only a divisor of the period. Trying again ...");
}
// Return found period.
return result;
}
}