This repository has been archived by the owner on Jan 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 923
/
Copy pathShor.qs
380 lines (343 loc) · 15.7 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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.Quantum.Samples.IntegerFactorization {
open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Characterization;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Oracles;
open Microsoft.Quantum.Random;
///////////////////////////////////////////////////////////////////////////////////////////////
// Introduction ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// This sample contains Q# code implementing Shor's quantum algorithm for
// factoring integers. The implementation is 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.
// Instead of using Fourier based arithmetic, we use the addition circuit
// by Gidney, which makes use of auxiliary qubits but is expected to
// outperform Fourier based arithmetic in a fault-tolerant setting due to
// the cost of rotation synthesis and magic state distillation.
/// # Summary
/// Uses Shor's algorithm to factor the parameter `number`
///
/// # Input
/// ## number
/// A semiprime integer to be factored
///
/// # Output
/// Pair of numbers p > 1 and q > 1 such that p⋅q = `number`
operation FactorSemiprimeInteger(number : Int)
: (Int, Int) {
// First check the most trivial case, if the provided number is even
if number % 2 == 0 {
Message("An even number has been given; 2 is a factor.");
return (number / 2, 2);
}
// These mutables will keep track of if we found the factors,
// and if so, what they are. The default value for the factors
// is (1,1).
mutable foundFactors = false;
mutable factors = (1, 1);
repeat {
// Next try to guess a number co-prime to `number`
// Get a random integer in the interval [1,number-1]
let generator = DrawRandomInt(1, number - 1);
// Check if the random integer indeed co-prime using
// Microsoft.Quantum.Math.IsCoprimeI.
// If true use Quantum algorithm for Period finding.
if IsCoprimeI(generator, number) {
// Print a message using Microsoft.Quantum.Intrinsic.Message
// indicating that we are doing something quantum.
Message($"Estimating period of {generator}");
// Call Quantum Period finding algorithm for
// `generator` mod `number`.
let period = EstimatePeriod(generator, number);
// Set the flag and factors values if the continued fractions
// classical algorithm succeeds.
set (foundFactors, factors) = MaybeFactorsFromPeriod(number, generator, period);
}
// In this case, we guessed a divisor by accident.
else {
// Find a divisor using Microsoft.Quantum.Math.GreatestCommonDivisorI
let gcd = GreatestCommonDivisorI(number, generator);
// Don't forget to tell the user that we were lucky and didn't do anything
// quantum by using Microsoft.Quantum.Intrinsic.Message.
Message($"We have guessed a divisor of {number} to be {gcd} by accident.");
// Set the flag `foundFactors` to true, indicating that we succeeded in finding
// factors.
set foundFactors = true;
set factors = (gcd, number / gcd);
}
}
until foundFactors
fixup {
Message("The estimated period did not yield a valid factor, trying again.");
}
// Return the factorization
return factors;
}
/// # 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 implements |x⟩ ↦ |x⋅a mod N⟩.
// The implementation details can be found in `Modular.qs`, `Compare.qs`
// and `Add.qs`.
// 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.
ModularMultiplyByConstant(IntAsBigInt(modulus),
IntAsBigInt(ExpModI(generator, power, modulus)),
LittleEndian(target));
}
/// # Summary
/// Interprets `target` as encoding an unsigned little-endian integer k
/// and performs transformation |k⟩ ↦ |gᵖ⋅k mod N⟩ where
/// p is `power`, g is `generator` and N is `modulus` using
/// Fourier based arithmetic.
///
/// # 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 ApplyOrderFindingOracleFourierArithmetic(
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 implements |x⟩ ↦ |x⋅a mod N ⟩.
// Here, we forward to the library implementation, which uses Fourier based
// arithmetic.
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.
///
/// # Output
/// The period ( multiplicative order ) of the generator mod `modulus`
operation EstimatePeriod(
generator : Int, modulus : Int
)
: Int {
// Here we check that the inputs to the EstimatePeriod operation are valid.
Fact(IsCoprimeI(generator, modulus), "`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^(bits precision) 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;
// A variable that stores our current estimate for the frequency
// of the form s/r.
mutable frequencyEstimate = 0;
set frequencyEstimate = EstimateFrequency(
generator, modulus, bitsize
);
if frequencyEstimate != 0 {
set result = PeriodFromFrequency(modulus, frequencyEstimate, bitsPrecision, result);
}
else {
Message("The estimated frequency was 0, trying again.");
}
return result;
}
/// # Summary
/// Estimates the frequency of a 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.
/// ## bitsize
/// Number of bits needed to represent the modulus.
///
/// # Output
/// The numerator k of dyadic fraction k/2^bitsPrecision
/// approximating s/r.
operation EstimateFrequency(
generator : Int,
modulus : Int,
bitsize : Int
)
: Int {
mutable frequencyEstimate = 0;
let bitsPrecision = 2 * bitsize + 1;
// Allocate qubits for the superposition of eigenstates of
// the oracle that is used in period finding.
use 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 an unsigned integer
// in little endian encoding.
let eigenstateRegisterLE = LittleEndian(eigenstateRegister);
ApplyXorInPlace(1, eigenstateRegisterLE);
let oracle = ApplyOrderFindingOracle(generator, modulus, _, _);
// Use phase estimation with a semiclassical Fourier transform to
// estimate the frequency.
use c = Qubit();
for idx in bitsPrecision - 1..-1..0 {
within {
H(c);
} apply {
Controlled oracle([c], (1 <<< idx, eigenstateRegisterLE!));
R1Frac(frequencyEstimate, bitsPrecision - 1 - idx, c);
}
if MResetZ(c) == One {
set frequencyEstimate += 1 <<< (bitsPrecision - 1 - idx);
}
}
// Return all the qubits used for oracle's eigenstate back to 0 state
// using Microsoft.Quantum.Intrinsic.ResetAll.
ResetAll(eigenstateRegister);
return frequencyEstimate;
}
/// # Summary
/// Find the period of a number from an input frequency.
///
/// # Input
/// ## modulus
/// The modulus which defines the residue ring Z mod `modulus`
/// in which the multiplicative order of `generator` is being estimated.
/// ## frequencyEstimate
/// The frequency that we want to convert to a period.
/// ## bitsPrecision
/// Number of bits of precision with which we need to
/// estimate s/r to recover period r using continued
/// fractions algorithm.
/// ## currentDivisor
/// The divisor of the generator period found so far.
///
/// # Output
/// The period as calculated from the estimated frequency via
/// the continued fractions algorithm.
///
/// # See Also
/// - Microsoft.Quantum.Math.ContinuedFractionConvergentI
function PeriodFromFrequency(
modulus : Int,
frequencyEstimate : Int,
bitsPrecision : Int,
currentDivisor : Int
)
: Int {
// Now we use Microsoft.Quantum.Math.ContinuedFractionConvergentI
// function to recover s/r from dyadic fraction k/2^bitsPrecision.
let (numerator, period) = (ContinuedFractionConvergentI(Fraction(frequencyEstimate, 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));
// Return the newly found divisor.
// Uses Microsoft.Quantum.Math.GreatestCommonDivisorI function from Microsoft.Quantum.Math.
return (periodAbs * currentDivisor) / GreatestCommonDivisorI(currentDivisor, periodAbs);
}
/// # Summary
/// Tries to find the factors of `modulus` given a `period` and `generator`.
///
/// # Input
/// ## modulus
/// The modulus which defines the residue ring Z mod `modulus`
/// in which the multiplicative order of `generator` is being estimated.
/// ## generator
/// The unsigned integer multiplicative order ( period )
/// of which is being estimated. Must be co-prime to `modulus`.
/// ## period
/// The estimated period ( multiplicative order ) of the generator mod `modulus`.
///
/// # Output
/// A tuple of a flag indicating whether factors were found successfully,
/// and a pair of integers representing the factors that were found.
/// Note that the second output is only meaningful when the first
/// output is `true`.
///
/// # See Also
/// - Microsoft.Quantum.Math.GreatestCommonDivisorI
function MaybeFactorsFromPeriod(modulus : Int, generator : Int, period : Int)
: (Bool, (Int, Int)) {
// Period finding reduces to factoring only if period is even
if period % 2 == 0 {
// Compute `generator` ^ `period/2` mod `number`
// using Microsoft.Quantum.Math.ExpModI.
let halfPower = ExpModI(generator, period / 2, modulus);
// If we are unlucky, halfPower is just -1 mod N,
// which is a trivial case and not useful for factoring.
if halfPower != modulus - 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, modulus),
GreatestCommonDivisorI(halfPower + 1, modulus)
);
// Add a flag that we found the factors, and return computed non-trivial factors.
return (true, (factor, modulus / factor));
} else {
// Return a flag indicating we hit a trivial case and didn't get any factors.
return (false, (1, 1));
}
} else {
// When period is odd we have to pick another generator to estimate
// period of and start over.
Message("Estimated period was odd, trying again.");
return (false, (1, 1));
}
}
}