Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RCCX and RC3X Gates #164

1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"tqdm>=4.56.0",
"setuptools>=52.0.0",
"torch>=1.8.0",
"torchdiffeq>=0.2.3",
"torchpack>=0.3.0",
"qiskit==0.38.0",
"matplotlib>=3.3.2",
Expand Down
2 changes: 2 additions & 0 deletions test/operators/test_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
# {'qiskit': qiskit_gate.?, 'tq': tq.CU2},
{"qiskit": qiskit_gate.CU3Gate, "tq": tq.CU3},
{"qiskit": qiskit_gate.ECRGate, "tq": tq.ECR},
{"qiskit": qiskit_gate.RCCXGate, "tq": tq.RCCX},
{"qiskit": qiskit_gate.RC3XGate, "tq": tq.RC3X},
]

import os
Expand Down
127 changes: 127 additions & 0 deletions torchquantum/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
"reset",
"ecr",
"echoedcrossresonance",
"rccx",
"rc3x",
]


Expand Down Expand Up @@ -1166,6 +1168,40 @@ def singleexcitation_matrix(params):
],
dtype=C_DTYPE,
),
"rccx": torch.tensor(
[
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, -1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, -1j],
[0, 0, 0, 0, 0, 0, 1j, 0],
],
dtype=C_DTYPE,
),
"rc3x": torch.tensor(
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1j, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1j, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0],
],
dtype=C_DTYPE,
),
"ecr": torch.tensor(
[[0, 0, 1, 1j], [0, 0, 1j, 1], [1, -1j, 0, 0], [-1j, 1, 0, 0]], dtype=C_DTYPE
)
Expand Down Expand Up @@ -3220,6 +3256,95 @@ def ecr(
inverse=inverse,
)

def rccx(
q_device,
wires,
params=None,
n_wires=None,
static=False,
parent_graph=None,
inverse=False,
comp_method="bmm",
):
"""Perform the rccx (simplified Toffoli) gate.

Args:
q_device (tq.QuantumDevice): The QuantumDevice.
wires (Union[List[int], int]): Which qubit(s) to apply the gate.
params (torch.Tensor, optional): Parameters (if any) of the gate.
Default to None.
n_wires (int, optional): Number of qubits the gate is applied to.
Default to None.
static (bool, optional): Whether use static mode computation.
Default to False.
parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
current operation. Default to None.
inverse (bool, optional): Whether inverse the gate. Default to False.
comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
matrix vector multiplication. Default to 'bmm'.

Returns:
None.
"""
name = "rccx"
mat = mat_dict[name]
gate_wrapper(
name=name,
mat=mat,
method=comp_method,
q_device=q_device,
wires=wires,
params=params,
n_wires=n_wires,
static=static,
parent_graph=parent_graph,
inverse=inverse,
)

def rc3x(
q_device,
wires,
params=None,
n_wires=None,
static=False,
parent_graph=None,
inverse=False,
comp_method="bmm",
):
"""Perform the rc3x (simplified 3-controlled Toffoli) gate.

Args:
q_device (tq.QuantumDevice): The QuantumDevice.
wires (Union[List[int], int]): Which qubit(s) to apply the gate.
params (torch.Tensor, optional): Parameters (if any) of the gate.
Default to None.
n_wires (int, optional): Number of qubits the gate is applied to.
Default to None.
static (bool, optional): Whether use static mode computation.
Default to False.
parent_graph (tq.QuantumGraph, optional): Parent QuantumGraph of
current operation. Default to None.
inverse (bool, optional): Whether inverse the gate. Default to False.
comp_method (bool, optional): Use 'bmm' or 'einsum' method to perform
matrix vector multiplication. Default to 'bmm'.

Returns:
None.
"""
name = "rc3x"
mat = mat_dict[name]
gate_wrapper(
name=name,
mat=mat,
method=comp_method,
q_device=q_device,
wires=wires,
params=params,
n_wires=n_wires,
static=static,
parent_graph=parent_graph,
inverse=inverse,
)

h = hadamard
sh = shadamard
Expand Down Expand Up @@ -3304,4 +3429,6 @@ def ecr(
"singleexcitation": singleexcitation,
"ecr": ecr,
"echoedcrossresonance": echoedcrossresonance,
"rccx": rccx,
"rc3x": rc3x,
}
15 changes: 8 additions & 7 deletions torchquantum/measurement.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from torchquantum.functional import mat_dict
from torchquantum.operators import op_name_dict
from copy import deepcopy
import matplotlib.pyplot as plt

