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

Timeout GreedyPauliSimp #1684

Merged
merged 23 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions pytket/binders/passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,14 +970,19 @@ PYBIND11_MODULE(passes, m) {
"\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:param timeout: Sets maximum out of time spent finding solution."
"\n:param thread_timeout: Sets maximum out of time spent finding a "
"single solution in one thread."
"\n:param only_reduce: Only returns modified circuit if it has "
"fewer two-qubit gates."
"\n:param trials: Sets maximum number of found solutions. The "
"smallest circuit is returned, prioritising the number of 2qb-gates, "
"then the number of gates, then the depth."
"\n:return: a pass to perform the simplification",
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,
py::arg("timeout") = 100, py::arg("only_reduce") = false);
py::arg("thread_timeout") = 100, py::arg("only_reduce") = false,
py::arg("trials") = 1);
m.def(
"PauliSquash", &PauliSquash,
"Applies :py:meth:`PauliSimp` followed by "
Expand Down
8 changes: 6 additions & 2 deletions pytket/binders/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,12 +451,16 @@ PYBIND11_MODULE(transform, m) {
"\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:param timeout: Sets maximum out of time spent finding solution."
"\n:param thread_timeout: Sets maximum out of time spent finding a "
"single solution in one thread."
"\n:param trials: Sets maximum number of found solutions. The "
"smallest circuit is returned, prioritising the number of 2qb-gates, "
"then the number of gates, then the depth."
"\n:return: a pass to perform the simplification",
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,
py::arg("timeout") = 100)
py::arg("thread_timeout") = 100, py::arg("trials") = 1)
.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.13.0")
self.requires("tkassert/0.3.4@tket/stable")
self.requires("tket/1.3.47@tket/stable")
self.requires("tket/1.3.48@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
4 changes: 2 additions & 2 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Features:
conditions.
* Add `custom_deserialisation` argument to `BasePass` and `SequencePass`
`from_dict` method to support construction of `CustomPass` from json.
* Add `timeout` argument to `GreedyPauliSimp`.
* Add `only_reduce` argument to `GreedyPauliSimp`.
* Add `thread_timeout`, `only_reduce`, and `trials` arguments
to `GreedyPauliSimp`.
* Add option to not relabel `ClassicalExpBox` when calling `rename_units`
and `flatten_registers`
* Implement `dagger()` and `transpose()` for `CustomGate`.
Expand Down
5 changes: 3 additions & 2 deletions pytket/pytket/_tket/passes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ 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, max_lookahead: int = 500, max_tqe_candidates: int = 500, seed: int = 0, allow_zzphase: bool = False, timeout: int = 100, only_reduce: bool = False) -> 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, thread_timeout: int = 100, only_reduce: bool = False, trials: int = 1) -> 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.

Expand All @@ -457,8 +457,9 @@ def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3, max_l
: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.
:param timeout: Sets maximum out of time spent finding solution.
:param thread_timeout: Sets maximum out of time spent finding a single solution in one thread.
:param only_reduce: Only returns modified circuit if it has fewer two-qubit gates.
:param trials: Sets maximum number of found solutions. The smallest circuit is returned, prioritising the number of 2qb-gates, then the number of gates, then the depth.
: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
5 changes: 3 additions & 2 deletions pytket/pytket/_tket/transform.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ 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, max_tqe_candidates: int = 500, max_lookahead: int = 500, seed: int = 0, allow_zzphase: bool = False, timeout: int = 100) -> 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, thread_timeout: int = 100, trials: int = 1) -> 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.

Expand All @@ -177,7 +177,8 @@ class Transform:
: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.
:param timeout: Sets maximum out of time spent finding solution.
:param thread_timeout: Sets maximum out of time spent finding a single solution in one thread.
:param trials: Sets maximum number of found solutions. The smallest circuit is returned, prioritising the number of 2qb-gates, then the number of gates, then the depth.
:return: a pass to perform the simplification
"""
@staticmethod
Expand Down
3 changes: 2 additions & 1 deletion pytket/tests/passes_serialisation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,9 @@ def nonparam_predicate_dict(name: str) -> Dict[str, Any]:
"max_tqe_candidates": 100,
"seed": 2,
"allow_zzphase": True,
"timeout": 5000,
"thread_timeout": 5000,
"only_reduce": False,
"trials": 1,
}
),
# lists must be sorted by OpType value
Expand Down
16 changes: 12 additions & 4 deletions pytket/tests/predicates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,9 +1035,8 @@ def test_greedy_pauli_synth() -> None:
rega[0], regb[0]
).SWAP(regb[1], rega[0])
d = circ.copy()
pss = GreedyPauliSimp(0.5, 0.5)
assert not GreedyPauliSimp(0.5, 0.5, timeout=0, only_reduce=False).apply(d)
assert pss.apply(d)
assert GreedyPauliSimp(0.5, 0.5, thread_timeout=10, trials=5).apply(d)

assert np.allclose(circ.get_unitary(), d.get_unitary())
assert d.name == "test"
# test gateset
Expand All @@ -1059,7 +1058,6 @@ def test_greedy_pauli_synth() -> None:
circ.measure_all()
circ.Reset(0)
circ.add_pauliexpbox(pg1, [2, 3])
assert not GreedyPauliSimp(0.5, 0.5, 100, 100, 0, True, 0).apply(circ)
assert GreedyPauliSimp(0.5, 0.5, 100, 100, 0, True, 100).apply(circ)
# PauliExpBoxes implemented using ZZPhase
d = Circuit(4, 4, name="test")
Expand All @@ -1084,6 +1082,16 @@ def test_greedy_pauli_synth() -> None:
GreedyPauliSimp().apply(circ)
err_msg = "Predicate requirements are not satisfied"
assert err_msg in str(e.value)
# large circuit that doesn't complete within thread_timeout argument
c = Circuit(13)
for _ in range(20):
for i in range(13):
for j in range(i + 1, 13):
c.CX(i, j)
c.Rz(0.23, j)
c.H(i)
assert not GreedyPauliSimp(thread_timeout=1).apply(c)
assert GreedyPauliSimp().apply(c)


def test_auto_rebase_deprecation(recwarn: Any) -> None:
Expand Down
15 changes: 10 additions & 5 deletions schemas/compiler_pass_v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,17 @@
"type": "boolean",
"definition": "parameter controlling the use of ZZPhase gates in \"GreedyPauliSimp\""
},
"timeout": {
"thread_timeout": {
"type": "number",
"definition": "parameter controlling the maximum runtime of \"GreedyPauliSimp\""
"definition": "parameter controlling the maximum runtime of a single thread in \"GreedyPauliSimp\""
},
"only_reduce": {
"type": "boolean",
"definition": "parameter controlling whether \"GreedyPauliSimp\" can return circuits with more two qubit gates"
},
"trials": {
"type": "number",
"definition": "parameter controlling the number of random solutions found when calling \"GreedyPauliSimp\""
}
},
"required": [
Expand Down Expand Up @@ -913,10 +917,11 @@
"max_tqe_candidates",
"seed",
"allow_zzphase",
"timeout",
"only_reduce"
"thread_timeout",
"only_reduce",
"trials"
],
"maxProperties": 9
"maxProperties": 10
}
},
{
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.47"
version = "1.3.48"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
8 changes: 5 additions & 3 deletions tket/include/tket/Predicates/PassGenerators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,15 +355,17 @@ PassPtr gen_special_UCC_synthesis(
* @param max_tqe_candidates
* @param seed
* @param allow_zzphase
* @param timeout
* @param thread_timeout
* @param only_reduce
* @param trials
* @return PassPtr
*/
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, unsigned timeout = 100,
bool only_reduce = false);
unsigned seed = 0, bool allow_zzphase = false,
unsigned thread_timeout = 100, bool only_reduce = false,
unsigned trials = 1);

/**
* Generate a pass to simplify the circuit where it acts on known basis states.
Expand Down
28 changes: 27 additions & 1 deletion tket/include/tket/Transformations/GreedyPauliOptimisation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#pragma once

#include <atomic>

#include "Transform.hpp"
#include "tket/Circuit/Circuit.hpp"
#include "tket/Clifford/UnitaryTableau.hpp"
Expand Down Expand Up @@ -600,6 +602,29 @@ class GPGraph {
std::tuple<std::vector<PauliNode_ptr>, std::vector<PauliNode_ptr>>
gpg_from_unordered_set(const std::vector<SymPauliTensor>& unordered_set);

/**
* @brief Converts the given circuit into a GPGraph and conjugates each node
* by greedily applying 2-qubit Clifford gates until the node can be realised
* as a single-qubit gate, a measurement, or a reset. The final Clifford
* operator is synthesized in a similar fashion. Allows early termination
* from a thread via a stop_flag.
*
* @param circ
* @param stop_flag
* @param discount_rate
* @param depth_weight
* @param max_lookahead
* @param max_tqe_candidates
* @param seed
* @param allow_zzphase
* @return Circuit
*/
Circuit greedy_pauli_graph_synthesis_flag(
Circuit circ, std::atomic<bool>& stop_flag, 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);

/**
* @brief Converts the given circuit into a GPGraph and conjugates each node
* by greedily applying 2-qubit Clifford gates until the node can be realised
Expand Down Expand Up @@ -643,7 +668,8 @@ Circuit greedy_pauli_set_synthesis(
Transform greedy_pauli_optimisation(
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, unsigned timeout = 100);
unsigned seed = 0, bool allow_zzphase = false,
unsigned thread_timeout = 100, unsigned trials = 1);

} // namespace Transforms

Expand Down
5 changes: 3 additions & 2 deletions tket/src/Predicates/CompilerPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,11 +522,12 @@ PassPtr deserialise(
unsigned max_lookahead = content.at("max_lookahead").get<unsigned>();
unsigned seed = content.at("seed").get<unsigned>();
bool allow_zzphase = content.at("allow_zzphase").get<bool>();
unsigned timeout = content.at("timeout").get<unsigned>();
unsigned timeout = content.at("thread_timeout").get<unsigned>();
bool only_reduce = content.at("only_reduce").get<bool>();
unsigned trials = content.at("trials").get<unsigned>();
pp = gen_greedy_pauli_simp(
discount_rate, depth_weight, max_lookahead, max_tqe_candidates, seed,
allow_zzphase, timeout, only_reduce);
allow_zzphase, timeout, only_reduce, trials);

} else if (passname == "PauliSimp") {
// SEQUENCE PASS - DESERIALIZABLE ONLY
Expand Down
48 changes: 29 additions & 19 deletions tket/src/Predicates/PassGenerators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,24 +1017,33 @@ PassPtr gen_synthesise_pauli_graph(
PassPtr gen_greedy_pauli_simp(
double discount_rate, double depth_weight, unsigned max_lookahead,
unsigned max_tqe_candidates, unsigned seed, bool allow_zzphase,
unsigned timeout, bool only_reduce) {
Transform t =
Transform([discount_rate, depth_weight, max_lookahead, max_tqe_candidates,
seed, allow_zzphase, timeout, only_reduce](Circuit& circ) {
Transform gpo = Transforms::greedy_pauli_optimisation(
discount_rate, depth_weight, max_lookahead, max_tqe_candidates,
seed, allow_zzphase, timeout);
if (only_reduce) {
Circuit gpo_circ = circ;
if (gpo.apply(gpo_circ) &&
gpo_circ.count_n_qubit_gates(2) < circ.count_n_qubit_gates(2)) {
circ = gpo_circ;
return true;
}
return false;
}
return gpo.apply(circ);
});
unsigned thread_timeout, bool only_reduce, unsigned trials) {
Transform t = Transform([discount_rate, depth_weight, max_lookahead,
max_tqe_candidates, seed, allow_zzphase,
thread_timeout, only_reduce, trials](Circuit& circ) {
Transform gpo = Transforms::greedy_pauli_optimisation(
discount_rate, depth_weight, max_lookahead, max_tqe_candidates, seed,
allow_zzphase, thread_timeout, trials);
if (only_reduce) {
Circuit gpo_circ = circ;
// comparison will be inaccurate if circuit has PauliExpBox
gpo_circ.decompose_boxes_recursively();
unsigned original_n_2qb_gates = gpo_circ.count_n_qubit_gates(2);
unsigned original_n_gates = gpo_circ.n_gates();
unsigned original_depth = gpo_circ.depth();
if (gpo.apply(gpo_circ) &&
std::make_tuple(
gpo_circ.count_n_qubit_gates(2), gpo_circ.n_gates(),
gpo_circ.depth()) <
std::make_tuple(
original_n_2qb_gates, original_n_gates, original_depth)) {
circ = gpo_circ;
return true;
}
return false;
}
return gpo.apply(circ);
});
OpTypeSet ins = {
OpType::Z,
OpType::X,
Expand Down Expand Up @@ -1083,8 +1092,9 @@ PassPtr gen_greedy_pauli_simp(
j["max_tqe_candidates"] = max_tqe_candidates;
j["seed"] = seed;
j["allow_zzphase"] = allow_zzphase;
j["timeout"] = timeout;
j["thread_timeout"] = thread_timeout;
j["only_reduce"] = only_reduce;
j["trials"] = trials;

return std::make_shared<StandardPass>(precons, t, postcon, j);
}
Expand Down
Loading
Loading