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

Allow constant ZZPhase fidelity in DecomposeTK2 #1558

Merged
merged 22 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 20 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
8 changes: 4 additions & 4 deletions pytket/binders/passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Transforms::TwoQbFidelities get_fidelities(const py::kwargs &kwargs) {
} else if (kwargstr == "ZZMax_fidelity") {
fid.ZZMax_fidelity = py::cast<double>(kwarg.second);
} else if (kwargstr == "ZZPhase_fidelity") {
fid.ZZPhase_fidelity = py::cast<Func>(kwarg.second);
fid.ZZPhase_fidelity = py::cast<std::variant<double, Func>>(kwarg.second);
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
} else {
throw py::type_error(
"got an unexpected keyword argument '" + kwargstr + "'");
Expand Down Expand Up @@ -405,9 +405,9 @@ PYBIND11_MODULE(passes, m) {
"`ZZPhase_fidelity`. If provided, the `CX` and `ZZMax` fidelities "
"must be given by a single floating point fidelity. The `ZZPhase` "
"fidelity is given as a lambda float -> float, mapping a ZZPhase "
"angle parameter to its fidelity. These parameters will be used "
"to return the optimal decomposition of each TK2 gate, taking "
"noise into consideration.\n\n"
"angle parameter to its fidelity, or by a single float. These parameters "
"will be used to return the optimal decomposition of each TK2 gate, "
"taking noise into consideration.\n\n"
"If no fidelities are provided, the TK2 gates will be decomposed "
"exactly using CX gates. For equal fidelities, ZZPhase will be prefered "
"over ZZMax and CX if the decomposition results in fewer two-qubit "
Expand Down
8 changes: 4 additions & 4 deletions pytket/binders/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Transforms::TwoQbFidelities get_fidelities(const py::kwargs &kwargs) {
} else if (kwargstr == "ZZMax_fidelity") {
fid.ZZMax_fidelity = py::cast<double>(kwarg.second);
} else if (kwargstr == "ZZPhase_fidelity") {
fid.ZZPhase_fidelity = py::cast<Func>(kwarg.second);
fid.ZZPhase_fidelity = py::cast<std::variant<double, Func>>(kwarg.second);
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
} else {
throw py::type_error(
"got an unexpected keyword argument '" + kwargstr + "'");
Expand Down Expand Up @@ -228,9 +228,9 @@ PYBIND11_MODULE(transform, m) {
"`ZZPhase_fidelity`. If provided, the `CX` and `ZZMax` fidelities "
"must be given by a single floating point fidelity. The `ZZPhase` "
"fidelity is given as a lambda float -> float, mapping a ZZPhase "
"angle parameter to its fidelity. These parameters will be used "
"to return the optimal decomposition of each TK2 gate, taking "
"noise into consideration.\n\n"
"angle parameter to its fidelity, or by a single float. These "
"parameters will be used to return the optimal decomposition of each "
"TK2 gate, taking noise into consideration.\n\n"
"Using the `allow_swaps=True` (default) option, qubits will be "
"swapped when convenient to reduce the two-qubit gate count of the "
"decomposed TK2.\n\n"
Expand Down
2 changes: 1 addition & 1 deletion pytket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def package(self):
cmake.install()

def requirements(self):
self.requires("tket/1.3.20@tket/stable")
self.requires("tket/1.3.21@tket/stable")
self.requires("tklog/0.3.3@tket/stable")
self.requires("tkrng/0.3.3@tket/stable")
self.requires("tkassert/0.3.4@tket/stable")
Expand Down
8 changes: 8 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Changelog
=========

Unreleased
----------

Features:

* DecomposeTK2 pass and transform can now accept a float for ZZPhase_fidelity.
* DecomposeTK2 pass now has a json representation when it contains no functions.

1.32.0 (September 2024)
-----------------------

Expand Down
2 changes: 1 addition & 1 deletion pytket/pytket/_tket/passes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ def DecomposeTK2(allow_swaps: bool = True, **kwargs: Any) -> BasePass:

Gate fidelities can be passed as keyword arguments to perform noise-aware decompositions. If the fidelities of several gate types are provided, the best will be chosen.

We currently support `CX_fidelity`, `ZZMax_fidelity` and `ZZPhase_fidelity`. If provided, the `CX` and `ZZMax` fidelities must be given by a single floating point fidelity. The `ZZPhase` fidelity is given as a lambda float -> float, mapping a ZZPhase angle parameter to its fidelity. These parameters will be used to return the optimal decomposition of each TK2 gate, taking noise into consideration.
We currently support `CX_fidelity`, `ZZMax_fidelity` and `ZZPhase_fidelity`. If provided, the `CX` and `ZZMax` fidelities must be given by a single floating point fidelity. The `ZZPhase` fidelity is given as a lambda float -> float, mapping a ZZPhase angle parameter to its fidelity, or by a single float. These parameters will be used to return the optimal decomposition of each TK2 gate, taking noise into consideration.

If no fidelities are provided, the TK2 gates will be decomposed exactly using CX gates. For equal fidelities, ZZPhase will be prefered over ZZMax and CX if the decomposition results in fewer two-qubit gates.

Expand Down
2 changes: 1 addition & 1 deletion pytket/pytket/_tket/transform.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class Transform:

All TK2 gate parameters must be normalised, i.e. they must satisfy `NormalisedTK2Predicate`.

Gate fidelities are passed as keyword arguments to perform noise-aware decompositions. We currently support `CX_fidelity`, `ZZMax_fidelity` and `ZZPhase_fidelity`. If provided, the `CX` and `ZZMax` fidelities must be given by a single floating point fidelity. The `ZZPhase` fidelity is given as a lambda float -> float, mapping a ZZPhase angle parameter to its fidelity. These parameters will be used to return the optimal decomposition of each TK2 gate, taking noise into consideration.
Gate fidelities are passed as keyword arguments to perform noise-aware decompositions. We currently support `CX_fidelity`, `ZZMax_fidelity` and `ZZPhase_fidelity`. If provided, the `CX` and `ZZMax` fidelities must be given by a single floating point fidelity. The `ZZPhase` fidelity is given as a lambda float -> float, mapping a ZZPhase angle parameter to its fidelity, or by a single float. These parameters will be used to return the optimal decomposition of each TK2 gate, taking noise into consideration.

Using the `allow_swaps=True` (default) option, qubits will be swapped when convenient to reduce the two-qubit gate count of the decomposed TK2.

Expand Down
12 changes: 12 additions & 0 deletions pytket/tests/passes_serialisation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,18 @@ def nonparam_predicate_dict(name: str) -> Dict[str, Any]:
"AutoRebase": standard_pass_dict(
{"name": "AutoRebase", "basis_allowed": ["H", "TK1", "CX"], "allow_swaps": True}
),
# ZZPhase must be a float and not a function.
"DecomposeTK2": standard_pass_dict(
{
"name": "DecomposeTK2",
"fidelities": {
"CX": None,
"ZZMax": 1.0,
"ZZPhase": 0.5,
},
"allow_swaps": True,
}
),
}

# non-parametrized passes that satisfy pass.from_dict(d).to_dict()==d
Expand Down
10 changes: 10 additions & 0 deletions pytket/tests/transform_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,16 @@ def test_DecomposeTK2() -> None:
assert c.n_gates_of_type(OpType.CX) == 0
assert c.n_gates_of_type(OpType.ZZMax) == 3

c = Circuit(2).add_gate(OpType.TK2, [0.5, 0.5, 0.5], [0, 1])
Transform.DecomposeTK2(False, ZZPhase_fidelity=0.8).apply(c)
assert c.n_gates_of_type(OpType.CX) == 0
assert c.n_gates_of_type(OpType.ZZPhase) == 3

c = Circuit(2).add_gate(OpType.TK2, [0.5, 0.5, 0.5], [0, 1])
Transform.DecomposeTK2(False, ZZPhase_fidelity=lambda _: 0.8).apply(c)
assert c.n_gates_of_type(OpType.CX) == 0
assert c.n_gates_of_type(OpType.ZZPhase) == 3


def test_fidelity_KAK() -> None:
c = get_KAK_test_circuit()
Expand Down
5 changes: 5 additions & 0 deletions schemas/compiler_pass_v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,11 @@
"type": ["number", "null"],
"minimum": 0,
"maximum": 1
},
"ZZPhase": {
"type": ["number", "null"],
"minimum": 0,
"maximum": 1
}
}
},
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.20"
version = "1.3.21"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
3 changes: 2 additions & 1 deletion tket/include/tket/Transformations/Transform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ inline const Transform id =
struct TwoQbFidelities {
std::optional<double> CX_fidelity;
std::optional<double> ZZMax_fidelity;
std::optional<std::function<double(double)>> ZZPhase_fidelity;
std::optional<std::variant<double, std::function<double(double)>>>
ZZPhase_fidelity;
};

} // namespace Transforms
Expand Down
9 changes: 9 additions & 0 deletions tket/include/tket/Utils/Expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
#include "Json.hpp"
#include "Symbols.hpp"

