From e359143a569ef5e679459aaf20cd97cfaa31f027 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:20:59 +0200 Subject: [PATCH 01/17] ApplyXorInPlace with BigInt constant. --- Standard/src/Arithmetic/Arithmetic.qs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Standard/src/Arithmetic/Arithmetic.qs b/Standard/src/Arithmetic/Arithmetic.qs index 189d0e79ce3..f50306a5e72 100644 --- a/Standard/src/Arithmetic/Arithmetic.qs +++ b/Standard/src/Arithmetic/Arithmetic.qs @@ -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) + : 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. From 9545056ede7d3a0df8198a69d3c2e4e2f9813e33 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:21:48 +0200 Subject: [PATCH 02/17] Comparison with constant. --- Standard/src/Arithmetic/Comparators.qs | 49 ++++++++++++++++++++++++-- Standard/tests/ArithmeticTests.qs | 26 +++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 56446f17d76..2b68606fb5c 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -2,9 +2,11 @@ // 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; /// # Summary /// This operation tests if an integer represented by a register of qubits @@ -56,4 +58,47 @@ namespace Microsoft.Quantum.Arithmetic { } } + operation LessThanConstantUsingRippleCarry(c : BigInt, x : LittleEndian, output : Qubit) + : Unit { + let bitwidth = Length(x!); + AssertAllZero([output]); + + 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 l = TrailingZeroes(c); + using ((tmpConstants, tmpAnd) = (Qubit[bitwidth - l], Qubit[bitwidth - 1 - l])) { + within { + ApplyXorInPlaceL(c >>> l, LittleEndian(tmpConstants)); + ApplyXorInPlaceL(c, x); + for (i in 0..bitwidth - l - 2) { + ApplyAnd(tmpConstants[i], x![i + l], tmpAnd[i]); + if (i > 0) { + CNOT(tmpAnd[i - 1], tmpAnd[i]); + } + CNOT(tmpAnd[i], tmpConstants[i + 1]); + } + } apply { + ApplyAnd(Tail(tmpConstants), Tail(x!), output); + CNOT(Tail(tmpAnd), output); + } + } + } + } + + internal function TrailingZeroes(number : BigInt) : Int { + mutable zeroes = 0; + mutable copy = number; + while (copy % 2L == 0L) { + set zeroes += 1; + set copy /= 2L; + } + return zeroes; + } + } diff --git a/Standard/tests/ArithmeticTests.qs b/Standard/tests/ArithmeticTests.qs index e841a3092c3..cd87dfca8a4 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,28 @@ 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}"); + } + } + } + } + } } From fcd053643a85daee1785600a83708d9392f04ae9 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:24:57 +0200 Subject: [PATCH 03/17] Save one AND gate. --- Standard/src/Arithmetic/Comparators.qs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 2b68606fb5c..4e47e8bba9a 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -77,8 +77,10 @@ namespace Microsoft.Quantum.Arithmetic { ApplyXorInPlaceL(c >>> l, LittleEndian(tmpConstants)); ApplyXorInPlaceL(c, x); for (i in 0..bitwidth - l - 2) { - ApplyAnd(tmpConstants[i], x![i + l], tmpAnd[i]); - if (i > 0) { + if (i == 0) { + CNOT(x![i + l], tmpAnd[i]); + } else { + ApplyAnd(tmpConstants[i], x![i + l], tmpAnd[i]); CNOT(tmpAnd[i - 1], tmpAnd[i]); } CNOT(tmpAnd[i], tmpConstants[i + 1]); From fda06a67021dcac11abc78de6f08a5288203d278 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:33:03 +0200 Subject: [PATCH 04/17] Save one temporary qubit. --- Standard/src/Arithmetic/Comparators.qs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 4e47e8bba9a..8b4af61d30d 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -78,16 +78,20 @@ namespace Microsoft.Quantum.Arithmetic { ApplyXorInPlaceL(c, x); for (i in 0..bitwidth - l - 2) { if (i == 0) { - CNOT(x![i + l], tmpAnd[i]); + CNOT(x![i + l], tmpConstants[i + 1]); } else { ApplyAnd(tmpConstants[i], x![i + l], tmpAnd[i]); - CNOT(tmpAnd[i - 1], tmpAnd[i]); + if (i == 1) { + CNOT(x![i + l - 1], tmpAnd[i]); + } else { + CNOT(tmpAnd[i - 1], tmpAnd[i]); + } + CNOT(tmpAnd[i], tmpConstants[i + 1]); } - CNOT(tmpAnd[i], tmpConstants[i + 1]); } } apply { ApplyAnd(Tail(tmpConstants), Tail(x!), output); - CNOT(Tail(tmpAnd), output); + CNOT(Length(tmpAnd) == 1 ? x![l] | Tail(tmpAnd), output); } } } From e7320c9e71c67ba9c8d4e188ad96e749f6cc7af5 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:35:24 +0200 Subject: [PATCH 05/17] Remove unused qubit. --- Standard/src/Arithmetic/Comparators.qs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 8b4af61d30d..625c526eeca 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -72,7 +72,7 @@ namespace Microsoft.Quantum.Arithmetic { X(output); } else { let l = TrailingZeroes(c); - using ((tmpConstants, tmpAnd) = (Qubit[bitwidth - l], Qubit[bitwidth - 1 - l])) { + using ((tmpConstants, tmpAnd) = (Qubit[bitwidth - l], Qubit[bitwidth - 2 - l])) { within { ApplyXorInPlaceL(c >>> l, LittleEndian(tmpConstants)); ApplyXorInPlaceL(c, x); @@ -80,18 +80,18 @@ namespace Microsoft.Quantum.Arithmetic { if (i == 0) { CNOT(x![i + l], tmpConstants[i + 1]); } else { - ApplyAnd(tmpConstants[i], x![i + l], tmpAnd[i]); + ApplyAnd(tmpConstants[i], x![i + l], tmpAnd[i - 1]); if (i == 1) { - CNOT(x![i + l - 1], tmpAnd[i]); + CNOT(x![i + l - 1], tmpAnd[i - 1]); } else { - CNOT(tmpAnd[i - 1], tmpAnd[i]); + CNOT(tmpAnd[i - 2], tmpAnd[i - 1]); } - CNOT(tmpAnd[i], tmpConstants[i + 1]); + CNOT(tmpAnd[i - 1], tmpConstants[i + 1]); } } } apply { ApplyAnd(Tail(tmpConstants), Tail(x!), output); - CNOT(Length(tmpAnd) == 1 ? x![l] | Tail(tmpAnd), output); + CNOT(Length(tmpAnd) == 0 ? x![l] | Tail(tmpAnd), output); } } } From 525368050cc2f93d7b85e1abe200923295eb1958 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:39:30 +0200 Subject: [PATCH 06/17] Simplify code. --- Standard/src/Arithmetic/Comparators.qs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 625c526eeca..0f5ac9ea47c 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -73,6 +73,7 @@ namespace Microsoft.Quantum.Arithmetic { } else { let l = TrailingZeroes(c); using ((tmpConstants, tmpAnd) = (Qubit[bitwidth - l], Qubit[bitwidth - 2 - l])) { + let tmpCarry = x![l..l] + tmpAnd; within { ApplyXorInPlaceL(c >>> l, LittleEndian(tmpConstants)); ApplyXorInPlaceL(c, x); @@ -80,18 +81,18 @@ namespace Microsoft.Quantum.Arithmetic { if (i == 0) { CNOT(x![i + l], tmpConstants[i + 1]); } else { - ApplyAnd(tmpConstants[i], x![i + l], tmpAnd[i - 1]); + ApplyAnd(tmpConstants[i], x![i + l], tmpCarry[i]); if (i == 1) { - CNOT(x![i + l - 1], tmpAnd[i - 1]); + CNOT(x![i + l - 1], tmpCarry[i]); } else { - CNOT(tmpAnd[i - 2], tmpAnd[i - 1]); + CNOT(tmpCarry[i - 1], tmpCarry[i]); } - CNOT(tmpAnd[i - 1], tmpConstants[i + 1]); + CNOT(tmpCarry[i], tmpConstants[i + 1]); } } } apply { ApplyAnd(Tail(tmpConstants), Tail(x!), output); - CNOT(Length(tmpAnd) == 0 ? x![l] | Tail(tmpAnd), output); + CNOT(Tail(tmpCarry), output); } } } From 6c602998e254e18703851f79beda12c7c279fda1 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:42:18 +0200 Subject: [PATCH 07/17] Simplify code. --- Standard/src/Arithmetic/Comparators.qs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 0f5ac9ea47c..8fe2406b54e 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -78,17 +78,11 @@ namespace Microsoft.Quantum.Arithmetic { ApplyXorInPlaceL(c >>> l, LittleEndian(tmpConstants)); ApplyXorInPlaceL(c, x); for (i in 0..bitwidth - l - 2) { - if (i == 0) { - CNOT(x![i + l], tmpConstants[i + 1]); - } else { + if (i > 0) { ApplyAnd(tmpConstants[i], x![i + l], tmpCarry[i]); - if (i == 1) { - CNOT(x![i + l - 1], tmpCarry[i]); - } else { - CNOT(tmpCarry[i - 1], tmpCarry[i]); - } - CNOT(tmpCarry[i], tmpConstants[i + 1]); + CNOT(tmpCarry[i - 1], tmpCarry[i]); } + CNOT(tmpCarry[i], tmpConstants[i + 1]); } } apply { ApplyAnd(Tail(tmpConstants), Tail(x!), output); From 35578cb4f537e8ef5473f2e740093fe2a6f43c47 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 14:45:07 +0200 Subject: [PATCH 08/17] Save another qubit. --- Standard/src/Arithmetic/Comparators.qs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 8fe2406b54e..e8d616fd5c0 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -72,17 +72,17 @@ namespace Microsoft.Quantum.Arithmetic { X(output); } else { let l = TrailingZeroes(c); - using ((tmpConstants, tmpAnd) = (Qubit[bitwidth - l], Qubit[bitwidth - 2 - l])) { + using ((tmpConstants, tmpAnd) = (Qubit[bitwidth - 1 - l], Qubit[bitwidth - 2 - l])) { let tmpCarry = x![l..l] + tmpAnd; within { - ApplyXorInPlaceL(c >>> l, LittleEndian(tmpConstants)); + ApplyXorInPlaceL(c >>> l + 1, LittleEndian(tmpConstants)); ApplyXorInPlaceL(c, x); for (i in 0..bitwidth - l - 2) { if (i > 0) { - ApplyAnd(tmpConstants[i], x![i + l], tmpCarry[i]); + ApplyAnd(tmpConstants[i - 1], x![i + l], tmpCarry[i]); CNOT(tmpCarry[i - 1], tmpCarry[i]); } - CNOT(tmpCarry[i], tmpConstants[i + 1]); + CNOT(tmpCarry[i], tmpConstants[i]); } } apply { ApplyAnd(Tail(tmpConstants), Tail(x!), output); From 912be5d956f3b9e5058d29b119c6dc030ca9912d Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 15:07:38 +0200 Subject: [PATCH 09/17] Remove temporary qubits for constants. --- Standard/src/Arithmetic/Comparators.qs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index e8d616fd5c0..81bf2bc1bbf 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -71,21 +71,28 @@ namespace Microsoft.Quantum.Arithmetic { CNOT(Tail(x!), output); X(output); } else { + let bits = BigIntAsBoolArray(c); let l = TrailingZeroes(c); - using ((tmpConstants, tmpAnd) = (Qubit[bitwidth - 1 - l], Qubit[bitwidth - 2 - l])) { + using (tmpAnd = Qubit[bitwidth - 2 - l]) { let tmpCarry = x![l..l] + tmpAnd; within { - ApplyXorInPlaceL(c >>> l + 1, LittleEndian(tmpConstants)); ApplyXorInPlaceL(c, x); for (i in 0..bitwidth - l - 2) { if (i > 0) { - ApplyAnd(tmpConstants[i - 1], x![i + l], tmpCarry[i]); + 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]); } - CNOT(tmpCarry[i], tmpConstants[i]); } } apply { - ApplyAnd(Tail(tmpConstants), Tail(x!), output); + within { + ApplyIfA(X, bits[bitwidth - 1], Tail(tmpCarry)); + } apply { + ApplyAnd(Tail(tmpCarry), Tail(x!), output); + } CNOT(Tail(tmpCarry), output); } } From 5d62b1abed354a8d835fae2a262fe32db91e73a1 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 15:15:32 +0200 Subject: [PATCH 10/17] Simplify code. --- Standard/src/Arithmetic/Comparators.qs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 81bf2bc1bbf..c89e681bc28 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -77,15 +77,13 @@ namespace Microsoft.Quantum.Arithmetic { let tmpCarry = x![l..l] + tmpAnd; within { ApplyXorInPlaceL(c, x); - for (i in 0..bitwidth - l - 2) { - if (i > 0) { - 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]); + 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]); } } apply { within { From f232d3e7ddf211f806f2a2c3e3ca85e3fabbed6c Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 27 Aug 2020 15:26:31 +0200 Subject: [PATCH 11/17] Code finished. --- Standard/src/Arithmetic/Comparators.qs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index c89e681bc28..8d982b533f7 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -7,6 +7,7 @@ namespace Microsoft.Quantum.Arithmetic { 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 @@ -72,11 +73,11 @@ namespace Microsoft.Quantum.Arithmetic { X(output); } else { let bits = BigIntAsBoolArray(c); - let l = TrailingZeroes(c); + let l = IndexOf(EqualB(true, _), bits); using (tmpAnd = Qubit[bitwidth - 2 - l]) { let tmpCarry = x![l..l] + tmpAnd; within { - ApplyXorInPlaceL(c, x); + ApplyPauliFromBitString(PauliX, true, bits, x!); for (i in 1..bitwidth - l - 2) { within { ApplyIfA(X, bits[i + l], tmpCarry[i - 1]); @@ -97,14 +98,4 @@ namespace Microsoft.Quantum.Arithmetic { } } - internal function TrailingZeroes(number : BigInt) : Int { - mutable zeroes = 0; - mutable copy = number; - while (copy % 2L == 0L) { - set zeroes += 1; - set copy /= 2L; - } - return zeroes; - } - } From fbf0742cecf37d9577534e801cb020aba2b12b2d Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 09:34:53 +0200 Subject: [PATCH 12/17] Docs. --- Standard/src/Arithmetic/Comparators.qs | 102 +++++++++++++++++++------ 1 file changed, 77 insertions(+), 25 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 8d982b533f7..254aa8ef48d 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -59,43 +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) : Unit { - let bitwidth = Length(x!); - AssertAllZero([output]); + body (...) { + let bitwidth = Length(x!); + AssertAllZero([output]); + Fact(c >= 0L, "Constant input `c` must not be negative"); - 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); - 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) { + 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); + 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]); + } + } apply { within { - ApplyIfA(X, bits[i + l], tmpCarry[i - 1]); + ApplyIfA(X, bits[bitwidth - 1], Tail(tmpCarry)); } apply { - ApplyAnd(tmpCarry[i - 1], x![i + l], tmpCarry[i]); + ApplyAnd(Tail(tmpCarry), Tail(x!), output); } - CNOT(tmpCarry[i - 1], tmpCarry[i]); + CNOT(Tail(tmpCarry), output); } + } + } + } + adjoint self; + + controlled (controls, ...) { + using (q = Qubit()) { + within { + LessThanConstantUsingRippleCarry(c, x, q); } apply { - within { - ApplyIfA(X, bits[bitwidth - 1], Tail(tmpCarry)); - } apply { - ApplyAnd(Tail(tmpCarry), Tail(x!), output); + if (Length(controls) == 1) { + ApplyAnd(Head(controls), q, output); + } else { + Controlled X(controls + [q], output); } - CNOT(Tail(tmpCarry), output); } } } + adjoint controlled self; } } From b5d93fe268647c8c928d3484cd09ab3c3519ddfd Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 11:24:13 +0200 Subject: [PATCH 13/17] Resources count test. --- Standard/tests/ArithmeticTests.qs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Standard/tests/ArithmeticTests.qs b/Standard/tests/ArithmeticTests.qs index cd87dfca8a4..f43fbd04a3a 100644 --- a/Standard/tests/ArithmeticTests.qs +++ b/Standard/tests/ArithmeticTests.qs @@ -198,6 +198,26 @@ namespace Microsoft.Quantum.ArithmeticTests { } } } + + @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}"); + } apply { + using ((input, output) = (Qubit[4], Qubit())) { + within { + AllowAtMostNQubits(qubitCount, $"Too many qubits allocated for constant {idx}"); + } apply { + LessThanConstantUsingRippleCarry(IntAsBigInt(idx), LittleEndian(input), output); + } + } + } + } + } } From 03a187f2cdf7c8a1cd93e7d46b646bb33618523f Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Wed, 2 Sep 2020 11:27:28 +0200 Subject: [PATCH 14/17] Apply suggestions from code review Co-authored-by: Chris Granade --- Standard/src/Arithmetic/Comparators.qs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 254aa8ef48d..e53f00ac617 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -97,7 +97,7 @@ namespace Microsoft.Quantum.Arithmetic { body (...) { let bitwidth = Length(x!); AssertAllZero([output]); - Fact(c >= 0L, "Constant input `c` must not be negative"); + Fact(c >= 0L, $"Constant input `c` must not be negative, but was {c}"); if (c == 0L) { // do nothing; output stays 0 @@ -108,7 +108,7 @@ namespace Microsoft.Quantum.Arithmetic { X(output); } else { let bits = BigIntAsBoolArray(c); - let l = IndexOf(EqualB(true, _), bits); + let l = IndexOf(Identity, bits); using (tmpAnd = Qubit[bitwidth - 2 - l]) { let tmpCarry = x![l..l] + tmpAnd; within { From 3c0bb3b0f8135e37367ca3fce611899d799962c3 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Wed, 2 Sep 2020 11:27:58 +0200 Subject: [PATCH 15/17] Refactor tests for greater-than comparison. --- Standard/tests/ArithmeticTests.qs | 43 ++++++++++++++++++++++++++++- Standard/tests/QubitizationTests.qs | 37 +------------------------ 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Standard/tests/ArithmeticTests.qs b/Standard/tests/ArithmeticTests.qs index f43fbd04a3a..b91d753e522 100644 --- a/Standard/tests/ArithmeticTests.qs +++ b/Standard/tests/ArithmeticTests.qs @@ -177,6 +177,47 @@ 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 { + for (bitwidth in 2..6) { + 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 TestLessThanConstantUsingRippleCarry() : Unit { TestLessThanConstantUsingRippleCarryForBitWidth(3); @@ -206,7 +247,7 @@ namespace Microsoft.Quantum.ArithmeticTests { for ((idx, (tCount, qubitCount)) in Enumerated(Zip(tCounts, qubitCounts))) { within { - AllowAtMostNCallsCA(tCount * 4, T, $"Too many T operations for constant {idx}"); + AllowAtMostNCallsCA(tCount, T, $"Too many T operations for constant {idx}"); } apply { using ((input, output) = (Qubit[4], Qubit())) { within { 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); - - - } - } - } - } - } } From 15f0dfee18e259c79821152ed9cdb932b7c9fc2e Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Wed, 2 Sep 2020 12:36:19 +0200 Subject: [PATCH 16/17] Addressing Chris comments. --- Standard/src/Arithmetic/Arithmetic.qs | 24 ++++++++++++++ Standard/src/Arithmetic/Comparators.qs | 43 ++++++++++++++++---------- Standard/tests/ArithmeticTests.qs | 33 ++++++++++---------- 3 files changed, 66 insertions(+), 34 deletions(-) diff --git a/Standard/src/Arithmetic/Arithmetic.qs b/Standard/src/Arithmetic/Arithmetic.qs index f50306a5e72..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( @@ -49,6 +62,17 @@ namespace Microsoft.Quantum.Arithmetic { /// 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( diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index e53f00ac617..98ebf417f62 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -92,8 +92,8 @@ namespace Microsoft.Quantum.Arithmetic { /// 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) - : Unit { + operation CompareLessThanConstantUsingRippleCarry(c : BigInt, x : LittleEndian, output : Qubit) + : Unit is Adj+Ctl { body (...) { let bitwidth = Length(x!); AssertAllZero([output]); @@ -113,21 +113,9 @@ namespace Microsoft.Quantum.Arithmetic { 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]); - } + ApplyToEachA(ApplyAndOrStep, Zip4(bits[l + 1...], Most(tmpCarry), x![l + 1...], Rest(tmpCarry))); } apply { - within { - ApplyIfA(X, bits[bitwidth - 1], Tail(tmpCarry)); - } apply { - ApplyAnd(Tail(tmpCarry), Tail(x!), output); - } - CNOT(Tail(tmpCarry), output); + ApplyAndOrStep(bits[bitwidth - 1], Tail(tmpCarry), Tail(x!), output); } } } @@ -137,7 +125,7 @@ namespace Microsoft.Quantum.Arithmetic { controlled (controls, ...) { using (q = Qubit()) { within { - LessThanConstantUsingRippleCarry(c, x, q); + CompareLessThanConstantUsingRippleCarry(c, x, q); } apply { if (Length(controls) == 1) { ApplyAnd(Head(controls), q, output); @@ -150,4 +138,25 @@ namespace Microsoft.Quantum.Arithmetic { 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 b91d753e522..dae06007dbd 100644 --- a/Standard/tests/ArithmeticTests.qs +++ b/Standard/tests/ArithmeticTests.qs @@ -203,28 +203,27 @@ namespace Microsoft.Quantum.ArithmeticTests { @Test("ResourcesEstimator") operation TestCompareUsingRippleCarryOperationCalls() : Unit { - for (bitwidth in 2..6) { - 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); - } + 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 TestLessThanConstantUsingRippleCarry() : Unit { - TestLessThanConstantUsingRippleCarryForBitWidth(3); - TestLessThanConstantUsingRippleCarryForBitWidth(4); + operation TestCompareLessThanConstantUsingRippleCarry() : Unit { + TestCompareLessThanConstantUsingRippleCarryForBitWidth(3); + TestCompareLessThanConstantUsingRippleCarryForBitWidth(4); } - internal operation TestLessThanConstantUsingRippleCarryForBitWidth(bitwidth : Int) : Unit { + internal operation TestCompareLessThanConstantUsingRippleCarryForBitWidth(bitwidth : Int) : Unit { using ((input, output) = (Qubit[bitwidth], Qubit())) { let inputReg = LittleEndian(input); for (qinput in 0..2^bitwidth - 1) { @@ -232,7 +231,7 @@ namespace Microsoft.Quantum.ArithmeticTests { within { ApplyXorInPlace(qinput, inputReg); } apply { - LessThanConstantUsingRippleCarry(IntAsBigInt(cinput), inputReg, output); + CompareLessThanConstantUsingRippleCarry(IntAsBigInt(cinput), inputReg, output); EqualityFactB(IsResultOne(MResetZ(output)), qinput < cinput, $"Unexpected result for cinput = {cinput} and qinput = {qinput}"); } } @@ -241,7 +240,7 @@ namespace Microsoft.Quantum.ArithmeticTests { } @Test("ResourcesEstimator") - operation TestLessThanConstantUsingRippleCarryOperationCalls() : Unit { + 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]; @@ -253,7 +252,7 @@ namespace Microsoft.Quantum.ArithmeticTests { within { AllowAtMostNQubits(qubitCount, $"Too many qubits allocated for constant {idx}"); } apply { - LessThanConstantUsingRippleCarry(IntAsBigInt(idx), LittleEndian(input), output); + CompareLessThanConstantUsingRippleCarry(IntAsBigInt(idx), LittleEndian(input), output); } } } From ff1303388264ebfc20c067f9850292773a95e690 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Wed, 2 Sep 2020 12:44:24 +0200 Subject: [PATCH 17/17] Ceanup. --- Standard/src/Arithmetic/Comparators.qs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index 98ebf417f62..cc4703c0bb7 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -39,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);