Skip to content

Commit

Permalink
Add pulse shape
Browse files Browse the repository at this point in the history
Different pulse shapes are generated using window function from scipy.signal.windows.
A generate_pulse_shape method is defined and can be used to generate pulse shape with the arbitrary area and required maximum.
A default single qubit compiler is defined and simplifies the various compiler subclasses.
  • Loading branch information
BoxiLi committed Aug 18, 2021
1 parent 16818cc commit c4f6c95
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 138 deletions.
2 changes: 1 addition & 1 deletion src/qutip_qip/compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from .instruction import Instruction
from .scheduler import Scheduler
from .gatecompiler import GateCompiler
from .gatecompiler import *
from .cavityqedcompiler import CavityQEDCompiler
from .spinchaincompiler import SpinChainCompiler
from .circuitqedcompiler import SCQubitsCompiler
137 changes: 64 additions & 73 deletions src/qutip_qip/compiler/cavityqedcompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
###############################################################################
from functools import partial
import numpy as np
import scipy

from ..circuit import QubitCircuit
from ..operations import Gate
Expand Down Expand Up @@ -71,52 +73,21 @@ def __init__(
self.gate_compiler.update({
"ISWAP": self.iswap_compiler,
"SQRTISWAP": self.sqrtiswap_compiler,
"RZ": self.rz_compiler,
"RX": self.rx_compiler,
"RZ": partial(self.default_single_qubit_compiler, scaling=0.5),
"RX": partial(self.default_single_qubit_compiler, scaling=0.5),
"GLOBALPHASE": self.globalphase_compiler
})
self.wq = np.sqrt(self.params["eps"]**2 + self.params["delta"]**2)
self.Delta = self.wq - self.params["w0"]
self.global_phase = global_phase

def rz_compiler(self, gate, args):
"""
Compiler for the RZ 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)]
return [Instruction(gate, tlist, pulse_info)]

def rx_compiler(self, gate, args):
"""
Compiler for the RX 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)]

def sqrtiswap_compiler(self, gate, args):
"""
Compiler for the SQRTISWAP gate
Notes
-----
This version of sqrtiswap_compiler has very low fidelity, please use
iswap
"""
# FIXME This decomposition has poor behaviour
def _two_qubit_compiler(self, gate_name, 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(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 @@ -127,55 +98,75 @@ 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.
if gate_name == "ISWAP":
area = 1. / 2.
correction_angle = -np.pi / 2.
elif gate_name == "SQRTISWAP":
area = 1. / 4.
correction_angle = -np.pi / 4.
else:
raise ValueError(f"Gate {gate.name} cannot not be compiled.")
coeff, tlist = self.generate_pulse_shape(
args["window"], 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 :qobj:`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._two_qubit_compiler("SQRTISWAP", gate, 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 :qobj:`Instruction`, including the compiled pulse
information for this gate.
"""
return self._two_qubit_compiler("ISWAP", gate, args)

def globalphase_compiler(self, gate, args):
"""
Expand Down
35 changes: 18 additions & 17 deletions src/qutip_qip/compiler/circuitqedcompiler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from functools import partial
import numpy as np

from ..circuit import Gate
Expand All @@ -15,10 +16,17 @@ class SCQubitsCompiler(GateCompiler):
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,
"RX": partial(
self.default_single_qubit_compiler, param_name="omega_single"),
"RY": partial(
self.default_single_qubit_compiler, param_name="omega_single"),
"CNOT": self.cnot_compiler,
})
self.args = { # Default configuration
"window": "hann",
"num_samples": 1000,
"params": self.params,
}

def _normalized_gauss_pulse(self):
"""
Expand All @@ -32,7 +40,7 @@ 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
Expand All @@ -42,20 +50,7 @@ def single_qubit_compiler(self, gate, args):
"""
Compiler for the RX and RY 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)]
return [Instruction(gate, tlist, pulse_info)]
return partial(self.default_single_qubit_compiler,param_name="omega_single")

def cnot_compiler(self, gate, args):
"""
Expand All @@ -78,6 +73,12 @@ def cnot_compiler(self, gate, args):
sign = np.sign(amplitude) * np.sign(area)
tlist = tlist / amplitude * area * sign
coeff = coeff * amplitude * sign
from scipy.integrate import simps
print(amplitude)
print(simps(coeff, tlist))
coeff, tlist = self.generate_pulse_shape(
args["window"], args["num_samples"], maximum=zx_coeff, area=area)
print(simps(coeff, tlist))
pulse_info = [("zx" + str(q1) + str(q2), coeff)]
result += [Instruction(gate, tlist, pulse_info)]

Expand Down

0 comments on commit c4f6c95

Please sign in to comment.