From ee11790d63ae75a1e42139f84eee9202531e39b6 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 27 Jun 2022 08:08:43 +0200 Subject: [PATCH 1/3] Resource estimation tests. --- .../tests/Canon/Utils/MultiplexerTests.cs | 32 +++++++++++++++++++ Standard/tests/MultiplexerTests.qs | 11 +++++++ 2 files changed, 43 insertions(+) create mode 100644 Standard/tests/Canon/Utils/MultiplexerTests.cs diff --git a/Standard/tests/Canon/Utils/MultiplexerTests.cs b/Standard/tests/Canon/Utils/MultiplexerTests.cs new file mode 100644 index 00000000000..4b3d9d7cdca --- /dev/null +++ b/Standard/tests/Canon/Utils/MultiplexerTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + + +using Microsoft.Quantum.Simulation.Simulators; +using Xunit; + +namespace Microsoft.Quantum.Tests +{ + public class MultiplexerTests + { + [Fact] + public void TestMultiplexerResources() + { + foreach (var (numControls, expectedTCount, expectedWidth) in new [] { + (1, 0, 6), + (2, 28, 8), + (3, 84, 10), + (4, 196, 12), + (5, 420, 14), + (6, 868, 16) + }) { + var estimator = new ResourcesEstimator(); + EstimateMultiplexOperationsCosts.Run(estimator, numControls, 1 << numControls).Wait(); + var tcount = (double)estimator.Data.Rows.Find("T")[1]; + var width = (double)estimator.Data.Rows.Find("Width")[1]; + Assert.Equal(expectedTCount, tcount); + Assert.Equal(expectedWidth, width); + } + } + } +} diff --git a/Standard/tests/MultiplexerTests.qs b/Standard/tests/MultiplexerTests.qs index acb5df2d7d6..692837fa02a 100644 --- a/Standard/tests/MultiplexerTests.qs +++ b/Standard/tests/MultiplexerTests.qs @@ -497,6 +497,17 @@ namespace Microsoft.Quantum.Tests { } } + internal operation EstimateMultiplexOperationsCosts(numControls : Int, numData : Int) : Unit { + Diag.Fact(numData <= 2^numControls, "Too few address bits"); + + use address = Qubit[numControls]; + use target = Qubit[5]; + + let unitaries = [ApplyPauliFromBitString(PauliX, true, [true, size = 5], _), size = numData]; + + MultiplexOperations(unitaries, LittleEndian(address), target); + } + } From 59ee43700bed24f7bb0e650658274fb20b4d6799 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 27 Jun 2022 08:18:24 +0200 Subject: [PATCH 2/3] Improved implementation. --- Standard/src/Canon/Utils/Multiplexer.qs | 145 ++++++++++-------- .../tests/Canon/Utils/MultiplexerTests.cs | 10 +- 2 files changed, 82 insertions(+), 73 deletions(-) diff --git a/Standard/src/Canon/Utils/Multiplexer.qs b/Standard/src/Canon/Utils/Multiplexer.qs index 7d1a8366da2..c7370561eb3 100644 --- a/Standard/src/Canon/Utils/Multiplexer.qs +++ b/Standard/src/Canon/Utils/Multiplexer.qs @@ -3,8 +3,10 @@ namespace Microsoft.Quantum.Canon { open Microsoft.Quantum.Arithmetic; - open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Math; /// # Summary @@ -389,88 +391,95 @@ namespace Microsoft.Quantum.Canon { /// ## target /// Generic qubit register that $V_j$ acts on. /// - /// # Remarks - /// `coefficients` will be padded with identity elements if - /// fewer than $2^n$ are specified. This implementation uses - /// $n - 1$ auxiliary qubits. - /// /// # References - /// - Toward the first quantum simulation with quantum speedup - /// Andrew M. Childs, Dmitri Maslov, Yunseong Nam, Neil J. Ross, Yuan Su - /// https://arxiv.org/abs/1711.10980 + /// - 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 operation MultiplexOperations<'T> (unitaries : ('T => Unit is Adj + Ctl)[], index : LittleEndian, target : 'T) : Unit is Adj + Ctl { - if (Length(index!) == 0) { - fail $"MultiplexOperations failed. Number of index qubits must be greater than 0."; - } + body (...) { + let (N, n) = DimensionsForMultiplexer(Length(unitaries), index); + + if N == 1 { // base case + Head(unitaries)(target); + } else { + let (most, tail) = MostAndTail(index![...n - 1]); + let parts = Partitioned([2^(n - 1)], unitaries); + + within { + X(tail); + } apply { + Controlled MultiplexOperations([tail], (parts[0], LittleEndian(most), target)); + } - if (Length(unitaries) > 0) { - let auxillaryRegister = []; - MultiplexOperationsWithAuxRegister(unitaries, auxillaryRegister, index, target); + Controlled MultiplexOperations([tail], (parts[1], LittleEndian(most), target)); + } } - } - /// # Summary - /// Implementation step of MultiplexOperations. - /// # See Also - /// - Microsoft.Quantum.Canon.MultiplexOperations - internal operation MultiplexOperationsWithAuxRegister<'T>( - unitaries : ('T => Unit is Adj + Ctl)[], - auxillaryRegister : Qubit[], - index : LittleEndian, - target : 'T - ) - : Unit is Adj + Ctl { - body (...) { - let nIndex = Length(index!); - let nStates = 2 ^ nIndex; - let nUnitaries = Length(unitaries); - let nUnitariesRight = MinI(nUnitaries, nStates / 2); - let nUnitariesLeft = MinI(nUnitaries, nStates); - let rightUnitaries = unitaries[0 .. nUnitariesRight - 1]; - let leftUnitaries = unitaries[nUnitariesRight .. nUnitariesLeft - 1]; - let newControls = LittleEndian((index!)[0 .. nIndex - 2]); - - if (nUnitaries > 0) { - if (Length(auxillaryRegister) == 1 and nIndex == 0) { - // Termination case - Controlled unitaries[0](auxillaryRegister, target); - } elif (Length(auxillaryRegister) == 0 and nIndex >= 1) { - // Start case - let newAuxQubit = Tail(index!); - - if (nUnitariesLeft > 0) { - MultiplexOperationsWithAuxRegister(leftUnitaries, [newAuxQubit], newControls, target); - } + controlled (ctls, ...) { + let nCtls = Length(ctls); - within { - X(newAuxQubit); - } apply { - MultiplexOperationsWithAuxRegister(rightUnitaries, [newAuxQubit], newControls, target); - } + if nCtls == 0 { + MultiplexOperations(unitaries, index, target); + } elif nCtls == 1 { + let (N, n) = DimensionsForMultiplexer(Length(unitaries), index); + + let ctl = Head(ctls); + + if N == 1 { // base case + Controlled (Head(unitaries))(ctls, target); } else { - // Recursion that reduces nIndex by 1 & sets Length(auxillaryRegister) to 1. - use newAuxQubit = Qubit(); + use helper = Qubit(); + + let (most, tail) = MostAndTail(index![...n - 1]); + let parts = Partitioned([2^(n - 1)], unitaries); + within { - Controlled X(auxillaryRegister + [(index!)[Length(index!) - 1]], newAuxQubit); + X(tail); } apply { - if (nUnitariesLeft > 0) { - MultiplexOperationsWithAuxRegister(leftUnitaries, [newAuxQubit], newControls, target); - } - - within { - Controlled X(auxillaryRegister, newAuxQubit); - } apply { - MultiplexOperationsWithAuxRegister(rightUnitaries, [newAuxQubit], newControls, target); - } + ApplyAnd(ctl, tail, helper); } + + Controlled MultiplexOperations([helper], (parts[0], LittleEndian(most), target)); + + CNOT(ctl, helper); + + Controlled MultiplexOperations([helper], (parts[1], LittleEndian(most), target)); + + Adjoint ApplyAnd(ctl, tail, helper); + } + } else { + use helper = Qubit(); + within { + Controlled X(ctls, helper); + } apply { + Controlled MultiplexOperations([helper], (unitaries, index, target)); } } } + } - controlled (controlRegister, ...) { - MultiplexOperationsWithAuxRegister(unitaries, controlRegister, index, target); - } + /// # Summary + /// Validates and adjusts dimensions for address register + /// + /// # Description + /// Given $N$ unitaries in `numUnitaries` and an address register of length $n'$, + /// this function first checks whether $N \neq 0$ and $\lceil\log_2 N\rceil = n \le n'$, + /// and then returns the tuple $(N, n)$. + /// + /// # Input + /// ## numUnitaries + /// The number of unitaries to multiplex. + /// ## address + /// The address register. + internal function DimensionsForMultiplexer(numUnitaries : Int, address : LittleEndian) : (Int, Int) { + let N = numUnitaries; + Fact(N > 0, "data cannot be empty"); + + let n = Ceiling(Lg(IntAsDouble(N))); + Fact(Length(address!) >= n, $"address register is too small, requires at least {n} qubits"); + + return (N, n); } } diff --git a/Standard/tests/Canon/Utils/MultiplexerTests.cs b/Standard/tests/Canon/Utils/MultiplexerTests.cs index 4b3d9d7cdca..05338c1acf7 100644 --- a/Standard/tests/Canon/Utils/MultiplexerTests.cs +++ b/Standard/tests/Canon/Utils/MultiplexerTests.cs @@ -14,11 +14,11 @@ public void TestMultiplexerResources() { foreach (var (numControls, expectedTCount, expectedWidth) in new [] { (1, 0, 6), - (2, 28, 8), - (3, 84, 10), - (4, 196, 12), - (5, 420, 14), - (6, 868, 16) + (2, 8, 8), + (3, 24, 10), + (4, 56, 12), + (5, 120, 14), + (6, 248, 16) }) { var estimator = new ResourcesEstimator(); EstimateMultiplexOperationsCosts.Run(estimator, numControls, 1 << numControls).Wait(); From 33e154b4ffc50de79a80685f00c17b9a5b5c650d Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Mon, 27 Jun 2022 08:43:50 +0200 Subject: [PATCH 3/3] Ergonomic changes. --- Standard/src/Canon/Utils/Multiplexer.qs | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Standard/src/Canon/Utils/Multiplexer.qs b/Standard/src/Canon/Utils/Multiplexer.qs index c7370561eb3..83e4ea97ea5 100644 --- a/Standard/src/Canon/Utils/Multiplexer.qs +++ b/Standard/src/Canon/Utils/Multiplexer.qs @@ -94,16 +94,16 @@ namespace Microsoft.Quantum.Canon { /// - Microsoft.Quantum.Canon.MultiplexPauli operation ApproximatelyMultiplexPauli(tolerance : Double, coefficients : Double[], pauli : Pauli, control : LittleEndian, target : Qubit) : Unit is Adj + Ctl { - if (pauli == PauliZ) { + if pauli == PauliZ { let op = ApproximatelyMultiplexZ(tolerance, coefficients, control, _); op(target); - } elif (pauli == PauliX) { + } elif pauli == PauliX { let op = ApproximatelyMultiplexPauli(tolerance, coefficients, PauliZ, control, _); ApplyWithCA(H, op, target); - } elif (pauli == PauliY) { + } elif pauli == PauliY { let op = ApproximatelyMultiplexPauli(tolerance, coefficients, PauliX, control, _); ApplyWithCA(Adjoint S, op, target); - } elif (pauli == PauliI) { + } elif pauli == PauliI { ApproximatelyApplyDiagonalUnitary(tolerance, coefficients, control); } else { fail $"MultiplexPauli failed. Invalid pauli {pauli}."; @@ -157,7 +157,7 @@ namespace Microsoft.Quantum.Canon { // NB: We don't currently use Any / Mapped for this, as we want to be // able to short-circuit. for coefficient in coefficients { - if (AbsD(coefficient) >= tolerance) { + if AbsD(coefficient) >= tolerance { return true; } } @@ -166,7 +166,7 @@ namespace Microsoft.Quantum.Canon { internal function AnyOutsideToleranceCP(tolerance : Double, coefficients : ComplexPolar[]) : Bool { for coefficient in coefficients { - if (AbsComplexPolar(coefficient) > tolerance) { + if AbsComplexPolar(coefficient) > tolerance { return true; } } @@ -220,20 +220,20 @@ namespace Microsoft.Quantum.Canon { // pad coefficients length at tail to a power of 2. let coefficientsPadded = Padded(-2 ^ Length(control!), 0.0, coefficients); - if (Length(coefficientsPadded) == 1) { + if Length(coefficientsPadded) == 1 { // Termination case - if (AbsD(coefficientsPadded[0]) > tolerance) { + if AbsD(coefficientsPadded[0]) > tolerance { Exp([PauliZ], coefficientsPadded[0], [target]); } } else { // Compute new coefficients. let (coefficients0, coefficients1) = MultiplexZCoefficients(coefficientsPadded); - ApproximatelyMultiplexZ(tolerance,coefficients0, LittleEndian((control!)[0 .. Length(control!) - 2]), target); - if (AnyOutsideToleranceD(tolerance, coefficients1)) { + ApproximatelyMultiplexZ(tolerance, coefficients0, LittleEndian(Most(control!)), target); + if AnyOutsideToleranceD(tolerance, coefficients1) { within { - CNOT((control!)[Length(control!) - 1], target); + CNOT(Tail(control!), target); } apply { - ApproximatelyMultiplexZ(tolerance,coefficients1, LittleEndian((control!)[0 .. Length(control!) - 2]), target); + ApproximatelyMultiplexZ(tolerance,coefficients1, LittleEndian(Most(control!)), target); } } } @@ -244,7 +244,7 @@ namespace Microsoft.Quantum.Canon { let coefficientsPadded = Padded(2 ^ (Length(control!) + 1), 0.0, Padded(-2 ^ Length(control!), 0.0, coefficients)); let (coefficients0, coefficients1) = MultiplexZCoefficients(coefficientsPadded); ApproximatelyMultiplexZ(tolerance,coefficients0, control, target); - if (AnyOutsideToleranceD(tolerance,coefficients1)) { + if AnyOutsideToleranceD(tolerance,coefficients1) { within { Controlled X(controlRegister, target); } apply { @@ -333,7 +333,7 @@ namespace Microsoft.Quantum.Canon { /// - Microsoft.Quantum.Canon.ApplyDiagonalUnitary operation ApproximatelyApplyDiagonalUnitary(tolerance : Double, coefficients : Double[], qubits : LittleEndian) : Unit is Adj + Ctl { - if (IsEmpty(qubits!)) { + if IsEmpty(qubits!) { fail "operation ApplyDiagonalUnitary -- Number of qubits must be greater than 0."; } @@ -342,11 +342,11 @@ namespace Microsoft.Quantum.Canon { // Compute new coefficients. let (coefficients0, coefficients1) = MultiplexZCoefficients(coefficientsPadded); - ApproximatelyMultiplexZ(tolerance,coefficients1, LittleEndian((qubits!)[0 .. Length(qubits!) - 2]), (qubits!)[Length(qubits!) - 1]); + ApproximatelyMultiplexZ(tolerance,coefficients1, LittleEndian(Most(qubits!)), Tail(qubits!)); - if (Length(coefficientsPadded) == 2) { + if Length(coefficientsPadded) == 2 { // Termination case - if (AbsD(coefficients0[0]) > tolerance) { + if AbsD(coefficients0[0]) > tolerance { Exp([PauliI], 1.0 * coefficients0[0], qubits!); } } else { @@ -395,7 +395,7 @@ namespace Microsoft.Quantum.Canon { /// - 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 - operation MultiplexOperations<'T> (unitaries : ('T => Unit is Adj + Ctl)[], index : LittleEndian, target : 'T) + operation MultiplexOperations<'T>(unitaries : ('T => Unit is Adj + Ctl)[], index : LittleEndian, target : 'T) : Unit is Adj + Ctl { body (...) { let (N, n) = DimensionsForMultiplexer(Length(unitaries), index);