Skip to content

Commit

Permalink
Merge 1a5ba54 into 0cf9aba
Browse files Browse the repository at this point in the history
  • Loading branch information
BoxiLi committed Sep 13, 2021
2 parents 0cf9aba + 1a5ba54 commit 22e3a22
Show file tree
Hide file tree
Showing 7 changed files with 509 additions and 120 deletions.
27 changes: 27 additions & 0 deletions doc/source/qip-processor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 <https://docs.scipy.org/doc/scipy/reference/signal.windows.html>`_ 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
================

Expand Down
194 changes: 132 additions & 62 deletions src/qutip_qip/compiler/cavityqedcompiler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from functools import partial
import numpy as np
import scipy

from ..circuit import QubitCircuit
from ..operations import Gate
Expand All @@ -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
----------
Expand Down Expand Up @@ -47,44 +63,84 @@ 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:`CavityQEDCompiler.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)
Expand All @@ -95,55 +151,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):
"""
Expand Down
Loading

0 comments on commit 22e3a22

Please sign in to comment.