diff --git a/Standard/src/Arithmetic/Arithmetic.qs b/Standard/src/Arithmetic/Arithmetic.qs index 189d0e79ce3..696402ad7fb 100644 --- a/Standard/src/Arithmetic/Arithmetic.qs +++ b/Standard/src/Arithmetic/Arithmetic.qs @@ -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( @@ -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. diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 56446f17d76..cc4703c0bb7 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -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 @@ -36,19 +39,15 @@ 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); @@ -56,4 +55,104 @@ 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 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, 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); + } + } + } diff --git a/Standard/tests/ArithmeticTests.qs b/Standard/tests/ArithmeticTests.qs index e841a3092c3..dae06007dbd 100644 --- a/Standard/tests/ArithmeticTests.qs +++ b/Standard/tests/ArithmeticTests.qs @@ -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 { @@ -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); + } + } + } + } + } } diff --git a/Standard/tests/QubitizationTests.qs b/Standard/tests/QubitizationTests.qs index e8cf65dea46..68f1c0c7b1c 100644 --- a/Standard/tests/QubitizationTests.qs +++ b/Standard/tests/QubitizationTests.qs @@ -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 @@ -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); - - - } - } - } - } - } }