diff --git a/doc/source/qip-processor.rst b/doc/source/qip-processor.rst index 934046ecc..19f5590dd 100644 --- a/doc/source/qip-processor.rst +++ b/doc/source/qip-processor.rst @@ -191,6 +191,9 @@ To let it find the optimal pulses, we need to give the parameters for :func:`~qu Compiler and scheduler ====================== +Compiler +-------- + In order to simulate quantum circuits at the level of time evolution. We need to first compile the circuit into the Hamiltonian model, i.e. the control pulses. @@ -236,6 +239,9 @@ The second one is turned on from ``t=1`` to ``t=2`` with the same strength. The compiled pulse here is different from what is shown in the plot in the previous subsection because the scheduler is turned off by default. +Scheduler +--------- + The scheduler is implemented in the class :class:`.compiler.Scheduler`, based on the idea of https://doi.org/10.1117/12.666419. It schedules the order of quantum gates and instructions for the @@ -287,6 +293,27 @@ one will need to wrap the :class:`.Gate` object with :class:`.compiler.instructi with a parameter `duration`. The result will then be the start time of each instruction. +Pulse shape +----------- + +Apart from square pulses, compilers also support different pulse shapes. +All pulse shapes from `SciPy window functions `_ that do not require additional parameters are supported. +The method :obj:`.GateCompiler.generate_pulse_shape` allows one to generate pulse shapes that fulfil the given maximum intensity and the total integral area. + +.. plot:: + + from qutip_qip.compiler import GateCompiler + compiler = GateCompiler() + coeff, tlist = compiler.generate_pulse_shape( + "hann", 1000, maximum=2., area=1.) + fig, ax = plt.subplots(figsize=(4,2)) + ax.plot(tlist, coeff) + ax.set_xlabel("Time") + fig.show() + +For predefined compilers, the compiled pulse shape can also be configured by the key word ``"shape"`` and ``"num_samples"`` in the dictionary attribute :attr:`.GateCompiler.args` +or the ``args`` parameter of :obj:`.GateCompiler.compile`. + Noise Simulation ================ diff --git a/src/qutip_qip/compiler/cavityqedcompiler.py b/src/qutip_qip/compiler/cavityqedcompiler.py index ca593eb4b..eb597f9c9 100644 --- a/src/qutip_qip/compiler/cavityqedcompiler.py +++ b/src/qutip_qip/compiler/cavityqedcompiler.py @@ -1,4 +1,6 @@ +from functools import partial import numpy as np +import scipy from ..circuit import QubitCircuit from ..operations import Gate @@ -10,8 +12,22 @@ class CavityQEDCompiler(GateCompiler): """ - Decompose a :class:`.QubitCircuit` into - the pulse sequence for the processor. + Compiler for :obj:`.DispersiveCavityQED`. + Compiled pulse strength is in the unit of GHz. + + Supported native gates: "RX", "RY", "RZ", "ISWAP", "SQRTISWAP", + "GLOBALPHASE". + + Default configuration (see :obj:`.GateCompiler.args` and + :obj:`.GateCompiler.compile`): + + +-----------------+--------------------+ + | key | value | + +=================+====================+ + | ``shape`` | ``rectangular`` | + +-----------------+--------------------+ + |``params`` | Hardware Parameters| + +-----------------+--------------------+ Parameters ---------- @@ -47,44 +63,85 @@ def __init__( self.Delta = self.wq - self.params["w0"] self.global_phase = global_phase - def rz_compiler(self, gate, args): + def _rotation_compiler(self, gate, op_label, param_label, args): """ - Compiler for the RZ gate + Single qubit gate 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 - g = self.params["sz"][targets[0]] - coeff = np.sign(gate.arg_value) * g - tlist = abs(gate.arg_value) / (2*g) / np.pi / 2 - pulse_info = [("sz" + str(targets[0]), coeff)] + coeff, tlist = self.generate_pulse_shape( + args["shape"], args["num_samples"], + maximum=self.params[param_label][targets[0]], + # The operator is Pauli Z/X/Y, without 1/2. + area=gate.arg_value / 2. / np.pi * 0.5) + pulse_info = [(op_label + str(targets[0]), coeff)] return [Instruction(gate, tlist, pulse_info)] - def rx_compiler(self, gate, args): + def rz_compiler(self, gate, args): """ - Compiler for the RX gate + 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. """ - targets = gate.targets - g = self.params["sx"][targets[0]] - coeff = np.sign(gate.arg_value) * g - tlist = abs(gate.arg_value) / (2*g) / np.pi / 2 - pulse_info = [("sx" + str(targets[0]), coeff)] - return [Instruction(gate, tlist, pulse_info)] + return self._rotation_compiler(gate, "sz", "sz", args) - def sqrtiswap_compiler(self, gate, args): + def rx_compiler(self, gate, args): """ - Compiler for the SQRTISWAP gate + Compiler for the RX gate - Notes - ----- - This version of sqrtiswap_compiler has very low fidelity, please use - iswap + 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. """ - # FIXME This decomposition has poor behaviour + return self._rotation_compiler(gate, "sx", "sx", args) + + def _swap_compiler(self, gate, area, correction_angle, args): q1, q2 = gate.targets pulse_info = [] pulse_name = "sz" + str(q1) coeff = self.wq[q1] - self.params["w0"] pulse_info += [(pulse_name, coeff)] - pulse_name = "sz" + str(q1) + pulse_name = "sz" + str(q2) coeff = self.wq[q2] - self.params["w0"] pulse_info += [(pulse_name, coeff)] pulse_name = "g" + str(q1) @@ -95,55 +152,69 @@ def sqrtiswap_compiler(self, gate, args): pulse_info += [(pulse_name, coeff)] J = self.params["g"][q1] * self.params["g"][q2] * ( - 1 / self.Delta[q1] + 1 / self.Delta[q2]) / 2 - tlist = (4 * np.pi / abs(J)) / 8 / np.pi / 2 + 1. / self.Delta[q1] + 1. / self.Delta[q2]) / 2. + coeff, tlist = self.generate_pulse_shape( + args["shape"], args["num_samples"], maximum=J, area=area) instruction_list = [Instruction(gate, tlist, pulse_info)] # corrections - gate1 = Gate("RZ", [q1], None, arg_value=-np.pi/4) - compiled_gate1 = self.rz_compiler(gate1, args) + gate1 = Gate("RZ", [q1], None, arg_value=correction_angle) + compiled_gate1 = self.gate_compiler["RZ"](gate1, args) instruction_list += compiled_gate1 - gate2 = Gate("RZ", [q2], None, arg_value=-np.pi/4) - compiled_gate2 = self.rz_compiler(gate2, args) + gate2 = Gate("RZ", [q2], None, arg_value=correction_angle) + compiled_gate2 = self.gate_compiler["RZ"](gate2, args) instruction_list += compiled_gate2 - gate3 = Gate("GLOBALPHASE", None, None, arg_value=-np.pi/4) + gate3 = Gate("GLOBALPHASE", None, None, arg_value=correction_angle) self.globalphase_compiler(gate3, args) return instruction_list - def iswap_compiler(self, gate, args): - """ - Compiler for the ISWAP gate + def sqrtiswap_compiler(self, gate, args): """ - q1, q2 = gate.targets - pulse_info = [] - pulse_name = "sz" + str(q1) - coeff = self.wq[q1] - self.params["w0"] - pulse_info += [(pulse_name, coeff)] - pulse_name = "sz" + str(q2) - coeff = self.wq[q2] - self.params["w0"] - pulse_info += [(pulse_name, coeff)] - pulse_name = "g" + str(q1) - coeff = self.params["g"][q1] - pulse_info += [(pulse_name, coeff)] - pulse_name = "g" + str(q2) - coeff = self.params["g"][q2] - pulse_info += [(pulse_name, coeff)] + Compiler for the SQRTISWAP 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. - J = self.params["g"][q1] * self.params["g"][q2] * ( - 1 / self.Delta[q1] + 1 / self.Delta[q2]) / 2 - tlist = (4 * np.pi / abs(J)) / 4 / np.pi / 2 - instruction_list = [Instruction(gate, tlist, pulse_info)] + Notes + ----- + This version of sqrtiswap_compiler has very low fidelity, please use + iswap + """ + # FIXME This decomposition has poor behaviour. + return self._swap_compiler( + gate, area=1/4, correction_angle=-np.pi/4, args=args) - # corrections - gate1 = Gate("RZ", [q1], None, arg_value=-np.pi/2.) - compiled_gate1 = self.rz_compiler(gate1, args) - instruction_list += compiled_gate1 - gate2 = Gate("RZ", [q2], None, arg_value=-np.pi/2) - compiled_gate2 = self.rz_compiler(gate2, args) - instruction_list += compiled_gate2 - gate3 = Gate("GLOBALPHASE", None, None, arg_value=-np.pi/2) - self.globalphase_compiler(gate3, args) - return instruction_list + def iswap_compiler(self, gate, args): + """ + Compiler for the ISWAP 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._swap_compiler( + gate, area=1/2, correction_angle=-np.pi/2, args=args) def globalphase_compiler(self, gate, args): """ diff --git a/src/qutip_qip/compiler/circuitqedcompiler.py b/src/qutip_qip/compiler/circuitqedcompiler.py index 32af4f5e3..e819bfd12 100644 --- a/src/qutip_qip/compiler/circuitqedcompiler.py +++ b/src/qutip_qip/compiler/circuitqedcompiler.py @@ -11,14 +11,34 @@ 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 | + +-----------------+-----------------------+ """ def __init__(self, num_qubits, params): super(SCQubitsCompiler, self).__init__(num_qubits, params=params) self.gate_compiler.update({ - "RX": self.single_qubit_compiler, - "RY": self.single_qubit_compiler, + "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): """ @@ -32,37 +52,105 @@ def _normalized_gauss_pulse(self): # td normalization so that the total integral area is 1 td = 2.4384880692912567 sigma = 1/6 * td # 3 sigma - tlist = np.linspace(0, td, 100) + 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 + 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 single_qubit_compiler(self, gate, args): + def _rotation_compiler(self, gate, op_label, param_label, args): """ - Compiler for the RX and RY gate. + Single qubit gate 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:`SCQubitsCompiler.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 - omega_single = self.params["omega_single"][targets[0]] - tlist, coeff = self._normalized_gauss_pulse() - sign = np.sign(omega_single) * np.sign(gate.arg_value) - tlist = tlist / omega_single * gate.arg_value/np.pi/2 * sign - coeff = coeff * omega_single * sign - if gate.name == "RY": - pulse_prefix = "sy" - elif gate.name == "RX": - pulse_prefix = "sx" - else: - raise ValueError(f"Gate {gate.name} cnot not be compiled.") - pulse_info = [(pulse_prefix + str(targets[0]), coeff)] + coeff, tlist = self.generate_pulse_shape( + args["shape"], args["num_samples"], + maximum=self.params[param_label][targets[0]], + area=gate.arg_value / 2. / np.pi) + pulse_info = [(op_label + str(targets[0]), coeff)] return [Instruction(gate, tlist, pulse_info)] + 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) + + 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) + 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 + 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] @@ -78,6 +166,8 @@ def cnot_compiler(self, gate, args): 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)] diff --git a/src/qutip_qip/compiler/gatecompiler.py b/src/qutip_qip/compiler/gatecompiler.py index 5e0a5b5de..85d81029c 100644 --- a/src/qutip_qip/compiler/gatecompiler.py +++ b/src/qutip_qip/compiler/gatecompiler.py @@ -1,9 +1,11 @@ +import warnings import numpy as np +from scipy import signal + from .instruction import Instruction from .scheduler import Scheduler from ..circuit import QubitCircuit from ..operations import Gate -import warnings __all__ = ['GateCompiler'] @@ -36,8 +38,18 @@ class GateCompiler(object): Note that for continuous pulse, the first coeff should always be 0. args: dict - Arguments for individual compiling routines. - It adds more flexibility in customizing compiler. + The compilation configurations. + It will be passed to each compiling functions. + Available arguments: + + * ``shape``: The compiled pulse shape. ``rectangular`` or + one of the `SciPy window functions + `_. + * ``num_samples``: + Number of samples for continuous pulses. + It has no effect for rectangular pulses. + * ``params``: Hardware parameters computed in the :obj:`Processor`. + """ def __init__(self, num_qubits=None, params=None, pulse_dict=None, N=None): self.gate_compiler = {} @@ -48,8 +60,11 @@ def __init__(self, num_qubits=None, params=None, pulse_dict=None, N=None): "GLOBALPHASE": self.globalphase_compiler, "IDLE": self.idle_compiler } - self.args = {} - self.args.update({"params": self.params}) + self.args = { # Default configuration + "shape": "rectangular", + "num_samples": None, + "params": self.params, + } self.global_phase = 0. if pulse_dict is not None: warnings.warn( @@ -287,3 +302,87 @@ def _process_idling_tlist( # idling until the start time idling_tlist.append([start_time]) return np.concatenate(idling_tlist) + + @classmethod + def generate_pulse_shape(cls, window, num_samples, maximum=1., area=1.): + """ + Return a tuple consisting of a coeff list and a time sequence + according to a given window function. + + Parameters + ---------- + window : str + The name ``"rectangular"`` for constant pulse or + the name of a Scipy window function. + See + `the Scipy documentation + `_ + for detail. + num_samples : int + The number of the samples of the coefficients. + maximum : float + The maximum of the coefficients. + The absolute value will be used if negative. + area : float + The total area if one integrates coeff as a function of the time. + If the area is negative, the pulse is flipped vertically + (i.e. the pulse is multiplied by the sign of the area). + + Returns + ------- + coeff, tlist : + If the default window ``"shape"="rectangular"`` is used, + both are float numbers. + If Scipy window functions are used, both are a 1-dimensional numpy + array with the same size. + + Notes + ----- + If Scipy window functions are used, it is suggested to set + ``Processor.pulse_mode`` to ``"continuous"``. + Also, finite number of samples will also make + the total integral of the coefficients slightly deviate from ``area``. + """ + coeff, tlist = _normalized_window(window, num_samples) + sign = np.sign(area) + coeff *= np.abs(maximum) * sign + tlist *= abs(area) / np.abs(maximum) + return coeff, tlist + + +""" +Normalized scipy window functions. +The scipy implementation only makes sure that it is maximum is 1. +Here, we save a default t_max so that the integral is always 1. +""" +_default_window_t_max = { + "boxcar": 1., + "triang": 2., + "blackman": 1./0.42, + "hamming": 1./0.54, + "hann": 2., + "bartlett": 2., + "flattop": 1./0.21557897160000217, + "parzen": 1./0.375, + "bohman": 1./0.4052847750978287, + "blackmanharris": 1./0.35875003586900384, + "nuttall": 1./0.36358193632191405, + "barthann": 2., + "cosine": np.pi/2., + } + + +def _normalized_window(window, num_samples): + """ + Return a normalized window functions. + """ + if window == "rectangular": + return 1., 1. + t_max = _default_window_t_max.get(window, None) + if t_max is None: + raise ValueError(f"Window function {window} is not supported.") + coeff = signal.windows.get_window( + window, num_samples + ) + tlist = np.linspace(0, t_max, num_samples) + return coeff, tlist diff --git a/src/qutip_qip/compiler/spinchaincompiler.py b/src/qutip_qip/compiler/spinchaincompiler.py index d7f253277..63b9c8749 100644 --- a/src/qutip_qip/compiler/spinchaincompiler.py +++ b/src/qutip_qip/compiler/spinchaincompiler.py @@ -1,3 +1,4 @@ +from functools import partial import numpy as np from ..circuit import QubitCircuit @@ -10,8 +11,22 @@ class SpinChainCompiler(GateCompiler): """ - Compile a :class:`.QubitCircuit` into - the pulse sequence for the processor. + Compiler for :obj:`.SpinChain`. + Compiled pulse strength is in the unit of MHz. + + Supported native gates: "RX", "RY", "RZ", "ISWAP", "SQRTISWAP", + "GLOBALPHASE". + + Default configuration (see :obj:`.GateCompiler.args` and + :obj:`.GateCompiler.compile`): + + +-----------------+--------------------+ + | key | value | + +=================+====================+ + | ``shape`` | ``rectangular`` | + +-----------------+--------------------+ + |``params`` | Hardware Parameters| + +-----------------+--------------------+ Parameters ---------- @@ -70,37 +85,85 @@ def __init__( }) self.global_phase = global_phase - def rz_compiler(self, gate, args): + def _rotation_compiler(self, gate, op_label, param_label, args): """ - Compiler for the RZ gate + Single qubit gate 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 - g = self.params["sz"][targets[0]] - coeff = np.sign(gate.arg_value) * g - tlist = abs(gate.arg_value) / (2 * g) / np.pi / 2 - pulse_info = [("sz" + str(targets[0]), coeff)] + coeff, tlist = self.generate_pulse_shape( + args["shape"], args["num_samples"], + maximum=self.params[param_label][targets[0]], + # The operator is Pauli Z/X/Y, without 1/2. + area=gate.arg_value / 2. / np.pi * 0.5) + pulse_info = [(op_label + str(targets[0]), coeff)] return [Instruction(gate, tlist, pulse_info)] - def rx_compiler(self, gate, args): + def rz_compiler(self, gate, args): """ - Compiler for the RX gate + 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. """ - targets = gate.targets - g = self.params["sx"][targets[0]] - coeff = np.sign(gate.arg_value) * g - tlist = abs(gate.arg_value) / (2 * g) / np.pi / 2 - pulse_info = [("sx" + str(targets[0]), coeff)] - return [Instruction(gate, tlist, pulse_info)] + return self._rotation_compiler(gate, "sz", "sz", args) - def iswap_compiler(self, gate, args): + def rx_compiler(self, gate, args): """ - Compiler for the ISWAP gate + 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", "sx", args) + + def _swap_compiler(self, gate, area, args): targets = gate.targets q1, q2 = min(targets), max(targets) g = self.params["sxsy"][q1] - coeff = -g - tlist = np.pi / (4 * g) / np.pi / 2 + maximum = g + coeff, tlist = self.generate_pulse_shape( + args["shape"], args["num_samples"], maximum, area) if self.N != 2 and q1 == 0 and q2 == self.N - 1: pulse_name = "g" + str(q2) else: @@ -108,21 +171,45 @@ def iswap_compiler(self, gate, args): pulse_info = [(pulse_name, coeff)] return [Instruction(gate, tlist, pulse_info)] + def iswap_compiler(self, gate, args): + """ + Compiler for the ISWAP 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._swap_compiler(gate, area=-1/8, args=args) + def sqrtiswap_compiler(self, gate, args): """ - Compiler for the SQRTISWAP gate + Compiler for the SQRTISWAP 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. """ - targets = gate.targets - q1, q2 = min(targets), max(targets) - g = self.params["sxsy"][q1] - coeff = -g - tlist = np.pi / (8 * g) / np.pi / 2 - if self.N != 2 and q1 == 0 and q2 == self.N - 1: - pulse_name = "g" + str(q2) - else: - pulse_name = "g" + str(q1) - pulse_info = [(pulse_name, coeff)] - return [Instruction(gate, tlist, pulse_info)] + return self._swap_compiler(gate, area=-1/16, args=args) def globalphase_compiler(self, gate, args): """ diff --git a/src/qutip_qip/device/cavityqed.py b/src/qutip_qip/device/cavityqed.py index 8c90cd24f..7b2a58903 100644 --- a/src/qutip_qip/device/cavityqed.py +++ b/src/qutip_qip/device/cavityqed.py @@ -157,7 +157,6 @@ def set_up_params(self): if any((w0 - self.wq)/(w0 + self.wq) > 0.05): warnings.warn( "The rotating-wave approximation might not be valid.") - print(self.params) @property def sx_ops(self): diff --git a/tests/test_compiler.py b/tests/test_compiler.py index feff8b31e..0fdc5c47a 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -1,6 +1,7 @@ import pytest import numpy as np from numpy.testing import assert_array_equal +from qutip_qip.compiler.gatecompiler import _default_window_t_max from qutip_qip.device import ( DispersiveCavityQED, CircularSpinChain, LinearSpinChain) @@ -151,3 +152,21 @@ def test_compiler_result_format(): processor.set_all_tlist(tlist) assert_array_equal(processor.pulses[0].coeff, coeffs[0]) assert_array_equal(processor.pulses[0].tlist, tlist[0]) + + +@pytest.mark.parametrize("shape", _default_window_t_max.keys()) +def test_pulse_shape(shape): + """Test different pulse shape functions""" + num_qubits = 1 + circuit = QubitCircuit(num_qubits) + circuit.add_gate("X", 0) + processor = LinearSpinChain(num_qubits) + compiler = SpinChainCompiler(num_qubits, processor.params) + compiler.args.update({"shape": shape, "num_samples": 100}) + processor.load_circuit(circuit, compiler=compiler) + processor.pulse_mode = "continuous" + init_state = basis(2, 0) + num_result = processor.run_state(init_state).states[-1] + ideal_result = circuit.run(init_state) + ifid = 1 - fidelity(num_result, ideal_result) + assert(pytest.approx(ifid, abs=0.01) == 0)