Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Less-than comparison with a constant #318

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
49 changes: 49 additions & 0 deletions Standard/src/Arithmetic/Arithmetic.qs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ namespace Microsoft.Quantum.Arithmetic {
/// An integer which is assumed to be non-negative.
/// ## target
/// A quantum register which is used to store `value` in little-endian encoding.
///
/// # Example
/// ```Q#
/// using (qs = Qubit[6]) {
/// // prepare qs in state |42>
/// ApplyXorInPlace(42, LittleEndian(qs));
/// // check will pass
/// EqualityFactI(MeasureInteger(LittleEndian(qs)), 42);
/// }
/// ```
///
/// # See Also
/// - Microsoft.Quantum.Arithmetic.ApplyXorInPlaceL
operation ApplyXorInPlace(value : Int, target : LittleEndian)
: Unit is Adj + Ctl {
ApplyToEachCA(
Expand All @@ -32,6 +45,42 @@ namespace Microsoft.Quantum.Arithmetic {
);
}

/// # Summary
/// Applies a bitwise-XOR operation between a classical integer and a
/// big integer represented by a register of qubits.
///
/// # Description
/// Applies `X` operations to qubits in a little-endian register based on
/// 1 bits in a big integer.
///
/// Let us denote `value` by a and let y be an unsigned integer encoded in `target`,
/// then `InPlaceXorLE` performs an operation given by the following map:
/// $\ket{y}\rightarrow \ket{y\oplus a}$ , where $\oplus$ is the bitwise exclusive OR operator.
///
/// # Input
/// ## value
/// A big integer which is assumed to be non-negative.
/// ## target
/// A quantum register which is used to store `value` in little-endian encoding.
///
/// # Example
/// ```Q#
/// using (qs = Qubit[6]) {
/// // prepare qs in state |42>, using big integer
/// ApplyXorInPlaceL(42L, LittleEndian(qs));
/// }
/// ```
///
/// # See Also
/// - Microsoft.Quantum.Arithmetic.ApplyXorInPlace
operation ApplyXorInPlaceL(value : BigInt, target : LittleEndian)
: Unit is Adj + Ctl {
ApplyToEachCA(
CControlledCA(X),
Zip(BigIntAsBoolArray(value), target!)
);
}

/// # Summary
/// Applies the three-qubit majority operation in-place on a register of
/// qubits.
Expand Down
119 changes: 109 additions & 10 deletions Standard/src/Arithmetic/Comparators.qs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
// Licensed under the MIT License.

namespace Microsoft.Quantum.Arithmetic {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Logical;

/// # Summary
/// This operation tests if an integer represented by a register of qubits
Expand Down Expand Up @@ -36,24 +39,120 @@ namespace Microsoft.Quantum.Arithmetic {
/// https://arxiv.org/abs/quant-ph/0410184
operation CompareUsingRippleCarry(x : LittleEndian, y : LittleEndian, output : Qubit)
: Unit is Adj + Ctl {
if (Length(x!) != Length(y!)) {
fail "Size of integer registers must be equal.";
}
EqualityFactI(Length(x!), Length(y!), "Size of integer registers must be equal.");

using (auxiliary = Qubit()) {
within {
let nQubitsX = Length(x!);

// Take 2's complement
ApplyToEachCA(X, x! + [auxiliary]);
ApplyToEachA(X, x! + [auxiliary]);

ApplyMajorityInPlace(x![0], [y![0], auxiliary]);
ApplyToEachCA(MAJ, Zip3(Most(x!), Rest(y!), Rest(x!)));
// propagate carrys
ApplyToEachA(MAJ, Zip3([auxiliary] + Most(x!), y!, x!));
} apply {
X(output);
CNOT(Tail(x!), output);
}
}
}

/// # Summary
/// This operation tests if an integer represented by a register of qubits is
/// less than a big integer provided as a constant.
///
/// # Description
/// Given two integers `x` and `c`, `x` stored in a qubit register, and `c` being
/// a big integer constant, this operation checks if they satisfy `x < c`. If true,
/// the output qubit is changed to state $\ket 1$. The output qubit is assumed to
/// be in state $\ket 0$ when the operation is being called.
///
/// # Input
/// ## c
/// Non-negative constant number to compared to
/// ## x
/// Number in qubit register to compare
/// ## output
/// Qubit that stores the result of comparison (must be initialized to $\ket 0$)
///
/// # Remark
/// This operation applies several optimizations in addition to the construction
/// described in the original work. Special cases are applied if `c` is $0$,
/// `c` is $2^n$, or `c` is $2^{n-1}$, where $n$ is the number of bits in `x`.
/// Qubits and AND gates are saved for each trailing `0` in the bit representation of
/// `c`. Further, one AND gate is saved for the least significant bit in `c`, which
/// is 1, after the trailing `0`s have been removed. Further, all qubits associated
/// to constant inputs in the construction of the original work are propagated and
/// not allocated in this implementation.
///
/// # References
/// - Qubitization of Arbitrary Basis Quantum Chemistry Leveraging Sparsity and Low Rank Factorization
/// Dominic W. Berry, Craig Gidney, Mario Motta, Jarrod R. McClean, Ryan Babbush
/// Quantum 3, 208 (2019)
/// https://arxiv.org/abs/1902.02134v4
operation CompareLessThanConstantUsingRippleCarry(c : BigInt, x : LittleEndian, output : Qubit)
: Unit is Adj+Ctl {
body (...) {
let bitwidth = Length(x!);
AssertAllZero([output]);
Fact(c >= 0L, $"Constant input `c` must not be negative, but was {c}");

if (c == 0L) {
// do nothing; output stays 0
} elif (c >= (1L <<< bitwidth)) {
X(output);
} elif (c == (1L <<< (bitwidth - 1))) {
CNOT(Tail(x!), output);
X(output);
} else {
let bits = BigIntAsBoolArray(c);
let l = IndexOf(Identity<Bool>, bits);
using (tmpAnd = Qubit[bitwidth - 2 - l]) {
let tmpCarry = x![l..l] + tmpAnd;
within {
ApplyPauliFromBitString(PauliX, true, bits, x!);
ApplyToEachA(ApplyAndOrStep, Zip4(bits[l + 1...], Most(tmpCarry), x![l + 1...], Rest(tmpCarry)));
} apply {
ApplyAndOrStep(bits[bitwidth - 1], Tail(tmpCarry), Tail(x!), output);
}
}
}
}
adjoint self;

controlled (controls, ...) {
using (q = Qubit()) {
within {
CompareLessThanConstantUsingRippleCarry(c, x, q);
} apply {
if (Length(controls) == 1) {
ApplyAnd(Head(controls), q, output);
} else {
Controlled X(controls + [q], output);
}
}
}
}
adjoint controlled self;
}

/// # Summary
/// Applies to input-transformed AND or OR gate
///
/// # Description
/// Given a $\ket 0$ initialized `target`, applies
/// $a \land \bar b$, if `isOr` is `false`, and
/// $a \lor b$, if `isOr` is `true`
///
/// # Remark
/// The OR gate is realized using `ApplyAnd`
internal operation ApplyAndOrStep(isOr : Bool, a : Qubit, b : Qubit, target : Qubit)
: Unit is Adj {
within {
ApplyIfA(X, isOr, a);
X(b);
} apply {
ApplyAnd(a, b, target);
ApplyIfA(X, isOr, target);
}
}

}
86 changes: 85 additions & 1 deletion Standard/tests/ArithmeticTests.qs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
// Licensed under the MIT License.
namespace Microsoft.Quantum.ArithmeticTests {
open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Measurement;


operation InPlaceXorTestHelper (testValue : Int, numberOfQubits : Int) : Unit {
Expand Down Expand Up @@ -173,7 +176,88 @@ namespace Microsoft.Quantum.ArithmeticTests {
}
}
}


@Test("ToffoliSimulator")
operation TestCompareUsingRippleCarry() : Unit {
TestCompareUsingRippleCarryForBitWidth(3);
TestCompareUsingRippleCarryForBitWidth(4);
}

internal operation TestCompareUsingRippleCarryForBitWidth(bitwidth : Int) : Unit {
using ((input1, input2, output) = (Qubit[bitwidth], Qubit[bitwidth], Qubit())) {
let inputReg1 = LittleEndian(input1);
let inputReg2 = LittleEndian(input2);
for (qinput1 in 0..2^bitwidth - 1) {
for (qinput2 in 0..2^bitwidth - 1) {
within {
ApplyXorInPlace(qinput1, inputReg1);
ApplyXorInPlace(qinput2, inputReg2);
} apply {
CompareUsingRippleCarry(inputReg1, inputReg2, output);
EqualityFactB(IsResultOne(MResetZ(output)), qinput1 > qinput2, $"Unexpected result for qinput1 = {qinput1} and qinput2 = {qinput2}");
}
}
}
}
}

@Test("ResourcesEstimator")
operation TestCompareUsingRippleCarryOperationCalls() : Unit {
let bitwidth = 4;
within {
AllowAtMostNCallsCA(2 * bitwidth, CCNOT, $"Too many CCNOT operations for bitwidth {bitwidth}");
} apply {
using ((input1, input2, output) = (Qubit[bitwidth], Qubit[bitwidth], Qubit())) {
within {
AllowAtMostNQubits(1, $"Too many qubits allocated for bitwidth {bitwidth}");
} apply {
CompareUsingRippleCarry(LittleEndian(input1), LittleEndian(input2), output);
}
}
}
}

@Test("ToffoliSimulator")
operation TestCompareLessThanConstantUsingRippleCarry() : Unit {
TestCompareLessThanConstantUsingRippleCarryForBitWidth(3);
TestCompareLessThanConstantUsingRippleCarryForBitWidth(4);
}

internal operation TestCompareLessThanConstantUsingRippleCarryForBitWidth(bitwidth : Int) : Unit {
using ((input, output) = (Qubit[bitwidth], Qubit())) {
let inputReg = LittleEndian(input);
for (qinput in 0..2^bitwidth - 1) {
for (cinput in 0..2^bitwidth - 1) {
within {
ApplyXorInPlace(qinput, inputReg);
} apply {
CompareLessThanConstantUsingRippleCarry(IntAsBigInt(cinput), inputReg, output);
EqualityFactB(IsResultOne(MResetZ(output)), qinput < cinput, $"Unexpected result for cinput = {cinput} and qinput = {qinput}");
}
}
}
}
}

@Test("ResourcesEstimator")
operation TestCompareLessThanConstantUsingRippleCarryOperationCalls() : Unit {
let tCounts = [0, 12, 8, 12, 4, 12, 8, 12, 0, 12, 8, 12, 4, 12, 8, 12, 0];
let qubitCounts = [0, 2, 1, 2, 0, 2, 1, 2, 0, 2, 1, 2, 0, 2, 1, 2, 0];

for ((idx, (tCount, qubitCount)) in Enumerated(Zip(tCounts, qubitCounts))) {
within {
AllowAtMostNCallsCA(tCount, T, $"Too many T operations for constant {idx}");
} apply {
using ((input, output) = (Qubit[4], Qubit())) {
within {
AllowAtMostNQubits(qubitCount, $"Too many qubits allocated for constant {idx}");
} apply {
CompareLessThanConstantUsingRippleCarry(IntAsBigInt(idx), LittleEndian(input), output);
}
}
}
}
}
}


37 changes: 1 addition & 36 deletions Standard/tests/QubitizationTests.qs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Microsoft.Quantum.Tests {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Measurement;

// BlockEncoding.qs tests

Expand Down Expand Up @@ -182,40 +183,4 @@ namespace Microsoft.Quantum.Tests {
}
}
}

operation ApplyRippleCarryComparatorTest() : Unit{
body (...) {
let nQubits = 4;
let intMax = 2^nQubits-1;
for(x in 0..intMax){
for(y in 0..intMax){
mutable result = Zero;
if(x > y){
set result = One;
}

Message($"Test case. {x} > {y} = {result}");
using(qubits = Qubit[nQubits*2 + 1]){
let xRegister = LittleEndian(qubits[0..nQubits-1]);
let yRegister = LittleEndian(qubits[nQubits..2*nQubits-1]);
let output = qubits[2*nQubits];

ApplyXorInPlace(x, xRegister);
ApplyXorInPlace(y, yRegister);
CompareUsingRippleCarry(xRegister, yRegister, output);

AssertProb([PauliZ], [output], result, 1.0, "", 1e-10);
if(result == One){
X(output);
}

(Adjoint ApplyXorInPlace)(y, yRegister);
(Adjoint ApplyXorInPlace)(x, xRegister);


}
}
}
}
}
}