Skip to content
This repository has been 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
25 changes: 25 additions & 0 deletions Standard/src/Arithmetic/Arithmetic.qs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ 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.
operation ApplyXorInPlaceL(value : BigInt, target : LittleEndian)
msoeken marked this conversation as resolved.
Show resolved Hide resolved
: 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
98 changes: 96 additions & 2 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 @@ -56,4 +59,95 @@ namespace Microsoft.Quantum.Arithmetic {
}
}

/// # 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 LessThanConstantUsingRippleCarry(c : BigInt, x : LittleEndian, output : Qubit)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can defer to API review, but I'd suggest maybe something like "CompareLessThanConstantUsingRippleCarry"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, but we need further discussion because we also have CompareUsingRippleCarry at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point; that can definitely create some confusion. Agreed with the need for further discussion.

: Unit {
msoeken marked this conversation as resolved.
Show resolved Hide resolved
body (...) {
let bitwidth = Length(x!);
AssertAllZero([output]);
Fact(c >= 0L, "Constant input `c` must not be negative");
msoeken marked this conversation as resolved.
Show resolved Hide resolved

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(EqualB(true, _), bits);
msoeken marked this conversation as resolved.
Show resolved Hide resolved
using (tmpAnd = Qubit[bitwidth - 2 - l]) {
let tmpCarry = x![l..l] + tmpAnd;
within {
ApplyPauliFromBitString(PauliX, true, bits, x!);
for (i in 1..bitwidth - l - 2) {
within {
ApplyIfA(X, bits[i + l], tmpCarry[i - 1]);
} apply {
ApplyAnd(tmpCarry[i - 1], x![i + l], tmpCarry[i]);
}
CNOT(tmpCarry[i - 1], tmpCarry[i]);
msoeken marked this conversation as resolved.
Show resolved Hide resolved
}
} apply {
within {
ApplyIfA(X, bits[bitwidth - 1], Tail(tmpCarry));
} apply {
ApplyAnd(Tail(tmpCarry), Tail(x!), output);
}
CNOT(Tail(tmpCarry), output);
}
}
}
}
adjoint self;

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

}
46 changes: 45 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,48 @@ namespace Microsoft.Quantum.ArithmeticTests {
}
}
}


@Test("ToffoliSimulator")
operation TestLessThanConstantUsingRippleCarry() : Unit {
TestLessThanConstantUsingRippleCarryForBitWidth(3);
TestLessThanConstantUsingRippleCarryForBitWidth(4);
}

internal operation TestLessThanConstantUsingRippleCarryForBitWidth(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 {
LessThanConstantUsingRippleCarry(IntAsBigInt(cinput), inputReg, output);
EqualityFactB(IsResultOne(MResetZ(output)), qinput < cinput, $"Unexpected result for cinput = {cinput} and qinput = {qinput}");
}
}
}
}
}

@Test("ResourcesEstimator")
operation TestLessThanConstantUsingRippleCarryOperationCalls() : 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 * 4, T, $"Too many T operations for constant {idx}");
msoeken marked this conversation as resolved.
Show resolved Hide resolved
} apply {
using ((input, output) = (Qubit[4], Qubit())) {
within {
AllowAtMostNQubits(qubitCount, $"Too many qubits allocated for constant {idx}");
} apply {
LessThanConstantUsingRippleCarry(IntAsBigInt(idx), LittleEndian(input), output);
}
}
}
}
}
}