Skip to content

Commit

Permalink
Add timeout parameter to GreedyPauliSimp (#1664)
Browse files Browse the repository at this point in the history
* add timeout

* add timeout to serialisation

* Update changelog.rst

* bump library

* Update transform.cpp

* Update test_GreedyPauli.cpp

* update stubs

* fix serialisation

* Update test_GreedyPauli.cpp

* optionally relabel classical expressions when renaming units

* Update predicates_test.py

* update flattenrelabelregisters

* reformat, add false to rename_units again

* bump library

* Update passes_serialisation_test.py

* bump library version

* Update test_GreedyPauli.cpp

* Update test_GreedyPauli.cpp

* Update test_GreedyPauli.cpp
  • Loading branch information
sjdilkes authored Nov 11, 2024
1 parent c52838b commit fbc3ebc
Show file tree
Hide file tree
Showing 20 changed files with 124 additions and 47 deletions.
7 changes: 6 additions & 1 deletion pytket/binders/circuit/Circuit/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,12 @@ void def_circuit(py::class_<Circuit, std::shared_ptr<Circuit>> &pyCircuit) {
.def(
"flatten_registers", &Circuit::flatten_registers,
"Combines all qubits into a single register namespace with "
"the default name, and likewise for bits")
"the default name, and likewise for bits"
"\n\n:param relabel_classical_expression: Determines whether python "
"classical expressions held in `ClassicalExpBox` have their "
"expression "
"relabelled to match relabelled Bit.",
py::arg("relabel_classical_expression") = true)

// Circuit composition:
.def(
Expand Down
11 changes: 8 additions & 3 deletions pytket/binders/passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -752,9 +752,12 @@ PYBIND11_MODULE(passes, m) {
"FlattenRelabelRegistersPass", &gen_flatten_relabel_registers_pass,
"Removes empty Quantum wires from the Circuit and relabels all Qubit to "
"a register from passed name. \n\n:param label: Name to relabel "
"remaining Qubit to, default 'q'.\n:return: A pass that removes empty "
"remaining Qubit to, default 'q'.\n:param relabel_classical_expressions: "
"Whether to relabel arguments of expressions held in `ClassicalExpBox`. "
"\n:return: A pass that removes empty "
"wires and relabels.",
py::arg("label") = q_default_reg());
py::arg("label") = q_default_reg(),
py::arg("relabel_classical_expressions") = true);

m.def(
"RenameQubitsPass", &gen_rename_qubits_pass,
Expand Down Expand Up @@ -957,10 +960,12 @@ 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: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("seed") = 0, py::arg("allow_zzphase") = false,
py::arg("timeout") = 100);
m.def(
"PauliSquash", &PauliSquash,
"Applies :py:meth:`PauliSimp` followed by "
Expand Down
4 changes: 3 additions & 1 deletion pytket/binders/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,12 @@ 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: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("seed") = 0, py::arg("allow_zzphase") = false,
py::arg("timeout") = 100)
.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.40@tket/stable")
self.requires("tket/1.3.42@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
3 changes: 3 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ 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 option to not relabel `ClassicalExpBox` when calling `rename_units`
and `flatten_registers`

Fixes:

Expand Down
4 changes: 3 additions & 1 deletion pytket/pytket/_tket/circuit.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2104,9 +2104,11 @@ class Circuit:
:param types: the set of operation types of interest
:return: the circuit depth with respect to operations matching an element of `types`
"""
def flatten_registers(self) -> dict[pytket._tket.unit_id.UnitID, pytket._tket.unit_id.UnitID]:
def flatten_registers(self, relabel_classical_expression: bool = True) -> dict[pytket._tket.unit_id.UnitID, pytket._tket.unit_id.UnitID]:
"""
Combines all qubits into a single register namespace with the default name, and likewise for bits
:param relabel_classical_expression: Determines whether python classical expressions held in `ClassicalExpBox` have their expression relabelled to match relabelled Bit.
"""
def free_symbols(self) -> set[sympy.Symbol]:
"""
Expand Down
6 changes: 4 additions & 2 deletions pytket/pytket/_tket/passes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -410,11 +410,12 @@ def FlattenRegisters() -> BasePass:
"""
Merges all quantum and classical registers into their respective default registers with contiguous indexing.
"""
def FlattenRelabelRegistersPass(label: str = 'q') -> BasePass:
def FlattenRelabelRegistersPass(label: str = 'q', relabel_classical_expressions: bool = True) -> BasePass:
"""
Removes empty Quantum wires from the Circuit and relabels all Qubit to a register from passed name.
:param label: Name to relabel remaining Qubit to, default 'q'.
:param relabel_classical_expressions: Whether to relabel arguments of expressions held in `ClassicalExpBox`.
:return: A pass that removes empty wires and relabels.
"""
def FullMappingPass(arc: pytket._tket.architecture.Architecture, placer: pytket._tket.placement.Placement, config: typing.Sequence[pytket._tket.mapping.RoutingMethod]) -> BasePass:
Expand Down Expand Up @@ -444,7 +445,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) -> 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, timeout: int = 100) -> 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 @@ -454,6 +455,7 @@ 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.
: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
3 changes: 2 additions & 1 deletion pytket/pytket/_tket/transform.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,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) -> 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, timeout: int = 100) -> 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 @@ -175,6 +175,7 @@ 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.
:return: a pass to perform the simplification
"""
@staticmethod
Expand Down
1 change: 1 addition & 0 deletions pytket/tests/passes_serialisation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ def nonparam_predicate_dict(name: str) -> Dict[str, Any]:
"max_tqe_candidates": 100,
"seed": 2,
"allow_zzphase": True,
"timeout": 5000,
}
),
# lists must be sorted by OpType value
Expand Down
4 changes: 3 additions & 1 deletion pytket/tests/predicates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ def test_greedy_pauli_synth() -> None:
).SWAP(regb[1], rega[0])
d = circ.copy()
pss = GreedyPauliSimp(0.5, 0.5)
assert not GreedyPauliSimp(0.5, 0.5, timeout=0).apply(d)
assert pss.apply(d)
assert np.allclose(circ.get_unitary(), d.get_unitary())
assert d.name == "test"
Expand All @@ -1058,7 +1059,8 @@ def test_greedy_pauli_synth() -> None:
circ.measure_all()
circ.Reset(0)
circ.add_pauliexpbox(pg1, [2, 3])
assert GreedyPauliSimp(0.5, 0.5, 100, 100, 0, True).apply(circ)
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")
d.H(0)
Expand Down
14 changes: 10 additions & 4 deletions schemas/compiler_pass_v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@
"allow_zzphase": {
"type": "boolean",
"definition": "parameter controlling the use of ZZPhase gates in \"GreedyPauliSimp\""
},
"timeout": {
"type": "number",
"definition": "parameter controlling the maximum runtime of \"GreedyPauliSimp\""
}
},
"required": [
Expand Down Expand Up @@ -644,9 +648,10 @@
},
"then": {
"required": [
"label"
"label",
"relabel_classical_registers"
],
"maxProperties": 2
"maxProperties": 3
}
},
{
Expand Down Expand Up @@ -903,9 +908,10 @@
"max_lookahead",
"max_tqe_candidates",
"seed",
"allow_zzphase"
"allow_zzphase",
"timeout"
],
"maxProperties": 7
"maxProperties": 8
}
},
{
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.40"
version = "1.3.42"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
16 changes: 9 additions & 7 deletions tket/include/tket/Circuit/Circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,9 +749,12 @@ class Circuit {
/**
* Convert all quantum and classical bits to use default registers.
*
* @param relabel_classical_expression Whether expressions in ClassicalExpBox
* have their expr updated to match the input wires
*
* @return mapping from old to new unit IDs
*/
unit_map_t flatten_registers();
unit_map_t flatten_registers(bool relabel_classical_expression = true);

//_________________________________________________

Expand Down Expand Up @@ -1044,7 +1047,8 @@ class Circuit {
* @return true iff circuit was modified
*/
template <typename UnitA, typename UnitB>
bool rename_units(const std::map<UnitA, UnitB> &qm);
bool rename_units(
const std::map<UnitA, UnitB> &qm, bool relabel_classicalexpbox = true);

/** Automatically rewire holes when removing vertices from the circuit? */
enum class GraphRewiring { Yes, No };
Expand Down Expand Up @@ -1719,7 +1723,8 @@ JSON_DECL(Circuit)
/** Templated method definitions */

template <typename UnitA, typename UnitB>
bool Circuit::rename_units(const std::map<UnitA, UnitB> &qm) {
bool Circuit::rename_units(
const std::map<UnitA, UnitB> &qm, bool relabel_classicalexpbox) {
// Can only work for Unit classes
static_assert(std::is_base_of<UnitID, UnitA>::value);
static_assert(std::is_base_of<UnitID, UnitB>::value);
Expand Down Expand Up @@ -1768,21 +1773,18 @@ bool Circuit::rename_units(const std::map<UnitA, UnitB> &qm) {
"Unit already exists in circuit: " + pair.first.repr());
TKET_ASSERT(modified);
}

// For every ClassicalExpBox, update its logic expressions
if (!bm.empty()) {
if (!bm.empty() && relabel_classicalexpbox) {
BGL_FORALL_VERTICES(v, dag, DAG) {
Op_ptr op = get_Op_ptr_from_Vertex(v);
if (op->get_type() == OpType::ClassicalExpBox) {
const ClassicalExpBoxBase &cbox =
static_cast<const ClassicalExpBoxBase &>(*op);
// rename_units is marked as const to get around the Op_ptr
// cast, but it can still mutate a python object
modified |= cbox.rename_units(bm);
}
}
}

return modified;
}

Expand Down
6 changes: 4 additions & 2 deletions tket/include/tket/Predicates/PassGenerators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ PassPtr gen_clifford_push_through_pass();
* Qubits removed from the Circuit are preserved in the bimap, but not updated
* to a new labelling.
*/
PassPtr gen_flatten_relabel_registers_pass(const std::string& label);
PassPtr gen_flatten_relabel_registers_pass(
const std::string& label, bool relabel_classical_expressions = true);
/**
* Pass to rename some or all qubits according to the given map.
*
Expand Down Expand Up @@ -354,12 +355,13 @@ PassPtr gen_special_UCC_synthesis(
* @param max_tqe_candidates
* @param seed
* @param allow_zzphase
* @param timeout
* @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 seed = 0, bool allow_zzphase = false, unsigned timeout = 100);

/**
* Generate a pass to simplify the circuit where it acts on known basis states.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ 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 seed = 0, bool allow_zzphase = false, unsigned timeout = 100);

} // namespace Transforms

Expand Down
4 changes: 2 additions & 2 deletions tket/src/Circuit/basic_circ_manip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ void Circuit::remove_edge(const Edge& edge) {
boost::remove_edge(edge, this->dag);
}

unit_map_t Circuit::flatten_registers() {
unit_map_t Circuit::flatten_registers(bool relabel_classical_expression) {
unit_map_t rename_map;
unsigned q_index = 0;
unsigned c_index = 0;
Expand All @@ -434,7 +434,7 @@ unit_map_t Circuit::flatten_registers() {
}
}
try {
rename_units(rename_map);
rename_units(rename_map, relabel_classical_expression);
} catch (const std::exception& e) {
std::stringstream ss;
ss << "Unable to flatten registers: " << e.what();
Expand Down
6 changes: 4 additions & 2 deletions tket/src/Predicates/CompilerPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,8 @@ PassPtr deserialise(
pp = gen_euler_pass(q, p, s);
} else if (passname == "FlattenRelabelRegistersPass") {
pp = gen_flatten_relabel_registers_pass(
content.at("label").get<std::string>());
content.at("label").get<std::string>(),
content.at("relabel_classical_expressions").get<bool>());
} else if (passname == "RoutingPass") {
Architecture arc = content.at("architecture").get<Architecture>();
std::vector<RoutingMethodPtr> con = content.at("routing_config");
Expand Down Expand Up @@ -521,9 +522,10 @@ 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>();
pp = gen_greedy_pauli_simp(
discount_rate, depth_weight, max_lookahead, max_tqe_candidates, seed,
allow_zzphase);
allow_zzphase, timeout);

} else if (passname == "PauliSimp") {
// SEQUENCE PASS - DESERIALIZABLE ONLY
Expand Down
12 changes: 8 additions & 4 deletions tket/src/Predicates/PassGenerators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,8 @@ PassPtr gen_clifford_push_through_pass() {
return std::make_shared<StandardPass>(precons, t, pc, j);
}

PassPtr gen_flatten_relabel_registers_pass(const std::string& label) {
PassPtr gen_flatten_relabel_registers_pass(
const std::string& label, bool relabel_classical_expressions) {
Transform t =
Transform([=](Circuit& circuit, std::shared_ptr<unit_bimaps_t> maps) {
unsigned n_qubits = circuit.n_qubits();
Expand All @@ -366,7 +367,7 @@ PassPtr gen_flatten_relabel_registers_pass(const std::string& label) {
relabelling_map.insert({all_qubits[i], Qubit(label, i)});
}

circuit.rename_units(relabelling_map);
circuit.rename_units(relabelling_map, relabel_classical_expressions);
changed |= update_maps(maps, relabelling_map, relabelling_map);
return changed;
});
Expand All @@ -376,6 +377,7 @@ PassPtr gen_flatten_relabel_registers_pass(const std::string& label) {
nlohmann::json j;
j["name"] = "FlattenRelabelRegistersPass";
j["label"] = label;
j["relabel_classical_expressions"] = relabel_classical_expressions;
return std::make_shared<StandardPass>(precons, t, postcons, j);
}

Expand Down Expand Up @@ -1015,10 +1017,11 @@ 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 max_tqe_candidates, unsigned seed, bool allow_zzphase,
unsigned timeout) {
Transform t = Transforms::greedy_pauli_optimisation(
discount_rate, depth_weight, max_lookahead, max_tqe_candidates, seed,
allow_zzphase);
allow_zzphase, timeout);
OpTypeSet ins = {
OpType::Z,
OpType::X,
Expand Down Expand Up @@ -1067,6 +1070,7 @@ PassPtr gen_greedy_pauli_simp(
j["max_tqe_candidates"] = max_tqe_candidates;
j["seed"] = seed;
j["allow_zzphase"] = allow_zzphase;
j["timeout"] = timeout;

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

0 comments on commit fbc3ebc

Please sign in to comment.