Source code for scikit_quri.circuit.pre_defined

# mypy: ignore-errors
import numpy as np
from numpy.random import default_rng, Generator
from functools import reduce
from typing import Optional, List
from .circuit import LearningCircuit
from numpy.typing import NDArray
from quri_parts.circuit import QuantumGate, CZ, CNOT


[docs]def create_qcl_ansatz( n_qubit: int, c_depth: int, time_step: float = 0.5, seed: Optional[int] = 0 ) -> LearningCircuit: """Create a circuit used in this page: https://dojo.qulacs.org/ja/latest/notebooks/5.2_Quantum_Circuit_Learning.html Args: n_qubit: number of qubits c_depth: circuit depth time_step: the evolution time used for the hamiltonian dynamics seed: seed for random numbers. used for determining the interaction strength of the hamiltonian simulation Examples: >>> n_qubit = 4 >>> circuit = create_qcl_ansatz(n_qubit, 3, 0.5) >>> qnn = QNNRegressor(circuit) >>> qnn.fit(x_train, y_train) """ def preprocess_x(x: NDArray[np.float64], index: int) -> float: xa = x[index % len(x)] return min(1, max(-1, xa)) circuit = LearningCircuit(n_qubit) for i in range(n_qubit): # Capture copy of i by `i=i`. # Without this, i in lambda is a reference to the i, so the lambda always # recognize i as n_qubit - 1. circuit.add_input_RY_gate(i, lambda x, i=i: np.arcsin(preprocess_x(x, i))) circuit.add_input_RZ_gate( i, lambda x, i=i: np.arccos(preprocess_x(x, i) * preprocess_x(x, i)) ) time_evol_gate = _create_time_evol_gate(n_qubit, time_step) for _ in range(c_depth): circuit.add_gate(time_evol_gate) for i in range(n_qubit): circuit.add_parametric_RX_gate(i) circuit.add_parametric_RZ_gate(i) circuit.add_parametric_RX_gate(i) return circuit
def _create_time_evol_gate( n_qubit, time_step=0.77, rng: Optional[Generator] = None, seed: Optional[int] = 0 ) -> QuantumGate: """create a hamiltonian dynamics with transverse field ising model with random interaction and random magnetic field Args: n_qubit: number of qubits time_step: evolution time rng: random number generator seed: seed for random number Return: qulacs' gate object """ if rng is None: rng = default_rng(seed) ham = _make_hamiltonian(n_qubit, rng) # Create time evolution operator by diagonalization. # H*P = P*D <-> H = P*D*P^dagger diag, eigen_vecs = np.linalg.eigh(ham) time_evol_op = np.dot( np.dot(eigen_vecs, np.diag(np.exp(-1j * time_step * diag))), eigen_vecs.T.conj() ) # e^-iHT # Convert to a qulacs gate time_evol_gate = QuantumGate( name="UnitaryMatrix", target_indices=[i for i in range(n_qubit)], unitary_matrix=time_evol_op, ) return time_evol_gate def _make_hamiltonian(n_qubit, rng: Optional[Generator] = None, seed: Optional[int] = 0): if rng is None: rng = default_rng(seed) X_mat = np.array([[0, 1], [1, 0]]) Z_mat = np.array([[1, 0], [0, -1]]) ham = np.zeros((2**n_qubit, 2**n_qubit), dtype=complex) for i in range(n_qubit): Jx = rng.uniform(-1.0, 1.0) ham += Jx * _make_fullgate([[i, X_mat]], n_qubit) for j in range(i + 1, n_qubit): J_ij = rng.uniform(-1.0, 1.0) ham += J_ij * _make_fullgate([[i, Z_mat], [j, Z_mat]], n_qubit) return ham def _make_fullgate(list_SiteAndOperator, n_qubit): """ Receive `list_SiteAndOperator = [ [i_0, O_0], [i_1, O_1], ...]` and insert identity to qubits which is not present in the list to create (2**n_qubit, 2**n_qubit) matrix I(0) * ... * O_0(i_0) * ... * O_1(i_1) ... """ I_mat = np.eye(2, dtype=complex) list_Site = [SiteAndOperator[0] for SiteAndOperator in list_SiteAndOperator] list_SingleGates = [] cnt = 0 for i in range(n_qubit): if i in list_Site: list_SingleGates.append(list_SiteAndOperator[cnt][1]) cnt += 1 else: list_SingleGates.append(I_mat) return reduce(np.kron, list_SingleGates)
[docs]def preprocess_x(x: np.ndarray, i: int) -> float: xa = x[i % len(x)] clamped = min(1, max(-1, xa)) return clamped
[docs]def create_farhi_neven_ansatz( n_qubit: int, c_depth: int, seed: Optional[int] = 0 ) -> LearningCircuit: circuit = LearningCircuit(n_qubit) rng = default_rng(seed) for i in range(n_qubit): circuit.add_input_RY_gate(i, lambda x, i=i: np.arcsin(preprocess_x(x, i))) circuit.add_input_RZ_gate( i, lambda x, i=i: np.arccos(preprocess_x(x, i) * preprocess_x(x, i)) ) zyu = list(range(n_qubit)) for _ in range(c_depth): rng.shuffle(zyu) for i in range(0, n_qubit - 1, 2): circuit.circuit.add_CNOT_gate(zyu[i + 1], zyu[i]) circuit.add_parametric_RX_gate(zyu[i]) circuit.add_parametric_RY_gate(zyu[i]) circuit.circuit.add_CNOT_gate(zyu[i + 1], zyu[i]) circuit.add_parametric_RY_gate(zyu[i]) circuit.add_parametric_RX_gate(zyu[i]) return circuit
[docs]def create_ibm_embedding_circuit(n_qubit: int) -> LearningCircuit: """create circuit proposed in https://arxiv.org/abs/1802.06002. Args: n_qubits: number of qubits """ def preprocess_x(x: NDArray[np.float64], index: int) -> float: xa: float = x[index % len(x)] return xa circuit = LearningCircuit(n_qubit) for i in range(n_qubit): circuit.add_H_gate(i) for i in range(n_qubit): j = (i + 1) % n_qubit circuit.add_input_RZ_gate(i, lambda x, i=i: preprocess_x(x, i)) circuit.add_CNOT_gate(i, j) circuit.add_input_RZ_gate( j, lambda x, i=i: (np.pi - preprocess_x(x, i) * (np.pi - preprocess_x(x, j))) ) circuit.add_CNOT_gate(i, j) for i in range(n_qubit): circuit.add_H_gate(i) for i in range(n_qubit): j = (i + 1) % n_qubit circuit.add_input_RZ_gate(i, lambda x, i=i: preprocess_x(x, i)) circuit.add_CNOT_gate(i, j) circuit.add_input_RZ_gate( j, lambda x, i=i: (np.pi - preprocess_x(x, i) * (np.pi - preprocess_x(x, j))) ) circuit.add_CNOT_gate(i, j) return circuit
[docs]def create_dqn_cl(n_qubit: int, c_depth: int, s_qubit: int) -> LearningCircuit: # from https://arxiv.org/abs/2112.15002 def preprocess_x(x: np.ndarray, i: int) -> float: xa = x[i % len(x)] clamped = min(1, max(-1, xa)) return clamped circuit = LearningCircuit(n_qubit) for i in range(n_qubit): circuit.add_input_RY_gate(i, lambda x, i=i: preprocess_x(x, i)) circuit.add_parametric_RY_gate(i) for _ in range(c_depth): for i in range(n_qubit): # 元の論文ではすべての組に対して張っていたっぽいが、それはゲート数が多すぎるだろ circuit.add_gate(CZ(i, (i + 1) % n_qubit)) for i in range(s_qubit, n_qubit - 1): circuit.add_gate(CNOT(i, (i + 1) % n_qubit)) circuit.add_gate(CNOT(n_qubit - 1, s_qubit)) for i in range(n_qubit): circuit.add_parametric_RX_gate(i) circuit.add_parametric_RY_gate(i) circuit.add_parametric_RX_gate(i) return circuit
[docs]def create_dqn_cl_no_cz(n_qubit: int, c_depth: int) -> LearningCircuit: # from https://arxiv.org/abs/2112.15002 def preprocess_x(x: np.ndarray, i: int) -> float: xa = x[i % len(x)] clamped = min(1, max(-1, xa)) return clamped circuit = LearningCircuit(n_qubit) for i in range(n_qubit): circuit.add_input_RY_gate(i, lambda x, i=i: preprocess_x(x, i)) circuit.add_parametric_RY_gate(i) for _ in range(c_depth): for i in range(n_qubit): circuit.add_gate(CNOT(i, (i + 1) % n_qubit)) circuit.add_parametric_RX_gate(i) circuit.add_parametric_RY_gate(i) circuit.add_parametric_RX_gate(i) circuit.add_gate(CNOT(n_qubit - 1, 0)) return circuit
[docs]def create_qcnn_ansatz(n_qubit: int, seed: Optional[int] = 0) -> LearningCircuit: """Creates circuit used in https://www.tensorflow.org/quantum/tutorials/qcnn?hl=en, Section 1. Args: n_qubit: number of qubits. must be even. seed: seed for random numbers. used for determining the interaction strength of the hamiltonian simulation """ rng = default_rng(seed) def one_qubit_unitary(circuit: LearningCircuit, index: int) -> List[int]: ids = [] id = circuit.add_parametric_RX_gate(index) ids.append(id) id = circuit.add_parametric_RY_gate(index) ids.append(id) id = circuit.add_parametric_RZ_gate(index) ids.append(id) return ids def _two_qubit_unitary( circuit: LearningCircuit, targets: List[int], pauli_ids: List[int] ) -> LearningCircuit: circuit.add_parametric_multi_Pauli_rotation_gate(targets, pauli_ids) return circuit def two_qubit_unitary(circuit: LearningCircuit, src: int, dest: int) -> LearningCircuit: one_qubit_unitary(circuit, src) one_qubit_unitary(circuit, dest) targets = [src, dest] pauli_xx_ids = [1, 1] circuit = _two_qubit_unitary(circuit, targets, pauli_xx_ids) pauli_yy_ids = [2, 2] circuit = _two_qubit_unitary(circuit, targets, pauli_yy_ids) pauli_zz_ids = [3, 3] circuit = _two_qubit_unitary(circuit, targets, pauli_zz_ids) one_qubit_unitary(circuit, src) one_qubit_unitary(circuit, dest) return circuit def conv_circuit(circuit: LearningCircuit, src: int, dest: int) -> LearningCircuit: return two_qubit_unitary(circuit, src, dest) def pooling_circuit(circuit: LearningCircuit, src: int, dest: int) -> LearningCircuit: ids = one_qubit_unitary(circuit, dest) one_qubit_unitary(circuit, src) circuit.add_CNOT_gate(src, dest) circuit.add_parametric_RZ_gate(dest, share_with=ids[2], share_with_coef=-1) circuit.add_parametric_RY_gate(dest, share_with=ids[1], share_with_coef=-1) circuit.add_parametric_RX_gate(dest, share_with=ids[0], share_with_coef=-1) return circuit circuit = LearningCircuit(n_qubit) for i in range(n_qubit): circuit.add_input_RX_gate(i, lambda x: x[0]) # cluster state for i in range(n_qubit): circuit.add_H_gate(i) for this_bit in range(n_qubit): next_bit = (this_bit + 1) if this_bit < n_qubit - 1 else 0 circuit.add_CNOT_gate(this_bit, next_bit) circuit.add_Z_gate(next_bit) targets = [] def tree(ns: List[int]) -> Optional[dict]: n = len(ns) if n <= 0: return None node = {} node["ns"] = ns left = tree(ns[: n // 2]) right = tree(ns[n - (n // 2) :]) if left is not None and right is not None: targets.append([max(left["ns"]), max(right["ns"])]) return node tree([x for x in range(n_qubit)]) for t in targets: circuit = conv_circuit(circuit, t[0], t[1]) circuit = pooling_circuit(circuit, t[0], t[1]) return circuit