Skip to content

Commit

Permalink
[feature] auto_rebase_pass function for attempting automatic rebase p…
Browse files Browse the repository at this point in the history
…asses
  • Loading branch information
ss2165 committed Feb 16, 2022
1 parent bc63dc5 commit 0f0991f
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 2 deletions.
2 changes: 1 addition & 1 deletion pytket/binders/circuit/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace tket {
void init_library(py::module &m) {
/* Circuit library */
py::module_ library_m = m.def_submodule(
"library",
"_library",
"Library of reusable circuits and circuit generator functions.");
library_m.def(
"_BRIDGE_using_CX_0", &CircPool::BRIDGE_using_CX_0,
Expand Down
3 changes: 3 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ API changes:
``map`` property instead.)
* The deprecated ``Backend.compile_circuit`` method is removed. (Use
``get_compiled_circuit`` instead.)
* ``RebaseCustom`` takes one allowed gateset parameter rather than separate single qubit and multiqubit gatesets.

Minor new features:

* Add ``delay_measures`` option to ``DefaultMappingPass``.
* New ``pytket.passes.auto_rebase_pass`` which attempts to construct a rebase pass given a target gate set from known decompositions.
* New ``pytket.circuit.pool`` module containing functions to generate common circuits.

0.19.0 (February 2022)
----------------------
Expand Down
1 change: 1 addition & 0 deletions pytket/pytket/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
from pytket._tket.passes import * # type: ignore

from .script import compilation_pass_from_script, compilation_pass_grammar
from .auto_rebase import auto_rebase_pass
98 changes: 98 additions & 0 deletions pytket/pytket/passes/auto_rebase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright 2019-2022 Cambridge Quantum Computing
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from token import OP
from typing import Set, Union, Callable, Dict, FrozenSet, TYPE_CHECKING
from pytket.circuit import Circuit, OpType # type: ignore
from pytket._tket.circuit import _library # type: ignore
from pytket.passes import RebaseCustom # type: ignore

if TYPE_CHECKING:
from sympy import Expr # type: ignore


class NoAutoRebase(Exception):
"""Automatic rebase could not be found."""


_CX_CIRCS: Dict[OpType, Callable[[], "Circuit"]] = {
OpType.CX: _library._CX,
OpType.ZZMax: _library._CX_using_ZZMax,
OpType.XXPhase: _library._CX_using_XXPhase_0,
OpType.ECR: _library._CX_using_ECR,
OpType.CZ: lambda: Circuit(2).H(1).CZ(0, 1).H(1),
}


def get_cx_decomposition(gateset: Set[OpType]) -> Circuit:
"""Return a Circuit expressing a CX in terms of a two qubit gate in the
gateset if one is available, raise an error otherwise.
:param gateset: Target gate set.
:type gateset: Set[OpType]
:raises NoAutoRebase: No suitable CX decomposition found.
:return: Decomposuition circuit.
:rtype: Circuit
"""
if any((matching := k) in gateset for k in _CX_CIRCS):
return _CX_CIRCS[matching]()
raise NoAutoRebase("No known decomposition from CX to available gateset.")


Param = Union[str, "Expr"]

_TK1_circs: Dict[FrozenSet[OpType], Callable[[Param, Param, Param], "Circuit"]] = {
frozenset({OpType.TK1}): _library._TK1_to_TK1,
frozenset({OpType.PhasedX, OpType.Rz}): _library._TK1_to_PhasedXRz,
frozenset({OpType.Rx, OpType.Rz}): _library._TK1_to_RzRx,
frozenset({OpType.Rz, OpType.H}): _library._TK1_to_RzH,
frozenset({OpType.Rz, OpType.SX}): _library._TK1_to_RzSX,
}


def get_TK1_decomposition_function(
gateset: Set[OpType],
) -> Callable[[Param, Param, Param], "Circuit"]:
"""Return a function for generating TK1 equivalent circuits, which take the
three TK1 parameters as arguments and return a TK1 equivalent single qubit
circuit. If no such function is available, raise an error.
:raises NoAutoRebase: No suitable TK1 decomposition found.
:return: TK1 decomposition function.
:rtype: Callable[[Param, Param, Param], "Circuit"]
"""
if any((matching := k).issubset(gateset) for k in _TK1_circs):
return _TK1_circs[matching]
raise NoAutoRebase("No known decomposition from TK1 to available gateset.")


def auto_rebase_pass(gateset: Set[OpType]) -> RebaseCustom:
"""Attempt to generate a rebase pass automatically for the given target
gateset.
Checks if there are known existing decompositions from CX
to target gateset and TK1 to target gateset and uses those to construct a
custom rebase.
Raises an error if no known decompositions can be found, in which case try
using RebaseCustom with your own decompositions.
:param gateset: Set of supported OpTypes, target gate set.
:type gateset: FrozenSet[OpType]
:raises NoAutoRebase: No suitable CX or TK1 decomposition found.
:return: Rebase pass.
:rtype: RebaseCustom
"""
return RebaseCustom(
gateset, get_cx_decomposition(gateset), get_TK1_decomposition_function(gateset)
)
53 changes: 52 additions & 1 deletion pytket/tests/transform_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@

from pathlib import Path
from pytket.circuit import Circuit, OpType, PauliExpBox # type: ignore
from pytket._tket.circuit import _library # type: ignore
from pytket.pauli import Pauli # type: ignore
from pytket.passes import RemoveRedundancies, KAKDecomposition, ThreeQubitSquash, CommuteThroughMultis, PauliSquash, FullPeepholeOptimise, GlobalisePhasedX # type: ignore
from pytket.passes import RemoveRedundancies, KAKDecomposition, ThreeQubitSquash, CommuteThroughMultis, PauliSquash, FullPeepholeOptimise, GlobalisePhasedX, RebaseCustom # type: ignore
from pytket.passes import auto_rebase_pass
from pytket.passes.auto_rebase import _CX_CIRCS, NoAutoRebase
from pytket.predicates import CompilationUnit # type: ignore
from pytket.transform import Transform, CXConfigType, PauliSynthStrat # type: ignore
from pytket.qasm import circuit_from_qasm
Expand Down Expand Up @@ -729,6 +732,54 @@ def test_full_peephole_optimise() -> None:
assert n_cx1 < n_cz


def test_auto_rebase() -> None:
pass_params = [
({OpType.CX, OpType.Rz, OpType.Rx}, _library._CX(), _library._TK1_to_RzRx),
(
{OpType.CZ, OpType.Rz, OpType.SX, OpType.ZZPhase},
_CX_CIRCS[OpType.CZ](),
_library._TK1_to_RzSX,
),
(
{OpType.ZZMax, OpType.T, OpType.Rz, OpType.H},
_library._CX_using_ZZMax(),
_library._TK1_to_RzH,
),
(
{OpType.XXPhase, OpType.T, OpType.Rz, OpType.H},
_library._CX_using_XXPhase_0(),
_library._TK1_to_RzH,
),
(
{OpType.ECR, OpType.PhasedX, OpType.Rz, OpType.CnX},
_library._CX_using_ECR(),
_library._TK1_to_PhasedXRz,
),
(
{OpType.CX, OpType.TK1, OpType.U3, OpType.CnX},
_library._CX(),
_library._TK1_to_TK1,
),
]

circ = get_test_circuit()

for gateset, cx_circ, TK1_func in pass_params:
rebase = auto_rebase_pass(gateset)
assert rebase.to_dict() == RebaseCustom(gateset, cx_circ, TK1_func).to_dict()

c2 = circ.copy()
assert rebase.apply(c2)

with pytest.raises(NoAutoRebase) as cx_err:
_ = auto_rebase_pass({OpType.ZZPhase, OpType.TK1})
assert "CX" in str(cx_err.value)

with pytest.raises(NoAutoRebase) as cx_err:
_ = auto_rebase_pass({OpType.CX, OpType.H, OpType.T})
assert "TK1" in str(cx_err.value)


if __name__ == "__main__":
test_remove_redundancies()
test_reduce_singles()
Expand Down

0 comments on commit 0f0991f

Please sign in to comment.