Skip to content
Merged
2 changes: 1 addition & 1 deletion TKET_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.1.45
2.1.46
1 change: 1 addition & 0 deletions pytket/docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Fixes:
- Update `CH` decomposition to use a single `CX` gate.
- Fix QASM conversion of circuits containing multiple RNG calls writing to the
same register.
- Update Conditional Block merging in `GreedyPauliSimp`.

## 2.9.1 (July 2025)

Expand Down
24 changes: 20 additions & 4 deletions tket/src/Transformations/GreedyPauliConverters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,12 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) {
GPVertSet to_search = end_line_;
GPVertSet commuted;
GPVert new_vert = boost::add_vertex(graph_);
// If the node is a ConditionalBlock, this might
// store another node that can be merged with it
std::optional<GPVert> merged_cond = std::nullopt;

graph_[new_vert] = node;

while (!to_search.empty()) {
// Get next candidate parent
GPVert to_compare = *to_search.begin();
Expand All @@ -176,9 +181,11 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) {
if (block1.cond_bits() == block2.cond_bits() &&
block1.cond_value() == block2.cond_value()) {
block2.append(block1);
boost::clear_vertex(new_vert, graph_);
boost::remove_vertex(new_vert, graph_);
return;
merged_cond = to_compare;
// If the two blocks can be merged,
// we still need to check the new vertex’s dependencies
// on the others
continue;
}
}
if (nodes_commute(node, compare_node)) {
Expand Down Expand Up @@ -222,6 +229,16 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) {
end_line_.erase(to_compare);
}
}
if (merged_cond != std::nullopt) {
// We have merged the new block into this existing conditional block.
// Now we transfer all dependency edges.
for (const GPVert& pred : get_predecessors(new_vert)) {
boost::add_edge(pred, *merged_cond, graph_);
}
boost::clear_vertex(new_vert, graph_);
boost::remove_vertex(new_vert, graph_);
return;
}
end_line_.insert(new_vert);
if (get_predecessors(new_vert).empty()) start_line_.insert(new_vert);
}
Expand Down Expand Up @@ -272,7 +289,6 @@ void GPGraph::apply_gate_at_end(
unit_vector_t args = cmd.get_args();
qubit_vector_t qbs = cmd.get_qubits();
OpType type = op->get_type();

for (const UnitID& arg : args) {
if (arg.type() == UnitType::Qubit) {
auto it = end_measures_.left.find(arg.index().at(0));
Expand Down
22 changes: 21 additions & 1 deletion tket/test/src/test_GreedyPauli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "tket/Predicates/CompilerPass.hpp"
#include "tket/Predicates/PassGenerators.hpp"
#include "tket/Predicates/PassLibrary.hpp"
#include "tket/Transformations/BasicOptimisation.hpp"
#include "tket/Transformations/GreedyPauliOptimisation.hpp"
#include "tket/Utils/Expression.hpp"

Expand Down Expand Up @@ -368,6 +369,23 @@ SCENARIO("Complete synthesis") {
REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ));
REQUIRE(circ == d);
}

GIVEN("Circuit with conditional gates 3") {
Circuit c(2, 1);
c.add_op<unsigned>(OpType::Rx, 1.0246, {0});
c.add_conditional_gate<unsigned>(OpType::Ry, {-0.25}, {1}, {0}, 0);
c.add_conditional_gate<unsigned>(OpType::CZ, {}, {0, 1}, {0}, 0);
REQUIRE(Transforms::greedy_pauli_optimisation().apply(c));

Circuit comp(2, 1);
comp.add_op<unsigned>(OpType::Rx, 1.0246, {0});
comp.add_conditional_gate<unsigned>(OpType::Ry, {-0.25}, {1}, {0}, 0);
comp.add_conditional_gate<unsigned>(OpType::CZ, {}, {1, 0}, {0}, 0);
// Previously Conditional Ry was being commuted ahead of Rz and then CZ
// merged in without checking commuting with Rx Returning the same circuit
// is expected, except the CZ gate is "flipped"
REQUIRE(comp == c);
}
GIVEN("Circuit with conditional gates and measures") {
Circuit circ(2, 2);
Op_ptr cond1 = std::make_shared<Conditional>(
Expand Down Expand Up @@ -405,6 +423,7 @@ SCENARIO("Complete synthesis") {
// should all be canceled
Circuit d(2, 2);
REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ));
Transforms::remove_redundancies().apply(circ);
REQUIRE(circ == d);
}
GIVEN("Circuit with classical gates") {
Expand Down Expand Up @@ -677,7 +696,8 @@ SCENARIO("Complete synthesis") {
.apply(d));
REQUIRE(test_unitary_comparison(circ, d, true));
// if the first XY was implemented using a ZZPhase
// then 2 TQEs is needed to conjugate the remaining two strings to weight 2
// then 2 TQEs is needed to conjugate the remaining two strings to
// weight 2
// hence 5 2-qubit gates in total.
REQUIRE(d.count_n_qubit_gates(2) == 4);
}
Expand Down
Loading