Skip to content

Commit 3e53889

Browse files
sjdilkesyao-cqc
andauthored
Update merging of Conditional block in greedy_pauli_optimisation (#2014)
* update CH decomposition * update conditional block merging * Update TKET_VERSION * Update changelog.md * bump * Transfer dependency edges when the new conditional block is removed * Run clang format * Keep searching for anti-commuting predecessors after merging conditional blocks * Bump tket version --------- Co-authored-by: yao-cqc <yao.tang@cambridgequantum.com>
1 parent 74f169b commit 3e53889

4 files changed

Lines changed: 43 additions & 6 deletions

File tree

‎TKET_VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.1.45
1+
2.1.46

‎pytket/docs/changelog.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Fixes:
77
- Update `CH` decomposition to use a single `CX` gate.
88
- Fix QASM conversion of circuits containing multiple RNG calls writing to the
99
same register.
10+
- Update Conditional Block merging in `GreedyPauliSimp`.
1011

1112
## 2.9.1 (July 2025)
1213

‎tket/src/Transformations/GreedyPauliConverters.cpp‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,12 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) {
149149
GPVertSet to_search = end_line_;
150150
GPVertSet commuted;
151151
GPVert new_vert = boost::add_vertex(graph_);
152+
// If the node is a ConditionalBlock, this might
153+
// store another node that can be merged with it
154+
std::optional<GPVert> merged_cond = std::nullopt;
155+
152156
graph_[new_vert] = node;
157+
153158
while (!to_search.empty()) {
154159
// Get next candidate parent
155160
GPVert to_compare = *to_search.begin();
@@ -176,9 +181,11 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) {
176181
if (block1.cond_bits() == block2.cond_bits() &&
177182
block1.cond_value() == block2.cond_value()) {
178183
block2.append(block1);
179-
boost::clear_vertex(new_vert, graph_);
180-
boost::remove_vertex(new_vert, graph_);
181-
return;
184+
merged_cond = to_compare;
185+
// If the two blocks can be merged,
186+
// we still need to check the new vertex’s dependencies
187+
// on the others
188+
continue;
182189
}
183190
}
184191
if (nodes_commute(node, compare_node)) {
@@ -222,6 +229,16 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) {
222229
end_line_.erase(to_compare);
223230
}
224231
}
232+
if (merged_cond != std::nullopt) {
233+
// We have merged the new block into this existing conditional block.
234+
// Now we transfer all dependency edges.
235+
for (const GPVert& pred : get_predecessors(new_vert)) {
236+
boost::add_edge(pred, *merged_cond, graph_);
237+
}
238+
boost::clear_vertex(new_vert, graph_);
239+
boost::remove_vertex(new_vert, graph_);
240+
return;
241+
}
225242
end_line_.insert(new_vert);
226243
if (get_predecessors(new_vert).empty()) start_line_.insert(new_vert);
227244
}
@@ -272,7 +289,6 @@ void GPGraph::apply_gate_at_end(
272289
unit_vector_t args = cmd.get_args();
273290
qubit_vector_t qbs = cmd.get_qubits();
274291
OpType type = op->get_type();
275-
276292
for (const UnitID& arg : args) {
277293
if (arg.type() == UnitType::Qubit) {
278294
auto it = end_measures_.left.find(arg.index().at(0));

‎tket/test/src/test_GreedyPauli.cpp‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "tket/Predicates/CompilerPass.hpp"
2828
#include "tket/Predicates/PassGenerators.hpp"
2929
#include "tket/Predicates/PassLibrary.hpp"
30+
#include "tket/Transformations/BasicOptimisation.hpp"
3031
#include "tket/Transformations/GreedyPauliOptimisation.hpp"
3132
#include "tket/Utils/Expression.hpp"
3233

@@ -368,6 +369,23 @@ SCENARIO("Complete synthesis") {
368369
REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ));
369370
REQUIRE(circ == d);
370371
}
372+
373+
GIVEN("Circuit with conditional gates 3") {
374+
Circuit c(2, 1);
375+
c.add_op<unsigned>(OpType::Rx, 1.0246, {0});
376+
c.add_conditional_gate<unsigned>(OpType::Ry, {-0.25}, {1}, {0}, 0);
377+
c.add_conditional_gate<unsigned>(OpType::CZ, {}, {0, 1}, {0}, 0);
378+
REQUIRE(Transforms::greedy_pauli_optimisation().apply(c));
379+
380+
Circuit comp(2, 1);
381+
comp.add_op<unsigned>(OpType::Rx, 1.0246, {0});
382+
comp.add_conditional_gate<unsigned>(OpType::Ry, {-0.25}, {1}, {0}, 0);
383+
comp.add_conditional_gate<unsigned>(OpType::CZ, {}, {1, 0}, {0}, 0);
384+
// Previously Conditional Ry was being commuted ahead of Rz and then CZ
385+
// merged in without checking commuting with Rx Returning the same circuit
386+
// is expected, except the CZ gate is "flipped"
387+
REQUIRE(comp == c);
388+
}
371389
GIVEN("Circuit with conditional gates and measures") {
372390
Circuit circ(2, 2);
373391
Op_ptr cond1 = std::make_shared<Conditional>(
@@ -405,6 +423,7 @@ SCENARIO("Complete synthesis") {
405423
// should all be canceled
406424
Circuit d(2, 2);
407425
REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ));
426+
Transforms::remove_redundancies().apply(circ);
408427
REQUIRE(circ == d);
409428
}
410429
GIVEN("Circuit with classical gates") {
@@ -677,7 +696,8 @@ SCENARIO("Complete synthesis") {
677696
.apply(d));
678697
REQUIRE(test_unitary_comparison(circ, d, true));
679698
// if the first XY was implemented using a ZZPhase
680-
// then 2 TQEs is needed to conjugate the remaining two strings to weight 2
699+
// then 2 TQEs is needed to conjugate the remaining two strings to
700+
// weight 2
681701
// hence 5 2-qubit gates in total.
682702
REQUIRE(d.count_n_qubit_gates(2) == 4);
683703
}

0 commit comments

Comments
 (0)