__all__ = [
"find_observable_groups",
Expand All @@ -32,7 +33,7 @@ def gen_bitstrings(n_wires):
return ["{:0{}b}".format(k, n_wires) for k in range(2**n_wires)]


def measure(qdev, n_shots=1024):
def measure(qdev, n_shots=1024, draw_id=None):
"""Measure the target state and obtain classical bitstream distribution
Args:
q_state: input tq.QuantumDevice
Expand All @@ -58,12 +59,12 @@ def measure(qdev, n_shots=1024):
distri = OrderedDict(sorted(distri.items()))
distri_all.append(distri)

# if draw_id is not None:
# plt.bar(distri_all[draw_id].keys(), distri_all[draw_id].values())
# plt.xticks(rotation="vertical")
# plt.xlabel("bitstring [qubit0, qubit1, ..., qubitN]")
# plt.title("distribution of measured bitstrings")
# plt.show()
if draw_id is not None:
plt.bar(distri_all[draw_id].keys(), distri_all[draw_id].values())
plt.xticks(rotation="vertical")
plt.xlabel("bitstring [qubit0, qubit1, ..., qubitN]")
plt.title("distribution of measured bitstrings")
plt.show()
return distri_all


Expand Down
29 changes: 29 additions & 0 deletions torchquantum/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
"SingleExcitation",
"EchoedCrossResonance",
"ECR",
"RCCX",
"RC3X",
]


Expand Down Expand Up @@ -122,6 +124,8 @@ class Operator(tq.QuantumModule):
"MultiXCNOT",
"Reset",
"EchoedCrossResonance",
"RCCX",
"RC3X",
]

parameterized_ops = [
Expand Down Expand Up @@ -881,6 +885,29 @@ class MultiRZ(DiagonalOperation, metaclass=ABCMeta):
def _matrix(cls, params, n_wires):
return tqf.multirz_matrix(params, n_wires)

class RCCX(Operation, metaclass=ABCMeta):
"""Class for RCCX Gate."""

num_params = 0
num_wires = 3
matrix = mat_dict["rccx"]
func = staticmethod(tqf.rccx)

@classmethod
def _matrix(cls, params):
return cls.matrix

class RC3X(Operation, metaclass=ABCMeta):
"""Class for RC3X Gate."""

num_params = 0
num_wires = 4
matrix = mat_dict["rc3x"]
func = staticmethod(tqf.rc3x)

@classmethod
def _matrix(cls, params):
return cls.matrix

class RXX(Operation, metaclass=ABCMeta):
"""Class for RXX Gate."""
Expand Down Expand Up @@ -1388,4 +1415,6 @@ def _matrix(cls, params):
"singleexcitation": SingleExcitation,
"ecr": ECR,
"echoedcrossresonance": ECR,
"rccx": RCCX,
"rc3x": RC3X,
}
765 changes: 765 additions & 0 deletions torchquantum/pulse/ISCA_tutorial_pulse.ipynb

Large diffs are not rendered by default.

293 changes: 293 additions & 0 deletions torchquantum/pulse/MESolver_example.ipynb

Large diffs are not rendered by default.

443 changes: 443 additions & 0 deletions torchquantum/pulse/SESolver_example.ipynb

Large diffs are not rendered by default.

234 changes: 234 additions & 0 deletions torchquantum/pulse/Two_qubit_simple_example.ipynb

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions torchquantum/pulse/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .utils import *
from .sesolve import sesolve
from .mesolve import mesolve
# from .smesolve import smesolve
1 change: 1 addition & 0 deletions torchquantum/pulse/hardware/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .hardware import hardware
11 changes: 11 additions & 0 deletions torchquantum/pulse/hardware/hardware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import torch
import numpy as np
import torchquantum as tq
import torchdiffeq




class Hardware(torch.nn.Modele):
def __init__(self,):

1 change: 1 addition & 0 deletions torchquantum/pulse/mesolve/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .mesolve import mesolve
67 changes: 67 additions & 0 deletions torchquantum/pulse/mesolve/mesolve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import torch
import math
from ..solver import Solver
from ..utils import *
from torchdiffeq import odeint

def mesolve(
dens0,
H=None,
n_dt=None,
dt=0.22,
*,
L_ops=None,
exp_ops=None,
options=None,
dtype=None,
device=None
):
if options is None:
options = {}

if not 'step_size' in options:
options['step_size'] = 0.001

t_save = torch.tensor(list(range(n_dt)))*dt

args = (H, dens0, t_save, exp_ops, options)

solver = MESolver(*args, L_ops=L_ops)

solver.run()

psi_save, exp_save = solver.y_save, solver.exp_save

return psi_save, exp_save

def _lindblad_helper(L, rho):
Ldag = torch.conj(L)
return L @ rho @ Ldag - 0.5 * Ldag @ L @ rho - 0.5 * rho @ Ldag @ L

def lindbladian(H,rho,L_ops):
if L_ops is None:
return -1j * (H @ rho - rho @ H)

if type(L_ops) is not list:
L_ops = [L_ops]

_dissipator = [_lindblad_helper(L, rho) for L in L_ops]
dissipator = torch.stack(_dissipator)
return -1j * (H @ rho - rho @ H) + dissipator.sum(0)

class MESolver(Solver):

def __init__(self, *args, L_ops):
super().__init__(*args)
self.L_ops = L_ops


def f(self, t, y):
h = self.H(t)
return lindbladian(h,y,self.L_ops)


def run(self):
# self.y_save = odeint(self.f, self.psi0, self.t_save, method='rk4', options=self.options)
self.y_save = odeint(self.f, self.psi0, self.t_save)
self.exp_save = None
Loading
Loading