From 8c31fb8b80c435ab4cf2578795fae60cabd4b546 Mon Sep 17 00:00:00 2001 From: Mu-Te Joshua Lau <71618875+JoshuaLau0220@users.noreply.github.com> Date: Sun, 17 Mar 2024 17:33:38 +0800 Subject: [PATCH 1/3] :recycle: add control gate type --- src/convert/qcir_to_tableau.cpp | 58 +++++- src/convert/qcir_to_tensor.cpp | 27 +-- src/convert/qcir_to_zxgraph.cpp | 67 +++--- src/convert/tableau_to_qcir.cpp | 4 +- src/duostra/duostra.cpp | 8 +- src/duostra/mapping_eqv_checker.cpp | 10 +- src/extractor/extract.cpp | 53 +---- src/qcir/gate_type.cpp | 29 ++- src/qcir/gate_type.hpp | 72 +++---- src/qcir/optimizer/basic_optimization.cpp | 50 ++--- src/qcir/optimizer/gate_optimization.cpp | 4 +- src/qcir/optimizer/optimizer.cpp | 2 +- src/qcir/optimizer/optimizer.hpp | 4 +- src/qcir/optimizer/trivial_optimization.cpp | 26 ++- src/qcir/oracle/k_lut.cpp | 18 +- src/qcir/qcir.cpp | 13 +- src/qcir/qcir.hpp | 2 - src/qcir/qcir_cmd.cpp | 81 +++----- src/qcir/qcir_reader.cpp | 215 +------------------- src/tensor/decomposer.cpp | 9 +- src/tensor/decomposer.hpp | 19 +- tests/conversion/qc2zx/dof/large.dof | 2 - tests/conversion/qc2zx/ref/large.log | 4 - 23 files changed, 294 insertions(+), 483 deletions(-) diff --git a/src/convert/qcir_to_tableau.cpp b/src/convert/qcir_to_tableau.cpp index 8069fdb7..5f13397b 100644 --- a/src/convert/qcir_to_tableau.cpp +++ b/src/convert/qcir_to_tableau.cpp @@ -302,15 +302,57 @@ bool append_to_tableau(qcir::RYGate const& op, experimental::Tableau& tableau, Q } template <> -bool append_to_tableau(qcir::LegacyGateType const& op, experimental::Tableau& tableau, QubitIdList const& qubits) { - if (op.get_type() == "cx") { - tableau.cx(qubits[0], qubits[1]); - } else if (op.get_type() == "cz") { - tableau.cz(qubits[0], qubits[1]); - } else { - experimental::implement_rotation_gate(tableau, op.get_rotation_category(), op.get_phase(), qubits); +bool append_to_tableau(qcir::ControlGate const& op, experimental::Tableau& tableau, QubitIdList const& qubits) { + if (auto target_op = op.get_target_operation().get_underlying_if()) { + if (op.get_num_qubits() == 2 && target_op->get_phase() == dvlab::Phase(1)) { + tableau.cx(qubits[0], qubits[1]); + } else { + experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::px, target_op->get_phase(), qubits); + } + return true; } - return true; + + if (auto target_op = op.get_target_operation().get_underlying_if()) { + if (op.get_num_qubits() == 2 && target_op->get_phase() == dvlab::Phase(1)) { + tableau.sdg(qubits[1]); + tableau.cx(qubits[0], qubits[1]); + tableau.s(qubits[1]); + } else { + experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::py, target_op->get_phase(), qubits); + } + return true; + } + + if (auto target_op = op.get_target_operation().get_underlying_if()) { + if (op.get_num_qubits() == 2 && target_op->get_phase() == dvlab::Phase(1)) { + tableau.cz(qubits[0], qubits[1]); + } else { + experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::pz, target_op->get_phase(), qubits); + } + return true; + } + + if (auto target_op = op.get_target_operation().get_underlying_if()) { + experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::rx, target_op->get_phase(), qubits); + return true; + } + + if (auto target_op = op.get_target_operation().get_underlying_if()) { + experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::ry, target_op->get_phase(), qubits); + return true; + } + + if (auto target_op = op.get_target_operation().get_underlying_if()) { + experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::rz, target_op->get_phase(), qubits); + return true; + } + + return false; +} + +template <> +bool append_to_tableau(qcir::LegacyGateType const& /* op */, experimental::Tableau& /* tableau */, QubitIdList const& /* qubits*/) { + return false; } namespace experimental { diff --git a/src/convert/qcir_to_tensor.cpp b/src/convert/qcir_to_tensor.cpp index 7ce77b68..6d19c17f 100644 --- a/src/convert/qcir_to_tensor.cpp +++ b/src/convert/qcir_to_tensor.cpp @@ -86,26 +86,19 @@ std::optional> to_tensor(RYGate const& op) { } template <> -std::optional> to_tensor(LegacyGateType const& op) { - switch (op.get_rotation_category()) { - case GateRotationCategory::pz: - return QTensor::control(QTensor::pzgate(op.get_phase()), op.get_num_qubits() - 1); - case GateRotationCategory::rz: - return QTensor::control(QTensor::rzgate(op.get_phase()), op.get_num_qubits() - 1); - case GateRotationCategory::px: - return QTensor::control(QTensor::pxgate(op.get_phase()), op.get_num_qubits() - 1); - case GateRotationCategory::rx: - return QTensor::control(QTensor::rxgate(op.get_phase()), op.get_num_qubits() - 1); - case GateRotationCategory::py: - return QTensor::control(QTensor::pygate(op.get_phase()), op.get_num_qubits() - 1); - case GateRotationCategory::ry: - return QTensor::control(QTensor::rygate(op.get_phase()), op.get_num_qubits() - 1); - - default: - return std::nullopt; +std::optional> to_tensor(ControlGate const& op) { + if (auto target_tensor = to_tensor(op.get_target_operation())) { + return QTensor::control(*target_tensor, op.get_num_qubits() - op.get_target_operation().get_num_qubits()); + } else { + return std::nullopt; } } +template <> +std::optional> to_tensor(LegacyGateType const& /* op */) { + return std::nullopt; +} + /** * @brief Convert gate to tensor * diff --git a/src/convert/qcir_to_zxgraph.cpp b/src/convert/qcir_to_zxgraph.cpp index e68910ab..347be089 100644 --- a/src/convert/qcir_to_zxgraph.cpp +++ b/src/convert/qcir_to_zxgraph.cpp @@ -26,7 +26,7 @@ namespace qsyn { using zx::ZXVertex, zx::ZXGraph, zx::VertexType, zx::EdgeType; -using qcir::GateRotationCategory, qcir::QCir; +using qcir::QCir; namespace { @@ -375,33 +375,46 @@ std::optional to_zxgraph(qcir::RYGate const& op) { } template <> -std::optional to_zxgraph(qcir::LegacyGateType const& op) { - assert(op.get_num_qubits() != 1); - switch (op.get_rotation_category()) { - case GateRotationCategory::rz: - return create_mcr_zx_form(op.get_num_qubits(), op.get_phase(), RotationAxis::z); - case GateRotationCategory::rx: - return create_mcr_zx_form(op.get_num_qubits(), op.get_phase(), RotationAxis::x); - case GateRotationCategory::ry: - return create_mcr_zx_form(op.get_num_qubits(), op.get_phase(), RotationAxis::y); - - case GateRotationCategory::pz: - if (op.get_num_qubits() == 2 && op.get_phase() == Phase(1)) { - return create_cz_zx_form(); - } else { - return create_mcp_zx_form(op.get_num_qubits(), op.get_phase(), RotationAxis::z); - } - case GateRotationCategory::px: - if (op.get_num_qubits() == 2 && op.get_phase() == Phase(1)) { - return create_cx_zx_form(); - } else { - return create_mcp_zx_form(op.get_num_qubits(), op.get_phase(), RotationAxis::x); - } - case GateRotationCategory::py: - return create_mcp_zx_form(op.get_num_qubits(), op.get_phase(), RotationAxis::y); - default: - return std::nullopt; +std::optional to_zxgraph(qcir::ControlGate const& op) { + auto const& target_op = op.get_target_operation(); + + if (auto const px = target_op.get_underlying_if()) { + if (op.get_num_qubits() == 2 && px->get_phase() == Phase(1)) { + return create_cx_zx_form(); + } + return create_mcp_zx_form(op.get_num_qubits(), px->get_phase(), RotationAxis::x); + } + + if (auto const py = target_op.get_underlying_if()) { + return create_mcp_zx_form(op.get_num_qubits(), py->get_phase(), RotationAxis::y); + } + + if (auto const pz = target_op.get_underlying_if()) { + if (op.get_num_qubits() == 2 && pz->get_phase() == Phase(1)) { + return create_cz_zx_form(); + } + + return create_mcp_zx_form(op.get_num_qubits(), pz->get_phase(), RotationAxis::z); } + + if (auto const rx = target_op.get_underlying_if()) { + return create_mcr_zx_form(op.get_num_qubits(), rx->get_phase(), RotationAxis::x); + } + + if (auto const ry = target_op.get_underlying_if()) { + return create_mcr_zx_form(op.get_num_qubits(), ry->get_phase(), RotationAxis::y); + } + + if (auto const rz = target_op.get_underlying_if()) { + return create_mcr_zx_form(op.get_num_qubits(), rz->get_phase(), RotationAxis::z); + } + + return std::nullopt; +} + +template <> +std::optional to_zxgraph(qcir::LegacyGateType const& /* op */) { + return std::nullopt; } std::optional to_zxgraph(qcir::QCirGate const& gate) { diff --git a/src/convert/tableau_to_qcir.cpp b/src/convert/tableau_to_qcir.cpp index 69c2ab9e..e14ebbe4 100644 --- a/src/convert/tableau_to_qcir.cpp +++ b/src/convert/tableau_to_qcir.cpp @@ -31,7 +31,7 @@ void add_clifford_gate(qcir::QCir& qcir, CliffordOperator const& op) { qcir.append(qcir::SGate(), {gsl::narrow(qubits[0])}); break; case COT::cx: - qcir.add_gate("cx", {gsl::narrow(qubits[0]), gsl::narrow(qubits[1])}, {}, true); + qcir.append(qcir::CXGate(), {gsl::narrow(qubits[0]), gsl::narrow(qubits[1])}); break; case COT::sdg: qcir.append(qcir::SdgGate(), {gsl::narrow(qubits[0])}); @@ -52,7 +52,7 @@ void add_clifford_gate(qcir::QCir& qcir, CliffordOperator const& op) { qcir.append(qcir::ZGate(), {gsl::narrow(qubits[0])}); break; case COT::cz: - qcir.add_gate("cz", {gsl::narrow(qubits[0]), gsl::narrow(qubits[1])}, {}, true); + qcir.append(qcir::CZGate(), {gsl::narrow(qubits[0]), gsl::narrow(qubits[1])}); break; case COT::swap: qcir.append(qcir::SwapGate(), {gsl::narrow(qubits[0]), gsl::narrow(qubits[1])}); diff --git a/src/duostra/duostra.cpp b/src/duostra/duostra.cpp index 7d754c38..e5089fc5 100644 --- a/src/duostra/duostra.cpp +++ b/src/duostra/duostra.cpp @@ -137,14 +137,14 @@ void Duostra::build_circuit_by_result() { if (qubits[1] != max_qubit_id) { qu.emplace_back(qubits[1]); } - if (operation.get_operation() == SwapGate{}) { + if (operation.get_operation().is()) { // NOTE - Decompose SWAP into three CX QubitIdList qu_reverse; qu_reverse.emplace_back(qubits[1]); qu_reverse.emplace_back(qubits[0]); - _physical_circuit->append(LegacyGateType(std::make_tuple(GateRotationCategory::px, 2, Phase(1))), qu); - _physical_circuit->append(LegacyGateType(std::make_tuple(GateRotationCategory::px, 2, Phase(1))), qu_reverse); - _physical_circuit->append(LegacyGateType(std::make_tuple(GateRotationCategory::px, 2, Phase(1))), qu); + _physical_circuit->append(CXGate(), qu); + _physical_circuit->append(CXGate(), qu_reverse); + _physical_circuit->append(CXGate(), qu); } else { _physical_circuit->append(operation.get_operation(), qu); } diff --git a/src/duostra/mapping_eqv_checker.cpp b/src/duostra/mapping_eqv_checker.cpp index 588dd36d..a844e904 100644 --- a/src/duostra/mapping_eqv_checker.cpp +++ b/src/duostra/mapping_eqv_checker.cpp @@ -53,7 +53,7 @@ bool MappingEquivalenceChecker::check() { if (swaps.contains(phys_gate)) { continue; } - if (phys_gate->get_operation() == CXGate{} || phys_gate->get_operation() == CZGate{}) { + if (phys_gate->get_num_qubits() == 2) { if (is_swap(phys_gate)) { if (!execute_swap(phys_gate, swaps)) return false; } else { @@ -78,12 +78,12 @@ bool MappingEquivalenceChecker::check() { * @return false */ bool MappingEquivalenceChecker::is_swap(QCirGate* candidate) { - if (candidate->get_operation() != CXGate{}) return false; + if (candidate->get_operation() != CXGate()) return false; QCirGate* q0_gate = get_next(*_physical, candidate->get_id(), 0); QCirGate* q1_gate = get_next(*_physical, candidate->get_id(), 1); if (q0_gate != q1_gate || q0_gate == nullptr || q1_gate == nullptr) return false; - if (q0_gate->get_operation() != CXGate{}) return false; + if (q0_gate->get_operation() != CXGate()) return false; // q1gate == q0 gate if (candidate->get_qubit(0) != q1_gate->get_qubit(1) || candidate->get_qubit(1) != q0_gate->get_qubit(0)) return false; @@ -93,7 +93,7 @@ bool MappingEquivalenceChecker::is_swap(QCirGate* candidate) { q1_gate = get_next(*_physical, candidate->get_id(), 1); if (q0_gate != q1_gate || q0_gate == nullptr || q1_gate == nullptr) return false; - if (q0_gate->get_operation() != CXGate{}) return false; + if (q0_gate->get_operation() != CXGate()) return false; // q1 gate == q0 gate if (candidate->get_qubit(0) != q1_gate->get_qubit(1) || candidate->get_qubit(1) != q0_gate->get_qubit(0)) return false; @@ -108,7 +108,7 @@ bool MappingEquivalenceChecker::is_swap(QCirGate* candidate) { QCirGate* log_gate0 = _dependency[logical_gate_ctrl_id.value()]; QCirGate* log_gate1 = _dependency[logical_gate_targ_id.value()]; - return log_gate0 != log_gate1 || log_gate0 == nullptr || log_gate0->get_operation() != CXGate{}; + return log_gate0 != log_gate1 || log_gate0 == nullptr || log_gate0->get_operation() != CXGate(); } /** diff --git a/src/extractor/extract.cpp b/src/extractor/extract.cpp index 9c1599c3..c3148ff6 100644 --- a/src/extractor/extract.cpp +++ b/src/extractor/extract.cpp @@ -9,7 +9,6 @@ #include #include -#include #include "duostra/duostra.hpp" #include "duostra/mapping_eqv_checker.hpp" @@ -237,7 +236,7 @@ bool Extractor::extract_czs(bool check) { std::vector gates; for (auto const& [s, t] : remove_list) { _graph->remove_edge(s, t, EdgeType::hadamard); - gates.emplace_back(0, LegacyGateType{std::make_tuple(GateRotationCategory::pz, 2, dvlab::Phase(1))}, QubitIdList{_qubit_map[s->get_qubit()], _qubit_map[t->get_qubit()]}); + gates.emplace_back(0, CZGate(), QubitIdList{_qubit_map[s->get_qubit()], _qubit_map[t->get_qubit()]}); } if (!gates.empty()) { prepend_series_gates(gates); @@ -269,7 +268,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); - _logical_circuit->add_gate("cx", {ctrl, targ}, dvlab::Phase(0), false); + _logical_circuit->prepend(CXGate(), {ctrl, targ}); } } @@ -675,38 +674,6 @@ void Extractor::_block_elimination(dvlab::BooleanMatrix& best_matrix, size_t& mi } } -// void Extractor::_block_elimination(size_t& best_block, dvlab::BooleanMatrix& best_matrix, size_t& min_cost, size_t block_size) { -// dvlab::BooleanMatrix copied_matrix = _biadjacency; -// copied_matrix.gaussian_elimination_skip(block_size, true, true); -// if (FILTER_DUPLICATE_CXS) _filter_duplicate_cxs(); - -// // NOTE - Construct Duostra Input -// std::unordered_map front_id2_vertex; -// size_t cnt = 0; -// for (auto& f : _frontier) { -// front_id2_vertex[cnt] = f; -// cnt++; -// } -// std::vector ops; -// for (auto& [t, c] : copied_matrix.get_row_operations()) { -// // NOTE - targ and ctrl are opposite here -// size_t ctrl = _qubit_map[front_id2_vertex[c]->get_qubit()]; -// size_t targ = _qubit_map[front_id2_vertex[t]->get_qubit()]; -// spdlog::debug("Adding CX: {} {}", ctrl, targ); -// ops.emplace_back(GateRotationCategory::px, dvlab::Phase(0), std::make_tuple(ctrl, targ), std::make_tuple(0, 0)); -// } - -// // NOTE - Get Mapping result, Device is passed by copy -// qsyn::duostra::Duostra duo(ops, _graph->get_num_outputs(), _device.value(), {.verify_result = false, .silent = true, .use_tqdm = false}); -// size_t depth = duo.map(true); -// spdlog::debug("Block size: {}, depth: {}, #cx: {}", block_size, depth, ops.size()); -// if (depth < min_cost) { -// min_cost = depth; -// best_matrix = copied_matrix; -// best_block = block_size; -// } -// } - /** * @brief Permute qubit if input and output are not match * @@ -848,16 +815,6 @@ void Extractor::prepend_series_gates(std::vector const& logical for (auto const& gate : logical) { _logical_circuit->prepend(gate.get_operation(), gate.get_qubits()); } - - // for (auto const& gates : physical) { - // auto qubits = gates.get_qubits(); - // if (gates.is_swap()) { - // prepend_swap_gate(get<0>(qubits), get<1>(qubits), _physical_circuit); - // _num_swaps++; - // } else if (gates.get_phase() != dvlab::Phase(0)) { - // _physical_circuit->add_gate(gates.get_type_str(), {get<0>(qubits), get<1>(qubits)}, gates.get_phase(), false); - // } - // } } /** @@ -869,9 +826,9 @@ void Extractor::prepend_series_gates(std::vector const& logical */ void Extractor::prepend_swap_gate(QubitIdType q0, QubitIdType q1, QCir* circuit) { // NOTE - No qubit permutation in Physical Circuit - circuit->add_gate("cx", {q0, q1}, dvlab::Phase(1), false); - circuit->add_gate("cx", {q1, q0}, dvlab::Phase(1), false); - circuit->add_gate("cx", {q0, q1}, dvlab::Phase(1), false); + circuit->prepend(CXGate(), {q0, q1}); + circuit->prepend(CXGate(), {q1, q0}); + circuit->prepend(CXGate(), {q0, q1}); } /** diff --git a/src/qcir/gate_type.cpp b/src/qcir/gate_type.cpp index 1d9ea4f2..91ff2d13 100644 --- a/src/qcir/gate_type.cpp +++ b/src/qcir/gate_type.cpp @@ -8,26 +8,32 @@ #include "qcir/gate_type.hpp" #include "qcir/qcir_translate.hpp" +#include "util/dvlab_string.hpp" #include "util/util.hpp" namespace qsyn::qcir { -std::optional str_to_operation(std::string const& str, std::vector const& params) { +namespace { +std::optional str_to_basic_operation(std::string str, std::vector const& params) { + str = dvlab::str::tolower_string(str); if (params.empty()) { if (str == "id") return IdGate(); if (str == "h") return HGate(); if (str == "swap") return SwapGate(); if (str == "ecr") return ECRGate(); + if (str == "z") return ZGate(); if (str == "s") return SGate(); if (str == "sdg") return SdgGate(); if (str == "t") return TGate(); if (str == "tdg") return TdgGate(); - if (str == "x") return XGate(); + + if (str == "x" || str == "not") return XGate(); if (str == "sx" || str == "x_1_2") return SXGate(); if (str == "sxdg") return SXdgGate(); if (str == "tx") return TXGate(); if (str == "txdg") return TXdgGate(); + if (str == "y") return YGate(); if (str == "sy" || str == "y_1_2") return SYGate(); if (str == "sydg") return SYdgGate(); @@ -45,6 +51,24 @@ std::optional str_to_operation(std::string const& str, std::vector str_to_operation(std::string str, std::vector const& params) { + str = dvlab::str::tolower_string(str); + + auto const n_ctrls = str.find_first_not_of('c'); + str = str.substr(n_ctrls); + + auto basic_op_type = str_to_basic_operation(str, params); + + if (!basic_op_type.has_value()) return std::nullopt; + + if (n_ctrls > 0) { + return ControlGate(*basic_op_type, n_ctrls); + } + + return basic_op_type; +} Operation adjoint(LegacyGateType const& op) { return LegacyGateType(std::make_tuple(op.get_rotation_category(), op.get_num_qubits(), -op.get_phase())); @@ -111,6 +135,7 @@ std::optional str_to_gate_type(std::string_view str) { return std::nullopt; } + std::string gate_type_to_str(GateRotationCategory category, std::optional num_qubits, std::optional phase) { std::string type_str = std::invoke([num_qubits]() { if (!num_qubits.has_value()) { diff --git a/src/qcir/gate_type.hpp b/src/qcir/gate_type.hpp index 9d794e64..56e723c0 100644 --- a/src/qcir/gate_type.hpp +++ b/src/qcir/gate_type.hpp @@ -19,6 +19,7 @@ #include "tableau/tableau.hpp" #include "tensor/qtensor.hpp" #include "util/phase.hpp" +#include "util/util.hpp" #include "zx/zxgraph.hpp" namespace qsyn { @@ -63,7 +64,7 @@ class Operation; Operation adjoint(Operation const& op); bool is_clifford(Operation const& op); -std::optional str_to_operation(std::string const& str, std::vector const& params = {}); +std::optional str_to_operation(std::string str, std::vector const& params = {}); /** * @brief A type-erased interface for a quantum gate. @@ -210,39 +211,6 @@ class HGate { inline Operation adjoint(HGate const& op) { return op; } inline bool is_clifford(HGate const& /* op */) { return true; } -class CXGate { -public: - CXGate() = default; - std::string get_type() const { return "cx"; } - std::string get_repr() const { return "cx"; } - size_t get_num_qubits() const { return 2; } -}; - -inline Operation adjoint(CXGate const& op) { return op; } -inline bool is_clifford(CXGate const& /* op */) { return true; } - -class CYGate { -public: - CYGate() = default; - std::string get_type() const { return "cy"; } - std::string get_repr() const { return "cy"; } - size_t get_num_qubits() const { return 2; } -}; - -inline Operation adjoint(CYGate const& op) { return op; } -inline bool is_clifford(CYGate const& /* op */) { return true; } - -class CZGate { -public: - CZGate() = default; - std::string get_type() const { return "cz"; } - std::string get_repr() const { return "cz"; } - size_t get_num_qubits() const { return 2; } -}; - -inline Operation adjoint(CZGate const& op) { return op; } -inline bool is_clifford(CZGate const& /* op */) { return true; } - class SwapGate { public: SwapGate() = default; @@ -438,6 +406,35 @@ inline bool is_clifford(RXGate const& op) { return op.get_phase().denominator() inline Operation adjoint(RYGate const& op) { return RYGate(-op.get_phase()); } inline bool is_clifford(RYGate const& op) { return op.get_phase().denominator() <= 2; } +class ControlGate { +public: + ControlGate(Operation op, size_t n_ctrls = 1) : _op(std::move(op)), _n_ctrls(n_ctrls) { DVLAB_ASSERT(n_ctrls > 0, "Cannot instantiate a control gate with zero controls"); } + std::string get_type() const { return std::string(_n_ctrls, 'c') + _op.get_type(); } + std::string get_repr() const { return std::string(_n_ctrls, 'c') + _op.get_repr(); } + size_t get_num_qubits() const { return _op.get_num_qubits() + _n_ctrls; } + + Operation get_target_operation() const { return _op; } + void set_target_operation(Operation op) { _op = std::move(op); } + + size_t get_num_ctrls() const { return _n_ctrls; } + +private: + Operation _op; + size_t _n_ctrls; +}; + +inline Operation adjoint(ControlGate const& op) { return ControlGate(adjoint(op.get_target_operation()), op.get_num_ctrls()); } +inline bool is_clifford(ControlGate const& op) { return op.get_target_operation() == XGate() || op.get_target_operation() == YGate() || op.get_target_operation() == ZGate(); } + +// NOLINTBEGIN(readability-identifier-naming) // pseudo classes +inline ControlGate CXGate() { return ControlGate(XGate()); } +inline ControlGate CYGate() { return ControlGate(YGate()); } +inline ControlGate CZGate() { return ControlGate(ZGate()); } +inline ControlGate CCXGate() { return ControlGate(XGate(), 2); } +inline ControlGate CCYGate() { return ControlGate(YGate(), 2); } +inline ControlGate CCZGate() { return ControlGate(ZGate(), 2); } +// NOLINTEND(readability-identifier-naming) + } // namespace qsyn::qcir namespace qsyn { @@ -517,6 +514,13 @@ template <> std::optional> to_tensor(qcir::RYGate const& op); template <> bool append_to_tableau(qcir::RYGate const& op, experimental::Tableau& tableau, QubitIdList const& qubits); + +template <> +std::optional to_zxgraph(qcir::ControlGate const& op); +template <> +std::optional> to_tensor(qcir::ControlGate const& op); +template <> +bool append_to_tableau(qcir::ControlGate const& op, experimental::Tableau& tableau, QubitIdList const& qubits); } // namespace qsyn template <> diff --git a/src/qcir/optimizer/basic_optimization.cpp b/src/qcir/optimizer/basic_optimization.cpp index b56f5f7d..3b57092e 100644 --- a/src/qcir/optimizer/basic_optimization.cpp +++ b/src/qcir/optimizer/basic_optimization.cpp @@ -182,11 +182,11 @@ bool Optimizer::parse_gate(QCirGate* gate, bool do_swap, bool minimize_czs) { return true; } - if (gate->get_operation() == CZGate{}) { + if (gate->get_operation() == CZGate()) { _match_czs(gate, do_swap, minimize_czs); } - if (gate->get_operation() == CXGate{}) { + if (gate->get_operation() == CXGate()) { _match_cxs(gate, do_swap, minimize_czs); } return false; @@ -205,7 +205,7 @@ QCir Optimizer::_build_from_storage(size_t n_qubits, bool reversed) { for (auto& [qubit, gate_list] : _gates) { while (!gate_list.empty()) { QCirGate* g = gate_list[0]; - if (g->get_operation() != CXGate{} && g->get_operation() != CZGate{}) { + if (g->get_operation() != CXGate() && g->get_operation() != CZGate()) { reversed ? circuit.prepend(g->get_operation(), g->get_qubits()) : circuit.append(g->get_operation(), g->get_qubits()); @@ -223,7 +223,7 @@ QCir Optimizer::_build_from_storage(size_t n_qubits, bool reversed) { continue; } - auto const type = g->get_operation() != CZGate{} && g->get_qubit(0) != qubit; + auto const type = g->get_operation() != CZGate() && g->get_qubit(0) != qubit; std::vector removed; available_ids.emplace(g->get_id()); for (size_t i = 1; i < gate_list.size(); i++) { @@ -233,10 +233,10 @@ QCir Optimizer::_build_from_storage(size_t n_qubits, bool reversed) { ? circuit.prepend(g2->get_operation(), g2->get_qubits()) : circuit.append(g2->get_operation(), g2->get_qubits()); removed.emplace(removed.begin(), i); - } else if (g2->get_operation() != CXGate{} && g2->get_operation() != CZGate{}) { + } else if (g2->get_operation() != CXGate() && g2->get_operation() != CZGate()) { break; - } else if ((!type && (g2->get_operation() == CZGate{} || g2->get_qubit(0) == qubit)) || - (type && (g2->get_operation() == CXGate{} && g2->get_qubit(1) == qubit))) { + } else if ((!type && (g2->get_operation() == CZGate() || g2->get_qubit(0) == qubit)) || + (type && (g2->get_operation() == CXGate() && g2->get_qubit(1) == qubit))) { if (available_ids.contains(g2->get_id())) { available_ids.erase(g2->get_id()); auto q2 = qubit == g2->get_qubit(0) ? g2->get_qubit(1) : g2->get_qubit(0); @@ -287,19 +287,19 @@ void Optimizer::_add_cx(QubitIdType t1, QubitIdType t2, bool do_swap) { if (_availty[t1]) { if (!_availty[t2]) { for (QCirGate* gate : _available[t1] | std::views::reverse) { - if (gate->get_operation() == CXGate{} && gate->get_qubit(0) == t2 && gate->get_qubit(1) == t1) { + if (gate->get_operation() == CXGate() && gate->get_qubit(0) == t2 && gate->get_qubit(1) == t1) { found_match = true; break; } } if (found_match && do_swap) { // NOTE - - do the CNOT(t,c)CNOT(c,t) = CNOT(c,t)SWAP(c,t) commutation - if (count_if(_available[t2].begin(), _available[t2].end(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t2, t1}; })) { + if (count_if(_available[t2].begin(), _available[t2].end(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t2, t1}; })) { _statistics.DO_SWAP++; spdlog::trace("Apply a do_swap commutation"); auto cnot = _store_cx(t1, t2); - _gates[t1].erase(--(find_if(_gates[t1].rbegin(), _gates[t1].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t2, t1}; })).base()); - _gates[t2].erase(--(find_if(_gates[t2].rbegin(), _gates[t2].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t2, t1}; })).base()); + _gates[t1].erase(--(find_if(_gates[t1].rbegin(), _gates[t1].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t2, t1}; })).base()); + _gates[t2].erase(--(find_if(_gates[t2].rbegin(), _gates[t2].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t2, t1}; })).base()); _availty[t1] = false; _availty[t2] = true; _gates[t1].emplace_back(cnot); @@ -326,20 +326,20 @@ void Optimizer::_add_cx(QubitIdType t1, QubitIdType t2, bool do_swap) { found_match = false; for (QCirGate* gate : _available[t1] | std::views::reverse) { - if (gate->get_operation() == CXGate{} && gate->get_qubit(0) == t1 && gate->get_qubit(1) == t2) { + if (gate->get_operation() == CXGate() && gate->get_qubit(0) == t1 && gate->get_qubit(1) == t2) { found_match = true; break; } } // NOTE - do CNOT(c,t)CNOT(c,t) = id if (found_match) { - if (count_if(_available[t2].begin(), _available[t2].end(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t1, t2}; })) { + if (count_if(_available[t2].begin(), _available[t2].end(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t1, t2}; })) { _statistics.CNOT_CANCEL++; spdlog::trace("Cancel with previous CX"); - _available[t1].erase(--(find_if(_available[t1].rbegin(), _available[t1].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t1, t2}; })).base()); - _available[t2].erase(--(find_if(_available[t2].rbegin(), _available[t2].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t1, t2}; })).base()); - _gates[t1].erase(--(find_if(_gates[t1].rbegin(), _gates[t1].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t1, t2}; })).base()); - _gates[t2].erase(--(find_if(_gates[t2].rbegin(), _gates[t2].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{t1, t2}; })).base()); + _available[t1].erase(--(find_if(_available[t1].rbegin(), _available[t1].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t1, t2}; })).base()); + _available[t2].erase(--(find_if(_available[t2].rbegin(), _available[t2].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t1, t2}; })).base()); + _gates[t1].erase(--(find_if(_gates[t1].rbegin(), _gates[t1].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t1, t2}; })).base()); + _gates[t2].erase(--(find_if(_gates[t2].rbegin(), _gates[t2].rend(), [&](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{t1, t2}; })).base()); } else { found_match = false; } @@ -360,7 +360,7 @@ bool Optimizer::_replace_cx_and_cz_with_s_and_cx(QubitIdType t1, QubitIdType t2) if (std::ranges::none_of( _available[ctrl], [&ctrl = ctrl, &targ = targ](QCirGate* g) { - return g->get_operation() == CXGate{} && g->get_qubit(0) == ctrl && g->get_qubit(1) == targ; + return g->get_operation() == CXGate() && g->get_qubit(0) == ctrl && g->get_qubit(1) == targ; })) { continue; } @@ -369,7 +369,7 @@ bool Optimizer::_replace_cx_and_cz_with_s_and_cx(QubitIdType t1, QubitIdType t2) if (std::ranges::any_of( _available[targ], [&ctrl = ctrl, &targ = targ](QCirGate* gate_other) { - return gate_other->get_operation() == CXGate{} && gate_other->get_qubits() == QubitIdList{ctrl, targ}; + return gate_other->get_operation() == CXGate() && gate_other->get_qubits() == QubitIdList{ctrl, targ}; })) { return std::make_pair(ctrl, targ); } @@ -380,7 +380,7 @@ bool Optimizer::_replace_cx_and_cz_with_s_and_cx(QubitIdType t1, QubitIdType t2) // Then we can commute the CZ gate next to the CNOT and hence use it." // NOTE - looking at the gates behind the Z-like gates for (QCirGate* gate : _gates[targ] | std::views::take(_gates[targ].size() - _available[targ].size()) | std::views::reverse) { - if (gate->get_operation() != CXGate{}) + if (gate->get_operation() != CXGate()) break; if (gate->get_qubit(1) != targ) break; @@ -406,9 +406,9 @@ bool Optimizer::_replace_cx_and_cz_with_s_and_cx(QubitIdType t1, QubitIdType t2) if (_availty[targ]) { _available[targ].clear(); } - _available[ctrl].erase(--(find_if(_available[ctrl].rbegin(), _available[ctrl].rend(), [&ctrl = ctrl, &targ = targ](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{ctrl, targ}; })).base()); - _gates[ctrl].erase(--(find_if(_gates[ctrl].rbegin(), _gates[ctrl].rend(), [&ctrl = ctrl, &targ = targ](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{ctrl, targ}; })).base()); - _gates[targ].erase(--(find_if(_gates[targ].rbegin(), _gates[targ].rend(), [&ctrl = ctrl, &targ = targ](QCirGate* g) { return g->get_operation() == CXGate{} && g->get_qubits() == QubitIdList{ctrl, targ}; })).base()); + _available[ctrl].erase(--(find_if(_available[ctrl].rbegin(), _available[ctrl].rend(), [&ctrl = ctrl, &targ = targ](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{ctrl, targ}; })).base()); + _gates[ctrl].erase(--(find_if(_gates[ctrl].rbegin(), _gates[ctrl].rend(), [&ctrl = ctrl, &targ = targ](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{ctrl, targ}; })).base()); + _gates[targ].erase(--(find_if(_gates[targ].rbegin(), _gates[targ].rend(), [&ctrl = ctrl, &targ = targ](QCirGate* g) { return g->get_operation() == CXGate() && g->get_qubits() == QubitIdList{ctrl, targ}; })).base()); auto s1 = _store_sdg(targ); auto s2 = _store_s(targ); @@ -450,7 +450,7 @@ void Optimizer::_add_cz(QubitIdType t1, QubitIdType t2, bool do_minimize_czs) { } // NOTE - Try to cancel CZ - for (auto& targ_cz : _available[t1] | std::views::filter([](QCirGate* g) { return g->get_operation() == CZGate{}; })) { + for (auto& targ_cz : _available[t1] | std::views::filter([](QCirGate* g) { return g->get_operation() == CZGate(); })) { if (std::ranges::all_of( QubitIdList{t1, t2}, [&](auto const& q) { return dvlab::contains(targ_cz->get_qubits(), q); @@ -498,7 +498,7 @@ 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_gates()) { - if (g->get_operation() == CXGate{} || g->get_operation() == CZGate{}) { + if (g->get_operation() == CXGate() || g->get_operation() == CZGate()) { two_qubit++; } else if (g->get_operation() == HGate()) { had++; diff --git a/src/qcir/optimizer/gate_optimization.cpp b/src/qcir/optimizer/gate_optimization.cpp index eb5645d3..00eec685 100644 --- a/src/qcir/optimizer/gate_optimization.cpp +++ b/src/qcir/optimizer/gate_optimization.cpp @@ -120,7 +120,7 @@ void Optimizer::_match_z_rotations(QCirGate* gate) { } void Optimizer::_match_czs(QCirGate* gate, bool do_swap, bool do_minimize_czs) { - assert(gate->get_operation() == CZGate{}); + assert(gate->get_operation() == CZGate()); auto control_qubit = gate->get_qubit(0); auto target_qubit = gate->get_qubit(1); if (control_qubit > target_qubit) { // NOTE - Symmetric, let ctrl smaller than targ @@ -148,7 +148,7 @@ void Optimizer::_match_czs(QCirGate* gate, bool do_swap, bool do_minimize_czs) { } void Optimizer::_match_cxs(QCirGate* gate, bool do_swap, bool do_minimize_czs) { - assert(gate->get_operation() == CXGate{}); + assert(gate->get_operation() == CXGate()); auto control_qubit = gate->get_qubit(0); auto target_qubit = gate->get_qubit(1); diff --git a/src/qcir/optimizer/optimizer.cpp b/src/qcir/optimizer/optimizer.cpp index a4429bc0..15cb1f71 100644 --- a/src/qcir/optimizer/optimizer.cpp +++ b/src/qcir/optimizer/optimizer.cpp @@ -136,7 +136,7 @@ bool Optimizer::is_single_x_rotation(QCirGate* g) { * @return false */ bool Optimizer::is_cx_or_cz_gate(QCirGate* g) { - return g->get_num_qubits() == 2 && (g->get_operation() == CXGate{} || g->get_operation() == CZGate{}); + return g->get_num_qubits() == 2 && (g->get_operation() == CXGate() || g->get_operation() == CZGate()); } /** diff --git a/src/qcir/optimizer/optimizer.hpp b/src/qcir/optimizer/optimizer.hpp index e159dbfd..1ff7af80 100644 --- a/src/qcir/optimizer/optimizer.hpp +++ b/src/qcir/optimizer/optimizer.hpp @@ -140,12 +140,12 @@ class Optimizer { } inline QCirGate* _store_cx(QubitIdType ctrl, QubitIdType targ) { - _storage.emplace_back(std::make_unique(_storage.size(), LegacyGateType(std::make_tuple(GateRotationCategory::px, 2, dvlab::Phase(1))), QubitIdList{ctrl, targ})); + _storage.emplace_back(std::make_unique(_storage.size(), CXGate(), QubitIdList{ctrl, targ})); return _storage.back().get(); } inline QCirGate* _store_cz(QubitIdType ctrl, QubitIdType targ) { - _storage.emplace_back(std::make_unique(_storage.size(), LegacyGateType(std::make_tuple(GateRotationCategory::pz, 2, dvlab::Phase(1))), QubitIdList{ctrl, targ})); + _storage.emplace_back(std::make_unique(_storage.size(), CZGate(), QubitIdList{ctrl, targ})); return _storage.back().get(); } diff --git a/src/qcir/optimizer/trivial_optimization.cpp b/src/qcir/optimizer/trivial_optimization.cpp index deb164d5..a22588ef 100644 --- a/src/qcir/optimizer/trivial_optimization.cpp +++ b/src/qcir/optimizer/trivial_optimization.cpp @@ -172,13 +172,13 @@ void Optimizer::_fuse_z_phase(QCir& qcir, QCirGate* prev_gate, QCirGate* gate) { * @return modified circuit */ void Optimizer::_cancel_cx_or_cz(QCir& qcir, QCirGate* prev_gate, QCirGate* gate) { - if (prev_gate->get_operation() == CXGate{} && - gate->get_operation() == CXGate{} && + if (prev_gate->get_operation() == CXGate() && + gate->get_operation() == CXGate() && prev_gate->get_qubits() == gate->get_qubits()) { qcir.remove_gate(prev_gate->get_id()); return; - } else if (prev_gate->get_operation() == CZGate{} && - gate->get_operation() == CZGate{}) { + } else if (prev_gate->get_operation() == CZGate() && + gate->get_operation() == CZGate()) { if ((prev_gate->get_qubit(0) == gate->get_qubit(0) && prev_gate->get_qubit(1) == gate->get_qubit(1)) || (prev_gate->get_qubit(0) == gate->get_qubit(1) && @@ -252,7 +252,10 @@ QCir replace_single_qubit_gate_sequence(QCir& qcir, QubitIdType qubit, size_t ga if (replace_count == seq_len) { for (auto& type : seq) { - replaced.add_gate(type, gate->get_qubits(), dvlab::Phase(0), true); + auto const op = str_to_operation(type); + if (op.has_value()) { + replaced.append(*op, gate->get_qubits()); + } } replace_count--; continue; @@ -269,10 +272,15 @@ std::vector zx_optimize(std::vector const& partial) { for (std::string const& type : partial) { auto gate_type = str_to_gate_type(type); auto const& [category, num_qubits, gate_phase] = gate_type.value(); - if (gate_phase.has_value()) - qcir.add_gate(type, QubitIdList{0}, gate_phase.value(), true); - else - qcir.add_gate(type, QubitIdList{0}, dvlab::Phase(0), true); + if (gate_phase.has_value()) { + if (auto op = str_to_operation(type, {*gate_phase}); op.has_value()) { + qcir.append(op.value(), {0}); + } + } else { + if (auto op = str_to_operation(type); op.has_value()) { + qcir.append(op.value(), {0}); + } + } } auto zx = to_zxgraph(qcir).value(); diff --git a/src/qcir/oracle/k_lut.cpp b/src/qcir/oracle/k_lut.cpp index 731a2887..956c88f0 100644 --- a/src/qcir/oracle/k_lut.cpp +++ b/src/qcir/oracle/k_lut.cpp @@ -23,9 +23,9 @@ #include #include +#include "qcir/gate_type.hpp" #include "qcir/oracle/xag.hpp" #include "qsyn/qsyn_type.hpp" -#include "util/phase.hpp" namespace { @@ -287,7 +287,7 @@ void LUT::_construct_lut_1() { kitty::set_bit(tt, 0); auto qcir = QCir(2); qcir.append(qcir::XGate(), {0}); - qcir.add_gate("cx", {0, 1}, {}, true); + qcir.append(qcir::CXGate(), {0, 1}); qcir.append(qcir::XGate(), {0}); _table[tt] = qcir; } @@ -295,7 +295,7 @@ void LUT::_construct_lut_1() { auto tt = kitty::dynamic_truth_table(1); kitty::set_bit(tt, 1); auto qcir = QCir(2); - qcir.add_gate("cx", {0, 1}, {}, true); + qcir.append(qcir::CXGate(), {0, 1}); _table[tt] = qcir; } { @@ -315,8 +315,8 @@ void LUT::_construct_lut_2() { auto qcir = QCir(3); auto x = [&qcir](auto const& target) { qcir.append(XGate(), {target}); }; - auto cx = [&qcir](auto const& control, auto const& target) { qcir.add_gate("cx", {control, target}, {}, true); }; - auto ccx = [&qcir](auto const& c1, auto const& c2, auto const& target) { qcir.add_gate("ccx", {c1, c2, target}, {}, true); }; + auto cx = [&qcir](auto const& control, auto const& target) { qcir.append(CXGate(), {control, target}); }; + auto ccx = [&qcir](auto const& c1, auto const& c2, auto const& target) { qcir.append(CCXGate(), {c1, c2, target}); }; add_gates_2(i, x, cx, ccx); @@ -330,11 +330,9 @@ void LUT::_construct_lut_3() { kitty::create_from_words(tt, &i, &i + 1); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto qcir = QCir(4); - auto x = [&qcir](auto const& target) { qcir.add_gate("cx", {0, target + 1}, {}, true); }; - auto cx = [&qcir](auto const& control, auto const& target) { qcir.add_gate("ccx", {0, control + 1, target + 1}, {}, true); }; - auto ccx = [&qcir](auto const& c1, auto const& c2, auto const& target) { - qcir.add_gate("mcpx", {0, c1 + 1, c2 + 1, target + 1}, Phase(1), true); - }; + auto x = [&qcir](auto const& target) { qcir.append(CXGate(), {0, target + 1}); }; + auto cx = [&qcir](auto const& control, auto const& target) { qcir.append(CCXGate(), {0, control + 1, target + 1}); }; + auto ccx = [&qcir](auto const& c1, auto const& c2, auto const& target) { qcir.append(ControlGate(XGate(), 3), {0, c1 + 1, c2 + 1, target + 1}); }; // (a & b) ^ (!a & c) diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index 413b999e..436d4584 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -40,7 +40,7 @@ QCir::QCir(QCir const& other) { } for (auto& gate : other.get_gates()) { - // We do not call add_gate here because we want to keep the original gate id + // We do not call append here because we want to keep the original gate id _id_to_gates.emplace(gate->get_id(), std::make_unique(gate->get_id(), gate->get_operation(), gate->get_qubits())); _predecessors.emplace(gate->get_id(), std::vector>(gate->get_num_qubits(), std::nullopt)); _successors.emplace(gate->get_id(), std::vector>(gate->get_num_qubits(), std::nullopt)); @@ -317,7 +317,9 @@ QCirGate* QCir::add_gate(std::string type, QubitIdList const& bits, dvlab::Phase } size_t QCir::append(Operation const& op, QubitIdList const& bits) { - /* for now, assumes that op is always a legacy gate type */ + DVLAB_ASSERT( + op.get_num_qubits() == bits.size(), + fmt::format("Operation {} requires {} qubits, but {} qubits are given.", op.get_repr(), op.get_num_qubits(), bits.size())); _id_to_gates.emplace(_gate_id, std::make_unique(_gate_id, op, bits)); _predecessors.emplace(_gate_id, std::vector>(bits.size(), std::nullopt)); _successors.emplace(_gate_id, std::vector>(bits.size(), std::nullopt)); @@ -341,7 +343,9 @@ size_t QCir::append(Operation const& op, QubitIdList const& bits) { } size_t QCir::prepend(Operation const& op, QubitIdList const& bits) { - /* for now, assumes that op is always a legacy gate type */ + DVLAB_ASSERT( + op.get_num_qubits() == bits.size(), + fmt::format("Operation {} requires {} qubits, but {} qubits are given.", op.get_repr(), op.get_num_qubits(), bits.size())); _id_to_gates.emplace(_gate_id, std::make_unique(_gate_id, op, bits)); _predecessors.emplace(_gate_id, std::vector>(bits.size(), std::nullopt)); _successors.emplace(_gate_id, std::vector>(bits.size(), std::nullopt)); @@ -554,7 +558,8 @@ void QCir::translate(QCir const& qcir, std::string const& gate_set) { for (auto qubit_num : gate_qubit_list) { gate_qubit_id_list.emplace_back(cur_gate->get_qubit(qubit_num)); } - this->add_gate(gate_type, gate_qubit_id_list, gate_phase, true); + if (auto op = str_to_operation(gate_type); op.has_value()) + this->append(*op, gate_qubit_id_list); } } set_gate_set(gate_set); diff --git a/src/qcir/qcir.hpp b/src/qcir/qcir.hpp index 0bfcf7c2..202fedb1 100644 --- a/src/qcir/qcir.hpp +++ b/src/qcir/qcir.hpp @@ -139,8 +139,6 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia bool read_qcir_file(std::filesystem::path const& filepath); bool read_qc(std::filesystem::path const& filepath); bool read_qasm(std::filesystem::path const& filepath); - bool read_qsim(std::filesystem::path const& filepath); - bool read_quipper(std::filesystem::path const& filepath); bool write_qasm(std::filesystem::path const& filepath) const; diff --git a/src/qcir/qcir_cmd.cpp b/src/qcir/qcir_cmd.cpp index 9cefa5c2..dbd75fa4 100644 --- a/src/qcir/qcir_cmd.cpp +++ b/src/qcir/qcir_cmd.cpp @@ -432,77 +432,44 @@ dvlab::Command qcir_gate_add_cmd(QCirMgr& qcir_mgr) { type = dvlab::str::tolower_string(type); auto bits = parser.get("qubits"); - auto op = str_to_operation(type, parser.get>("--phase")); - if (!op.has_value() && bits.size() == 1 && type.starts_with("mc")) { - op = str_to_operation(type.substr(2), parser.get>("--phase")); - } - if (op.has_value()) { - if (bits.size() < op->get_num_qubits()) { - spdlog::error("Too few qubits are supplied for gate {}!!", type); - return CmdExecResult::error; - } else if (bits.size() > op->get_num_qubits()) { - spdlog::error("Too many qubits are supplied for gate {}!!", type); + if (type.starts_with("mc")) { + auto op = str_to_operation(type.substr(2), parser.get>("--phase")); + if (!op.has_value()) { + spdlog::error("Invalid gate type {}!!", type); return CmdExecResult::error; } - do_prepend - ? qcir_mgr.get()->prepend(*op, bits) - : qcir_mgr.get()->append(*op, bits); - return CmdExecResult::done; - } - - auto is_gate_category = [&](auto& category) { - return any_of(category.begin(), category.end(), - [&](auto& name_help) { - return type == name_help.first; - }); - }; - - dvlab::Phase phase{1}; - if (is_gate_category(single_qubit_gates_with_phase) || - is_gate_category(multi_qubit_gates_with_phase)) { - if (!parser.parsed("--phase")) { - spdlog::error("Phase must be specified for gate type {}!!", type); - return CmdExecResult::error; - } - phase = parser.get("--phase"); - } else if (parser.parsed("--phase")) { - spdlog::error("Phase is incompatible with gate type {}!!", type); - return CmdExecResult::error; - } - - if (is_gate_category(single_qubit_gates_no_phase) || - is_gate_category(single_qubit_gates_with_phase)) { - if (bits.empty()) { + if (bits.size() < op->get_num_qubits()) { spdlog::error("Too few qubits are supplied for gate {}!!", type); return CmdExecResult::error; - } else if (bits.size() > 1) { - spdlog::error("Too many qubits are supplied for gate {}!!", type); - return CmdExecResult::error; } - } - - if (is_gate_category(double_qubit_gates_no_phase)) { - if (bits.size() < 2) { - spdlog::error("Too few qubits are supplied for gate {}!!", type); - return CmdExecResult::error; - } else if (bits.size() > 2) { - spdlog::error("Too many qubits are supplied for gate {}!!", type); + auto const n_ctrls = bits.size() - op->get_num_qubits(); + if (n_ctrls > 0) { + do_prepend + ? qcir_mgr.get()->prepend(ControlGate(*op, n_ctrls), bits) + : qcir_mgr.get()->append(ControlGate(*op, n_ctrls), bits); + } else { + do_prepend + ? qcir_mgr.get()->prepend(*op, bits) + : qcir_mgr.get()->append(*op, bits); + } + } else { + auto op = str_to_operation(type, parser.get>("--phase")); + if (!op.has_value()) { + spdlog::error("Invalid gate type {}!!", type); return CmdExecResult::error; } - } - - if (is_gate_category(three_qubit_gates_no_phase)) { - if (bits.size() < 3) { + if (bits.size() < op->get_num_qubits()) { spdlog::error("Too few qubits are supplied for gate {}!!", type); return CmdExecResult::error; - } else if (bits.size() > 3) { + } else if (bits.size() > op->get_num_qubits()) { spdlog::error("Too many qubits are supplied for gate {}!!", type); return CmdExecResult::error; } + do_prepend + ? qcir_mgr.get()->prepend(*op, bits) + : qcir_mgr.get()->append(*op, bits); } - qcir_mgr.get()->add_gate(type, bits, phase, !do_prepend); - return CmdExecResult::done; }}; } diff --git a/src/qcir/qcir_reader.cpp b/src/qcir/qcir_reader.cpp index 1d00b997..eba77ae9 100644 --- a/src/qcir/qcir_reader.cpp +++ b/src/qcir/qcir_reader.cpp @@ -33,29 +33,7 @@ bool QCir::read_qcir_file(std::filesystem::path const& filepath) { return read_qasm(filepath); else if (extension == ".qc") return read_qc(filepath); - else if (extension == ".qsim") - return read_qsim(filepath); - else if (extension == ".quipper") - return read_quipper(filepath); - else if (extension == "") { - std::ifstream verify{filepath}; - if (!verify.is_open()) { - spdlog::error("Cannot open the file \"{}\"!!", filepath); - return false; - } - std::string first_item; - verify >> first_item; - - if (first_item == "Inputs:") - return read_quipper(filepath); - else if (isdigit(first_item[0])) - return read_qsim(filepath); - else { - spdlog::error("Cannot derive the type of file \"{}\"!!", filepath); - return false; - } - return true; - } else { + else { spdlog::error("File format \"{}\" is not supported!!", extension); return false; } @@ -132,7 +110,6 @@ bool QCir::read_qasm(std::filesystem::path const& filepath) { append(*op, qubit_ids); continue; } - add_gate(type, qubit_ids, phase.value(), true); } return true; } @@ -202,200 +179,22 @@ bool QCir::read_qc(std::filesystem::path const& filepath) { if (type == "Tof" || type == "tof") { if (qubit_ids.size() == 1) { append(XGate(), qubit_ids); - } else if (qubit_ids.size() == 2) - add_gate("cx", qubit_ids, dvlab::Phase(1), true); - else if (qubit_ids.size() == 3) - add_gate("ccx", qubit_ids, dvlab::Phase(1), true); - else { + } else if (qubit_ids.size() == 2) { + append(CXGate(), qubit_ids); + } else if (qubit_ids.size() == 3) { + append(CCXGate(), qubit_ids); + } else { spdlog::error("Toffoli gates with more than 2 controls are not supported!!"); return false; } } else if ((type == "Z" || type == "z") && qubit_ids.size() == 3) { - add_gate("ccz", qubit_ids, dvlab::Phase(1), true); + append(CCZGate(), qubit_ids); } else { auto op = str_to_operation(type); if (op.has_value()) { append(*op, qubit_ids); - continue; - } - add_gate(type, qubit_ids, dvlab::Phase(1), true); - } - } - } - return true; -} - -/** - * @brief Read QSIM - * - * @param filename - * @return true if successfully read - * @return false if error in file or not found - */ -bool QCir::read_qsim(std::filesystem::path const& filepath) { - // read file and open - std::ifstream qsim_file{filepath}; - if (!qsim_file.is_open()) { - spdlog::error("Cannot open the QSIM file \"{}\"!!", filepath); - return false; - } - - std::string line; - static std::vector const single_gate_list{"x", "y", "z", "h", "t", "x_1_2", "y_1_2", "rx", "rz", "s"}; - // decide qubit number - int n_qubit = 0; - getline(qsim_file, line); - n_qubit = stoi(line); - add_qubits(n_qubit); - - // add the gate - // Todo: implentment hz_1_2 gate and fs gate - - while (getline(qsim_file, line)) { - using dvlab::str::str_get_token; - if (line.empty()) continue; - std::string time, type, phase_str, qubit_id; - QubitIdList qubit_ids; - size_t pos = 0; - pos = str_get_token(line, time, pos); - pos = str_get_token(line, type, pos); - if (type == "cx" || type == "cz") { - // add 2 qubit gate - pos = str_get_token(line, qubit_id, pos); - qubit_ids.emplace_back(stoul(qubit_id)); - str_get_token(line, qubit_id, pos); - qubit_ids.emplace_back(stoul(qubit_id)); - add_gate(type, qubit_ids, dvlab::Phase(1), true); - } else if (type == "rx" || type == "rz") { - // add phase gate - pos = str_get_token(line, qubit_id, pos); - qubit_ids.emplace_back(stoul(qubit_id)); - str_get_token(line, phase_str, pos); - auto phase = dvlab::Phase::from_string(phase_str); - if (!phase.has_value()) { - spdlog::error("invalid phase on line {}!!", line); - return false; - } - if (auto op = str_to_operation(type, {*phase}); op.has_value()) { - append(*op, qubit_ids); - continue; - } - - add_gate(type, qubit_ids, phase.value(), true); - } else if (count(single_gate_list.begin(), single_gate_list.end(), type)) { - // add single qubit gate - str_get_token(line, qubit_id, pos); - qubit_ids.emplace_back(stoul(qubit_id)); - // FIXME - pass in the correct phase - if (auto op = str_to_operation(type); op.has_value()) { - append(*op, qubit_ids); - continue; - } - add_gate(type, qubit_ids, dvlab::Phase(0), true); - } else { - spdlog::error("Gate type {} is not supported!!", type); - return false; - } - } - return true; -} - -/** - * @brief Read QUIPPER - * - * @param filename - * @return true if successfully read - * @return false if error in file or not found - */ -bool QCir::read_quipper(std::filesystem::path const& filepath) { - // read file and open - std::ifstream quipper_file{filepath}; - if (!quipper_file.is_open()) { - spdlog::error("Cannot open the QUIPPER file \"{}\"!!", filepath); - return false; - } - - std::string line; - size_t n_qubit = 0; - std::vector single_list{"X", "T", "S", "H", "Z", "not"}; - - // Count qubit number - getline(quipper_file, line); - n_qubit = count(line.begin(), line.end(), 'Q'); - add_qubits(n_qubit); - - while (getline(quipper_file, line)) { - if (line.find("QGate") == 0) { - // addgate - std::string type = line.substr(line.find('[') + 2, line.find(']') - line.find('[') - 3); - size_t qubit_target = 0; - if (find(single_list.begin(), single_list.end(), type) != single_list.end()) { - qubit_target = stoul(line.substr(line.find('(') + 1, line.find(')') - line.find('(') - 1)); - QubitIdList qubit_ids; - - if (line.find("controls=") != std::string::npos) { - // have control - std::string ctrls_info; - ctrls_info = line.substr(line.find_last_of('[') + 1, line.find_last_of(']') - line.find_last_of('[') - 1); - - if (ctrls_info.find(std::to_string(qubit_target)) != std::string::npos) { - spdlog::error("Control qubit and target cannot be the same!!"); - return false; - } - - if (count(line.begin(), line.end(), '+') == 1) { - // one control - if (type != "not" && type != "X" && type != "Z") { - spdlog::error("Unsupported controlled gate type!! Only `cnot`, `CX` and `CZ` are supported."); - return false; - } - auto const qubit_control = stoul(ctrls_info.substr(1)); - qubit_ids.emplace_back(qubit_control); - qubit_ids.emplace_back(qubit_target); - type.insert(0, "C"); - add_gate(type, qubit_ids, dvlab::Phase(1), true); - } else if (count(line.begin(), line.end(), '+') == 2) { - // 2 controls - if (type != "not" && type != "X" && type != "Z") { - spdlog::error("Unsupported doubly-controlled gate type!! Only `ccx` and `ccz` are supported."); - return false; - } - size_t qubit_control1 = 0, qubit_control2 = 0; - qubit_control1 = stoul(ctrls_info.substr(1, ctrls_info.find(',') - 1)); - qubit_control2 = stoul(ctrls_info.substr(ctrls_info.find(',') + 2)); - qubit_ids.emplace_back(qubit_control1); - qubit_ids.emplace_back(qubit_control2); - qubit_ids.emplace_back(qubit_target); - type.insert(0, "CC"); - add_gate(type, qubit_ids, dvlab::Phase(1), true); - } else { - spdlog::error("Controlled gates with more than 2 controls are not supported!!"); - return false; - } - } else { - // without control - qubit_ids.emplace_back(qubit_target); - // FIXME - pass in the correct phase - add_gate(type, qubit_ids, dvlab::Phase(0), true); } - - } else { - spdlog::error("Unsupported gate type {}!!", type); - return false; } - continue; - } else if (line.find("Outputs") == 0) { - return true; - } else if (line.find("Comment") == 0 || line.find("QTerm0") == 0 || line.find("QMeas") == 0 || line.find("QDiscard") == 0) - continue; - else if (line.find("QInit0") == 0) { - spdlog::error("Unsupported expression: QInit0"); - return false; - } else if (line.find("QRot") == 0) { - spdlog::error("Unsupported expression: QRot"); - return false; - } else { - spdlog::error("Unsupported expression: {}", line); } } return true; diff --git a/src/tensor/decomposer.cpp b/src/tensor/decomposer.cpp index 83259dad..e8adf714 100644 --- a/src/tensor/decomposer.cpp +++ b/src/tensor/decomposer.cpp @@ -18,8 +18,13 @@ namespace qsyn::tensor { */ void Decomposer::_encode_control_gate(QubitIdList const& target, std::vector& qubit_list, std::vector& gate_list) { qubit_list.emplace_back(target); - gate_list.emplace_back(target.size() == 2 ? "cx" : "x"); - _quantum_circuit.add_gate(target.size() == 2 ? "cx" : "x", target, {}, true); + if (target.size() == 2) { + gate_list.emplace_back("cx"); + _quantum_circuit.append(qcir::CXGate(), target); + } else { + gate_list.emplace_back("x"); + _quantum_circuit.append(qcir::XGate(), target); + } } /** diff --git a/src/tensor/decomposer.hpp b/src/tensor/decomposer.hpp index 7c3e938d..d8fdd28f 100644 --- a/src/tensor/decomposer.hpp +++ b/src/tensor/decomposer.hpp @@ -340,8 +340,11 @@ bool Decomposer::Decomposer::_graycode(Tensor const& matrix, size_t i, size_t // do unpabbing DVLAB_ASSERT(gate_list.size() == qubit_list.size(), "Sizes of gate list and qubit list are different"); - for (auto const& q : std::views::iota(0UL, gate_list.size()) | std::views::reverse) - _quantum_circuit.add_gate(gate_list[q], qubit_list[q], {}, true); + for (auto const& q : std::views::iota(0UL, gate_list.size()) | std::views::reverse) { + if (auto const op = qcir::str_to_operation(gate_list[q], {}); op.has_value()) { + _quantum_circuit.append(*op, qubit_list[q]); + } + } return true; } @@ -421,9 +424,9 @@ bool Decomposer::_decompose_cnu(Tensor const& t, size_t diff_pos, size_t inde template bool Decomposer::_decompose_cnx(const std::vector& ctrls, const size_t extract_qubit, const size_t index, const size_t ctrl_gates) { if (ctrls.size() == 1) { - _quantum_circuit.add_gate("cx", {int(ctrls[0]), int(extract_qubit)}, {}, true); + _quantum_circuit.append(qcir::CXGate(), {gsl::narrow(ctrls[0]), gsl::narrow(extract_qubit)}); } else if (ctrls.size() == 2) { - _quantum_circuit.add_gate("ccx", {int(ctrls[0]), int(ctrls[1]), int(extract_qubit)}, {}, true); + _quantum_circuit.append(qcir::CCXGate(), {gsl::narrow(ctrls[0]), gsl::narrow(ctrls[1]), gsl::narrow(extract_qubit)}); } else { using float_type = U::value_type; if (!_decompose_cnu(QTensor::xgate(), extract_qubit, index, ctrl_gates)) return false; @@ -455,13 +458,13 @@ bool Decomposer::_decompose_cu(Tensor const& t, size_t ctrl, size_t targ) { } if (std::abs(angles.beta) > eps) { - _quantum_circuit.add_gate("cx", {int(ctrl), int(targ)}, {}, true); + _quantum_circuit.append(qcir::CXGate(), {gsl::narrow(ctrl), gsl::narrow(targ)}); if (std::abs((angles.alpha + angles.gamma) / 2) > eps) { _quantum_circuit.append(qcir::RZGate(Phase{((angles.alpha + angles.gamma) / 2) * (-1.0)}), {gsl::narrow(targ)}); } _quantum_circuit.append(qcir::RYGate(Phase{angles.beta * (-1.0)}), {gsl::narrow(targ)}); - _quantum_circuit.add_gate("cx", {int(ctrl), int(targ)}, {}, true); + _quantum_circuit.append(qcir::CXGate(), {gsl::narrow(ctrl), gsl::narrow(targ)}); _quantum_circuit.append(qcir::RYGate(Phase{angles.beta}), {gsl::narrow(targ)}); if (std::abs(angles.alpha) > eps) { @@ -470,9 +473,9 @@ bool Decomposer::_decompose_cu(Tensor const& t, size_t ctrl, size_t targ) { } else { if (std::abs((angles.alpha + angles.gamma) / 2) > eps) { - _quantum_circuit.add_gate("cx", {int(ctrl), int(targ)}, {}, true); + _quantum_circuit.append(qcir::CXGate(), {gsl::narrow(ctrl), gsl::narrow(targ)}); _quantum_circuit.append(qcir::RZGate(Phase{((angles.alpha + angles.gamma) / 2) * (-1.0)}), {gsl::narrow(targ)}); - _quantum_circuit.add_gate("cx", {int(ctrl), int(targ)}, {}, true); + _quantum_circuit.append(qcir::CXGate(), {gsl::narrow(ctrl), gsl::narrow(targ)}); } if (std::abs(angles.alpha) > eps) { _quantum_circuit.append(qcir::RZGate(Phase(angles.alpha)), {gsl::narrow(targ)}); diff --git a/tests/conversion/qc2zx/dof/large.dof b/tests/conversion/qc2zx/dof/large.dof index 8b9460fa..9fd28f71 100644 --- a/tests/conversion/qc2zx/dof/large.dof +++ b/tests/conversion/qc2zx/dof/large.dof @@ -16,6 +16,4 @@ qcir gate add y 0 qcir gate add sy 1 qcir gate add sdg 1 qc2zx -qcir read benchmark/qsim/circuits/circuit_q24 -r -qc2zx quit -f diff --git a/tests/conversion/qc2zx/ref/large.log b/tests/conversion/qc2zx/ref/large.log index 4ddf1393..2bbac85a 100644 --- a/tests/conversion/qc2zx/ref/large.log +++ b/tests/conversion/qc2zx/ref/large.log @@ -53,9 +53,5 @@ qsyn> qcir gate add sdg 1 qsyn> qc2zx -qsyn> qcir read benchmark/qsim/circuits/circuit_q24 -r - -qsyn> qc2zx - qsyn> quit -f From 478958a9857f17d44224a0a72e49e79f23f36e4f Mon Sep 17 00:00:00 2001 From: Mu-Te Joshua Lau <71618875+JoshuaLau0220@users.noreply.github.com> Date: Sun, 17 Mar 2024 19:46:29 +0800 Subject: [PATCH 2/3] :fire: remove legacy gate type interface --- src/convert/qcir_to_tableau.cpp | 110 ++++++-------- src/convert/qcir_to_tensor.cpp | 5 - src/convert/qcir_to_zxgraph.cpp | 5 - src/device/device.cpp | 12 +- src/qcir/gate_type.cpp | 159 -------------------- src/qcir/gate_type.hpp | 51 ------- src/qcir/optimizer/optimizer.hpp | 1 - src/qcir/optimizer/trivial_optimization.cpp | 18 +-- src/qcir/qcir.cpp | 33 ---- src/qcir/qcir.hpp | 1 - 10 files changed, 51 insertions(+), 344 deletions(-) diff --git a/src/convert/qcir_to_tableau.cpp b/src/convert/qcir_to_tableau.cpp index 5f13397b..742506ab 100644 --- a/src/convert/qcir_to_tableau.cpp +++ b/src/convert/qcir_to_tableau.cpp @@ -28,29 +28,6 @@ namespace { #include -Pauli to_pauli(qcir::GateRotationCategory const& category) { - switch (category) { - case qcir::GateRotationCategory::rz: - case qcir::GateRotationCategory::pz: - return Pauli::z; - case qcir::GateRotationCategory::rx: - case qcir::GateRotationCategory::px: - return Pauli::x; - case qcir::GateRotationCategory::ry: - case qcir::GateRotationCategory::py: - return Pauli::y; - default: - DVLAB_UNREACHABLE("Invalid rotation category"); - } -} - -bool is_r_type_rotation(qcir::GateRotationCategory const& category) { - return ( - category == qcir::GateRotationCategory::rz || - category == qcir::GateRotationCategory::rx || - category == qcir::GateRotationCategory::ry); -} - template bool contains(R const& r, T const& value) { return std::ranges::find(r, value) != r.end(); @@ -104,14 +81,25 @@ std::vector get_qubit_idx_vec(QubitIdList const& qubits) { return ret; } -void implement_mcrz(Tableau& tableau, QubitIdList const& qubits, dvlab::Phase const& phase) { +void implement_mcr(Tableau& tableau, QubitIdList const& qubits, dvlab::Phase const& ph, Pauli pauli) { if (std::holds_alternative(tableau.back())) { tableau.push_back(std::vector{}); } + + dvlab::Phase const phase = + ph * + dvlab::Rational(1, static_cast(std::pow(2, gsl::narrow(qubits.size()) - 1))); + + auto const targ = gsl::narrow(qubits.back()); + // convert rotation plane first + if (pauli == Pauli::x) { + tableau.h(targ); + } else if (pauli == Pauli::y) { + tableau.v(targ); + } // guaranteed to be a vector of PauliRotation auto& last_rotation_group = std::get>(tableau.back()); - auto const targ = gsl::narrow(qubits.back()); for (auto const comb_size : std::views::iota(0ul, qubits.size())) { bool const is_neg = comb_size % 2; auto qubit_idx_vec = get_qubit_idx_vec(qubits); @@ -126,12 +114,30 @@ void implement_mcrz(Tableau& tableau, QubitIdList const& qubits, dvlab::Phase co last_rotation_group.push_back(PauliRotation(pauli_range.begin(), pauli_range.end(), is_neg ? -phase : phase)); } while (next_combination(qubit_idx_vec, comb_size)); } + // restore rotation plane + if (pauli == Pauli::x) { + tableau.h(targ); + } else if (pauli == Pauli::y) { + tableau.vdg(targ); + } } -void implement_mcpz(Tableau& tableau, QubitIdList const& qubits, dvlab::Phase const& phase) { +void implement_mcp(Tableau& tableau, QubitIdList const& qubits, dvlab::Phase const& ph, Pauli pauli) { if (std::holds_alternative(tableau.back())) { tableau.push_back(std::vector{}); } + + dvlab::Phase const phase = + ph * + dvlab::Rational(1, static_cast(std::pow(2, gsl::narrow(qubits.size()) - 1))); + + auto const targ = gsl::narrow(qubits.back()); + // convert rotation plane first + if (pauli == Pauli::x) { + tableau.h(targ); + } else if (pauli == Pauli::y) { + tableau.v(targ); + } // guaranteed to be a vector of PauliRotation auto& last_rotation_group = std::get>(tableau.back()); @@ -148,29 +154,6 @@ void implement_mcpz(Tableau& tableau, QubitIdList const& qubits, dvlab::Phase co last_rotation_group.push_back(PauliRotation(pauli_range.begin(), pauli_range.end(), is_neg ? -phase : phase)); } while (next_combination(qubit_idx_vec, comb_size)); } -} - -void implement_rotation_gate(Tableau& tableau, qcir::GateRotationCategory category, dvlab::Phase const& ph, QubitIdList const& qubits) { - auto const pauli = to_pauli(category); - - auto const targ = gsl::narrow(qubits.back()); - // convert rotation plane first - if (pauli == Pauli::x) { - tableau.h(targ); - } else if (pauli == Pauli::y) { - tableau.v(targ); - } - - dvlab::Phase const phase = - ph * - dvlab::Rational(1, static_cast(std::pow(2, gsl::narrow(qubits.size()) - 1))); - // implement rotation in Z plane - if (is_r_type_rotation(category)) { - implement_mcrz(tableau, qubits, phase); - } else { - implement_mcpz(tableau, qubits, phase); - } - // restore rotation plane if (pauli == Pauli::x) { tableau.h(targ); @@ -215,7 +198,7 @@ bool append_to_tableau(qcir::PZGate const& op, experimental::Tableau& tableau, Q } else if (op.get_phase() == dvlab::Phase(-1, 2)) { tableau.sdg(qubits[0]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::pz, op.get_phase(), qubits); + experimental::implement_mcp(tableau, qubits, op.get_phase(), experimental::Pauli::z); } return true; @@ -230,7 +213,7 @@ bool append_to_tableau(qcir::PXGate const& op, experimental::Tableau& tableau, Q } else if (op.get_phase() == dvlab::Phase(-1, 2)) { tableau.vdg(qubits[0]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::px, op.get_phase(), qubits); + experimental::implement_mcp(tableau, qubits, op.get_phase(), experimental::Pauli::x); } return true; @@ -249,7 +232,7 @@ bool append_to_tableau(qcir::PYGate const& op, experimental::Tableau& tableau, Q tableau.vdg(qubits[0]); tableau.s(qubits[0]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::py, op.get_phase(), qubits); + experimental::implement_mcp(tableau, qubits, op.get_phase(), experimental::Pauli::y); } return true; @@ -264,7 +247,7 @@ bool append_to_tableau(qcir::RZGate const& op, experimental::Tableau& tableau, Q } else if (op.get_phase() == dvlab::Phase(-1, 2)) { tableau.sdg(qubits[0]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::rz, op.get_phase(), qubits); + experimental::implement_mcr(tableau, qubits, op.get_phase(), experimental::Pauli::z); } return true; } @@ -278,7 +261,7 @@ bool append_to_tableau(qcir::RXGate const& op, experimental::Tableau& tableau, Q } else if (op.get_phase() == dvlab::Phase(-1, 2)) { tableau.vdg(qubits[0]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::rx, op.get_phase(), qubits); + experimental::implement_mcr(tableau, qubits, op.get_phase(), experimental::Pauli::x); } return true; } @@ -296,7 +279,7 @@ bool append_to_tableau(qcir::RYGate const& op, experimental::Tableau& tableau, Q tableau.vdg(qubits[0]); tableau.s(qubits[0]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::ry, op.get_phase(), qubits); + experimental::implement_mcr(tableau, qubits, op.get_phase(), experimental::Pauli::y); } return true; } @@ -307,7 +290,7 @@ bool append_to_tableau(qcir::ControlGate const& op, experimental::Tableau& table if (op.get_num_qubits() == 2 && target_op->get_phase() == dvlab::Phase(1)) { tableau.cx(qubits[0], qubits[1]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::px, target_op->get_phase(), qubits); + experimental::implement_mcp(tableau, qubits, target_op->get_phase(), experimental::Pauli::x); } return true; } @@ -318,7 +301,7 @@ bool append_to_tableau(qcir::ControlGate const& op, experimental::Tableau& table tableau.cx(qubits[0], qubits[1]); tableau.s(qubits[1]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::py, target_op->get_phase(), qubits); + experimental::implement_mcp(tableau, qubits, target_op->get_phase(), experimental::Pauli::y); } return true; } @@ -327,34 +310,29 @@ bool append_to_tableau(qcir::ControlGate const& op, experimental::Tableau& table if (op.get_num_qubits() == 2 && target_op->get_phase() == dvlab::Phase(1)) { tableau.cz(qubits[0], qubits[1]); } else { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::pz, target_op->get_phase(), qubits); + experimental::implement_mcp(tableau, qubits, target_op->get_phase(), experimental::Pauli::z); } return true; } if (auto target_op = op.get_target_operation().get_underlying_if()) { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::rx, target_op->get_phase(), qubits); + experimental::implement_mcr(tableau, qubits, target_op->get_phase(), experimental::Pauli::x); return true; } if (auto target_op = op.get_target_operation().get_underlying_if()) { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::ry, target_op->get_phase(), qubits); + experimental::implement_mcr(tableau, qubits, target_op->get_phase(), experimental::Pauli::y); return true; } if (auto target_op = op.get_target_operation().get_underlying_if()) { - experimental::implement_rotation_gate(tableau, qcir::GateRotationCategory::rz, target_op->get_phase(), qubits); + experimental::implement_mcr(tableau, qubits, target_op->get_phase(), experimental::Pauli::z); return true; } return false; } -template <> -bool append_to_tableau(qcir::LegacyGateType const& /* op */, experimental::Tableau& /* tableau */, QubitIdList const& /* qubits*/) { - return false; -} - namespace experimental { std::optional to_tableau(qcir::QCir const& qcir) { diff --git a/src/convert/qcir_to_tensor.cpp b/src/convert/qcir_to_tensor.cpp index 6d19c17f..bf152278 100644 --- a/src/convert/qcir_to_tensor.cpp +++ b/src/convert/qcir_to_tensor.cpp @@ -94,11 +94,6 @@ std::optional> to_tensor(ControlGate const& op) { } } -template <> -std::optional> to_tensor(LegacyGateType const& /* op */) { - return std::nullopt; -} - /** * @brief Convert gate to tensor * diff --git a/src/convert/qcir_to_zxgraph.cpp b/src/convert/qcir_to_zxgraph.cpp index 347be089..5e201d92 100644 --- a/src/convert/qcir_to_zxgraph.cpp +++ b/src/convert/qcir_to_zxgraph.cpp @@ -412,11 +412,6 @@ std::optional to_zxgraph(qcir::ControlGate const& op) { return std::nullopt; } -template <> -std::optional to_zxgraph(qcir::LegacyGateType const& /* op */) { - return std::nullopt; -} - std::optional to_zxgraph(qcir::QCirGate const& gate) { auto ret = to_zxgraph(gate.get_operation()); diff --git a/src/device/device.cpp b/src/device/device.cpp index 555054ba..78dfcdc2 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -515,13 +515,11 @@ bool Device::_parse_gate_set(std::string const& gate_set_str) { _topology->add_gate_type(op->get_repr().substr(0, op->get_repr().find_first_of('('))); return std::make_optional(op->get_type()); } - auto gate_type = str_to_gate_type(str); - if (!gate_type.has_value()) { - spdlog::error("unsupported gate type \"{}\"!!", str); - return std::nullopt; - }; - _topology->add_gate_type(gate_type_to_str(*gate_type)); - return std::make_optional(gate_type_to_str(*gate_type)); + if (auto op = qcir::str_to_operation(str, {dvlab::Phase()}); op.has_value()) { + _topology->add_gate_type(op->get_repr().substr(0, op->get_repr().find_first_of('('))); + return std::make_optional(op->get_type()); + } + return std::nullopt; }); return std::ranges::all_of(gate_set_view, [](auto const& gate_type) { return gate_type.has_value(); }); diff --git a/src/qcir/gate_type.cpp b/src/qcir/gate_type.cpp index 91ff2d13..8f3f8871 100644 --- a/src/qcir/gate_type.cpp +++ b/src/qcir/gate_type.cpp @@ -9,7 +9,6 @@ #include "qcir/qcir_translate.hpp" #include "util/dvlab_string.hpp" -#include "util/util.hpp" namespace qsyn::qcir { @@ -70,164 +69,6 @@ std::optional str_to_operation(std::string str, std::vector str_to_gate_type(std::string_view str) { - std::optional num_qubits = 1; - if (str.starts_with("mc")) { - num_qubits = std::nullopt; - str.remove_prefix(2); - } else { - while (str.starts_with("c")) { - (*num_qubits)++; - str.remove_prefix(1); - } - } - // single-qubit Z-rotation gates - if (str == "pz" || str == "p") - return GateType{GateRotationCategory::pz, num_qubits, std::nullopt}; - if (str == "rz") - return GateType{GateRotationCategory::rz, num_qubits, std::nullopt}; - if (str == "z") - return GateType{GateRotationCategory::pz, num_qubits, dvlab::Phase(1)}; - if (str == "s") - return GateType{GateRotationCategory::pz, num_qubits, dvlab::Phase(1, 2)}; - if (str == "sdg") - return GateType{GateRotationCategory::pz, num_qubits, dvlab::Phase(-1, 2)}; - if (str == "t") - return GateType{GateRotationCategory::pz, num_qubits, dvlab::Phase(1, 4)}; - if (str == "tdg") - return GateType{GateRotationCategory::pz, num_qubits, dvlab::Phase(-1, 4)}; - - // single-qubit X-rotation gates - if (str == "px") - return GateType{GateRotationCategory::px, num_qubits, std::nullopt}; - - if (str == "rx") - return GateType{GateRotationCategory::rx, num_qubits, std::nullopt}; - - if (str == "x" || str == "not") - return GateType{GateRotationCategory::px, num_qubits, dvlab::Phase(1)}; - - if (str == "sx" || str == "x_1_2") - return GateType{GateRotationCategory::px, num_qubits, dvlab::Phase(1, 2)}; - - if (str == "sxdg") - return GateType{GateRotationCategory::px, num_qubits, dvlab::Phase(-1, 2)}; - - // single-qubit Y-rotation gates - if (str == "py") - return GateType{GateRotationCategory::py, num_qubits, std::nullopt}; - if (str == "ry") - return GateType{GateRotationCategory::ry, num_qubits, std::nullopt}; - if (str == "y") - return GateType{GateRotationCategory::py, num_qubits, dvlab::Phase(1)}; - if (str == "sy" || str == "y_1_2") - return GateType{GateRotationCategory::py, num_qubits, dvlab::Phase(1, 2)}; - if (str == "sydg") - return GateType{GateRotationCategory::py, num_qubits, dvlab::Phase(-1, 2)}; - - return std::nullopt; -} - -std::string gate_type_to_str(GateRotationCategory category, std::optional num_qubits, std::optional phase) { - std::string type_str = std::invoke([num_qubits]() { - if (!num_qubits.has_value()) { - return std::string{"mc"}; - } else - return std::string(num_qubits.value() - 1, 'c'); - }); - - type_str += std::invoke([phase, category]() { - switch (category) { - using dvlab::Phase; - case GateRotationCategory::pz: { - if (phase == Phase(0)) { - return "id"; - } - if (phase == Phase(1)) { - return "z"; - } - if (phase == Phase(1, 2)) { - return "s"; - } - if (phase == Phase(-1, 2)) { - return "sdg"; - } - if (phase == Phase(1, 4)) { - return "t"; - } - if (phase == Phase(-1, 4)) { - return "tdg"; - } - return "p"; - } - case GateRotationCategory::px: { - if (phase == Phase(0)) { - return "id"; - } - if (phase == Phase(1)) { - return "x"; - } - if (phase == Phase(1, 2)) { - return "sx"; - } - if (phase == Phase(-1, 2)) { - return "sxdg"; - } - if (phase == Phase(1, 4)) { - return "tx"; - } - if (phase == Phase(-1, 4)) { - return "txdg"; - } - return "px"; - } - case GateRotationCategory::py: { - if (phase == Phase(0)) { - return "id"; - } - if (phase == Phase(1)) { - return "y"; - } - if (phase == Phase(1, 2)) { - return "sy"; - } - if (phase == Phase(-1, 2)) { - return "sydg"; - } - if (phase == Phase(1, 4)) { - return "ty"; - } - if (phase == Phase(-1, 4)) { - return "tydg"; - } - return "py"; - } - case GateRotationCategory::rz: - return "rz"; - case GateRotationCategory::rx: - return "rx"; - case GateRotationCategory::ry: - return "ry"; - default: - DVLAB_UNREACHABLE("Should be unreachable!!"); - } - }); - - return type_str; -} - -std::string gate_type_to_str(GateType const& type) { - return gate_type_to_str(std::get<0>(type), std::get<1>(type), std::get<2>(type)); -} - dvlab::utils::ordered_hashmap EQUIVALENCE_LIBRARY = { {"sherbrooke", {{"h", { {"s", {0}, dvlab::Phase(0)}, diff --git a/src/qcir/gate_type.hpp b/src/qcir/gate_type.hpp index 56e723c0..84038234 100644 --- a/src/qcir/gate_type.hpp +++ b/src/qcir/gate_type.hpp @@ -33,21 +33,6 @@ bool append_to_tableau(T const& /* op */, experimental::Tableau& /* tableau */, namespace qsyn::qcir { -enum class GateRotationCategory { - pz, - rz, - px, - rx, - py, - ry, -}; - -using GateType = std::tuple, std::optional>; - -std::optional str_to_gate_type(std::string_view str); -std::string gate_type_to_str(GateRotationCategory category, std::optional num_qubits = std::nullopt, std::optional phase = std::nullopt); -std::string gate_type_to_str(GateType const& type); - namespace detail { class DummyOperationType { @@ -169,26 +154,6 @@ class Operation { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-s std::unique_ptr _pimpl; }; -class LegacyGateType { -public: - LegacyGateType(GateType type) : _type(type) {} - std::string get_type() const { return gate_type_to_str(_type); } - std::string get_repr() const { - return get_type() + ((get_type().find_first_of("pr") != std::string::npos) ? fmt::format("({})", std::get<2>(_type)->get_print_string()) : ""); - } - size_t get_num_qubits() const { return std::get<1>(_type).value_or(0); } - - GateRotationCategory get_rotation_category() const { return std::get<0>(_type); } - dvlab::Phase get_phase() const { return std::get<2>(_type).value_or(dvlab::Phase(0, 1)); } - void set_phase(dvlab::Phase phase) { std::get<2>(_type) = phase; } - -private: - GateType _type; -}; - -Operation adjoint(LegacyGateType const& op); -bool is_clifford(LegacyGateType const& op); - class IdGate { public: IdGate() {} @@ -438,12 +403,6 @@ inline ControlGate CCZGate() { return ControlGate(ZGate(), 2); } } // namespace qsyn::qcir namespace qsyn { -template <> -std::optional to_zxgraph(qcir::LegacyGateType const& op); -template <> -std::optional> to_tensor(qcir::LegacyGateType const& op); -template <> -bool append_to_tableau(qcir::LegacyGateType const& op, experimental::Tableau& tableau, QubitIdList const& qubits); template <> std::optional to_zxgraph(qcir::IdGate const& op); @@ -522,13 +481,3 @@ std::optional> to_tensor(qcir::ControlGate const& op); template <> bool append_to_tableau(qcir::ControlGate const& op, experimental::Tableau& tableau, QubitIdList const& qubits); } // namespace qsyn - -template <> -struct fmt::formatter { - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } - - template - auto format(qsyn::qcir::GateRotationCategory const& type, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "{}", qsyn::qcir::gate_type_to_str(type)); - } -}; diff --git a/src/qcir/optimizer/optimizer.hpp b/src/qcir/optimizer/optimizer.hpp index 1ff7af80..dbd68884 100644 --- a/src/qcir/optimizer/optimizer.hpp +++ b/src/qcir/optimizer/optimizer.hpp @@ -24,7 +24,6 @@ class Phase; namespace qsyn::qcir { class QCir; -enum class GateRotationCategory; using Qubit2Gates = std::unordered_map>; class Optimizer { diff --git a/src/qcir/optimizer/trivial_optimization.cpp b/src/qcir/optimizer/trivial_optimization.cpp index a22588ef..64be7210 100644 --- a/src/qcir/optimizer/trivial_optimization.cpp +++ b/src/qcir/optimizer/trivial_optimization.cpp @@ -118,9 +118,6 @@ void Optimizer::_fuse_x_phase(QCir& qcir, QCirGate* prev_gate, QCirGate* gate) { if (gate.get_operation().is()) { return gate.get_operation().get_underlying().get_phase(); } - if (gate.get_operation().is()) { - return gate.get_operation().get_underlying().get_phase(); - } throw std::runtime_error("Invalid gate type"); }; @@ -149,9 +146,6 @@ void Optimizer::_fuse_z_phase(QCir& qcir, QCirGate* prev_gate, QCirGate* gate) { if (gate.get_operation().is()) { return gate.get_operation().get_underlying().get_phase(); } - if (gate.get_operation().is()) { - return gate.get_operation().get_underlying().get_phase(); - } throw std::runtime_error("Invalid gate type"); }; @@ -270,16 +264,8 @@ std::vector zx_optimize(std::vector const& partial) { QCir qcir(1); for (std::string const& type : partial) { - auto gate_type = str_to_gate_type(type); - auto const& [category, num_qubits, gate_phase] = gate_type.value(); - if (gate_phase.has_value()) { - if (auto op = str_to_operation(type, {*gate_phase}); op.has_value()) { - qcir.append(op.value(), {0}); - } - } else { - if (auto op = str_to_operation(type); op.has_value()) { - qcir.append(op.value(), {0}); - } + if (auto const op = str_to_operation(type); op.has_value()) { + qcir.append(op.value(), {0}); } } diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index 436d4584..1e1edd5a 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -25,7 +25,6 @@ #include "./qcir_qubit.hpp" #include "./qcir_translate.hpp" #include "qsyn/qsyn_type.hpp" -#include "util/phase.hpp" #include "util/scope_guard.hpp" #include "util/text_format.hpp" #include "util/util.hpp" @@ -284,38 +283,6 @@ bool QCir::remove_qubit(QubitIdType id) { } } -/** - * @brief Add Gate - * - * @param type - * @param bits - * @param phase - * @param append if true, append the gate, else prepend - * - * @return QCirGate* - */ -QCirGate* QCir::add_gate(std::string type, QubitIdList const& bits, dvlab::Phase phase, bool append) { - type = dvlab::str::tolower_string(type); - auto gate_type = str_to_gate_type(type); - if (!gate_type.has_value()) { - spdlog::error("Gate type {} is not supported!!", type); - return nullptr; - } - auto const& [category, num_qubits, gate_phase] = *gate_type; - if (num_qubits.has_value() && num_qubits.value() != bits.size()) { - spdlog::error("Gate {} requires {} qubits, but {} qubits are given.", type, num_qubits.value(), bits.size()); - return nullptr; - } - if (gate_phase.has_value()) { - phase = gate_phase.value(); - } - - return get_gate( - append - ? this->append(LegacyGateType(std::make_tuple(category, bits.size(), phase)), bits) - : this->prepend(LegacyGateType(std::make_tuple(category, bits.size(), phase)), bits)); -} - size_t QCir::append(Operation const& op, QubitIdList const& bits) { DVLAB_ASSERT( op.get_num_qubits() == bits.size(), diff --git a/src/qcir/qcir.hpp b/src/qcir/qcir.hpp index 202fedb1..faacfd67 100644 --- a/src/qcir/qcir.hpp +++ b/src/qcir/qcir.hpp @@ -131,7 +131,6 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia QCirQubit* insert_qubit(QubitIdType id); void add_qubits(size_t num); bool remove_qubit(QubitIdType qid); - QCirGate* add_gate(std::string type, QubitIdList const& bits, dvlab::Phase phase, bool append); size_t append(Operation const& op, QubitIdList const& bits); size_t prepend(Operation const& op, QubitIdList const& bits); bool remove_gate(size_t id); From 7cb746883e6ad26ff4134a0d558afcb801024e55 Mon Sep 17 00:00:00 2001 From: Mu-Te Joshua Lau <71618875+JoshuaLau0220@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:16:46 +0800 Subject: [PATCH 3/3] :art: lint --- src/duostra/scheduler_search.cpp | 3 --- src/qcir/qcir_cmd.cpp | 10 +++++----- src/tableau/pauli_rotation.cpp | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/duostra/scheduler_search.cpp b/src/duostra/scheduler_search.cpp index 7a2a6717..03192bc1 100644 --- a/src/duostra/scheduler_search.cpp +++ b/src/duostra/scheduler_search.cpp @@ -9,12 +9,9 @@ #include #include -#include #include -#include #include -#include "./duostra.hpp" #include "./scheduler.hpp" #include "util/util.hpp" diff --git a/src/qcir/qcir_cmd.cpp b/src/qcir/qcir_cmd.cpp index dbd75fa4..0d869485 100644 --- a/src/qcir/qcir_cmd.cpp +++ b/src/qcir/qcir_cmd.cpp @@ -339,7 +339,7 @@ dvlab::Command qcir_print_cmd(QCirMgr const& qcir_mgr) { } dvlab::Command qcir_gate_add_cmd(QCirMgr& qcir_mgr) { - static dvlab::utils::ordered_hashmap single_qubit_gates_no_phase = { + static dvlab::utils::ordered_hashmap const single_qubit_gates_no_phase = { {"h", "Hadamard gate"}, {"x", "Pauli-X gate"}, {"y", "Pauli-Y gate"}, @@ -351,7 +351,7 @@ dvlab::Command qcir_gate_add_cmd(QCirMgr& qcir_mgr) { {"sx", "√X gate"}, {"sy", "√Y gate"}}; - static dvlab::utils::ordered_hashmap single_qubit_gates_with_phase = { + static dvlab::utils::ordered_hashmap const single_qubit_gates_with_phase = { {"rz", "Rz(θ) gate"}, {"ry", "Rx(θ) gate"}, {"rx", "Ry(θ) gate"}, @@ -361,18 +361,18 @@ dvlab::Command qcir_gate_add_cmd(QCirMgr& qcir_mgr) { {"py", "Py = (e^iθ/2)Ry gate"} // }; - static dvlab::utils::ordered_hashmap double_qubit_gates_no_phase = { + static dvlab::utils::ordered_hashmap const double_qubit_gates_no_phase = { {"cx", "CX (CNOT) gate"}, {"cz", "CZ gate"}, {"swap", "SWAP gate"}, {"ecr", "Echoed crossed resonance gate"}}; - static dvlab::utils::ordered_hashmap three_qubit_gates_no_phase = { + static dvlab::utils::ordered_hashmap const three_qubit_gates_no_phase = { {"ccx", "CCX (CCNOT, Toffoli) gate"}, {"ccz", "CCZ gate"} // }; - static dvlab::utils::ordered_hashmap multi_qubit_gates_with_phase = { + static dvlab::utils::ordered_hashmap const multi_qubit_gates_with_phase = { {"mcrz", "Multi-Controlled Rz(θ) gate"}, {"mcrx", "Multi-Controlled Rx(θ) gate"}, {"mcry", "Multi-Controlled Ry(θ) gate"}, diff --git a/src/tableau/pauli_rotation.cpp b/src/tableau/pauli_rotation.cpp index 89632717..1f6c8ac4 100644 --- a/src/tableau/pauli_rotation.cpp +++ b/src/tableau/pauli_rotation.cpp @@ -82,7 +82,7 @@ PauliProduct::PauliProduct(std::initializer_list const& pauli_list, bool } for (size_t i = 0; i < pauli_list.size(); ++i) { - switch (pauli_list.begin()[i]) { + switch (pauli_list.begin()[i]) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) case Pauli::i: break; case Pauli::z: