diff --git a/MachineLearning/src/Runtime/InputEncoding.qs b/MachineLearning/src/Runtime/InputEncoding.qs index cf243f2a3c2..201b1d5e3be 100644 --- a/MachineLearning/src/Runtime/InputEncoding.qs +++ b/MachineLearning/src/Runtime/InputEncoding.qs @@ -2,113 +2,113 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.MachineLearning { + open Microsoft.Quantum.Preparation; open Microsoft.Quantum.Convert; open Microsoft.Quantum.Math; open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - function _CanApplyTwoQubitCase(datum: Double[]) : Bool { - return((Length(datum)==4) and (Microsoft.Quantum.Math.AbsD(datum[0]*datum[3]-datum[1]*datum[2])< 1E-12) and (Microsoft.Quantum.Math.AbsD(datum[0])> 1E-4)); - } + function _CanApplyTwoQubitCase(datum: Double[]) : Bool { + return((Length(datum)==4) and (Microsoft.Quantum.Math.AbsD(datum[0]*datum[3]-datum[1]*datum[2])< 1E-12) and (Microsoft.Quantum.Math.AbsD(datum[0])> 1E-4)); + } - operation _ApplyTwoQubitCase(datum: Double[], reg: LittleEndian) : Unit is Adj + Ctl { - let x = datum[1]/datum[0]; - let y = datum[2]/datum[0]; - // we now encoding [1,x,y,x*y] - let ax = 2.0 * ArcTan(x); - let ay = 2.0 * ArcTan(y); - R(PauliY, ay, (reg!)[1]); - R(PauliY, ax, (reg!)[0]); - } + operation _ApplyTwoQubitCase(datum: Double[], reg: LittleEndian) : Unit is Adj + Ctl { + let x = datum[1]/datum[0]; + let y = datum[2]/datum[0]; + // we now encoding [1,x,y,x*y] + let ax = 2.0 * ArcTan(x); + let ay = 2.0 * ArcTan(y); + R(PauliY, ay, (reg!)[1]); + R(PauliY, ax, (reg!)[0]); + } - function _Unnegate(negLocs: Int[], coefficients : ComplexPolar[]) : ComplexPolar[] { - mutable ret = coefficients; - for (idxNegative in negLocs) { - if (idxNegative >= Length(coefficients)) { - fail $"Cannot set the phase at index {idxNegative}, only {Length(coefficients)} coefficients were provided."; - } - let coefficient = coefficients[idxNegative]; - set ret w/= idxNegative <- ComplexPolar(coefficient::Magnitude, 0.0); - } - return ret; - } + function _Unnegate(negLocs: Int[], coefficients : ComplexPolar[]) : ComplexPolar[] { + mutable ret = coefficients; + for (idxNegative in negLocs) { + if (idxNegative >= Length(coefficients)) { + fail $"Cannot set the phase at index {idxNegative}, only {Length(coefficients)} coefficients were provided."; + } + let coefficient = coefficients[idxNegative]; + set ret w/= idxNegative <- ComplexPolar(coefficient::Magnitude, 0.0); + } + return ret; + } - /// Do special processing on the first cNegative entries - operation _EncodeSparseNegativeInput(cNegative: Int, tolerance: Double,coefficients : ComplexPolar[], reg: LittleEndian): Unit is Adj + Ctl - { - let negLocs = collectNegativeLocs(cNegative, coefficients); - // Prepare the state disregarding the sign of negative components. - NoisyPrepareArbitraryState(tolerance, _Unnegate(negLocs, coefficients), reg); - // Reflect about the negative coefficients to apply the negative signs - // at the end. - for (ineg in 0..(cNegative - 1)) { - let jx = negLocs[ineg]; - if (jx > -1) { - ReflectAboutInteger(jx, reg); //TODO:REVIEW: this assumes that 2^Length(reg) is the minimal pad to Length(coefficients) - } - } - } + /// Do special processing on the first cNegative entries + operation _EncodeSparseNegativeInput(cNegative: Int, tolerance: Double,coefficients : ComplexPolar[], reg: LittleEndian): Unit is Adj + Ctl + { + let negLocs = collectNegativeLocs(cNegative, coefficients); + // Prepare the state disregarding the sign of negative components. + ApproximatelyPrepareArbitraryState(tolerance, _Unnegate(negLocs, coefficients), reg); + // Reflect about the negative coefficients to apply the negative signs + // at the end. + for (ineg in 0..(cNegative - 1)) { + let jx = negLocs[ineg]; + if (jx > -1) { + ReflectAboutInteger(jx, reg); //TODO:REVIEW: this assumes that 2^Length(reg) is the minimal pad to Length(coefficients) + } + } + } - function NoisyInputEncoder(tolerance: Double,coefficients : Double[]) : (LittleEndian => Unit is Adj + Ctl) { - //First quantize the coefficients: for a coef x find such y*tolerance, where y is integer and |x-y*tolerance| \neq tolerance/2 - let nCoefficients = Length(coefficients); + function NoisyInputEncoder(tolerance: Double,coefficients : Double[]) : (LittleEndian => Unit is Adj + Ctl) { + //First quantize the coefficients: for a coef x find such y*tolerance, where y is integer and |x-y*tolerance| \neq tolerance/2 + let nCoefficients = Length(coefficients); mutable coefficientsComplexPolar = new ComplexPolar[nCoefficients]; mutable cNegative = 0; for (idx in 0 .. nCoefficients - 1) { - mutable coef = coefficients[idx]; - if (tolerance > 1E-9) { - set coef = tolerance * IntAsDouble(Round(coefficients[idx] / tolerance)); //quantization - } - mutable ang = 0.0; - if (coef < 0.0) { - set cNegative += 1; - set coef = -coef; - set ang = PI(); - } + mutable coef = coefficients[idx]; + if (tolerance > 1E-9) { + set coef = tolerance * IntAsDouble(Round(coefficients[idx] / tolerance)); //quantization + } + mutable ang = 0.0; + if (coef < 0.0) { + set cNegative += 1; + set coef = -coef; + set ang = PI(); + } set coefficientsComplexPolar w/= idx <- ComplexPolar(coef, ang); } - // Check if we can apply the explicit two-qubit case. + // Check if we can apply the explicit two-qubit case. if (_CanApplyTwoQubitCase(coefficients)) { - return _ApplyTwoQubitCase(coefficients, _); - } - // If not, we may be able to use a special protocol in the case that - // there are only a few negative coefficients. - // Here, by a "few," we mean fewer than the number of qubits required - // to encode features. - if ((cNegative > 0) and (IntAsDouble(cNegative) < Lg(IntAsDouble(Length(coefficients))) + 1.0)) { - return _EncodeSparseNegativeInput(cNegative, tolerance, coefficientsComplexPolar, _); //TODO:MORE:ACCEPTANCE ("Wines" passing soi far) - } - - // Finally, we fall back to arbitrary state preparation. - return NoisyPrepareArbitraryState(tolerance, coefficientsComplexPolar, _); - } //EncodeNoisyInput + return _ApplyTwoQubitCase(coefficients, _); + } + // If not, we may be able to use a special protocol in the case that + // there are only a few negative coefficients. + // Here, by a "few," we mean fewer than the number of qubits required + // to encode features. + if ((cNegative > 0) and (IntAsDouble(cNegative) < Lg(IntAsDouble(Length(coefficients))) + 1.0)) { + return _EncodeSparseNegativeInput(cNegative, tolerance, coefficientsComplexPolar, _); //TODO:MORE:ACCEPTANCE ("Wines" passing soi far) + } + + // Finally, we fall back to arbitrary state preparation. + return ApproximatelyPrepareArbitraryState(tolerance, coefficientsComplexPolar, _); + } //EncodeNoisyInput - //TODO:REVIEW: Design consideration! The implicit qubit count must be read off from the state encoder, NOT from the gate sequence! + //TODO:REVIEW: Design consideration! The implicit qubit count must be read off from the state encoder, NOT from the gate sequence! - /// Create amplitude encoding of an array of real-valued coefficients - /// The vector of 'coefficients' does not have to be unitary - function InputEncoder(coefficients : Double[]): (LittleEndian => Unit is Adj + Ctl) { - //default implementation, does not respect sparcity - let nCoefficients = Length(coefficients); + /// Create amplitude encoding of an array of real-valued coefficients + /// The vector of 'coefficients' does not have to be unitary + function InputEncoder(coefficients : Double[]): (LittleEndian => Unit is Adj + Ctl) { + //default implementation, does not respect sparcity + let nCoefficients = Length(coefficients); mutable coefficientsComplexPolar = new ComplexPolar[nCoefficients]; mutable allPositive = true; for (idx in 0 .. nCoefficients - 1) { - mutable coef = coefficients[idx]; - mutable ang = 0.0; - if (coef < 0.0) - { - set allPositive = false; - set coef = -coef; - set ang =Microsoft.Quantum.Math.PI(); - } + mutable coef = coefficients[idx]; + mutable ang = 0.0; + if (coef < 0.0) { + set allPositive = false; + set coef = -coef; + set ang = PI(); + } set coefficientsComplexPolar w/= idx<-ComplexPolar(coef,ang); } if (_CanApplyTwoQubitCase(coefficients)) { - return _ApplyTwoQubitCase(coefficients,_); - } - return NoisyPrepareArbitraryState(1E-12, coefficientsComplexPolar, _); //this is preparing the state almost exactly so far - } + return _ApplyTwoQubitCase(coefficients, _); + } + return ApproximatelyPrepareArbitraryState(1E-12, coefficientsComplexPolar, _); //this is preparing the state almost exactly so far + } } \ No newline at end of file diff --git a/MachineLearning/src/Runtime/SpecialMultiplexor.qs b/MachineLearning/src/Runtime/SpecialMultiplexor.qs deleted file mode 100644 index a95ea0f58ec..00000000000 --- a/MachineLearning/src/Runtime/SpecialMultiplexor.qs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Canon { - open Microsoft.Quantum.Arithmetic; - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Arrays; - open Microsoft.Quantum.Math; - - /// # Summary - /// Applies a Pauli rotation conditioned on an array of qubits. - /// - /// This applies the multiply-controlled unitary operation $U$ that performs - /// rotations by angle $\theta_j$ about single-qubit Pauli operator $P$ - /// when controlled by the $n$-qubit number state $\ket{j}$. - /// - /// $U = \sum^{2^n-1}_{j=0}\ket{j}\bra{j}\otimes e^{i P \theta_j}$. - /// - /// # Input - /// ## tolerance - /// Coefficients under this tolerance level should be ignored - /// ## coefficients - /// Array of up to $2^n$ coefficients $\theta_j$. The $j$th coefficient - /// indexes the number state $\ket{j}$ encoded in little-endian format. - /// - /// ## pauli - /// Pauli operator $P$ that determines axis of rotation. - /// - /// ## control - /// $n$-qubit control register that encodes number states $\ket{j}$ in - /// little-endian format. - /// - /// ## target - /// Single qubit register that is rotated by $e^{i P \theta_j}$. - /// - /// # Remarks - /// `coefficients` will be padded with elements $\theta_j = 0.0$ if - /// fewer than $2^n$ are specified. - operation NoisyMultiplexPauli (tolerance: Double,coefficients : Double[], pauli : Pauli, control : LittleEndian, target : Qubit) : Unit - { - body (...) - { - if (pauli == PauliZ) - { - let op = NoisyMultiplexZ(tolerance, coefficients, control, _); - op(target); - } - elif (pauli == PauliX) - { - let op = NoisyMultiplexPauli(tolerance,coefficients, PauliZ, control, _); - ApplyWithCA(H, op, target); - } - elif (pauli == PauliY) - { - let op = NoisyMultiplexPauli(tolerance,coefficients, PauliX, control, _); - ApplyWithCA(Adjoint S, op, target); - } - elif (pauli == PauliI) - { - NoisyApplyDiagonalUnitary(tolerance,coefficients, control); - } - else - { - fail $"MultiplexPauli failed. Invalid pauli {pauli}."; - } - } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; - } - - - function significantReal(tol: Double, rg:Double[]):Bool - { - for(j in 0..(Length(rg)-1)) - { - if (AbsD(rg[j])>tol) - { - return true; - } - } - return false; - } - - /// # Summary - /// Applies a Pauli Z rotation conditioned on an array of qubits. - /// - /// This applies the multiply-controlled unitary operation $U$ that performs - /// rotations by angle $\theta_j$ about single-qubit Pauli operator $Z$ - /// when controlled by the $n$-qubit number state $\ket{j}$. - /// - /// $U = \sum^{2^n-1}_{j=0}\ket{j}\bra{j}\otimes e^{i Z \theta_j}$. - /// - /// # Input - /// ## coefficients - /// Array of up to $2^n$ coefficients $\theta_j$. The $j$th coefficient - /// indexes the number state $\ket{j}$ encoded in little-endian format. - /// - /// ## control - /// $n$-qubit control register that encodes number states $\ket{j}$ in - /// little-endian format. - /// - /// ## target - /// Single qubit register that is rotated by $e^{i P \theta_j}$. - /// - /// # Remarks - /// `coefficients` will be padded with elements $\theta_j = 0.0$ if - /// fewer than $2^n$ are specified. - /// - /// # References - /// - Synthesis of Quantum Logic Circuits - /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov - /// https://arxiv.org/abs/quant-ph/0406176 - operation NoisyMultiplexZ (tolerance: Double, coefficients : Double[], control : LittleEndian, target : Qubit) : Unit - { - body (...) - { - // pad coefficients length at tail to a power of 2. - let coefficientsPadded = Padded(-2 ^ Length(control!), 0.0, coefficients); - - if (Length(coefficientsPadded) == 1) - { - // Termination case - if (AbsD(coefficientsPadded[0])> tolerance) - { - Exp([PauliZ], coefficientsPadded[0], [target]); - } - } - else - { - // Compute new coefficients. - let (coefficients0, coefficients1) = specialMultiplexZComputeCoefficients_(coefficientsPadded); - NoisyMultiplexZ(tolerance,coefficients0, LittleEndian((control!)[0 .. Length(control!) - 2]), target); - if (significantReal(tolerance,coefficients1)) - { - CNOT((control!)[Length(control!) - 1], target); - NoisyMultiplexZ(tolerance,coefficients1, LittleEndian((control!)[0 .. Length(control!) - 2]), target); - CNOT((control!)[Length(control!) - 1], target); - } - } - } - - adjoint invert; - - controlled (controlRegister, ...) - { - // pad coefficients length to a power of 2. - let coefficientsPadded = Padded(2 ^ (Length(control!) + 1), 0.0, Padded(-2 ^ Length(control!), 0.0, coefficients)); - let (coefficients0, coefficients1) = specialMultiplexZComputeCoefficients_(coefficientsPadded); - NoisyMultiplexZ(tolerance,coefficients0, control, target); - if (significantReal(tolerance,coefficients1)) - { - Controlled X(controlRegister, target); - NoisyMultiplexZ(tolerance,coefficients1, control, target); - Controlled X(controlRegister, target); - } - } - - controlled adjoint invert; - } - - - /// # Summary - /// Applies an array of complex phases to numeric basis states of a register of qubits. - /// - /// That is, this implements the diagonal unitary operation $U$ that applies a complex phase - /// $e^{i \theta_j}$ on the $n$-qubit number state $\ket{j}$. - /// - /// $U = \sum^{2^n-1}_{j=0}e^{i\theta_j}\ket{j}\bra{j}$. - /// - /// TODO: REIMPLEMENT THIS along the Welch et Bocharov lines - /// # Input - /// ## tolerance - /// Coefficients under this tolerance level should be ignored - /// ## coefficients - /// Array of up to $2^n$ coefficients $\theta_j$. The $j$th coefficient - /// indexes the number state $\ket{j}$ encoded in little-endian format. - /// - /// ## control - /// $n$-qubit control register that encodes number states $\ket{j}$ in - /// little-endian format. - /// - /// # Remarks - /// `coefficients` will be padded with elements $\theta_j = 0.0$ if - /// fewer than $2^n$ are specified. - /// - /// # References - /// - Synthesis of Quantum Logic Circuits - /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov - /// https://arxiv.org/abs/quant-ph/0406176 - operation NoisyApplyDiagonalUnitary (tolerance: Double, coefficients : Double[], qubits : LittleEndian) : Unit - { - body (...) - { - if (IsEmpty(qubits!)) { - fail "operation ApplyDiagonalUnitary -- Number of qubits must be greater than 0."; - } - - // pad coefficients length at tail to a power of 2. - let coefficientsPadded = Padded(-2 ^ Length(qubits!), 0.0, coefficients); - - // Compute new coefficients. - let (coefficients0, coefficients1) = specialMultiplexZComputeCoefficients_(coefficientsPadded); - NoisyMultiplexZ(tolerance,coefficients1, LittleEndian((qubits!)[0 .. Length(qubits!) - 2]), (qubits!)[Length(qubits!) - 1]); - - if (Length(coefficientsPadded) == 2) - { - - // Termination case - if (AbsD(coefficients0[0])>tolerance) - { - Exp([PauliI], 1.0 * coefficients0[0], qubits!); - } - } - else - { - NoisyApplyDiagonalUnitary(tolerance,coefficients0, LittleEndian((qubits!)[0 .. Length(qubits!) - 2])); - } - } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; - } - - - /// # Summary - /// Implementation step of multiply-controlled Z rotations. - /// # See Also - /// - Microsoft.Quantum.Canon.MultiplexZ - function specialMultiplexZComputeCoefficients_ (coefficients : Double[]) : (Double[], Double[]) - { - let newCoefficientsLength = Length(coefficients) / 2; - mutable coefficients0 = new Double[newCoefficientsLength]; - mutable coefficients1 = new Double[newCoefficientsLength]; - - for (idxCoeff in 0 .. newCoefficientsLength - 1) - { - set coefficients0 w/= idxCoeff <- 0.5 * (coefficients[idxCoeff] + coefficients[idxCoeff + newCoefficientsLength]); - set coefficients1 w/= idxCoeff <- 0.5 * (coefficients[idxCoeff] - coefficients[idxCoeff + newCoefficientsLength]); - } - - return (coefficients0, coefficients1); - } - - - - -} - - diff --git a/MachineLearning/src/Runtime/SpecialSP.qs b/MachineLearning/src/Runtime/SpecialSP.qs deleted file mode 100644 index 620e108feff..00000000000 --- a/MachineLearning/src/Runtime/SpecialSP.qs +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -namespace Microsoft.Quantum.MachineLearning { - open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Arithmetic; - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Math; - open Microsoft.Quantum.Arrays; - - // This library returns operations that prepare a specified quantum state - // from the computational basis state $\ket{0...0}$. - - /// # Summary - /// Returns an operation that prepares the given quantum state. - /// - /// The returned operation $U$ prepares an arbitrary quantum - /// state $\ket{\psi}$ with positive coefficients $\alpha_j\ge 0$ from - /// the $n$-qubit computational basis state $\ket{0...0}$. - /// - /// The action of U on a newly-allocated register is given by - /// $$ - /// \begin{align} - /// U \ket{0\cdots 0} = \ket{\psi} = \frac{\sum_{j=0}^{2^n-1}\alpha_j \ket{j}}{\sqrt{\sum_{j=0}^{2^n-1}|\alpha_j|^2}}. - /// \end{align} - /// $$ - /// - /// # Input - /// ## coefficients - /// Array of up to $2^n$ coefficients $\alpha_j$. The $j$th coefficient - /// indexes the number state $\ket{j}$ encoded in little-endian format. - /// - /// # Output - /// A state-preparation unitary operation $U$. - /// - /// # Remarks - /// Negative input coefficients $\alpha_j < 0$ will be treated as though - /// positive with value $|\alpha_j|$. `coefficients` will be padded with - /// elements $\alpha_j = 0.0$ if fewer than $2^n$ are specified. - /// - /// ## Example - /// The following snippet prepares the quantum state $\ket{\psi}=\sqrt{1/8}\ket{0}+\sqrt{7/8}\ket{2}$ - /// in the qubit register `qubitsLE`. - /// ```qsharp - /// let amplitudes = [Sqrt(0.125), 0.0, Sqrt(0.875), 0.0]; - /// let op = StatePreparationPositiveCoefficients(amplitudes); - /// using (qubits = Qubit[2]) { - /// let qubitsLE = LittleEndian(qubits); - /// op(qubitsLE); - /// } - /// ``` - function NoisyStatePreparationPositiveCoefficients (tolerance: Double, 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); - } - return NoisyPrepareArbitraryState(tolerance, coefficientsComplexPolar, _); - } - - /// # Summary - /// Returns an operation that prepares a specific quantum state. - /// - /// The returned operation $U$ prepares an arbitrary quantum - /// state $\ket{\psi}$ with complex coefficients $r_j e^{i t_j}$ from - /// the $n$-qubit computational basis state $\ket{0...0}$. - /// - /// The action of U on a newly-allocated register is given by - /// $$ - /// \begin{align} - /// U\ket{0...0}=\ket{\psi}=\frac{\sum_{j=0}^{2^n-1}r_j e^{i t_j}\ket{j}}{\sqrt{\sum_{j=0}^{2^n-1}|r_j|^2}}. - /// \end{align} - /// $$ - /// - /// # Input - /// ## coefficients - /// Array of up to $2^n$ complex coefficients represented by their - /// absolute value and phase $(r_j, t_j)$. The $j$th coefficient - /// indexes the number state $\ket{j}$ encoded in little-endian format. - /// - /// # Output - /// A state-preparation unitary operation $U$. - /// - /// # Remarks - /// Negative input coefficients $r_j < 0$ will be treated as though - /// positive with value $|r_j|$. `coefficients` will be padded with - /// elements $(r_j, t_j) = (0.0, 0.0)$ if fewer than $2^n$ are - /// specified. - /// - /// ## Example - /// The following snippet prepares the quantum state $\ket{\psi}=e^{i 0.1}\sqrt{1/8}\ket{0}+\sqrt{7/8}\ket{2}$ - /// in the qubit register `qubitsLE`. - /// ```qsharp - /// let amplitudes = [Sqrt(0.125), 0.0, Sqrt(0.875), 0.0]; - /// let phases = [0.1, 0.0, 0.0, 0.0]; - /// mutable complexNumbers = new ComplexPolar[4]; - /// for (idx in 0..3) { - /// set complexNumbers[idx] = ComplexPolar(amplitudes[idx], phases[idx]); - /// } - /// let op = StatePreparationComplexCoefficients(complexNumbers); - /// using (qubits = Qubit[2]) { - /// let qubitsLE = LittleEndian(qubits); - /// op(qubitsLE); - /// } - /// ``` - function NoisyStatePreparationComplexCoefficients (tolerance: Double, coefficients : ComplexPolar[]) : (LittleEndian => Unit is Adj + Ctl) { - return NoisyPrepareArbitraryState(tolerance, coefficients, _); - } - - /// # Summary - /// Returns an operation that prepares a given quantum state. - /// - /// The returned operation $U$ prepares an arbitrary quantum - /// state $\ket{\psi}$ with complex coefficients $r_j e^{i t_j}$ from - /// the $n$-qubit computational basis state $\ket{0...0}$. - /// - /// $$ - /// \begin{align} - /// U\ket{0...0}=\ket{\psi}=\frac{\sum_{j=0}^{2^n-1}r_j e^{i t_j}\ket{j}}{\sqrt{\sum_{j=0}^{2^n-1}|r_j|^2}}. - /// \end{align} - /// $$ - /// - /// # Input - /// ## coefficients - /// Array of up to $2^n$ complex coefficients represented by their - /// absolute value and phase $(r_j, t_j)$. The $j$th coefficient - /// indexes the number state $\ket{j}$ encoded in little-endian format. - /// - /// ## qubits - /// Qubit register encoding number states in little-endian format. This is - /// expected to be initialized in the computational basis state - /// $\ket{0...0}$. - /// - /// # Remarks - /// Negative input coefficients $r_j < 0$ will be treated as though - /// positive with value $|r_j|$. `coefficients` will be padded with - /// elements $(r_j, t_j) = (0.0, 0.0)$ if fewer than $2^n$ are - /// specified. - /// - /// # References - /// - Synthesis of Quantum Logic Circuits - /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov - /// https://arxiv.org/abs/quant-ph/0406176 - operation NoisyPrepareArbitraryState (tolerance:Double, coefficients : ComplexPolar[], qubits : LittleEndian) : Unit is Adj + Ctl { - // pad coefficients at tail length to a power of 2. - let coefficientsPadded = Padded(-2 ^ Length(qubits!), ComplexPolar(0.0, 0.0), coefficients); - let target = (qubits!)[0]; - let op = (Adjoint _NoisyPrepareArbitraryState(tolerance,coefficientsPadded, _, _))(_, target); - op( - // Determine what controls to apply to `op`. - Length(qubits!) > 1 - ? LittleEndian((qubits!)[1 .. Length(qubits!) - 1]) - | LittleEndian(new Qubit[0]) - ); - } - - - function significantComplex(tol: Double, rg:ComplexPolar[]):Bool { - for (j in 0..(Length(rg)-1)) { - if (AbsComplexPolar(rg[j])>tol) { - return true; - } - } - return false; - } - - /// # Summary - /// Implementation step of arbitrary state preparation procedure. - /// - /// # See Also - /// - PrepareArbitraryState - /// - Microsoft.Quantum.Canon.MultiplexPauli - operation _NoisyPrepareArbitraryState(tolerance: Double, coefficients : ComplexPolar[], control : LittleEndian, target : Qubit) : Unit is Adj + Ctl { - // For each 2D block, compute disentangling single-qubit rotation parameters - let (disentanglingY, disentanglingZ, newCoefficients) = _NoisyStatePreparationSBMComputeCoefficients(coefficients); - if (significantReal(tolerance,disentanglingZ)) { - NoisyMultiplexPauli(tolerance,disentanglingZ, PauliZ, control, target); - } - if (significantReal(tolerance,disentanglingY)) { - NoisyMultiplexPauli(tolerance,disentanglingY, PauliY, control, target); - } - // target is now in |0> state up to the phase given by arg of newCoefficients. - - // Continue recursion while there are control qubits. - if (Length(control!) == 0) { - let (abs, arg) = newCoefficients[0]!; - if (AbsD(arg)> tolerance) - { - Exp([PauliI], -1.0 * arg, [target]); - } - } else { - if (significantComplex(tolerance,newCoefficients)) { - let newControl = LittleEndian((control!)[1 .. Length(control!) - 1]); - let newTarget = (control!)[0]; - _NoisyPrepareArbitraryState(tolerance,newCoefficients, newControl, newTarget); - } - } - } - - /// # Summary - /// Computes the Bloch sphere coordinates for a single-qubit state. - /// - /// Given two complex numbers $a0, a1$ that represent the qubit state, computes coordinates - /// on the Bloch sphere such that - /// $a0 \ket{0} + a1 \ket{1} = r e^{it}(e^{-i \phi /2}\cos{(\theta/2)}\ket{0}+e^{i \phi /2}\sin{(\theta/2)}\ket{1})$. - /// - /// # Input - /// ## a0 - /// Complex coefficient of state $\ket{0}$. - /// ## a1 - /// Complex coefficient of state $\ket{1}$. - /// - /// # Output - /// A tuple containing `(ComplexPolar(r, t), phi, theta)`. - function NoisyBlochSphereCoordinates (a0 : ComplexPolar, a1 : ComplexPolar) : (ComplexPolar, Double, Double) { - let abs0 = AbsComplexPolar(a0); - let abs1 = AbsComplexPolar(a1); - let arg0 = ArgComplexPolar(a0); - let arg1 = ArgComplexPolar(a1); - let r = Sqrt(abs0 * abs0 + abs1 * abs1); - let t = 0.5 * (arg0 + arg1); - let phi = arg1 - arg0; - let theta = 2.0 * ArcTan2(abs1, abs0); - return (ComplexPolar(r, t), phi, theta); - } - - /// # Summary - /// Implementation step of arbitrary state preparation procedure. - /// # See Also - /// - Microsoft.Quantum.Canon.PrepareArbitraryState - function _NoisyStatePreparationSBMComputeCoefficients (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]; - for (idxCoeff in 0 .. 2 .. Length(coefficients) - 1) { - let (rt, phi, theta) = NoisyBlochSphereCoordinates(coefficients[idxCoeff], coefficients[idxCoeff + 1]); - set disentanglingZ w/= idxCoeff / 2 <- 0.5 * phi; - set disentanglingY w/= idxCoeff / 2 <- 0.5 * theta; - set newCoefficients w/= idxCoeff / 2 <- rt; - } - return (disentanglingY, disentanglingZ, newCoefficients); - } -} \ No newline at end of file diff --git a/Standard/src/Canon/Utils/Multiplexer.qs b/Standard/src/Canon/Utils/Multiplexer.qs index f6cfce0f835..0b4a2901e31 100644 --- a/Standard/src/Canon/Utils/Multiplexer.qs +++ b/Standard/src/Canon/Utils/Multiplexer.qs @@ -8,13 +8,20 @@ namespace Microsoft.Quantum.Canon { open Microsoft.Quantum.Math; /// # Summary - /// Applies a Pauli rotation conditioned on an array of qubits. - /// - /// This applies the multiply-controlled unitary operation $U$ that performs + /// Applies a Pauli rotation conditioned on an array of qubits. + /// + /// # Description + /// This applies a multiply controlled unitary operation that performs /// rotations by angle $\theta_j$ about single-qubit Pauli operator $P$ /// when controlled by the $n$-qubit number state $\ket{j}$. + /// In particular, the action of this operation is represented by the + /// unitary /// - /// $U = \sum^{2^n-1}_{j=0}\ket{j}\bra{j}\otimes e^{i P \theta_j}$. + /// $$ + /// \begin{align} + /// U = \sum^{2^n - 1}_{j=0} \ket{j}\bra{j} \otimes e^{i P \theta_j}. + /// \end{align} + /// ## /// /// # Input /// ## coefficients @@ -34,51 +41,157 @@ namespace Microsoft.Quantum.Canon { /// # Remarks /// `coefficients` will be padded with elements $\theta_j = 0.0$ if /// fewer than $2^n$ are specified. - operation MultiplexPauli (coefficients : Double[], pauli : Pauli, control : LittleEndian, target : Qubit) : Unit - { - body (...) - { - if (pauli == PauliZ) - { - let op = MultiplexZ(coefficients, control, _); - op(target); - } - elif (pauli == PauliX) - { - let op = MultiplexPauli(coefficients, PauliZ, control, _); - ApplyWithCA(H, op, target); - } - elif (pauli == PauliY) - { - let op = MultiplexPauli(coefficients, PauliX, control, _); - ApplyWithCA(Adjoint S, op, target); - } - elif (pauli == PauliI) - { - ApplyDiagonalUnitary(coefficients, control); + /// + /// # See Also + /// - ApproximatelyMultiplexPauli + operation MultiplexPauli(coefficients : Double[], pauli : Pauli, control : LittleEndian, target : Qubit) + : Unit is Adj + Ctl { + ApproximatelyMultiplexPauli(0.0, coefficients, pauli, control, target); + } + + /// # Summary + /// Applies a Pauli rotation conditioned on an array of qubits, truncating + /// small rotation angles according to a given tolerance. + /// + /// # Description + /// This applies a multiply controlled unitary operation that performs + /// rotations by angle $\theta_j$ about single-qubit Pauli operator $P$ + /// when controlled by the $n$-qubit number state $\ket{j}$. + /// In particular, the action of this operation is represented by the + /// unitary + /// + /// $$ + /// \begin{align} + /// U = \sum^{2^n - 1}_{j=0} \ket{j}\bra{j} \otimes e^{i P \theta_j}. + /// \end{align} + /// ## + /// + /// # Input + /// ## tolerance + /// A tolerance below which small coefficients are truncated. + /// + /// ## coefficients + /// Array of up to $2^n$ coefficients $\theta_j$. The $j$th coefficient + /// indexes the number state $\ket{j}$ encoded in little-endian format. + /// + /// ## pauli + /// Pauli operator $P$ that determines axis of rotation. + /// + /// ## control + /// $n$-qubit control register that encodes number states $\ket{j}$ in + /// little-endian format. + /// + /// ## target + /// Single qubit register that is rotated by $e^{i P \theta_j}$. + /// + /// # Remarks + /// `coefficients` will be padded with elements $\theta_j = 0.0$ if + /// fewer than $2^n$ are specified. + /// + /// # See Also + /// - MultiplexPauli + operation ApproximatelyMultiplexPauli(tolerance : Double, coefficients : Double[], pauli : Pauli, control : LittleEndian, target : Qubit) + : Unit is Adj + Ctl { + if (pauli == PauliZ) { + let op = ApproximatelyMultiplexZ(tolerance, coefficients, control, _); + op(target); + } elif (pauli == PauliX) { + let op = ApproximatelyMultiplexPauli(tolerance, coefficients, PauliZ, control, _); + ApplyWithCA(H, op, target); + } elif (pauli == PauliY) { + let op = ApproximatelyMultiplexPauli(tolerance, coefficients, PauliX, control, _); + ApplyWithCA(Adjoint S, op, target); + } elif (pauli == PauliI) { + ApproximatelyApplyDiagonalUnitary(tolerance, coefficients, control); + } else { + fail $"MultiplexPauli failed. Invalid pauli {pauli}."; + } + } + + /// # Summary + /// Applies a Pauli Z rotation conditioned on an array of qubits. + /// + /// # Description + /// This applies the multiply controlled unitary operation that performs + /// rotations by angle $\theta_j$ about single-qubit Pauli operator $Z$ + /// when controlled by the $n$-qubit number state $\ket{j}$. + /// In particular, this operation can be represented by the unitary + /// + /// $$ + /// \begin{align} + /// U = \sum^{2^n-1}_{j=0} \ket{j}\bra{j} \otimes e^{i Z \theta_j}. + /// \end{align} + /// $$ + /// + /// # Input + /// ## coefficients + /// Array of up to $2^n$ coefficients $\theta_j$. The $j$th coefficient + /// indexes the number state $\ket{j}$ encoded in little-endian format. + /// + /// ## control + /// $n$-qubit control register that encodes number states $\ket{j}$ in + /// little-endian format. + /// + /// ## target + /// Single qubit register that is rotated by $e^{i P \theta_j}$. + /// + /// # Remarks + /// `coefficients` will be padded with elements $\theta_j = 0.0$ if + /// fewer than $2^n$ are specified. + /// + /// # References + /// - Synthesis of Quantum Logic Circuits + /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov + /// https://arxiv.org/abs/quant-ph/0406176 + /// + /// # See Also + /// - ApproximatelyMultiplexZ + operation MultiplexZ(coefficients : Double[], control : LittleEndian, target : Qubit) + : Unit is Adj + Ctl { + ApproximatelyMultiplexZ(0.0, coefficients, control, target); + } + + function _AnyOutsideToleranceD(tolerance : Double, coefficients : Double[]) : Bool { + // NB: We don't currently use Any / Mapped for this, as we want to be + // able to short-circuit. That should be implied by immutable + // semantics, but that's not yet the case. + for (coefficient in coefficients) { + if (AbsD(coefficient) >= tolerance) { + return true; } - else - { - fail $"MultiplexPauli failed. Invalid pauli {pauli}."; + } + return false; + } + + function _AnyOutsideToleranceCP(tolerance : Double, coefficients : ComplexPolar[]) : Bool { + for (coefficient in coefficients) { + if (AbsComplexPolar(coefficient) > tolerance) { + return true; } } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; + return false; } - - + /// # Summary - /// Applies a Pauli Z rotation conditioned on an array of qubits. - /// - /// This applies the multiply-controlled unitary operation $U$ that performs + /// Applies a Pauli Z rotation conditioned on an array of qubits, truncating + /// small rotation angles according to a given tolerance. + /// + /// # Description + /// This applies the multiply controlled unitary operation that performs /// rotations by angle $\theta_j$ about single-qubit Pauli operator $Z$ /// when controlled by the $n$-qubit number state $\ket{j}$. + /// In particular, this operation can be represented by the unitary /// - /// $U = \sum^{2^n-1}_{j=0}\ket{j}\bra{j}\otimes e^{i Z \theta_j}$. + /// $$ + /// \begin{align} + /// U = \sum^{2^n-1}_{j=0} \ket{j}\bra{j} \otimes e^{i Z \theta_j}. + /// \end{align} + /// $$ /// /// # Input + /// ## tolerance + /// A tolerance below which small coefficients are truncated. + /// /// ## coefficients /// Array of up to $2^n$ coefficients $\theta_j$. The $j$th coefficient /// indexes the number state $\ket{j}$ encoded in little-endian format. @@ -98,53 +211,62 @@ namespace Microsoft.Quantum.Canon { /// - Synthesis of Quantum Logic Circuits /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov /// https://arxiv.org/abs/quant-ph/0406176 - operation MultiplexZ (coefficients : Double[], control : LittleEndian, target : Qubit) : Unit - { - body (...) - { + /// + /// # See Also + /// - MultiplexZ + operation ApproximatelyMultiplexZ(tolerance : Double, coefficients : Double[], control : LittleEndian, target : Qubit) : Unit is Adj + Ctl { + body (...) { // 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 - Exp([PauliZ], coefficientsPadded[0], [target]); - } - else - { + if (AbsD(coefficientsPadded[0]) > tolerance) { + Exp([PauliZ], coefficientsPadded[0], [target]); + } + } else { // Compute new coefficients. - let (coefficients0, coefficients1) = MultiplexZComputeCoefficients_(coefficientsPadded); - MultiplexZ(coefficients0, LittleEndian((control!)[0 .. Length(control!) - 2]), target); - CNOT((control!)[Length(control!) - 1], target); - MultiplexZ(coefficients1, LittleEndian((control!)[0 .. Length(control!) - 2]), target); - CNOT((control!)[Length(control!) - 1], target); + let (coefficients0, coefficients1) = _MultiplexZCoefficients(coefficientsPadded); + ApproximatelyMultiplexZ(tolerance,coefficients0, LittleEndian((control!)[0 .. Length(control!) - 2]), target); + if (_AnyOutsideToleranceD(tolerance, coefficients1)) { + within { + CNOT((control!)[Length(control!) - 1], target); + } apply { + ApproximatelyMultiplexZ(tolerance,coefficients1, LittleEndian((control!)[0 .. Length(control!) - 2]), target); + } + } } } - - adjoint invert; - - controlled (controlRegister, ...) - { + + controlled (controlRegister, ...) { // pad coefficients length to a power of 2. let coefficientsPadded = Padded(2 ^ (Length(control!) + 1), 0.0, Padded(-2 ^ Length(control!), 0.0, coefficients)); - let (coefficients0, coefficients1) = MultiplexZComputeCoefficients_(coefficientsPadded); - MultiplexZ(coefficients0, control, target); - Controlled X(controlRegister, target); - MultiplexZ(coefficients1, control, target); - Controlled X(controlRegister, target); + let (coefficients0, coefficients1) = _MultiplexZCoefficients(coefficientsPadded); + ApproximatelyMultiplexZ(tolerance,coefficients0, control, target); + if (_AnyOutsideToleranceD(tolerance,coefficients1)) { + within { + Controlled X(controlRegister, target); + } apply { + ApproximatelyMultiplexZ(tolerance,coefficients1, control, target); + } + } } - - controlled adjoint invert; } - - + /// # Summary - /// Applies an array of complex phases to numeric basis states of a register of qubits. - /// - /// That is, this implements the diagonal unitary operation $U$ that applies a complex phase + /// Applies an array of complex phases to numeric basis states of a register + /// of qubits. + /// + /// # Description + /// This operation implements a diagonal unitary that applies a complex phase /// $e^{i \theta_j}$ on the $n$-qubit number state $\ket{j}$. + /// In particular, this operation can be represented by the unitary /// - /// $U = \sum^{2^n-1}_{j=0}e^{i\theta_j}\ket{j}\bra{j}$. + /// $$ + /// \begin{align} + /// U = \sum^{2^n-1}_{j=0}e^{i\theta_j}\ket{j}\bra{j}. + /// \end{align} + /// $$ /// /// # Input /// ## coefficients @@ -163,62 +285,95 @@ namespace Microsoft.Quantum.Canon { /// - Synthesis of Quantum Logic Circuits /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov /// https://arxiv.org/abs/quant-ph/0406176 - operation ApplyDiagonalUnitary (coefficients : Double[], qubits : LittleEndian) : Unit - { - body (...) - { - if (Length(qubits!) == 0) - { - fail $"operation ApplyDiagonalUnitary -- Number of qubits must be greater than 0."; - } - - // pad coefficients length at tail to a power of 2. - let coefficientsPadded = Padded(-2 ^ Length(qubits!), 0.0, coefficients); - - // Compute new coefficients. - let (coefficients0, coefficients1) = MultiplexZComputeCoefficients_(coefficientsPadded); - MultiplexZ(coefficients1, LittleEndian((qubits!)[0 .. Length(qubits!) - 2]), (qubits!)[Length(qubits!) - 1]); - - if (Length(coefficientsPadded) == 2) - { - // Termination case + /// + /// # See Also + /// - ApproximatelyApplyDiagonalUnitary + operation ApplyDiagonalUnitary(coefficients : Double[], qubits : LittleEndian) : Unit is Adj + Ctl { + ApproximatelyApplyDiagonalUnitary(0.0, coefficients, qubits); + } + + /// # Summary + /// Applies an array of complex phases to numeric basis states of a register + /// of qubits, truncating small rotation angles according to a given + /// tolerance. + /// + /// # Description + /// This operation implements a diagonal unitary that applies a complex phase + /// $e^{i \theta_j}$ on the $n$-qubit number state $\ket{j}$. + /// In particular, this operation can be represented by the unitary + /// + /// $$ + /// \begin{align} + /// U = \sum^{2^n-1}_{j=0}e^{i\theta_j}\ket{j}\bra{j}. + /// \end{align} + /// $$ + /// + /// # Input + /// ## tolerance + /// A tolerance below which small coefficients are truncated. + /// + /// ## coefficients + /// Array of up to $2^n$ coefficients $\theta_j$. The $j$th coefficient + /// indexes the number state $\ket{j}$ encoded in little-endian format. + /// + /// ## control + /// $n$-qubit control register that encodes number states $\ket{j}$ in + /// little-endian format. + /// + /// # Remarks + /// `coefficients` will be padded with elements $\theta_j = 0.0$ if + /// fewer than $2^n$ are specified. + /// + /// # References + /// - Synthesis of Quantum Logic Circuits + /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov + /// https://arxiv.org/abs/quant-ph/0406176 + /// + /// # See Also + /// - ApplyDiagonalUnitary + operation ApproximatelyApplyDiagonalUnitary(tolerance : Double, coefficients : Double[], qubits : LittleEndian) + : Unit is Adj + Ctl { + if (IsEmpty(qubits!)) { + fail "operation ApplyDiagonalUnitary -- Number of qubits must be greater than 0."; + } + + // pad coefficients length at tail to a power of 2. + let coefficientsPadded = Padded(-2 ^ Length(qubits!), 0.0, coefficients); + + // Compute new coefficients. + let (coefficients0, coefficients1) = _MultiplexZCoefficients(coefficientsPadded); + ApproximatelyMultiplexZ(tolerance,coefficients1, LittleEndian((qubits!)[0 .. Length(qubits!) - 2]), (qubits!)[Length(qubits!) - 1]); + + if (Length(coefficientsPadded) == 2) { + // Termination case + if (AbsD(coefficients0[0]) > tolerance) { Exp([PauliI], 1.0 * coefficients0[0], qubits!); } - else - { - ApplyDiagonalUnitary(coefficients0, LittleEndian((qubits!)[0 .. Length(qubits!) - 2])); - } + } else { + ApproximatelyApplyDiagonalUnitary(tolerance, coefficients0, LittleEndian(Most(qubits!))); } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; } - - + /// # Summary /// Implementation step of multiply-controlled Z rotations. /// # See Also /// - Microsoft.Quantum.Canon.MultiplexZ - function MultiplexZComputeCoefficients_ (coefficients : Double[]) : (Double[], Double[]) - { + function _MultiplexZCoefficients(coefficients : Double[]) : (Double[], Double[]) { let newCoefficientsLength = Length(coefficients) / 2; mutable coefficients0 = new Double[newCoefficientsLength]; mutable coefficients1 = new Double[newCoefficientsLength]; - - for (idxCoeff in 0 .. newCoefficientsLength - 1) - { + + for (idxCoeff in 0 .. newCoefficientsLength - 1) { set coefficients0 w/= idxCoeff <- 0.5 * (coefficients[idxCoeff] + coefficients[idxCoeff + newCoefficientsLength]); set coefficients1 w/= idxCoeff <- 0.5 * (coefficients[idxCoeff] - coefficients[idxCoeff + newCoefficientsLength]); } - + return (coefficients0, coefficients1); } - - + /// # Summary - /// Applies an array of operations controlled by an array of number states. - /// + /// Applies an array of operations controlled by an array of number states. + /// /// That is, applies Multiply-controlled unitary operation $U$ that applies a /// unitary $V_j$ when controlled by $n$-qubit number state $\ket{j}$. /// @@ -239,42 +394,36 @@ namespace Microsoft.Quantum.Canon { /// # Remarks /// `coefficients` will be padded with identity elements if /// fewer than $2^n$ are specified. This implementation uses - /// $n-1$ ancilla qubits. + /// $n - 1$ auxillary 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) : Unit - { - body (...) - { - if (Length(index!) == 0) - { - fail $"MultiplexOperations failed. Number of index qubits must be greater than 0."; - } - - if (Length(unitaries) > 0) - { - let ancilla = new Qubit[0]; - _MultiplexOperations(unitaries, ancilla, index, target); - } + 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."; + } + + if (Length(unitaries) > 0) { + let auxillaryRegister = new Qubit[0]; + _MultiplexOperations(unitaries, auxillaryRegister, index, target); } - - adjoint invert; - controlled distribute; - controlled adjoint distribute; } - - + /// # Summary /// Implementation step of MultiplexOperations. /// # See Also /// - Microsoft.Quantum.Canon.MultiplexOperations - operation _MultiplexOperations<'T>(unitaries : ('T => Unit is Adj + Ctl)[], ancilla : Qubit[], index : LittleEndian, target : 'T) : Unit - { - body (...) - { + operation _MultiplexOperations<'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); @@ -283,59 +432,48 @@ namespace Microsoft.Quantum.Canon { let rightUnitaries = unitaries[0 .. nUnitariesRight - 1]; let leftUnitaries = unitaries[nUnitariesRight .. nUnitariesLeft - 1]; let newControls = LittleEndian((index!)[0 .. nIndex - 2]); - - if (nUnitaries > 0) - { - if (Length(ancilla) == 1 and nIndex == 0) - { + + if (nUnitaries > 0) { + if (Length(auxillaryRegister) == 1 and nIndex == 0) { // Termination case - Controlled unitaries[0](ancilla, target); - } - elif (Length(ancilla) == 0 and nIndex >= 1) - { + Controlled unitaries[0](auxillaryRegister, target); + } elif (Length(auxillaryRegister) == 0 and nIndex >= 1) { // Start case - let newAncilla = [(index!)[Length(index!) - 1]]; - - if (nUnitariesLeft > 0) - { - _MultiplexOperations(leftUnitaries, newAncilla, newControls, target); + let newAuxQubit = Tail(index!); + + if (nUnitariesLeft > 0) { + _MultiplexOperations(leftUnitaries, [newAuxQubit], newControls, target); } - - X(newAncilla[0]); - _MultiplexOperations(rightUnitaries, newAncilla, newControls, target); - X(newAncilla[0]); - } - else - { - // Recursion that reduces nIndex by 1 & sets Length(ancilla) to 1. - using (newAncilla = Qubit[1]) - { - Controlled X(ancilla + [(index!)[Length(index!) - 1]], newAncilla[0]); - - if (nUnitariesLeft > 0) - { - _MultiplexOperations(leftUnitaries, newAncilla, newControls, target); + + within { + X(newAuxQubit); + } apply { + _MultiplexOperations(rightUnitaries, [newAuxQubit], newControls, target); + } + } else { + // Recursion that reduces nIndex by 1 & sets Length(auxillaryRegister) to 1. + using (newAuxQubit = Qubit()) { + within { + Controlled X(auxillaryRegister + [(index!)[Length(index!) - 1]], newAuxQubit); + } apply { + if (nUnitariesLeft > 0) { + _MultiplexOperations(leftUnitaries, [newAuxQubit], newControls, target); + } + + within { + Controlled X(auxillaryRegister, newAuxQubit); + } apply { + _MultiplexOperations(rightUnitaries, [newAuxQubit], newControls, target); + } } - - Controlled X(ancilla, newAncilla[0]); - _MultiplexOperations(rightUnitaries, newAncilla, newControls, target); - Controlled X(ancilla, newAncilla[0]); - Controlled X(ancilla + [(index!)[Length(index!) - 1]], newAncilla[0]); } } } } - - adjoint invert; - - controlled (controlRegister, ...) - { + + controlled (controlRegister, ...) { _MultiplexOperations(unitaries, controlRegister, index, target); } - - controlled adjoint invert; } - -} - +} diff --git a/Standard/src/Preparation/StatePreparation.qs b/Standard/src/Preparation/Arbitrary.qs similarity index 82% rename from Standard/src/Preparation/StatePreparation.qs rename to Standard/src/Preparation/Arbitrary.qs index ec3f9a6d845..8b12642d855 100644 --- a/Standard/src/Preparation/StatePreparation.qs +++ b/Standard/src/Preparation/Arbitrary.qs @@ -12,8 +12,8 @@ namespace Microsoft.Quantum.Preparation { // from the computational basis state $\ket{0...0}$. /// # Summary - /// Returns an operation that prepares the given quantum state. - /// + /// Returns an operation that prepares the given quantum state. + /// /// The returned operation $U$ prepares an arbitrary quantum /// state $\ket{\psi}$ with positive coefficients $\alpha_j\ge 0$ from /// the $n$-qubit computational basis state $\ket{0...0}$. @@ -61,8 +61,8 @@ namespace Microsoft.Quantum.Preparation { } /// # Summary - /// Returns an operation that prepares a specific quantum state. - /// + /// Returns an operation that prepares a specific quantum state. + /// /// The returned operation $U$ prepares an arbitrary quantum /// state $\ket{\psi}$ with complex coefficients $r_j e^{i t_j}$ from /// the $n$-qubit computational basis state $\ket{0...0}$. @@ -108,11 +108,11 @@ namespace Microsoft.Quantum.Preparation { function StatePreparationComplexCoefficients (coefficients : ComplexPolar[]) : (LittleEndian => Unit is Adj + Ctl) { return PrepareArbitraryState(coefficients, _); } - - + + /// # Summary - /// Returns an operation that prepares a given quantum state. - /// + /// Returns an operation that prepares a given quantum state. + /// /// The returned operation $U$ prepares an arbitrary quantum /// state $\ket{\psi}$ with complex coefficients $r_j e^{i t_j}$ from /// the $n$-qubit computational basis state $\ket{0...0}$. @@ -144,12 +144,16 @@ namespace Microsoft.Quantum.Preparation { /// - Synthesis of Quantum Logic Circuits /// Vivek V. Shende, Stephen S. Bullock, Igor L. Markov /// https://arxiv.org/abs/quant-ph/0406176 - operation PrepareArbitraryState (coefficients : ComplexPolar[], qubits : LittleEndian) : Unit is Adj + Ctl { + operation PrepareArbitraryState(coefficients : ComplexPolar[], qubits : LittleEndian) : Unit is Adj + Ctl { + ApproximatelyPrepareArbitraryState(0.0, coefficients, qubits); + } + + /// TODO + operation ApproximatelyPrepareArbitraryState(tolerance : Double, coefficients : ComplexPolar[], qubits : LittleEndian) : Unit is Adj + Ctl { // pad coefficients at tail length to a power of 2. let coefficientsPadded = Padded(-2 ^ Length(qubits!), ComplexPolar(0.0, 0.0), coefficients); let target = (qubits!)[0]; - let op = (Adjoint _PrepareArbitraryState(coefficientsPadded, _, _))(_, target); - + let op = (Adjoint _ApproximatelyPrepareArbitraryState(tolerance, coefficientsPadded, _, _))(_, target); op( // Determine what controls to apply to `op`. Length(qubits!) > 1 @@ -158,40 +162,43 @@ namespace Microsoft.Quantum.Preparation { ); } - /// # Summary /// Implementation step of arbitrary state preparation procedure. /// /// # See Also /// - PrepareArbitraryState /// - Microsoft.Quantum.Canon.MultiplexPauli - operation _PrepareArbitraryState(coefficients : ComplexPolar[], control : LittleEndian, target : Qubit) : Unit is Adj + Ctl - { + operation _ApproximatelyPrepareArbitraryState(tolerance: Double, coefficients : ComplexPolar[], control : LittleEndian, target : Qubit) + : Unit is Adj + Ctl { // For each 2D block, compute disentangling single-qubit rotation parameters let (disentanglingY, disentanglingZ, newCoefficients) = _StatePreparationSBMComputeCoefficients(coefficients); - MultiplexPauli(disentanglingZ, PauliZ, control, target); - MultiplexPauli(disentanglingY, PauliY, control, target); - + if (_AnyOutsideToleranceD(tolerance, disentanglingZ)) { + ApproximatelyMultiplexPauli(tolerance, disentanglingZ, PauliZ, control, target); + } + if (_AnyOutsideToleranceD(tolerance, disentanglingY)) { + ApproximatelyMultiplexPauli(tolerance, disentanglingY, PauliY, control, target); + } // target is now in |0> state up to the phase given by arg of newCoefficients. - + // Continue recursion while there are control qubits. - if (Length(control!) == 0) - { + if (Length(control!) == 0) { let (abs, arg) = newCoefficients[0]!; - Exp([PauliI], -1.0 * arg, [target]); - } - else - { - let newControl = LittleEndian((control!)[1 .. Length(control!) - 1]); - let newTarget = (control!)[0]; - _PrepareArbitraryState(newCoefficients, newControl, newTarget); + if (AbsD(arg) > tolerance) { + Exp([PauliI], -1.0 * arg, [target]); + } + } else { + if (_AnyOutsideToleranceCP(tolerance, newCoefficients)) { + let newControl = LittleEndian((control!)[1 .. Length(control!) - 1]); + let newTarget = (control!)[0]; + _ApproximatelyPrepareArbitraryState(tolerance,newCoefficients, newControl, newTarget); + } } } - - + + /// # Summary - /// Computes the Bloch sphere coordinates for a single-qubit state. - /// + /// Computes the Bloch sphere coordinates for a single-qubit state. + /// /// Given two complex numbers $a0, a1$ that represent the qubit state, computes coordinates /// on the Bloch sphere such that /// $a0 \ket{0} + a1 \ket{1} = r e^{it}(e^{-i \phi /2}\cos{(\theta/2)}\ket{0}+e^{i \phi /2}\sin{(\theta/2)}\ket{1})$. @@ -204,8 +211,7 @@ namespace Microsoft.Quantum.Preparation { /// /// # Output /// A tuple containing `(ComplexPolar(r, t), phi, theta)`. - function BlochSphereCoordinates (a0 : ComplexPolar, a1 : ComplexPolar) : (ComplexPolar, Double, Double) - { + function BlochSphereCoordinates (a0 : ComplexPolar, a1 : ComplexPolar) : (ComplexPolar, Double, Double) { let abs0 = AbsComplexPolar(a0); let abs1 = AbsComplexPolar(a1); let arg0 = ArgComplexPolar(a0);