diff --git a/library/fixed_point/qsharp.json b/library/fixed_point/qsharp.json index 2458f86ce6..200f063d69 100644 --- a/library/fixed_point/qsharp.json +++ b/library/fixed_point/qsharp.json @@ -1,6 +1,16 @@ { "author": "Microsoft", "license": "MIT", + "dependencies": { + "Unstable": { + "github": { + "owner": "microsoft", + "repo": "qsharp", + "ref": "main", + "path": "library/unstable" + } + } + }, "files": [ "src/Addition.qs", "src/Comparison.qs", diff --git a/library/fixed_point/src/Polynomial.qs b/library/fixed_point/src/Polynomial.qs index 746a8c13ac..88f8773613 100644 --- a/library/fixed_point/src/Polynomial.qs +++ b/library/fixed_point/src/Polynomial.qs @@ -27,7 +27,6 @@ operation EvaluatePolynomialFxP(coefficients : Double[], fpx : FixedPoint, resul } controlled (controls, ...) { IdenticalFormatFactFxP([fpx, result]); - AssertAllZeroFxP(result); let degree = Length(coefficients) - 1; let p = fpx::IntegerBits; let n = Length(fpx::Register); @@ -76,7 +75,6 @@ operation EvaluateEvenPolynomialFxP(coefficients : Double[], fpx : FixedPoint, r } controlled (controls, ...) { IdenticalFormatFactFxP([fpx, result]); - AssertAllZeroFxP(result); let halfDegree = Length(coefficients) - 1; let n = Length(fpx::Register); @@ -114,7 +112,6 @@ operation EvaluateOddPolynomialFxP(coefficients : Double[], fpx : FixedPoint, re } controlled (controls, ...) { IdenticalFormatFactFxP([fpx, result]); - AssertAllZeroFxP(result); let halfDegree = Length(coefficients) - 1; let n = Length(fpx::Register); if halfDegree >= 0 { diff --git a/library/modular/qsharp.json b/library/modular/qsharp.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/library/modular/qsharp.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/library/modular/src/modular.qs b/library/modular/src/modular.qs deleted file mode 100644 index e98f62ca98..0000000000 --- a/library/modular/src/modular.qs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - - - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Math; - open Microsoft.Quantum.Diagnostics; - open Microsoft.Quantum.Arrays; - - /// # Summary - /// Performs a modular increment of a qubit register by an integer constant. - /// - /// # Description - /// Let us denote `increment` by $a$, `modulus` by $N$ and integer encoded in `target` by $y$. - /// Then the operation performs the following transformation: - /// \begin{align} - /// \ket{y} \mapsto \ket{(y + a) \operatorname{mod} N} - /// \end{align} - /// Integers are encoded in little-endian format. - /// - /// # Input - /// ## increment - /// Integer increment $a$ to be added to $y$. - /// ## modulus - /// Integer $N$ that mods $y + a$. - /// ## target - /// Integer $y$ in `LittleEndian` format that `increment` $a$ is added to. - /// - /// # See Also - /// - Microsoft.Quantum.Arithmetic.IncrementPhaseByModularInteger - /// - /// # Remarks - /// Assumes that the initial value of target is less than $N$ - /// and that the increment $a$ is less than $N$. - /// Note that - /// implements - /// the same operation in the `PhaseLittleEndian` basis. - operation IncrementByModularInteger(increment : Int, modulus : Int, target : LittleEndian) - : Unit is Adj + Ctl { - let inner = IncrementPhaseByModularInteger(increment, modulus, _); - - use extraZeroBit = Qubit(); - ApplyPhaseLEOperationOnLECA(inner, LittleEndian(target! + [extraZeroBit])); - } - - /// # Summary - /// Performs a modular increment of a qubit register by an integer constant. - /// - /// # Description - /// Let us denote `increment` by $a$, `modulus` by $N$ and integer encoded in `target` by $y$. - /// Then the operation performs the following transformation: - /// \begin{align} - /// \ket{y} \mapsto \ket{(y + a) \operatorname{mod} N} - /// \end{align} - /// Integers are encoded in little-endian format in QFT basis. - /// - /// # Input - /// ## increment - /// Integer increment $a$ to be added to $y$. - /// ## modulus - /// Integer $N$ that mods $y + a$. - /// ## target - /// Integer $y$ in phase-encoded little-endian format that `increment` $a$ is added to. - /// - /// # See Also - /// - Microsoft.Quantum.Arithmetic.IncrementByModularInteger - /// - /// # Remarks - /// Assumes that `target` has the highest bit set to 0. - /// Also assumes that the value of target is less than $N$. - /// - /// For the circuit diagram and explanation see Figure 5 on [Page 5 - /// of arXiv:quant-ph/0205095v3](https://arxiv.org/pdf/quant-ph/0205095v3.pdf#page=5). - operation IncrementPhaseByModularInteger(increment : Int, modulus : Int, target : PhaseLittleEndian) - : Unit is Adj + Ctl { - body (...) { - Controlled IncrementPhaseByModularInteger([], (increment, modulus, target)); - } - - controlled (controls, ...) { - Fact(modulus <= 2 ^ (Length(target!) - 1), $"`multiplier` must be big enough to fit integers modulo `modulus`" + $"with highest bit set to 0"); - - if ExtraArithmeticAssertionsEnabled() { - // assert that the highest bit is zero, by switching to computational basis - ApplyLEOperationOnPhaseLEA(AssertMostSignificantBit(Zero, _), target); - - // check that the input is less than modulus - AssertPhaseLessThan(modulus, target); - } - - // note that controlled version is correct only under the assumption - // that the value of target is less than modulus - use lessThanModulusFlag = Qubit(); - let copyMostSignificantBitPhaseLE = ApplyLEOperationOnPhaseLEA(CopyMostSignificantBit(_, lessThanModulusFlag), _); - - // lets track the state of target register through the computation - Controlled IncrementPhaseByInteger(controls, (increment, target)); - - // the state is |x+a⟩ in QFT basis - Adjoint IncrementPhaseByInteger(modulus, target); - - // the state is |x+a-N⟩ in QFT basis - copyMostSignificantBitPhaseLE(target); - - // lessThanModulusFlag is set to 1 if x+a < N - Controlled IncrementPhaseByInteger([lessThanModulusFlag], (modulus, target)); - - // the state is |x+a (mod N)⟩ in QFT basis - // Now let us restore the lessThanModulusFlag qubit back to zero - Controlled (Adjoint IncrementPhaseByInteger)(controls, (increment, target)); - X(lessThanModulusFlag); - copyMostSignificantBitPhaseLE(target); - Controlled IncrementPhaseByInteger(controls, (increment, target)); - } - } - - /// # Summary - /// Performs a modular multiply-and-add by integer constants on a qubit register. - /// - /// # Description - /// Implements the map - /// $$ - /// \begin{align} - /// \ket{x} \ket{b} \mapsto \ket{x} \ket{(b + a \cdot x) \operatorname{mod} N} - /// \end{align} - /// $$ - /// for a given modulus $N$, constant multiplier $a$, and summand $b$. - /// - /// # Input - /// ## constMultiplier - /// An integer $a$ by which `multiplicand` is being multiplied. - /// Must be between 0 and `modulus`-1, inclusive. - /// ## modulus - /// The modulus $N$ which addition and multiplication is taken with respect to. - /// ## multiplicand - /// A quantum register representing an unsigned integer whose value, multiplied by `constMultiplier`, is to - /// be added to each basis state label of `summand`. Corresponds to the - /// register in state $\ket{x}$ above. - /// ## summand - /// A quantum register representing an unsigned integer to use as the target - /// for this operation. Corresponds to the register initially in $\ket{b}$ above. - /// - /// # See Also - /// - Microsoft.Quantum.Arithmetic.MultiplyAndAddPhaseByModularInteger - /// - /// # Remarks - /// - For the circuit diagram and explanation see Figure 6 on [Page 7 - /// of arXiv:quant-ph/0205095v3](https://arxiv.org/pdf/quant-ph/0205095v3.pdf#page=7) - /// - This operation corresponds to CMULT(a)MOD(N) in - /// [arXiv:quant-ph/0205095v3](https://arxiv.org/pdf/quant-ph/0205095v3.pdf) - operation MultiplyAndAddByModularInteger(constMultiplier : Int, modulus : Int, multiplicand : LittleEndian, summand : LittleEndian) - : Unit is Adj + Ctl { - let inner = MultiplyAndAddPhaseByModularInteger(constMultiplier, modulus, multiplicand, _); - - use extraZeroBit = Qubit(); - ApplyPhaseLEOperationOnLECA(inner, LittleEndian(summand! + [extraZeroBit])); - } - - /// # Summary - /// The same as MultiplyAndAddByModularInteger, but assumes that the summand encodes - /// integers in QFT basis. - /// - /// # Input - /// ## constMultiplier - /// An integer $a$ by which `multiplicand` is being multiplied. - /// Must be between 0 and `modulus`-1, inclusive. - /// ## modulus - /// The modulus $N$ which addition and multiplication is taken with respect to. - /// ## multiplicand - /// A quantum register representing an unsigned integer whose value, multiplied by `constMultiplier`, is to - /// be added to each basis state label of `summand`. - /// ## phaseSummand - /// A quantum register representing an unsigned integer to use as the target - /// for this operation. - /// - /// # Remarks - /// Assumes that `phaseSummand` has the highest bit set to 0. - /// Also assumes that the value of `phaseSummand` is less than $N$. - /// - /// # See Also - /// - Microsoft.Quantum.Arithmetic.MultiplyAndAddByModularInteger - operation MultiplyAndAddPhaseByModularInteger(constMultiplier : Int, modulus : Int, multiplicand : LittleEndian, phaseSummand : PhaseLittleEndian) - : Unit is Adj + Ctl { - Fact(modulus <= 2 ^ (Length(phaseSummand!) - 1), $"`multiplicand` must be big enough to fit integers modulo `modulus`" + $"with highest bit set to 0"); - Fact(constMultiplier >= 0 and constMultiplier < modulus, $"`constMultiplier` must be between 0 and `modulus`-1"); - - if (ExtraArithmeticAssertionsEnabled()) { - // assert that the highest bit is zero, by switching to computational basis - ApplyLEOperationOnPhaseLECA(AssertMostSignificantBit(Zero, _), phaseSummand); - - // check that the input is less than modulus - AssertPhaseLessThan(modulus, phaseSummand); - } - - for i in IndexRange(multiplicand!) { - let summand = (ExpModI(2, i, modulus) * constMultiplier) % modulus; - Controlled IncrementPhaseByModularInteger([(multiplicand!)[i]], (summand, modulus, phaseSummand)); - } - } - - /// # Summary - /// Performs modular multiplication by an integer constant on a qubit register. - /// - /// # Description - /// Let us denote `modulus` by $N$ and `constMultiplier` by $a$. - /// Then this operation implements a unitary operation defined by the following map on the - /// computational basis: - /// $$ - /// \begin{align} - /// \ket{y} \mapsto \ket{(a \cdot y) \operatorname{mod} N} - /// \end{align} - /// $$ - /// for all $y$ between $0$ and $N - 1$. - /// - /// # Input - /// ## constMultiplier - /// Constant by which multiplicand is being multiplied. Must be co-prime to modulus. - /// ## modulus - /// The multiplication operation is performed modulo `modulus`. - /// ## multiplicand - /// The number being multiplied by a constant. - /// This is an array of qubits encoding an integer in little-endian format. - /// - /// # Remarks - /// - For the circuit diagram and explanation see Figure 7 on [Page 8 - /// of arXiv:quant-ph/0205095v3](https://arxiv.org/pdf/quant-ph/0205095v3.pdf#page=8) - /// - This operation corresponds to Uₐ in - /// [arXiv:quant-ph/0205095v3](https://arxiv.org/pdf/quant-ph/0205095v3.pdf) - operation MultiplyByModularInteger(constMultiplier : Int, modulus : Int, multiplicand : LittleEndian) : Unit is Adj + Ctl { - // Check the preconditions using Microsoft.Quantum.Canon.EqualityFactB - Fact((0 <= constMultiplier and constMultiplier < modulus == true), $"`constMultiplier` must be between 0 and `modulus`"); - EqualityFactB(modulus <= 2 ^ Length(multiplicand!), true, $"`multiplicand` must be big enough to fit integers modulo `modulus`"); - EqualityFactB(IsCoprimeI(constMultiplier, modulus), true, $"`constMultiplier` and `modulus` must be co-prime"); - - use summand = Qubit[Length(multiplicand!)]; - // recall that newly allocated qubits are all in 0 state - // and therefore summandLE encodes 0. - let summandLE = LittleEndian(summand); - - // Let us look at what is the result of operations below assuming - // multiplicand is in computational basis and encodes x - // Currently the joint state of multiplicand and summandLE is - // |x⟩|0⟩ - MultiplyAndAddByModularInteger(constMultiplier, modulus, multiplicand, summandLE); - - // now the joint state is |x⟩|x⋅a(mod N)⟩ - ApplyToEachCA(SWAP, Zipped(summandLE!, multiplicand!)); - - // now the joint state is |x⋅a(mod N)⟩|x⟩ - let inverseMod = InverseModI(constMultiplier, modulus); - - // note that the operation below implements the following map: - // |x⟩|y⟩ ↦ |x⟩|y - a⁻¹⋅x (mod N)⟩ - Adjoint MultiplyAndAddByModularInteger(inverseMod, modulus, multiplicand, summandLE); - // now the joint state is |x⋅a(mod N)⟩|x - a⁻¹⋅x⋅a (mod N)⟩ = |x⋅a(mod N)⟩|0⟩ - } - diff --git a/library/signed/qsharp.json b/library/signed/qsharp.json new file mode 100644 index 0000000000..2681182fe8 --- /dev/null +++ b/library/signed/qsharp.json @@ -0,0 +1,4 @@ +{ + "author": "Microsoft", + "license": "MIT" +} \ No newline at end of file diff --git a/library/signed/src/Signed.qs b/library/signed/src/Signed.qs new file mode 100644 index 0000000000..e2cd65a094 --- /dev/null +++ b/library/signed/src/Signed.qs @@ -0,0 +1,834 @@ +import Microsoft.Quantum.Math.Min; +import Microsoft.Quantum.Diagnostics.CheckAllZero; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import Std.Arrays.Tail, Std.Arrays.Most; +import Std.Core.Length; +import Std.Diagnostics.Fact; + +/// # 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); +} + +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 +/// 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); + } + } +} + + +/// # 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)); + } +} + + + + /// # 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 +/// 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); +} + + + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + + +open Microsoft.Quantum.Intrinsic; +open Microsoft.Quantum.Canon; +open Microsoft.Quantum.Diagnostics; +open Microsoft.Quantum.Arrays; + +/// # 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 +/// 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 +/// 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 +internal 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 +internal 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. +internal 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 +internal 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]); +} + +// /// # 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); + + Fact( + nQubits == Length(ys), + "Input registers must have the same number of qubits." + ); + + 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)); + } + } + } +} + + + + + /// # 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))); +} + +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; +} +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; +} +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 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 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); + } +} + +