Skip to content

Commit

Permalink
Backend gate set updated. (#23)
Browse files Browse the repository at this point in the history
* Updated Backend gateset, ``required_predicates``, ``rebase_pass`` and ``default_compilation_pass``. Some clean-up.

* Added `RemoveRedundancies` to `default_compilation_pass`.

* Removed some TODO's.

* Removed Cn[X,Y,Z,Ry] gates from the backend gateset (their names don't reflect the number of qubits they are acting on).

* Added a fixture to test multicontrol (3-qubit) gates conversion.
  • Loading branch information
yapolyak authored Jul 26, 2023
1 parent 21f781a commit 7eefaa1
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 54 deletions.
119 changes: 65 additions & 54 deletions pytket/extensions/cutensornet/backends/cutensornet_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,78 @@
tk_to_tensor_network,
measure_qubits_state,
)
from pytket.predicates import Predicate, GateSetPredicate, NoClassicalBitsPredicate # type: ignore
from pytket.passes import auto_rebase_pass
from pytket.predicates import ( # type: ignore
Predicate,
GateSetPredicate,
NoClassicalBitsPredicate,
NoSymbolsPredicate,
)
from pytket.passes import ( # type: ignore
BasePass,
SequencePass,
DecomposeBoxes,
RemoveRedundancies,
SynthesiseTket,
FullPeepholeOptimise,
RebaseCustom,
SquashCustom,
)
from pytket.utils.operators import QubitPauliOperator


# TODO: this is temporary - probably don't need it eventually?
def _sq(a: Expr, b: Expr, c: Expr) -> Circuit:
circ = Circuit(1)
if c != 0:
circ.Rz(c, 0)
if b != 0:
circ.Rx(b, 0)
if a != 0:
circ.Rz(a, 0)
return circ


class CuTensorNetBackend(Backend):
"""A pytket Backend wrapping around the cuTensorNet simulator."""

_supports_state = True
_supports_expectation = True
_persistent_handles = False
_GATE_SET = {
OpType.X,
OpType.Y,
OpType.Z,
OpType.S,
OpType.Sdg,
OpType.T,
OpType.Tdg,
OpType.V,
OpType.Vdg,
OpType.SX,
OpType.SXdg,
OpType.H,
OpType.Rx,
OpType.Ry,
OpType.Rz,
OpType.U1,
OpType.U2,
OpType.U3,
OpType.TK1,
OpType.TK2,
OpType.CX,
OpType.CY,
OpType.CZ,
OpType.CH,
OpType.CV,
OpType.CVdg,
OpType.CSX,
OpType.CSXdg,
OpType.CRz,
OpType.CRy,
OpType.CRx,
OpType.CU1,
OpType.CU3,
OpType.CCX,
OpType.ECR,
OpType.SWAP,
OpType.CSWAP,
OpType.ISWAP,
OpType.XXPhase,
OpType.YYPhase,
OpType.ZZPhase,
OpType.ZZMax,
OpType.ESWAP,
OpType.PhasedX,
OpType.FSim,
OpType.Sycamore,
}

# TODO: add self._backend_info?
def __init__(self) -> None:
Expand All @@ -84,7 +125,6 @@ def backend_info(self) -> Optional[BackendInfo]:
"""Returns information on the backend."""
return None

# TODO: Surely we can allow for more gate sets - needs thorough testing though.
@property
def required_predicates(self) -> List[Predicate]:
"""Returns the minimum set of predicates that a circuit must satisfy.
Expand All @@ -97,38 +137,19 @@ def required_predicates(self) -> List[Predicate]:
"""
preds = [
NoClassicalBitsPredicate(),
GateSetPredicate(
{
OpType.Rx,
OpType.Ry,
OpType.Rz,
OpType.ZZMax,
}
),
NoSymbolsPredicate(),
GateSetPredicate(self._GATE_SET),
]
return preds

# TODO: also probably needs improvement.
def rebase_pass(self) -> BasePass:
"""Defines rebasing method.
Returns:
Custom rebase pass object.
Automatic rebase pass object based on the backend gate set.
"""
cx_circ = Circuit(2)
cx_circ.Sdg(0)
cx_circ.V(1)
cx_circ.Sdg(1)
cx_circ.Vdg(1)
cx_circ.add_gate(OpType.ZZMax, [0, 1])
cx_circ.Vdg(1)
cx_circ.Sdg(1)
cx_circ.add_phase(0.5)
return RebaseCustom(
{OpType.Rx, OpType.Ry, OpType.Rz, OpType.ZZMax}, cx_circ, _sq
)

# TODO: same as above?
return auto_rebase_pass(self._GATE_SET)

def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass:
"""Returns a default compilation pass.
Expand All @@ -146,17 +167,15 @@ def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass:
Compilation pass guaranteeing required predicates.
"""
assert optimisation_level in range(3)
squash = SquashCustom({OpType.Rz, OpType.Rx, OpType.Ry}, _sq)
seq = [DecomposeBoxes()] # Decompose boxes into basic gates
seq = [
DecomposeBoxes(),
RemoveRedundancies(),
] # Decompose boxes into basic gates
if optimisation_level == 1:
seq.append(SynthesiseTket()) # Optional fast optimisation
elif optimisation_level == 2:
seq.append(FullPeepholeOptimise()) # Optional heavy optimisation
seq.append(self.rebase_pass()) # Map to target gate set
if optimisation_level != 0:
seq.append(
squash
) # Optionally simplify 1qb gate chains within this gate set
return SequencePass(seq)

def circuit_status(self, handle: ResultHandle) -> CircuitStatus:
Expand Down Expand Up @@ -211,15 +230,7 @@ def process_circuits(
"Global phase is dependent on a symbolic parameter, so cannot "
"adjust for phase"
)
# Qubits order:
# implicit_perm = circuit.implicit_qubit_permutation()
# res_qubits = [
# implicit_perm[qb] for qb in sorted(circuit.qubits, reverse=False)
# ] # reverse was set to True in the pytket-example but this fails tests.
res_qubits = [qb for qb in sorted(circuit.qubits)]
# The below line is as per pytket-Qulacs, but this alone fails the implicit
# permutation test result.
# res_qubits = sorted(circuit.qubits, reverse=False)
handle = ResultHandle(str(uuid4()))
self._cache[handle] = {
"result": BackendResult(q_bits=res_qubits, state=state)
Expand Down
19 changes: 19 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,25 @@ def q4_lcu1() -> Circuit:
return circuit


@pytest.fixture
def q4_multicontrols() -> Circuit:
circ = Circuit(4)
circ.X(0)
circ.X(1)
circ.X(2)
circ.X(3)
circ.CCX(0, 1, 2)
circ.CCX(0, 1, 3)
circ.CSWAP(0, 1, 2)
circ.CCX(0, 2, 3)
circ.CSWAP(3, 1, 0)
circ.CCX(3, 2, 1)
circ.CCX(3, 2, 0)
circ.X(1)
circ.CCX(3, 1, 0)
return circ


@pytest.fixture
def q5_empty() -> Circuit:
circuit = Circuit(5)
Expand Down
1 change: 1 addition & 0 deletions tests/test_cutensornet_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def test_expectation_value() -> None:
pytest.lazy_fixture("q3_v0cx02"), # type: ignore
pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore
pytest.lazy_fixture("q4_lcu1"), # type: ignore
pytest.lazy_fixture("q4_multicontrols"), # type: ignore
],
)
def test_compile_convert_statevec_overlap(circuit: Circuit) -> None:
Expand Down
1 change: 1 addition & 0 deletions tests/test_tensor_network_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def circuit_overlap_contract(circuit_ket: Circuit) -> float:
pytest.lazy_fixture("q3_v0cx02"), # type: ignore
pytest.lazy_fixture("q3_cx01cz12x1rx0"), # type: ignore
pytest.lazy_fixture("q4_lcu1"), # type: ignore
pytest.lazy_fixture("q4_multicontrols"), # type: ignore
],
)
def test_convert_statevec_overlap(circuit: Circuit) -> None:
Expand Down

0 comments on commit 7eefaa1

Please sign in to comment.