Skip to content

Commit

Permalink
Add more samples (#1010)
Browse files Browse the repository at this point in the history
This adds some additional samples folks can use for estimation.
  • Loading branch information
swernli authored Jan 11, 2024
1 parent 6a450b9 commit bf6c787
Show file tree
Hide file tree
Showing 4 changed files with 385 additions and 223 deletions.
251 changes: 28 additions & 223 deletions samples/algorithms/Shor.qs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
///
/// This Q# program implements Shor's algorithm.
namespace Sample {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Random;
open Microsoft.Quantum.Math;
Expand Down Expand Up @@ -139,7 +140,7 @@ namespace Sample {
Message($"Found factor={factor}");
return (true, (factor, modulus / factor));
}
}
}
// Return a flag indicating we hit a trivial case and didn't get
// any factors.
Message($"Found trivial factors.");
Expand Down Expand Up @@ -297,64 +298,38 @@ namespace Sample {
}

/// # 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`.
/// 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`.
/// 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.
/// 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 little-endian 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(
GreatestCommonDivisorI(generator, modulus) == 1,
"`generator` and `modulus` must be co-prime");

// The oracle we use for order finding implements |x⟩ ↦ |x⋅a mod N⟩.
// We also use `ExpModI` to compute a by which x must be multiplied.
// Also note that we interpret target as unsigned integer in
// little-endian fromat.
/// Register interpreted as little-endian which is multiplied by
/// given power of the generator. The multiplication is performed modulo
/// `modulus`.
internal operation ApplyOrderFindingOracle(
generator : Int, modulus : Int, power : Int, target : Qubit[]
)
: Unit
is Adj + Ctl {
// The oracle we use for order finding implements |x⟩ ↦ |x⋅a mod N⟩. We
// also use `ExpModI` to compute a by which x must be multiplied. Also
// note that we interpret target as unsigned integer in little-endian
// format.
ModularMultiplyByConstant(
modulus,
ExpModI(generator, power, modulus),
target);
}

//
// Arithmetic helper functions to implement order-finding oracle.
//

/// # Summary
/// Returns the number of trailing zeroes of a number.
///
/// ## Example
/// let zeroes = NTrailingZeroes(21); // NTrailingZeroes(0b1101) = 0
/// let zeroes = NTrailingZeroes(20); // NTrailingZeroes(0b1100) = 2
function NTrailingZeroes(number : Int) : Int {
Fact(number != 0, "NTrailingZeroes: number cannot be 0.");
mutable nZeroes = 0;
mutable copy = number;
while (copy % 2 == 0) {
set nZeroes += 1;
set copy /= 2;
}
return nZeroes;
}