/** Helper struct for use with std::visit */
johnchildren marked this conversation as resolved.
Show resolved Hide resolved
template <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
// explicit deduction guide (not needed as of C++20)
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

namespace tket {

/** Representation of a phase as a multiple of \f$ \pi \f$ */
Expand Down
5 changes: 4 additions & 1 deletion tket/src/Predicates/CompilerPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "tket/Predicates/CompilerPass.hpp"

#include <memory>
#include <optional>
#include <tklog/TketLog.hpp>

#include "tket/Mapping/RoutingMethodJson.hpp"
Expand Down Expand Up @@ -393,7 +394,9 @@ void from_json(const nlohmann::json& j, PassPtr& pp) {
content.at("fidelities").at("CX").get<std::optional<double>>();
fid.ZZMax_fidelity =
content.at("fidelities").at("ZZMax").get<std::optional<double>>();
fid.ZZPhase_fidelity = std::nullopt;
fid.ZZPhase_fidelity =
content.at("fidelities")
.value<std::optional<double>>("ZZPhase", std::nullopt);
bool allow_swaps = content.at("allow_swaps").get<bool>();
pp = DecomposeTK2(fid, allow_swaps);
} else if (passname == "PeepholeOptimise2Q") {
Expand Down
13 changes: 12 additions & 1 deletion tket/src/Predicates/PassGenerators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,18 @@ PassPtr DecomposeTK2(const Transforms::TwoQbFidelities& fid, bool allow_swaps) {
j["allow_swaps"] = allow_swaps;
nlohmann::json fid_json;
fid_json["CX"] = fid.CX_fidelity;
fid_json["ZZPhase"] = "SERIALIZATION OF FUNCTIONS IS NOT SUPPORTED";
if (fid.ZZPhase_fidelity.has_value()) {
std::visit(
overloaded{
[&fid_json](double arg) { fid_json["ZZPhase"] = arg; },
[&fid_json](std::function<double(double)>) {
fid_json["ZZPhase"] =
"SERIALIZATION OF FUNCTIONS IS NOT SUPPORTED";
}},
fid.ZZPhase_fidelity.value());
} else {
fid_json["ZZPhase"] = std::nullptr_t{};
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
}
fid_json["ZZMax"] = fid.ZZMax_fidelity;
j["fidelities"] = fid_json;
return std::make_shared<StandardPass>(precons, t, postcons, j);
Expand Down
17 changes: 15 additions & 2 deletions tket/src/Transformations/Decomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,14 @@ static double best_noise_aware_decomposition(
unsigned max_nzz = fid.ZZMax_fidelity ? 1 : 3;
for (unsigned n_zz = 0; n_zz <= max_nzz; ++n_zz) {
if (n_zz > 0) {
double gate_fid = (*fid.ZZPhase_fidelity)(angles[n_zz - 1]);
double gate_fid = std::visit(
overloaded{// Constant value
[](double arg) { return arg; },
// A value depending on the angle
[angles, n_zz](std::function<double(double)> arg) {
johnchildren marked this conversation as resolved.
Show resolved Hide resolved
return (arg)(angles[n_zz - 1]);
}},
*fid.ZZPhase_fidelity);
if (gate_fid < 0 || gate_fid > 1) {
throw std::domain_error(
"ZZPhase_fidelity returned a value outside of [0, 1].");
Expand Down Expand Up @@ -857,7 +864,13 @@ Transform decompose_TK2(const TwoQbFidelities &fid, bool allow_swaps) {
}
}
if (fid.ZZMax_fidelity && fid.ZZPhase_fidelity) {
if (*fid.ZZMax_fidelity < (*fid.ZZPhase_fidelity)(.5)) {
double ZZPhase_half = std::visit(
johnchildren marked this conversation as resolved.
Show resolved Hide resolved
overloaded{// A constant value.
[](double arg) { return arg; },
// A value depending on the input.
[](std::function<double(double)> arg) { return (arg)(.5); }},
*fid.ZZPhase_fidelity);
if (*fid.ZZMax_fidelity < ZZPhase_half) {
throw std::domain_error(
"The ZZMax fidelity cannot be smaller than the ZZPhase(0.5) "
"fidelity");
Expand Down
1 change: 1 addition & 0 deletions tket/test/src/test_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ SCENARIO("Test compiler pass serializations") {
COMPPASSJSONTEST(KAKDecomposition, KAKDecomposition(OpType::CX, 0.98))
COMPPASSJSONTEST(
DecomposeTK2, DecomposeTK2({0.98, std::nullopt, std::nullopt}, false))
COMPPASSJSONTEST(DecomposeTK2, DecomposeTK2({0.98, 0.98, 0.98}))
COMPPASSJSONTEST(ThreeQubitSquash, ThreeQubitSquash(false))
COMPPASSJSONTEST(
EulerAngleReduction, gen_euler_pass(OpType::Rx, OpType::Ry, false))
Expand Down
Loading