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

Add timeout parameter to GreedyPauliSimp #1664

Merged
merged 21 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
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.41@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
6 changes: 2 additions & 4 deletions pytket/tests/passes_serialisation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
from pathlib import Path
from typing import Any, Dict, List

from sympy import Expr

from pytket.circuit import Node, Circuit, Qubit, OpType
from pytket.predicates import Predicate
from pytket.architecture import Architecture
Expand All @@ -36,7 +34,6 @@
CommuteThroughMultis,
RepeatWithMetricPass,
RebaseCustom,
CXMappingPass,
FullMappingPass,
DefaultMappingPass,
AASRouting,
Expand Down Expand Up @@ -299,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 Expand Up @@ -699,7 +697,7 @@ def tk1_rep(a: ParamType, b: ParamType, c: ParamType) -> Circuit:
np_pass = dm_pass_0.get_sequence()[2]
d_pass = dm_pass.get_sequence()[1]
assert d_pass.to_dict()["StandardPass"]["name"] == "DelayMeasures"
assert d_pass.to_dict()["StandardPass"]["allow_partial"] == False
assert not d_pass.to_dict()["StandardPass"]["allow_partial"]
assert p_pass.to_dict()["StandardPass"]["name"] == "PlacementPass"
assert np_pass.to_dict()["StandardPass"]["name"] == "NaivePlacementPass"
assert r_pass.to_dict()["StandardPass"]["name"] == "RoutingPass"
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 @@ -1040,6 +1040,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 @@ -1062,7 +1063,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.41"
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
Loading
Loading