"""Quasiprobability-decomposition gates"""
from torch import nn
from .gate import Hadamard, PauliX, SDaggerGate, SGate
from .operation import GateQPD, MeasureQPD
[docs]
class SingleGateQPD(GateQPD):
r"""A base class for single-qubit QPD gates.
Args:
bases: The probabilistic basis operations for the decomposition. A nested structure where:
- `bases[i]` is the i-th term of the QPD (corresponding to `coeffs[i]`).
- `bases[i][j]` is the `nn.Sequential` operations applied to the j-th qubit.
coeffs: The coefficients for quasiprobability representation.
label: The label of the gate. Default: ``None``
name: The name of the quantum operation. Default: ``None``
nqubit: The number of qubits that the quantum operation acts on. Default: 1
wires: The indices of the qubits that the quantum operation acts on. Default: ``None``
den_mat: Whether the quantum operation acts on density matrices or state vectors.
Default: ``False`` (which means state vectors)
tsr_mode: Whether the quantum operation is in tensor mode, which means the input and output are represented by
a tensor of shape :math:`(\text{batch}, 2, ..., 2)`. Default: ``False``
"""
def __init__(
self,
bases: 'nn.ModuleList[nn.ModuleList[nn.Sequential]]',
coeffs: list[float],
label: int | None = None,
name: str | None = None,
nqubit: int = 1,
wires: list[int] | None = None,
den_mat: bool = False,
tsr_mode: bool = False,
) -> None:
if wires is None:
wires = [0]
assert len(wires) == 1
for basis in bases:
assert len(basis) == 1
super().__init__(
bases=bases,
coeffs=coeffs,
label=label,
name=name,
nqubit=nqubit,
wires=wires,
den_mat=den_mat,
tsr_mode=tsr_mode,
)
[docs]
class DoubleGateQPD(GateQPD):
r"""A base class for two-qubit QPD gates.
Args:
bases: The probabilistic basis operations for the decomposition. A nested structure where:
- `bases[i]` is the i-th term of the QPD (corresponding to `coeffs[i]`).
- `bases[i][j]` is the `nn.Sequential` operations applied to the j-th qubit.
coeffs: The coefficients for quasiprobability representation.
label: The label of the gate. Default: ``None``
name: The name of the quantum operation. Default: ``None``
nqubit: The number of qubits that the quantum operation acts on. Default: 2
wires: The indices of the qubits that the quantum operation acts on. Default: ``None``
den_mat: Whether the quantum operation acts on density matrices or state vectors.
Default: ``False`` (which means state vectors)
tsr_mode: Whether the quantum operation is in tensor mode, which means the input and output are represented by
a tensor of shape :math:`(\text{batch}, 2, ..., 2)`. Default: ``False``
"""
def __init__(
self,
bases: 'nn.ModuleList[nn.ModuleList[nn.Sequential]]',
coeffs: list[float],
label: int | None = None,
name: str | None = None,
nqubit: int = 2,
wires: list[int] | None = None,
den_mat: bool = False,
tsr_mode: bool = False,
) -> None:
if wires is None:
wires = [0, 1]
assert len(wires) == 2
for basis in bases:
assert len(basis) == 2
super().__init__(
bases=bases,
coeffs=coeffs,
label=label,
name=name,
nqubit=nqubit,
wires=wires,
den_mat=den_mat,
tsr_mode=tsr_mode,
)
[docs]
def decompose(self) -> tuple[SingleGateQPD, SingleGateQPD]:
"""Decompose the gate into two single-qubit QPD gates."""
bases1 = nn.ModuleList([])
bases2 = nn.ModuleList([])
for basis in self.bases:
bases1.append(nn.ModuleList([basis[0]]))
bases2.append(nn.ModuleList([basis[1]]))
name = self.name + f'_label{self.label}_'
gate1 = SingleGateQPD(
bases1, self.coeffs, self.label, name + '1', self.nqubit, [self.wires[0]], self.den_mat, self.tsr_mode
)
gate2 = SingleGateQPD(
bases2, self.coeffs, self.label, name + '2', self.nqubit, [self.wires[1]], self.den_mat, self.tsr_mode
)
return gate1, gate2
[docs]
class MoveQPD(DoubleGateQPD):
r"""QPD representation of the move operation.
Args:
nqubit: The number of qubits that the quantum operation acts on. Default: 2
wires: The indices of the qubits that the quantum operation acts on. Default: ``None``
label: The label of the gate. Default: ``None``
den_mat: Whether the quantum operation acts on density matrices or state vectors.
Default: ``False`` (which means state vectors)
tsr_mode: Whether the quantum operation is in tensor mode, which means the input and output are represented by
a tensor of shape :math:`(\text{batch}, 2, ..., 2)`. Default: ``False``
"""
def __init__(
self,
nqubit: int = 2,
wires: list[int] | None = None,
label: int | None = None,
den_mat: bool = False,
tsr_mode: bool = False,
) -> None:
if wires is None:
wires = [0, 1]
h1 = Hadamard(nqubit=nqubit, wires=wires[0], den_mat=den_mat, tsr_mode=True)
m1 = MeasureQPD(nqubit=nqubit, wires=wires[0])
sdg1 = SDaggerGate(nqubit=nqubit, wires=wires[0], den_mat=den_mat, tsr_mode=True)
x2 = PauliX(nqubit=nqubit, wires=wires[1], den_mat=den_mat, tsr_mode=True)
h2 = Hadamard(nqubit=nqubit, wires=wires[1], den_mat=den_mat, tsr_mode=True)
s2 = SGate(nqubit=nqubit, wires=wires[1], den_mat=den_mat, tsr_mode=True)
measure_i = nn.Sequential()
measure_x = nn.Sequential(h1, m1)
measure_y = nn.Sequential(sdg1, h1, m1)
measure_z = nn.Sequential(m1)
prep_0 = nn.Sequential()
prep_1 = nn.Sequential(x2)
prep_plus = nn.Sequential(h2)
prep_minus = nn.Sequential(x2, h2)
prep_iplus = nn.Sequential(h2, s2)
prep_iminus = nn.Sequential(x2, h2, s2)
bases = nn.ModuleList(
[
nn.ModuleList([measure_i, prep_0]),
nn.ModuleList([measure_i, prep_1]),
nn.ModuleList([measure_x, prep_plus]),
nn.ModuleList([measure_x, prep_minus]),
nn.ModuleList([measure_y, prep_iplus]),
nn.ModuleList([measure_y, prep_iminus]),
nn.ModuleList([measure_z, prep_0]),
nn.ModuleList([measure_z, prep_1]),
]
)
coeffs = [0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5]
super().__init__(
bases=bases,
coeffs=coeffs,
label=label,
name='MoveQPD',
nqubit=nqubit,
wires=wires,
den_mat=den_mat,
tsr_mode=tsr_mode,
)