Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
25 changes: 25 additions & 0 deletions Standard/src/Canon/Range.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Canon {

/// # Summary
/// Returns true if and only if input range is empty.
///
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be pretty basic in this case, but could we add an /// # Input and /// # Output block here?

/// # Input
/// ## rng
/// Any range
///
/// # Output
/// True, if and only if `rng` is empty
///
/// # Remark
/// This function needs to check at most one range index
/// to determine whether the range is empty.
function IsRangeEmpty(rng : Range) : Bool {
for (idx in rng) {
return false;
}
return true;
}
}
40 changes: 12 additions & 28 deletions Standard/src/Preparation/Arbitrary.qs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Preparation {
Expand Down Expand Up @@ -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, _);
}

Expand Down Expand Up @@ -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);
Expand All @@ -265,14 +259,6 @@ namespace Microsoft.Quantum.Preparation {
ApproximatelyMultiplexPauli(tolerance, disentangling, axis, actualControl, register[idxTarget]);
}

internal function RangeLength(rng : Range) : Int {
mutable len = 0;
for (idx in rng) {
set len += 1;
}
return len;
}

internal operation ApplyGlobalRotationStep(
angle : Double, idxTarget : Int, register : Qubit[]
) : Unit is Adj + Ctl {
Expand All @@ -285,15 +271,15 @@ namespace Microsoft.Quantum.Preparation {
/// # See Also
/// - PrepareArbitraryState
/// - Microsoft.Quantum.Canon.MultiplexPauli
function _ApproximatelyUnprepareArbitraryStatePlan(
internal function ApproximatelyUnprepareArbitraryStatePlan(
tolerance : Double, coefficients : ComplexPolar[],
(rngControl : Range, idxTarget : Int)
)
: (Qubit[] => Unit is Adj + Ctl)[] {
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), _)];
}
Expand All @@ -304,17 +290,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;
Expand Down Expand Up @@ -352,7 +336,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];
Expand Down
29 changes: 12 additions & 17 deletions Standard/src/Preparation/Mixed.qs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Preparation {
Expand All @@ -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}
Expand Down Expand Up @@ -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
Expand All @@ -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 <xref:microsoft.quantum.preparation.preparesinglequbitidentity>).
Expand All @@ -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);
}
Expand Down
55 changes: 23 additions & 32 deletions Standard/src/Preparation/QuantumROM.qs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Quantum.Preparation {
Expand Down Expand Up @@ -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);
Expand All @@ -87,49 +88,43 @@ 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;
let nTotal = nGarbageQubits + nBitsIndices;
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[])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would also be good to use internal function here for consistency

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this function is tested in a separate test project, so it cannot be internal (for now). So I leave the _ such that it does not end up in docs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make a qsharp-compiler feature request for internalsvisibleto?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this case would it be an idea to create the internal function and make _QuantumROMDiscretization a wrapper to it with a deprecation note, and create a task to refactor said test project?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave this for now until we have a solution. I agree with @cgrande to open a feature request for this. An alternative solution would be to have a non-internal function with an attribute such as @UsedForTesting indicating that no docs should be generated.

: (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
mutable bars = 0;
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) {
Expand All @@ -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 {
Expand All @@ -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));
}

Expand All @@ -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];
Expand All @@ -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, _, _);
}

Expand Down
Loading