/// # Summary
/// Performs modular in-place multiplication by a classical constant.
///
Expand All @@ -370,7 +345,7 @@ namespace Sample {
/// Constant by which to multiply |𝑦⟩
/// ## y
/// Quantum register of target
operation ModularMultiplyByConstant(modulus : Int, c : Int, y : Qubit[])
internal operation ModularMultiplyByConstant(modulus : Int, c : Int, y : Qubit[])
: Unit is Adj + Ctl {
use qs = Qubit[Length(y)];
for idx in IndexRange(y) {
Expand All @@ -395,7 +370,6 @@ namespace Sample {
/// Performs modular in-place addition of a classical constant into a
/// quantum register.
///
/// # Description
/// Given the classical constants `c` and `modulus`, and an input quantum
/// register |𝑦⟩ in little-endian format, this operation computes
/// `(x+c) % modulus` into |𝑦⟩.
Expand All @@ -407,7 +381,7 @@ namespace Sample {
/// Constant to add to |𝑦⟩
/// ## y
/// Quantum register of target
operation ModularAddConstant(modulus : Int, c : Int, y : Qubit[])
internal operation ModularAddConstant(modulus : Int, c : Int, y : Qubit[])
: Unit is Adj + Ctl {
body (...) {
Controlled ModularAddConstant([], (modulus, c, y));
Expand All @@ -426,184 +400,15 @@ namespace Sample {
within {
Controlled X(ctrls, control);
} apply {
Controlled ModularAddConstant(
[control],
(modulus, c, y));
Controlled ModularAddConstant([control], (modulus, c, y));
}
} else {
use carry = Qubit();
Controlled AddConstant(
ctrls, (c, y + [carry]));
Controlled Adjoint AddConstant(
ctrls, (modulus, y + [carry]));
Controlled AddConstant(
[carry], (modulus, y));
Controlled CompareGreaterThanOrEqualConstant(
ctrls, (c, y, carry));
}
}
}

/// # Summary
/// Performs in-place addition of a constant into a quantum register.
///
/// # Description
/// Given a non-empty quantum register |𝑦⟩ of length 𝑛+1 and a positive
// constant 𝑐 < 2ⁿ, computes |𝑦 + c⟩ into |𝑦⟩.
///
/// # Input
/// ## c
/// Constant number to add to |𝑦⟩.
/// ## y
/// Quantum register of second summand and target; must not be empty.
operation AddConstant(c : Int, y : Qubit[]) : Unit is Adj + Ctl {
// We are using this version instead of the library version that is
// based on Fourier angles to show an advantage of sparse simulation
// in this sample.
let n = Length(y);
Fact(n > 0, "Bit width must be at least 1");
Fact(c >= 0, "constant must not be negative");
Fact(c < 2^n, "constant must be smaller than {2^n)}");
if c != 0 {
// If c has j trailing zeroes than the j least significant bits of y
// won't be affected by the addition and can therefore be ignored by
// applying the addition only to the other qubits and shifting c
// accordingly.
let j = NTrailingZeroes(c);
use x = Qubit[n - j];
within {
ApplyXorInPlace(c >>> j, x);
} apply {
IncByLE(x, y[j...]);
}
}
}

/// # Summary
/// Performs greater-than-or-equals comparison to a constant.
///
/// # Description
/// Toggles output qubit `target` if and only if input register `x` is
/// greater than or equal to `c`.
///
/// # Input
/// ## c
/// Constant value for comparison.
/// ## x
/// Quantum register to compare against.
/// ## target
/// Target qubit for comparison result.
///
/// # Reference
/// This construction is described in [Lemma 3, arXiv:2201.10200]
operation CompareGreaterThanOrEqualConstant(
c : Int,
x : Qubit[],
target : Qubit)
: Unit is Adj+Ctl {
let bitWidth = Length(x);
if c == 0 {
X(target);
} elif c >= (2^bitWidth) {
// do nothing
} elif c == (2^(bitWidth - 1)) {
CNOT(Tail(x), target);
} else {
// normalize constant
let l = NTrailingZeroes(c);
let cNormalized = c >>> l;
let xNormalized = x[l...];
let bitWidthNormalized = Length(xNormalized);
use qs = Qubit[bitWidthNormalized - 1];
let cs1 = [Head(xNormalized)] + Most(qs);
Fact(Length(cs1) == Length(qs),
"Arrays should be of the same length.");
within {
for i in 0..Length(cs1)-1 {
let op =
cNormalized &&& (1 <<< (i+1)) != 0 ?
ApplyAnd | ApplyOr;
op(cs1[i], xNormalized[i+1], qs[i]);
}
} apply {
CNOT(Tail(qs), target);
}
}
}

/// # Summary
/// Inverts a given target qubit if and only if both control qubits are in
/// the 1 state, using measurement to perform the adjoint operation.
///
/// # Description
/// Inverts `target` if and only if both controls are 1, but assumes that
/// `target` is in state 0. The operation has T-count 4, T-depth 2 and
/// requires no helper qubit, and may therefore be preferable to a CCNOT
/// operation, if `target` is known to be 0.
/// The adjoint of this operation is measurement based and requires no T
/// gates.
/// Although the Toffoli gate (CCNOT) will perform faster in in simulations,
/// this version has lower T gate requirements.
///
/// # Input
/// ## control1
/// First control qubit
/// ## control2
/// Second control qubit
/// ## target
/// Target auxiliary qubit; must be in state 0
///
/// # References
/// - Cody Jones: "Novel constructions for the fault-tolerant
/// Toffoli gate",
/// Phys. Rev. A 87, 022328, 2013
/// [arXiv:1212.5069](https://arxiv.org/abs/1212.5069)
/// doi:10.1103/PhysRevA.87.022328
/// - Craig Gidney: "Halving the cost of quantum addition",
/// Quantum 2, page 74, 2018
/// [arXiv:1709.06648](https://arxiv.org/abs/1709.06648)
/// doi:10.1103/PhysRevA.85.044302
/// - Mathias Soeken: "Quantum Oracle Circuits and the Christmas
/// Tree Pattern",
/// [Blog article from December 19, 2019](https://msoeken.github.io/blog_qac.html)
/// (note: explains the multiple controlled construction)
operation ApplyAnd(control1 : Qubit, control2 : Qubit, target : Qubit)
: Unit is Adj {
body (...) {
H(target);
T(target);
CNOT(control1, target);
CNOT(control2, target);
within {
CNOT(target, control1);
CNOT(target, control2);
}
apply {
Adjoint T(control1);
Adjoint T(control2);
T(target);
}
H(target);
S(target);
}
adjoint (...) {
H(target);
if (M(target) == One) {
X(target);
CZ(control1, control2);
Controlled IncByI(ctrls, (c, y + [carry]));
Controlled Adjoint IncByI(ctrls, (modulus, y + [carry]));
Controlled IncByI([carry], (modulus, y));
Controlled ApplyIfLessOrEqualL(ctrls, (X, IntAsBigInt(c), y, carry));
}
}
}

/// # Summary
/// Applies X to the target if any of the controls are 1.
operation ApplyOr(control1 : Qubit, control2 : Qubit, target : Qubit)
: Unit is Adj {
within {
ApplyToEachA(X, [control1, control2]);
} apply {
ApplyAnd(control1, control2, target);
X(target);
}
}
}
Loading

0 comments on commit bf6c787

Please sign in to comment.