예제 #1
0
    def __init__(self, basis=None):
        """Optimize1qGatesDecomposition initializer.

        Args:
            basis (list[str]): Basis gates to consider, e.g. `['u3', 'cx']`. For the effects
                of this pass, the basis is the set intersection between the `basis` parameter
                and the Euler basis.
        """
        super().__init__()

        self._target_basis = basis
        self._decomposers = None

        if basis:
            self._decomposers = {}
            basis_set = set(basis)
            euler_basis_gates = one_qubit_decompose.ONE_QUBIT_EULER_BASIS_GATES
            for euler_basis_name, gates in euler_basis_gates.items():
                if set(gates).issubset(basis_set):
                    basis_copy = copy.copy(self._decomposers)
                    for base in basis_copy.keys():
                        # check if gates are a superset of another basis
                        if set(base).issubset(set(gates)):
                            # if so, remove that basis
                            del self._decomposers[base]
                        # check if the gates are a subset of another basis
                        elif set(gates).issubset(set(base)):
                            # if so, don't bother
                            break
                    # if not a subset, add it to the list
                    else:
                        self._decomposers[tuple(
                            gates
                        )] = one_qubit_decompose.OneQubitEulerDecomposer(
                            euler_basis_name)
예제 #2
0
    def run(self, unitary, **options):
        # Approximation degree is set directly as an attribute on the
        # instance by the UnitarySynthesis pass here as it's not part of
        # plugin interface. However if for some reason it's not set assume
        # it's 1.
        approximation_degree = getattr(self, "_approximation_degree", 1)
        basis_gates = options["basis_gates"]
        coupling_map = options["coupling_map"][0]
        natural_direction = options["natural_direction"]
        pulse_optimize = options["pulse_optimize"]
        gate_lengths = options["gate_lengths"]
        gate_errors = options["gate_errors"]
        qubits = options["coupling_map"][1]
        target = options["target"]

        euler_basis = _choose_euler_basis(basis_gates)
        if euler_basis is not None:
            decomposer1q = one_qubit_decompose.OneQubitEulerDecomposer(
                euler_basis)
        else:
            decomposer1q = None

        preferred_direction = None
        if target is not None:
            decomposer2q, preferred_direction = self._find_decomposer_2q_from_target(
                target, qubits, pulse_optimize)
        else:
            decomposer2q = _basis_gates_to_decomposer_2q(
                basis_gates, pulse_optimize=pulse_optimize)

        synth_dag = None
        wires = None
        if unitary.shape == (2, 2):
            if decomposer1q is None:
                return None
            synth_dag = circuit_to_dag(decomposer1q._decompose(unitary))
        elif unitary.shape == (4, 4):
            if not decomposer2q:
                return None
            synth_dag, wires = self._synth_natural_direction(
                unitary,
                coupling_map,
                qubits,
                decomposer2q,
                gate_lengths,
                gate_errors,
                natural_direction,
                approximation_degree,
                pulse_optimize,
                target,
                preferred_direction,
            )
        else:
            synth_dag = circuit_to_dag(
                isometry.Isometry(unitary, 0, 0).definition)

        return synth_dag, wires
    def run(self, dag: DAGCircuit) -> DAGCircuit:
        """Run the UnitarySynthesis pass on `dag`.

        Args:
            dag: input dag.

        Returns:
            Output dag with UnitaryGates synthesized to target basis.

        Raises:
            TranspilerError:
                1. pulse_optimize is True but pulse optimal decomposition is not known
                   for requested basis.
                2. pulse_optimize is True and natural_direction is True but a preferred
                   gate direction can't be determined from the coupling map or the
                   relative gate lengths.
        """

        euler_basis = _choose_euler_basis(self._basis_gates)
        kak_gate = _choose_kak_gate(self._basis_gates)
        decomposer1q, decomposer2q = None, None
        if euler_basis is not None:
            decomposer1q = one_qubit_decompose.OneQubitEulerDecomposer(
                euler_basis)
        if kak_gate is not None:
            decomposer2q = TwoQubitBasisDecomposer(
                kak_gate,
                euler_basis=euler_basis,
                pulse_optimize=self._pulse_optimize)

        for node in dag.named_nodes(*self._synth_gates):
            if self._basis_gates and node.name in self._basis_gates:
                continue
            synth_dag = None
            wires = None
            if len(node.qargs) == 1:
                if decomposer1q is None:
                    continue
                synth_dag = circuit_to_dag(
                    decomposer1q._decompose(node.op.to_matrix()))
            elif len(node.qargs) == 2:
                if decomposer2q is None:
                    continue
                synth_dag, wires = self._synth_natural_direction(
                    node, dag, decomposer2q)
            else:
                synth_dag = circuit_to_dag(
                    isometry.Isometry(node.op.to_matrix(), 0, 0).definition)

            dag.substitute_node_with_dag(node, synth_dag, wires=wires)

        return dag
    def __init__(self, basis=None):
        """Optimize1qGatesDecomposition initializer.

        Args:
            basis (list[str]): Basis gates to consider, e.g. `['u3', 'cx']`. For the effects
                of this pass, the basis is the set intersection between the `basis` parameter
                and the Euler basis.
        """
        super().__init__()
        self.basis = None
        if basis:
            self.basis = []
            basis_set = set(basis)
            for basis_name, gates in one_qubit_decompose.ONE_QUBIT_EULER_BASIS_GATES.items():
                if set(gates).issubset(basis_set):
                    self.basis.append(one_qubit_decompose.OneQubitEulerDecomposer(basis_name))
