From 03a309070f4171666656c19bd7f4d47764f1971a Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:00:15 +0000 Subject: [PATCH] add stop flag --- .../GreedyPauliOptimisation.hpp | 25 ++++++ .../GreedyPauliOptimisation.cpp | 81 +++++++++++++++---- 2 files changed, 89 insertions(+), 17 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index bfaebb4179..27343c1a85 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -14,6 +14,8 @@ #pragma once +#include + #include "Transform.hpp" #include "tket/Circuit/Circuit.hpp" #include "tket/Clifford/UnitaryTableau.hpp" @@ -600,6 +602,29 @@ class GPGraph { std::tuple, std::vector> gpg_from_unordered_set(const std::vector& unordered_set); +/** + * @brief Converts the given circuit into a GPGraph and conjugates each node + * by greedily applying 2-qubit Clifford gates until the node can be realised + * as a single-qubit gate, a measurement, or a reset. The final Clifford + * operator is synthesized in a similar fashion. Allows early termination + * from a thread via a stop_flag. + * + * @param circ + * @param stop_flag + * @param discount_rate + * @param depth_weight + * @param max_lookahead + * @param max_tqe_candidates + * @param seed + * @param allow_zzphase + * @return Circuit + */ +Circuit greedy_pauli_graph_synthesis_flag( + const Circuit& circ, std::atomic& stop_flag, + 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); + /** * @brief Converts the given circuit into a GPGraph and conjugates each node * by greedily applying 2-qubit Clifford gates until the node can be realised diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 2920ad5941..831fdacd93 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -328,7 +328,7 @@ struct DepthTracker { static void tableau_row_nodes_synthesis( std::vector& rows, Circuit& circ, DepthTracker& depth_tracker, double depth_weight, unsigned max_lookahead, - unsigned max_tqe_candidates, unsigned seed) { + unsigned max_tqe_candidates, unsigned seed, std::atomic& stop_flag) { // only consider nodes with a non-zero cost std::vector remaining_indices; for (unsigned i = 0; i < rows.size(); i++) { @@ -337,6 +337,10 @@ static void tableau_row_nodes_synthesis( } } while (remaining_indices.size() != 0) { + // check early termination + if (stop_flag) { + return; + }; // get nodes with min cost std::vector min_nodes_indices = {remaining_indices[0]}; unsigned min_cost = rows[remaining_indices[0]]->tqe_cost(); @@ -608,10 +612,16 @@ static void pauli_exps_synthesis( std::vector& rows, Circuit& circ, DepthTracker& depth_tracker, double discount_rate, double depth_weight, unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, - bool allow_zzphase) { + bool allow_zzphase, std::atomic& stop_flag) { while (true) { + // check timeout + if (stop_flag) { + return; + }; + consume_nodes( rotation_sets, circ, depth_tracker, discount_rate, depth_weight); + if (rotation_sets.empty()) break; std::vector& first_set = rotation_sets[0]; // get nodes with min cost @@ -635,6 +645,7 @@ static void pauli_exps_synthesis( // sample std::vector sampled_tqes = sample_tqes(tqe_candidates, max_tqe_candidates, seed); + // for each tqe we compute costs which might subject to normalisation std::map> tqe_candidates_cost; for (const TQE& tqe : sampled_tqes) { @@ -722,21 +733,22 @@ Circuit greedy_pauli_set_synthesis( std::vector> rotation_sets{rotation_set}; DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps + std::atomic dummy_stop_flag(false); pauli_exps_synthesis( rotation_sets, rows, c, depth_tracker, 0, depth_weight, max_lookahead, - max_tqe_candidates, seed, allow_zzphase); + max_tqe_candidates, seed, allow_zzphase, dummy_stop_flag); // synthesise the tableau tableau_row_nodes_synthesis( rows, c, depth_tracker, depth_weight, max_lookahead, max_tqe_candidates, - seed); + seed, dummy_stop_flag); c.replace_SWAPs(); return c; } -Circuit greedy_pauli_graph_synthesis( - const Circuit& circ, double discount_rate, double depth_weight, - unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, - bool allow_zzphase) { +Circuit greedy_pauli_graph_synthesis_flag( + const Circuit& circ, std::atomic& stop_flag, double discount_rate, + double depth_weight, unsigned max_lookahead, unsigned max_tqe_candidates, + unsigned seed, bool allow_zzphase) { if (max_lookahead == 0) { throw GreedyPauliSimpError("max_lookahead must be greater than 0."); } @@ -758,16 +770,36 @@ Circuit greedy_pauli_graph_synthesis( rev_unit_map.insert({pair.second, pair.first}); } GPGraph gpg(circ_flat); + + // We regularly check whether the timeout has ocurred + if (stop_flag) { + return Circuit(); + } + auto [rotation_sets, rows, measures] = gpg.get_sequence(); + + if (stop_flag) { + return Circuit(); + } + DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( rotation_sets, rows, new_circ, depth_tracker, discount_rate, depth_weight, - max_lookahead, max_tqe_candidates, seed, allow_zzphase); + max_lookahead, max_tqe_candidates, seed, allow_zzphase, stop_flag); + + if (stop_flag) { + return Circuit(); + } // synthesise the tableau tableau_row_nodes_synthesis( rows, new_circ, depth_tracker, depth_weight, max_lookahead, - max_tqe_candidates, seed); + max_tqe_candidates, seed, stop_flag); + + if (stop_flag) { + return Circuit(); + } + for (auto it = measures.begin(); it != measures.end(); ++it) { new_circ.add_measure(it->left, it->right); } @@ -776,6 +808,16 @@ Circuit greedy_pauli_graph_synthesis( return new_circ; } +Circuit greedy_pauli_graph_synthesis( + const Circuit& circ, double discount_rate, double depth_weight, + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, + bool allow_zzphase) { + std::atomic dummy_stop_flag(false); + return greedy_pauli_graph_synthesis_flag( + circ, dummy_stop_flag, discount_rate, depth_weight, max_lookahead, + max_tqe_candidates, seed, allow_zzphase); +} + } // namespace GreedyPauliSimp Transform greedy_pauli_optimisation( @@ -786,7 +828,9 @@ Transform greedy_pauli_optimisation( max_tqe_candidates, seed, allow_zzphase, thread_timeout, trials, threads](Circuit& circ) { std::mt19937 seed_gen(seed); - std::queue> all_threads; + std::queue< + std::pair, std::shared_ptr>>> + all_threads; std::vector circuits; unsigned max_threads = std::min(threads, std::thread::hardware_concurrency()); @@ -797,11 +841,13 @@ Transform greedy_pauli_optimisation( while (threads_started < trials || !all_threads.empty()) { // Start new jobs if we haven't reached the max threads or trials if (threads_started < trials && all_threads.size() < max_threads) { + auto stop_flag = std::make_shared>(false); std::future future = std::async( - std::launch::async, GreedyPauliSimp::greedy_pauli_graph_synthesis, - circ, discount_rate, depth_weight, max_lookahead, + std::launch::async, + GreedyPauliSimp::greedy_pauli_graph_synthesis_flag, circ, + std::ref(*stop_flag), discount_rate, depth_weight, max_lookahead, max_tqe_candidates, seed_gen(), allow_zzphase); - all_threads.push(std::move(future)); + all_threads.emplace(std::move(future), stop_flag); threads_started++; // continue to come straight back to this if statement, meaning we // maximise threads @@ -809,7 +855,7 @@ Transform greedy_pauli_optimisation( } // Check the oldest thread for completion - auto& thread = all_threads.front(); + auto& [thread, stop_flag] = all_threads.front(); if (thread.wait_for(std::chrono::seconds(thread_timeout)) == std::future_status::ready) { Circuit c = thread.get(); @@ -818,7 +864,7 @@ Transform greedy_pauli_optimisation( all_threads.pop(); } else { // If the thread is not ready, move it to the back of the queue - all_threads.push(std::move(all_threads.front())); + *stop_flag = true; all_threads.pop(); } } @@ -835,7 +881,7 @@ Transform greedy_pauli_optimisation( // Return the smallest circuit if any were found within the single // thread_timeout if (circuits.empty()) return false; - circ = *std::min_element( + auto min = std::min_element( circuits.begin(), circuits.end(), [](const Circuit& a, const Circuit& b) { return std::make_tuple( @@ -843,6 +889,7 @@ Transform greedy_pauli_optimisation( std::make_tuple( b.count_n_qubit_gates(2), b.n_gates(), b.depth()); }); + circ = *min; return true; }); }