Source code for qmatchatea.utils.qk_utils

# This code is part of qmatchatea.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
Utility functions to generate common quantum circuits and have
a convenient visualization of the quantum state
"""
from copy import deepcopy

import numpy as np
from qiskit.circuit import Barrier
from qiskit.quantum_info import Statevector

__all__ = ["qiskit_get_statevect", "W_qiskit", "GHZ_qiskit", "QFT_qiskit", "get_index"]


class MeasureObservables(Barrier):
    """
    Class to apply mid-circuit observables measurements with qmatchatea.

    Parameters
    ----------

    label: str
        Name of the observable. It will be used to recover the information
        at the end of the simulation.
    num_qubits: int
        Number of qubits on which the observable is defined. It should be
        the number of qubits in the quantum circuit.
    observables: TNObservables
        The observable class with the information about what you want to
        measure. The input observables will be copied, and it is thus NOT
        modified in place.
    """

    def __init__(self, label, num_qubits, observables):
        self._obs = deepcopy(observables)
        # pylint: disable-next=unexpected-keyword-arg
        super().__init__(num_qubits)
        self._name = "MeasureObservables"
        self._label = label

    @property
    def observables(self):
        """Observables property"""
        return self._obs

    @property
    def label(self):
        """Return the label of the observable"""
        return self._label

    def inverse(self, annotated: bool = False):
        """Special case. Return self."""
        return self


# pylint: disable-next=invalid-name
class qk_transpilation_params:
    """Class to contain the
    transpilation parameters used to map
    a qiskit circuit

    Parameters
    ----------
    linearize: bool, optional
        If True use qiskit transpiler to linearize the circuit. Default to True.
    basis_gates: list, optional
        If not empty decompose using qiskit transpiler into basis gate set
    optimization: integer, optional
        Level of optimization in qiskit transpiler. Default to 1. See
        https://docs.quantum.ibm.com/guides/set-optimization for more details
        about the qiskit transpiler.
    tensor_compiler: bool, optional
        If True, contract all the two-qubit gates before running the experiment.
        Default to False.
    """

    def __init__(
        self, linearize=True, basis_gates=None, optimization=1, tensor_compiler=False
    ):
        self._linearize = linearize
        self._basis_gates = [] if basis_gates is None else basis_gates
        self._optimization = optimization
        self._tensor_compiler = tensor_compiler

    @property
    def linearize(self):
        """linearize property"""
        return self._linearize

    @property
    def basis_gates(self):
        """basis_gates property"""
        return self._basis_gates

    @property
    def optimization(self):
        """optimization property"""
        return self._optimization

    @property
    def tensor_compiler(self):
        """tensor_compiler property"""
        return self._tensor_compiler


[docs] def get_index(qc, qubit): """ Get the index of the qubit/bit of a quantum circuit. Parameters ---------- qc : QuantumCircuit qiskit quantum circuit where the qubit is defined qubit : Qubit/Bit Qubit of which you want to know the index Returns ------- int Index of the qubit """ return qc.find_bit(qubit)[0]
[docs] def qiskit_get_statevect(qc): """ Returns the statevector of the qiskit quantum circuit *qc* Parameters ---------- qc: Quantum circuit Quantum circuit of which we want the statevector Returns ------- st: array_like Statevector of the quantum circuit after the application of the reverse operation on the qubit's ordering """ qc = deepcopy(qc) return Statevector(qc).data
def cphase_swap_qiskit(circuit, control, target, phase): """ Apply to a quantum circuit *circuit* the cphase and swap gate. Acts in place. Parameters ---------- circuit: Quantum Circuit Qiskit quantum circuit control: int Index of the control qubit for the controlled phase target: int Index of the target qubit for the controlled phase phase: double Phase to apply in the controlled phase in radiants Returns ------- None: None Acts in place """ circuit.cp(phase, control, target) circuit.swap(control, target) # pylint: disable-next=invalid-name def F_gate(circuit, kk): """ Apply to a quantum circuit *circuit* the F gate, composed by Ry, CZ, Ry Acts in place. Parameters ---------- circuit: Quantum Circuit Qiskit quantum circuit k: int Index of the control qubit for the controlled phase Returns ------- None: None Acts in place """ nn = circuit.num_qubits theta = np.arccos(np.sqrt(1 / (nn - kk))) circuit.ry(-theta, kk + 1) circuit.cz(kk + 1, kk) circuit.ry(theta, kk + 1) # pylint: disable-next=invalid-name
[docs] def QFT_qiskit(circuit, nn): """ Apply the QFT to a qiskit quantum circuit *circuit* in a recursive way Parameters ---------- circuit : quantum circuit quantum circuit where we want to apply the QFT nn : int number of qubits in *circuit* Returns ------- None: None Acts in place """ # pylint: disable-next=no-else-return if nn == 0: return circuit elif nn == 1: circuit.h(0) return circuit circuit.h(0) for ii in range(nn - 1): cphase_swap_qiskit(circuit, ii, ii + 1, np.pi * 1 / 2 ** (ii + 1)) return QFT_qiskit(circuit, nn - 1)
# pylint: disable-next=invalid-name
[docs] def GHZ_qiskit(circ): """ Generates a GHZ state in the quantum circuit *circ* composed by n qubits. Acts in place. Parameters ---------- circ: Quantum Circuit The quantum circuit in the state '00...0' where to build the GHZ Returns ------- None: None Acts in place. """ nn = circ.num_qubits circ.h(0) for ii in range(1, nn): circ.cx(ii - 1, ii)
# pylint: disable-next=invalid-name
[docs] def W_qiskit(circ): """ Generates a W state in the quantum circuit *circ* composed by n qubits. Acts in place. Parameters ---------- circ: Quantum Circuit The quantum circuit in the state '00...0' where to build the W Returns ------- None: None Acts in place. """ nn = circ.num_qubits circ.x(0) for ii in range(nn - 1): F_gate(circ, ii) circ.barrier() for ii in range(nn - 1): circ.cx(ii + 1, ii) circ.barrier()