Source code for qutip_qip.compiler.circuitqedcompiler

import numpy as np

from ..circuit import Gate
from ..compiler import GateCompiler, Instruction


__all__ = ["SCQubitsCompiler"]


[docs]class SCQubitsCompiler(GateCompiler): """ Compiler for :class:`.SCQubits`. Compiled pulse strength is in the unit of GHz. Supported native gates: "RX", "RY", "CNOT". Default configuration (see :obj:`.GateCompiler.args` and :obj:`.GateCompiler.compile`): +-----------------+-----------------------+ | key | value | +=================+=======================+ | ``shape`` | ``hann`` | +-----------------+-----------------------+ |``num_samples`` | 1000 | +-----------------+-----------------------+ |``params`` | Hardware Parameters | +-----------------+-----------------------+ Parameters ---------- num_qubits: int The number of qubits in the system. params: dict A Python dictionary contains the name and the value of the parameters. See :meth:`.SCQubitsModel` for the definition. Attributes ---------- num_qubits: int The number of the component systems. params: dict A Python dictionary contains the name and the value of the parameters, such as laser frequency, detuning etc. gate_compiler: dict The Python dictionary in the form of {gate_name: decompose_function}. It saves the decomposition scheme for each gate. Examples -------- >>> import numpy as np >>> from qutip_qip.circuit import QubitCircuit >>> from qutip_qip.device import ModelProcessor, SCQubitsModel >>> from qutip_qip.compiler import SCQubitsCompiler >>> >>> qc = QubitCircuit(2) >>> qc.add_gate("CNOT", targets=0, controls=1) >>> >>> model = SCQubitsModel(2) >>> processor = ModelProcessor(model=model) >>> compiler = SCQubitsCompiler(2, params=model.params) >>> processor.load_circuit(qc, compiler=compiler); # doctest: +SKIP Notice that the above example is equivalent to using directly the :obj:`.SCQubits`. """ def __init__(self, num_qubits, params): super(SCQubitsCompiler, self).__init__(num_qubits, params=params) self.gate_compiler.update( { "RY": self.ry_compiler, "RX": self.rx_compiler, "CNOT": self.cnot_compiler, } ) self.args = { # Default configuration "shape": "hann", "num_samples": 1000, "params": self.params, } def _normalized_gauss_pulse(self): """ Return a truncated and normalize Gaussian curve. The returned pulse is truncated from a Gaussian distribution with -3*sigma < t < 3*sigma. The amplitude is shifted so that the pulse start from 0. In addition, the pulse is normalized so that the total integral area is 1. """ # td normalization so that the total integral area is 1 td = 2.4384880692912567 sigma = 1 / 6 * td # 3 sigma tlist = np.linspace(0, td, 1000) max_pulse = 1 - np.exp(-((0 - td / 2) ** 2) / 2 / sigma**2) coeff = ( np.exp(-((tlist - td / 2) ** 2) / 2 / sigma**2) - np.exp(-((0 - td / 2) ** 2) / 2 / sigma**2) ) / max_pulse return tlist, coeff def _rotation_compiler(self, gate, op_label, param_label, args): """ Single qubit rotation compiler. Parameters ---------- gate : :obj:`.Gate`: The quantum gate to be compiled. op_label : str Label of the corresponding control Hamiltonian. param_label : str Label of the hardware parameters saved in :obj:`GateCompiler.params`. args : dict The compilation configuration defined in the attributes :obj:`.GateCompiler.args` or given as a parameter in :obj:`.GateCompiler.compile`. Returns ------- A list of :obj:`.Instruction`, including the compiled pulse information for this gate. """ targets = gate.targets coeff, tlist = self.generate_pulse_shape( args["shape"], args["num_samples"], maximum=self.params[param_label][targets[0]], area=gate.arg_value / 2.0 / np.pi, ) pulse_info = [(op_label + str(targets[0]), coeff)] return [Instruction(gate, tlist, pulse_info)]
[docs] def ry_compiler(self, gate, args): """ Compiler for the RZ gate Parameters ---------- gate : :obj:`.Gate`: The quantum gate to be compiled. args : dict The compilation configuration defined in the attributes :obj:`.GateCompiler.args` or given as a parameter in :obj:`.GateCompiler.compile`. Returns ------- A list of :obj:`.Instruction`, including the compiled pulse information for this gate. """ return self._rotation_compiler(gate, "sy", "omega_single", args)
[docs] def rx_compiler(self, gate, args): """ Compiler for the RX gate Parameters ---------- gate : :obj:`.Gate`: The quantum gate to be compiled. args : dict The compilation configuration defined in the attributes :obj:`.GateCompiler.args` or given as a parameter in :obj:`.GateCompiler.compile`. Returns ------- A list of :obj:`.Instruction`, including the compiled pulse information for this gate. """ return self._rotation_compiler(gate, "sx", "omega_single", args)
[docs] def cnot_compiler(self, gate, args): """ Compiler for CNOT gate using the cross resonance iteraction. See https://journals.aps.org/prb/abstract/10.1103/PhysRevB.81.134507 for reference. Parameters ---------- gate : :obj:`.Gate`: The quantum gate to be compiled. args : dict The compilation configuration defined in the attributes :obj:`.GateCompiler.args` or given as a parameter in :obj:`.GateCompiler.compile`. Returns ------- A list of :obj:`.Instruction`, including the compiled pulse information for this gate. """ result = [] q1 = gate.controls[0] q2 = gate.targets[0] gate1 = Gate("RX", q2, arg_value=-np.pi / 2) result += self.gate_compiler[gate1.name](gate1, args) zx_coeff = self.params["zx_coeff"][q1] tlist, coeff = self._normalized_gauss_pulse() amplitude = zx_coeff area = 1 / 2 sign = np.sign(amplitude) * np.sign(area) tlist = tlist / amplitude * area * sign coeff = coeff * amplitude * sign coeff, tlist = self.generate_pulse_shape( args["shape"], args["num_samples"], maximum=zx_coeff, area=area ) pulse_info = [("zx" + str(q1) + str(q2), coeff)] result += [Instruction(gate, tlist, pulse_info)] gate3 = Gate("RX", q1, arg_value=-np.pi / 2) result += self.gate_compiler[gate3.name](gate3, args) gate4 = Gate("RY", q1, arg_value=-np.pi / 2) result += self.gate_compiler[gate4.name](gate4, args) gate5 = Gate("RX", q1, arg_value=np.pi / 2) result += self.gate_compiler[gate5.name](gate5, args) return result