Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 95 additions & 86 deletions Standard/src/Canon/Utils/Multiplexer.qs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -92,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}.";
Expand Down Expand Up @@ -155,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;
}
}
Expand All @@ -164,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;
}
}
Expand Down Expand Up @@ -218,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);
}
}
}
Expand All @@ -242,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 {
Expand Down Expand Up @@ -331,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.";
}

Expand All @@ -340,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 {
Expand Down Expand Up @@ -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
operation MultiplexOperations<'T> (unitaries : ('T => Unit is Adj + Ctl)[], index : LittleEndian, target : 'T)
/// - 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);
}

}
32 changes: 32 additions & 0 deletions Standard/tests/Canon/Utils/MultiplexerTests.cs
Original file line number Diff line number Diff line change
@@ -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, 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();
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);
}
}
}
}
11 changes: 11 additions & 0 deletions Standard/tests/MultiplexerTests.qs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}