From 3cae584bf600e58b95165074d0982f2f77f53f89 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 14:15:47 +0200 Subject: [PATCH 01/20] Squashed commit of the following: commit 984156791d29db5190698ba0b17d3e4733c7f9af Author: Mathias Soeken Date: Mon Aug 31 10:41:17 2020 +0200 Fix bug in QuantumROM. commit 9726ff259748c5e04c2ce39754bb48697fd2ea4e Merge: 58d428ed 0b02a6a5 Author: Mathias Soeken Date: Mon Aug 31 09:49:43 2020 +0200 Merge branch 'master' into msoeken/state-preparation-cleanup commit 58d428ed6476d096fe5678dbc04f6e95b19e0d1f Author: Mathias Soeken Date: Thu Aug 27 11:59:12 2020 +0200 Cleanup remaining files. commit b35222ab855c85c8bab699b0a489af089f811d12 Author: Mathias Soeken Date: Thu Aug 27 11:54:33 2020 +0200 Clean-up arbitrary state preparation. commit e8076f770f3fe4fdba7c17cd176443abcd9aac98 Author: Mathias Soeken Date: Wed Aug 26 09:31:17 2020 +0200 Cleanup quantum ROM. commit 860c0ab12a0fac8a1d053bd6118897761bc547fa Author: Mathias Soeken Date: Wed Aug 26 09:21:21 2020 +0200 Cleanup uniform superposition. commit b94ec8c2dcc26b9afe763e2961218b15a1e7cac3 Author: Mathias Soeken Date: Tue Aug 25 15:56:26 2020 +0200 Cleanup QuantumROM. --- Standard/src/Preparation/Arbitrary.qs | 43 ++-- Standard/src/Preparation/Mixed.qs | 29 ++- Standard/src/Preparation/QuantumROM.qs | 55 ++--- Standard/src/Preparation/Reference.qs | 18 +- .../src/Preparation/UniformSuperposition.qs | 44 ++-- Standard/tests/QuantumROMTests.qs | 52 +++-- Standard/tests/StatePreparationTests.qs | 196 +++++++----------- .../UniformSuperpositionPreparationTests.qs | 51 ++--- 8 files changed, 199 insertions(+), 289 deletions(-) diff --git a/Standard/src/Preparation/Arbitrary.qs b/Standard/src/Preparation/Arbitrary.qs index 2eed1e0b8e5..0fbdeb72ee3 100644 --- a/Standard/src/Preparation/Arbitrary.qs +++ b/Standard/src/Preparation/Arbitrary.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Preparation { @@ -49,14 +49,8 @@ namespace Microsoft.Quantum.Preparation { /// op(qubitsLE); /// } /// ``` - function StatePreparationPositiveCoefficients (coefficients : Double[]) : (LittleEndian => Unit is Adj + Ctl) { - let nCoefficients = Length(coefficients); - mutable coefficientsComplexPolar = new ComplexPolar[nCoefficients]; - - for (idx in 0 .. nCoefficients - 1) { - set coefficientsComplexPolar w/= idx <- ComplexPolar(AbsD(coefficients[idx]), 0.0); - } - + function StatePreparationPositiveCoefficients(coefficients : Double[]) : (LittleEndian => Unit is Adj + Ctl) { + let coefficientsComplexPolar = Mapped(Compose(ComplexPolar(_, 0.0), AbsD), coefficients); return PrepareArbitraryState(coefficientsComplexPolar, _); } @@ -218,7 +212,7 @@ namespace Microsoft.Quantum.Preparation { qubits : LittleEndian ) : Unit is Adj + Ctl { - (_CompileApproximateArbitraryStatePreparation(tolerance, coefficients, Length(qubits!)))(qubits); + (CompileApproximateArbitraryStatePreparation(tolerance, coefficients, Length(qubits!)))(qubits); } /// # Summary @@ -234,7 +228,7 @@ namespace Microsoft.Quantum.Preparation { // NB: This is currently not marked as internal, as the QML library // currently uses this function. Please see the relevant GitHub issue // at https://github.com/microsoft/QuantumLibraries/issues/239. - function _CompileApproximateArbitraryStatePreparation( + internal function CompileApproximateArbitraryStatePreparation( tolerance : Double, coefficients : ComplexPolar[], nQubits : Int @@ -248,7 +242,7 @@ namespace Microsoft.Quantum.Preparation { nQubits > 1 ? (1 .. (nQubits - 1)) | (1..0); - let plan = _ApproximatelyUnprepareArbitraryStatePlan( + let plan = ApproximatelyUnprepareArbitraryStatePlan( tolerance, coefficientsPadded, (rngControl, idxTarget) ); let unprepare = BoundCA(plan); @@ -265,12 +259,11 @@ namespace Microsoft.Quantum.Preparation { ApproximatelyMultiplexPauli(tolerance, disentangling, axis, actualControl, register[idxTarget]); } - internal function RangeLength(rng : Range) : Int { - mutable len = 0; + internal function IsRangeEmpty(rng : Range) : Bool { for (idx in rng) { - set len += 1; + return false; } - return len; + return true; } internal operation ApplyGlobalRotationStep( @@ -285,7 +278,7 @@ namespace Microsoft.Quantum.Preparation { /// # See Also /// - PrepareArbitraryState /// - Microsoft.Quantum.Canon.MultiplexPauli - function _ApproximatelyUnprepareArbitraryStatePlan( + internal function ApproximatelyUnprepareArbitraryStatePlan( tolerance : Double, coefficients : ComplexPolar[], (rngControl : Range, idxTarget : Int) ) @@ -293,7 +286,7 @@ namespace Microsoft.Quantum.Preparation { mutable plan = new (Qubit[] => Unit is Adj + Ctl)[0]; // For each 2D block, compute disentangling single-qubit rotation parameters - let (disentanglingY, disentanglingZ, newCoefficients) = _StatePreparationSBMComputeCoefficients(coefficients); + let (disentanglingY, disentanglingZ, newCoefficients) = StatePreparationSBMComputeCoefficients(coefficients); if (AnyOutsideToleranceD(tolerance, disentanglingZ)) { set plan += [ApplyMultiplexStep(tolerance, disentanglingZ, PauliZ, (rngControl, idxTarget), _)]; } @@ -304,17 +297,15 @@ namespace Microsoft.Quantum.Preparation { // target is now in |0> state up to the phase given by arg of newCoefficients. // Continue recursion while there are control qubits. - if (RangeLength(rngControl) == 0) { + if (IsRangeEmpty(rngControl)) { let (abs, arg) = newCoefficients[0]!; if (AbsD(arg) > tolerance) { set plan += [ApplyGlobalRotationStep(-1.0 * arg, idxTarget, _)]; } - } else { - if (AnyOutsideToleranceCP(tolerance, newCoefficients)) { - let newControl = (RangeStart(rngControl) + 1)..RangeStep(rngControl)..RangeEnd(rngControl); - let newTarget = RangeStart(rngControl); - set plan += _ApproximatelyUnprepareArbitraryStatePlan(tolerance, newCoefficients, (newControl, newTarget)); - } + } elif (AnyOutsideToleranceCP(tolerance, newCoefficients)) { + let newControl = (RangeStart(rngControl) + 1)..RangeStep(rngControl)..RangeEnd(rngControl); + let newTarget = RangeStart(rngControl); + set plan += ApproximatelyUnprepareArbitraryStatePlan(tolerance, newCoefficients, (newControl, newTarget)); } return plan; @@ -352,7 +343,7 @@ namespace Microsoft.Quantum.Preparation { /// Implementation step of arbitrary state preparation procedure. /// # See Also /// - Microsoft.Quantum.Canon.PrepareArbitraryState - function _StatePreparationSBMComputeCoefficients (coefficients : ComplexPolar[]) : (Double[], Double[], ComplexPolar[]) { + internal function StatePreparationSBMComputeCoefficients (coefficients : ComplexPolar[]) : (Double[], Double[], ComplexPolar[]) { mutable disentanglingZ = new Double[Length(coefficients) / 2]; mutable disentanglingY = new Double[Length(coefficients) / 2]; mutable newCoefficients = new ComplexPolar[Length(coefficients) / 2]; diff --git a/Standard/src/Preparation/Mixed.qs b/Standard/src/Preparation/Mixed.qs index 6d6eddcf75e..7de63073113 100644 --- a/Standard/src/Preparation/Mixed.qs +++ b/Standard/src/Preparation/Mixed.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Preparation { @@ -8,8 +8,8 @@ namespace Microsoft.Quantum.Preparation { open Microsoft.Quantum.Random; /// # Summary - /// Prepares a qubit in the maximally mixed state. - /// + /// Prepares a qubit in the maximally mixed state. + /// /// It prepares the given qubit in the $\boldone / 2$ state by applying the depolarizing channel /// $$ /// \begin{align} @@ -38,9 +38,9 @@ namespace Microsoft.Quantum.Preparation { /// # Summary /// Given a register, prepares that register in the maximally mixed state. - /// + /// /// The register is prepared in the $\boldone / 2^N$ state by applying the - /// complete depolarizing + /// complete depolarizing /// channel to each qubit, where $N$ is the length of the register. /// /// # Input @@ -55,10 +55,10 @@ namespace Microsoft.Quantum.Preparation { } /// # Summary - /// Prepares a qubit in the +1 (`Zero`) eigenstate of the given Pauli operator. - /// If the identity operator is given, then the qubit is prepared in the maximally - /// mixed state. - /// + /// Prepares a qubit in the +1 (`Zero`) eigenstate of the given Pauli operator. + /// If the identity operator is given, then the qubit is prepared in the maximally + /// mixed state. + /// /// If the qubit was initially in the $\ket{0}$ state, this operation prepares the /// qubit in the $+1$ eigenstate of a given Pauli operator, or, for `PauliI`, /// in the maximally mixed state instead (see ). @@ -73,16 +73,11 @@ namespace Microsoft.Quantum.Preparation { /// ## qubit /// A qubit to be prepared. operation PrepareQubit (basis : Pauli, qubit : Qubit) : Unit { - if (basis == PauliI) - { + if (basis == PauliI) { PrepareSingleQubitIdentity(qubit); - } - elif (basis == PauliX) - { + } elif (basis == PauliX) { H(qubit); - } - elif (basis == PauliY) - { + } elif (basis == PauliY) { H(qubit); S(qubit); } diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index 96108141b5a..9e49dae3d9d 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Preparation { @@ -61,7 +61,8 @@ namespace Microsoft.Quantum.Preparation { /// - Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity /// Ryan Babbush, Craig Gidney, Dominic W. Berry, Nathan Wiebe, Jarrod McClean, Alexandru Paler, Austin Fowler, Hartmut Neven /// https://arxiv.org/abs/1805.03662 - function QuantumROM(targetError: Double, coefficients: Double[]) : ((Int, (Int, Int)), Double, ((LittleEndian, Qubit[]) => Unit is Adj + Ctl)) { + function QuantumROM(targetError: Double, coefficients: Double[]) + : ((Int, (Int, Int)), Double, ((LittleEndian, Qubit[]) => Unit is Adj + Ctl)) { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, coefficients); let nCoeffs = Length(coefficients); @@ -87,7 +88,8 @@ namespace Microsoft.Quantum.Preparation { /// A tuple `(x,(y,z))` where `x = y + z` is the total number of qubits allocated, /// `y` is the number of qubits for the `LittleEndian` register, and `z` is the Number /// of garbage qubits. - function QuantumROMQubitCount(targetError: Double, nCoeffs: Int) : (Int, (Int, Int)) { + function QuantumROMQubitCount(targetError: Double, nCoeffs: Int) + : (Int, (Int, Int)) { let nBitsPrecision = -Ceiling(Lg(0.5*targetError))+1; let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); let nGarbageQubits = nBitsIndices + 2 * nBitsPrecision + 1; @@ -95,33 +97,26 @@ namespace Microsoft.Quantum.Preparation { return (nTotal, (nBitsIndices, nGarbageQubits)); } - // Implementation step of `QuantumROM`. This splits a single - // qubit array into the subarrays required by the operation. - function _QuantumROMQubitManager(targetError: Double, nCoeffs: Int, qubits: Qubit[]) : ((LittleEndian, Qubit[]), Qubit[]) { - let (nTotal, (nIndexRegister, nGarbageQubits)) = QuantumROMQubitCount(targetError, nCoeffs); - let registers = Partitioned([nIndexRegister, nGarbageQubits], qubits); - return((LittleEndian(registers[0]), registers[1]), registers[2]); - } - // Classical processing // This discretizes the coefficients such that - // |coefficient[i] * oneNorm - discretizedCoefficient[i] * discreizedOneNorm| * nCoeffs <= 2^{1-bitsPrecision}. - function _QuantumROMDiscretization(bitsPrecision: Int, coefficients: Double[]) : (Double, Int[], Int[]) { + // |coefficient[i] * oneNorm - discretizedCoefficient[i] * discretizedOneNorm| * nCoeffs <= 2^{1-bitsPrecision}. + function _QuantumROMDiscretization(bitsPrecision: Int, coefficients: Double[]) + : (Double, Int[], Int[]) { let oneNorm = PNorm(1.0, coefficients); let nCoefficients = Length(coefficients); if (bitsPrecision > 31) { fail $"Bits of precision {bitsPrecision} unsupported. Max is 31."; } if (nCoefficients <= 1) { - fail $"Cannot prepare state with less than 2 coefficients."; + fail "Cannot prepare state with less than 2 coefficients."; } if (oneNorm == 0.0) { - fail $"State must have at least one coefficient > 0"; + fail "State must have at least one coefficient > 0"; } let barHeight = 2^bitsPrecision - 1; - mutable altIndex = RangeAsIntArray(0..nCoefficients-1); + mutable altIndex = RangeAsIntArray(0..nCoefficients - 1); mutable keepCoeff = Mapped(RoundedDiscretizationCoefficients(_, oneNorm, nCoefficients, barHeight), coefficients); // Calculate difference between number of discretized bars vs. maximum @@ -129,7 +124,7 @@ namespace Microsoft.Quantum.Preparation { for (idxCoeff in IndexRange(keepCoeff)) { set bars += keepCoeff[idxCoeff] - barHeight; } - //Message($"Excess bars {bars}."); + // Uniformly distribute excess bars across coefficients. for (idx in 0..AbsI(bars) - 1) { if (bars > 0) { @@ -156,28 +151,24 @@ namespace Microsoft.Quantum.Preparation { for (rep in 0..nCoefficients * 10) { if (nBarSource > 0 and nBarSink > 0) { - let idxSink = barSink[nBarSink-1]; - let idxSource = barSource[nBarSource-1]; + let idxSink = barSink[nBarSink - 1]; + let idxSource = barSource[nBarSource - 1]; set nBarSink = nBarSink - 1; set nBarSource = nBarSource - 1; set keepCoeff w/= idxSource <- keepCoeff[idxSource] - barHeight + keepCoeff[idxSink]; set altIndex w/= idxSink <- idxSource; - if (keepCoeff[idxSource] < barHeight) - { + if (keepCoeff[idxSource] < barHeight) { set barSink w/= nBarSink <- idxSource; set nBarSink = nBarSink + 1; - } - elif(keepCoeff[idxSource] > barHeight) - { + } elif(keepCoeff[idxSource] > barHeight) { set barSource w/= nBarSource <- idxSource; set nBarSource = nBarSource + 1; } } elif (nBarSource > 0) { - //Message($"rep: {rep}, nBarSource {nBarSource}."); - let idxSource = barSource[nBarSource-1]; + let idxSource = barSource[nBarSource - 1]; set nBarSource = nBarSource - 1; set keepCoeff w/= idxSource <- barHeight; } else { @@ -189,7 +180,8 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROM implementation. - internal function RoundedDiscretizationCoefficients(coefficient: Double, oneNorm: Double, nCoefficients: Int, barHeight: Int) : Int { + internal function RoundedDiscretizationCoefficients(coefficient: Double, oneNorm: Double, nCoefficients: Int, barHeight: Int) + : Int { return Round((AbsD(coefficient) / oneNorm) * IntAsDouble(nCoefficients) * IntAsDouble(barHeight)); } @@ -202,7 +194,7 @@ namespace Microsoft.Quantum.Preparation { let garbageIdx2 = garbageIdx1 + nBitsPrecision; let garbageIdx3 = garbageIdx2 + 1; - let altIndexRegister = LittleEndian(garbageRegister[0..garbageIdx0-1]); + let altIndexRegister = LittleEndian(garbageRegister[0..garbageIdx0 - 1]); let keepCoeffRegister = LittleEndian(garbageRegister[garbageIdx0..garbageIdx1 - 1]); let uniformKeepCoeffRegister = LittleEndian(garbageRegister[garbageIdx1..garbageIdx2 - 1]); let flagQubit = garbageRegister[garbageIdx3 - 1]; @@ -220,13 +212,12 @@ namespace Microsoft.Quantum.Preparation { let indexRegisterSize = Length(indexRegister!); // Swap in register based on comparison - for (idx in 0..nBitsIndices - 1) { - (Controlled SWAP)([flagQubit], (indexRegister![nBitsIndices - idx - 1], altIndexRegister![idx])); - } + ApplyToEachCA((Controlled SWAP)([flagQubit], _), Zip(indexRegister!, altIndexRegister!)); } // Used in QuantumROM implementation. - internal function QuantumROMBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[]) : ((LittleEndian, LittleEndian) => Unit is Adj + Ctl) { + internal function QuantumROMBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[]) + : ((LittleEndian, LittleEndian) => Unit is Adj + Ctl) { return WriteQuantumROMBitString(idx, keepCoeff, altIndex, _, _); } diff --git a/Standard/src/Preparation/Reference.qs b/Standard/src/Preparation/Reference.qs index 9203f0439ec..a03b94672a7 100644 --- a/Standard/src/Preparation/Reference.qs +++ b/Standard/src/Preparation/Reference.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Preparation { @@ -6,8 +6,8 @@ namespace Microsoft.Quantum.Preparation { open Microsoft.Quantum.Arrays; /// # Summary - /// Pairwise entangles two qubit registers. - /// + /// Pairwise entangles two qubit registers. + /// /// That is, given two registers, prepares the maximally entangled state /// $\frac{1}{\sqrt{2}} \left(\ket{00} + \ket{11} \right)$ between each pair of qubits on the respective registers, /// assuming that each register starts in the $\ket{0\cdots 0}$ state. @@ -24,17 +24,17 @@ namespace Microsoft.Quantum.Preparation { for ((leftQubit, rightQubit) in Zip(left, right)) { H(leftQubit); - Controlled X([leftQubit], rightQubit); + CNOT(leftQubit, rightQubit); } } /// # Summary - /// Prepares the Choi–Jamiłkowski state for a given operation onto given reference + /// Prepares the Choi–Jamiołkowski state for a given operation onto given reference /// and target registers. /// /// # Input /// ## op - /// Operation $\Lambda$ whose Choi–Jamiłkowski state $J(\Lambda) / 2^N$ + /// Operation $\Lambda$ whose Choi–Jamiołkowski state $J(\Lambda) / 2^N$ /// is to be prepared, where $N$ is the number of qubits on which /// `op` acts. /// ## reference @@ -70,7 +70,7 @@ namespace Microsoft.Quantum.Preparation { /// # Summary - /// Prepares the Choi–Jamiłkowski state for a given operation with a controlled variant onto given reference + /// Prepares the Choi–Jamiołkowski state for a given operation with a controlled variant onto given reference /// and target registers. /// /// # See Also @@ -82,7 +82,7 @@ namespace Microsoft.Quantum.Preparation { /// # Summary - /// Prepares the Choi–Jamiłkowski state for a given operation with an adjoint variant onto given reference + /// Prepares the Choi–Jamiołkowski state for a given operation with an adjoint variant onto given reference /// and target registers. /// /// # See Also @@ -94,7 +94,7 @@ namespace Microsoft.Quantum.Preparation { /// # Summary - /// Prepares the Choi–Jamiłkowski state for a given operation with both controlled and adjoint variants onto given reference + /// Prepares the Choi–Jamiołkowski state for a given operation with both controlled and adjoint variants onto given reference /// and target registers. /// /// # See Also diff --git a/Standard/src/Preparation/UniformSuperposition.qs b/Standard/src/Preparation/UniformSuperposition.qs index 177c43dcef9..a4d3773cd49 100644 --- a/Standard/src/Preparation/UniformSuperposition.qs +++ b/Standard/src/Preparation/UniformSuperposition.qs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Preparation { @@ -39,23 +39,24 @@ namespace Microsoft.Quantum.Preparation { /// PrepareUniformSuperposition(nIndices, LittleEndian(indexRegister)); /// } /// ``` - operation PrepareUniformSuperposition(nIndices: Int, indexRegister: LittleEndian) : Unit is Adj + Ctl { - if(nIndices == 0) { - fail $"Cannot prepare uniform superposition over {nIndices} state."; + operation PrepareUniformSuperposition(nIndices: Int, indexRegister: LittleEndian) + : Unit is Adj+Ctl { + if (nIndices == 0) { + fail "Cannot prepare uniform superposition over 0 state."; } elif (nIndices == 1) { // Superposition over one state, so do nothing. - } elif (nIndices == 2){ + } elif (nIndices == 2) { H(indexRegister![0]); } else { - let nQubits = Ceiling(Lg(IntAsDouble(nIndices))); - if (nQubits > Length(indexRegister!)){ + let nQubits = BitSizeI(nIndices - 1); + if (nQubits > Length(indexRegister!)) { fail $"Cannot prepare uniform superposition over {nIndices} states as it is larger than the qubit register."; } using (flagQubit = Qubit[3]) { let targetQubits = indexRegister![0..nQubits - 1]; let qubits = flagQubit + targetQubits; - let stateOracle = StateOracle(PrepareUniformSuperposition_(nIndices, nQubits, _, _)); + let stateOracle = StateOracle(PrepareUniformSuperpositionOracle(nIndices, nQubits, _, _)); (StandardAmplitudeAmplification(1, stateOracle, 0))(qubits); @@ -66,27 +67,24 @@ namespace Microsoft.Quantum.Preparation { /// # Summary /// Implementation step of - operation PrepareUniformSuperposition_(nIndices: Int, nQubits: Int, idxFlag: Int, qubits: Qubit[]) : Unit { - body (...) { - let targetQubits = qubits[3..3 + nQubits-1]; - let flagQubit = qubits[0]; - let auxillaryQubits = qubits[1..2]; - let theta = ArcSin(Sqrt(IntAsDouble(2^nQubits)/IntAsDouble(nIndices)) * Sin(PI() / 6.0)); - //let theta = PI() * 0.5; + internal operation PrepareUniformSuperpositionOracle(nIndices: Int, nQubits: Int, idxFlag: Int, qubits: Qubit[]) + : Unit is Adj+Ctl { + let targetQubits = qubits[3...]; + let flagQubit = qubits[0]; + let auxillaryQubits = qubits[1..2]; + let theta = ArcSin(Sqrt(IntAsDouble(2^nQubits) / IntAsDouble(nIndices)) * Sin(PI() / 6.0)); - ApplyToEachCA(H, targetQubits); - using(compareQubits = Qubit[nQubits]) { + ApplyToEachCA(H, targetQubits); + using (compareQubits = Qubit[nQubits]) { + within { ApplyXorInPlace(nIndices - 1, LittleEndian(compareQubits)); + } apply { CompareUsingRippleCarry(LittleEndian(targetQubits), LittleEndian(compareQubits), auxillaryQubits[0]); X(auxillaryQubits[0]); - ApplyXorInPlace(nIndices - 1, LittleEndian(compareQubits)); } - Exp([PauliY], -theta, [auxillaryQubits[1]]); - (Controlled X)(auxillaryQubits, flagQubit); } - adjoint auto; - controlled auto; - adjoint controlled auto; + Ry(2.0 * theta, auxillaryQubits[1]); + (Controlled X)(auxillaryQubits, flagQubit); } } diff --git a/Standard/tests/QuantumROMTests.qs b/Standard/tests/QuantumROMTests.qs index 2768277ea53..301b25ea71d 100644 --- a/Standard/tests/QuantumROMTests.qs +++ b/Standard/tests/QuantumROMTests.qs @@ -1,8 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Tests { + open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; @@ -13,7 +14,8 @@ namespace Microsoft.Quantum.Tests { open Microsoft.Quantum.Random; // Tests the discretization algorithm - operation QuantumROMDiscretizationTest() : Unit { + @Test("QuantumSimulator") + operation TestQuantumROMDiscretization() : Unit { for(rep in 0..20){ let coeffs = DrawRandomInt(2, 5002); let bitsPrecision = DrawRandomInt(1, 31); @@ -30,7 +32,7 @@ namespace Microsoft.Quantum.Tests { // Reconstruct coefficients mutable coefficientsOutInt = new Int[coeffs]; - for (idx in 0..coeffs-1) + for (idx in 0..coeffs - 1) { set coefficientsOutInt w/= idx <- coefficientsOutInt[idx] + keepCoeff[idx]; if (altIndex[idx] >= 0) @@ -43,55 +45,51 @@ namespace Microsoft.Quantum.Tests { mutable coefficientsOut = new Double[coeffs]; mutable errors = new Double[coeffs]; mutable maxError = 0.0; - for (i in 0..coeffs-1) - { + for (i in 0..coeffs - 1) { set coefficientsOut w/= i <- oneNorm * IntAsDouble(coefficientsOutInt[i]) / IntAsDouble(barHeight * coeffs); let error = AbsD(coefficients[i] - coefficientsOut[i]) / oneNorm /( PowD(2.0, IntAsDouble(-bitsPrecision)) / IntAsDouble(coeffs)); set errors w/= i <- error; - if(AbsD(error) > AbsD(maxError)){ + if (AbsD(error) > AbsD(maxError)) { set maxError = error; } } Message($"coeffs {coeffs}, bitsPrecision {bitsPrecision}, maxError {maxError}"); - for(i in 0..coeffs-1){ - if(errors[i] < IntAsDouble(3)){ - // test passes - } - else{ + for (i in 0..coeffs - 1) { + if (errors[i] >= IntAsDouble(3)) { fail $"index {i} reconstructed coefficient incorrect. Error is {errors[i]}"; } } } } - operation QuantumROMTest() : Unit { + @Test("QuantumSimulator") + operation TestQuantumROM() : Unit { for(coeffs in 2..7){ for(nBitsPrecision in -1..-1..-2){ let targetError = PowD(2.0, IntAsDouble(nBitsPrecision)); let probtargetError = targetError / IntAsDouble(coeffs); let coefficients = DrawMany(DrawRandomDouble, coeffs, (0.0, 1.0)); let ((nTotal, (nCoeffQubits, nGarbageQubits)), oneNorm, op) = QuantumROM(targetError, coefficients); + Message($"Test case coeffs {coeffs}, bitsPrecision {nCoeffQubits}, global targetError {targetError}, probability error {probtargetError}."); - for (idx in 0..coeffs-1) - { - let tmp = AbsD(coefficients[idx]) / oneNorm; + for ((idx, coefficient) in Enumerated(coefficients)) { + let tmp = AbsD(coefficient) / oneNorm; Message($"{idx} expected prob = {tmp}."); } Message($"Qubits used: {nGarbageQubits} + {nCoeffQubits}"); - using(qubits = Qubit[nTotal]){ - let (register, rest) = _QuantumROMQubitManager(targetError, coeffs, qubits); - let (coeffQubits, garbageQubits) = register; - op(register); - // Now check that probability of each number state in nCoeffQubits is as expected. - for(stateIndex in 0..coeffs-1){ - let prob = AbsD(coefficients[stateIndex]) / oneNorm; - Message($"Testing probability {prob} on index {stateIndex}"); - //BAssertProbIntBE(stateIndex, AbsD(coefficients[stateIndex]) / oneNorm, BigEndian(coeffQubits), targetError / IntAsDouble(coeffs)); - } - - (Adjoint op)(register); + using ((coeffRegister, garbageQubits) = (Qubit[nCoeffQubits], Qubit[nGarbageQubits])) { + let coeffQubits = LittleEndian(coeffRegister); + // Check that probability of each number state in nCoeffQubits is as expected. + within { + op(coeffQubits, garbageQubits); + } apply { + for (stateIndex in 0..coeffs - 1) { + let prob = AbsD(coefficients[stateIndex]) / oneNorm; + AssertProbInt(stateIndex, prob, coeffQubits, probtargetError); + } + } } } } diff --git a/Standard/tests/StatePreparationTests.qs b/Standard/tests/StatePreparationTests.qs index b0d03a1295f..85ce82dff17 100644 --- a/Standard/tests/StatePreparationTests.qs +++ b/Standard/tests/StatePreparationTests.qs @@ -1,66 +1,58 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Tests { - open Microsoft.Quantum.Preparation; open Microsoft.Quantum.Arithmetic; + open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; open Microsoft.Quantum.Math; open Microsoft.Quantum.Measurement; - open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Preparation; // number of qubits, abs(amplitude), phase newtype StatePreparationTestCase = (Int, Double[], Double[]); - operation StatePreparationPositiveCoefficientsTest () : Unit { + @Test("QuantumSimulator") + operation TestStatePreparationPositiveCoefficients() : Unit { let tolerance = 1E-09; - mutable testCases = new StatePreparationTestCase[100]; - mutable nTests = 0; - - // Test positive coefficients. - set testCases w/= nTests <- StatePreparationTestCase(1, [0.773761, 0.633478], [0.0, 0.0]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(2, [0.183017, 0.406973, 0.604925, 0.659502], [0.0, 0.0, 0.0, 0.0]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(3, [0.0986553, 0.359005, 0.465689, 0.467395, 0.419893, 0.118445, 0.461883, 0.149609], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(4, [0.271471, 0.0583654, 0.11639, 0.36112, 0.307383, 0.193371, 0.274151, 0.332542, 0.130172, 0.222546, 0.314879, 0.210704, 0.212429, 0.245518, 0.30666, 0.22773], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); - set nTests = nTests + 1; - - // Test negative coefficients. Should give same probabilities as positive coefficients. - set testCases w/= nTests <- StatePreparationTestCase(1, [-0.773761, 0.633478], [0.0, 0.0]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(2, [0.183017, -0.406973, 0.604925, 0.659502], [0.0, 0.0, 0.0, 0.0]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(3, [0.0986553, -0.359005, 0.465689, -0.467395, 0.419893, 0.118445, -0.461883, 0.149609], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(4, [-0.271471, 0.0583654, 0.11639, 0.36112, -0.307383, 0.193371, -0.274151, 0.332542, 0.130172, 0.222546, 0.314879, -0.210704, 0.212429, 0.245518, -0.30666, -0.22773], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); - set nTests = nTests + 1; - - // Test unnormalized coefficients - set testCases w/= nTests <- StatePreparationTestCase(3, [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445, 0.461883, 0.149609], new Double[0]); - set nTests = nTests + 1; - - // Test missing coefficients - set testCases w/= nTests <- StatePreparationTestCase(3, [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445], new Double[0]); - set nTests = nTests + 1; + + let testCases = [ + // Test positive coefficients + StatePreparationTestCase(1, [0.773761, 0.633478], [0.0, 0.0]), + StatePreparationTestCase(2, [0.183017, 0.406973, 0.604925, 0.659502], [0.0, 0.0, 0.0, 0.0]), + StatePreparationTestCase(3, [0.0986553, 0.359005, 0.465689, 0.467395, 0.419893, 0.118445, 0.461883, 0.149609], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), + StatePreparationTestCase(4, [0.271471, 0.0583654, 0.11639, 0.36112, 0.307383, 0.193371, 0.274151, 0.332542, 0.130172, 0.222546, 0.314879, 0.210704, 0.212429, 0.245518, 0.30666, 0.22773], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), + + // Test negative coefficients; should give same probabilities as positive coefficients + StatePreparationTestCase(1, [-0.773761, 0.633478], [0.0, 0.0]), + StatePreparationTestCase(2, [0.183017, -0.406973, 0.604925, 0.659502], [0.0, 0.0, 0.0, 0.0]), + StatePreparationTestCase(3, [0.0986553, -0.359005, 0.465689, -0.467395, 0.419893, 0.118445, -0.461883, 0.149609], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), + StatePreparationTestCase(4, [-0.271471, 0.0583654, 0.11639, 0.36112, -0.307383, 0.193371, -0.274151, 0.332542, 0.130172, 0.222546, 0.314879, -0.210704, 0.212429, 0.245518, -0.30666, -0.22773], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), + + // Test unnormalized coefficients + StatePreparationTestCase(3, [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445, 0.461883, 0.149609], new Double[0]), + + // Test missing coefficients + StatePreparationTestCase(3, [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445], new Double[0]) + ]; // Loop over multiple qubit tests - for (idxTest in 0 .. nTests - 1) { - let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCases[idxTest]!; + for (testCase in testCases) { + let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCase!; let nCoefficients = Length(coefficientsAmplitude); - // Test negative coefficients. Should give same results as positive coefficients. + // Test negative coefficients. Should give same results as positive coefficients using (qubits = Qubit[nQubits]) { let qubitsLE = LittleEndian(qubits); let op = StatePreparationPositiveCoefficients(coefficientsAmplitude); op(qubitsLE); let normalizedCoefficients = PNormalized(2.0, coefficientsAmplitude); - for (idxCoeff in 0 .. nCoefficients - 1) { + for (idxCoeff in IndexRange(coefficientsAmplitude)) { let amp = normalizedCoefficients[idxCoeff]; let prob = amp * amp; AssertProbInt(idxCoeff, prob, qubitsLE, tolerance); @@ -73,34 +65,25 @@ namespace Microsoft.Quantum.Tests { // Test phase factor on 1-qubit uniform superposition. - operation StatePreparationComplexCoefficientsQubitPhaseTest () : Unit { + @Test("QuantumSimulator") + operation TestStatePreparationComplexCoefficientsQubitPhase() : Unit { let tolerance = 1E-09; - mutable testCases = new StatePreparationTestCase[10]; - mutable nTests = 0; - - // Test phase factor on uniform superposition. - set testCases w/= nTests <- StatePreparationTestCase(1, [1.0, 1.0], [0.01, -0.01]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(1, [1.0, 1.0], [0.01, -0.05]); - set nTests = nTests + 1; + + let testCases = [ + // Test phase factor on uniform superposition + StatePreparationTestCase(1, [1.0, 1.0], [0.01, -0.01]), + StatePreparationTestCase(1, [1.0, 1.0], [0.01, -0.05]) + ]; // Loop over tests - for (idxTest in 0 .. nTests - 1) { - let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCases[idxTest]!; - Message($"Test case {idxTest}"); + for (testCase in testCases) { + let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCase!; let nCoefficients = Length(coefficientsAmplitude); using (qubits = Qubit[nQubits]) { let qubitsLE = LittleEndian(qubits); - mutable coefficients = new ComplexPolar[nCoefficients]; - mutable coefficientsPositive = new Double[nCoefficients]; - - for (idxCoeff in 0 .. nCoefficients - 1) { - set coefficients w/= idxCoeff <- ComplexPolar(coefficientsAmplitude[idxCoeff], coefficientsPhase[idxCoeff]); - set coefficientsPositive w/= idxCoeff <- coefficientsAmplitude[idxCoeff]; - } - + let coefficients = Mapped(ComplexPolar, Zip(coefficientsAmplitude, coefficientsPhase)); let normalizedCoefficients = PNormalized(2.0, coefficientsAmplitude); // Test phase factor on uniform superposition @@ -111,7 +94,7 @@ namespace Microsoft.Quantum.Tests { op(qubitsLE); AssertProbInt(0, prob, qubitsLE, tolerance); AssertProbInt(1, prob, qubitsLE, tolerance); - AssertPhase(phase, (qubitsLE!)[0], tolerance); + Microsoft.Quantum.Diagnostics.AssertPhase(phase, (qubitsLE!)[0], tolerance); ResetAll(qubits); } } @@ -119,40 +102,28 @@ namespace Microsoft.Quantum.Tests { // Test probabilities and phases factor of multi-qubit uniform superposition. - operation StatePreparationComplexCoefficientsMultiQubitPhaseTest () : Unit { + @Test("QuantumSimulator") + operation TestStatePreparationComplexCoefficientsMultiQubitPhase() : Unit { let tolerance = 1E-09; - mutable testCases = new StatePreparationTestCase[10]; - mutable nTests = 0; - - // Test probability and phases of uniform superposition. - set testCases w/= nTests <- StatePreparationTestCase(1, [1.0, 1.0], [0.01, -0.01]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], ConstantArray(8, PI())); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445, 0.461883, 0.149609]); - set nTests = nTests + 1; + + let testCases = [ + // Test probability and phases of uniform superposition + StatePreparationTestCase(1, [1.0, 1.0], [0.01, -0.01]), + StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), + StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], ConstantArray(8, PI())), + StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01]), + StatePreparationTestCase(3, [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445, 0.461883, 0.149609]) + ]; // Loop over tests - for (idxTest in 0 .. nTests - 1) { - let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCases[idxTest]!; - Message($"Test case {idxTest}"); + for (testCase in testCases) { + let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCase!; let nCoefficients = Length(coefficientsAmplitude); using (qubits = Qubit[nQubits]) { let qubitsLE = LittleEndian(qubits); - mutable coefficients = new ComplexPolar[nCoefficients]; - mutable coefficientsPositive = new Double[nCoefficients]; - - for (idxCoeff in 0 .. nCoefficients - 1) { - set coefficients w/= idxCoeff <- ComplexPolar(coefficientsAmplitude[idxCoeff], coefficientsPhase[idxCoeff]); - set coefficientsPositive w/= idxCoeff <- coefficientsAmplitude[idxCoeff]; - } - + let coefficients = Mapped(ComplexPolar, Zip(coefficientsAmplitude, coefficientsPhase)); let normalizedCoefficients = PNormalized(2.0, coefficientsAmplitude); // Test probability and phases of uniform superposition @@ -167,14 +138,12 @@ namespace Microsoft.Quantum.Tests { Controlled (ApplyToEachCA(H, _))(control, qubitsLE!); X(control[0]); - for (idxCoeff in 0 .. nCoefficients - 1) { - let amp = normalizedCoefficients[idxCoeff]; + for ((idxCoeff, amp) in Enumerated(normalizedCoefficients)) { let prob = amp * amp; AssertProbInt(idxCoeff, prob, qubitsLE, tolerance); } - ResetAll(control); - ResetAll(qubits); + ResetAll(control + qubits); //Test phase for (repeats in 0 .. nCoefficients / 2) { @@ -186,9 +155,8 @@ namespace Microsoft.Quantum.Tests { let indexMeasuredInteger = MeasureInteger(qubitsLE); let phase = coefficientsPhase[indexMeasuredInteger]; Message($"StatePreparationComplexCoefficientsTest: expected phase = {phase}."); - AssertPhase(-0.5 * phase, control[0], tolerance); - ResetAll(control); - ResetAll(qubits); + Microsoft.Quantum.Diagnostics.AssertPhase(-0.5 * phase, control[0], tolerance); + ResetAll(control + qubits); } } } @@ -197,39 +165,30 @@ namespace Microsoft.Quantum.Tests { // Test probabilities and phases of arbitrary multi-qubit superposition. - operation StatePreparationComplexCoefficientsArbitraryMultiQubitPhaseTest () : Unit { + @Test("QuantumSimulator") + operation TestStatePreparationComplexCoefficientsArbitraryMultiQubitPhase() : Unit { let tolerance = 1E-09; - mutable testCases = new StatePreparationTestCase[10]; - mutable nTests = 0; - set testCases w/= nTests <- StatePreparationTestCase(1, [1.0986553, 0.359005], [0.419893, 0.118445]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(2, [1.0986553, 0.359005, -0.123, 9.238], [0.419893, 0.118445, -0.467395, 0.419893]); - set nTests = nTests + 1; - set testCases w/= nTests <- StatePreparationTestCase(3, [1.0986553, 0.359005, 0.465689, 0.467395, 0.419893, 0.118445, 0.123, 9.238], [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445, 0.461883, 0.149609]); - set nTests = nTests + 1; + + let testCases = [ + StatePreparationTestCase(1, [1.0986553, 0.359005], [0.419893, 0.118445]), + StatePreparationTestCase(2, [1.0986553, 0.359005, -0.123, 9.238], [0.419893, 0.118445, -0.467395, 0.419893]), + StatePreparationTestCase(3, [1.0986553, 0.359005, 0.465689, 0.467395, 0.419893, 0.118445, 0.123, 9.238], [1.0986553, 0.359005, 0.465689, -0.467395, 0.419893, 0.118445, 0.461883, 0.149609]) + ]; // Loop over tests - for (idxTest in 0 .. nTests - 1) { - let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCases[idxTest]!; - Message($"Test case {idxTest}"); + for (testCase in testCases) { + let (nQubits, coefficientsAmplitude, coefficientsPhase) = testCase!; let nCoefficients = Length(coefficientsAmplitude); using (qubits = Qubit[nQubits]) { let qubitsLE = LittleEndian(qubits); - mutable coefficients = new ComplexPolar[nCoefficients]; - mutable coefficientsPositive = new Double[nCoefficients]; - - for (idxCoeff in 0 .. nCoefficients - 1) { - set coefficients w/= idxCoeff <- ComplexPolar(coefficientsAmplitude[idxCoeff], coefficientsPhase[idxCoeff]); - set coefficientsPositive w/= idxCoeff <- coefficientsAmplitude[idxCoeff]; - } - + let coefficients = Mapped(ComplexPolar, Zip(coefficientsAmplitude, coefficientsPhase)); let normalizedCoefficients = PNormalized(2.0, coefficientsAmplitude); // Test probability and phases of arbitrary superposition let opComplex = StatePreparationComplexCoefficients(coefficients); - let opReal = StatePreparationPositiveCoefficients(coefficientsPositive); + let opReal = StatePreparationPositiveCoefficients(coefficientsAmplitude); using (control = Qubit[1]) { @@ -240,14 +199,12 @@ namespace Microsoft.Quantum.Tests { Controlled opReal(control, qubitsLE); X(control[0]); - for (idxCoeff in 0 .. nCoefficients - 1) { - let amp = normalizedCoefficients[idxCoeff]; + for ((idxCoeff, amp) in Enumerated(normalizedCoefficients)) { let prob = amp * amp; AssertProbInt(idxCoeff, prob, qubitsLE, tolerance); } - ResetAll(control); - ResetAll(qubits); + ResetAll(control + qubits); // Test phase for (repeats in 0 .. nCoefficients / 2) { @@ -259,9 +216,8 @@ namespace Microsoft.Quantum.Tests { let indexMeasuredInteger = MeasureInteger(qubitsLE); let phase = coefficientsPhase[indexMeasuredInteger]; Message($"StatePreparationComplexCoefficientsTest: expected phase = {phase}."); - AssertPhase(-0.5 * phase, control[0], tolerance); - ResetAll(control); - ResetAll(qubits); + Microsoft.Quantum.Diagnostics.AssertPhase(-0.5 * phase, control[0], tolerance); + ResetAll(control + qubits); } } } diff --git a/Standard/tests/UniformSuperpositionPreparationTests.qs b/Standard/tests/UniformSuperpositionPreparationTests.qs index bebb308de36..306ee5f7d40 100644 --- a/Standard/tests/UniformSuperpositionPreparationTests.qs +++ b/Standard/tests/UniformSuperpositionPreparationTests.qs @@ -1,48 +1,29 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. namespace Microsoft.Quantum.Tests { - open Microsoft.Quantum.Preparation; open Microsoft.Quantum.Arithmetic; - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Canon; open Microsoft.Quantum.Convert; - open Microsoft.Quantum.Math; - open Microsoft.Quantum.Measurement; - open Microsoft.Quantum.Arrays; - - - operation PrepareUniformSuperpositionTest() : Unit { - body (...) { - let nQubits = 5; - using(qubits = Qubit[nQubits]) { - for(nIndices in 1..2^nQubits) - { - Message($"Testing nIndices {nIndices} on {nQubits} qubits"); - PrepareUniformSuperposition(nIndices, LittleEndian(qubits)); - - ApplyToEachCA(H,qubits); - - using(flag = Qubit[1]) - { - (ControlledOnInt(0, X))(qubits, flag[0]); - AssertProb([PauliZ], flag, One, IntAsDouble(nIndices)/IntAsDouble(2^nQubits), "", 1e-10); - (ControlledOnInt(0, X))(qubits, flag[0]); - ApplyToEachCA(H,qubits); + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Preparation; - let measuredInt = MeasureInteger(LittleEndian(qubits)); - if(measuredInt >= nIndices){ - fail $"Measured integer {measuredInt} which is bigger than expected of number state {nIndices}."; - } + @Test("QuantumSimulator") + operation TestPrepareUniformSuperposition() : Unit { + let nQubits = 5; + using(qubits = Qubit[nQubits]) { + for(nIndices in 1..2^nQubits) { + Message($"Testing nIndices {nIndices} on {nQubits} qubits"); + PrepareUniformSuperposition(nIndices, LittleEndian(qubits)); - ResetAll(flag); - } - ResetAll(qubits); + let prob = 1.0 / IntAsDouble(nIndices); + for (stateIndex in 0..2^nQubits - 1) { + AssertProbInt(stateIndex, stateIndex < nIndices ? prob | 0.0, LittleEndian(qubits), 1e-10); } + + ResetAll(qubits); } } } } - - From 4e7db2bbab77db650632b55a70304547f54ff6c9 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 14:48:13 +0200 Subject: [PATCH 02/20] Remove duplicate code and reduce T-count. --- Standard/src/Canon/Multiplexer.qs | 62 +++++++++---------------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/Standard/src/Canon/Multiplexer.qs b/Standard/src/Canon/Multiplexer.qs index eca76e3a365..7e20620fc01 100644 --- a/Standard/src/Canon/Multiplexer.qs +++ b/Standard/src/Canon/Multiplexer.qs @@ -5,6 +5,7 @@ namespace Microsoft.Quantum.Canon { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Diagnostics; open Microsoft.Quantum.Math; /// # Summary @@ -85,11 +86,12 @@ namespace Microsoft.Quantum.Canon { } } else { // Recursion that reduces nIndex by 1 & sets Length(auxiliary) to 1. - using (newauxiliary = Qubit[1]) { - let op = LogicalANDMeasAndFix(_, _); + let controls = [Tail(index!)] + auxiliary; + using ((newauxiliary, andauxiliary) = (Qubit[1], Qubit[MaxI(0, Length(controls) - 2)])) { + let op = ApplyAndChain(andauxiliary, _, _); // Naive measurement-free approach uses 4x more T gates with // let op = (Controlled X); - op([index![Length(index!) - 1]] + auxiliary, newauxiliary[0]); + op(controls, newauxiliary[0]); if (nUnitariesRight > 0) { _MultiplexOperationsFromGenerator(rightUnitaries, newauxiliary, newControls, target); } @@ -98,7 +100,7 @@ namespace Microsoft.Quantum.Canon { } apply { _MultiplexOperationsFromGenerator(leftUnitaries, newauxiliary, newControls, target); } - (Adjoint op)([index![Length(index!) - 1]] + auxiliary, newauxiliary[0]); + (Adjoint op)(controls, newauxiliary[0]); } } } @@ -208,46 +210,18 @@ namespace Microsoft.Quantum.Canon { /// /// # References /// - [ *Craig Gidney*, 1709.06648](https://arxiv.org/abs/1709.06648) - internal operation LogicalANDMeasAndFix(ctrlRegister : Qubit[], target : Qubit) - : Unit { - body (...) { - if(Length(ctrlRegister) == 2){ - let c1 = ctrlRegister[0]; - let c2 = ctrlRegister[1]; - H(target); - T(target); - CNOT(c1,target); - CNOT(c2,target); - CNOT(target,c1); - CNOT(target,c2); - (Adjoint T)(c1); - (Adjoint T)(c2); - T(target); - CNOT(target,c2); - CNOT(target,c1); - H(target); - S(target); - } else { - (Controlled X)(ctrlRegister, target); - } - } - adjoint (...) { - if(Length(ctrlRegister) == 2){ - let c1 = ctrlRegister[0]; - let c2 = ctrlRegister[1]; - H(target); - let Meas = M(target); - if (Meas == One) { - within { - H(c2); - } apply { - CNOT(c1,c2); - } - X(target); - } - } else { - (Controlled X)(ctrlRegister, target); - } + internal operation ApplyAndChain(auxRegister : Qubit[], ctrlRegister : Qubit[], target : Qubit) + : Unit is Adj { + if (Length(ctrlRegister) == 0) { + X(target); + } elif (Length(ctrlRegister) == 1) { + CNOT(Head(ctrlRegister), target); + } else { + EqualityFactI(Length(auxRegister), Length(ctrlRegister) - 2, "Unexpected number of auxiliary qubits"); + let controls1 = ctrlRegister[0..0] + auxRegister; + let controls2 = Rest(ctrlRegister); + let targets = auxRegister + [target]; + ApplyToEachA(ApplyAnd, Zip3(controls1, controls2, targets)); } } } From 50f20f90da3401073c095886944bc555e00c1713 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 15:00:10 +0200 Subject: [PATCH 03/20] Cleanup. --- Standard/src/Canon/Multiplexer.qs | 64 +++++++++++++------------------ 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/Standard/src/Canon/Multiplexer.qs b/Standard/src/Canon/Multiplexer.qs index 7e20620fc01..31f21e0a379 100644 --- a/Standard/src/Canon/Multiplexer.qs +++ b/Standard/src/Canon/Multiplexer.qs @@ -44,7 +44,7 @@ namespace Microsoft.Quantum.Canon { } if (nUnitaries > 0) { let auxiliary = new Qubit[0]; - Adjoint _MultiplexOperationsFromGenerator(unitaryGeneratorWithOffset, auxiliary, index, target); + Adjoint MultiplexOperationsFromGeneratorImpl(unitaryGeneratorWithOffset, auxiliary, index, target); } } @@ -52,7 +52,7 @@ namespace Microsoft.Quantum.Canon { /// Implementation step of `MultiplexOperationsFromGenerator`. /// # See Also /// - Microsoft.Quantum.Canon.MultiplexOperationsFromGenerator - operation _MultiplexOperationsFromGenerator<'T>(unitaryGenerator : (Int, Int, (Int -> ('T => Unit is Adj + Ctl))), auxiliary: Qubit[], index: LittleEndian, target: 'T) + internal operation MultiplexOperationsFromGeneratorImpl<'T>(unitaryGenerator : (Int, Int, (Int -> ('T => Unit is Adj + Ctl))), auxiliary: Qubit[], index: LittleEndian, target: 'T) : Unit { body (...) { let nIndex = Length(index!); @@ -60,13 +60,13 @@ namespace Microsoft.Quantum.Canon { let (nUnitaries, unitaryOffset, unitaryFunction) = unitaryGenerator; - let nUnitariesLeft = MinI(nUnitaries, nStates/2); + let nUnitariesLeft = MinI(nUnitaries, nStates / 2); let nUnitariesRight = MinI(nUnitaries, nStates); let leftUnitaries = (nUnitariesLeft, unitaryOffset, unitaryFunction); - let rightUnitaries = (nUnitariesRight-nUnitariesLeft, unitaryOffset + nUnitariesLeft, unitaryFunction); + let rightUnitaries = (nUnitariesRight - nUnitariesLeft, unitaryOffset + nUnitariesLeft, unitaryFunction); - let newControls = LittleEndian(index![0..nIndex - 2]); + let newControls = LittleEndian(Most(index!)); if (nUnitaries > 0) { if (Length(auxiliary) == 1 and nIndex == 0) { @@ -75,39 +75,38 @@ namespace Microsoft.Quantum.Canon { (Controlled Adjoint (unitaryFunction(unitaryOffset)))(auxiliary, target); } elif (Length(auxiliary) == 0 and nIndex >= 1) { // Start case - let newauxiliary = [index![Length(index!) - 1]]; - if(nUnitariesRight > 0){ - _MultiplexOperationsFromGenerator(rightUnitaries, newauxiliary, newControls, target); + let newauxiliary = Tail(index!); + if (nUnitariesRight > 0) { + MultiplexOperationsFromGeneratorImpl(rightUnitaries, [newauxiliary], newControls, target); } within { - X(newauxiliary[0]); + X(newauxiliary); } apply { - _MultiplexOperationsFromGenerator(leftUnitaries, newauxiliary, newControls, target); + MultiplexOperationsFromGeneratorImpl(leftUnitaries, [newauxiliary], newControls, target); } } else { // Recursion that reduces nIndex by 1 & sets Length(auxiliary) to 1. let controls = [Tail(index!)] + auxiliary; - using ((newauxiliary, andauxiliary) = (Qubit[1], Qubit[MaxI(0, Length(controls) - 2)])) { - let op = ApplyAndChain(andauxiliary, _, _); - // Naive measurement-free approach uses 4x more T gates with - // let op = (Controlled X); - op(controls, newauxiliary[0]); - if (nUnitariesRight > 0) { - _MultiplexOperationsFromGenerator(rightUnitaries, newauxiliary, newControls, target); - } + using ((newauxiliary, andauxiliary) = (Qubit(), Qubit[MaxI(0, Length(controls) - 2)])) { within { - (Controlled X)(auxiliary, newauxiliary[0]); + ApplyAndChain(andauxiliary, controls, newauxiliary); } apply { - _MultiplexOperationsFromGenerator(leftUnitaries, newauxiliary, newControls, target); + if (nUnitariesRight > 0) { + MultiplexOperationsFromGeneratorImpl(rightUnitaries, [newauxiliary], newControls, target); + } + within { + (Controlled X)(auxiliary, newauxiliary); + } apply { + MultiplexOperationsFromGeneratorImpl(leftUnitaries, [newauxiliary], newControls, target); + } } - (Adjoint op)(controls, newauxiliary[0]); } } } } adjoint auto; controlled (controlRegister, (...)) { - _MultiplexOperationsFromGenerator(unitaryGenerator, auxiliary + controlRegister, index, target); + MultiplexOperationsFromGeneratorImpl(unitaryGenerator, auxiliary + controlRegister, index, target); } adjoint controlled auto; } @@ -193,23 +192,12 @@ namespace Microsoft.Quantum.Canon { } /// # Summary - /// Computes the logical AND of multiple qubits. - /// # Input - /// ## ctrlRegister - /// Qubits input to the multiple-input AND gate. - /// ## target - /// Qubit on which output of AND is written to. This is - /// assumed to always start in the $\ket{0}$ state. - /// # Remarks - /// When `ctrlRegister` has exactly $2$ qubits, - /// this is equivalent to the `CCNOT` operation but phase with a phase - /// $e^{i\Pi/2}$ on $\ket{001}$ and $-e^{i\Pi/2}$ on $\ket{101}$ and $\ket{011}$. - /// The Adjoint is also measure-and-fixup approach that is the inverse - /// of the original operation only in special cases (see references), - /// but uses fewer T-gates. + /// Computes a chain of AND gates /// - /// # References - /// - [ *Craig Gidney*, 1709.06648](https://arxiv.org/abs/1709.06648) + /// # Description + /// The auxiliary qubits to compute temporary results must be specified explicitly. + /// The length of that register is `Length(ctrlRegister) - 2`, if there are at least + /// two controls, otherwise the length is 0. internal operation ApplyAndChain(auxRegister : Qubit[], ctrlRegister : Qubit[], target : Qubit) : Unit is Adj { if (Length(ctrlRegister) == 0) { From 9c7061f604455b60a1801e7ecf1b7abd60abb0db Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 16:46:49 +0200 Subject: [PATCH 04/20] Squashed commit of the following: commit cb77faa3854d3c3b7b32938585ddb4596d8d84c2 Author: Mathias Soeken Date: Mon Aug 31 16:46:12 2020 +0200 Fix error. commit 984156791d29db5190698ba0b17d3e4733c7f9af Author: Mathias Soeken Date: Mon Aug 31 10:41:17 2020 +0200 Fix bug in QuantumROM. commit 9726ff259748c5e04c2ce39754bb48697fd2ea4e Merge: 58d428ed 0b02a6a5 Author: Mathias Soeken Date: Mon Aug 31 09:49:43 2020 +0200 Merge branch 'master' into msoeken/state-preparation-cleanup commit 58d428ed6476d096fe5678dbc04f6e95b19e0d1f Author: Mathias Soeken Date: Thu Aug 27 11:59:12 2020 +0200 Cleanup remaining files. commit b35222ab855c85c8bab699b0a489af089f811d12 Author: Mathias Soeken Date: Thu Aug 27 11:54:33 2020 +0200 Clean-up arbitrary state preparation. commit e8076f770f3fe4fdba7c17cd176443abcd9aac98 Author: Mathias Soeken Date: Wed Aug 26 09:31:17 2020 +0200 Cleanup quantum ROM. commit 860c0ab12a0fac8a1d053bd6118897761bc547fa Author: Mathias Soeken Date: Wed Aug 26 09:21:21 2020 +0200 Cleanup uniform superposition. commit b94ec8c2dcc26b9afe763e2961218b15a1e7cac3 Author: Mathias Soeken Date: Tue Aug 25 15:56:26 2020 +0200 Cleanup QuantumROM. --- .../JordanWigner/JordanWignerOptimizedBlockEncoding.qs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs b/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs index 887398988d2..e325737b0b3 100644 --- a/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs +++ b/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs @@ -389,8 +389,9 @@ namespace Microsoft.Quantum.Chemistry.JordanWigner { function _JordanWignerOptimizedBlockEncodingQubitManager_ (targetError : Double, nCoeffs : Int, nZ : Int, nMaj : Int, nIdxRegQubits : Int, ctrlRegister : Qubit[]) : ((LittleEndian, Qubit[], Qubit, Qubit[], Qubit[], Qubit[], LittleEndian, LittleEndian[]), (Qubit, Qubit[], Qubit[], Qubit[], LittleEndian[]), Qubit[]) { - - let ((qROMIdx, qROMGarbage), rest0) = _QuantumROMQubitManager(targetError, nCoeffs, ctrlRegister); + let (_, (nIndexRegister, nGarbageQubits)) = QuantumROMQubitCount(targetError, nCoeffs); + let parts = Partitioned([nIndexRegister, nGarbageQubits], ctrlRegister); + let ((qROMIdx, qROMGarbage), rest0) = ((LittleEndian(parts[0]), parts[1]), parts[2]); let ((signQubit, selectZControlRegisters, optimizedBEControlRegisters, pauliBases, indexRegisters, tmp), rest1) = _JordanWignerSelectQubitManager_(nZ, nMaj, nIdxRegQubits, rest0, new Qubit[0]); let registers = Partitioned([3], rest1); let pauliBasesIdx = LittleEndian(registers[0]); From bb56b3a65eaff94675ebc4a639eda5c6958c227f Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 16:46:57 +0200 Subject: [PATCH 05/20] Squashed commit of the following: commit cde7c0cdad4c6b1c9b84906587de575ae206450f Author: Mathias Soeken Date: Mon Aug 31 16:34:40 2020 +0200 Unzipped function. --- Standard/src/Arrays/Zip.qs | 34 ++++++++++++++++++++++++++++++++++ Standard/tests/ArrayTests.qs | 10 ++++++++++ 2 files changed, 44 insertions(+) diff --git a/Standard/src/Arrays/Zip.qs b/Standard/src/Arrays/Zip.qs index caa785a2ba5..9578ac2bdf0 100644 --- a/Standard/src/Arrays/Zip.qs +++ b/Standard/src/Arrays/Zip.qs @@ -131,6 +131,40 @@ namespace Microsoft.Quantum.Arrays { return output; } + /// # Summary + /// Given an array of 2-tuples, returns a tuple of two arrays, each containing + /// the elements of the tuples of the input array. + /// + /// # Type Parameters + /// ## 'T + /// The type of the first element in each tuple + /// ## 'U + /// The type of the second element in each tuple + /// + /// # Input + /// ## arr + /// An array containing 2-tuples + /// + /// # Output + /// Two arrays, the first one containing all first elements of the input + /// tuples, the second one containing all second elements of the input tuples. + /// + /// # Example + /// ```Q# + /// // split is same as ([6, 5, 5, 3, 2, 1], [true, false, false, false, true, false]) + /// let split = Unzipped([(6, true), (5, false), (5, false), (3, false), (2, true), (1, false)]); + /// ``` + function Unzipped<'T, 'U>(arr : ('T, 'U)[]) : ('T[], 'U[]) { + let nElements = Length(arr); + mutable first = new 'T[nElements]; + mutable second = new 'U[nElements]; + for (idxElement in 0 .. nElements - 1) { + let (left, right) = arr[idxElement]; + set first w/= idxElement <- left; + set second w/= idxElement <- right; + } + return (first, second); + } } diff --git a/Standard/tests/ArrayTests.qs b/Standard/tests/ArrayTests.qs index 5dbadf4d724..44a2f00965b 100644 --- a/Standard/tests/ArrayTests.qs +++ b/Standard/tests/ArrayTests.qs @@ -26,6 +26,16 @@ namespace Microsoft.Quantum.Tests { } } + @Test("QuantumSimulator") + function UnzippedTest() : Unit { + let first = [6, 5, 5, 3, 2, 1]; + let second = [true, false, false, false, true, false]; + + let (first2, second2) = Unzipped(Zip(first, second)); + AllEqualityFactI(first2, first, "Unexpected array of integers"); + AllEqualityFactB(second2, second, "Unexpected array of Booleans"); + } + @Test("QuantumSimulator") function LookupTest () : Unit { From 44a2f385bc94a12a2b408809a0030cc3c173a563 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 17:19:02 +0200 Subject: [PATCH 06/20] Preparing function and tests for QuantumROM with signed coefficients. --- Standard/src/Preparation/QuantumROM.qs | 33 +++++++++++++++--- Standard/tests/QuantumROMTests.qs | 46 +++++++++++++++++--------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index 9e49dae3d9d..118e8371e40 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -64,11 +64,29 @@ namespace Microsoft.Quantum.Preparation { function QuantumROM(targetError: Double, coefficients: Double[]) : ((Int, (Int, Int)), Double, ((LittleEndian, Qubit[]) => Unit is Adj + Ctl)) { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; - let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, coefficients); - let nCoeffs = Length(coefficients); + let positiveCoefficients = Mapped(AbsD, coefficients); + let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, positiveCoefficients); + let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, _); + let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, new Qubit[0], _); + let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); + return (qubitCounts, oneNorm, op); + } + + internal function SplitSign(coefficient : Double) : (Double, Bool) { + return (AbsD(coefficient), coefficient < 0.0); + } + + function QuantumROMWithSign(targetError : Double, coefficients : Double[]) + : ((Int, (Int, Int)), Double, ((LittleEndian, Qubit, Qubit[]) => Unit is Adj + Ctl)) { + let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; + let (positiveCoefficients, signs) = Unzipped(Mapped(SplitSign, coefficients)); + let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, positiveCoefficients); + let nCoeffs = Length(positiveCoefficients); + let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); + + let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, _, _); let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); return (qubitCounts, oneNorm, op); } @@ -186,7 +204,7 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROM implementation. - internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, garbageRegister: Qubit[]) + internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, signQubit : Qubit[], garbageRegister: Qubit[]) : Unit is Adj + Ctl { let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex)); let garbageIdx0 = nBitsIndices; @@ -215,6 +233,13 @@ namespace Microsoft.Quantum.Preparation { ApplyToEachCA((Controlled SWAP)([flagQubit], _), Zip(indexRegister!, altIndexRegister!)); } + // # Remark + // Application case for Maybe UDT + internal operation PrepareQuantumROMStateWithSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, signQubit : Qubit, garbageRegister: Qubit[]) + : Unit is Adj + Ctl { + PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, indexRegister, [signQubit], garbageRegister); + } + // Used in QuantumROM implementation. internal function QuantumROMBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[]) : ((LittleEndian, LittleEndian) => Unit is Adj + Ctl) { diff --git a/Standard/tests/QuantumROMTests.qs b/Standard/tests/QuantumROMTests.qs index 301b25ea71d..7d1c7b9e435 100644 --- a/Standard/tests/QuantumROMTests.qs +++ b/Standard/tests/QuantumROMTests.qs @@ -68,26 +68,40 @@ namespace Microsoft.Quantum.Tests { for(nBitsPrecision in -1..-1..-2){ let targetError = PowD(2.0, IntAsDouble(nBitsPrecision)); let probtargetError = targetError / IntAsDouble(coeffs); - let coefficients = DrawMany(DrawRandomDouble, coeffs, (0.0, 1.0)); - let ((nTotal, (nCoeffQubits, nGarbageQubits)), oneNorm, op) = QuantumROM(targetError, coefficients); + let coefficients = DrawMany(DrawRandomDouble, coeffs, (-1.0, 1.0)); - Message($"Test case coeffs {coeffs}, bitsPrecision {nCoeffQubits}, global targetError {targetError}, probability error {probtargetError}."); - for ((idx, coefficient) in Enumerated(coefficients)) { - let tmp = AbsD(coefficient) / oneNorm; - Message($"{idx} expected prob = {tmp}."); + if (true) { // quantum ROM without sign + let ((nTotal, (nCoeffQubits, nGarbageQubits)), oneNorm, op) = QuantumROM(targetError, coefficients); + + using ((coeffRegister, garbageQubits) = (Qubit[nCoeffQubits], Qubit[nGarbageQubits])) { + let coeffQubits = LittleEndian(coeffRegister); + + // Check that probability of each number state in nCoeffQubits is as expected. + within { + op(coeffQubits, garbageQubits); + } apply { + for (stateIndex in 0..coeffs - 1) { + let prob = AbsD(coefficients[stateIndex]) / oneNorm; + AssertProbInt(stateIndex, prob, coeffQubits, probtargetError); + } + } + } } - Message($"Qubits used: {nGarbageQubits} + {nCoeffQubits}"); - using ((coeffRegister, garbageQubits) = (Qubit[nCoeffQubits], Qubit[nGarbageQubits])) { - let coeffQubits = LittleEndian(coeffRegister); + if (true) { // quantum ROM with sign + let ((nTotal, (nCoeffQubits, nGarbageQubits)), oneNorm, op) = QuantumROMWithSign(targetError, coefficients); + + using ((coeffRegister, signQubit, garbageQubits) = (Qubit[nCoeffQubits], Qubit(), Qubit[nGarbageQubits])) { + let coeffQubits = LittleEndian(coeffRegister); - // Check that probability of each number state in nCoeffQubits is as expected. - within { - op(coeffQubits, garbageQubits); - } apply { - for (stateIndex in 0..coeffs - 1) { - let prob = AbsD(coefficients[stateIndex]) / oneNorm; - AssertProbInt(stateIndex, prob, coeffQubits, probtargetError); + // Check that probability of each number state in nCoeffQubits is as expected. + within { + op(coeffQubits, signQubit, garbageQubits); + } apply { + for (stateIndex in 0..coeffs - 1) { + let prob = AbsD(coefficients[stateIndex]) / oneNorm; + AssertProbInt(stateIndex, prob, coeffQubits, probtargetError); + } } } } From 45afe21fc13c76f2ed8a11473d27c9f3330e3e51 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 31 Aug 2020 21:15:44 +0200 Subject: [PATCH 07/20] Further unification. --- Standard/src/Preparation/QuantumROM.qs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index 118e8371e40..c201b4b0d29 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -69,7 +69,7 @@ namespace Microsoft.Quantum.Preparation { let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, new Qubit[0], _); + let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Bool[0], _, new Qubit[0], _); let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); return (qubitCounts, oneNorm, op); } @@ -86,7 +86,7 @@ namespace Microsoft.Quantum.Preparation { let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, _, _); + let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); return (qubitCounts, oneNorm, op); } @@ -204,7 +204,7 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROM implementation. - internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, signQubit : Qubit[], garbageRegister: Qubit[]) + internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Bool[], indexRegister: LittleEndian, signQubit : Qubit[], garbageRegister: Qubit[]) : Unit is Adj + Ctl { let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex)); let garbageIdx0 = nBitsIndices; @@ -235,9 +235,9 @@ namespace Microsoft.Quantum.Preparation { // # Remark // Application case for Maybe UDT - internal operation PrepareQuantumROMStateWithSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, signQubit : Qubit, garbageRegister: Qubit[]) + internal operation PrepareQuantumROMStateWithSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Bool[], indexRegister: LittleEndian, signQubit : Qubit, garbageRegister: Qubit[]) : Unit is Adj + Ctl { - PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, indexRegister, [signQubit], garbageRegister); + PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, indexRegister, [signQubit], garbageRegister); } // Used in QuantumROM implementation. From d3dc13c49037ba8c6631781010649735951f4c2b Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Tue, 1 Sep 2020 13:19:22 +0200 Subject: [PATCH 08/20] Ineffecient way of creating sign bit. --- Standard/src/Arithmetic/Asserts.qs | 24 ++++++++++++++++-------- Standard/src/Preparation/QuantumROM.qs | 19 ++++++++++++++++--- Standard/tests/QuantumROMTests.qs | 4 ++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/Standard/src/Arithmetic/Asserts.qs b/Standard/src/Arithmetic/Asserts.qs index 3d9201c2486..f24d2454a3c 100644 --- a/Standard/src/Arithmetic/Asserts.qs +++ b/Standard/src/Arithmetic/Asserts.qs @@ -7,6 +7,7 @@ namespace Microsoft.Quantum.Arithmetic { open Microsoft.Quantum.Convert; open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Math; /// # Summary /// Asserts that the probability of a specific state of a quantum register has the @@ -40,16 +41,23 @@ namespace Microsoft.Quantum.Arithmetic { /// - `AssertProbInt(0,0.125,qubits,10e-10);` /// - `AssertProbInt(6,0.875,qubits,10e-10);` operation AssertProbInt(stateIndex : Int, expected : Double, qubits : LittleEndian, tolerance : Double) : Unit { - let nQubits = Length(qubits!); - let bits = IntAsBoolArray(stateIndex, nQubits); - using (flag = Qubit()) { - (ControlledOnBitString(bits, X))(qubits!, flag); - AssertProb([PauliZ], [flag], One, expected, $"AssertProbInt failed on stateIndex {stateIndex}, expected probability {expected}.", tolerance); + within { + (ControlledOnInt(stateIndex, X))(qubits!, flag); + } apply { + AssertMeasurementProbability([PauliZ], [flag], One, expected, $"AssertProbInt failed on stateIndex {stateIndex}, expected probability {expected}.", tolerance); + } + } + } - // Uncompute flag qubit. - (ControlledOnBitString(bits, X))(qubits!, flag); - Reset(flag); + operation AssertSignedProbInt(stateIndex : Int, expected : Double, sign : Qubit, qubits : LittleEndian, tolerance : Double) : Unit { + using (flag = Qubit()) { + let signOffset = expected < 0.0 ? 1 <<< Length(qubits!) | 0; + within { + (ControlledOnInt(stateIndex + signOffset, X))(qubits! + [sign], flag); + } apply { + AssertMeasurementProbability([PauliZ], [flag], One, AbsD(expected), $"AssertSignedProbInt failed on stateIndex {stateIndex}, expected probability {expected}.", tolerance); + } } } diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index c201b4b0d29..1a81842e8dc 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -8,6 +8,7 @@ namespace Microsoft.Quantum.Preparation { open Microsoft.Quantum.Convert; open Microsoft.Quantum.Math; open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Synthesis; /// # Summary /// Uses the Quantum ROM technique to represent a given density matrix. @@ -69,7 +70,7 @@ namespace Microsoft.Quantum.Preparation { let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Bool[0], _, new Qubit[0], _); + let op = PrepareQuantumROMStateWithoutSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, _); let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); return (qubitCounts, oneNorm, op); } @@ -86,7 +87,7 @@ namespace Microsoft.Quantum.Preparation { let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); + let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); return (qubitCounts, oneNorm, op); } @@ -206,7 +207,6 @@ namespace Microsoft.Quantum.Preparation { // Used in QuantumROM implementation. internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Bool[], indexRegister: LittleEndian, signQubit : Qubit[], garbageRegister: Qubit[]) : Unit is Adj + Ctl { - let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex)); let garbageIdx0 = nBitsIndices; let garbageIdx1 = garbageIdx0 + nBitsPrecision; let garbageIdx2 = garbageIdx1 + nBitsPrecision; @@ -222,6 +222,7 @@ namespace Microsoft.Quantum.Preparation { ApplyToEachCA(H, uniformKeepCoeffRegister!); // Write bitstrings to altIndex and keepCoeff register. + let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex)); MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister)); // Perform comparison @@ -231,6 +232,18 @@ namespace Microsoft.Quantum.Preparation { // Swap in register based on comparison ApplyToEachCA((Controlled SWAP)([flagQubit], _), Zip(indexRegister!, altIndexRegister!)); + + // FIXME incorporate preparation of sign bit into MultiplexOperationsFromGenerator + if (Length(signs) > 0) { + ApplyXControlledOnTruthTable(BoolArrayAsBigInt(signs), indexRegister!, Head(signQubit)); + } + } + + // # Remark + // Application case for Maybe UDT + internal operation PrepareQuantumROMStateWithoutSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, garbageRegister: Qubit[]) + : Unit is Adj + Ctl { + PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Bool[0], indexRegister, new Qubit[0], garbageRegister); } // # Remark diff --git a/Standard/tests/QuantumROMTests.qs b/Standard/tests/QuantumROMTests.qs index 7d1c7b9e435..e00062d8f19 100644 --- a/Standard/tests/QuantumROMTests.qs +++ b/Standard/tests/QuantumROMTests.qs @@ -99,8 +99,8 @@ namespace Microsoft.Quantum.Tests { op(coeffQubits, signQubit, garbageQubits); } apply { for (stateIndex in 0..coeffs - 1) { - let prob = AbsD(coefficients[stateIndex]) / oneNorm; - AssertProbInt(stateIndex, prob, coeffQubits, probtargetError); + let prob = coefficients[stateIndex] / oneNorm; + AssertSignedProbInt(stateIndex, prob, signQubit, coeffQubits, probtargetError); } } } From e41efcb21a29414623edbd08c407e429be25a4ba Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Tue, 1 Sep 2020 14:18:08 +0200 Subject: [PATCH 09/20] Embed sign computation in multiplex. --- Standard/src/Preparation/QuantumROM.qs | 38 ++++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index 1a81842e8dc..de237030bab 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -8,7 +8,6 @@ namespace Microsoft.Quantum.Preparation { open Microsoft.Quantum.Convert; open Microsoft.Quantum.Math; open Microsoft.Quantum.Arrays; - open Microsoft.Quantum.Synthesis; /// # Summary /// Uses the Quantum ROM technique to represent a given density matrix. @@ -71,7 +70,7 @@ namespace Microsoft.Quantum.Preparation { let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); let op = PrepareQuantumROMStateWithoutSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, _); - let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); + let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs, false); return (qubitCounts, oneNorm, op); } @@ -88,7 +87,7 @@ namespace Microsoft.Quantum.Preparation { let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); - let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs); + let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs, true); return (qubitCounts, oneNorm, op); } @@ -107,11 +106,11 @@ namespace Microsoft.Quantum.Preparation { /// A tuple `(x,(y,z))` where `x = y + z` is the total number of qubits allocated, /// `y` is the number of qubits for the `LittleEndian` register, and `z` is the Number /// of garbage qubits. - function QuantumROMQubitCount(targetError: Double, nCoeffs: Int) + function QuantumROMQubitCount(targetError: Double, nCoeffs: Int, hasSign : Bool) : (Int, (Int, Int)) { let nBitsPrecision = -Ceiling(Lg(0.5*targetError))+1; let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let nGarbageQubits = nBitsIndices + 2 * nBitsPrecision + 1; + let nGarbageQubits = nBitsIndices + 2 * nBitsPrecision + 1 + (hasSign ? 1 | 0); let nTotal = nGarbageQubits + nBitsIndices; return (nTotal, (nBitsIndices, nGarbageQubits)); } @@ -222,8 +221,14 @@ namespace Microsoft.Quantum.Preparation { ApplyToEachCA(H, uniformKeepCoeffRegister!); // Write bitstrings to altIndex and keepCoeff register. - let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex)); - MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister)); + if (Length(signs) == 0) { + let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex)); + MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister)); + } else { + let unitaryGenerator = (nCoeffs, QuantumROMWithSignBitStringWriterByIndex(_, keepCoeff, altIndex, signs)); + let altSignQubit = garbageRegister[garbageIdx3]; + MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister, Head(signQubit), altSignQubit)); + } // Perform comparison CompareUsingRippleCarry(uniformKeepCoeffRegister, keepCoeffRegister, flagQubit); @@ -233,9 +238,9 @@ namespace Microsoft.Quantum.Preparation { // Swap in register based on comparison ApplyToEachCA((Controlled SWAP)([flagQubit], _), Zip(indexRegister!, altIndexRegister!)); - // FIXME incorporate preparation of sign bit into MultiplexOperationsFromGenerator if (Length(signs) > 0) { - ApplyXControlledOnTruthTable(BoolArrayAsBigInt(signs), indexRegister!, Head(signQubit)); + let altSignQubit = garbageRegister[garbageIdx3]; + (Controlled SWAP)([flagQubit], (Head(signQubit), altSignQubit)); } } @@ -266,4 +271,19 @@ namespace Microsoft.Quantum.Preparation { ApplyXorInPlace(altIndex[idx], altIndexRegister); } + // Used in QuantumROMWithSign implementation. + internal function QuantumROMWithSignBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[], signs : Bool[]) + : ((LittleEndian, LittleEndian, Qubit, Qubit) => Unit is Adj + Ctl) { + return WriteQuantumWithSignROMBitString(idx, keepCoeff, altIndex, signs, _, _, _, _); + } + + // Used in QuantumROMWithSign implementation. + internal operation WriteQuantumWithSignROMBitString(idx: Int, keepCoeff: Int[], altIndex: Int[], signs : Bool[], keepCoeffRegister: LittleEndian, altIndexRegister: LittleEndian, signQubit : Qubit, altSignQubit : Qubit) + : Unit is Adj + Ctl { + ApplyXorInPlace(keepCoeff[idx], keepCoeffRegister); + ApplyXorInPlace(altIndex[idx], altIndexRegister); + ApplyIfCA(X, signs[idx], signQubit); + ApplyIfCA(X, signs[altIndex[idx]], altSignQubit); + } + } From 24b794abdb8b174e642b2ae39165fd8fd553de0a Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 3 Sep 2020 16:35:59 +0200 Subject: [PATCH 10/20] Picking changes from #212. --- Standard/src/Preparation/QuantumROM.qs | 59 ++++++++++++-------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index de237030bab..b7700819fc9 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -126,16 +126,19 @@ namespace Microsoft.Quantum.Preparation { fail $"Bits of precision {bitsPrecision} unsupported. Max is 31."; } if (nCoefficients <= 1) { - fail "Cannot prepare state with less than 2 coefficients."; + fail $"Cannot prepare state with less than 2 coefficients."; } if (oneNorm == 0.0) { - fail "State must have at least one coefficient > 0"; + fail $"State must have at least one coefficient > 0"; } - let barHeight = 2^bitsPrecision - 1; + let barHeight = 2 ^ bitsPrecision - 1; mutable altIndex = RangeAsIntArray(0..nCoefficients - 1); - mutable keepCoeff = Mapped(RoundedDiscretizationCoefficients(_, oneNorm, nCoefficients, barHeight), coefficients); + mutable keepCoeff = Mapped( + QuantumROMDiscretizationRoundCoefficients(_, oneNorm, nCoefficients, barHeight), + coefficients + ); // Calculate difference between number of discretized bars vs. maximum mutable bars = 0; @@ -145,49 +148,38 @@ namespace Microsoft.Quantum.Preparation { // Uniformly distribute excess bars across coefficients. for (idx in 0..AbsI(bars) - 1) { - if (bars > 0) { - set keepCoeff w/= idx <- keepCoeff[idx] - 1; - } else { - set keepCoeff w/= idx <- keepCoeff[idx] + 1; - } + set keepCoeff w/= idx <- keepCoeff[idx] + (bars > 0 ? -1 | +1); } - mutable barSink = new Int[nCoefficients]; - mutable barSource = new Int[nCoefficients]; - mutable nBarSink = 0; - mutable nBarSource = 0; + mutable barSink = new Int[0]; + mutable barSource = new Int[0]; for (idxCoeff in IndexRange(keepCoeff)) { if (keepCoeff[idxCoeff] > barHeight) { - set barSource w/= nBarSource <- idxCoeff; - set nBarSource = nBarSource + 1; + set barSource += [idxCoeff]; } elif (keepCoeff[idxCoeff] < barHeight) { - set barSink w/= nBarSink <- idxCoeff; - set nBarSink = nBarSink + 1; + set barSink += [idxCoeff]; } } for (rep in 0..nCoefficients * 10) { - if (nBarSource > 0 and nBarSink > 0) { - let idxSink = barSink[nBarSink - 1]; - let idxSource = barSource[nBarSource - 1]; - set nBarSink = nBarSink - 1; - set nBarSource = nBarSource - 1; + if (Length(barSink) > 0 and Length(barSource) > 0) { + let idxSink = Tail(barSink); + let idxSource = Tail(barSource); + set barSink = Most(barSink); + set barSource = Most(barSource); set keepCoeff w/= idxSource <- keepCoeff[idxSource] - barHeight + keepCoeff[idxSink]; set altIndex w/= idxSink <- idxSource; if (keepCoeff[idxSource] < barHeight) { - set barSink w/= nBarSink <- idxSource; - set nBarSink = nBarSink + 1; - } elif(keepCoeff[idxSource] > barHeight) { - set barSource w/= nBarSource <- idxSource; - set nBarSource = nBarSource + 1; + set barSink += [idxSource]; + } elif (keepCoeff[idxSource] > barHeight) { + set barSource += [idxSource]; } - } - elif (nBarSource > 0) { - let idxSource = barSource[nBarSource - 1]; - set nBarSource = nBarSource - 1; + } elif (Length(barSource) > 0) { + let idxSource = Tail(barSource); + set barSource = Most(barSource); set keepCoeff w/= idxSource <- barHeight; } else { return (oneNorm, keepCoeff, altIndex); @@ -197,6 +189,11 @@ namespace Microsoft.Quantum.Preparation { return (oneNorm, keepCoeff, altIndex); } + // Used in QuantumROM implementation. + internal function QuantumROMDiscretizationRoundCoefficients(coefficient: Double, oneNorm: Double, nCoefficients: Int, barHeight: Int) : Int { + return Round((AbsD(coefficient) / oneNorm) * IntAsDouble(nCoefficients) * IntAsDouble(barHeight)); + } + // Used in QuantumROM implementation. internal function RoundedDiscretizationCoefficients(coefficient: Double, oneNorm: Double, nCoefficients: Int, barHeight: Int) : Int { From 64081f8d881dee33ec21fca8b7cc91669bc9fbd5 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 3 Sep 2020 16:54:53 +0200 Subject: [PATCH 11/20] Changes from #212. --- Standard/src/Preparation/QuantumROM.qs | 41 ++++++++++--------- Standard/src/Preparation/Types.qs | 56 ++++++++++++++++++++++++++ Standard/tests/QuantumROMTests.qs | 6 ++- 3 files changed, 82 insertions(+), 21 deletions(-) create mode 100644 Standard/src/Preparation/Types.qs diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index b7700819fc9..ebf1fab0ff9 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -62,7 +62,7 @@ namespace Microsoft.Quantum.Preparation { /// Ryan Babbush, Craig Gidney, Dominic W. Berry, Nathan Wiebe, Jarrod McClean, Alexandru Paler, Austin Fowler, Hartmut Neven /// https://arxiv.org/abs/1805.03662 function QuantumROM(targetError: Double, coefficients: Double[]) - : ((Int, (Int, Int)), Double, ((LittleEndian, Qubit[]) => Unit is Adj + Ctl)) { + : (MixedStatePreparationRequirements, Double, ((LittleEndian, Qubit[]) => Unit is Adj + Ctl)) { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; let positiveCoefficients = Mapped(AbsD, coefficients); let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, positiveCoefficients); @@ -70,7 +70,7 @@ namespace Microsoft.Quantum.Preparation { let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); let op = PrepareQuantumROMStateWithoutSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, _); - let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs, false); + let qubitCounts = PurifiedMixedStateRequirements(targetError, nCoeffs); return (qubitCounts, oneNorm, op); } @@ -79,7 +79,7 @@ namespace Microsoft.Quantum.Preparation { } function QuantumROMWithSign(targetError : Double, coefficients : Double[]) - : ((Int, (Int, Int)), Double, ((LittleEndian, Qubit, Qubit[]) => Unit is Adj + Ctl)) { + : (MixedStatePreparationRequirements, Double, ((LittleEndian, Qubit, Qubit[]) => Unit is Adj + Ctl)) { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; let (positiveCoefficients, signs) = Unzipped(Mapped(SplitSign, coefficients)); let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, positiveCoefficients); @@ -87,32 +87,35 @@ namespace Microsoft.Quantum.Preparation { let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); - let qubitCounts = QuantumROMQubitCount(targetError, nCoeffs, true); - return (qubitCounts, oneNorm, op); + let qubitCounts = PurifiedMixedStateRequirements(targetError, nCoeffs); + return (qubitCounts w/ NGarbageQubits <- qubitCounts::NGarbageQubits + 1, oneNorm, op); } /// # Summary /// Returns the total number of qubits that must be allocated - /// to the operation returned by `QuantumROM`. + /// in order to apply the operation returned by + /// @"microsoft.quantum.preparation.purifiedmixedstate". /// /// # Input /// ## targetError /// The target error $\epsilon$. - /// ## nCoeffs - /// Number of coefficients specified in `QuantumROM`. + /// ## nCoefficients + /// The number of coefficients to be specified in preparing a mixed state. /// /// # Output - /// ## First parameter - /// A tuple `(x,(y,z))` where `x = y + z` is the total number of qubits allocated, - /// `y` is the number of qubits for the `LittleEndian` register, and `z` is the Number - /// of garbage qubits. - function QuantumROMQubitCount(targetError: Double, nCoeffs: Int, hasSign : Bool) - : (Int, (Int, Int)) { - let nBitsPrecision = -Ceiling(Lg(0.5*targetError))+1; - let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let nGarbageQubits = nBitsIndices + 2 * nBitsPrecision + 1 + (hasSign ? 1 | 0); - let nTotal = nGarbageQubits + nBitsIndices; - return (nTotal, (nBitsIndices, nGarbageQubits)); + /// A description of how many qubits are required in total, and for each of + /// the index and garbage registers used by the + /// @"microsoft.quantum.preparation.purifiedmixedstate" function. + /// + /// # See Also + /// - Microsoft.Quantum.Preparation.PurifiedMixedState + function PurifiedMixedStateRequirements(targetError : Double, nCoefficients : Int) + : MixedStatePreparationRequirements { + let nBitsPrecision = -Ceiling(Lg(0.5*targetError)) + 1; + let nIndexQubits = Ceiling(Lg(IntAsDouble(nCoefficients))); + let nGarbageQubits = nIndexQubits + 2 * nBitsPrecision + 1; + let nTotal = nGarbageQubits + nIndexQubits; + return MixedStatePreparationRequirements(nTotal, (nIndexQubits, nGarbageQubits)); } // Classical processing diff --git a/Standard/src/Preparation/Types.qs b/Standard/src/Preparation/Types.qs new file mode 100644 index 00000000000..e765683ea17 --- /dev/null +++ b/Standard/src/Preparation/Types.qs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Preparation { + open Microsoft.Quantum.Arithmetic; + + /// # Summary + /// Represents a particular mixed state that can be prepared on an index + /// and a garbage register. + /// + /// # Input + /// ## Requirements + /// Specifies the size of the qubit registers required to prepare the + /// mixed state represented by this UDT value. + /// ## Norm + /// Specifies the 1-norm of the coefficients used to define this mixed + /// state. + /// ## Prepare + /// An operation that, given an index register and a garbage register initially + /// in the $\ket{0}$ and $\ket{00\cdots 0}$ states (respectively), + /// prepares the state represented by this UDT value on those registers. + /// + /// # See Also + /// - Microsoft.Quantum.PurifiedMixedState + newtype MixedStatePreparation = ( + Requirements: MixedStatePreparationRequirements, + Norm: Double, + Prepare: ((LittleEndian, Qubit[]) => Unit is Adj + Ctl) + ); + + /// # Summary + /// Represents the number of qubits required in order to prepare a given + /// mixed state. + /// + /// # Input + /// ## NTotalQubits + /// The total number of qubits required by the represented state preparation + /// operation. + /// ## NIndexQubits + /// The number of qubits required for the index register used by the + /// represented state preparation operation. + /// ## NGarbageQubits + /// The number of qubits required for the garbage register used by the + /// represented state preparation operation. + /// + /// # See Also + /// - Microsoft.Quantum.PurifiedMixedState + newtype MixedStatePreparationRequirements = ( + NTotalQubits: Int, + ( + NIndexQubits: Int, + NGarbageQubits: Int + ) + ); + +} diff --git a/Standard/tests/QuantumROMTests.qs b/Standard/tests/QuantumROMTests.qs index e00062d8f19..836f13b2348 100644 --- a/Standard/tests/QuantumROMTests.qs +++ b/Standard/tests/QuantumROMTests.qs @@ -71,7 +71,8 @@ namespace Microsoft.Quantum.Tests { let coefficients = DrawMany(DrawRandomDouble, coeffs, (-1.0, 1.0)); if (true) { // quantum ROM without sign - let ((nTotal, (nCoeffQubits, nGarbageQubits)), oneNorm, op) = QuantumROM(targetError, coefficients); + let (reqs, oneNorm, op) = QuantumROM(targetError, coefficients); + let (nTotal, (nCoeffQubits, nGarbageQubits)) = reqs!; using ((coeffRegister, garbageQubits) = (Qubit[nCoeffQubits], Qubit[nGarbageQubits])) { let coeffQubits = LittleEndian(coeffRegister); @@ -89,7 +90,8 @@ namespace Microsoft.Quantum.Tests { } if (true) { // quantum ROM with sign - let ((nTotal, (nCoeffQubits, nGarbageQubits)), oneNorm, op) = QuantumROMWithSign(targetError, coefficients); + let (reqs, oneNorm, op) = QuantumROMWithSign(targetError, coefficients); + let (nTotal, (nCoeffQubits, nGarbageQubits)) = reqs!; using ((coeffRegister, signQubit, garbageQubits) = (Qubit[nCoeffQubits], Qubit(), Qubit[nGarbageQubits])) { let coeffQubits = LittleEndian(coeffRegister); From 131ef140046501043ee3150bf14e8e1407225519 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 3 Sep 2020 17:09:17 +0200 Subject: [PATCH 12/20] Generalize. --- Standard/src/Preparation/QuantumROM.qs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index ebf1fab0ff9..1cd2b83b438 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -74,8 +74,8 @@ namespace Microsoft.Quantum.Preparation { return (qubitCounts, oneNorm, op); } - internal function SplitSign(coefficient : Double) : (Double, Bool) { - return (AbsD(coefficient), coefficient < 0.0); + internal function SplitSign(coefficient : Double) : (Double, Int) { + return (AbsD(coefficient), coefficient < 0.0 ? 1 | 0); } function QuantumROMWithSign(targetError : Double, coefficients : Double[]) @@ -204,7 +204,7 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROM implementation. - internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Bool[], indexRegister: LittleEndian, signQubit : Qubit[], garbageRegister: Qubit[]) + internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], indexRegister: LittleEndian, signQubit : Qubit[], garbageRegister: Qubit[]) : Unit is Adj + Ctl { let garbageIdx0 = nBitsIndices; let garbageIdx1 = garbageIdx0 + nBitsPrecision; @@ -215,6 +215,8 @@ namespace Microsoft.Quantum.Preparation { let keepCoeffRegister = LittleEndian(garbageRegister[garbageIdx0..garbageIdx1 - 1]); let uniformKeepCoeffRegister = LittleEndian(garbageRegister[garbageIdx1..garbageIdx2 - 1]); let flagQubit = garbageRegister[garbageIdx3 - 1]; + let dataRegister = LittleEndian(signQubit); + let altDataRegister = LittleEndian(garbageRegister[garbageIdx3...]); // Create uniform superposition over index and alt coeff register. PrepareUniformSuperposition(nCoeffs, indexRegister); @@ -226,8 +228,7 @@ namespace Microsoft.Quantum.Preparation { MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister)); } else { let unitaryGenerator = (nCoeffs, QuantumROMWithSignBitStringWriterByIndex(_, keepCoeff, altIndex, signs)); - let altSignQubit = garbageRegister[garbageIdx3]; - MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister, Head(signQubit), altSignQubit)); + MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister, dataRegister, altDataRegister)); } // Perform comparison @@ -248,12 +249,12 @@ namespace Microsoft.Quantum.Preparation { // Application case for Maybe UDT internal operation PrepareQuantumROMStateWithoutSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, garbageRegister: Qubit[]) : Unit is Adj + Ctl { - PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Bool[0], indexRegister, new Qubit[0], garbageRegister); + PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Int[0], indexRegister, new Qubit[0], garbageRegister); } // # Remark // Application case for Maybe UDT - internal operation PrepareQuantumROMStateWithSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Bool[], indexRegister: LittleEndian, signQubit : Qubit, garbageRegister: Qubit[]) + internal operation PrepareQuantumROMStateWithSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], indexRegister: LittleEndian, signQubit : Qubit, garbageRegister: Qubit[]) : Unit is Adj + Ctl { PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, indexRegister, [signQubit], garbageRegister); } @@ -272,18 +273,18 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROMWithSign implementation. - internal function QuantumROMWithSignBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[], signs : Bool[]) - : ((LittleEndian, LittleEndian, Qubit, Qubit) => Unit is Adj + Ctl) { + internal function QuantumROMWithSignBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[], signs : Int[]) + : ((LittleEndian, LittleEndian, LittleEndian, LittleEndian) => Unit is Adj + Ctl) { return WriteQuantumWithSignROMBitString(idx, keepCoeff, altIndex, signs, _, _, _, _); } // Used in QuantumROMWithSign implementation. - internal operation WriteQuantumWithSignROMBitString(idx: Int, keepCoeff: Int[], altIndex: Int[], signs : Bool[], keepCoeffRegister: LittleEndian, altIndexRegister: LittleEndian, signQubit : Qubit, altSignQubit : Qubit) + internal operation WriteQuantumWithSignROMBitString(idx: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], keepCoeffRegister: LittleEndian, altIndexRegister: LittleEndian, dataRegister : LittleEndian, altDataRegister : LittleEndian) : Unit is Adj + Ctl { ApplyXorInPlace(keepCoeff[idx], keepCoeffRegister); ApplyXorInPlace(altIndex[idx], altIndexRegister); - ApplyIfCA(X, signs[idx], signQubit); - ApplyIfCA(X, signs[altIndex[idx]], altSignQubit); + ApplyXorInPlace(signs[idx], dataRegister); + ApplyXorInPlace(signs[altIndex[idx]], altDataRegister); } } From 91c72c3edc2a731fb0db091dc13484a1cbc83500 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 09:31:06 +0200 Subject: [PATCH 13/20] Generalize data register. --- Standard/src/Preparation/QuantumROM.qs | 47 +++++++------------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index 1cd2b83b438..e805e51aa02 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -204,7 +204,7 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROM implementation. - internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], indexRegister: LittleEndian, signQubit : Qubit[], garbageRegister: Qubit[]) + internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], indexRegister: LittleEndian, dataQubits : Qubit[], garbageRegister: Qubit[]) : Unit is Adj + Ctl { let garbageIdx0 = nBitsIndices; let garbageIdx1 = garbageIdx0 + nBitsPrecision; @@ -215,7 +215,7 @@ namespace Microsoft.Quantum.Preparation { let keepCoeffRegister = LittleEndian(garbageRegister[garbageIdx0..garbageIdx1 - 1]); let uniformKeepCoeffRegister = LittleEndian(garbageRegister[garbageIdx1..garbageIdx2 - 1]); let flagQubit = garbageRegister[garbageIdx3 - 1]; - let dataRegister = LittleEndian(signQubit); + let dataRegister = LittleEndian(dataQubits); let altDataRegister = LittleEndian(garbageRegister[garbageIdx3...]); // Create uniform superposition over index and alt coeff register. @@ -223,13 +223,8 @@ namespace Microsoft.Quantum.Preparation { ApplyToEachCA(H, uniformKeepCoeffRegister!); // Write bitstrings to altIndex and keepCoeff register. - if (Length(signs) == 0) { - let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex)); - MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister)); - } else { - let unitaryGenerator = (nCoeffs, QuantumROMWithSignBitStringWriterByIndex(_, keepCoeff, altIndex, signs)); - MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister, dataRegister, altDataRegister)); - } + let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex, signs)); + MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister, dataRegister, altDataRegister)); // Perform comparison CompareUsingRippleCarry(uniformKeepCoeffRegister, keepCoeffRegister, flagQubit); @@ -237,12 +232,7 @@ namespace Microsoft.Quantum.Preparation { let indexRegisterSize = Length(indexRegister!); // Swap in register based on comparison - ApplyToEachCA((Controlled SWAP)([flagQubit], _), Zip(indexRegister!, altIndexRegister!)); - - if (Length(signs) > 0) { - let altSignQubit = garbageRegister[garbageIdx3]; - (Controlled SWAP)([flagQubit], (Head(signQubit), altSignQubit)); - } + ApplyToEachCA((Controlled SWAP)([flagQubit], _), Zip(indexRegister! + dataRegister!, altIndexRegister! + altDataRegister!)); } // # Remark @@ -260,31 +250,20 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROM implementation. - internal function QuantumROMBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[]) - : ((LittleEndian, LittleEndian) => Unit is Adj + Ctl) { - return WriteQuantumROMBitString(idx, keepCoeff, altIndex, _, _); - } - - // Used in QuantumROM implementation. - internal operation WriteQuantumROMBitString(idx: Int, keepCoeff: Int[], altIndex: Int[], keepCoeffRegister: LittleEndian, altIndexRegister: LittleEndian) - : Unit is Adj + Ctl { - ApplyXorInPlace(keepCoeff[idx], keepCoeffRegister); - ApplyXorInPlace(altIndex[idx], altIndexRegister); - } - - // Used in QuantumROMWithSign implementation. - internal function QuantumROMWithSignBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[], signs : Int[]) + internal function QuantumROMBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[], data : Int[]) : ((LittleEndian, LittleEndian, LittleEndian, LittleEndian) => Unit is Adj + Ctl) { - return WriteQuantumWithSignROMBitString(idx, keepCoeff, altIndex, signs, _, _, _, _); + return WriteQuantumROMBitString(idx, keepCoeff, altIndex, data, _, _, _, _); } - // Used in QuantumROMWithSign implementation. - internal operation WriteQuantumWithSignROMBitString(idx: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], keepCoeffRegister: LittleEndian, altIndexRegister: LittleEndian, dataRegister : LittleEndian, altDataRegister : LittleEndian) + // Used in QuantumROM implementation. + internal operation WriteQuantumROMBitString(idx: Int, keepCoeff: Int[], altIndex: Int[], data : Int[], keepCoeffRegister: LittleEndian, altIndexRegister: LittleEndian, dataRegister : LittleEndian, altDataRegister : LittleEndian) : Unit is Adj + Ctl { ApplyXorInPlace(keepCoeff[idx], keepCoeffRegister); ApplyXorInPlace(altIndex[idx], altIndexRegister); - ApplyXorInPlace(signs[idx], dataRegister); - ApplyXorInPlace(signs[altIndex[idx]], altDataRegister); + if (Length(dataRegister!) > 0) { + ApplyXorInPlace(data[idx], dataRegister); + ApplyXorInPlace(data[altIndex[idx]], altDataRegister); + } } } From 5ef60819bbfc0331d3fe23500ea4540633fb930a Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 09:38:30 +0200 Subject: [PATCH 14/20] Unifying signatures. --- Standard/src/Preparation/QuantumROM.qs | 24 +++++------------------- Standard/tests/QuantumROMTests.qs | 2 +- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index e805e51aa02..3619f0c68e9 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -69,7 +69,7 @@ namespace Microsoft.Quantum.Preparation { let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMStateWithoutSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, _, _); + let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Int[0], _, new Qubit[0], _); let qubitCounts = PurifiedMixedStateRequirements(targetError, nCoeffs); return (qubitCounts, oneNorm, op); } @@ -79,14 +79,14 @@ namespace Microsoft.Quantum.Preparation { } function QuantumROMWithSign(targetError : Double, coefficients : Double[]) - : (MixedStatePreparationRequirements, Double, ((LittleEndian, Qubit, Qubit[]) => Unit is Adj + Ctl)) { + : (MixedStatePreparationRequirements, Double, ((LittleEndian, Qubit[], Qubit[]) => Unit is Adj + Ctl)) { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; let (positiveCoefficients, signs) = Unzipped(Mapped(SplitSign, coefficients)); let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, positiveCoefficients); let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMStateWithSign(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); + let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); let qubitCounts = PurifiedMixedStateRequirements(targetError, nCoeffs); return (qubitCounts w/ NGarbageQubits <- qubitCounts::NGarbageQubits + 1, oneNorm, op); } @@ -204,7 +204,7 @@ namespace Microsoft.Quantum.Preparation { } // Used in QuantumROM implementation. - internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], indexRegister: LittleEndian, dataQubits : Qubit[], garbageRegister: Qubit[]) + internal operation PrepareQuantumROMState(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], data : Int[], indexRegister: LittleEndian, dataQubits : Qubit[], garbageRegister: Qubit[]) : Unit is Adj + Ctl { let garbageIdx0 = nBitsIndices; let garbageIdx1 = garbageIdx0 + nBitsPrecision; @@ -223,7 +223,7 @@ namespace Microsoft.Quantum.Preparation { ApplyToEachCA(H, uniformKeepCoeffRegister!); // Write bitstrings to altIndex and keepCoeff register. - let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex, signs)); + let unitaryGenerator = (nCoeffs, QuantumROMBitStringWriterByIndex(_, keepCoeff, altIndex, data)); MultiplexOperationsFromGenerator(unitaryGenerator, indexRegister, (keepCoeffRegister, altIndexRegister, dataRegister, altDataRegister)); // Perform comparison @@ -235,20 +235,6 @@ namespace Microsoft.Quantum.Preparation { ApplyToEachCA((Controlled SWAP)([flagQubit], _), Zip(indexRegister! + dataRegister!, altIndexRegister! + altDataRegister!)); } - // # Remark - // Application case for Maybe UDT - internal operation PrepareQuantumROMStateWithoutSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], indexRegister: LittleEndian, garbageRegister: Qubit[]) - : Unit is Adj + Ctl { - PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Int[0], indexRegister, new Qubit[0], garbageRegister); - } - - // # Remark - // Application case for Maybe UDT - internal operation PrepareQuantumROMStateWithSign(nBitsPrecision: Int, nCoeffs: Int, nBitsIndices: Int, keepCoeff: Int[], altIndex: Int[], signs : Int[], indexRegister: LittleEndian, signQubit : Qubit, garbageRegister: Qubit[]) - : Unit is Adj + Ctl { - PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, indexRegister, [signQubit], garbageRegister); - } - // Used in QuantumROM implementation. internal function QuantumROMBitStringWriterByIndex(idx : Int, keepCoeff : Int[], altIndex : Int[], data : Int[]) : ((LittleEndian, LittleEndian, LittleEndian, LittleEndian) => Unit is Adj + Ctl) { diff --git a/Standard/tests/QuantumROMTests.qs b/Standard/tests/QuantumROMTests.qs index 836f13b2348..72673c1341d 100644 --- a/Standard/tests/QuantumROMTests.qs +++ b/Standard/tests/QuantumROMTests.qs @@ -98,7 +98,7 @@ namespace Microsoft.Quantum.Tests { // Check that probability of each number state in nCoeffQubits is as expected. within { - op(coeffQubits, signQubit, garbageQubits); + op(coeffQubits, [signQubit], garbageQubits); } apply { for (stateIndex in 0..coeffs - 1) { let prob = coefficients[stateIndex] / oneNorm; From b761750b3831c78bd1770ae0f0766809fbef9429 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 09:59:28 +0200 Subject: [PATCH 15/20] Use UDT for PurifiedState. --- Standard/src/Preparation/QuantumROM.qs | 88 +++++++++++++++++--------- Standard/src/Preparation/Types.qs | 7 +- Standard/tests/QuantumROMTests.qs | 18 +++--- 3 files changed, 69 insertions(+), 44 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index 3619f0c68e9..677d589ec60 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -10,19 +10,46 @@ namespace Microsoft.Quantum.Preparation { open Microsoft.Quantum.Arrays; /// # Summary - /// Uses the Quantum ROM technique to represent a given density matrix. + /// Returns an operation that prepares a a purification of a given mixed + /// state. /// - /// Given a list of $N$ coefficients $\alpha_j$, this returns a unitary $U$ that uses the Quantum-ROM - /// technique to prepare - /// an approximation $\tilde\rho\sum_{j=0}^{N-1}p_j\ket{j}\bra{j}$ of the purification of the density matrix - /// $\rho=\sum_{j=0}^{N-1}\frac{|alpha_j|}{\sum_k |\alpha_k|}\ket{j}\bra{j}$. In this approximation, the - /// error $\epsilon$ is such that $|p_j-\frac{|alpha_j|}{\sum_k |\alpha_k|}|\le \epsilon / N$ and - /// $\|\tilde\rho - \rho\| \le \epsilon$. In other words, + /// # Description + /// Uses the Quantum ROM technique to represent a given density matrix, + /// returning that representation as a state preparation operation. + /// + /// In particular, given a list of $N$ coefficients $\alpha_j$, this + /// function returns an operation that uses the Quantum ROM technique to + /// prepare an approximation + /// $$ + /// \begin{align} + /// \tilde\rho = \sum_{j = 0}^{N - 1} p_j \ket{j}\bra{j} + /// \end{align} + /// $$ + /// of the mixed state /// $$ /// \begin{align} - /// U\ket{0}^{\lceil\log_2 N\rceil}\ket{0}^{m}=\sum_{j=0}^{N-1}\sqrt{p_j} \ket{j}\ket{\text{garbage}_j}. + /// \rho = \sum_{j = 0}^{N-1}\ frac{|alpha_j|}{\sum_k |\alpha_k|} \ket{j}\bra{j}, /// \end{align} /// $$ + /// where each $p_j$ is an approximation to the given coefficient $\alpha_j$ + /// such that + /// $$ + /// \begin{align} + /// \left| p_j - \frac{ |\alpha_j| }{ \sum_k |\alpha_k| } \le \frac{\epsilon}{N} + /// \end{align} + /// $$ + /// for each $j$. + /// + /// When passed an index register and a register of garbage qubits, + /// initially in the state $\ket{0} \ket{00\cdots 0}, the returned operation + /// prepares both registers into the purification of $\tilde \rho$, + /// $$ + /// \begin{align} + /// \sum_{j=0}^{N-1} \sqrt{p_j} \ket{j}\ket{\text{garbage}_j}, + /// \end{align} + /// $$ + /// such that resetting and deallocating the garbage register enacts the + /// desired preparation to within the target error $\epsilon$. /// /// # Input /// ## targetError @@ -32,27 +59,26 @@ namespace Microsoft.Quantum.Preparation { /// Negative numbers $-\alpha_j$ will be treated as positive $|\alpha_j|$. /// /// # Output - /// ## First parameter - /// A tuple `(x,(y,z))` where `x = y + z` is the total number of qubits allocated, - /// `y` is the number of qubits for the `LittleEndian` register, and `z` is the Number - /// of garbage qubits. - /// ## Second parameter - /// The one-norm $\sum_j |\alpha_j|$ of the coefficient array. - /// ## Third parameter - /// The unitary $U$. + /// An operation that prepares $\tilde \rho$ as a purification onto a joint + /// index and garbage register. /// /// # Remarks - /// ## Example + /// The coefficients provided to this operation are normalized following the + /// 1-norm, such that the coefficients are always considered to describe a + /// valid categorical probability distribution. + /// + /// # Example /// The following code snippet prepares an purification of the $3$-qubit state /// $\rho=\sum_{j=0}^{4}\frac{|alpha_j|}{\sum_k |\alpha_k|}\ket{j}\bra{j}$, where - /// $\vec\alpha=(1.0,2.0,3.0,4.0,5.0)$, and the error is `1e-3`; - /// ```qsharp - /// let coefficients = [1.0,2.0,3.0,4.0,5.0]; + /// $\vec\alpha=(1.0, 2.0, 3.0, 4.0, 5.0)$, and the target error is + /// $10^{-3}$: + /// ```Q# + /// let coefficients = [1.0, 2.0, 3.0, 4.0, 5.0]; /// let targetError = 1e-3; - /// let ((nTotalQubits, (nIndexQubits, nGarbageQubits)), oneNorm, op) = QuantumROM(targetError, coefficients); - /// using (indexRegister = Qubit[nIndexQubits]) { - /// using (garbageRegister = Qubit[nGarbageQubits]) { - /// op(LittleEndian(indexRegister), garbageRegister); + /// let purifiedState = PurifiedMixedState(targetError, coefficients); + /// using (indexRegister = Qubit[purifiedState::Requirements::NIndexQubits]) { + /// using (garbageRegister = Qubit[purifiedState::Requirements::NGarbageQubits]) { + /// purifiedState::Prepare(LittleEndian(indexRegister), new Qubit[0], garbageRegister); /// } /// } /// ``` @@ -61,25 +87,25 @@ namespace Microsoft.Quantum.Preparation { /// - Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity /// Ryan Babbush, Craig Gidney, Dominic W. Berry, Nathan Wiebe, Jarrod McClean, Alexandru Paler, Austin Fowler, Hartmut Neven /// https://arxiv.org/abs/1805.03662 - function QuantumROM(targetError: Double, coefficients: Double[]) - : (MixedStatePreparationRequirements, Double, ((LittleEndian, Qubit[]) => Unit is Adj + Ctl)) { + function PurifiedMixedState(targetError : Double, coefficients : Double[]) + : MixedStatePreparation { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; let positiveCoefficients = Mapped(AbsD, coefficients); let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, positiveCoefficients); let nCoeffs = Length(positiveCoefficients); let nBitsIndices = Ceiling(Lg(IntAsDouble(nCoeffs))); - let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Int[0], _, new Qubit[0], _); + let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, new Int[0], _, _, _); let qubitCounts = PurifiedMixedStateRequirements(targetError, nCoeffs); - return (qubitCounts, oneNorm, op); + return MixedStatePreparation(qubitCounts, oneNorm, op); } internal function SplitSign(coefficient : Double) : (Double, Int) { return (AbsD(coefficient), coefficient < 0.0 ? 1 | 0); } - function QuantumROMWithSign(targetError : Double, coefficients : Double[]) - : (MixedStatePreparationRequirements, Double, ((LittleEndian, Qubit[], Qubit[]) => Unit is Adj + Ctl)) { + function PurifiedMixedStateWithSign(targetError : Double, coefficients : Double[]) + : MixedStatePreparation { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; let (positiveCoefficients, signs) = Unzipped(Mapped(SplitSign, coefficients)); let (oneNorm, keepCoeff, altIndex) = _QuantumROMDiscretization(nBitsPrecision, positiveCoefficients); @@ -88,7 +114,7 @@ namespace Microsoft.Quantum.Preparation { let op = PrepareQuantumROMState(nBitsPrecision, nCoeffs, nBitsIndices, keepCoeff, altIndex, signs, _, _, _); let qubitCounts = PurifiedMixedStateRequirements(targetError, nCoeffs); - return (qubitCounts w/ NGarbageQubits <- qubitCounts::NGarbageQubits + 1, oneNorm, op); + return MixedStatePreparation(qubitCounts w/ NGarbageQubits <- qubitCounts::NGarbageQubits + 1, oneNorm, op); } /// # Summary diff --git a/Standard/src/Preparation/Types.qs b/Standard/src/Preparation/Types.qs index e765683ea17..12a8cf2b7fc 100644 --- a/Standard/src/Preparation/Types.qs +++ b/Standard/src/Preparation/Types.qs @@ -16,8 +16,9 @@ namespace Microsoft.Quantum.Preparation { /// Specifies the 1-norm of the coefficients used to define this mixed /// state. /// ## Prepare - /// An operation that, given an index register and a garbage register initially - /// in the $\ket{0}$ and $\ket{00\cdots 0}$ states (respectively), + /// An operation that, given an index register, a data register, and a + /// garbage register initially in the $\ket{0}$, $\let{00\dots 0}$, and + /// $\ket{00\dots 0}$ states (respectively), /// prepares the state represented by this UDT value on those registers. /// /// # See Also @@ -25,7 +26,7 @@ namespace Microsoft.Quantum.Preparation { newtype MixedStatePreparation = ( Requirements: MixedStatePreparationRequirements, Norm: Double, - Prepare: ((LittleEndian, Qubit[]) => Unit is Adj + Ctl) + Prepare: ((LittleEndian, Qubit[], Qubit[]) => Unit is Adj + Ctl) ); /// # Summary diff --git a/Standard/tests/QuantumROMTests.qs b/Standard/tests/QuantumROMTests.qs index 72673c1341d..fab33207d79 100644 --- a/Standard/tests/QuantumROMTests.qs +++ b/Standard/tests/QuantumROMTests.qs @@ -71,18 +71,17 @@ namespace Microsoft.Quantum.Tests { let coefficients = DrawMany(DrawRandomDouble, coeffs, (-1.0, 1.0)); if (true) { // quantum ROM without sign - let (reqs, oneNorm, op) = QuantumROM(targetError, coefficients); - let (nTotal, (nCoeffQubits, nGarbageQubits)) = reqs!; + let purifiedState = PurifiedMixedState(targetError, coefficients); - using ((coeffRegister, garbageQubits) = (Qubit[nCoeffQubits], Qubit[nGarbageQubits])) { + using ((coeffRegister, garbageQubits) = (Qubit[purifiedState::Requirements::NIndexQubits], Qubit[purifiedState::Requirements::NGarbageQubits])) { let coeffQubits = LittleEndian(coeffRegister); // Check that probability of each number state in nCoeffQubits is as expected. within { - op(coeffQubits, garbageQubits); + purifiedState::Prepare(coeffQubits, new Qubit[0], garbageQubits); } apply { for (stateIndex in 0..coeffs - 1) { - let prob = AbsD(coefficients[stateIndex]) / oneNorm; + let prob = AbsD(coefficients[stateIndex]) / purifiedState::Norm; AssertProbInt(stateIndex, prob, coeffQubits, probtargetError); } } @@ -90,18 +89,17 @@ namespace Microsoft.Quantum.Tests { } if (true) { // quantum ROM with sign - let (reqs, oneNorm, op) = QuantumROMWithSign(targetError, coefficients); - let (nTotal, (nCoeffQubits, nGarbageQubits)) = reqs!; + let purifiedState = PurifiedMixedStateWithSign(targetError, coefficients); - using ((coeffRegister, signQubit, garbageQubits) = (Qubit[nCoeffQubits], Qubit(), Qubit[nGarbageQubits])) { + using ((coeffRegister, signQubit, garbageQubits) = (Qubit[purifiedState::Requirements::NIndexQubits], Qubit(), Qubit[purifiedState::Requirements::NGarbageQubits])) { let coeffQubits = LittleEndian(coeffRegister); // Check that probability of each number state in nCoeffQubits is as expected. within { - op(coeffQubits, [signQubit], garbageQubits); + purifiedState::Prepare(coeffQubits, [signQubit], garbageQubits); } apply { for (stateIndex in 0..coeffs - 1) { - let prob = coefficients[stateIndex] / oneNorm; + let prob = coefficients[stateIndex] / purifiedState::Norm; AssertSignedProbInt(stateIndex, prob, signQubit, coeffQubits, probtargetError); } } From 39ac8d355b55475bd28f8fe9ad0c81598a73ef64 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 11:39:52 +0200 Subject: [PATCH 16/20] Docs and cleanup. --- Standard/src/Preparation/QuantumROM.qs | 49 ++++++++++++++++++++++---- Standard/tests/QuantumROMTests.qs | 2 +- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Standard/src/Preparation/QuantumROM.qs b/Standard/src/Preparation/QuantumROM.qs index 677d589ec60..a5f04748782 100644 --- a/Standard/src/Preparation/QuantumROM.qs +++ b/Standard/src/Preparation/QuantumROM.qs @@ -104,7 +104,47 @@ namespace Microsoft.Quantum.Preparation { return (AbsD(coefficient), coefficient < 0.0 ? 1 | 0); } - function PurifiedMixedStateWithSign(targetError : Double, coefficients : Double[]) + /// # Summary + /// Same as @"microsoft.quantum.preparation.purifiedmixedstate" but + /// also prepares sign of the coefficient on an extra qubit. + /// + /// # Input + /// ## targetError + /// The target error $\epsilon$. + /// ## coefficients + /// Array of $N$ coefficients specifying the probability of basis states. + /// Negative numbers $-\alpha_j$ will be treated as positive $|\alpha_j|$, + /// but the sign of a negative number will be prepared on a separate data + /// qubit. + /// + /// # Output + /// An operation that prepares $\tilde \rho$ as a purification onto a joint + /// index and garbage register. + /// + /// # Remarks + /// The coefficients provided to this operation are normalized following the + /// 1-norm, such that the coefficients are always considered to describe a + /// valid categorical probability distribution. + /// + /// # Example + /// The following code snippet prepares an purification of the $3$-qubit state + /// $\rho=\sum_{j=0}^{4}\frac{|alpha_j|}{\sum_k |\alpha_k|}\ket{j}\bra{j}$, where + /// $\vec\alpha=(1.0, 2.0, 3.0, 4.0, 5.0)$, and the target error is + /// $10^{-3}$: + /// ```Q# + /// let coefficients = [1.0, -2.0, 3.0, -4.0, 5.0]; + /// let targetError = 1e-3; + /// let purifiedState = PurifiedMixedStateAndSign(targetError, coefficients); + /// using ((indexRegister, sign) = (Qubit[purifiedState::Requirements::NIndexQubits], Qubit())) { + /// using (garbageRegister = Qubit[purifiedState::Requirements::NGarbageQubits]) { + /// purifiedState::Prepare(LittleEndian(indexRegister), [sign], garbageRegister); + /// } + /// } + /// ``` + /// + /// # See Also + /// - PurifiedMixedState + function PurifiedMixedStateAndSign(targetError : Double, coefficients : Double[]) : MixedStatePreparation { let nBitsPrecision = -Ceiling(Lg(0.5 * targetError)) + 1; let (positiveCoefficients, signs) = Unzipped(Mapped(SplitSign, coefficients)); @@ -165,7 +205,7 @@ namespace Microsoft.Quantum.Preparation { mutable altIndex = RangeAsIntArray(0..nCoefficients - 1); mutable keepCoeff = Mapped( - QuantumROMDiscretizationRoundCoefficients(_, oneNorm, nCoefficients, barHeight), + RoundedDiscretizationCoefficients(_, oneNorm, nCoefficients, barHeight), coefficients ); @@ -218,11 +258,6 @@ namespace Microsoft.Quantum.Preparation { return (oneNorm, keepCoeff, altIndex); } - // Used in QuantumROM implementation. - internal function QuantumROMDiscretizationRoundCoefficients(coefficient: Double, oneNorm: Double, nCoefficients: Int, barHeight: Int) : Int { - return Round((AbsD(coefficient) / oneNorm) * IntAsDouble(nCoefficients) * IntAsDouble(barHeight)); - } - // Used in QuantumROM implementation. internal function RoundedDiscretizationCoefficients(coefficient: Double, oneNorm: Double, nCoefficients: Int, barHeight: Int) : Int { diff --git a/Standard/tests/QuantumROMTests.qs b/Standard/tests/QuantumROMTests.qs index fab33207d79..2de16c86543 100644 --- a/Standard/tests/QuantumROMTests.qs +++ b/Standard/tests/QuantumROMTests.qs @@ -89,7 +89,7 @@ namespace Microsoft.Quantum.Tests { } if (true) { // quantum ROM with sign - let purifiedState = PurifiedMixedStateWithSign(targetError, coefficients); + let purifiedState = PurifiedMixedStateAndSign(targetError, coefficients); using ((coeffRegister, signQubit, garbageQubits) = (Qubit[purifiedState::Requirements::NIndexQubits], Qubit(), Qubit[purifiedState::Requirements::NGarbageQubits])) { let coeffQubits = LittleEndian(coeffRegister); From 927cc7acdbf9ef55f9b763911aee367b193ea72f Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 12:05:54 +0200 Subject: [PATCH 17/20] Incoprate API changes. --- .../JordanWignerOptimizedBlockEncoding.qs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs b/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs index 08caf657bfb..2d54980197e 100644 --- a/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs +++ b/Chemistry/src/Runtime/JordanWigner/JordanWignerOptimizedBlockEncoding.qs @@ -370,14 +370,14 @@ namespace Microsoft.Quantum.Chemistry.JordanWigner { operation _JordanWignerOptimizedBlockEncodingStatePrep__ (targetError : Double, optimizedBEGeneratorSystem : OptimizedBEGeneratorSystem, qROMIdxRegister : LittleEndian, qROMGarbage : Qubit[], signQubit : Qubit, selectZControlRegisters : Qubit[], OptimizedBEControlRegisters : Qubit[], pauliBases : Qubit[], pauliBasesIdx : LittleEndian, indexRegisters : LittleEndian[]) : Unit { body (...) { - let (nTerms, oneNorm0, intToGenIdx) = optimizedBEGeneratorSystem!; + let (nTerms, _, _) = optimizedBEGeneratorSystem!; let coefficients = _OptimizedBEGeneratorSystemCoeff_(optimizedBEGeneratorSystem); - let (qROMQubitCount, oneNorm, qROMUnitary) = QuantumROM(targetError, coefficients); + let purifiedState = PurifiedMixedState(targetError, coefficients); let unitaryGenerator = (nTerms, _ToJordanWignerSelectInput_(_, optimizedBEGeneratorSystem)); let pauliBasesUnitaryGenerator = (5, _ToPauliBases_); //let multiplexer = MultiplexerFromGenerator; - qROMUnitary(qROMIdxRegister, qROMGarbage); + purifiedState::Prepare(qROMIdxRegister, new Qubit[0], qROMGarbage); MultiplexOperationsFromGenerator(unitaryGenerator, qROMIdxRegister, (signQubit, selectZControlRegisters, OptimizedBEControlRegisters, pauliBasesIdx, indexRegisters)); MultiplexOperationsFromGenerator(pauliBasesUnitaryGenerator, pauliBasesIdx, pauliBases); } @@ -389,8 +389,8 @@ namespace Microsoft.Quantum.Chemistry.JordanWigner { function _JordanWignerOptimizedBlockEncodingQubitManager_ (targetError : Double, nCoeffs : Int, nZ : Int, nMaj : Int, nIdxRegQubits : Int, ctrlRegister : Qubit[]) : ((LittleEndian, Qubit[], Qubit, Qubit[], Qubit[], Qubit[], LittleEndian, LittleEndian[]), (Qubit, Qubit[], Qubit[], Qubit[], LittleEndian[]), Qubit[]) { - let (_, (nIndexRegister, nGarbageQubits)) = QuantumROMQubitCount(targetError, nCoeffs, false); - let parts = Partitioned([nIndexRegister, nGarbageQubits], ctrlRegister); + let requirements = PurifiedMixedStateRequirements(targetError, nCoeffs); + let parts = Partitioned([requirements::NIndexQubits, requirements::NGarbageQubits], ctrlRegister); let ((qROMIdx, qROMGarbage), rest0) = ((LittleEndian(parts[0]), parts[1]), parts[2]); let ((signQubit, selectZControlRegisters, optimizedBEControlRegisters, pauliBases, indexRegisters, tmp), rest1) = _JordanWignerSelectQubitManager_(nZ, nMaj, nIdxRegQubits, rest0, new Qubit[0]); let registers = Partitioned([3], rest1); @@ -401,7 +401,7 @@ namespace Microsoft.Quantum.Chemistry.JordanWigner { function _JordanWignerOptimizedBlockEncodingQubitCount_ (targetError : Double, nCoeffs : Int, nZ : Int, nMaj : Int, nIdxRegQubits : Int, nTarget : Int) : ((Int, Int), (Int, Int, Int, Int, Int, Int, Int, Int[], Int)) { let (nSelectTotal, (a0, a1, a2, a3, a4)) = _JordanWignerSelectQubitCount_(nZ, nMaj, nIdxRegQubits); - let (nQROMTotal, (b0, b1)) = QuantumROMQubitCount(targetError, nCoeffs, false); + let (nQROMTotal, (b0, b1)) = (PurifiedMixedStateRequirements(targetError, nCoeffs))!; let pauliBasesIdx = 3; return (((nSelectTotal + nQROMTotal) + pauliBasesIdx, nTarget), (b0, b1, a0, a1, a2, a3, pauliBasesIdx, a4, nTarget)); } From 6ac7ddb38a80c631aef13df9077879d73b88ed65 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 12:06:01 +0200 Subject: [PATCH 18/20] Fix bug on Mac. --- .../tests/SamplesTests/DocsInvokingChemistryLibrary.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Chemistry/tests/SamplesTests/DocsInvokingChemistryLibrary.cs b/Chemistry/tests/SamplesTests/DocsInvokingChemistryLibrary.cs index 37a1b82a232..aa4f770c158 100644 --- a/Chemistry/tests/SamplesTests/DocsInvokingChemistryLibrary.cs +++ b/Chemistry/tests/SamplesTests/DocsInvokingChemistryLibrary.cs @@ -19,6 +19,7 @@ // The System namespace provides a number of useful built-in // types and methods that we'll use throughout this sample. using System; +using System.IO; // We use this for convnience functions for manipulation arrays. using System.Linq; @@ -84,7 +85,7 @@ static void LoadFromBroombridgeFile() var root = @"Molecules"; // This deserializes Broombridge. - var broombridge = Broombridge.Deserializers.DeserializeBroombridge($@"{root}\{filename}"); + var broombridge = Broombridge.Deserializers.DeserializeBroombridge(Path.Combine(root, filename)); // Note that the deserializer returns a list of `ProblemDescriptions` instances // as the file might describe multiple Hamiltonians. In this example, there is @@ -122,7 +123,7 @@ static void LoadFromLiquidFile() var root = @"Molecules"; // Deserialize the LiQuiD format. - var problem = LiQuiD.Deserialize($@"{root}\{filename}").First(); + var problem = LiQuiD.Deserialize(Path.Combine(root, filename)).First(); // This extracts the `OrbitalIntegralHamiltonian` from problem // description format. @@ -135,7 +136,7 @@ static void LoadFromLiquidFile() static void ResourceEstimate() { // Filename of Hamiltonian to be loaded. - var filename = "Molecules/LiH_0.2.yaml"; + var filename = Path.Combine("Molecules", "LiH_0.2.yaml"); // This deserializes Broombridge. var problem = Broombridge.Deserializers.DeserializeBroombridge(filename).ProblemDescriptions.First(); From 86a1c78e515f1aadcc4353f763ab4de6fb427c4d Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 12:06:16 +0200 Subject: [PATCH 19/20] Make public because of #239. --- Standard/src/Preparation/Arbitrary.qs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Standard/src/Preparation/Arbitrary.qs b/Standard/src/Preparation/Arbitrary.qs index 7d172c155b8..575d88eb528 100644 --- a/Standard/src/Preparation/Arbitrary.qs +++ b/Standard/src/Preparation/Arbitrary.qs @@ -212,7 +212,7 @@ namespace Microsoft.Quantum.Preparation { qubits : LittleEndian ) : Unit is Adj + Ctl { - (CompileApproximateArbitraryStatePreparation(tolerance, coefficients, Length(qubits!)))(qubits); + (_CompileApproximateArbitraryStatePreparation(tolerance, coefficients, Length(qubits!)))(qubits); } /// # Summary @@ -228,7 +228,7 @@ namespace Microsoft.Quantum.Preparation { // NB: This is currently not marked as internal, as the QML library // currently uses this function. Please see the relevant GitHub issue // at https://github.com/microsoft/QuantumLibraries/issues/239. - internal function CompileApproximateArbitraryStatePreparation( + function _CompileApproximateArbitraryStatePreparation( tolerance : Double, coefficients : ComplexPolar[], nQubits : Int From 2e5eab7ff353033e0d3e9de4f88441371f331ad0 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 7 Sep 2020 21:34:00 +0200 Subject: [PATCH 20/20] Add deprecated functions. --- Standard/src/Preparation/Deprecated.qs | 95 ++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Standard/src/Preparation/Deprecated.qs diff --git a/Standard/src/Preparation/Deprecated.qs b/Standard/src/Preparation/Deprecated.qs new file mode 100644 index 00000000000..4fdb28a5d35 --- /dev/null +++ b/Standard/src/Preparation/Deprecated.qs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. + // Licensed under the MIT License. + +namespace Microsoft.Quantum.Preparation { + + open Microsoft.Quantum.Arithmetic; + + /// # Summary + /// Uses the Quantum ROM technique to represent a given density matrix. + /// + /// Given a list of $N$ coefficients $\alpha_j$, this returns a unitary $U$ that uses the Quantum-ROM + /// technique to prepare + /// an approximation $\tilde\rho\sum_{j=0}^{N-1}p_j\ket{j}\bra{j}$ of the purification of the density matrix + /// $\rho=\sum_{j=0}^{N-1}\frac{|alpha_j|}{\sum_k |\alpha_k|}\ket{j}\bra{j}$. In this approximation, the + /// error $\epsilon$ is such that $|p_j-\frac{|alpha_j|}{\sum_k |\alpha_k|}|\le \epsilon / N$ and + /// $\|\tilde\rho - \rho\| \le \epsilon$. In other words, + /// $$ + /// \begin{align} + /// U\ket{0}^{\lceil\log_2 N\rceil}\ket{0}^{m}=\sum_{j=0}^{N-1}\sqrt{p_j} \ket{j}\ket{\text{garbage}_j}. + /// \end{align} + /// $$ + /// + /// # Input + /// ## targetError + /// The target error $\epsilon$. + /// ## coefficients + /// Array of $N$ coefficients specifying the probability of basis states. + /// Negative numbers $-\alpha_j$ will be treated as positive $|\alpha_j|$. + /// + /// # Output + /// ## First parameter + /// A tuple `(x,(y,z))` where `x = y + z` is the total number of qubits allocated, + /// `y` is the number of qubits for the `LittleEndian` register, and `z` is the Number + /// of garbage qubits. + /// ## Second parameter + /// The one-norm $\sum_j |\alpha_j|$ of the coefficient array. + /// ## Third parameter + /// The unitary $U$. + /// + /// # Remarks + /// ## Example + /// The following code snippet prepares an purification of the $3$-qubit state + /// $\rho=\sum_{j=0}^{4}\frac{|alpha_j|}{\sum_k |\alpha_k|}\ket{j}\bra{j}$, where + /// $\vec\alpha=(1.0,2.0,3.0,4.0,5.0)$, and the error is `1e-3`; + /// ```qsharp + /// let coefficients = [1.0,2.0,3.0,4.0,5.0]; + /// let targetError = 1e-3; + /// let ((nTotalQubits, (nIndexQubits, nGarbageQubits)), oneNorm, op) = QuantumROM(targetError, coefficients); + /// using (indexRegister = Qubit[nIndexQubits]) { + /// using (garbageRegister = Qubit[nGarbageQubits]) { + /// op(LittleEndian(indexRegister), garbageRegister); + /// } + /// } + /// ``` + /// + /// # References + /// - Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity + /// Ryan Babbush, Craig Gidney, Dominic W. Berry, Nathan Wiebe, Jarrod McClean, Alexandru Paler, Austin Fowler, Hartmut Neven + /// https://arxiv.org/abs/1805.03662 + @Deprecated("Microsoft.Quantum.Preparation.PurifiedMixedState") + function QuantumROM(targetError: Double, coefficients: Double[]) + : ((Int, (Int, Int)), Double, ((LittleEndian, Qubit[]) => Unit is Adj + Ctl)) { + let preparation = PurifiedMixedState(targetError, coefficients); + return ( + preparation::Requirements!, + preparation::Norm, + IgnoreDataRegister(preparation::Prepare, _, _) + ); + } + + internal operation IgnoreDataRegister(op : ((LittleEndian, Qubit[], Qubit[]) => Unit is Adj + Ctl), indexRegister : LittleEndian, garbageRegister : Qubit[]) : Unit is Adj + Ctl { + op(indexRegister, new Qubit[0], garbageRegister); + } + + /// # Summary + /// Returns the total number of qubits that must be allocated + /// to the operation returned by `QuantumROM`. + /// + /// # Input + /// ## targetError + /// The target error $\epsilon$. + /// ## nCoeffs + /// Number of coefficients specified in `QuantumROM`. + /// + /// # Output + /// ## First parameter + /// A tuple `(x,(y,z))` where `x = y + z` is the total number of qubits allocated, + /// `y` is the number of qubits for the `LittleEndian` register, and `z` is the Number + /// of garbage qubits. + @Deprecated("Microsoft.Quantum.Preparation.PurifiedMixedStateRequirements") + function QuantumROMQubitCount(targetError: Double, nCoeffs: Int) + : (Int, (Int, Int)) { + return (PurifiedMixedStateRequirements(targetError, nCoeffs))!; + } +}