From 605b9a30c08c371dcaeb848e2486cba40fe9a8c8 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 13 Aug 2024 12:42:20 -0700 Subject: [PATCH 01/15] export AND --- library/std/src/intrinsic.qs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/intrinsic.qs b/library/std/src/intrinsic.qs index 52f021850f..3734c8a0c0 100644 --- a/library/std/src/intrinsic.qs +++ b/library/std/src/intrinsic.qs @@ -1155,5 +1155,5 @@ namespace Microsoft.Quantum.Intrinsic { body intrinsic; } - export CCNOT, CNOT, Exp, H, I, M, Measure, R, R1, R1Frac, Reset, ResetAll, RFrac, Rx, Rxx, Ry, Ryy, Rz, Rzz, S, SWAP, T, X, Y, Z, Message; + export AND, CCNOT, CNOT, Exp, H, I, M, Measure, R, R1, R1Frac, Reset, ResetAll, RFrac, Rx, Rxx, Ry, Ryy, Rz, Rzz, S, SWAP, T, X, Y, Z, Message; } From 58bc258af5c80891f660c299bbd23135a8bd873d Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 13 Aug 2024 19:30:12 -0700 Subject: [PATCH 02/15] signed math library --- library/signed/README.md | 0 library/signed/qsharp.json | 11 + library/signed/src/Comparison.qs | 99 +++++++ library/signed/src/Operations.qs | 277 ++++++++++++++++++++ library/signed/src/RippleCarry.qs | 416 ++++++++++++++++++++++++++++++ library/signed/src/Tests.qs | 35 +++ library/signed/src/Utils.qs | 56 ++++ 7 files changed, 894 insertions(+) create mode 100644 library/signed/README.md create mode 100644 library/signed/qsharp.json create mode 100644 library/signed/src/Comparison.qs create mode 100644 library/signed/src/Operations.qs create mode 100644 library/signed/src/RippleCarry.qs create mode 100644 library/signed/src/Tests.qs create mode 100644 library/signed/src/Utils.qs diff --git a/library/signed/README.md b/library/signed/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/library/signed/qsharp.json b/library/signed/qsharp.json new file mode 100644 index 0000000000..5001a1c231 --- /dev/null +++ b/library/signed/qsharp.json @@ -0,0 +1,11 @@ +{ + "author": "Microsoft", + "license": "MIT", + "files": [ + "src/Comparison.qs", + "src/Operations.qs", + "src/RippleCarry.qs", + "src/Tests.qs", + "src/Utils.qs" + ] +} \ No newline at end of file diff --git a/library/signed/src/Comparison.qs b/library/signed/src/Comparison.qs new file mode 100644 index 0000000000..6db741542e --- /dev/null +++ b/library/signed/src/Comparison.qs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Std.Arrays.Tail, Std.Arrays.Zipped, Std.Arrays.Most, Std.Arrays.Rest; +import Utils.ApplyCCNOTChain; + +/// # Summary +/// Wrapper for signed integer comparison: `result = xs > ys`. +/// +/// # Input +/// ## xs +/// First $n$-bit number +/// ## ys +/// Second $n$-bit number +/// ## result +/// Will be flipped if $xs > ys$ +operation CompareGTSI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { + use tmp = Qubit(); + CNOT(Tail(xs), tmp); + CNOT(Tail(ys), tmp); + X(tmp); + Controlled CompareGTI([tmp], (xs, ys, result)); + X(tmp); + CCNOT(tmp, Tail(ys), result); + CNOT(Tail(xs), tmp); + CNOT(Tail(ys), tmp); +} + + +/// # Summary +/// Wrapper for integer comparison: `result = x > y`. +/// +/// # Input +/// ## xs +/// First $n$-bit number +/// ## ys +/// Second $n$-bit number +/// ## result +/// Will be flipped if $x > y$ +operation CompareGTI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { + GreaterThan(xs, ys, result); +} + +/// # Summary +/// Applies a greater-than comparison between two integers encoded into +/// qubit registers, flipping a target qubit based on the result of the +/// comparison. +/// +/// # Description +/// Carries out a strictly greater than comparison of two integers $x$ and $y$, encoded +/// in qubit registers xs and ys. If $x > y$, then the result qubit will be flipped, +/// otherwise the result qubit will retain its state. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer $x$. +/// ## ys +/// LittleEndian qubit register encoding the second integer $y$. +/// ## result +/// Single qubit that will be flipped if $x > y$. +/// +/// # References +/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David +/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. +/// https://arxiv.org/abs/quant-ph/0410184v1 +/// +/// - Thomas Haener, Martin Roetteler, Krysta M. Svore: "Factoring using 2n+2 qubits +/// with Toffoli based modular multiplication", 2016 +/// https://arxiv.org/abs/1611.07995 +/// +/// # Remarks +/// Uses the trick that $x - y = (x'+y)'$, where ' denotes the one's complement. +operation GreaterThan(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { + body (...) { + (Controlled GreaterThan)([], (xs, ys, result)); + } + controlled (controls, ...) { + let nQubits = Length(xs); + + if (nQubits == 1) { + X(ys[0]); + (Controlled CCNOT)(controls, (xs[0], ys[0], result)); + X(ys[0]); + } else { + within { + ApplyToEachCA(X, ys); + ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); + } apply { + within { + (Adjoint ApplyCNOTChain)(Rest(xs)); + ApplyCCNOTChain(Most(ys), xs); + } apply { + (Controlled CCNOT)(controls, (xs[nQubits-1], ys[nQubits-1], result)); + } + (Controlled CNOT)(controls, (xs[nQubits-1], result)); + } + } + } +} \ No newline at end of file diff --git a/library/signed/src/Operations.qs b/library/signed/src/Operations.qs new file mode 100644 index 0000000000..6cba1647d4 --- /dev/null +++ b/library/signed/src/Operations.qs @@ -0,0 +1,277 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Std.Arrays.Tail, Std.Arrays.Most, Std.Arrays.Enumerated; +import Utils.AndLadder; +import RippleCarry.RippleCarryAdderNoCarryTTK, RippleCarry.RippleCarryAdderTTK; +import Std.Diagnostics.Fact; +import Comparison.CompareGTI; + + +operation SquareSI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { + body (...) { + Controlled SquareSI([], (xs, result)); + } + controlled (controls, ...) { + let n = Length(xs); + use signx = Qubit(); + use signy = Qubit(); + + within { + CNOT(Tail(xs), signx); + Controlled Invert2sSI([signx], xs); + } apply { + Controlled SquareI(controls, (xs, result)); + } + } +} + +operation SquareI(xs : Qubit[], result : Qubit[]) : Unit { + body (...) { + Controlled SquareI([], (xs, result)); + } + controlled (controls, ...) { + let n = Length(xs); + + + let numControls = Length(controls); + if numControls == 0 { + use aux = Qubit(); + for (idx, ctl) in Enumerated(xs) { + within { + CNOT(ctl, aux); + } apply { + Controlled AddI([aux], (xs, (result[idx..idx + n]))); + } + } + } elif numControls == 1 { + use aux = Qubit(); + for (idx, ctl) in Enumerated(xs) { + within { + AND(controls[0], ctl, aux); + } apply { + Controlled AddI([aux], (xs, (result[idx..idx + n]))); + } + } + } else { + use helper = Qubit[numControls]; + within { + AndLadder(controls, Most(helper)); + } apply { + for (idx, ctl) in Enumerated(xs) { + within { + AND(Tail(Most(helper)), ctl, Tail(helper)); + } apply { + Controlled AddI([Tail(helper)], (xs, (result[idx..idx + n]))); + } + } + } + } + } + adjoint auto; + controlled adjoint auto; +} + + + +operation MultiplyI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { + body (...) { + let na = Length(xs); + let nb = Length(ys); + + for (idx, actl) in Enumerated(xs) { + Controlled AddI([actl], (ys, (result[idx..idx + nb]))); + } + } + controlled (controls, ...) { + let na = Length(xs); + let nb = Length(ys); + + // Perform various optimizations based on number of controls + let numControls = Length(controls); + if numControls == 0 { + MultiplyI(xs, ys, result); + } elif numControls == 1 { + use aux = Qubit(); + for (idx, actl) in Enumerated(xs) { + within { + AND(controls[0], actl, aux); + } apply { + Controlled AddI([aux], (ys, (result[idx..idx + nb]))); + } + } + } else { + use helper = Qubit[numControls]; + within { + AndLadder(controls, Most(helper)); + } apply { + for (idx, actl) in Enumerated(xs) { + within { + AND(Tail(Most(helper)), actl, Tail(helper)); + } apply { + Controlled AddI([Tail(helper)], (ys, (result[idx..idx + nb]))); + } + } + } + } + } +} + + +operation MultiplySI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit { + body (...) { + Controlled MultiplySI([], (xs, ys, result)); + } + controlled (controls, ...) { + use signx = Qubit(); + use signy = Qubit(); + + within { + CNOT(Tail(xs), signx); + CNOT(Tail(ys), signy); + Controlled Invert2sSI([signx], xs); + Controlled Invert2sSI([signy], ys); + } apply { + Controlled MultiplyI(controls, (xs, ys, result)); + within { + CNOT(signx, signy); + } apply { + // No controls required since `result` will still be zero + // if we did not perform the multiplication above. + Controlled Invert2sSI([signy], result); + } + } + } + adjoint auto; + controlled adjoint auto; +} + + + +/// # Summary +/// Implements a reversible sum gate. Given a carry-in bit encoded in +/// qubit `carryIn` and two summand bits encoded in `summand1` and `summand2`, +/// computes the bitwise xor of `carryIn`, `summand1` and `summand2` in the qubit +/// `summand2`. +/// +/// # Input +/// ## carryIn +/// Carry-in qubit. +/// ## summand1 +/// First summand qubit. +/// ## summand2 +/// Second summand qubit, is replaced with the lower bit of the sum of +/// `summand1` and `summand2`. +/// +/// # Remarks +/// In contrast to the `Carry` operation, this does not compute the carry-out bit. +operation Sum(carryIn : Qubit, summand1 : Qubit, summand2 : Qubit) : Unit is Adj + Ctl { + CNOT(summand1, summand2); + CNOT(carryIn, summand2); +} + + +/// # Summary +/// Automatically chooses between addition with +/// carry and without, depending on the register size of `ys`. +/// +/// # Input +/// ## xs +/// $n$-bit addend. +/// ## ys +/// Addend with at least $n$ qubits. Will hold the result. +operation AddI(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { + if Length(xs) == Length(ys) { + RippleCarryAdderNoCarryTTK(xs, ys); + } elif Length(ys) > Length(xs) { + use qs = Qubit[Length(ys) - Length(xs) - 1]; + RippleCarryAdderTTK(xs + qs, Most(ys), Tail(ys)); + } else { + fail "xs must not contain more qubits than ys"; + } +} + + + +/// # Summary +/// Inverts a given integer modulo 2's complement. +/// +/// # Input +/// ## xs +/// n-bit signed integer (SignedLittleEndian), will be inverted modulo +/// 2's complement. +operation Invert2sSI(xs : Qubit[]) : Unit is Adj + Ctl { + body (...) { + Controlled Invert2sSI([], xs); + } + controlled (controls, ...) { + ApplyToEachCA((Controlled X)(controls, _), xs); + + use aux = Qubit[Length(xs)]; + within { + Controlled X(controls, aux[0]); + } apply { + AddI(aux, xs); + } + } +} + +operation DivideI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { + body (...) { + Controlled DivideI([], (xs, ys, result)); + } + controlled (controls, ...) { + let n = Length(result); + + Fact(n == Length(ys), "Integer division requires + equally-sized registers ys and result."); + Fact(n == Length(xs), "Integer division + requires an n-bit dividend registers."); + + let xpadded = xs + result; + + for i in (n - 1)..(-1)..0 { + let xtrunc = xpadded[i..i + n-1]; + Controlled CompareGTI(controls, (ys, xtrunc, result[i])); + // if ys > xtrunc, we don't subtract: + (Controlled X)(controls, result[i]); + (Controlled Adjoint AddI)([result[i]], (ys, xtrunc)); + } + } +} + +/// # Summary +/// Computes the reciprocal 1/x for an unsigned integer x +/// using integer division. The result, interpreted as an integer, +/// will be `floor(2^(2*n-1) / x)`. +/// +/// # Input +/// ## xs +/// n-bit unsigned integer +/// ## result +/// 2n-bit output, must be in $\ket{0}$ initially. +/// +/// # Remarks +/// For the input x=0, the output will be all-ones. +operation ComputeReciprocalI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { + body (...) { + Controlled ComputeReciprocalI([], (xs, result)); + } + controlled (controls, ...) { + let n = Length(xs); + Fact(Length(result) == 2 * n, "Result register must contain 2n qubits."); + use lhs = Qubit[2 * n]; + use padding = Qubit[n]; + let paddedxs = xs + padding; + X(Tail(lhs)); // initialize left-hand side to 2^{2n-1} + // ... and divide: + (Controlled DivideI)(controls, (lhs, paddedxs, result)); + // uncompute lhs + for i in 0..2 * n - 1 { + (Controlled AddI)([result[i]], (paddedxs[0..2 * n-1-i], lhs[i..2 * n-1])); + } + X(Tail(lhs)); + } +} + +export Sum, MultiplyI, MultiplySI, SquareSI, SquareI, AddI, Invert2sSI, DivideI, ComputeReciprocalI; \ No newline at end of file diff --git a/library/signed/src/RippleCarry.qs b/library/signed/src/RippleCarry.qs new file mode 100644 index 0000000000..fad2816bd7 --- /dev/null +++ b/library/signed/src/RippleCarry.qs @@ -0,0 +1,416 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Std.Diagnostics.Fact; +import Operations.Sum; +import Std.Arrays.Most, Std.Arrays.Rest, Std.Arrays.Zipped; + +/// # Summary +/// Implements a reversible carry gate. Given a carry-in bit encoded in +/// qubit `carryIn` and two summand bits encoded in `summand1` and `summand2`, +/// computes the bitwise xor of `carryIn`, `summand1` and `summand2` in the +/// qubit `summand2` and the carry-out is xored to the qubit `carryOut`. +/// +/// # Input +/// ## carryIn +/// Carry-in qubit. +/// ## summand1 +/// First summand qubit. +/// ## summand2 +/// Second summand qubit, is replaced with the lower bit of the sum of +/// `summand1` and `summand2`. +/// ## carryOut +/// Carry-out qubit, will be xored with the higher bit of the sum. +operation Carry(carryIn : Qubit, summand1 : Qubit, summand2 : Qubit, carryOut : Qubit) : Unit is Adj + Ctl { + CCNOT(summand1, summand2, carryOut); + CNOT(summand1, summand2); + CCNOT(carryIn, summand2, carryOut); +} + + +/// # Summary +/// Reversible, in-place ripple-carry addition of two integers. +/// +/// # Description +/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, +/// and a qubit carry, the operation computes the sum of the two integers +/// where the $n$ least significant bits of the result are held in `ys` and +/// the carry out bit is xored to the qubit `carry`. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer summand. +/// ## ys +/// LittleEndian qubit register encoding the second integer summand, is +/// modified to hold the $n$ least significant bits of the sum. +/// ## carry +/// Carry qubit, is xored with the most significant bit of the sum. +/// +/// # References +/// - Thomas G. Draper: "Addition on a Quantum Computer", 2000. +/// https://arxiv.org/abs/quant-ph/0008033 +/// +/// # Remarks +/// The specified controlled operation makes use of symmetry and mutual +/// cancellation of operations to improve on the default implementation +/// that adds a control to every operation. +operation RippleCarryAdderD(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { + body (...) { + Controlled RippleCarryAdderD([], (xs, ys, carry)); + } + controlled (controls, ...) { + let nQubits = Length(xs); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + use auxRegister = Qubit[nQubits]; + for idx in 0..(nQubits-2) { + Carry(auxRegister[idx], xs[idx], ys[idx], auxRegister[idx + 1]); // (1) + } + Controlled Carry(controls, (auxRegister[nQubits-1], xs[nQubits-1], ys[nQubits-1], carry)); + Controlled CNOT(controls, (xs[nQubits-1], ys[nQubits-1])); + Controlled Sum(controls, (auxRegister[nQubits-1], xs[nQubits-1], ys[nQubits-1])); + for idx in (nQubits-2)..(-1)..0 { + Adjoint Carry(auxRegister[idx], xs[idx], ys[idx], auxRegister[idx + 1]); // cancels with (1) + Controlled Sum(controls, (auxRegister[idx], xs[idx], ys[idx])); + } + } +} + +/// # Summary +/// Reversible, in-place ripple-carry operation that is used in the +/// integer addition operation RippleCarryAdderCDKM below. +/// Given two qubit registers `xs` and `ys` of the same length, the operation +/// applies a ripple carry sequence of CNOT and CCNOT gates with qubits +/// in `xs` and `ys` as the controls and qubits in `xs` as the targets. +/// +/// # Input +/// ## xs +/// First qubit register, containing controls and targets. +/// ## ys +/// Second qubit register, contributing to the controls. +/// ## ancilla +/// The ancilla qubit used in RippleCarryAdderCDKM passed to this operation. +/// +/// # References +/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David +/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. +/// https://arxiv.org/abs/quant-ph/0410184v1 +operation ApplyOuterCDKMAdder(xs : Qubit[], ys : Qubit[], ancilla : Qubit) : Unit is Adj + Ctl { + let nQubits = Length(xs); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + Fact( + nQubits >= 3, + "Need at least 3 qubits per register." + ); + + CNOT(xs[2], xs[1]); + CCNOT(ancilla, ys[1], xs[1]); + for idx in 2..nQubits - 2 { + CNOT(xs[idx + 1], xs[idx]); + CCNOT(xs[idx-1], ys[idx], xs[idx]); + } +} + +/// # Summary +/// The core operation in the RippleCarryAdderCDKM, used with the above +/// ApplyOuterCDKMAdder operation, i.e. conjugated with this operation to obtain +/// the inner operation of the RippleCarryAdderCDKM. This operation computes +/// the carry out qubit and applies a sequence of NOT gates on part of the input `ys`. +/// +/// # Input +/// ## xs +/// First qubit register. +/// ## ys +/// Second qubit register. +/// ## ancilla +/// The ancilla qubit used in RippleCarryAdderCDKM passed to this operation. +/// ## carry +/// Carry out qubit in the RippleCarryAdderCDKM operation. +/// +/// # References +/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David +/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. +/// https://arxiv.org/abs/quant-ph/0410184v1 +operation CarryOutCoreCDKM( + xs : Qubit[], + ys : Qubit[], + ancilla : Qubit, + carry : Qubit +) : Unit is Adj + Ctl { + let nQubits = Length(xs); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + CNOT(xs[nQubits - 1], carry); + CCNOT(xs[nQubits - 2], ys[nQubits - 1], carry); + ApplyToEachCA(X, Most(Rest(ys))); // X on ys[1..(nQubits-2)] + CNOT(ancilla, ys[1]); + ApplyToEachCA(CNOT, Zipped(Rest(Most(xs)), Rest(Rest(ys)))); +} + +/// # Summary +/// Reversible, in-place ripple-carry addition of two integers. +/// +/// # Description +/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, +/// and a qubit carry, the operation computes the sum of the two integers +/// where the $n$ least significant bits of the result are held in `ys` and +/// the carry out bit is xored to the qubit `carry`. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer summand. +/// ## ys +/// LittleEndian qubit register encoding the second integer summand, is +/// modified to hold the n least significant bits of the sum. +/// ## carry +/// Carry qubit, is xored with the most significant bit of the sum. +/// +/// # References +/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David +/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. +/// https://arxiv.org/abs/quant-ph/0410184v1 +/// +/// # Remarks +/// This operation has the same functionality as RippleCarryAdderD, but +/// only uses one auxiliary qubit instead of $n$. +operation RippleCarryAdderCDKM(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { + let nQubits = Length(xs); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + use auxiliary = Qubit(); + within { + ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); + CNOT(xs[1], auxiliary); + CCNOT(xs[0], ys[0], auxiliary); + ApplyOuterCDKMAdder(xs, ys, auxiliary); + } apply { + CarryOutCoreCDKM(xs, ys, auxiliary, carry); + } + ApplyToEachCA(X, Most(Rest(ys))); + CNOT(xs[0], ys[0]); +} + +/// # Summary +/// Implements the inner addition function for the operation +/// RippleCarryAdderTTK. This is the inner operation that is conjugated +/// with the outer operation to construct the full adder. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer summand +/// input to RippleCarryAdderTTK. +/// ## ys +/// LittleEndian qubit register encoding the second integer summand +/// input to RippleCarryAdderTTK. +/// ## carry +/// Carry qubit, is xored with the most significant bit of the sum. +/// +/// # References +/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum +/// Addition Circuits and Unbounded Fan-Out", Quantum Information and +/// Computation, Vol. 10, 2010. +/// https://arxiv.org/abs/0910.2530 +/// +/// # Remarks +/// The specified controlled operation makes use of symmetry and mutual +/// cancellation of operations to improve on the default implementation +/// that adds a control to every operation. +operation ApplyInnerTTKAdder(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { + body (...) { + (Controlled ApplyInnerTTKAdder)([], (xs, ys, carry)); + } + controlled (controls, ...) { + let nQubits = Length(xs); + + for idx in 0..nQubits - 2 { + CCNOT(xs[idx], ys[idx], xs[idx + 1]); + } + (Controlled CCNOT)(controls, (xs[nQubits-1], ys[nQubits-1], carry)); + for idx in nQubits - 1..-1..1 { + Controlled CNOT(controls, (xs[idx], ys[idx])); + CCNOT(xs[idx-1], ys[idx-1], xs[idx]); + } + } +} + +/// # Summary +/// Implements the outer operation for RippleCarryAdderTTK to conjugate +/// the inner operation to construct the full adder. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer summand +/// input to RippleCarryAdderTTK. +/// ## ys +/// LittleEndian qubit register encoding the second integer summand +/// input to RippleCarryAdderTTK. +/// +/// # References +/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum +/// Addition Circuits and Unbounded Fan-Out", Quantum Information and +/// Computation, Vol. 10, 2010. +/// https://arxiv.org/abs/0910.2530 +operation ApplyOuterTTKAdder(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { + let nQubits = Length(xs); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); + Adjoint ApplyCNOTChain(Rest(xs)); +} + +/// # Summary +/// Reversible, in-place ripple-carry addition of two integers. +/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, +/// and a qubit carry, the operation computes the sum of the two integers +/// where the $n$ least significant bits of the result are held in `ys` and +/// the carry out bit is xored to the qubit `carry`. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer summand. +/// ## ys +/// LittleEndian qubit register encoding the second integer summand, is +/// modified to hold the $n$ least significant bits of the sum. +/// ## carry +/// Carry qubit, is xored with the most significant bit of the sum. +/// +/// # References +/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum +/// Addition Circuits and Unbounded Fan-Out", Quantum Information and +/// Computation, Vol. 10, 2010. +/// https://arxiv.org/abs/0910.2530 +/// +/// # Remarks +/// This operation has the same functionality as RippleCarryAdderD and, +/// RippleCarryAdderCDKM but does not use any ancilla qubits. +operation RippleCarryAdderTTK(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { + let nQubits = Length(xs); + + + if (nQubits > 1) { + CNOT(xs[nQubits-1], carry); + within { + ApplyOuterTTKAdder(xs, ys); + } apply { + ApplyInnerTTKAdder(xs, ys, carry); + } + } else { + CCNOT(xs[0], ys[0], carry); + } + CNOT(xs[0], ys[0]); +} + +/// # Summary +/// Implements the inner addition function for the operation +/// RippleCarryAdderNoCarryTTK. This is the inner operation that is conjugated +/// with the outer operation to construct the full adder. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer summand +/// input to RippleCarryAdderNoCarryTTK. +/// ## ys +/// LittleEndian qubit register encoding the second integer summand +/// input to RippleCarryAdderNoCarryTTK. +/// +/// # References +/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum +/// Addition Circuits and Unbounded Fan-Out", Quantum Information and +/// Computation, Vol. 10, 2010. +/// https://arxiv.org/abs/0910.2530 +/// +/// # Remarks +/// The specified controlled operation makes use of symmetry and mutual +/// cancellation of operations to improve on the default implementation +/// that adds a control to every operation. +operation ApplyInnerTTKAdderWithoutCarry(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { + body (...) { + (Controlled ApplyInnerTTKAdderWithoutCarry)([], (xs, ys)); + } + controlled (controls, ...) { + let nQubits = Length(xs); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + for idx in 0..nQubits - 2 { + CCNOT(xs[idx], ys[idx], xs[idx + 1]); + } + for idx in nQubits - 1..-1..1 { + Controlled CNOT(controls, (xs[idx], ys[idx])); + CCNOT(xs[idx - 1], ys[idx - 1], xs[idx]); + } + } +} + +/// # Summary +/// Reversible, in-place ripple-carry addition of two integers without carry out. +/// +/// # Description +/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, +/// the operation computes the sum of the two integers modulo $2^n$, +/// where $n$ is the bit size of the inputs `xs` and `ys`. It does not compute +/// the carry out bit. +/// +/// # Input +/// ## xs +/// LittleEndian qubit register encoding the first integer summand. +/// ## ys +/// LittleEndian qubit register encoding the second integer summand, is +/// modified to hold the $n$ least significant bits of the sum. +/// +/// # References +/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum +/// Addition Circuits and Unbounded Fan-Out", Quantum Information and +/// Computation, Vol. 10, 2010. +/// https://arxiv.org/abs/0910.2530 +/// +/// # Remarks +/// This operation has the same functionality as RippleCarryAdderTTK but does +/// not return the carry bit. +operation RippleCarryAdderNoCarryTTK(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { + let nQubits = Length(xs); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + if (nQubits > 1) { + within { + ApplyOuterTTKAdder(xs, ys); + } apply { + ApplyInnerTTKAdderWithoutCarry(xs, ys); + } + } + CNOT(xs[0], ys[0]); +} + + + + + +export RippleCarryAdderNoCarryTTK, ApplyInnerTTKAdderWithoutCarry, RippleCarryAdderTTK, RippleCarryAdderCDKM, RippleCarryAdderD, Carry; \ No newline at end of file diff --git a/library/signed/src/Tests.qs b/library/signed/src/Tests.qs new file mode 100644 index 0000000000..4fdf1cb3ec --- /dev/null +++ b/library/signed/src/Tests.qs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Std.Diagnostics.Fact; + +/// This entrypoint runs tests for the signed integer library. +operation Main() : Unit { + use a = Qubit[2]; + use b = Qubit[2]; + use c = Qubit[4]; + + // 0b10 * 0b01 == 0b10 (1 * 2 = 2) + X(a[0]); + X(b[1]); + TestOp(Operations.MultiplyI, a, b, c, 2); + + // 0b01 * 0b10 == 0b10 (1 * 2 = 2) + X(a[1]); + X(b[0]); + TestOp(Operations.MultiplyI, a, b, c, 2); + + // 0b11 * 0b11 == 0b1001 (3 * 3 = 9) + X(a[0]); + X(b[0]); + X(a[1]); + X(b[1]); + TestOp(Operations.MultiplyI, a, b, c, 9); +} + +operation TestOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Qubit[], c : Qubit[], expect : Int) : Unit { + op(a, b, c); + let res = MeasureInteger(c); + Fact(res == expect, $"Expected {expect}, got {res}"); + ResetAll(a + b + c); +} \ No newline at end of file diff --git a/library/signed/src/Utils.qs b/library/signed/src/Utils.qs new file mode 100644 index 0000000000..69394050ba --- /dev/null +++ b/library/signed/src/Utils.qs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Util functions for interal use by the signed integer library. + +import Std.Math.Min; +import Std.Diagnostics.Fact; +import Std.Arrays.Head, Std.Arrays.Tail, Std.Arrays.Most, Std.Arrays.Rest; + +operation AndLadder(controls : Qubit[], targets : Qubit[]) : Unit is Adj { + let controls1 = [Head(controls)] + Most(targets); + let controls2 = Rest(controls); + for (a, b, c) in Zipped3(controls1, controls2, targets) { + AND(a, b, c); + } +} + +function Zipped3<'T1, 'T2, 'T3>(first : 'T1[], second : 'T2[], third : 'T3[]) : ('T1, 'T2, 'T3)[] { + let nElements = Min([Length(first), Length(second), Length(third)]); + if nElements == 0 { + return []; + } + mutable output = [(first[0], second[0], third[0]), size = nElements]; + for idxElement in 1..nElements - 1 { + set output w/= idxElement <- (first[idxElement], second[idxElement], third[idxElement]); + } + return output; +} + +/// # Summary +/// Implements a cascade of CCNOT gates controlled on corresponding bits of two +/// qubit registers, acting on the next qubit of one of the registers. +/// Starting from the qubits at position 0 in both registers as controls, CCNOT is +/// applied to the qubit at position 1 of the target register, then controlled by +/// the qubits at position 1 acting on the qubit at position 2 in the target register, +/// etc., ending with an action on the target qubit in position `Length(nQubits)-1`. +/// +/// # Input +/// ## register +/// Qubit register, only used for controls. +/// ## targets +/// Qubit register, used for controls and as target. +/// +/// # Remarks +/// The target qubit register must have one qubit more than the other register. +operation ApplyCCNOTChain(register : Qubit[], targets : Qubit[]) : Unit is Adj + Ctl { + let nQubits = Length(targets); + + Fact( + nQubits == Length(register) + 1, + "Target register must have one more qubit." + ); + + ApplyToEachCA(CCNOT, Zipped3(register, Most(targets), Rest(targets))); +} + From f744b1fc9d8692c9b5f4c6df8a42d957cedee454 Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 13 Aug 2024 19:36:22 -0700 Subject: [PATCH 03/15] add export to comparisons --- library/signed/src/Comparison.qs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/signed/src/Comparison.qs b/library/signed/src/Comparison.qs index 6db741542e..be5b8f9721 100644 --- a/library/signed/src/Comparison.qs +++ b/library/signed/src/Comparison.qs @@ -96,4 +96,9 @@ operation GreaterThan(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj } } } -} \ No newline at end of file +} + +export + CompareGTSI, + CompareGTI, + GreaterThan; \ No newline at end of file From efe5f4ab799665c9a3e601325d8bd91109da2e3b Mon Sep 17 00:00:00 2001 From: sezna Date: Tue, 13 Aug 2024 20:08:35 -0700 Subject: [PATCH 04/15] add readme and docs --- library/signed/README.md | 2 + library/signed/src/Operations.qs | 92 +++++++++++++++++++++++++++++-- library/signed/src/RippleCarry.qs | 12 ++-- library/signed/src/Utils.qs | 3 +- 4 files changed, 98 insertions(+), 11 deletions(-) diff --git a/library/signed/README.md b/library/signed/README.md index e69de29bb2..3d8a844083 100644 --- a/library/signed/README.md +++ b/library/signed/README.md @@ -0,0 +1,2 @@ +# Signed +The `signed` library defines signed quantum integer primitives and operations. \ No newline at end of file diff --git a/library/signed/src/Operations.qs b/library/signed/src/Operations.qs index 6cba1647d4..ebefe57644 100644 --- a/library/signed/src/Operations.qs +++ b/library/signed/src/Operations.qs @@ -7,7 +7,19 @@ import RippleCarry.RippleCarryAdderNoCarryTTK, RippleCarry.RippleCarryAdderTTK; import Std.Diagnostics.Fact; import Comparison.CompareGTI; - +/// # Summary +/// Square signed integer `xs` and store +/// the result in `result`, which must be zero initially. +/// +/// # Input +/// ## xs +/// 𝑛-bit integer to square +/// ## result +/// 2𝑛-bit result, must be in state |0⟩ +/// initially. +/// +/// # Remarks +/// The implementation relies on `SquareI`. operation SquareSI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { body (...) { Controlled SquareSI([], (xs, result)); @@ -26,6 +38,21 @@ operation SquareSI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { } } +/// # Summary +/// Computes the square of the integer `xs` into `result`, +/// which must be zero initially. +/// +/// # Input +/// ## xs +/// 𝑛-bit number to square +/// ## result +/// 2𝑛-bit result, must be in state |0⟩ initially. +/// +/// # Remarks +/// Uses a standard shift-and-add approach to compute the square. Saves +/// 𝑛-1 qubits compared to the straight-forward solution which first +/// copies out `xs` before applying a regular multiplier and then undoing +/// the copy operation. operation SquareI(xs : Qubit[], result : Qubit[]) : Unit { body (...) { Controlled SquareI([], (xs, result)); @@ -73,7 +100,23 @@ operation SquareI(xs : Qubit[], result : Qubit[]) : Unit { } - +/// # Summary +/// Multiply integer `xs` by integer `ys` and store the result in `result`, +/// which must be zero initially. +/// +/// # Input +/// ## xs +/// 𝑛₁-bit multiplicand +/// ## ys +/// 𝑛₂-bit multiplier +/// ## result +/// (𝑛₁+𝑛₂)-bit result, must be in state |0⟩ initially. +/// +/// # Remarks +/// Uses a standard shift-and-add approach to implement the multiplication. +/// The controlled version was improved by copying out π‘₯α΅’ to an ancilla +/// qubit conditioned on the control qubits, and then controlling the +/// addition on the ancilla qubit. operation MultiplyI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { body (...) { let na = Length(xs); @@ -117,7 +160,18 @@ operation MultiplyI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj } } - +/// # Summary +/// Multiply signed integer `xs` by signed integer `ys` and store +/// the result in `result`, which must be zero initially. +/// +/// # Input +/// ## xs +/// 𝑛₁-bit multiplicand +/// ## ys +/// 𝑛₂-bit multiplier +/// ## result +/// (𝑛₁+𝑛₂)-bit result, must be in state |0⟩ +/// initially. operation MultiplySI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit { body (...) { Controlled MultiplySI([], (xs, ys, result)); @@ -216,6 +270,27 @@ operation Invert2sSI(xs : Qubit[]) : Unit is Adj + Ctl { } } +/// # Summary +/// Divides two quantum integers. +/// +/// # Description +/// `xs` will hold the +/// remainder `xs - floor(xs/ys) * ys` and `result` will hold +/// `floor(xs/ys)`. +/// +/// # Input +/// ## xs +/// $n$-bit dividend, will be replaced by the remainder. +/// ## ys +/// $n$-bit divisor +/// ## result +/// $n$-bit result, must be in state $\ket{0}$ initially +/// and will be replaced by the result of the integer division. +/// +/// # Remarks +/// Uses a standard shift-and-subtract approach to implement the division. +/// The controlled version is specialized such the subtraction does not +/// require additional controls. operation DivideI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { body (...) { Controlled DivideI([], (xs, ys, result)); @@ -274,4 +349,13 @@ operation ComputeReciprocalI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl } } -export Sum, MultiplyI, MultiplySI, SquareSI, SquareI, AddI, Invert2sSI, DivideI, ComputeReciprocalI; \ No newline at end of file +export + Sum, + MultiplyI, + MultiplySI, + SquareSI, + SquareI, + AddI, + Invert2sSI, + DivideI, + ComputeReciprocalI; \ No newline at end of file diff --git a/library/signed/src/RippleCarry.qs b/library/signed/src/RippleCarry.qs index fad2816bd7..a3d932b94d 100644 --- a/library/signed/src/RippleCarry.qs +++ b/library/signed/src/RippleCarry.qs @@ -409,8 +409,10 @@ operation RippleCarryAdderNoCarryTTK(xs : Qubit[], ys : Qubit[]) : Unit is Adj + CNOT(xs[0], ys[0]); } - - - - -export RippleCarryAdderNoCarryTTK, ApplyInnerTTKAdderWithoutCarry, RippleCarryAdderTTK, RippleCarryAdderCDKM, RippleCarryAdderD, Carry; \ No newline at end of file +export + RippleCarryAdderNoCarryTTK, + ApplyInnerTTKAdderWithoutCarry, + RippleCarryAdderTTK, + RippleCarryAdderCDKM, + RippleCarryAdderD, + Carry; \ No newline at end of file diff --git a/library/signed/src/Utils.qs b/library/signed/src/Utils.qs index 69394050ba..8e0bcbfcb5 100644 --- a/library/signed/src/Utils.qs +++ b/library/signed/src/Utils.qs @@ -52,5 +52,4 @@ operation ApplyCCNOTChain(register : Qubit[], targets : Qubit[]) : Unit is Adj + ); ApplyToEachCA(CCNOT, Zipped3(register, Most(targets), Rest(targets))); -} - +} \ No newline at end of file From dfc386a3a3b381812cd60fe67efad2bacc3f6acd Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 14 Aug 2024 16:06:53 -0700 Subject: [PATCH 05/15] wip --- library/signed/qsharp.json | 1 + library/signed/src/Tests.qs | 85 ++++++++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/library/signed/qsharp.json b/library/signed/qsharp.json index 5001a1c231..45472c573a 100644 --- a/library/signed/qsharp.json +++ b/library/signed/qsharp.json @@ -3,6 +3,7 @@ "license": "MIT", "files": [ "src/Comparison.qs", + "src/Measurement.qs", "src/Operations.qs", "src/RippleCarry.qs", "src/Tests.qs", diff --git a/library/signed/src/Tests.qs b/library/signed/src/Tests.qs index 4fdf1cb3ec..d09cfa46ec 100644 --- a/library/signed/src/Tests.qs +++ b/library/signed/src/Tests.qs @@ -1,3 +1,5 @@ +import Operations.Invert2sSI; +import Measurement.MeasureSignedInteger; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. @@ -5,6 +7,37 @@ import Std.Diagnostics.Fact; /// This entrypoint runs tests for the signed integer library. operation Main() : Unit { + UnsignedTests(); + SignedTests(); + +} +operation SignedTests() : Unit { + use a = Qubit[32]; + use b = Qubit[32]; + use c = Qubit[64]; + + // 0b11111110 (-2 in twos complement) * 0b00000001 == 0b11111110 (-2) + X(a[1]); + Operations.Invert2sSI(a); + X(b[0]); + TestSignedIntOp(Operations.MultiplySI, a, b, c, -2); + + // 0b11111110 (-2 in twos complement) * 0b11111111 (-1 in twos complement) == 0b00000010 (2) + X(a[1]); + Operations.Invert2sSI(a); + X(b[0]); + Operations.Invert2sSI(b); + TestSignedIntOp(Operations.MultiplySI, a, b, c, 2); + + + // 0b11111110 (-2 in twos complement) squared is 0b00000100 (4) + X(a[1]); + Operations.Invert2sSI(a); + TestSignedIntOp((a, b, _) => Operations.SquareSI(a, c), a, b, c, 4); + +} + +operation UnsignedTests() : Unit { use a = Qubit[2]; use b = Qubit[2]; use c = Qubit[4]; @@ -12,24 +45,66 @@ operation Main() : Unit { // 0b10 * 0b01 == 0b10 (1 * 2 = 2) X(a[0]); X(b[1]); - TestOp(Operations.MultiplyI, a, b, c, 2); + TestIntOp(Operations.MultiplyI, a, b, c, 2); // 0b01 * 0b10 == 0b10 (1 * 2 = 2) X(a[1]); X(b[0]); - TestOp(Operations.MultiplyI, a, b, c, 2); + TestIntOp(Operations.MultiplyI, a, b, c, 2); // 0b11 * 0b11 == 0b1001 (3 * 3 = 9) X(a[0]); X(b[0]); X(a[1]); X(b[1]); - TestOp(Operations.MultiplyI, a, b, c, 9); + TestIntOp(Operations.MultiplyI, a, b, c, 9); + + + use a = Qubit[8]; + use b = Qubit[8]; + use c = Qubit[16]; + + // 0b00001010 * 0b00001011 == 0b01100100 (10 * 11 = 110) + X(a[1]); + X(a[3]); + X(b[1]); + X(b[3]); + X(b[0]); + TestIntOp(Operations.MultiplyI, a, b, c, 110); + + // 0b00001010 ^ 2 = 0b01100100 (10 ^ 2 = 100) + X(a[1]); + X(a[3]); + TestIntOp((a, b, _) => Operations.SquareI(a, c), a, b, c, 100); + + // 0b00001010 / 0b00000010 == 0b00000101 (10 / 2 = 5) + X(a[1]); + X(a[3]); + X(b[1]); + // need smaller result register for div, mod, etc + use d = Qubit[8]; + TestIntOp(Operations.DivideI, a, b, d, 5); + + // 0b00001010 + 0b00000011 == 0b00001101 (10 + 3 = 13) + X(a[1]); + X(a[3]); + X(b[0]); + X(b[1]); + TestIntOp((a, b, _) => Operations.AddI(a, b), a, b, b, 13); + + } -operation TestOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Qubit[], c : Qubit[], expect : Int) : Unit { +operation TestIntOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Qubit[], c : Qubit[], expect : Int) : Unit { op(a, b, c); let res = MeasureInteger(c); Fact(res == expect, $"Expected {expect}, got {res}"); ResetAll(a + b + c); -} \ No newline at end of file +} + +operation TestSignedIntOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Qubit[], c : Qubit[], expect : Int) : Unit { + op(a, b, c); + let res = MeasureSignedInteger(c); + Fact(res == expect, $"Expected {expect}, got {res}"); + ResetAll(a + b + c); +} From d065478442bb5a3c801fa48c6ed09b181d3ca5a9 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 15 Aug 2024 08:44:23 -0700 Subject: [PATCH 06/15] add measuresignedint --- library/signed/src/Tests.qs | 58 +++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/library/signed/src/Tests.qs b/library/signed/src/Tests.qs index d09cfa46ec..f4e12ded4f 100644 --- a/library/signed/src/Tests.qs +++ b/library/signed/src/Tests.qs @@ -1,21 +1,61 @@ -import Operations.Invert2sSI; -import Measurement.MeasureSignedInteger; // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import Std.Diagnostics.Fact; +import Operations.Invert2sSI; +import Measurement.MeasureSignedInteger; /// This entrypoint runs tests for the signed integer library. operation Main() : Unit { - UnsignedTests(); - SignedTests(); + UnsignedOpTests(); + MeasureSignedIntTests(); + SignedOpTests(); } -operation SignedTests() : Unit { + +operation MeasureSignedIntTests() : Unit { + use a = Qubit[4]; + + // 0b0001 == 1 + X(a[0]); + let res = MeasureSignedInteger(a, 4); + Fact(res == 1, $"Expected 1, received {res}"); + + // 0b1111 == -1 + X(a[0]); + X(a[1]); + X(a[2]); + X(a[3]); + let res = MeasureSignedInteger(a, 4); + Fact(res == -1, $"Expected -1, received {res}"); + + // 0b01000 == 8 + use a = Qubit[5]; + X(a[3]); + let res = MeasureSignedInteger(a, 5); + Fact(res == 8, $"Expected 8, received {res}"); + + // 0b11110 == -2 + X(a[1]); + X(a[2]); + X(a[3]); + X(a[4]); + let res = MeasureSignedInteger(a, 5); + Fact(res == -2, $"Expected -2, received {res}"); + + // 0b11000 == -8 + X(a[3]); + X(a[4]); + let res = MeasureSignedInteger(a, 5); + Fact(res == -8, $"Expected -8, received {res}"); + +} + +operation SignedOpTests() : Unit { use a = Qubit[32]; use b = Qubit[32]; use c = Qubit[64]; - + // 0b11111110 (-2 in twos complement) * 0b00000001 == 0b11111110 (-2) X(a[1]); Operations.Invert2sSI(a); @@ -34,10 +74,10 @@ operation SignedTests() : Unit { X(a[1]); Operations.Invert2sSI(a); TestSignedIntOp((a, b, _) => Operations.SquareSI(a, c), a, b, c, 4); - + } -operation UnsignedTests() : Unit { +operation UnsignedOpTests() : Unit { use a = Qubit[2]; use b = Qubit[2]; use c = Qubit[4]; @@ -104,7 +144,7 @@ operation TestIntOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Q operation TestSignedIntOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Qubit[], c : Qubit[], expect : Int) : Unit { op(a, b, c); - let res = MeasureSignedInteger(c); + let res = MeasureSignedInteger(c, 64); Fact(res == expect, $"Expected {expect}, got {res}"); ResetAll(a + b + c); } From 8380c353e9e34c0439746720a40dcf4a29c6866d Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 15 Aug 2024 12:40:09 -0700 Subject: [PATCH 07/15] wip --- library/signed/qsharp.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/signed/qsharp.json b/library/signed/qsharp.json index 45472c573a..e7351cc151 100644 --- a/library/signed/qsharp.json +++ b/library/signed/qsharp.json @@ -1,6 +1,16 @@ { "author": "Microsoft", "license": "MIT", + "dependencies": { + "Unstable": { + "github": { + "owner": "Microsoft", + "repo": "qsharp", + "ref": "main", + "path": "library/unstable" + } + } + }, "files": [ "src/Comparison.qs", "src/Measurement.qs", From 072cd9ffa97f956d53992f372148fd3fbaacc4e0 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 15 Aug 2024 13:13:27 -0700 Subject: [PATCH 08/15] use unstable for ripple carry --- library/signed/qsharp.json | 1 - library/signed/src/Operations.qs | 6 +- library/signed/src/RippleCarry.qs | 418 ------------------------------ library/signed/src/Tests.qs | 2 - 4 files changed, 3 insertions(+), 424 deletions(-) delete mode 100644 library/signed/src/RippleCarry.qs diff --git a/library/signed/qsharp.json b/library/signed/qsharp.json index e7351cc151..4355d48a9c 100644 --- a/library/signed/qsharp.json +++ b/library/signed/qsharp.json @@ -15,7 +15,6 @@ "src/Comparison.qs", "src/Measurement.qs", "src/Operations.qs", - "src/RippleCarry.qs", "src/Tests.qs", "src/Utils.qs" ] diff --git a/library/signed/src/Operations.qs b/library/signed/src/Operations.qs index ebefe57644..b8186c9421 100644 --- a/library/signed/src/Operations.qs +++ b/library/signed/src/Operations.qs @@ -3,7 +3,7 @@ import Std.Arrays.Tail, Std.Arrays.Most, Std.Arrays.Enumerated; import Utils.AndLadder; -import RippleCarry.RippleCarryAdderNoCarryTTK, RippleCarry.RippleCarryAdderTTK; +import Unstable.Arithmetic.RippleCarryTTKIncByLE; import Std.Diagnostics.Fact; import Comparison.CompareGTI; @@ -236,10 +236,10 @@ operation Sum(carryIn : Qubit, summand1 : Qubit, summand2 : Qubit) : Unit is Adj /// Addend with at least $n$ qubits. Will hold the result. operation AddI(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { if Length(xs) == Length(ys) { - RippleCarryAdderNoCarryTTK(xs, ys); + RippleCarryTTKIncByLE(xs, ys); } elif Length(ys) > Length(xs) { use qs = Qubit[Length(ys) - Length(xs) - 1]; - RippleCarryAdderTTK(xs + qs, Most(ys), Tail(ys)); + RippleCarryTTKIncByLE(xs + qs, ys); } else { fail "xs must not contain more qubits than ys"; } diff --git a/library/signed/src/RippleCarry.qs b/library/signed/src/RippleCarry.qs deleted file mode 100644 index a3d932b94d..0000000000 --- a/library/signed/src/RippleCarry.qs +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import Std.Diagnostics.Fact; -import Operations.Sum; -import Std.Arrays.Most, Std.Arrays.Rest, Std.Arrays.Zipped; - -/// # Summary -/// Implements a reversible carry gate. Given a carry-in bit encoded in -/// qubit `carryIn` and two summand bits encoded in `summand1` and `summand2`, -/// computes the bitwise xor of `carryIn`, `summand1` and `summand2` in the -/// qubit `summand2` and the carry-out is xored to the qubit `carryOut`. -/// -/// # Input -/// ## carryIn -/// Carry-in qubit. -/// ## summand1 -/// First summand qubit. -/// ## summand2 -/// Second summand qubit, is replaced with the lower bit of the sum of -/// `summand1` and `summand2`. -/// ## carryOut -/// Carry-out qubit, will be xored with the higher bit of the sum. -operation Carry(carryIn : Qubit, summand1 : Qubit, summand2 : Qubit, carryOut : Qubit) : Unit is Adj + Ctl { - CCNOT(summand1, summand2, carryOut); - CNOT(summand1, summand2); - CCNOT(carryIn, summand2, carryOut); -} - - -/// # Summary -/// Reversible, in-place ripple-carry addition of two integers. -/// -/// # Description -/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, -/// and a qubit carry, the operation computes the sum of the two integers -/// where the $n$ least significant bits of the result are held in `ys` and -/// the carry out bit is xored to the qubit `carry`. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer summand. -/// ## ys -/// LittleEndian qubit register encoding the second integer summand, is -/// modified to hold the $n$ least significant bits of the sum. -/// ## carry -/// Carry qubit, is xored with the most significant bit of the sum. -/// -/// # References -/// - Thomas G. Draper: "Addition on a Quantum Computer", 2000. -/// https://arxiv.org/abs/quant-ph/0008033 -/// -/// # Remarks -/// The specified controlled operation makes use of symmetry and mutual -/// cancellation of operations to improve on the default implementation -/// that adds a control to every operation. -operation RippleCarryAdderD(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { - body (...) { - Controlled RippleCarryAdderD([], (xs, ys, carry)); - } - controlled (controls, ...) { - let nQubits = Length(xs); - - Fact( - nQubits == Length(ys), - "Input registers must have the same number of qubits." - ); - - use auxRegister = Qubit[nQubits]; - for idx in 0..(nQubits-2) { - Carry(auxRegister[idx], xs[idx], ys[idx], auxRegister[idx + 1]); // (1) - } - Controlled Carry(controls, (auxRegister[nQubits-1], xs[nQubits-1], ys[nQubits-1], carry)); - Controlled CNOT(controls, (xs[nQubits-1], ys[nQubits-1])); - Controlled Sum(controls, (auxRegister[nQubits-1], xs[nQubits-1], ys[nQubits-1])); - for idx in (nQubits-2)..(-1)..0 { - Adjoint Carry(auxRegister[idx], xs[idx], ys[idx], auxRegister[idx + 1]); // cancels with (1) - Controlled Sum(controls, (auxRegister[idx], xs[idx], ys[idx])); - } - } -} - -/// # Summary -/// Reversible, in-place ripple-carry operation that is used in the -/// integer addition operation RippleCarryAdderCDKM below. -/// Given two qubit registers `xs` and `ys` of the same length, the operation -/// applies a ripple carry sequence of CNOT and CCNOT gates with qubits -/// in `xs` and `ys` as the controls and qubits in `xs` as the targets. -/// -/// # Input -/// ## xs -/// First qubit register, containing controls and targets. -/// ## ys -/// Second qubit register, contributing to the controls. -/// ## ancilla -/// The ancilla qubit used in RippleCarryAdderCDKM passed to this operation. -/// -/// # References -/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David -/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. -/// https://arxiv.org/abs/quant-ph/0410184v1 -operation ApplyOuterCDKMAdder(xs : Qubit[], ys : Qubit[], ancilla : Qubit) : Unit is Adj + Ctl { - let nQubits = Length(xs); - - Fact( - nQubits == Length(ys), - "Input registers must have the same number of qubits." - ); - - Fact( - nQubits >= 3, - "Need at least 3 qubits per register." - ); - - CNOT(xs[2], xs[1]); - CCNOT(ancilla, ys[1], xs[1]); - for idx in 2..nQubits - 2 { - CNOT(xs[idx + 1], xs[idx]); - CCNOT(xs[idx-1], ys[idx], xs[idx]); - } -} - -/// # Summary -/// The core operation in the RippleCarryAdderCDKM, used with the above -/// ApplyOuterCDKMAdder operation, i.e. conjugated with this operation to obtain -/// the inner operation of the RippleCarryAdderCDKM. This operation computes -/// the carry out qubit and applies a sequence of NOT gates on part of the input `ys`. -/// -/// # Input -/// ## xs -/// First qubit register. -/// ## ys -/// Second qubit register. -/// ## ancilla -/// The ancilla qubit used in RippleCarryAdderCDKM passed to this operation. -/// ## carry -/// Carry out qubit in the RippleCarryAdderCDKM operation. -/// -/// # References -/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David -/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. -/// https://arxiv.org/abs/quant-ph/0410184v1 -operation CarryOutCoreCDKM( - xs : Qubit[], - ys : Qubit[], - ancilla : Qubit, - carry : Qubit -) : Unit is Adj + Ctl { - let nQubits = Length(xs); - - Fact( - nQubits == Length(ys), - "Input registers must have the same number of qubits." - ); - - CNOT(xs[nQubits - 1], carry); - CCNOT(xs[nQubits - 2], ys[nQubits - 1], carry); - ApplyToEachCA(X, Most(Rest(ys))); // X on ys[1..(nQubits-2)] - CNOT(ancilla, ys[1]); - ApplyToEachCA(CNOT, Zipped(Rest(Most(xs)), Rest(Rest(ys)))); -} - -/// # Summary -/// Reversible, in-place ripple-carry addition of two integers. -/// -/// # Description -/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, -/// and a qubit carry, the operation computes the sum of the two integers -/// where the $n$ least significant bits of the result are held in `ys` and -/// the carry out bit is xored to the qubit `carry`. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer summand. -/// ## ys -/// LittleEndian qubit register encoding the second integer summand, is -/// modified to hold the n least significant bits of the sum. -/// ## carry -/// Carry qubit, is xored with the most significant bit of the sum. -/// -/// # References -/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David -/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. -/// https://arxiv.org/abs/quant-ph/0410184v1 -/// -/// # Remarks -/// This operation has the same functionality as RippleCarryAdderD, but -/// only uses one auxiliary qubit instead of $n$. -operation RippleCarryAdderCDKM(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { - let nQubits = Length(xs); - - Fact( - nQubits == Length(ys), - "Input registers must have the same number of qubits." - ); - - use auxiliary = Qubit(); - within { - ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); - CNOT(xs[1], auxiliary); - CCNOT(xs[0], ys[0], auxiliary); - ApplyOuterCDKMAdder(xs, ys, auxiliary); - } apply { - CarryOutCoreCDKM(xs, ys, auxiliary, carry); - } - ApplyToEachCA(X, Most(Rest(ys))); - CNOT(xs[0], ys[0]); -} - -/// # Summary -/// Implements the inner addition function for the operation -/// RippleCarryAdderTTK. This is the inner operation that is conjugated -/// with the outer operation to construct the full adder. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer summand -/// input to RippleCarryAdderTTK. -/// ## ys -/// LittleEndian qubit register encoding the second integer summand -/// input to RippleCarryAdderTTK. -/// ## carry -/// Carry qubit, is xored with the most significant bit of the sum. -/// -/// # References -/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum -/// Addition Circuits and Unbounded Fan-Out", Quantum Information and -/// Computation, Vol. 10, 2010. -/// https://arxiv.org/abs/0910.2530 -/// -/// # Remarks -/// The specified controlled operation makes use of symmetry and mutual -/// cancellation of operations to improve on the default implementation -/// that adds a control to every operation. -operation ApplyInnerTTKAdder(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { - body (...) { - (Controlled ApplyInnerTTKAdder)([], (xs, ys, carry)); - } - controlled (controls, ...) { - let nQubits = Length(xs); - - for idx in 0..nQubits - 2 { - CCNOT(xs[idx], ys[idx], xs[idx + 1]); - } - (Controlled CCNOT)(controls, (xs[nQubits-1], ys[nQubits-1], carry)); - for idx in nQubits - 1..-1..1 { - Controlled CNOT(controls, (xs[idx], ys[idx])); - CCNOT(xs[idx-1], ys[idx-1], xs[idx]); - } - } -} - -/// # Summary -/// Implements the outer operation for RippleCarryAdderTTK to conjugate -/// the inner operation to construct the full adder. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer summand -/// input to RippleCarryAdderTTK. -/// ## ys -/// LittleEndian qubit register encoding the second integer summand -/// input to RippleCarryAdderTTK. -/// -/// # References -/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum -/// Addition Circuits and Unbounded Fan-Out", Quantum Information and -/// Computation, Vol. 10, 2010. -/// https://arxiv.org/abs/0910.2530 -operation ApplyOuterTTKAdder(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { - let nQubits = Length(xs); - - Fact( - nQubits == Length(ys), - "Input registers must have the same number of qubits." - ); - - ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); - Adjoint ApplyCNOTChain(Rest(xs)); -} - -/// # Summary -/// Reversible, in-place ripple-carry addition of two integers. -/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, -/// and a qubit carry, the operation computes the sum of the two integers -/// where the $n$ least significant bits of the result are held in `ys` and -/// the carry out bit is xored to the qubit `carry`. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer summand. -/// ## ys -/// LittleEndian qubit register encoding the second integer summand, is -/// modified to hold the $n$ least significant bits of the sum. -/// ## carry -/// Carry qubit, is xored with the most significant bit of the sum. -/// -/// # References -/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum -/// Addition Circuits and Unbounded Fan-Out", Quantum Information and -/// Computation, Vol. 10, 2010. -/// https://arxiv.org/abs/0910.2530 -/// -/// # Remarks -/// This operation has the same functionality as RippleCarryAdderD and, -/// RippleCarryAdderCDKM but does not use any ancilla qubits. -operation RippleCarryAdderTTK(xs : Qubit[], ys : Qubit[], carry : Qubit) : Unit is Adj + Ctl { - let nQubits = Length(xs); - - - if (nQubits > 1) { - CNOT(xs[nQubits-1], carry); - within { - ApplyOuterTTKAdder(xs, ys); - } apply { - ApplyInnerTTKAdder(xs, ys, carry); - } - } else { - CCNOT(xs[0], ys[0], carry); - } - CNOT(xs[0], ys[0]); -} - -/// # Summary -/// Implements the inner addition function for the operation -/// RippleCarryAdderNoCarryTTK. This is the inner operation that is conjugated -/// with the outer operation to construct the full adder. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer summand -/// input to RippleCarryAdderNoCarryTTK. -/// ## ys -/// LittleEndian qubit register encoding the second integer summand -/// input to RippleCarryAdderNoCarryTTK. -/// -/// # References -/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum -/// Addition Circuits and Unbounded Fan-Out", Quantum Information and -/// Computation, Vol. 10, 2010. -/// https://arxiv.org/abs/0910.2530 -/// -/// # Remarks -/// The specified controlled operation makes use of symmetry and mutual -/// cancellation of operations to improve on the default implementation -/// that adds a control to every operation. -operation ApplyInnerTTKAdderWithoutCarry(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { - body (...) { - (Controlled ApplyInnerTTKAdderWithoutCarry)([], (xs, ys)); - } - controlled (controls, ...) { - let nQubits = Length(xs); - - Fact( - nQubits == Length(ys), - "Input registers must have the same number of qubits." - ); - - for idx in 0..nQubits - 2 { - CCNOT(xs[idx], ys[idx], xs[idx + 1]); - } - for idx in nQubits - 1..-1..1 { - Controlled CNOT(controls, (xs[idx], ys[idx])); - CCNOT(xs[idx - 1], ys[idx - 1], xs[idx]); - } - } -} - -/// # Summary -/// Reversible, in-place ripple-carry addition of two integers without carry out. -/// -/// # Description -/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`, -/// the operation computes the sum of the two integers modulo $2^n$, -/// where $n$ is the bit size of the inputs `xs` and `ys`. It does not compute -/// the carry out bit. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer summand. -/// ## ys -/// LittleEndian qubit register encoding the second integer summand, is -/// modified to hold the $n$ least significant bits of the sum. -/// -/// # References -/// - Yasuhiro Takahashi, Seiichiro Tani, Noboru Kunihiro: "Quantum -/// Addition Circuits and Unbounded Fan-Out", Quantum Information and -/// Computation, Vol. 10, 2010. -/// https://arxiv.org/abs/0910.2530 -/// -/// # Remarks -/// This operation has the same functionality as RippleCarryAdderTTK but does -/// not return the carry bit. -operation RippleCarryAdderNoCarryTTK(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { - let nQubits = Length(xs); - - Fact( - nQubits == Length(ys), - "Input registers must have the same number of qubits." - ); - - if (nQubits > 1) { - within { - ApplyOuterTTKAdder(xs, ys); - } apply { - ApplyInnerTTKAdderWithoutCarry(xs, ys); - } - } - CNOT(xs[0], ys[0]); -} - -export - RippleCarryAdderNoCarryTTK, - ApplyInnerTTKAdderWithoutCarry, - RippleCarryAdderTTK, - RippleCarryAdderCDKM, - RippleCarryAdderD, - Carry; \ No newline at end of file diff --git a/library/signed/src/Tests.qs b/library/signed/src/Tests.qs index f4e12ded4f..b997a1aa03 100644 --- a/library/signed/src/Tests.qs +++ b/library/signed/src/Tests.qs @@ -131,8 +131,6 @@ operation UnsignedOpTests() : Unit { X(b[0]); X(b[1]); TestIntOp((a, b, _) => Operations.AddI(a, b), a, b, b, 13); - - } operation TestIntOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Qubit[], c : Qubit[], expect : Int) : Unit { From 61a1bedea507f5ec25eaa5bd70320c59619a0df5 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 15 Aug 2024 15:22:29 -0700 Subject: [PATCH 09/15] add missed file --- library/signed/src/Measurement.qs | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 library/signed/src/Measurement.qs diff --git a/library/signed/src/Measurement.qs b/library/signed/src/Measurement.qs new file mode 100644 index 0000000000..f9ff7861d8 --- /dev/null +++ b/library/signed/src/Measurement.qs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import Std.Diagnostics.Fact; +import Operations.Invert2sSI; + +/// # Summary +/// Measures a signed integer of a given width. +/// If the width is 4, the qubit register will be measured as a 4-bit signed integer. +/// This means that bit n is the sign bit, and bits n-1 to 0 are the integer value. +/// If fewer than `width` qubits are provided, the remaining bits are assumed to be 0. +/// For example, if qubit register `[q1, q2, q3]` is passed in, but the width is 5, +/// the integer value will be measured as `[0, 0, q1, q2, q3]`, with the first 0 being +/// the sign bit (positive). This is in contrast to the standard library `MeasureInteger`, +/// which always measures unsigned integers up to and including 63 qubits in width. +/// If the length of the qubit register passed in is greater than the width, this operation +/// will throw an error. +/// +/// # Input +/// ## target +/// A qubit register representing the signed integer to be measured. +/// +/// ## width +/// The width of the signed integer to be measured. +operation MeasureSignedInteger(target : Qubit[], width : Int) : Int { + let nBits = Length(target); + Fact(nBits <= 64, $"`Length(target)` must be less than or equal to 64, but was {nBits}."); + Fact(nBits <= width, $"`Length(target)` must be less than or equal to `width`, but was {nBits}."); + + mutable coefficient = 1; + let signBit = MResetZ(target[nBits - 1]); + if (signBit == One) { + Operations.Invert2sSI(target); + set coefficient = -1; + } + + mutable number = 0; + for i in 0..nBits - 2 { + if (MResetZ(target[i]) == One) { + set number |||= 1 <<< i; + } + } + + ResetAll(target); + + number * coefficient +} + +export MeasureSignedInteger; \ No newline at end of file From 9081137602f139adba9de82960268ffe191ecb2c Mon Sep 17 00:00:00 2001 From: sezna Date: Mon, 19 Aug 2024 10:54:07 -0700 Subject: [PATCH 10/15] update ref for unstable dependency --- library/signed/qsharp.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/signed/qsharp.json b/library/signed/qsharp.json index 4355d48a9c..3e21747f14 100644 --- a/library/signed/qsharp.json +++ b/library/signed/qsharp.json @@ -6,7 +6,7 @@ "github": { "owner": "Microsoft", "repo": "qsharp", - "ref": "main", + "ref": "d1fb2a1", "path": "library/unstable" } } @@ -18,4 +18,4 @@ "src/Tests.qs", "src/Utils.qs" ] -} \ No newline at end of file +} From e3e04eef809b83943194a6c62bff8cd0482d67cc Mon Sep 17 00:00:00 2001 From: Alex Hansen Date: Wed, 21 Aug 2024 19:14:35 -0700 Subject: [PATCH 11/15] Update library/signed/src/Comparison.qs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: CΓ©sar Zaragoza CortΓ©s --- library/signed/src/Comparison.qs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/library/signed/src/Comparison.qs b/library/signed/src/Comparison.qs index be5b8f9721..9931e76cc9 100644 --- a/library/signed/src/Comparison.qs +++ b/library/signed/src/Comparison.qs @@ -16,14 +16,15 @@ import Utils.ApplyCCNOTChain; /// Will be flipped if $xs > ys$ operation CompareGTSI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { use tmp = Qubit(); - CNOT(Tail(xs), tmp); - CNOT(Tail(ys), tmp); - X(tmp); - Controlled CompareGTI([tmp], (xs, ys, result)); - X(tmp); - CCNOT(tmp, Tail(ys), result); - CNOT(Tail(xs), tmp); - CNOT(Tail(ys), tmp); + within { + CNOT(Tail(xs), tmp); + CNOT(Tail(ys), tmp); + } apply { + X(tmp); + Controlled CompareGTI([tmp], (xs, ys, result)); + X(tmp); + CCNOT(tmp, Tail(ys), result); + } } From 7db566eba0bdf8a59ff3d6c7239b24396085c263 Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 21 Aug 2024 19:19:44 -0700 Subject: [PATCH 12/15] PR feedback --- library/signed/src/Comparison.qs | 4 +++- library/signed/src/Measurement.qs | 1 + library/signed/src/Operations.qs | 17 ++++++----------- library/signed/src/Utils.qs | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/library/signed/src/Comparison.qs b/library/signed/src/Comparison.qs index 9931e76cc9..a77cd0ad10 100644 --- a/library/signed/src/Comparison.qs +++ b/library/signed/src/Comparison.qs @@ -2,6 +2,7 @@ // Licensed under the MIT License. import Std.Arrays.Tail, Std.Arrays.Zipped, Std.Arrays.Most, Std.Arrays.Rest; +import Std.Diagnostics.Fact; import Utils.ApplyCCNOTChain; /// # Summary @@ -23,7 +24,7 @@ operation CompareGTSI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj X(tmp); Controlled CompareGTI([tmp], (xs, ys, result)); X(tmp); - CCNOT(tmp, Tail(ys), result); + CCNOT(tmp, Tail(ys), result); } } @@ -76,6 +77,7 @@ operation GreaterThan(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj (Controlled GreaterThan)([], (xs, ys, result)); } controlled (controls, ...) { + Fact(Length(xs) == Length(ys), "Input qubit arrays must have the same length."); let nQubits = Length(xs); if (nQubits == 1) { diff --git a/library/signed/src/Measurement.qs b/library/signed/src/Measurement.qs index f9ff7861d8..95d0213c8b 100644 --- a/library/signed/src/Measurement.qs +++ b/library/signed/src/Measurement.qs @@ -26,6 +26,7 @@ operation MeasureSignedInteger(target : Qubit[], width : Int) : Int { let nBits = Length(target); Fact(nBits <= 64, $"`Length(target)` must be less than or equal to 64, but was {nBits}."); Fact(nBits <= width, $"`Length(target)` must be less than or equal to `width`, but was {nBits}."); + Fact(nBits > 0, $"`width` must be greater than 0, but was {width}."); mutable coefficient = 1; let signBit = MResetZ(target[nBits - 1]); diff --git a/library/signed/src/Operations.qs b/library/signed/src/Operations.qs index b8186c9421..05d54d3b05 100644 --- a/library/signed/src/Operations.qs +++ b/library/signed/src/Operations.qs @@ -53,7 +53,7 @@ operation SquareSI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { /// 𝑛-1 qubits compared to the straight-forward solution which first /// copies out `xs` before applying a regular multiplier and then undoing /// the copy operation. -operation SquareI(xs : Qubit[], result : Qubit[]) : Unit { +operation SquareI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { body (...) { Controlled SquareI([], (xs, result)); } @@ -81,25 +81,22 @@ operation SquareI(xs : Qubit[], result : Qubit[]) : Unit { } } } else { - use helper = Qubit[numControls]; + use aux = Qubit[numControls]; within { - AndLadder(controls, Most(helper)); + AndLadder(controls, Most(aux)); } apply { for (idx, ctl) in Enumerated(xs) { within { - AND(Tail(Most(helper)), ctl, Tail(helper)); + AND(Tail(Most(aux)), ctl, Tail(aux)); } apply { - Controlled AddI([Tail(helper)], (xs, (result[idx..idx + n]))); + Controlled AddI([Tail(aux)], (xs, (result[idx..idx + n]))); } } } } } - adjoint auto; - controlled adjoint auto; } - /// # Summary /// Multiply integer `xs` by integer `ys` and store the result in `result`, /// which must be zero initially. @@ -172,7 +169,7 @@ operation MultiplyI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj /// ## result /// (𝑛₁+𝑛₂)-bit result, must be in state |0⟩ /// initially. -operation MultiplySI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit { +operation MultiplySI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { body (...) { Controlled MultiplySI([], (xs, ys, result)); } @@ -196,8 +193,6 @@ operation MultiplySI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit { } } } - adjoint auto; - controlled adjoint auto; } diff --git a/library/signed/src/Utils.qs b/library/signed/src/Utils.qs index 8e0bcbfcb5..7f8c9cf01c 100644 --- a/library/signed/src/Utils.qs +++ b/library/signed/src/Utils.qs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// Util functions for interal use by the signed integer library. +// Util functions for internal use by the signed integer library. import Std.Math.Min; import Std.Diagnostics.Fact; From c15458861657518f71d34de97fd8bf504169d6ca Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 21 Aug 2024 19:40:04 -0700 Subject: [PATCH 13/15] updates for some PR feedback --- library/signed/src/Comparison.qs | 57 -------------------------------- library/signed/src/Operations.qs | 42 +++++------------------ library/signed/src/Tests.qs | 7 ---- 3 files changed, 9 insertions(+), 97 deletions(-) diff --git a/library/signed/src/Comparison.qs b/library/signed/src/Comparison.qs index a77cd0ad10..dcfad2cffd 100644 --- a/library/signed/src/Comparison.qs +++ b/library/signed/src/Comparison.qs @@ -43,63 +43,6 @@ operation CompareGTI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + GreaterThan(xs, ys, result); } -/// # Summary -/// Applies a greater-than comparison between two integers encoded into -/// qubit registers, flipping a target qubit based on the result of the -/// comparison. -/// -/// # Description -/// Carries out a strictly greater than comparison of two integers $x$ and $y$, encoded -/// in qubit registers xs and ys. If $x > y$, then the result qubit will be flipped, -/// otherwise the result qubit will retain its state. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer $x$. -/// ## ys -/// LittleEndian qubit register encoding the second integer $y$. -/// ## result -/// Single qubit that will be flipped if $x > y$. -/// -/// # References -/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David -/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. -/// https://arxiv.org/abs/quant-ph/0410184v1 -/// -/// - Thomas Haener, Martin Roetteler, Krysta M. Svore: "Factoring using 2n+2 qubits -/// with Toffoli based modular multiplication", 2016 -/// https://arxiv.org/abs/1611.07995 -/// -/// # Remarks -/// Uses the trick that $x - y = (x'+y)'$, where ' denotes the one's complement. -operation GreaterThan(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { - body (...) { - (Controlled GreaterThan)([], (xs, ys, result)); - } - controlled (controls, ...) { - Fact(Length(xs) == Length(ys), "Input qubit arrays must have the same length."); - let nQubits = Length(xs); - - if (nQubits == 1) { - X(ys[0]); - (Controlled CCNOT)(controls, (xs[0], ys[0], result)); - X(ys[0]); - } else { - within { - ApplyToEachCA(X, ys); - ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); - } apply { - within { - (Adjoint ApplyCNOTChain)(Rest(xs)); - ApplyCCNOTChain(Most(ys), xs); - } apply { - (Controlled CCNOT)(controls, (xs[nQubits-1], ys[nQubits-1], result)); - } - (Controlled CNOT)(controls, (xs[nQubits-1], result)); - } - } - } -} export CompareGTSI, diff --git a/library/signed/src/Operations.qs b/library/signed/src/Operations.qs index 05d54d3b05..fbe5964cf0 100644 --- a/library/signed/src/Operations.qs +++ b/library/signed/src/Operations.qs @@ -68,7 +68,7 @@ operation SquareI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { within { CNOT(ctl, aux); } apply { - Controlled AddI([aux], (xs, (result[idx..idx + n]))); + Controlled RippleCarryTTKIncByLE([aux], (xs, (result[idx..idx + n]))); } } } elif numControls == 1 { @@ -77,7 +77,7 @@ operation SquareI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { within { AND(controls[0], ctl, aux); } apply { - Controlled AddI([aux], (xs, (result[idx..idx + n]))); + Controlled RippleCarryTTKIncByLE([aux], (xs, (result[idx..idx + n]))); } } } else { @@ -89,7 +89,7 @@ operation SquareI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl { within { AND(Tail(Most(aux)), ctl, Tail(aux)); } apply { - Controlled AddI([Tail(aux)], (xs, (result[idx..idx + n]))); + Controlled RippleCarryTTKIncByLE([Tail(aux)], (xs, (result[idx..idx + n]))); } } } @@ -120,7 +120,7 @@ operation MultiplyI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj let nb = Length(ys); for (idx, actl) in Enumerated(xs) { - Controlled AddI([actl], (ys, (result[idx..idx + nb]))); + Controlled RippleCarryTTKIncByLE([actl], (ys, (result[idx..idx + nb]))); } } controlled (controls, ...) { @@ -137,7 +137,7 @@ operation MultiplyI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj within { AND(controls[0], actl, aux); } apply { - Controlled AddI([aux], (ys, (result[idx..idx + nb]))); + Controlled RippleCarryTTKIncByLE([aux], (ys, (result[idx..idx + nb]))); } } } else { @@ -149,7 +149,7 @@ operation MultiplyI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj within { AND(Tail(Most(helper)), actl, Tail(helper)); } apply { - Controlled AddI([Tail(helper)], (ys, (result[idx..idx + nb]))); + Controlled RippleCarryTTKIncByLE([Tail(helper)], (ys, (result[idx..idx + nb]))); } } } @@ -219,29 +219,6 @@ operation Sum(carryIn : Qubit, summand1 : Qubit, summand2 : Qubit) : Unit is Adj CNOT(carryIn, summand2); } - -/// # Summary -/// Automatically chooses between addition with -/// carry and without, depending on the register size of `ys`. -/// -/// # Input -/// ## xs -/// $n$-bit addend. -/// ## ys -/// Addend with at least $n$ qubits. Will hold the result. -operation AddI(xs : Qubit[], ys : Qubit[]) : Unit is Adj + Ctl { - if Length(xs) == Length(ys) { - RippleCarryTTKIncByLE(xs, ys); - } elif Length(ys) > Length(xs) { - use qs = Qubit[Length(ys) - Length(xs) - 1]; - RippleCarryTTKIncByLE(xs + qs, ys); - } else { - fail "xs must not contain more qubits than ys"; - } -} - - - /// # Summary /// Inverts a given integer modulo 2's complement. /// @@ -260,7 +237,7 @@ operation Invert2sSI(xs : Qubit[]) : Unit is Adj + Ctl { within { Controlled X(controls, aux[0]); } apply { - AddI(aux, xs); + RippleCarryTTKIncByLE(aux, xs); } } } @@ -305,7 +282,7 @@ operation DivideI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + Controlled CompareGTI(controls, (ys, xtrunc, result[i])); // if ys > xtrunc, we don't subtract: (Controlled X)(controls, result[i]); - (Controlled Adjoint AddI)([result[i]], (ys, xtrunc)); + (Controlled Adjoint RippleCarryTTKIncByLE)([result[i]], (ys, xtrunc)); } } } @@ -338,7 +315,7 @@ operation ComputeReciprocalI(xs : Qubit[], result : Qubit[]) : Unit is Adj + Ctl (Controlled DivideI)(controls, (lhs, paddedxs, result)); // uncompute lhs for i in 0..2 * n - 1 { - (Controlled AddI)([result[i]], (paddedxs[0..2 * n-1-i], lhs[i..2 * n-1])); + (Controlled RippleCarryTTKIncByLE)([result[i]], (paddedxs[0..2 * n-1-i], lhs[i..2 * n-1])); } X(Tail(lhs)); } @@ -350,7 +327,6 @@ export MultiplySI, SquareSI, SquareI, - AddI, Invert2sSI, DivideI, ComputeReciprocalI; \ No newline at end of file diff --git a/library/signed/src/Tests.qs b/library/signed/src/Tests.qs index b997a1aa03..3f8b166a61 100644 --- a/library/signed/src/Tests.qs +++ b/library/signed/src/Tests.qs @@ -124,13 +124,6 @@ operation UnsignedOpTests() : Unit { // need smaller result register for div, mod, etc use d = Qubit[8]; TestIntOp(Operations.DivideI, a, b, d, 5); - - // 0b00001010 + 0b00000011 == 0b00001101 (10 + 3 = 13) - X(a[1]); - X(a[3]); - X(b[0]); - X(b[1]); - TestIntOp((a, b, _) => Operations.AddI(a, b), a, b, b, 13); } operation TestIntOp(op : (Qubit[], Qubit[], Qubit[]) => Unit, a : Qubit[], b : Qubit[], c : Qubit[], expect : Int) : Unit { From d36202ac0353f9d6d57744507b9434bebf238dcc Mon Sep 17 00:00:00 2001 From: sezna Date: Wed, 21 Aug 2024 19:48:25 -0700 Subject: [PATCH 14/15] PR feedback --- library/signed/src/Comparison.qs | 61 ++++++++++++++++++++++++++------ library/signed/src/Operations.qs | 4 +-- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/library/signed/src/Comparison.qs b/library/signed/src/Comparison.qs index dcfad2cffd..3359d1d902 100644 --- a/library/signed/src/Comparison.qs +++ b/library/signed/src/Comparison.qs @@ -22,29 +22,70 @@ operation CompareGTSI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj CNOT(Tail(ys), tmp); } apply { X(tmp); - Controlled CompareGTI([tmp], (xs, ys, result)); + Controlled GreaterThan([tmp], (xs, ys, result)); X(tmp); CCNOT(tmp, Tail(ys), result); } } - /// # Summary -/// Wrapper for integer comparison: `result = x > y`. +/// Applies a greater-than comparison between two integers encoded into +/// qubit registers, flipping a target qubit based on the result of the +/// comparison. +/// +/// # Description +/// Carries out a strictly greater than comparison of two integers $x$ and $y$, encoded +/// in qubit registers xs and ys. If $x > y$, then the result qubit will be flipped, +/// otherwise the result qubit will retain its state. /// /// # Input /// ## xs -/// First $n$-bit number +/// LittleEndian qubit register encoding the first integer $x$. /// ## ys -/// Second $n$-bit number +/// LittleEndian qubit register encoding the second integer $y$. /// ## result -/// Will be flipped if $x > y$ -operation CompareGTI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { - GreaterThan(xs, ys, result); -} +/// Single qubit that will be flipped if $x > y$. +/// +/// # References +/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David +/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. +/// https://arxiv.org/abs/quant-ph/0410184v1 +/// +/// - Thomas Haener, Martin Roetteler, Krysta M. Svore: "Factoring using 2n+2 qubits +/// with Toffoli based modular multiplication", 2016 +/// https://arxiv.org/abs/1611.07995 +/// +/// # Remarks +/// Uses the trick that $x - y = (x'+y)'$, where ' denotes the one's complement. +operation GreaterThan(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { + body (...) { + (Controlled GreaterThan)([], (xs, ys, result)); + } + controlled (controls, ...) { + Fact(Length(xs) == Length(ys), "Input qubit arrays must have the same length."); + let nQubits = Length(xs); + if (nQubits == 1) { + X(ys[0]); + (Controlled CCNOT)(controls, (xs[0], ys[0], result)); + X(ys[0]); + } else { + within { + ApplyToEachCA(X, ys); + ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); + } apply { + within { + (Adjoint ApplyCNOTChain)(Rest(xs)); + ApplyCCNOTChain(Most(ys), xs); + } apply { + (Controlled CCNOT)(controls, (xs[nQubits-1], ys[nQubits-1], result)); + } + (Controlled CNOT)(controls, (xs[nQubits-1], result)); + } + } + } +} export CompareGTSI, - CompareGTI, GreaterThan; \ No newline at end of file diff --git a/library/signed/src/Operations.qs b/library/signed/src/Operations.qs index fbe5964cf0..3123dfa916 100644 --- a/library/signed/src/Operations.qs +++ b/library/signed/src/Operations.qs @@ -5,7 +5,7 @@ import Std.Arrays.Tail, Std.Arrays.Most, Std.Arrays.Enumerated; import Utils.AndLadder; import Unstable.Arithmetic.RippleCarryTTKIncByLE; import Std.Diagnostics.Fact; -import Comparison.CompareGTI; +import Comparison.GreaterThan; /// # Summary /// Square signed integer `xs` and store @@ -279,7 +279,7 @@ operation DivideI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + for i in (n - 1)..(-1)..0 { let xtrunc = xpadded[i..i + n-1]; - Controlled CompareGTI(controls, (ys, xtrunc, result[i])); + Controlled GreaterThan(controls, (ys, xtrunc, result[i])); // if ys > xtrunc, we don't subtract: (Controlled X)(controls, result[i]); (Controlled Adjoint RippleCarryTTKIncByLE)([result[i]], (ys, xtrunc)); From b9ee54328a5cd88233a1a6ff1e76e07e502c3583 Mon Sep 17 00:00:00 2001 From: sezna Date: Thu, 22 Aug 2024 13:56:15 -0700 Subject: [PATCH 15/15] remove Zipped util function --- library/signed/src/Comparison.qs | 66 +------------------------------- library/signed/src/Operations.qs | 4 +- library/signed/src/Utils.qs | 44 ++------------------- 3 files changed, 7 insertions(+), 107 deletions(-) diff --git a/library/signed/src/Comparison.qs b/library/signed/src/Comparison.qs index 3359d1d902..e283fab1b3 100644 --- a/library/signed/src/Comparison.qs +++ b/library/signed/src/Comparison.qs @@ -3,7 +3,7 @@ import Std.Arrays.Tail, Std.Arrays.Zipped, Std.Arrays.Most, Std.Arrays.Rest; import Std.Diagnostics.Fact; -import Utils.ApplyCCNOTChain; +import Unstable.Arithmetic.ApplyIfGreaterLE; /// # Summary /// Wrapper for signed integer comparison: `result = xs > ys`. @@ -22,70 +22,8 @@ operation CompareGTSI(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj CNOT(Tail(ys), tmp); } apply { X(tmp); - Controlled GreaterThan([tmp], (xs, ys, result)); + Controlled ApplyIfGreaterLE([tmp], (X, xs, ys, result)); X(tmp); CCNOT(tmp, Tail(ys), result); } } - -/// # Summary -/// Applies a greater-than comparison between two integers encoded into -/// qubit registers, flipping a target qubit based on the result of the -/// comparison. -/// -/// # Description -/// Carries out a strictly greater than comparison of two integers $x$ and $y$, encoded -/// in qubit registers xs and ys. If $x > y$, then the result qubit will be flipped, -/// otherwise the result qubit will retain its state. -/// -/// # Input -/// ## xs -/// LittleEndian qubit register encoding the first integer $x$. -/// ## ys -/// LittleEndian qubit register encoding the second integer $y$. -/// ## result -/// Single qubit that will be flipped if $x > y$. -/// -/// # References -/// - Steven A. Cuccaro, Thomas G. Draper, Samuel A. Kutin, David -/// Petrie Moulton: "A new quantum ripple-carry addition circuit", 2004. -/// https://arxiv.org/abs/quant-ph/0410184v1 -/// -/// - Thomas Haener, Martin Roetteler, Krysta M. Svore: "Factoring using 2n+2 qubits -/// with Toffoli based modular multiplication", 2016 -/// https://arxiv.org/abs/1611.07995 -/// -/// # Remarks -/// Uses the trick that $x - y = (x'+y)'$, where ' denotes the one's complement. -operation GreaterThan(xs : Qubit[], ys : Qubit[], result : Qubit) : Unit is Adj + Ctl { - body (...) { - (Controlled GreaterThan)([], (xs, ys, result)); - } - controlled (controls, ...) { - Fact(Length(xs) == Length(ys), "Input qubit arrays must have the same length."); - let nQubits = Length(xs); - - if (nQubits == 1) { - X(ys[0]); - (Controlled CCNOT)(controls, (xs[0], ys[0], result)); - X(ys[0]); - } else { - within { - ApplyToEachCA(X, ys); - ApplyToEachCA(CNOT, Zipped(Rest(xs), Rest(ys))); - } apply { - within { - (Adjoint ApplyCNOTChain)(Rest(xs)); - ApplyCCNOTChain(Most(ys), xs); - } apply { - (Controlled CCNOT)(controls, (xs[nQubits-1], ys[nQubits-1], result)); - } - (Controlled CNOT)(controls, (xs[nQubits-1], result)); - } - } - } -} - -export - CompareGTSI, - GreaterThan; \ No newline at end of file diff --git a/library/signed/src/Operations.qs b/library/signed/src/Operations.qs index 3123dfa916..15099324d9 100644 --- a/library/signed/src/Operations.qs +++ b/library/signed/src/Operations.qs @@ -5,7 +5,7 @@ import Std.Arrays.Tail, Std.Arrays.Most, Std.Arrays.Enumerated; import Utils.AndLadder; import Unstable.Arithmetic.RippleCarryTTKIncByLE; import Std.Diagnostics.Fact; -import Comparison.GreaterThan; +import Unstable.Arithmetic.ApplyIfGreaterLE; /// # Summary /// Square signed integer `xs` and store @@ -279,7 +279,7 @@ operation DivideI(xs : Qubit[], ys : Qubit[], result : Qubit[]) : Unit is Adj + for i in (n - 1)..(-1)..0 { let xtrunc = xpadded[i..i + n-1]; - Controlled GreaterThan(controls, (ys, xtrunc, result[i])); + Controlled ApplyIfGreaterLE(controls, (X, ys, xtrunc, result[i])); // if ys > xtrunc, we don't subtract: (Controlled X)(controls, result[i]); (Controlled Adjoint RippleCarryTTKIncByLE)([result[i]], (ys, xtrunc)); diff --git a/library/signed/src/Utils.qs b/library/signed/src/Utils.qs index 7f8c9cf01c..7c41e14b64 100644 --- a/library/signed/src/Utils.qs +++ b/library/signed/src/Utils.qs @@ -8,48 +8,10 @@ import Std.Diagnostics.Fact; import Std.Arrays.Head, Std.Arrays.Tail, Std.Arrays.Most, Std.Arrays.Rest; operation AndLadder(controls : Qubit[], targets : Qubit[]) : Unit is Adj { + Fact(Length(controls) == Length(targets), "The number of control qubits must match the number of target qubits."); let controls1 = [Head(controls)] + Most(targets); let controls2 = Rest(controls); - for (a, b, c) in Zipped3(controls1, controls2, targets) { - AND(a, b, c); + for i in 0..Length(controls1) - 1 { + AND(controls1[i], controls2[i], targets[i]); } -} - -function Zipped3<'T1, 'T2, 'T3>(first : 'T1[], second : 'T2[], third : 'T3[]) : ('T1, 'T2, 'T3)[] { - let nElements = Min([Length(first), Length(second), Length(third)]); - if nElements == 0 { - return []; - } - mutable output = [(first[0], second[0], third[0]), size = nElements]; - for idxElement in 1..nElements - 1 { - set output w/= idxElement <- (first[idxElement], second[idxElement], third[idxElement]); - } - return output; -} - -/// # Summary -/// Implements a cascade of CCNOT gates controlled on corresponding bits of two -/// qubit registers, acting on the next qubit of one of the registers. -/// Starting from the qubits at position 0 in both registers as controls, CCNOT is -/// applied to the qubit at position 1 of the target register, then controlled by -/// the qubits at position 1 acting on the qubit at position 2 in the target register, -/// etc., ending with an action on the target qubit in position `Length(nQubits)-1`. -/// -/// # Input -/// ## register -/// Qubit register, only used for controls. -/// ## targets -/// Qubit register, used for controls and as target. -/// -/// # Remarks -/// The target qubit register must have one qubit more than the other register. -operation ApplyCCNOTChain(register : Qubit[], targets : Qubit[]) : Unit is Adj + Ctl { - let nQubits = Length(targets); - - Fact( - nQubits == Length(register) + 1, - "Target register must have one more qubit." - ); - - ApplyToEachCA(CCNOT, Zipped3(register, Most(targets), Rest(targets))); } \ No newline at end of file