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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);


}
}
}
}
}
}