Source code for dc_qiskit_algorithms.MöttönenStatePreparation

# Copyright 2018 Carsten Blank
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import math
from typing import List, Union

import numpy as np
from qiskit import QuantumRegister, QuantumCircuit
from qiskit.circuit import Gate, Instruction, Qubit
from qiskit.extensions import RYGate, RZGate
from scipy import sparse

from .UniformRotation import UniformRotationGate


[docs]def get_alpha_z(omega, n, k): # type: (sparse.dok_matrix, int, int) -> sparse.dok_matrix """ Computes the rotation angles alpha for the z-rotations :param omega: the input phase :param n: total number of qubits :param k: current qubit :return: a sparse vector """ alpha_z_k = sparse.dok_matrix((2 ** (n - k), 1), dtype=np.float64) for (i, _), om in omega.items(): i += 1 j = int(np.ceil(i * 2 ** (-k))) s_condition = 2 ** (k - 1) * (2 * j - 1) s_i = 1.0 if i > s_condition else -1.0 alpha_z_k[j - 1, 0] = alpha_z_k[j - 1, 0] + s_i * om / 2 ** (k - 1) return alpha_z_k
[docs]def get_alpha_y(a, n, k): # type: (sparse.dok_matrix, int, int) -> sparse.dok_matrix """ Computes the rotation angles alpha for the y-rotations :param a: the input absolute values :param n: total number of qubits :param k: current qubit :return: a sparse vector """ alpha = sparse.dok_matrix((2**(n - k), 1), dtype=np.float64) numerator = sparse.dok_matrix((2 ** (n - k), 1), dtype=np.float64) denominator = sparse.dok_matrix((2 ** (n - k), 1), dtype=np.float64) for (i, _), e in a.items(): j = int(math.ceil((i + 1) / 2**k)) l = (i + 1) - (2*j - 1)*2**(k-1) is_part_numerator = 1 <= l <= 2**(k-1) if is_part_numerator: numerator[j - 1, 0] += e*e denominator[j - 1, 0] += e*e for (j, _), e in numerator.items(): numerator[j, 0] = np.sqrt(e) for (j, _), e in denominator.items(): denominator[j, 0] = 1/np.sqrt(e) pre_alpha = numerator.multiply(denominator) # type: sparse.csr_matrix for (j, _), e in pre_alpha.todok().items(): alpha[j, 0] = 2*np.arcsin(e) return alpha
[docs]class RZGateNegatedAngle(RZGate): def __init__(self, phi): super().__init__(-phi)
[docs]class RYGateNegatedAngle(RYGate): def __init__(self, phi): super().__init__(-phi)
# noinspection NonAsciiCharacters
[docs]class MöttönenStatePreparationGate(Gate): """Uniform rotation Y gate (Möttönen).""" def __init__(self, vector, neglect_absolute_value=False): # type: (Union[sparse.dok_matrix, List[complex], List[float]], bool) -> None """ Create the composite gate for the Möttönen state preparation scheme with an input vector, which registers/qubits to apply it to, and the circuit (if any) :param vector: the input complex sparse vector :param neglect_absolute_value: When given a vector, the absolute value is neglected which means that only the phase/angle is applied (RZ rotations) """ if isinstance(vector, list): vector = sparse.dok_matrix([vector]).transpose() self.neglect_absolute_value = neglect_absolute_value num_qubits = int(math.log2(vector.shape[0])) super().__init__("state_prep_möttönen", num_qubits=num_qubits, params=[]) self.vector = vector # type: sparse.dok_matrix def _define(self): a = sparse.dok_matrix(self.vector.get_shape()) # type: sparse.dok_matrix omega = sparse.dok_matrix(self.vector.get_shape()) # type: sparse.dok_matrix for (i, j), v in self.vector.items(): a[i, j] = np.absolute(v) omega[i, j] = np.angle(v) q = QuantumRegister(self.num_qubits, "qubits") qc = QuantumCircuit(q, name=self.name) qc_rot_z = self.apply_rot_z(omega, q) qc = qc.combine(qc_rot_z) if not self.neglect_absolute_value: qc_rot_y = self.apply_rot_y(a, q) qc = qc.combine(qc_rot_y) self._definition = qc.inverse()
[docs] @staticmethod def apply_rot_y(a, qreg): # type: (sparse.dok_matrix, QuantumRegister) -> QuantumCircuit """ Applies the cascade of y-uniform rotations to the qubits :param a: the sparse absolute value vector :param qreg: quantum register to which the scheme are applied :return: None """ qc = QuantumCircuit(qreg, name='apply_rot_y') # type: QuantumCircuit num_qubits = int(math.log2(a.shape[0])) for k in range(1, num_qubits + 1): alpha_y_k = get_alpha_y(a, num_qubits, k) # type: sparse.dok_matrix control = qreg[k:] target = qreg[k - 1] qc.append(UniformRotationGate(gate=RYGateNegatedAngle, alpha=alpha_y_k), control + [target], []) return qc
[docs] @staticmethod def apply_rot_z(omega, qreg): # type: (sparse.dok_matrix, QuantumRegister) -> QuantumCircuit """ Applies the cascade of z-uniform rotations to the qubits :param omega: the sparse phase vector :param qreg: quantum register to which the scheme are applied :return: None """ qc = QuantumCircuit(qreg, name='apply_rot_z') # type: QuantumCircuit num_qubits = int(math.log2(omega.shape[0])) for k in range(1, num_qubits + 1): alpha_z_k = get_alpha_z(omega, num_qubits, k) control = qreg[k:] target = qreg[k - 1] # if len(alpha_z_k) != 0: qc.append(UniformRotationGate(gate=RZGateNegatedAngle, alpha=alpha_z_k), control + [target], []) return qc
# noinspection NonAsciiCharacters
[docs]def state_prep_möttönen(self, a, qubits): # type: (QuantumCircuit, Union[List[float], sparse.dok_matrix], Union[List[Qubit], QuantumRegister]) -> Instruction """ Convenience function to encapsulate the composite gate of the state preparation :param self: Quantum circuit to apply this to :param a: the input vector :param qubits: the qubits to be transformed :return: gate just added """ if isinstance(qubits, QuantumRegister): qubits = list(qubits) if isinstance(a, sparse.dok_matrix): return self.append(MöttönenStatePreparationGate(a), qubits, []) else: return self.append(MöttönenStatePreparationGate(sparse.dok_matrix([a]).transpose()), qubits)
# noinspection NonAsciiCharacters
[docs]def state_prep_möttönen_dg(self, a, qubits): # type: (QuantumCircuit, Union[List[float], sparse.dok_matrix], Union[List[Qubit], QuantumRegister]) -> Instruction """ Convenience function to encapsulate the composite gate of the dagger of the state preparation :param self: Composite Gate or Quantum circuit to apply this to :param a: the input vector :param qubits: the qubits to be transformed :return: gate or instruction set """ return state_prep_möttönen(self, a, qubits).inverse()
# noinspection NonAsciiCharacters QuantumCircuit.state_prep_möttönen = state_prep_möttönen # noinspection NonAsciiCharacters QuantumCircuit.state_prep_möttönen_dg = state_prep_möttönen_dg