Skip to content

Commit

Permalink
Refactor greedy pauli simp (#1611)
Browse files Browse the repository at this point in the history
* Initial refactor

* Refactor node types

* Move nodes definition into a new file

* Refactor synthesis

* Cleanup

* More refactoring

* Refactor pauli graph converters

* Initial implementation of GPGraph

* Migrate to GPGraph

* Ignore global phase

* Add supports for conditional gates

* Add support for classical ops

* flatten_registers should only rename qubits and bits

* Replace unsigned with Bit

* Revert "Replace unsigned with Bit"

This reverts commit 9e9a2fd.

* Manually check if qubits and bits can be flattened since ``is_simple()`` doesn't work with wasm

* Consistent enum names

* Add support for mid-circuit measurement

* Rename variables

* Add support for resets

* Update docstrings

* Update pass predicate

* Add more tests

* bump tket version

* Add changelog entry

* Fix docs errors

* bump tket version

* Remove unused headers

* bump tket version in pytket

* uncomment lines

* remove consts in SQ_CLIFF_DAGGER

* Merging conditionals

* Revert "Merging conditionals"

This reverts commit f5138f1.

* Optimise conditional handling

* remove ConditionalPauliRotation

* Remove clifford reduction and pass parameters when synthesis conditionals

* Seeded tie breaking

* Add limits to the search space

* Allowing ZZPhase gates

* update binder and serialisation

* Add test for ops handling in python

* add note for AC node cost

* bump tket version

* update changelog

* fix changelog format

* add missing prams in docstrings

* cast size_t to unsigned

* remove nondeterminism from test

* Remove more nondeterminism

* regen stubs

* Add debug info

* Revert "Add debug info"

This reverts commit e5030e2.

* Replace implicit wire swaps in optimised conditional circuits

* re-organise changelog

* Bump tket version

* fix bug in sign correction

* Add more tests
  • Loading branch information
yao-cqc authored Oct 24, 2024
1 parent 7bae471 commit 94de5fa
Show file tree
Hide file tree
Showing 21 changed files with 3,100 additions and 2,336 deletions.
13 changes: 12 additions & 1 deletion pytket/binders/passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,8 +927,19 @@ PYBIND11_MODULE(passes, m) {
"\n\n:param discount_rate: Rate used to discount the cost impact from "
"gadgets that are further away. Default to 0.7."
"\n:param depth_weight: Degree of depth optimisation. Default to 0.3."
"\n:param max_tqe_candidates: Maximum number of 2-qubit Clifford "
"gate candidates to evaluate at each step. Default to 500."
"\n:param max_lookahead: Maximum lookahead when evaluating each "
"Clifford gate candidate. Default to 500."
"\n:param seed: Unsigned integer seed used for sampling candidates "
"and tie breaking. Default to 0."
"\n:param allow_zzphase: If set to True, allows the algorithm to "
"implement 2-qubit rotations using ZZPhase gates when deemed "
"optimal. Defaults to False."
"\n:return: a pass to perform the simplification",
py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3);
py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3,
py::arg("max_lookahead") = 500, py::arg("max_tqe_candidates") = 500,
py::arg("seed") = 0, py::arg("allow_zzphase") = false);
m.def(
"PauliSquash", &PauliSquash,
"Applies :py:meth:`PauliSimp` followed by "
Expand Down
13 changes: 12 additions & 1 deletion pytket/binders/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,19 @@ PYBIND11_MODULE(transform, m) {
"gadgets that are further away. Default to 0.7."
"\n:param depth_weight: Degree of depth optimisation. Default to "
"0.3."
"\n:param max_tqe_candidates: Maximum number of 2-qubit Clifford "
"gate candidates to evaluate at each step. Default to 500."
"\n:param max_lookahead: Maximum lookahead when evaluating each "
"Clifford gate candidate. Default to 500."
"\n:param seed: Unsigned integer seed used for sampling candidates "
"and tie breaking. Default to 0."
"\n:param allow_zzphase: If set to True, allows the algorithm to "
"implement 2-qubit rotations using ZZPhase gates when deemed "
"optimal. Defaults to False."
"\n:return: a pass to perform the simplification",
py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3)
py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3,
py::arg("max_tqe_candidates") = 500, py::arg("max_lookahead") = 500,
py::arg("seed") = 0, py::arg("allow_zzphase") = false)
.def_static(
"ZZPhaseToRz", &Transforms::ZZPhase_to_Rz,
"Fixes all ZZPhase gate angles to [-1, 1) half turns.")
Expand Down
2 changes: 1 addition & 1 deletion pytket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def requirements(self):
self.requires("pybind11_json/0.2.14")
self.requires("symengine/0.12.0")
self.requires("tkassert/0.3.4@tket/stable")
self.requires("tket/1.3.34@tket/stable")
self.requires("tket/1.3.35@tket/stable")
self.requires("tklog/0.3.3@tket/stable")
self.requires("tkrng/0.3.3@tket/stable")
self.requires("tktokenswap/0.3.9@tket/stable")
Expand Down
12 changes: 12 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ Features:

* Add new `ClExprOp` operation type as an alternative to `ClassicalExpBox`; add
option to use this when converting from QASM.
* Several updates to `GreedyPauliSimp`:

* Support for mid-circuit measurements, resets, conditionals,
and classical gates.

* New parameters `max_lookahead` and `max_tqe_candidates` are added
to limit the search space.

* New parameter `seed` is added to support random sampling and tie breaking.

* New parameter `allow_zzphase` allows the algorithm to implement 2-qubit rotations
using ZZPhase gates when deemed optimal.

Fixes:

Expand Down
6 changes: 5 additions & 1 deletion pytket/pytket/_tket/passes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,16 @@ def GlobalisePhasedX(squash: bool = True) -> BasePass:
It is not recommended to use this pass with symbolic expressions, as in certain cases a blow-up in symbolic expression sizes may occur.
"""
def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3) -> BasePass:
def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3, max_lookahead: int = 500, max_tqe_candidates: int = 500, seed: int = 0, allow_zzphase: bool = False) -> BasePass:
"""
Construct a pass that converts a circuit into a graph of Pauli gadgets to account for commutation and phase folding, and resynthesises them using a greedy algorithm adapted from arxiv.org/abs/2103.08602. The method for synthesising the final Clifford operator is adapted from arxiv.org/abs/2305.10966.
:param discount_rate: Rate used to discount the cost impact from gadgets that are further away. Default to 0.7.
:param depth_weight: Degree of depth optimisation. Default to 0.3.
:param max_tqe_candidates: Maximum number of 2-qubit Clifford gate candidates to evaluate at each step. Default to 500.
:param max_lookahead: Maximum lookahead when evaluating each Clifford gate candidate. Default to 500.
:param seed: Unsigned integer seed used for sampling candidates and tie breaking. Default to 0.
:param allow_zzphase: If set to True, allows the algorithm to implement 2-qubit rotations using ZZPhase gates when deemed optimal. Defaults to False.
:return: a pass to perform the simplification
"""
def GuidedPauliSimp(strat: pytket._tket.transform.PauliSynthStrat = pytket._tket.transform.PauliSynthStrat.Sets, cx_config: pytket._tket.circuit.CXConfigType = pytket._tket.circuit.CXConfigType.Snake) -> BasePass:
Expand Down
6 changes: 5 additions & 1 deletion pytket/pytket/_tket/transform.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,16 @@ class Transform:
It is not recommended to use this transformation with symbolic expressions, as in certain cases a blow-up in symbolic expression sizes may occur.
"""
@staticmethod
def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3) -> Transform:
def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3, max_tqe_candidates: int = 500, max_lookahead: int = 500, seed: int = 0, allow_zzphase: bool = False) -> Transform:
"""
Convert a circuit into a graph of Pauli gadgets to account for commutation and phase folding, and resynthesises them using a greedy algorithm adapted from arxiv.org/abs/2103.08602. The method for synthesising the final Clifford operator is adapted from arxiv.org/abs/2305.10966.
:param discount_rate: Rate used to discount the cost impact from gadgets that are further away. Default to 0.7.
:param depth_weight: Degree of depth optimisation. Default to 0.3.
:param max_tqe_candidates: Maximum number of 2-qubit Clifford gate candidates to evaluate at each step. Default to 500.
:param max_lookahead: Maximum lookahead when evaluating each Clifford gate candidate. Default to 500.
:param seed: Unsigned integer seed used for sampling candidates and tie breaking. Default to 0.
:param allow_zzphase: If set to True, allows the algorithm to implement 2-qubit rotations using ZZPhase gates when deemed optimal. Defaults to False.
:return: a pass to perform the simplification
"""
@staticmethod
Expand Down
10 changes: 9 additions & 1 deletion pytket/tests/passes_serialisation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,15 @@ def nonparam_predicate_dict(name: str) -> Dict[str, Any]:
{"name": "RoundAngles", "n": 6, "only_zeros": False}
),
"GreedyPauliSimp": standard_pass_dict(
{"name": "GreedyPauliSimp", "discount_rate": 0.4, "depth_weight": 0.5}
{
"name": "GreedyPauliSimp",
"discount_rate": 0.4,
"depth_weight": 0.5,
"max_lookahead": 100,
"max_tqe_candidates": 100,
"seed": 2,
"allow_zzphase": True,
}
),
# lists must be sorted by OpType value
"AutoSquash": standard_pass_dict(
Expand Down
52 changes: 49 additions & 3 deletions pytket/tests/predicates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
UnitID,
Conditional,
Bit,
RangePredicateOp,
SetBitsOp,
MultiBitOp,
)
from pytket.circuit.named_types import ParamType, RenameUnitsMap
from pytket.pauli import Pauli
Expand Down Expand Up @@ -1020,18 +1023,61 @@ def test_clifford_push_through_measures() -> None:
assert coms[7].op.type == OpType.CopyBits