예제 #5
0
    def run(self, dag: DAGCircuit) -> DAGCircuit:
        """Run the UnitarySynthesis pass on `dag`.

        Args:
            dag: input dag.

        Returns:
            Output dag with UnitaryGates synthesized to target basis.
        """
        euler_basis = _choose_euler_basis(self._basis_gates)
        kak_gate = _choose_kak_gate(self._basis_gates)

        decomposer1q, decomposer2q = None, None
        if euler_basis is not None:
            decomposer1q = one_qubit_decompose.OneQubitEulerDecomposer(
                euler_basis)
        if kak_gate is not None:
            decomposer2q = TwoQubitBasisDecomposer(kak_gate,
                                                   euler_basis=euler_basis)

        for node in dag.named_nodes("unitary"):

            synth_dag = None
            if len(node.qargs) == 1:
                if decomposer1q is None:
                    continue
                synth_dag = circuit_to_dag(
                    decomposer1q._decompose(node.op.to_matrix()))
            elif len(node.qargs) == 2:
                if decomposer2q is None:
                    continue
                synth_dag = circuit_to_dag(
                    decomposer2q(node.op.to_matrix(),
                                 basis_fidelity=self._approximation_degree))
            else:
                synth_dag = circuit_to_dag(
                    isometry.Isometry(node.op.to_matrix(), 0, 0).definition)

            dag.substitute_node_with_dag(node, synth_dag)

        return dag
예제 #6
0
def qs_decomposition(mat, opt_a1=True, decomposer_1q=None, decomposer_2q=None):
    """
    Decomposes unitary matrix into one and two qubit gates using Quantum Shannon Decomposition.

       ┌───┐               ┌───┐     ┌───┐     ┌───┐
      ─┤   ├─       ───────┤ Rz├─────┤ Ry├─────┤ Rz├─────
       │   │    ≃     ┌───┐└─┬─┘┌───┐└─┬─┘┌───┐└─┬─┘┌───┐
     /─┤   ├─       /─┤   ├──□──┤   ├──□──┤   ├──□──┤   ├
       └───┘          └───┘     └───┘     └───┘     └───┘

    The number of CX gates generated with the decomposition without optimizations is,

    .. math::

        \frac{9}{16} 4^n - frac{3}{2} 2^n

    If opt_a1=True, the CX count is further reduced by,

    .. math::

        \frac{1}{3} 4^{n - 2} - 1

    This decomposition is described in arXiv:quant-ph/0406176.

    Arguments:
       mat (ndarray): unitary matrix to decompose
       opt_a1 (bool): whether to try optimization A.1 from Shende. This should eliminate 1 cnot
          per call. If True CZ gates are left in the output. If desired these can be further decomposed
          to CX.
       decomposer_1q (None or Object): optional 1Q decomposer. If None, uses
          :class:`~qiskit.quantum_info.synthesis.one_qubit_decomposer.OneQubitEulerDecomser`
       decomposer_2q (None or Object): optional 2Q decomposer. If None, uses
          :class:`~qiskit.quantum_info.synthesis.two_qubit_decomposer.TwoQubitBasisDecomposer`
          with CXGate.

    Return:
       QuantumCircuit: Decomposed quantum circuit.
    """
    dim = mat.shape[0]
    nqubits = int(np.log2(dim))
    if np.allclose(np.identity(dim), mat):
        return QuantumCircuit(nqubits)
    if dim == 2:
        if decomposer_1q is None:
            decomposer_1q = one_qubit_decompose.OneQubitEulerDecomposer()
        circ = decomposer_1q(mat)
    elif dim == 4:
        if decomposer_2q is None:
            decomposer_2q = two_qubit_decompose.TwoQubitBasisDecomposer(CXGate())
        circ = decomposer_2q(mat)
    else:
        qr = QuantumRegister(nqubits)
        circ = QuantumCircuit(qr)
        dim_o2 = dim // 2
        # perform cosine-sine decomposition
        (u1, u2), vtheta, (v1h, v2h) = scipy.linalg.cossin(mat, separate=True, p=dim_o2, q=dim_o2)
        # left circ
        left_circ = _demultiplex(v1h, v2h, opt_a1=opt_a1)
        circ.append(left_circ.to_instruction(), qr)
        # middle circ
        if opt_a1:
            nangles = len(vtheta)
            half_size = nangles // 2
            # get UCG in terms of CZ
            circ_cz = _get_ucry_cz(nqubits, (2 * vtheta).tolist())
            circ.append(circ_cz.to_instruction(), range(nqubits))
            # merge final cz with right-side generic multiplexer
            u2[:, half_size:] = np.negative(u2[:, half_size:])
        else:
            circ.ucry((2 * vtheta).tolist(), qr[:-1], qr[-1])
        # right circ
        right_circ = _demultiplex(u1, u2, opt_a1=opt_a1)
        circ.append(right_circ.to_instruction(), qr)

    return circ