From 575587467019695a9c4b3751f78d4cff22382bec Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Tue, 5 Mar 2024 18:03:47 +0800 Subject: [PATCH 1/5] :recycle: refactor QCir::add_gate; cut down esoteric functions --- src/convert/qcir_to_tensor.cpp | 3 +- src/duostra/mapping_eqv_checker.cpp | 1 - src/extractor/extract.cpp | 37 +---- src/extractor/extract.hpp | 2 - src/qcir/optimizer/basic_optimization.cpp | 3 +- src/qcir/optimizer/trivial_optimization.cpp | 6 +- src/qcir/qcir.cpp | 141 +++++++++----------- src/qcir/qcir.hpp | 28 +--- src/qcir/qcir_action.cpp | 59 ++++---- src/qcir/qcir_cmd.cpp | 2 +- src/qcir/qcir_gate.cpp | 17 +-- src/qcir/qcir_gate.hpp | 8 +- src/qcir/qcir_print.cpp | 16 +-- src/qcir/qcir_writer.cpp | 1 - tests/conversion/zx2qc/ref/extractStep.log | 6 +- 15 files changed, 123 insertions(+), 207 deletions(-) diff --git a/src/convert/qcir_to_tensor.cpp b/src/convert/qcir_to_tensor.cpp index 59690a79..41fcf741 100644 --- a/src/convert/qcir_to_tensor.cpp +++ b/src/convert/qcir_to_tensor.cpp @@ -118,14 +118,13 @@ std::optional> to_tensor(QCir const &qcir) try { spdlog::warn("QCir is empty!!"); return std::nullopt; } - qcir.update_topological_order(); spdlog::debug("Add boundary"); QTensor tensor; // NOTE: Constucting an identity(_qubit.size()) takes much time and memory. // To make this process interruptible by SIGINT (ctrl-C), we grow the qubit size one by one - for (size_t i = 0; i < qcir.get_qubits().size(); ++i) { + for (size_t i = 0; i < qcir.get_num_qubits(); ++i) { if (stop_requested()) { spdlog::warn("Conversion interrupted."); return std::nullopt; diff --git a/src/duostra/mapping_eqv_checker.cpp b/src/duostra/mapping_eqv_checker.cpp index 1dddda31..640483cf 100644 --- a/src/duostra/mapping_eqv_checker.cpp +++ b/src/duostra/mapping_eqv_checker.cpp @@ -32,7 +32,6 @@ MappingEquivalenceChecker::MappingEquivalenceChecker(QCir* phy, QCir* log, Devic init = placer->place_and_assign(_device); } else _device.place(init); - _physical->update_topological_order(); for (auto const& qubit : _logical->get_qubits()) { _dependency[qubit->get_id()] = _reverse ? qubit->get_last() : qubit->get_first(); } diff --git a/src/extractor/extract.cpp b/src/extractor/extract.cpp index 59b64ac3..7de139e5 100644 --- a/src/extractor/extract.cpp +++ b/src/extractor/extract.cpp @@ -183,12 +183,12 @@ void Extractor::extract_singles() { std::vector> toggle_list; for (ZXVertex* o : _graph->get_outputs()) { if (_graph->get_first_neighbor(o).second == EdgeType::hadamard) { - prepend_single_qubit_gate("h", _qubit_map[o->get_qubit()], dvlab::Phase(0)); + _logical_circuit->add_gate("h", {_qubit_map[o->get_qubit()]}, dvlab::Phase(0), false); toggle_list.emplace_back(o, _graph->get_first_neighbor(o).first); } auto const ph = _graph->get_first_neighbor(o).first->get_phase(); if (ph != dvlab::Phase(0)) { - prepend_single_qubit_gate("rotate", _qubit_map[o->get_qubit()], ph); + _logical_circuit->add_gate("pz", {_qubit_map[o->get_qubit()]}, ph, false); _graph->get_first_neighbor(o).first->set_phase(dvlab::Phase(0)); } } @@ -269,7 +269,7 @@ void Extractor::extract_cxs() { auto ctrl = _qubit_map[front_id2_vertex[c]->get_qubit()]; auto targ = _qubit_map[front_id2_vertex[t]->get_qubit()]; spdlog::debug("Adding CX: {} {}", ctrl, targ); - prepend_double_qubit_gate("cx", {ctrl, targ}, dvlab::Phase(0)); + _logical_circuit->add_gate("cx", {ctrl, targ}, dvlab::Phase(0), false); } } @@ -323,7 +323,7 @@ size_t Extractor::extract_hadamards_from_matrix(bool check) { for (auto& [f, n] : front_neigh_pairs) { // NOTE - Add Hadamard according to the v of frontier (row) - prepend_single_qubit_gate("h", _qubit_map[f->get_qubit()], dvlab::Phase(0)); + _logical_circuit->add_gate("h", {_qubit_map[f->get_qubit()]}, dvlab::Phase(0), false); // NOTE - Set #qubit and #col according to the old frontier n->set_qubit(f->get_qubit()); n->set_col(f->get_col()); @@ -777,7 +777,7 @@ void Extractor::update_neighbors() { for (auto& [b, ep] : _graph->get_neighbors(f)) { if (_graph->get_inputs().contains(b)) { if (ep == EdgeType::hadamard) { - prepend_single_qubit_gate("h", _qubit_map[f->get_qubit()], dvlab::Phase(0)); + _logical_circuit->add_gate("h", {_qubit_map[f->get_qubit()]}, dvlab::Phase(0), false); } break; } @@ -838,33 +838,6 @@ void Extractor::update_matrix() { _biadjacency = get_biadjacency_matrix(*_graph, _frontier, _neighbors); } -/** - * @brief Prepend single-qubit gate to circuit. If _device is given, directly map to physical device. - * - * @param type - * @param qubit logical - * @param phase - */ -void Extractor::prepend_single_qubit_gate(std::string const& type, QubitIdType qubit, dvlab::Phase phase) { - if (type == "rotate") { - _logical_circuit->add_single_rz(qubit, phase, false); - } else { - _logical_circuit->add_gate(type, {qubit}, phase, false); - } -} - -/** - * @brief Prepend double-qubit gate to circuit. If _device is given, directly map to physical device. - * - * @param type - * @param qubits - * @param phase - */ -void Extractor::prepend_double_qubit_gate(std::string const& type, QubitIdList const& qubits, dvlab::Phase phase) { - assert(qubits.size() == 2); - _logical_circuit->add_gate(type, qubits, phase, false); -} - /** * @brief Prepend series of gates. * diff --git a/src/extractor/extract.hpp b/src/extractor/extract.hpp index 5cd5b6c4..3bf6628b 100644 --- a/src/extractor/extract.hpp +++ b/src/extractor/extract.hpp @@ -65,8 +65,6 @@ class Extractor { void update_graph_by_matrix(qsyn::zx::EdgeType et = qsyn::zx::EdgeType::hadamard); void update_matrix(); - void prepend_single_qubit_gate(std::string const& type, QubitIdType qubit, dvlab::Phase phase); - void prepend_double_qubit_gate(std::string const& type, QubitIdList const& qubits, dvlab::Phase phase); void prepend_series_gates(std::vector const& logical, std::vector const& physical = {}); void prepend_swap_gate(QubitIdType q0, QubitIdType q1, qcir::QCir* circuit); bool frontier_is_cleaned(); diff --git a/src/qcir/optimizer/basic_optimization.cpp b/src/qcir/optimizer/basic_optimization.cpp index 22d627e9..db185024 100644 --- a/src/qcir/optimizer/basic_optimization.cpp +++ b/src/qcir/optimizer/basic_optimization.cpp @@ -84,7 +84,6 @@ QCir Optimizer::_parse_once(QCir const& qcir, bool reversed, bool do_minimize_cz : spdlog::debug("Start parsing forward"); reset(qcir); - qcir.update_topological_order(); std::vector gates = qcir.get_topologically_ordered_gates(); if (reversed) { std::reverse(gates.begin(), gates.end()); @@ -541,7 +540,7 @@ void Optimizer::_add_rotation_gate(QubitIdType target, dvlab::Phase ph, GateRota std::vector Optimizer::_compute_stats(QCir const& circuit) { size_t two_qubit = 0, had = 0, non_pauli = 0; std::vector stats; - for (auto const& g : circuit.update_topological_order()) { + for (auto const& g : circuit.get_topologically_ordered_gates()) { if (g->is_cx() || g->is_cz()) { two_qubit++; } else if (g->is_h()) { diff --git a/src/qcir/optimizer/trivial_optimization.cpp b/src/qcir/optimizer/trivial_optimization.cpp index 7a0f2b1c..f4fe091c 100644 --- a/src/qcir/optimizer/trivial_optimization.cpp +++ b/src/qcir/optimizer/trivial_optimization.cpp @@ -81,7 +81,7 @@ std::optional Optimizer::trivial_optimization(QCir const& qcir) { * @return vector with size = circuit->getNqubit() */ std::vector Optimizer::_get_first_layer_gates(QCir& qcir, bool from_last) { - std::vector gate_list = qcir.update_topological_order(); + std::vector gate_list = qcir.get_topologically_ordered_gates(); if (from_last) reverse(gate_list.begin(), gate_list.end()); std::vector result; std::vector blocked; @@ -213,7 +213,6 @@ QCir replace_gate_sequence(QCir& qcir, QubitIdType qubit, size_t gate_num, replaced.add_qubits(qcir.get_num_qubits()); replaced.set_gate_set(qcir.get_gate_set()); - qcir.update_topological_order(); auto const& gate_list = qcir.get_topologically_ordered_gates(); size_t replace_count = 0; @@ -275,7 +274,6 @@ std::vector zx_optimize(std::vector const& partial) { extractor::Extractor ext(&zx, nullptr, std::nullopt); QCir* result = ext.extract(); - result->update_topological_order(); auto const gate_list = result->get_topologically_ordered_gates(); std::vector opt_partial; opt_partial.reserve(gate_list.size()); @@ -290,7 +288,6 @@ std::vector zx_optimize(std::vector const& partial) { void Optimizer::_partial_zx_optimization(QCir& qcir) { for (auto const qubit : std::views::iota(0ul, qcir.get_num_qubits())) { - qcir.update_topological_order(); auto const gate_list = qcir.get_topologically_ordered_gates(); std::vector type_seq; for (auto gate : gate_list) { @@ -330,7 +327,6 @@ void Optimizer::_partial_zx_optimization(QCir& qcir) { } for (auto const& [lhs, rhs] : replace_rules) { - qcir.update_topological_order(); auto const updated_gate_list = qcir.get_topologically_ordered_gates(); std::vector updated_type_seq; for (auto gate : updated_gate_list) { diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index 03dd0f9a..723c461f 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -32,13 +32,12 @@ namespace qsyn::qcir { QCir::QCir(QCir const &other) { namespace views = std::ranges::views; - other.update_topological_order(); this->add_qubits(other._qubits.size()); for (size_t i = 0; i < _qubits.size(); i++) { _qubits[i]->set_id(other._qubits[i]->get_id()); } - for (auto &gate : other._topological_order) { + for (auto &gate : other.get_topologically_ordered_gates()) { auto bit_range = gate->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; }); auto new_gate = this->add_gate( @@ -47,24 +46,24 @@ QCir::QCir(QCir const &other) { new_gate->set_id(gate->get_id()); } - if (!other._qgates.empty()) { - this->_set_next_gate_id(1 + std::ranges::max( - other._qgates | views::transform( - [](QCirGate *g) { return g->get_id(); }))); - } else { - this->_set_next_gate_id(0); - } - - if (!other._qubits.empty()) { - this->_set_next_qubit_id(1 + std::ranges::max( - other._qubits | views::transform( - [](QCirQubit *qb) { return qb->get_id(); }))); - } else { - this->_set_next_qubit_id(0); - } + _gate_id = other._qgates.empty() + ? 0 + : 1 + std::ranges::max( + other._qgates | + views::transform( + [](QCirGate *g) { return g->get_id(); })); + + _qubit_id = other._qubits.empty() + ? 0 + : 1 + std::ranges::max( + other._qubits | + views::transform( + [](QCirQubit *qb) { return qb->get_id(); })); this->set_filename(other._filename); this->add_procedures(other._procedures); + + _dirty = true; } /** * @brief Get Gate. @@ -96,8 +95,7 @@ QCirQubit *QCir::get_qubit(QubitIdType id) const { size_t QCir::calculate_depth() const { if (is_empty()) return 0; - if (_dirty) update_gate_time(); - _dirty = false; + update_gate_time(); return std::ranges::max(_qgates | std::views::transform([](QCirGate *qg) { return qg->get_time(); })); } @@ -164,30 +162,6 @@ bool QCir::remove_qubit(QubitIdType id) { } } -/** - * @brief Add rotate-z gate and transform it to T, S, Z, Sdg, or Tdg if possible - * - * @param bit - * @param phase - * @param append if true, append the gate else prepend - * @return QCirGate* - */ -QCirGate *QCir::add_single_rz(QubitIdType bit, dvlab::Phase phase, bool append) { - auto qubit = QubitIdList{bit}; - if (phase == dvlab::Phase(1, 4)) - return add_gate("t", qubit, phase, append); - else if (phase == dvlab::Phase(1, 2)) - return add_gate("s", qubit, phase, append); - else if (phase == dvlab::Phase(1)) - return add_gate("z", qubit, phase, append); - else if (phase == dvlab::Phase(3, 2)) - return add_gate("sdg", qubit, phase, append); - else if (phase == dvlab::Phase(7, 4)) - return add_gate("tdg", qubit, phase, append); - else - return add_gate("rz", qubit, phase, append); -} - /** * @brief Add Gate * @@ -213,43 +187,55 @@ QCirGate *QCir::add_gate(std::string type, QubitIdList bits, dvlab::Phase phase, if (gate_phase.has_value()) { phase = gate_phase.value(); } - auto temp = new QCirGate(_gate_id, category, phase); - - if (append) { - size_t max_time = 0; - for (size_t k = 0; k < bits.size(); k++) { - auto q = bits[k]; - temp->add_qubit(q, k == bits.size() - 1); // target is the last one - QCirQubit *target = get_qubit(q); - if (target->get_last() != nullptr) { - temp->set_parent(q, target->get_last()); - target->get_last()->set_child(q, temp); - if ((target->get_last()->get_time()) > max_time) - max_time = target->get_last()->get_time(); - } else { - target->set_first(temp); - } - target->set_last(temp); + auto temp = QCirGate(_gate_id++, category, phase); + + for (size_t k = 0; k < bits.size(); k++) { + temp.add_qubit(bits[k], k == bits.size() - 1); // target is the last one + } + + return append + ? this->append(std::move(temp)) + : this->prepend(std::move(temp)); +} + +QCirGate *QCir::append(QCirGate gate) { + _qgates.emplace_back(new QCirGate(std::move(gate))); + auto *g = _qgates.back(); + + size_t max_time = 0; + for (auto const &qb : g->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; })) { + QCirQubit *target = get_qubit(qb); + if (target->get_last() != nullptr) { + g->set_parent(qb, target->get_last()); + target->get_last()->set_child(qb, g); + if ((target->get_last()->get_time()) > max_time) + max_time = target->get_last()->get_time(); + } else { + target->set_first(g); } - temp->set_time(max_time + temp->get_delay()); - } else { - for (size_t k = 0; k < bits.size(); k++) { - auto q = bits[k]; - temp->add_qubit(q, k == bits.size() - 1); // target is the last one - QCirQubit *target = get_qubit(q); - if (target->get_first() != nullptr) { - temp->set_child(q, target->get_first()); - target->get_first()->set_parent(q, temp); - } else { - target->set_last(temp); - } - target->set_first(temp); + target->set_last(g); + } + g->set_time(max_time + g->get_delay()); + _dirty = true; + return g; +} + +QCirGate *QCir::prepend(QCirGate gate) { + _qgates.emplace_back(new QCirGate(std::move(gate))); + auto *g = _qgates.back(); + + for (auto const &qb : g->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; })) { + QCirQubit *target = get_qubit(qb); + if (target->get_first() != nullptr) { + g->set_child(qb, target->get_first()); + target->get_first()->set_parent(qb, g); + } else { + target->set_last(g); } - _dirty = true; + target->set_first(g); } - _qgates.emplace_back(temp); - _gate_id++; - return temp; + _dirty = true; + return g; } /** @@ -565,7 +551,6 @@ void QCir::print_gate_statistics(bool detail) const { } void QCir::translate(QCir const &qcir, std::string const &gate_set) { - qcir.update_topological_order(); add_qubits(qcir.get_num_qubits()); Equivalence equivalence = EQUIVALENCE_LIBRARY[gate_set]; for (auto const *cur_gate : qcir.get_topologically_ordered_gates()) { diff --git a/src/qcir/qcir.hpp b/src/qcir/qcir.hpp index 8d81272e..9facf4d5 100644 --- a/src/qcir/qcir.hpp +++ b/src/qcir/qcir.hpp @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -105,7 +106,6 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia std::swap(_gate_id, other._gate_id); std::swap(_qubit_id, other._qubit_id); std::swap(_dirty, other._dirty); - std::swap(_global_dfs_counter, other._global_dfs_counter); std::swap(_filename, other._filename); std::swap(_gate_set, other._gate_set); std::swap(_procedures, other._procedures); @@ -123,10 +123,7 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia size_t calculate_depth() const; std::vector const& get_qubits() const { return _qubits; } std::vector const& get_topologically_ordered_gates() const { - if (_dirty) { - update_topological_order(); - _dirty = false; - } + _update_topological_order(); return _topological_order; } std::vector const& get_gates() const { return _qgates; } @@ -152,7 +149,8 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia void add_qubits(size_t num); bool remove_qubit(QubitIdType qid); QCirGate* add_gate(std::string type, QubitIdList bits, dvlab::Phase phase, bool append); - QCirGate* add_single_rz(QubitIdType bit, dvlab::Phase phase, bool append); + QCirGate* append(QCirGate gate); + QCirGate* prepend(QCirGate gate); bool remove_gate(size_t id); bool read_qcir_file(std::filesystem::path const& filepath); @@ -172,28 +170,18 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia QCirGateStatistics get_gate_statistics() const; void update_gate_time() const; - void print_zx_form_topological_order(); void adjoint(); // DFS functions template void topological_traverse(F lambda) const { - if (_dirty) { - update_topological_order(); - _dirty = false; - } - for_each(_topological_order.begin(), _topological_order.end(), lambda); + std::ranges::for_each(get_topologically_ordered_gates(), lambda); } bool print_topological_order() const; - // pass a function F (public functions) into for_each - // lambdaFn such as mappingToZX / updateGateTime - std::vector const& update_topological_order() const; - // Member functions about circuit reporting - void print_depth() const; void print_gates(bool print_neighbors = false, std::span gate_ids = {}) const; void print_qcir() const; bool print_gate_as_diagram(size_t id, bool show_time) const; @@ -202,15 +190,11 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia private: void _dfs(QCirGate* curr_gate) const; - - // For Copy - void _set_next_gate_id(size_t id) { _gate_id = id; } - void _set_next_qubit_id(QubitIdType qid) { _qubit_id = qid; } + std::vector const& _update_topological_order() const; size_t _gate_id = 0; QubitIdType _qubit_id = 0; bool mutable _dirty = true; - unsigned mutable _global_dfs_counter = 0; std::string _filename = ""; std::string _gate_set = ""; std::vector _procedures = {}; diff --git a/src/qcir/qcir_action.cpp b/src/qcir/qcir_action.cpp index 9d7d3f14..d6be5666 100644 --- a/src/qcir/qcir_action.cpp +++ b/src/qcir/qcir_action.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "qcir/qcir.hpp" #include "qcir/qcir_gate.hpp" @@ -28,7 +29,6 @@ QCir* QCir::compose(QCir const& other) { if (get_qubit(qubit->get_id()) == nullptr) insert_qubit(qubit->get_id()); } - other.update_topological_order(); for (auto& targ_gate : other.get_topologically_ordered_gates()) { QubitIdList qubits; for (auto const& b : targ_gate->get_qubits()) { @@ -51,7 +51,6 @@ QCir* QCir::tensor_product(QCir const& other) { for (auto& qubit : targ_qubits) { old_q2_new_q[qubit->get_id()] = push_qubit(); } - other.update_topological_order(); for (auto& targ_gate : other.get_topologically_ordered_gates()) { QubitIdList qubits; for (auto const& b : targ_gate->get_qubits()) { @@ -68,28 +67,30 @@ QCir* QCir::tensor_product(QCir const& other) { * @param currentGate the gate to start DFS */ void QCir::_dfs(QCirGate* curr_gate) const { - std::stack> dfs; + std::stack> dfs_stack; - if (!curr_gate->is_visited(_global_dfs_counter)) { - dfs.emplace(false, curr_gate); + std::unordered_set visited; + + if (!visited.contains(curr_gate)) { + dfs_stack.emplace(false, curr_gate); } - while (!dfs.empty()) { - auto node = dfs.top(); - dfs.pop(); + while (!dfs_stack.empty()) { + auto node = dfs_stack.top(); + dfs_stack.pop(); if (node.first) { _topological_order.emplace_back(node.second); continue; } - if (node.second->is_visited(_global_dfs_counter)) { + if (visited.contains(node.second)) { continue; } - node.second->set_visited(_global_dfs_counter); - dfs.emplace(true, node.second); + visited.insert(node.second); + dfs_stack.emplace(true, node.second); for (auto const& info : node.second->get_qubits()) { if (info._next != nullptr) { - if (!(info._next->is_visited(_global_dfs_counter))) { - dfs.emplace(false, info._next); + if (!visited.contains(info._next)) { + dfs_stack.emplace(false, info._next); } } } @@ -101,21 +102,30 @@ void QCir::_dfs(QCirGate* curr_gate) const { * * @return const vector& */ -std::vector const& QCir::update_topological_order() const { +std::vector const& QCir::_update_topological_order() const { + if (!_dirty) + return _topological_order; + _topological_order.clear(); if (_qgates.empty()) return _topological_order; - _global_dfs_counter++; - auto dummy = new QCirGate(0, GateRotationCategory::id, dvlab::Phase(0)); + auto dummy = new QCirGate(0, GateRotationCategory::id, dvlab::Phase(0)); + auto children = dummy->get_qubits(); for (size_t i = 0; i < _qubits.size(); i++) { - dummy->add_dummy_child(_qubits[i]->get_first()); + children.push_back( + {._qubit = 0, + ._prev = nullptr, + ._next = _qubits[i]->get_first(), + ._isTarget = false}); } + dummy->set_qubits(children); _dfs(dummy); _topological_order.pop_back(); // pop dummy reverse(_topological_order.begin(), _topological_order.end()); assert(_topological_order.size() == _qgates.size()); delete dummy; + _dirty = false; return _topological_order; } @@ -126,7 +136,7 @@ bool QCir::print_topological_order() const { auto print_gate_id = [](QCirGate* gate) { fmt::println("{}", gate->get_id()); }; - topological_traverse(print_gate_id); + std::ranges::for_each(get_topologically_ordered_gates(), print_gate_id); return true; } @@ -134,8 +144,7 @@ bool QCir::print_topological_order() const { * @brief Update execution time of gates */ void QCir::update_gate_time() const { - update_topological_order(); - auto lambda = [](QCirGate* curr_gate) { + auto const lambda = [](QCirGate* curr_gate) { std::vector info = curr_gate->get_qubits(); size_t max_time = 0; for (size_t i = 0; i < info.size(); i++) { @@ -146,7 +155,8 @@ void QCir::update_gate_time() const { } curr_gate->set_time(max_time + curr_gate->get_delay()); }; - topological_traverse(lambda); + + std::ranges::for_each(get_topologically_ordered_gates(), lambda); } /** * @brief Reset QCir @@ -157,10 +167,9 @@ void QCir::reset() { _qubits.clear(); _topological_order.clear(); - _gate_id = 0; - _qubit_id = 0; - _dirty = true; - _global_dfs_counter = 1; + _gate_id = 0; + _qubit_id = 0; + _dirty = true; } void QCir::adjoint() { diff --git a/src/qcir/qcir_cmd.cpp b/src/qcir/qcir_cmd.cpp index 09958f9b..45eca7ed 100644 --- a/src/qcir/qcir_cmd.cpp +++ b/src/qcir/qcir_cmd.cpp @@ -328,7 +328,7 @@ dvlab::Command qcir_print_cmd(QCirMgr const& qcir_mgr) { else if (parser.parsed("--statistics")) { qcir_mgr.get()->print_qcir(); qcir_mgr.get()->print_gate_statistics(parser.parsed("--verbose")); - qcir_mgr.get()->print_depth(); + fmt::println("Depth : {}", qcir_mgr.get()->calculate_depth()); } else { qcir_mgr.get()->print_qcir_info(); } diff --git a/src/qcir/qcir_gate.cpp b/src/qcir/qcir_gate.cpp index ac1681d0..40b83a7d 100644 --- a/src/qcir/qcir_gate.cpp +++ b/src/qcir/qcir_gate.cpp @@ -39,7 +39,9 @@ void QCirGate::print_gate_info(bool show_time) const { auto pos = type_str.find("dg"); std::for_each(type_str.begin(), pos == std::string::npos ? type_str.end() : dvlab::iterator::next(type_str.begin(), pos), [](char& c) { c = dvlab::str::toupper(c); }); auto show_phase = type_str[0] == 'P' || type_str[0] == 'R'; - _print_single_qubit_or_controlled_gate(type_str, show_phase, show_time); + _print_single_qubit_or_controlled_gate(type_str, show_phase); + if (show_time) + fmt::println("Execute at t= {}", get_time()); } void QCirGate::set_rotation_category(GateRotationCategory type) { @@ -126,15 +128,6 @@ void QCirGate::set_parent(QubitIdType qubit, QCirGate* p) { } } -/** - * @brief Add dummy child c to gate - * - * @param c - */ -void QCirGate::add_dummy_child(QCirGate* c) { - _qubits.push_back({._qubit = 0, ._prev = nullptr, ._next = c, ._isTarget = false}); -} - /** * @brief Set child to gate on qubit. * @@ -175,7 +168,7 @@ void QCirGate::print_gate() const { * @param showRotate * @param showTime */ -void QCirGate::_print_single_qubit_or_controlled_gate(std::string gtype, bool show_rotation, bool show_time) const { +void QCirGate::_print_single_qubit_or_controlled_gate(std::string gtype, bool show_rotation) const { if (_qubits.size() > 1 && gtype.size() % 2 == 0) { gtype = " " + gtype; } @@ -212,8 +205,6 @@ void QCirGate::_print_single_qubit_or_controlled_gate(std::string gtype, bool sh } if (show_rotation) fmt::println("Rotate Phase: {0}", _phase); - if (show_time) - fmt::println("Execute at t= {}", get_time()); } void QCirGate::adjoint() { diff --git a/src/qcir/qcir_gate.hpp b/src/qcir/qcir_gate.hpp index de8b8d88..332f4b7c 100644 --- a/src/qcir/qcir_gate.hpp +++ b/src/qcir/qcir_gate.hpp @@ -80,10 +80,6 @@ class QCirGate { void add_qubit(QubitIdType qubit, bool is_target); void set_target_qubit(QubitIdType qubit); void set_control_qubit(QubitIdType qubit) { _qubits[0]._qubit = qubit; } - // DFS - bool is_visited(unsigned global) const { return global == _dfs_counter; } - void set_visited(unsigned global) { _dfs_counter = global; } - void add_dummy_child(QCirGate* c); // Printing functions void print_gate() const; @@ -107,12 +103,10 @@ class QCirGate { size_t _id; GateRotationCategory _rotation_category; size_t _time = 0; - unsigned _dfs_counter = 0; std::vector _qubits = {}; dvlab::Phase _phase; - // void _print_single_qubit_gate(std::string const& gtype, bool show_rotation = false, bool show_time = false) const; - void _print_single_qubit_or_controlled_gate(std::string gtype, bool show_rotation = false, bool show_time = false) const; + void _print_single_qubit_or_controlled_gate(std::string gtype, bool show_rotation = false) const; }; } // namespace qsyn::qcir diff --git a/src/qcir/qcir_print.cpp b/src/qcir/qcir_print.cpp index 01ca5e91..4694ef78 100644 --- a/src/qcir/qcir_print.cpp +++ b/src/qcir/qcir_print.cpp @@ -21,8 +21,7 @@ namespace qsyn::qcir { * @brief Print QCir Gates */ void QCir::print_gates(bool print_neighbors, std::span gate_ids) const { - if (_dirty) - update_gate_time(); + update_gate_time(); fmt::println("Listed by gate ID"); auto const print_predecessors = [](QCirGate const* const gate) { @@ -66,14 +65,6 @@ void QCir::print_gates(bool print_neighbors, std::span gate_ids) const { } } -/** - * @brief Print Depth of QCir - * - */ -void QCir::print_depth() const { - fmt::println("Depth : {}", calculate_depth()); -} - /** * @brief Print QCir */ @@ -85,8 +76,7 @@ void QCir::print_qcir() const { * @brief Print Qubits */ void QCir::print_circuit_diagram(spdlog::level::level_enum lvl) const { - if (_dirty) - update_gate_time(); + update_gate_time(); for (size_t i = 0; i < _qubits.size(); i++) _qubits[i]->print_qubit_line(lvl); @@ -104,7 +94,7 @@ bool QCir::print_gate_as_diagram(size_t id, bool show_time) const { return false; } - if (show_time && _dirty) + if (show_time) update_gate_time(); get_gate(id)->print_gate_info(show_time); return true; diff --git a/src/qcir/qcir_writer.cpp b/src/qcir/qcir_writer.cpp index f0d8b2a0..a67ba03b 100644 --- a/src/qcir/qcir_writer.cpp +++ b/src/qcir/qcir_writer.cpp @@ -94,7 +94,6 @@ bool QCir::draw(QCirDrawerType drawer, std::filesystem::path const& output_path, } std::string to_qasm(QCir const& qcir) { - qcir.update_topological_order(); std::string qasm = "OPENQASM 2.0;\n"; qasm += "include \"qelib1.inc\";\n"; qasm += fmt::format("qreg q[{}];\n", qcir.get_num_qubits()); diff --git a/tests/conversion/zx2qc/ref/extractStep.log b/tests/conversion/zx2qc/ref/extractStep.log index 582bd494..3a786a58 100644 --- a/tests/conversion/zx2qc/ref/extractStep.log +++ b/tests/conversion/zx2qc/ref/extractStep.log @@ -157,9 +157,9 @@ Q 2 - h( 6)--------------------------cz( 5)----------cz( 4)--td( 3)-- h( 2)- qsyn> extract step -zx 1 -qc 1 -l 100 qsyn> qcir print --diagram -Q 0 - h(52)--------------------------------------------------------------------------cz(51)-- h(47)--------------------------------------------------cz(45)--------------------------cz(41)--rz(39)-- h(37)------------------cz(34)------------------------------------------cz(29)--------------------------------------------------------------------------cx(20)--------------------------cz(17)-- h(10)--------------------------------------------------cz( 9)-- t( 8)-- h( 7)----------cz( 4)-- h( 0)- -Q 1 - h(60)-- h(59)----------cz(57)--------------------------cz(54)-- h(53)----------cz(51)----------cz(50)--td(49)-- h(48)----------cz(46)-- h(42)-- t(40)-- h(38)------------------------------------------cz(35)-- h(31)----------------------------------cz(30)----------cz(29)--rz(28)-- h(27)----------cz(26)--rz(24)-- h(22)----------cx(21)----------cx(20)----------cz(18)-- h(14)----------------------------------cz(13)-- t(12)-- h(11)----------cz( 9)----------cz( 5)-- h( 1)- -Q 2 - h(58)------------------cz(57)-- z(56)-- h(55)----------cz(54)----------------------------------cz(50)--------------------------cz(46)----------cz(45)--rz(44)-- h(43)----------cz(41)-- h(36)----------cz(35)----------cz(34)--rz(33)-- h(32)----------cz(30)------------------------------------------cz(26)-- t(25)-- h(23)----------cx(21)-- h(19)------------------cz(18)----------cz(17)--rz(16)-- h(15)----------cz(13)-- h( 6)----------------------------------cz( 5)----------cz( 4)--td( 3)-- h( 2)- +Q 0 - h(52)--------------------------------------------------------------------------cz(51)-- h(47)--------------------------------------------------cz(45)--------------------------cz(41)-- p(39)-- h(37)------------------cz(34)------------------------------------------cz(29)--------------------------------------------------------------------------cx(20)--------------------------cz(17)-- h(10)--------------------------------------------------cz( 9)-- t( 8)-- h( 7)----------cz( 4)-- h( 0)- +Q 1 - h(60)-- h(59)----------cz(57)--------------------------cz(54)-- h(53)----------cz(51)----------cz(50)--td(49)-- h(48)----------cz(46)-- h(42)-- t(40)-- h(38)------------------------------------------cz(35)-- h(31)----------------------------------cz(30)----------cz(29)-- p(28)-- h(27)----------cz(26)-- p(24)-- h(22)----------cx(21)----------cx(20)----------cz(18)-- h(14)----------------------------------cz(13)-- t(12)-- h(11)----------cz( 9)----------cz( 5)-- h( 1)- +Q 2 - h(58)------------------cz(57)-- z(56)-- h(55)----------cz(54)----------------------------------cz(50)--------------------------cz(46)----------cz(45)-- p(44)-- h(43)----------cz(41)-- h(36)----------cz(35)----------cz(34)-- p(33)-- h(32)----------cz(30)------------------------------------------cz(26)-- t(25)-- h(23)----------cx(21)-- h(19)------------------cz(18)----------cz(17)-- p(16)-- h(15)----------cz(13)-- h( 6)----------------------------------cz( 5)----------cz( 4)--td( 3)-- h( 2)- qsyn> qc2zx From bc652fafad62d2af58201e9c85a6e6c1ed05c283 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Wed, 6 Mar 2024 14:40:15 +0800 Subject: [PATCH 2/5] :fire: remove unreliable API QCirGate::get_time and add substitutes --- src/convert/qcir_to_zxgraph.cpp | 5 ++-- src/qcir/qcir.cpp | 27 +++++++++++++++------ src/qcir/qcir.hpp | 4 +--- src/qcir/qcir_action.cpp | 28 ++++++---------------- src/qcir/qcir_gate.cpp | 16 ++++++------- src/qcir/qcir_gate.hpp | 7 ++---- src/qcir/qcir_print.cpp | 42 ++++++++++++++++++++++++--------- src/qcir/qcir_qubit.cpp | 23 ------------------ src/qcir/qcir_qubit.hpp | 2 -- src/qcir/qcir_reader.cpp | 1 - 10 files changed, 71 insertions(+), 84 deletions(-) diff --git a/src/convert/qcir_to_zxgraph.cpp b/src/convert/qcir_to_zxgraph.cpp index 3db74f6a..0d3605d2 100644 --- a/src/convert/qcir_to_zxgraph.cpp +++ b/src/convert/qcir_to_zxgraph.cpp @@ -464,7 +464,8 @@ std::optional to_zxgraph(QCir const& qcir, size_t decomposition_mode) { spdlog::error("QCir is empty!!"); return std::nullopt; } - qcir.update_gate_time(); + auto const times = qcir.calculate_gate_times(); + ZXGraph graph; spdlog::debug("Add boundaries"); for (size_t i = 0; i < qcir.get_qubits().size(); i++) { @@ -487,7 +488,7 @@ std::optional to_zxgraph(QCir const& qcir, size_t decomposition_mode) { } for (auto& v : tmp->get_vertices()) { - v->set_col(v->get_col() + static_cast(gate->get_time())); + v->set_col(v->get_col() + static_cast(times.at(gate->get_id()))); } graph.concatenate(*tmp); diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index 723c461f..9ae9204a 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -95,8 +95,26 @@ QCirQubit *QCir::get_qubit(QubitIdType id) const { size_t QCir::calculate_depth() const { if (is_empty()) return 0; - update_gate_time(); - return std::ranges::max(_qgates | std::views::transform([](QCirGate *qg) { return qg->get_time(); })); + return std::ranges::max(calculate_gate_times() | std::views::values); +} + +std::unordered_map QCir::calculate_gate_times() const { + auto gate_times = std::unordered_map{}; + auto const lambda = [&](QCirGate *curr_gate) { + std::vector info = curr_gate->get_qubits(); + size_t max_time = 0; + for (size_t i = 0; i < info.size(); i++) { + if (info[i]._prev == nullptr) + continue; + if (gate_times.at(info[i]._prev->get_id()) > max_time) + max_time = gate_times.at(info[i]._prev->get_id()); + } + gate_times.emplace(curr_gate->get_id(), max_time + curr_gate->get_delay()); + }; + + std::ranges::for_each(get_topologically_ordered_gates(), lambda); + + return gate_times; } /** @@ -202,20 +220,16 @@ QCirGate *QCir::append(QCirGate gate) { _qgates.emplace_back(new QCirGate(std::move(gate))); auto *g = _qgates.back(); - size_t max_time = 0; for (auto const &qb : g->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; })) { QCirQubit *target = get_qubit(qb); if (target->get_last() != nullptr) { g->set_parent(qb, target->get_last()); target->get_last()->set_child(qb, g); - if ((target->get_last()->get_time()) > max_time) - max_time = target->get_last()->get_time(); } else { target->set_first(g); } target->set_last(g); } - g->set_time(max_time + g->get_delay()); _dirty = true; return g; } @@ -574,7 +588,6 @@ void QCir::translate(QCir const &qcir, std::string const &gate_set) { } } set_gate_set(gate_set); - update_gate_time(); } } // namespace qsyn::qcir diff --git a/src/qcir/qcir.hpp b/src/qcir/qcir.hpp index 9facf4d5..98d31d8a 100644 --- a/src/qcir/qcir.hpp +++ b/src/qcir/qcir.hpp @@ -121,6 +121,7 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia // Access functions size_t get_num_qubits() const { return _qubits.size(); } size_t calculate_depth() const; + std::unordered_map calculate_gate_times() const; std::vector const& get_qubits() const { return _qubits; } std::vector const& get_topologically_ordered_gates() const { _update_topological_order(); @@ -169,8 +170,6 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia QCirGateStatistics get_gate_statistics() const; - void update_gate_time() const; - void adjoint(); // DFS functions @@ -189,7 +188,6 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia void print_qcir_info() const; private: - void _dfs(QCirGate* curr_gate) const; std::vector const& _update_topological_order() const; size_t _gate_id = 0; diff --git a/src/qcir/qcir_action.cpp b/src/qcir/qcir_action.cpp index d6be5666..9e6be4f7 100644 --- a/src/qcir/qcir_action.cpp +++ b/src/qcir/qcir_action.cpp @@ -61,12 +61,14 @@ QCir* QCir::tensor_product(QCir const& other) { return this; } +namespace { + /** * @brief Perform DFS from currentGate * * @param currentGate the gate to start DFS */ -void QCir::_dfs(QCirGate* curr_gate) const { +void dfs(QCirGate* curr_gate, std::vector& topo_order) { std::stack> dfs_stack; std::unordered_set visited; @@ -78,7 +80,7 @@ void QCir::_dfs(QCirGate* curr_gate) const { auto node = dfs_stack.top(); dfs_stack.pop(); if (node.first) { - _topological_order.emplace_back(node.second); + topo_order.emplace_back(node.second); continue; } if (visited.contains(node.second)) { @@ -97,6 +99,8 @@ void QCir::_dfs(QCirGate* curr_gate) const { } } +} // namespace + /** * @brief Update topological order * @@ -119,7 +123,7 @@ std::vector const& QCir::_update_topological_order() const { ._isTarget = false}); } dummy->set_qubits(children); - _dfs(dummy); + dfs(dummy, _topological_order); _topological_order.pop_back(); // pop dummy reverse(_topological_order.begin(), _topological_order.end()); assert(_topological_order.size() == _qgates.size()); @@ -140,24 +144,6 @@ bool QCir::print_topological_order() const { return true; } -/** - * @brief Update execution time of gates - */ -void QCir::update_gate_time() const { - auto const lambda = [](QCirGate* curr_gate) { - std::vector info = curr_gate->get_qubits(); - size_t max_time = 0; - for (size_t i = 0; i < info.size(); i++) { - if (info[i]._prev == nullptr) - continue; - if (info[i]._prev->get_time() > max_time) - max_time = info[i]._prev->get_time(); - } - curr_gate->set_time(max_time + curr_gate->get_delay()); - }; - - std::ranges::for_each(get_topologically_ordered_gates(), lambda); -} /** * @brief Reset QCir * diff --git a/src/qcir/qcir_gate.cpp b/src/qcir/qcir_gate.cpp index 40b83a7d..088b55b5 100644 --- a/src/qcir/qcir_gate.cpp +++ b/src/qcir/qcir_gate.cpp @@ -32,7 +32,7 @@ std::string QCirGate::get_type_str() const { return gate_type_to_str(_rotation_category, get_num_qubits(), _phase); } -void QCirGate::print_gate_info(bool show_time) const { +void QCirGate::print_gate_info() const { auto type_str = get_type_str(); if (type_str.starts_with("mc") || type_str.starts_with("cc")) type_str = type_str.substr(2); if (type_str.starts_with("c")) type_str = type_str.substr(1); @@ -40,8 +40,6 @@ void QCirGate::print_gate_info(bool show_time) const { std::for_each(type_str.begin(), pos == std::string::npos ? type_str.end() : dvlab::iterator::next(type_str.begin(), pos), [](char& c) { c = dvlab::str::toupper(c); }); auto show_phase = type_str[0] == 'P' || type_str[0] == 'R'; _print_single_qubit_or_controlled_gate(type_str, show_phase); - if (show_time) - fmt::println("Execute at t= {}", get_time()); } void QCirGate::set_rotation_category(GateRotationCategory type) { @@ -146,12 +144,12 @@ void QCirGate::set_child(QubitIdType qubit, QCirGate* c) { /** * @brief Print Gate brief information */ -void QCirGate::print_gate() const { - fmt::print("ID:{:>4} ({:>3}) Time: {:>4} Qubit: {:>3} ", - _id, - get_type_str(), - _time, - fmt::join(_qubits | std::views::transform([](QubitInfo const& info) { return info._qubit; }), " ")); +void QCirGate::print_gate(std::optional time) const { + fmt::print("ID:{:>4} ({:>3}) ", _id, get_type_str()); + if (time.has_value()) + fmt::print("Time: {:>4} ", time.value()); + + fmt::print("Qubit: {:>3} ", fmt::join(_qubits | std::views::transform([](QubitInfo const& info) { return info._qubit; }), " ")); auto is_special_phase = get_phase().denominator() == 1 || get_phase().denominator() == 2 || get_phase() == Phase(1, 4) || get_phase() == Phase(-1, 4); auto is_p_type_rotation = get_rotation_category() == GateRotationCategory::py || get_rotation_category() == GateRotationCategory::px || get_rotation_category() == GateRotationCategory::pz; diff --git a/src/qcir/qcir_gate.hpp b/src/qcir/qcir_gate.hpp index 332f4b7c..6f052006 100644 --- a/src/qcir/qcir_gate.hpp +++ b/src/qcir/qcir_gate.hpp @@ -62,7 +62,6 @@ class QCirGate { GateType get_type() const { return std::make_tuple(_rotation_category, _qubits.size(), _phase); } GateRotationCategory get_rotation_category() const { return _rotation_category; } size_t get_id() const { return _id; } - size_t get_time() const { return _time; } size_t get_delay() const; dvlab::Phase get_phase() const { return _phase; } std::vector const& get_qubits() const { return _qubits; } @@ -73,7 +72,6 @@ class QCirGate { QubitInfo get_control() const { return _qubits[0]; } void set_id(size_t id) { _id = id; } - void set_time(size_t time) { _time = time; } void set_child(QubitIdType qubit, QCirGate* c); void set_parent(QubitIdType qubit, QCirGate* p); @@ -82,10 +80,10 @@ class QCirGate { void set_control_qubit(QubitIdType qubit) { _qubits[0]._qubit = qubit; } // Printing functions - void print_gate() const; + void print_gate(std::optional time) const; void set_rotation_category(GateRotationCategory type); void set_phase(dvlab::Phase p); - void print_gate_info(bool show_time) const; + void print_gate_info() const; bool is_h() const { return _rotation_category == GateRotationCategory::h; } bool is_x() const { return _rotation_category == GateRotationCategory::px && _phase == dvlab::Phase(1) && _qubits.size() == 1; } @@ -102,7 +100,6 @@ class QCirGate { protected: size_t _id; GateRotationCategory _rotation_category; - size_t _time = 0; std::vector _qubits = {}; dvlab::Phase _phase; diff --git a/src/qcir/qcir_print.cpp b/src/qcir/qcir_print.cpp index 4694ef78..73281e38 100644 --- a/src/qcir/qcir_print.cpp +++ b/src/qcir/qcir_print.cpp @@ -21,7 +21,6 @@ namespace qsyn::qcir { * @brief Print QCir Gates */ void QCir::print_gates(bool print_neighbors, std::span gate_ids) const { - update_gate_time(); fmt::println("Listed by gate ID"); auto const print_predecessors = [](QCirGate const* const gate) { @@ -42,9 +41,11 @@ void QCir::print_gates(bool print_neighbors, std::span gate_ids) const { fmt::println("- Successors : {}", fmt::join(gate->get_qubits() | std::views::transform(get_successor_gate_id), ", ")); }; + auto const times = calculate_gate_times(); + if (gate_ids.empty()) { for (auto const* gate : _qgates) { - gate->print_gate(); + gate->print_gate(times.at(gate->get_id())); if (print_neighbors) { print_predecessors(gate); print_successors(gate); @@ -52,11 +53,12 @@ void QCir::print_gates(bool print_neighbors, std::span gate_ids) const { } } else { for (auto id : gate_ids) { - if (get_gate(id) == nullptr) { + auto const gate = get_gate(id); + if (gate == nullptr) { spdlog::error("Gate ID {} not found!!", id); continue; } - get_gate(id)->print_gate(); + gate->print_gate(times.at(gate->get_id())); if (print_neighbors) { print_predecessors(get_gate(id)); print_successors(get_gate(id)); @@ -76,10 +78,26 @@ void QCir::print_qcir() const { * @brief Print Qubits */ void QCir::print_circuit_diagram(spdlog::level::level_enum lvl) const { - update_gate_time(); + auto const times = calculate_gate_times(); + + for (auto const* qubit : _qubits) { + QCirGate* current = qubit->get_first(); + size_t last_time = 1; + std::string line = fmt::format("Q{:>2} ", qubit->get_id()); + while (current != nullptr) { + DVLAB_ASSERT(last_time <= times.at(current->get_id()), "Gate time should not be smaller than last time!!"); + line += fmt::format( + "{}-{:>2}({:>2})-", + std::string(8 * (times.at(current->get_id()) - last_time), '-'), + current->get_type_str().substr(0, 2), + current->get_id()); + + last_time = times.at(current->get_id()) + 1; + current = current->get_qubit(qubit->get_id())._next; + } - for (size_t i = 0; i < _qubits.size(); i++) - _qubits[i]->print_qubit_line(lvl); + spdlog::log(lvl, "{}", line); + } } /** @@ -89,14 +107,16 @@ void QCir::print_circuit_diagram(spdlog::level::level_enum lvl) const { * @param showTime if true, show the time */ bool QCir::print_gate_as_diagram(size_t id, bool show_time) const { - if (get_gate(id) == nullptr) { + auto const gate = get_gate(id); + if (gate == nullptr) { spdlog::error("Gate ID {} not found!!", id); return false; } - if (show_time) - update_gate_time(); - get_gate(id)->print_gate_info(show_time); + gate->print_gate_info(); + if (show_time) { + fmt::println("Execute at t= {}", calculate_gate_times().at(id)); + } return true; } diff --git a/src/qcir/qcir_qubit.cpp b/src/qcir/qcir_qubit.cpp index 6c559f90..89d1569b 100644 --- a/src/qcir/qcir_qubit.cpp +++ b/src/qcir/qcir_qubit.cpp @@ -15,27 +15,4 @@ namespace qsyn::qcir { -/** - * @brief Print qubit info - * - */ -void QCirQubit::print_qubit_line(spdlog::level::level_enum lvl) const { - QCirGate* current = _bit_first; - size_t last_time = 1; - std::string line = fmt::format("Q{:>2} ", _id); - while (current != nullptr) { - DVLAB_ASSERT(last_time <= current->get_time(), "Gate time should not be smaller than last time!!"); - line += fmt::format( - "{}-{:>2}({:>2})-", - std::string(8 * (current->get_time() - last_time), '-'), - current->get_type_str().substr(0, 2), - current->get_id()); - - last_time = current->get_time() + 1; - current = current->get_qubit(_id)._next; - } - - spdlog::log(lvl, "{}", line); -} - } // namespace qsyn::qcir diff --git a/src/qcir/qcir_qubit.hpp b/src/qcir/qcir_qubit.hpp index abf31506..d99c6ffb 100644 --- a/src/qcir/qcir_qubit.hpp +++ b/src/qcir/qcir_qubit.hpp @@ -33,8 +33,6 @@ class QCirQubit { QubitIdType get_id() const { return _id; } QCirGate* get_last() const { return _bit_last; } QCirGate* get_first() const { return _bit_first; } - // Printing functions - void print_qubit_line(spdlog::level::level_enum lvl = spdlog::level::off) const; private: QubitIdType _id; diff --git a/src/qcir/qcir_reader.cpp b/src/qcir/qcir_reader.cpp index c40b9301..f5baa82b 100644 --- a/src/qcir/qcir_reader.cpp +++ b/src/qcir/qcir_reader.cpp @@ -123,7 +123,6 @@ bool QCir::read_qasm(std::filesystem::path const& filepath) { } add_gate(type, qubit_ids, phase.value(), true); } - update_gate_time(); return true; } From bb9cd0185063763944d6c7a9e3f00ce3c6d3b1a0 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Wed, 6 Mar 2024 21:21:37 +0800 Subject: [PATCH 3/5] :fire: eliminate new QCirGate from the code base --- src/qcir/optimizer/basic_optimization.cpp | 80 ++++------------------- src/qcir/optimizer/gate_optimization.cpp | 6 +- src/qcir/optimizer/optimizer.cpp | 1 - src/qcir/optimizer/optimizer.hpp | 49 +++++++++++++- src/qcir/qcir.cpp | 37 +++++++---- src/qcir/qcir.hpp | 27 ++++---- src/qcir/qcir_action.cpp | 9 ++- 7 files changed, 105 insertions(+), 104 deletions(-) diff --git a/src/qcir/optimizer/basic_optimization.cpp b/src/qcir/optimizer/basic_optimization.cpp index db185024..fd5905ca 100644 --- a/src/qcir/optimizer/basic_optimization.cpp +++ b/src/qcir/optimizer/basic_optimization.cpp @@ -106,29 +106,16 @@ QCir Optimizer::_parse_once(QCir const& qcir, bool reversed, bool do_minimize_cz result.add_procedures(qcir.get_procedures()); for (auto& t : _xs) { - auto x_gate = new QCirGate(_gate_count, GateRotationCategory::px, dvlab::Phase(1)); - x_gate->add_qubit(t, true); - _gate_count++; - Optimizer::_add_gate_to_circuit(result, x_gate, reversed); + Optimizer::_add_gate_to_circuit(result, _store_x(t), reversed); } _swaps = Optimizer::_get_swap_path(); for (auto& [c, t] : _swaps) { // TODO - Use SWAP gate to replace cnots - auto cnot_1 = new QCirGate(_gate_count, GateRotationCategory::px, dvlab::Phase(1)); - cnot_1->add_qubit(c, false); - cnot_1->add_qubit(t, true); - auto cnot_2 = new QCirGate(_gate_count + 1, GateRotationCategory::px, dvlab::Phase(1)); - cnot_2->add_qubit(t, false); - cnot_2->add_qubit(c, true); - auto cnot_3 = new QCirGate(_gate_count + 2, GateRotationCategory::px, dvlab::Phase(1)); - cnot_3->add_qubit(c, false); - cnot_3->add_qubit(t, true); - _gate_count += 3; - Optimizer::_add_gate_to_circuit(result, cnot_1, reversed); - Optimizer::_add_gate_to_circuit(result, cnot_2, reversed); - Optimizer::_add_gate_to_circuit(result, cnot_3, reversed); + Optimizer::_add_gate_to_circuit(result, _store_cx(c, t), reversed); + Optimizer::_add_gate_to_circuit(result, _store_cx(t, c), reversed); + Optimizer::_add_gate_to_circuit(result, _store_cx(c, t), reversed); } std::string statistics_str; fmt::format_to(std::back_inserter(statistics_str), " ParseForward No.{} iteration done.\n", _iter); @@ -265,9 +252,7 @@ QCir Optimizer::_build_from_storage(size_t n_qubits, bool reversed) { * @param target Index of the target qubit */ void Optimizer::_add_hadamard(QubitIdType target, bool erase) { - auto h_gate = new QCirGate(_gate_count, GateRotationCategory::h, dvlab::Phase(1)); - h_gate->add_qubit(target, true); - _gate_count++; + auto h_gate = _store_h(target); _gates[target].emplace_back(h_gate); if (erase) _hadamards.erase(target); _available[target].clear(); @@ -295,10 +280,7 @@ void Optimizer::_add_cx(QubitIdType t1, QubitIdType t2, bool do_swap) { if (count_if(_available[t2].begin(), _available[t2].end(), [&](QCirGate* g) { return Optimizer::two_qubit_gate_exists(g, GateRotationCategory::px, t2, t1); })) { _statistics.DO_SWAP++; spdlog::trace("Apply a do_swap commutation"); - auto cnot = new QCirGate(_gate_count, GateRotationCategory::px, dvlab::Phase(1)); - cnot->add_qubit(t1, false); - cnot->add_qubit(t2, true); - _gate_count++; + auto cnot = _store_cx(t1, t2); _gates[t1].erase(--(find_if(_gates[t1].rbegin(), _gates[t1].rend(), [&](QCirGate* g) { return Optimizer::two_qubit_gate_exists(g, GateRotationCategory::px, t2, t1); })).base()); _gates[t2].erase(--(find_if(_gates[t2].rbegin(), _gates[t2].rend(), [&](QCirGate* g) { return Optimizer::two_qubit_gate_exists(g, GateRotationCategory::px, t2, t1); })).base()); _availty[t1] = false; @@ -346,10 +328,7 @@ void Optimizer::_add_cx(QubitIdType t1, QubitIdType t2, bool do_swap) { } } if (!found_match) { - auto cnot = new QCirGate(_gate_count, GateRotationCategory::px, dvlab::Phase(1)); - cnot->add_qubit(t1, false); - cnot->add_qubit(t2, true); - _gate_count++; + auto cnot = _store_cx(t1, t2); _gates[t1].emplace_back(cnot); _gates[t2].emplace_back(cnot); _available[t1].emplace_back(cnot); @@ -368,10 +347,7 @@ bool Optimizer::_replace_cx_and_cz_with_s_and_cx(QubitIdType t1, QubitIdType t2) targ = !i ? t2 : t1; for (auto& g : _available[ctrl]) { if (g->is_cx() && g->get_control()._qubit == ctrl && g->get_targets()._qubit == targ) { - cnot = new QCirGate(_gate_count, GateRotationCategory::px, dvlab::Phase(1)); - _gate_count++; - cnot->add_qubit(g->get_control()._qubit, false); - cnot->add_qubit(g->get_targets()._qubit, true); + cnot = _store_cx(g->get_control()._qubit, g->get_targets()._qubit); if (_availty[targ]) { if (count_if(_available[targ].begin(), _available[targ].end(), [&](QCirGate* gate_other) { return Optimizer::two_qubit_gate_exists(gate_other, GateRotationCategory::px, ctrl, targ); })) { found_match = true; @@ -409,15 +385,9 @@ bool Optimizer::_replace_cx_and_cz_with_s_and_cx(QubitIdType t1, QubitIdType t2) _gates[ctrl].erase(--(find_if(_gates[ctrl].rbegin(), _gates[ctrl].rend(), [&](QCirGate* g) { return Optimizer::two_qubit_gate_exists(g, GateRotationCategory::px, ctrl, targ); })).base()); _gates[targ].erase(--(find_if(_gates[targ].rbegin(), _gates[targ].rend(), [&](QCirGate* g) { return Optimizer::two_qubit_gate_exists(g, GateRotationCategory::px, ctrl, targ); })).base()); - auto s1 = new QCirGate(_gate_count, GateRotationCategory::pz, dvlab::Phase(-1, 2)); - s1->add_qubit(targ, true); - _gate_count++; - auto s2 = new QCirGate(_gate_count, GateRotationCategory::pz, dvlab::Phase(1, 2)); - s2->add_qubit(targ, true); - _gate_count++; - auto s3 = new QCirGate(_gate_count, GateRotationCategory::pz, dvlab::Phase(1, 2)); - s3->add_qubit(ctrl, true); - _gate_count++; + auto s1 = _store_sdg(targ); + auto s2 = _store_s(targ); + auto s3 = _store_s(ctrl); if (!_available[targ].empty()) { _gates[targ].insert(dvlab::iterator::prev(_gates[targ].end(), _available[targ].size()), s1); @@ -481,16 +451,7 @@ void Optimizer::_add_cz(QubitIdType t1, QubitIdType t2, bool do_minimize_czs) { } // NOTE - No cancel found if (!found_match) { - auto cz = new QCirGate(_gate_count, GateRotationCategory::pz, dvlab::Phase(1)); - if (t1 < t2) { - cz->add_qubit(t1, false); - cz->add_qubit(t2, true); - } else { - cz->add_qubit(t2, false); - cz->add_qubit(t1, true); - } - - _gate_count++; + auto cz = (t1 < t2) ? _store_cz(t1, t2) : _store_cz(t2, t1); _gates[t1].emplace_back(cz); _gates[t2].emplace_back(cz); _available[t1].emplace_back(cz); @@ -514,23 +475,10 @@ bool Optimizer::two_qubit_gate_exists(QCirGate* g, GateRotationCategory gt, Qubi * @param type 0: Z-axis, 1: X-axis, 2: Y-axis */ void Optimizer::_add_rotation_gate(QubitIdType target, dvlab::Phase ph, GateRotationCategory const& rotation_category) { - auto rotate = std::invoke([&]() { - switch (rotation_category) { - case GateRotationCategory::pz: - return new QCirGate(_gate_count, rotation_category, ph); - case GateRotationCategory::px: - return new QCirGate(_gate_count, rotation_category, ph); - case GateRotationCategory::py: - return new QCirGate(_gate_count, rotation_category, ph); - default: - DVLAB_UNREACHABLE("wrong type!! Type shoud be PZ, PX or PY"); - } - }); - - rotate->add_qubit(target, true); + assert(rotation_category == GateRotationCategory::pz || rotation_category == GateRotationCategory::px || rotation_category == GateRotationCategory::py); + auto rotate = _store_rotation_gate(target, ph, rotation_category); _gates[target].emplace_back(rotate); _available[target].emplace_back(rotate); - _gate_count++; } /** diff --git a/src/qcir/optimizer/gate_optimization.cpp b/src/qcir/optimizer/gate_optimization.cpp index 455d54d1..d7a16605 100644 --- a/src/qcir/optimizer/gate_optimization.cpp +++ b/src/qcir/optimizer/gate_optimization.cpp @@ -50,9 +50,9 @@ void Optimizer::_match_hadamards(QCirGate* gate) { if (g2->get_phase().denominator() == 2) { _statistics.HS_EXCHANGE++; spdlog::trace("Transform H-S-H into Sdg-H-Sdg"); - auto zp = new QCirGate(_gate_count, GateRotationCategory::pz, -1 * g2->get_phase()); - zp->add_qubit(qubit, true); - _gate_count++; + auto zp = (g2->get_phase() == Phase(1, 2)) + ? _store_sdg(qubit) + : _store_s(qubit); g2->set_phase(zp->get_phase()); // NOTE - S to Sdg _gates[qubit].insert(_gates[qubit].end() - 2, zp); return; diff --git a/src/qcir/optimizer/optimizer.cpp b/src/qcir/optimizer/optimizer.cpp index 0e0a3f2f..eca0094d 100644 --- a/src/qcir/optimizer/optimizer.cpp +++ b/src/qcir/optimizer/optimizer.cpp @@ -29,7 +29,6 @@ void Optimizer::reset(QCir const& qcir) { _xs.clear(); _zs.clear(); _swaps.clear(); - _gate_count = 0; _statistics = {}; for (int i = 0; i < gsl::narrow(qcir.get_qubits().size()); i++) { _availty.emplace_back(false); diff --git a/src/qcir/optimizer/optimizer.hpp b/src/qcir/optimizer/optimizer.hpp index a9ab7025..b624150b 100644 --- a/src/qcir/optimizer/optimizer.hpp +++ b/src/qcir/optimizer/optimizer.hpp @@ -11,6 +11,7 @@ #include #include +#include "qcir/qcir_gate.hpp" #include "qsyn/qsyn_type.hpp" #include "util/ordered_hashset.hpp" @@ -23,7 +24,6 @@ class Phase; namespace qsyn::qcir { class QCir; -class QCirGate; enum class GateRotationCategory; using Qubit2Gates = std::unordered_map>; @@ -57,6 +57,7 @@ class Optimizer { private: size_t _iter = 0; + std::vector> _storage; Qubit2Gates _gates; Qubit2Gates _available; std::vector _availty; @@ -67,8 +68,6 @@ class Optimizer { dvlab::utils::ordered_hashset _zs; std::vector> _swaps; - size_t _gate_count = 0; - struct Statistics { size_t FUSE_PHASE = 0; size_t X_CANCEL = 0; @@ -121,6 +120,50 @@ class Optimizer { void _fuse_z_phase(QCir& qcir, QCirGate* prev_gate, QCirGate* gate); void _fuse_x_phase(QCir& qcir, QCirGate* prev_gate, QCirGate* gate); void _partial_zx_optimization(QCir& qcir); + + inline QCirGate* _store_x(QubitIdType qubit) { + _storage.emplace_back(std::make_unique(_storage.size(), GateRotationCategory::px, dvlab::Phase(1))); + _storage.back()->add_qubit(qubit, true); + return _storage.back().get(); + } + + inline QCirGate* _store_h(QubitIdType qubit) { + _storage.emplace_back(std::make_unique(_storage.size(), GateRotationCategory::h, dvlab::Phase(1))); + _storage.back()->add_qubit(qubit, true); + return _storage.back().get(); + } + + inline QCirGate* _store_s(QubitIdType qubit) { + _storage.emplace_back(std::make_unique(_storage.size(), GateRotationCategory::pz, dvlab::Phase(1, 2))); + _storage.back()->add_qubit(qubit, true); + return _storage.back().get(); + } + + inline QCirGate* _store_sdg(QubitIdType qubit) { + _storage.emplace_back(std::make_unique(_storage.size(), GateRotationCategory::pz, dvlab::Phase(-1, 2))); + _storage.back()->add_qubit(qubit, true); + return _storage.back().get(); + } + + inline QCirGate* _store_cx(QubitIdType ctrl, QubitIdType targ) { + _storage.emplace_back(std::make_unique(_storage.size(), GateRotationCategory::px, dvlab::Phase(1))); + _storage.back()->add_qubit(ctrl, false); + _storage.back()->add_qubit(targ, true); + return _storage.back().get(); + } + + inline QCirGate* _store_cz(QubitIdType ctrl, QubitIdType targ) { + _storage.emplace_back(std::make_unique(_storage.size(), GateRotationCategory::pz, dvlab::Phase(1))); + _storage.back()->add_qubit(ctrl, false); + _storage.back()->add_qubit(targ, true); + return _storage.back().get(); + } + + inline QCirGate* _store_rotation_gate(QubitIdType target, dvlab::Phase ph, GateRotationCategory const& rotation_category) { + _storage.emplace_back(std::make_unique(_storage.size(), rotation_category, ph)); + _storage.back()->add_qubit(target, true); + return _storage.back().get(); + } }; } // namespace qsyn::qcir diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index 9ae9204a..f52a2587 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -72,9 +72,8 @@ QCir::QCir(QCir const &other) { * @return QCirGate* */ QCirGate *QCir::get_gate(size_t id) const { - for (size_t i = 0; i < _qgates.size(); i++) { - if (_qgates[i]->get_id() == id) - return _qgates[i]; + if (_id_to_gate.contains(id)) { + return _id_to_gate.at(id).get(); } return nullptr; } @@ -205,20 +204,22 @@ QCirGate *QCir::add_gate(std::string type, QubitIdList bits, dvlab::Phase phase, if (gate_phase.has_value()) { phase = gate_phase.value(); } - auto temp = QCirGate(_gate_id++, category, phase); - - for (size_t k = 0; k < bits.size(); k++) { - temp.add_qubit(bits[k], k == bits.size() - 1); // target is the last one - } return append - ? this->append(std::move(temp)) - : this->prepend(std::move(temp)); + ? this->append(std::make_tuple(category, bits.size(), phase), std::move(bits)) + : this->prepend(std::make_tuple(category, bits.size(), phase), std::move(bits)); } -QCirGate *QCir::append(QCirGate gate) { - _qgates.emplace_back(new QCirGate(std::move(gate))); +QCirGate *QCir::append(GateType gate, QubitIdList bits) { + _id_to_gate.emplace(_gate_id, std::make_unique(_gate_id, std::get<0>(gate), *std::get<2>(gate))); + + for (size_t k = 0; k < bits.size(); k++) { + _id_to_gate[_gate_id]->add_qubit(bits[k], k == bits.size() - 1); // target is the last one + } + + _qgates.emplace_back(_id_to_gate[_gate_id].get()); auto *g = _qgates.back(); + _gate_id++; for (auto const &qb : g->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; })) { QCirQubit *target = get_qubit(qb); @@ -234,9 +235,16 @@ QCirGate *QCir::append(QCirGate gate) { return g; } -QCirGate *QCir::prepend(QCirGate gate) { - _qgates.emplace_back(new QCirGate(std::move(gate))); +QCirGate *QCir::prepend(GateType gate, QubitIdList bits) { + _id_to_gate.emplace(_gate_id, std::make_unique(_gate_id, std::get<0>(gate), *std::get<2>(gate))); + + for (size_t k = 0; k < bits.size(); k++) { + _id_to_gate[_gate_id]->add_qubit(bits[k], k == bits.size() - 1); // target is the last one + } + + _qgates.emplace_back(_id_to_gate[_gate_id].get()); auto *g = _qgates.back(); + _gate_id++; for (auto const &qb : g->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; })) { QCirQubit *target = get_qubit(qb); @@ -279,6 +287,7 @@ bool QCir::remove_gate(size_t id) { info[i]._next = nullptr; } std::erase(_qgates, target); + _id_to_gate.erase(id); _dirty = true; return true; } diff --git a/src/qcir/qcir.hpp b/src/qcir/qcir.hpp index 98d31d8a..34c33240 100644 --- a/src/qcir/qcir.hpp +++ b/src/qcir/qcir.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,7 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia std::swap(_qgates, other._qgates); std::swap(_qubits, other._qubits); std::swap(_topological_order, other._topological_order); + std::swap(_id_to_gate, other._id_to_gate); } friend void swap(QCir& a, QCir& b) noexcept { @@ -150,8 +152,8 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia void add_qubits(size_t num); bool remove_qubit(QubitIdType qid); QCirGate* add_gate(std::string type, QubitIdList bits, dvlab::Phase phase, bool append); - QCirGate* append(QCirGate gate); - QCirGate* prepend(QCirGate gate); + QCirGate* append(GateType gate, QubitIdList bits); + QCirGate* prepend(GateType gate, QubitIdList bits); bool remove_gate(size_t id); bool read_qcir_file(std::filesystem::path const& filepath); @@ -188,17 +190,18 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia void print_qcir_info() const; private: - std::vector const& _update_topological_order() const; + size_t _gate_id = 0; + QubitIdType _qubit_id = 0; + std::string _filename; + std::string _gate_set; + std::vector _procedures; + std::vector _qgates; + std::vector _qubits; + std::unordered_map> _id_to_gate; + std::vector mutable _topological_order; + bool mutable _dirty = true; - size_t _gate_id = 0; - QubitIdType _qubit_id = 0; - bool mutable _dirty = true; - std::string _filename = ""; - std::string _gate_set = ""; - std::vector _procedures = {}; - std::vector _qgates = {}; - std::vector _qubits = {}; - std::vector mutable _topological_order = {}; + std::vector const& _update_topological_order() const; }; std::string to_qasm(QCir const& qcir); diff --git a/src/qcir/qcir_action.cpp b/src/qcir/qcir_action.cpp index 9e6be4f7..af0008d9 100644 --- a/src/qcir/qcir_action.cpp +++ b/src/qcir/qcir_action.cpp @@ -113,8 +113,8 @@ std::vector const& QCir::_update_topological_order() const { _topological_order.clear(); if (_qgates.empty()) return _topological_order; - auto dummy = new QCirGate(0, GateRotationCategory::id, dvlab::Phase(0)); - auto children = dummy->get_qubits(); + auto dummy = QCirGate(0, GateRotationCategory::id, dvlab::Phase(0)); + auto children = dummy.get_qubits(); for (size_t i = 0; i < _qubits.size(); i++) { children.push_back( {._qubit = 0, @@ -122,12 +122,11 @@ std::vector const& QCir::_update_topological_order() const { ._next = _qubits[i]->get_first(), ._isTarget = false}); } - dummy->set_qubits(children); - dfs(dummy, _topological_order); + dummy.set_qubits(children); + dfs(&dummy, _topological_order); _topological_order.pop_back(); // pop dummy reverse(_topological_order.begin(), _topological_order.end()); assert(_topological_order.size() == _qgates.size()); - delete dummy; _dirty = false; return _topological_order; From d0671ea60ff609ed3aaaf0b65ed8c6fffd02eb12 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Wed, 6 Mar 2024 23:49:40 +0800 Subject: [PATCH 4/5] :bug: Tableau and Duostra not reading gate in the topological order --- src/convert/qcir_to_tableau.cpp | 4 ++-- src/duostra/duostra.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/convert/qcir_to_tableau.cpp b/src/convert/qcir_to_tableau.cpp index b6d51124..d2a13562 100644 --- a/src/convert/qcir_to_tableau.cpp +++ b/src/convert/qcir_to_tableau.cpp @@ -216,7 +216,7 @@ std::optional to_tableau(qcir::QCir const& qcir) { gate.get_type_str() == "ecr"; }; - for (auto const& gate : qcir.get_gates()) { + for (auto const& gate : qcir.get_topologically_ordered_gates()) { if (!is_allowed_clifford(*gate) && !is_allowed_rotation(*gate)) { spdlog::error("Gate ID {} of type {} is not allowed in at the moment!!", gate->get_id(), gate->get_type_str()); return std::nullopt; @@ -224,7 +224,7 @@ std::optional to_tableau(qcir::QCir const& qcir) { } Tableau result{qcir.get_num_qubits()}; - for (auto const& gate : qcir.get_gates()) { + for (auto const& gate : qcir.get_topologically_ordered_gates()) { if (gate->get_type_str() == "h") { result.h(gate->get_qubits()[0]._qubit); } else if (gate->get_type_str() == "s") { diff --git a/src/duostra/duostra.cpp b/src/duostra/duostra.cpp index 095afa08..3591b04e 100644 --- a/src/duostra/duostra.cpp +++ b/src/duostra/duostra.cpp @@ -58,7 +58,7 @@ Duostra::Duostra(std::vector const& cir, size_t n_qubit, Device dev, void Duostra::make_dependency() { spdlog::info("Creating dependency of quantum circuit..."); std::vector all_gates; - for (auto const& g : _logical_circuit->get_gates()) { + for (auto const& g : _logical_circuit->get_topologically_ordered_gates()) { auto rotation_category = g->get_rotation_category(); auto q2 = max_qubit_id; From 444fa935613c006761a783446d5a6b7c43d0e7e0 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Thu, 7 Mar 2024 00:12:48 +0800 Subject: [PATCH 5/5] :children_crossing_road: QCir::get_gates() is now always in the topological order --- src/convert/qcir_to_tableau.cpp | 4 +- src/convert/qcir_to_tensor.cpp | 2 +- src/convert/qcir_to_zxgraph.cpp | 2 +- src/duostra/duostra.cpp | 3 +- src/duostra/mapping_eqv_checker.cpp | 2 +- src/qcir/optimizer/basic_optimization.cpp | 4 +- src/qcir/optimizer/trivial_optimization.cpp | 12 +++--- src/qcir/qcir.cpp | 41 +++++++++------------ src/qcir/qcir.hpp | 39 ++++++++++---------- src/qcir/qcir_action.cpp | 40 +++++++------------- src/qcir/qcir_print.cpp | 6 +-- src/qcir/qcir_writer.cpp | 2 +- 12 files changed, 69 insertions(+), 88 deletions(-) diff --git a/src/convert/qcir_to_tableau.cpp b/src/convert/qcir_to_tableau.cpp index d2a13562..b6d51124 100644 --- a/src/convert/qcir_to_tableau.cpp +++ b/src/convert/qcir_to_tableau.cpp @@ -216,7 +216,7 @@ std::optional to_tableau(qcir::QCir const& qcir) { gate.get_type_str() == "ecr"; }; - for (auto const& gate : qcir.get_topologically_ordered_gates()) { + for (auto const& gate : qcir.get_gates()) { if (!is_allowed_clifford(*gate) && !is_allowed_rotation(*gate)) { spdlog::error("Gate ID {} of type {} is not allowed in at the moment!!", gate->get_id(), gate->get_type_str()); return std::nullopt; @@ -224,7 +224,7 @@ std::optional to_tableau(qcir::QCir const& qcir) { } Tableau result{qcir.get_num_qubits()}; - for (auto const& gate : qcir.get_topologically_ordered_gates()) { + for (auto const& gate : qcir.get_gates()) { if (gate->get_type_str() == "h") { result.h(gate->get_qubits()[0]._qubit); } else if (gate->get_type_str() == "s") { diff --git a/src/convert/qcir_to_tensor.cpp b/src/convert/qcir_to_tensor.cpp index 41fcf741..ad78e85d 100644 --- a/src/convert/qcir_to_tensor.cpp +++ b/src/convert/qcir_to_tensor.cpp @@ -140,7 +140,7 @@ std::optional> to_tensor(QCir const &qcir) try { spdlog::trace(" - Add Qubit: {} input: {} output: {}", qubit_id, oi_pair.second, oi_pair.first); } - for (auto const &gate : qcir.get_topologically_ordered_gates()) { + for (auto const &gate : qcir.get_gates()) { if (stop_requested()) { spdlog::warn("Conversion interrupted."); return std::nullopt; diff --git a/src/convert/qcir_to_zxgraph.cpp b/src/convert/qcir_to_zxgraph.cpp index 0d3605d2..1d5505c7 100644 --- a/src/convert/qcir_to_zxgraph.cpp +++ b/src/convert/qcir_to_zxgraph.cpp @@ -474,7 +474,7 @@ std::optional to_zxgraph(QCir const& qcir, size_t decomposition_mode) { graph.add_edge(input, output, EdgeType::simple); } - for (auto const& gate : qcir.get_topologically_ordered_gates()) { + for (auto const& gate : qcir.get_gates()) { if (stop_requested()) { spdlog::warn("Conversion interrupted."); return std::nullopt; diff --git a/src/duostra/duostra.cpp b/src/duostra/duostra.cpp index 3591b04e..f1aff3f8 100644 --- a/src/duostra/duostra.cpp +++ b/src/duostra/duostra.cpp @@ -58,7 +58,7 @@ Duostra::Duostra(std::vector const& cir, size_t n_qubit, Device dev, void Duostra::make_dependency() { spdlog::info("Creating dependency of quantum circuit..."); std::vector all_gates; - for (auto const& g : _logical_circuit->get_topologically_ordered_gates()) { + for (auto const& g : _logical_circuit->get_gates()) { auto rotation_category = g->get_rotation_category(); auto q2 = max_qubit_id; @@ -79,6 +79,7 @@ void Duostra::make_dependency() { } all_gates.emplace_back(std::move(temp_gate)); } + // REVIEW - is the reordering necessary? std::unordered_map reordered_map; for (size_t i = 0; i < all_gates.size(); i++) { reordered_map[all_gates[i].get_id()] = i; diff --git a/src/duostra/mapping_eqv_checker.cpp b/src/duostra/mapping_eqv_checker.cpp index 640483cf..537ec8ec 100644 --- a/src/duostra/mapping_eqv_checker.cpp +++ b/src/duostra/mapping_eqv_checker.cpp @@ -44,7 +44,7 @@ MappingEquivalenceChecker::MappingEquivalenceChecker(QCir* phy, QCir* log, Devic * @return false */ bool MappingEquivalenceChecker::check() { - auto execute_order = _physical->get_topologically_ordered_gates(); + auto execute_order = _physical->get_gates(); if (_reverse) reverse(execute_order.begin(), execute_order.end()); // NOTE - Now the order starts from back // NOTE - Traverse all physical gates, should match dependency of logical gate std::unordered_set swaps; diff --git a/src/qcir/optimizer/basic_optimization.cpp b/src/qcir/optimizer/basic_optimization.cpp index fd5905ca..9c0b29c5 100644 --- a/src/qcir/optimizer/basic_optimization.cpp +++ b/src/qcir/optimizer/basic_optimization.cpp @@ -84,7 +84,7 @@ QCir Optimizer::_parse_once(QCir const& qcir, bool reversed, bool do_minimize_cz : spdlog::debug("Start parsing forward"); reset(qcir); - std::vector gates = qcir.get_topologically_ordered_gates(); + std::vector gates = qcir.get_gates(); if (reversed) { std::reverse(gates.begin(), gates.end()); } @@ -488,7 +488,7 @@ void Optimizer::_add_rotation_gate(QubitIdType target, dvlab::Phase ph, GateRota std::vector Optimizer::_compute_stats(QCir const& circuit) { size_t two_qubit = 0, had = 0, non_pauli = 0; std::vector stats; - for (auto const& g : circuit.get_topologically_ordered_gates()) { + for (auto const& g : circuit.get_gates()) { if (g->is_cx() || g->is_cz()) { two_qubit++; } else if (g->is_h()) { diff --git a/src/qcir/optimizer/trivial_optimization.cpp b/src/qcir/optimizer/trivial_optimization.cpp index f4fe091c..022310c5 100644 --- a/src/qcir/optimizer/trivial_optimization.cpp +++ b/src/qcir/optimizer/trivial_optimization.cpp @@ -33,7 +33,7 @@ std::optional Optimizer::trivial_optimization(QCir const& qcir) { result.add_procedures(qcir.get_procedures()); result.set_gate_set(qcir.get_gate_set()); - auto gate_list = qcir.get_topologically_ordered_gates(); + auto gate_list = qcir.get_gates(); for (auto gate : gate_list) { if (stop_requested()) { spdlog::warn("optimization interrupted"); @@ -81,7 +81,7 @@ std::optional Optimizer::trivial_optimization(QCir const& qcir) { * @return vector with size = circuit->getNqubit() */ std::vector Optimizer::_get_first_layer_gates(QCir& qcir, bool from_last) { - std::vector gate_list = qcir.get_topologically_ordered_gates(); + std::vector gate_list = qcir.get_gates(); if (from_last) reverse(gate_list.begin(), gate_list.end()); std::vector result; std::vector blocked; @@ -213,7 +213,7 @@ QCir replace_gate_sequence(QCir& qcir, QubitIdType qubit, size_t gate_num, replaced.add_qubits(qcir.get_num_qubits()); replaced.set_gate_set(qcir.get_gate_set()); - auto const& gate_list = qcir.get_topologically_ordered_gates(); + auto const& gate_list = qcir.get_gates(); size_t replace_count = 0; if (gate_num == 0) { @@ -274,7 +274,7 @@ std::vector zx_optimize(std::vector const& partial) { extractor::Extractor ext(&zx, nullptr, std::nullopt); QCir* result = ext.extract(); - auto const gate_list = result->get_topologically_ordered_gates(); + auto const gate_list = result->get_gates(); std::vector opt_partial; opt_partial.reserve(gate_list.size()); for (auto gate : gate_list) { @@ -288,7 +288,7 @@ std::vector zx_optimize(std::vector const& partial) { void Optimizer::_partial_zx_optimization(QCir& qcir) { for (auto const qubit : std::views::iota(0ul, qcir.get_num_qubits())) { - auto const gate_list = qcir.get_topologically_ordered_gates(); + auto const gate_list = qcir.get_gates(); std::vector type_seq; for (auto gate : gate_list) { if ((size_t)gate->get_targets()._qubit == qubit || (size_t)gate->get_control()._qubit == qubit) { @@ -327,7 +327,7 @@ void Optimizer::_partial_zx_optimization(QCir& qcir) { } for (auto const& [lhs, rhs] : replace_rules) { - auto const updated_gate_list = qcir.get_topologically_ordered_gates(); + auto const updated_gate_list = qcir.get_gates(); std::vector updated_type_seq; for (auto gate : updated_gate_list) { if ((size_t)gate->get_targets()._qubit == qubit || (size_t)gate->get_control()._qubit == qubit) { diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index f52a2587..982c5572 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -37,7 +37,7 @@ QCir::QCir(QCir const &other) { _qubits[i]->set_id(other._qubits[i]->get_id()); } - for (auto &gate : other.get_topologically_ordered_gates()) { + for (auto &gate : other.get_gates()) { auto bit_range = gate->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; }); auto new_gate = this->add_gate( @@ -46,10 +46,10 @@ QCir::QCir(QCir const &other) { new_gate->set_id(gate->get_id()); } - _gate_id = other._qgates.empty() + _gate_id = other.get_gates().empty() ? 0 : 1 + std::ranges::max( - other._qgates | + other.get_gates() | views::transform( [](QCirGate *g) { return g->get_id(); })); @@ -72,8 +72,8 @@ QCir::QCir(QCir const &other) { * @return QCirGate* */ QCirGate *QCir::get_gate(size_t id) const { - if (_id_to_gate.contains(id)) { - return _id_to_gate.at(id).get(); + if (_id_to_gates.contains(id)) { + return _id_to_gates.at(id).get(); } return nullptr; } @@ -111,7 +111,7 @@ std::unordered_map QCir::calculate_gate_times() const { gate_times.emplace(curr_gate->get_id(), max_time + curr_gate->get_delay()); }; - std::ranges::for_each(get_topologically_ordered_gates(), lambda); + std::ranges::for_each(get_gates(), lambda); return gate_times; } @@ -211,14 +211,11 @@ QCirGate *QCir::add_gate(std::string type, QubitIdList bits, dvlab::Phase phase, } QCirGate *QCir::append(GateType gate, QubitIdList bits) { - _id_to_gate.emplace(_gate_id, std::make_unique(_gate_id, std::get<0>(gate), *std::get<2>(gate))); - + _id_to_gates.emplace(_gate_id, std::make_unique(_gate_id, std::get<0>(gate), *std::get<2>(gate))); + auto *g = _id_to_gates[_gate_id].get(); for (size_t k = 0; k < bits.size(); k++) { - _id_to_gate[_gate_id]->add_qubit(bits[k], k == bits.size() - 1); // target is the last one + g->add_qubit(bits[k], k == bits.size() - 1); // target is the last one } - - _qgates.emplace_back(_id_to_gate[_gate_id].get()); - auto *g = _qgates.back(); _gate_id++; for (auto const &qb : g->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; })) { @@ -236,14 +233,11 @@ QCirGate *QCir::append(GateType gate, QubitIdList bits) { } QCirGate *QCir::prepend(GateType gate, QubitIdList bits) { - _id_to_gate.emplace(_gate_id, std::make_unique(_gate_id, std::get<0>(gate), *std::get<2>(gate))); - + _id_to_gates.emplace(_gate_id, std::make_unique(_gate_id, std::get<0>(gate), *std::get<2>(gate))); + auto *g = _id_to_gates[_gate_id].get(); for (size_t k = 0; k < bits.size(); k++) { - _id_to_gate[_gate_id]->add_qubit(bits[k], k == bits.size() - 1); // target is the last one + g->add_qubit(bits[k], k == bits.size() - 1); // target is the last one } - - _qgates.emplace_back(_id_to_gate[_gate_id].get()); - auto *g = _qgates.back(); _gate_id++; for (auto const &qb : g->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; })) { @@ -286,8 +280,7 @@ bool QCir::remove_gate(size_t id) { info[i]._prev = nullptr; info[i]._next = nullptr; } - std::erase(_qgates, target); - _id_to_gate.erase(id); + _id_to_gates.erase(id); _dirty = true; return true; } @@ -372,7 +365,7 @@ QCirGateStatistics QCir::get_gate_statistics() const { stat.nct++; }; - for (auto &g : _qgates) { + for (auto g : _id_to_gates | std::views::values | std::views::transform([](auto &p) { return p.get(); })) { auto type = g->get_rotation_category(); switch (type) { case GateRotationCategory::h: @@ -511,14 +504,14 @@ QCirGateStatistics QCir::get_gate_statistics() const { std::unordered_set not_final, not_initial; - for (auto const &g : _qgates) { + for (auto const &g : get_gates()) { if (is_clifford(g)) continue; add_input_cone_to(g, not_final); add_output_cone_to(g, not_initial); } // the intersection of the two sets is the internal gates - for (auto const &g : _qgates) { + for (auto const &g : get_gates()) { if (g->get_type_str() == "h" && not_final.contains(g) && not_initial.contains(g)) { stat.h_internal++; } @@ -576,7 +569,7 @@ void QCir::print_gate_statistics(bool detail) const { void QCir::translate(QCir const &qcir, std::string const &gate_set) { add_qubits(qcir.get_num_qubits()); Equivalence equivalence = EQUIVALENCE_LIBRARY[gate_set]; - for (auto const *cur_gate : qcir.get_topologically_ordered_gates()) { + for (auto const *cur_gate : qcir.get_gates()) { std::string const type = cur_gate->get_type_str(); auto bit_range = cur_gate->get_qubits() | std::views::transform([](QubitInfo const &qb) { return qb._qubit; }); diff --git a/src/qcir/qcir.hpp b/src/qcir/qcir.hpp index 34c33240..9816beaf 100644 --- a/src/qcir/qcir.hpp +++ b/src/qcir/qcir.hpp @@ -22,6 +22,7 @@ #include "qcir/qcir_qubit.hpp" #include "qsyn/qsyn_type.hpp" #include "spdlog/common.h" +#include "util/ordered_hashmap.hpp" namespace dvlab { @@ -110,10 +111,9 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia std::swap(_filename, other._filename); std::swap(_gate_set, other._gate_set); std::swap(_procedures, other._procedures); - std::swap(_qgates, other._qgates); std::swap(_qubits, other._qubits); - std::swap(_topological_order, other._topological_order); - std::swap(_id_to_gate, other._id_to_gate); + std::swap(_gate_list, other._gate_list); + std::swap(_id_to_gates, other._id_to_gates); } friend void swap(QCir& a, QCir& b) noexcept { @@ -122,21 +122,28 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia // Access functions size_t get_num_qubits() const { return _qubits.size(); } + size_t get_num_gates() const { return _id_to_gates.size(); } size_t calculate_depth() const; std::unordered_map calculate_gate_times() const; std::vector const& get_qubits() const { return _qubits; } - std::vector const& get_topologically_ordered_gates() const { + + /** + * @brief Get the gates as a topologically ordered list + * + * @return std::vector const& + */ + std::vector const& get_gates() const { _update_topological_order(); - return _topological_order; + return _gate_list; } - std::vector const& get_gates() const { return _qgates; } + QCirGate* get_gate(size_t gid) const; QCirQubit* get_qubit(QubitIdType qid) const; std::string get_filename() const { return _filename; } std::vector const& get_procedures() const { return _procedures; } std::string get_gate_set() const { return _gate_set; } - bool is_empty() const { return _qubits.empty() || _qgates.empty(); } + bool is_empty() const { return _qubits.empty() || _id_to_gates.empty(); } void set_filename(std::string f) { _filename = std::move(f); } void add_procedures(std::vector const& ps) { _procedures.insert(_procedures.end(), ps.begin(), ps.end()); } @@ -174,14 +181,6 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia void adjoint(); - // DFS functions - template - void topological_traverse(F lambda) const { - std::ranges::for_each(get_topologically_ordered_gates(), lambda); - } - - bool print_topological_order() const; - // Member functions about circuit reporting void print_gates(bool print_neighbors = false, std::span gate_ids = {}) const; void print_qcir() const; @@ -195,13 +194,13 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia std::string _filename; std::string _gate_set; std::vector _procedures; - std::vector _qgates; std::vector _qubits; - std::unordered_map> _id_to_gate; - std::vector mutable _topological_order; - bool mutable _dirty = true; + dvlab::utils::ordered_hashmap> _id_to_gates; + + std::vector mutable _gate_list; // a cache for topologically ordered gates. This member should not be accessed directly. Instead, use get_gates() to ensure the cache is up-to-date. + bool mutable _dirty = true; // mark if the topological order is dirty - std::vector const& _update_topological_order() const; + void _update_topological_order() const; }; std::string to_qasm(QCir const& qcir); diff --git a/src/qcir/qcir_action.cpp b/src/qcir/qcir_action.cpp index af0008d9..d2fc9a23 100644 --- a/src/qcir/qcir_action.cpp +++ b/src/qcir/qcir_action.cpp @@ -29,7 +29,7 @@ QCir* QCir::compose(QCir const& other) { if (get_qubit(qubit->get_id()) == nullptr) insert_qubit(qubit->get_id()); } - for (auto& targ_gate : other.get_topologically_ordered_gates()) { + for (auto& targ_gate : other.get_gates()) { QubitIdList qubits; for (auto const& b : targ_gate->get_qubits()) { qubits.emplace_back(b._qubit); @@ -51,7 +51,7 @@ QCir* QCir::tensor_product(QCir const& other) { for (auto& qubit : targ_qubits) { old_q2_new_q[qubit->get_id()] = push_qubit(); } - for (auto& targ_gate : other.get_topologically_ordered_gates()) { + for (auto& targ_gate : other.get_gates()) { QubitIdList qubits; for (auto const& b : targ_gate->get_qubits()) { qubits.emplace_back(old_q2_new_q[b._qubit]->get_id()); @@ -106,13 +106,14 @@ void dfs(QCirGate* curr_gate, std::vector& topo_order) { * * @return const vector& */ -std::vector const& QCir::_update_topological_order() const { +void QCir::_update_topological_order() const { if (!_dirty) - return _topological_order; + return; + + _gate_list.clear(); + if (_id_to_gates.empty()) + return; - _topological_order.clear(); - if (_qgates.empty()) - return _topological_order; auto dummy = QCirGate(0, GateRotationCategory::id, dvlab::Phase(0)); auto children = dummy.get_qubits(); for (size_t i = 0; i < _qubits.size(); i++) { @@ -123,24 +124,12 @@ std::vector const& QCir::_update_topological_order() const { ._isTarget = false}); } dummy.set_qubits(children); - dfs(&dummy, _topological_order); - _topological_order.pop_back(); // pop dummy - reverse(_topological_order.begin(), _topological_order.end()); - assert(_topological_order.size() == _qgates.size()); + dfs(&dummy, _gate_list); + _gate_list.pop_back(); // pop dummy + reverse(_gate_list.begin(), _gate_list.end()); + assert(_gate_list.size() == get_num_gates()); _dirty = false; - return _topological_order; -} - -/** - * @brief Print topological order - */ -bool QCir::print_topological_order() const { - auto print_gate_id = [](QCirGate* gate) { - fmt::println("{}", gate->get_id()); - }; - std::ranges::for_each(get_topologically_ordered_gates(), print_gate_id); - return true; } /** @@ -148,9 +137,8 @@ bool QCir::print_topological_order() const { * */ void QCir::reset() { - _qgates.clear(); _qubits.clear(); - _topological_order.clear(); + _gate_list.clear(); _gate_id = 0; _qubit_id = 0; @@ -158,7 +146,7 @@ void QCir::reset() { } void QCir::adjoint() { - for (auto& g : _qgates) { + for (auto& g : _gate_list) { g->adjoint(); auto qubits = g->get_qubits(); for (auto& q : qubits) { diff --git a/src/qcir/qcir_print.cpp b/src/qcir/qcir_print.cpp index 73281e38..0217c844 100644 --- a/src/qcir/qcir_print.cpp +++ b/src/qcir/qcir_print.cpp @@ -44,7 +44,7 @@ void QCir::print_gates(bool print_neighbors, std::span gate_ids) const { auto const times = calculate_gate_times(); if (gate_ids.empty()) { - for (auto const* gate : _qgates) { + for (auto const* gate : _id_to_gates | std::views::values | std::views::transform([](auto const& p) { return p.get(); })) { gate->print_gate(times.at(gate->get_id())); if (print_neighbors) { print_predecessors(gate); @@ -71,7 +71,7 @@ void QCir::print_gates(bool print_neighbors, std::span gate_ids) const { * @brief Print QCir */ void QCir::print_qcir() const { - fmt::println("QCir ({} qubits, {} gates)", _qubits.size(), _qgates.size()); + fmt::println("QCir ({} qubits, {} gates)", get_num_qubits(), get_num_gates()); } /** @@ -122,7 +122,7 @@ bool QCir::print_gate_as_diagram(size_t id, bool show_time) const { void QCir::print_qcir_info() const { auto stat = get_gate_statistics(); - fmt::println("QCir ({} qubits, {} gates, {} 2-qubits gates, {} T-gates, {} depths)", _qubits.size(), _qgates.size(), stat.twoqubit, stat.tfamily, calculate_depth()); + fmt::println("QCir ({} qubits, {} gates, {} 2-qubits gates, {} T-gates, {} depths)", get_num_qubits(), get_num_gates(), stat.twoqubit, stat.tfamily, calculate_depth()); } } // namespace qsyn::qcir diff --git a/src/qcir/qcir_writer.cpp b/src/qcir/qcir_writer.cpp index a67ba03b..9e0b46f8 100644 --- a/src/qcir/qcir_writer.cpp +++ b/src/qcir/qcir_writer.cpp @@ -98,7 +98,7 @@ std::string to_qasm(QCir const& qcir) { qasm += "include \"qelib1.inc\";\n"; qasm += fmt::format("qreg q[{}];\n", qcir.get_num_qubits()); - for (auto const* cur_gate : qcir.get_topologically_ordered_gates()) { + for (auto const* cur_gate : qcir.get_gates()) { auto type_str = cur_gate->get_type_str(); std::vector pins = cur_gate->get_qubits(); auto is_special_phase = cur_gate->get_phase().denominator() == 1 || cur_gate->get_phase().denominator() == 2 || cur_gate->get_phase() == Phase(1, 4) || cur_gate->get_phase() == Phase(-1, 4);