def greedy_pauli_synth() -> None:
circ = Circuit(4, name="test")
def test_greedy_pauli_synth() -> None:
circ = Circuit(name="test")
rega = circ.add_q_register("a", 2)
regb = circ.add_q_register("b", 2)
d = circ.copy()
circ.Rz(0, rega[0]).H(regb[1]).CX(rega[0], rega[1]).Ry(0.3, rega[0]).S(regb[1]).CZ(
rega[0], regb[0]
).SWAP(regb[1], rega[0])
d = circ.copy()
pss = GreedyPauliSimp(0.5, 0.5)
assert pss.apply(d)
assert np.allclose(circ.get_unitary(), d.get_unitary())
assert d.name == "test"
# test gateset
range_predicate = RangePredicateOp(3, 0, 6)
set_bits = SetBitsOp([True, True])
multi_bit = MultiBitOp(set_bits, 2)
exp = Bit(2) & Bit(3)
eq_pred_values = [True, False, False, True]
and_values = [bool(i) for i in [0, 0, 0, 1]]
pg1 = PauliExpBox([Pauli.X, Pauli.Z], 0.3)
circ = Circuit(4, 4, name="test")
circ.add_pauliexpbox(pg1, [0, 1])
circ.add_gate(multi_bit, [0, 1, 2, 3])
circ.add_gate(range_predicate, [0, 1, 2, 3])
circ.add_classicalexpbox_bit(exp, [Bit(0)])
circ.add_c_predicate(eq_pred_values, [0, 1], 2, "EQ")
circ.add_c_modifier(and_values, [1], 2)
circ._add_wasm("funcname", "wasmfileuid", [1, 1], [], [Bit(0), Bit(1)], [0])
circ.measure_all()
circ.Reset(0)
circ.add_pauliexpbox(pg1, [2, 3])
assert GreedyPauliSimp(0.5, 0.5, 100, 100, 0, True).apply(circ)
# PauliExpBoxes implemented using ZZPhase
d = Circuit(4, 4, name="test")
d.H(0)
d.ZZPhase(0.3, 0, 1)
d.H(0)
d.add_gate(multi_bit, [0, 1, 2, 3])
d.add_gate(range_predicate, [0, 1, 2, 3])
d.add_classicalexpbox_bit(exp, [Bit(0)])
d.add_c_predicate(eq_pred_values, [0, 1], 2, "EQ")
d.add_c_modifier(and_values, [1], 2)
d._add_wasm("funcname", "wasmfileuid", [1, 1], [], [Bit(0), Bit(1)], [0])
d.measure_all()
d.Reset(0)
d.H(2)
d.ZZPhase(0.3, 2, 3)
d.H(2)
assert circ == d
# test barrier
circ = Circuit(1).add_barrier([0])
with pytest.raises(RuntimeError) as e:
GreedyPauliSimp().apply(circ)
err_msg = "Predicate requirements are not satisfied"
assert err_msg in str(e.value)


