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)
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))
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
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