def test_auto_rebase_deprecation(recwarn: Any) -> None:
Expand Down
24 changes: 22 additions & 2 deletions schemas/compiler_pass_v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,22 @@
"depth_weight": {
"type": "number",
"definition": "parameter controlling the degree of depth optimisation in \"GreedyPauliSimp\""
},
"max_lookahead": {
"type": "number",
"definition": "parameter controlling the lookahead when evaluating candidates in \"GreedyPauliSimp\""
},
"max_tqe_candidates": {
"type": "number",
"definition": "parameter controlling the number of candidates to evaluate in \"GreedyPauliSimp\""
},
"seed": {
"type": "number",
"definition": "parameter controlling the random sampling and tie breaking in \"GreedyPauliSimp\""
},
"allow_zzphase": {
"type": "boolean",
"definition": "parameter controlling the use of ZZPhase gates in \"GreedyPauliSimp\""
}
},
"required": [
Expand Down Expand Up @@ -883,9 +899,13 @@
"then": {
"required": [
"discount_rate",
"depth_weight"
"depth_weight",
"max_lookahead",
"max_tqe_candidates",
"seed",
"allow_zzphase"
],
"maxProperties": 3
"maxProperties": 7
}
},
{
Expand Down
2 changes: 2 additions & 0 deletions tket/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ target_sources(tket
src/Transformations/ContextualReduction.cpp
src/Transformations/Decomposition.cpp
src/Transformations/GreedyPauliOptimisation.cpp
src/Transformations/GreedyPauliConverters.cpp
src/Transformations/GreedyPauliOps.cpp
src/Transformations/MeasurePass.cpp
src/Transformations/OptimisationPass.cpp
src/Transformations/PauliOptimisation.cpp
Expand Down
2 changes: 1 addition & 1 deletion tket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

class TketConan(ConanFile):
name = "tket"
version = "1.3.34"
version = "1.3.35"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
9 changes: 8 additions & 1 deletion tket/include/tket/Predicates/PassGenerators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,16 @@ PassPtr gen_special_UCC_synthesis(
*
* @param discount_rate
* @param depth_weight
* @param max_lookahead
* @param max_tqe_candidates
* @param seed
* @param allow_zzphase
* @return PassPtr
*/
PassPtr gen_greedy_pauli_simp(double discount_rate, double depth_weight);
PassPtr gen_greedy_pauli_simp(
double discount_rate = 0.7, double depth_weight = 0.3,
unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500,
unsigned seed = 0, bool allow_zzphase = false);

/**
* Generate a pass to simplify the circuit where it acts on known basis states.
Expand Down
Loading

0 comments on commit 94de5fa

Please sign in to comment.