From 413f7d614b8ffd28d3c226afb69f439e14f890a9 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 11 Dec 2023 03:38:19 +0800 Subject: [PATCH 01/20] :construction: testing clang-tidy-17 checks --- src/argparse/arg_def.hpp | 4 ++-- src/argparse/arg_group.hpp | 6 +++--- src/util/data_structure_manager.hpp | 6 +++--- src/util/ordered_hashmap.hpp | 10 +++++----- src/util/ordered_hashtable.hpp | 4 ++-- src/util/trie.hpp | 6 +++--- src/zx/gflow/gflow.hpp | 8 ++++---- src/zx/zx_file_parser.hpp | 4 ++-- src/zx/zxgraph.cpp | 4 ++-- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/argparse/arg_def.hpp b/src/argparse/arg_def.hpp index 44126997d..c02f06814 100644 --- a/src/argparse/arg_def.hpp +++ b/src/argparse/arg_def.hpp @@ -25,9 +25,9 @@ struct heterogeneous_string_hash { struct Token { Token(std::string_view tok) - : token{tok}, parsed{false} {} + : token{tok} {} std::string token; - bool parsed; + bool parsed = false; }; using TokensSpan = std::span; diff --git a/src/argparse/arg_group.hpp b/src/argparse/arg_group.hpp index 6da697ebe..81f62f802 100644 --- a/src/argparse/arg_group.hpp +++ b/src/argparse/arg_group.hpp @@ -27,11 +27,11 @@ class ArgumentParser; class MutuallyExclusiveGroup { struct MutExGroupImpl { MutExGroupImpl(ArgumentParser& parser) - : _parser{&parser}, _required{false}, _parsed{false} {} + : _parser{&parser} {} ArgumentParser* _parser; dvlab::utils::ordered_hashset> _arguments; - bool _required; - bool _parsed; + bool _required = false; + bool _parsed = false; }; public: diff --git a/src/util/data_structure_manager.hpp b/src/util/data_structure_manager.hpp index 709ab1e44..1edb07a7b 100644 --- a/src/util/data_structure_manager.hpp +++ b/src/util/data_structure_manager.hpp @@ -34,7 +34,7 @@ template requires manager_manageable class DataStructureManager { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-special-member-functions) : copy-swap idiom public: - DataStructureManager(std::string_view name) : _next_id{0}, _focused_id{0}, _type_name{name} {} + DataStructureManager(std::string_view name) : _type_name{name} {} virtual ~DataStructureManager() = default; DataStructureManager(DataStructureManager const& other) : _next_id{other._next_id}, _focused_id{other._focused_id} { @@ -186,8 +186,8 @@ class DataStructureManager { // NOLINT(hicpp-special-member-functions, cppcoreg std::string get_type_name() const { return _type_name; } private: - size_t _next_id; - size_t _focused_id; + size_t _next_id = 0; + size_t _focused_id = 0; ordered_hashmap> _list; std::string _type_name; diff --git a/src/util/ordered_hashmap.hpp b/src/util/ordered_hashmap.hpp index d3e9fb7ee..d734b2974 100644 --- a/src/util/ordered_hashmap.hpp +++ b/src/util/ordered_hashmap.hpp @@ -157,17 +157,17 @@ class ordered_hashmap final : public ordered_hashtable insert_or_assign(Key&& key, T&& obj) { - auto ret = try_emplace(std::move(key), std::forward(obj)); + auto ret = try_emplace(std::move(key), std::move(obj)); if (ret.second == false) { - ret.first->second = std::forward(obj); + ret.first->second = std::move(obj); } return ret; } std::pair insert_or_assign(Key const& key, T&& obj) { - auto ret = try_emplace(key, std::forward(obj)); + auto ret = try_emplace(key, std::move(obj)); if (ret.second == false) { - ret.first->second = std::forward(obj); + ret.first->second = std::move(obj); } return ret; } @@ -230,7 +230,7 @@ T& ordered_hashmap::operator[](Key const& key) { */ template T& ordered_hashmap::operator[](Key&& key) { - if (!this->contains(key)) this->emplace(key, T()); + if (!this->contains(key)) this->emplace(std::move(key), T()); return at(key); } diff --git a/src/util/ordered_hashtable.hpp b/src/util/ordered_hashtable.hpp index 4d374200b..911a04443 100644 --- a/src/util/ordered_hashtable.hpp +++ b/src/util/ordered_hashtable.hpp @@ -104,7 +104,7 @@ class ordered_hashtable { // NOLINT(readability-identifier-naming) : ordered_ha VecIterType _end; }; - ordered_hashtable() : _size(0) {} + ordered_hashtable() {} virtual ~ordered_hashtable() = default; ordered_hashtable(ordered_hashtable const& other) = default; @@ -203,7 +203,7 @@ class ordered_hashtable { // NOLINT(readability-identifier-naming) : ordered_ha protected: std::unordered_map _key2id; container _data; - size_t _size; + size_t _size = 0; }; //------------------------------------------------------ diff --git a/src/util/trie.hpp b/src/util/trie.hpp index 21dabf48f..73c3e9276 100644 --- a/src/util/trie.hpp +++ b/src/util/trie.hpp @@ -20,7 +20,7 @@ namespace utils { struct TrieNode { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-special-member-functions) : copy-swap idiom public: - TrieNode() : is_word(false), frequency(0) {} + TrieNode() {} ~TrieNode() = default; TrieNode(TrieNode const& other) : is_word{other.is_word}, frequency{other.frequency} { @@ -47,8 +47,8 @@ struct TrieNode { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-s } std::unordered_map> children; - bool is_word; - size_t frequency; + bool is_word = false; + size_t frequency = 0; }; template diff --git a/src/zx/gflow/gflow.hpp b/src/zx/gflow/gflow.hpp index fe2b7fadc..4d1b60ef8 100644 --- a/src/zx/gflow/gflow.hpp +++ b/src/zx/gflow/gflow.hpp @@ -37,7 +37,7 @@ class GFlow { }; using MeasurementPlaneMap = std::unordered_map; - GFlow(ZXGraph* g) : _zxgraph{g}, _valid{false}, _do_independent_layers{false}, _do_extended{true} {} + GFlow(ZXGraph* g) : _zxgraph{g} {} bool calculate(); @@ -76,9 +76,9 @@ class GFlow { std::unordered_map _measurement_planes; std::unordered_map _vertex2levels; - bool _valid; - bool _do_independent_layers; - bool _do_extended; + bool _valid = false; + bool _do_independent_layers = false; + bool _do_extended = false; // helper members ZXVertexList _frontier; diff --git a/src/zx/zx_file_parser.hpp b/src/zx/zx_file_parser.hpp index eb795a052..89f028de1 100644 --- a/src/zx/zx_file_parser.hpp +++ b/src/zx/zx_file_parser.hpp @@ -23,13 +23,13 @@ class ZXFileParser { using VertexInfo = detail::VertexInfo; using StorageType = detail::StorageType; - ZXFileParser() : _line_no(1) {} + ZXFileParser() {} bool parse(std::filesystem::path const& filename); StorageType get_storage() const { return _storage; } private: - unsigned _line_no; + unsigned _line_no = 1; StorageType _storage; std::unordered_set _taken_input_qubits; std::unordered_set _taken_output_qubits; diff --git a/src/zx/zxgraph.cpp b/src/zx/zxgraph.cpp index bbf5afa9c..2d79f52dd 100644 --- a/src/zx/zxgraph.cpp +++ b/src/zx/zxgraph.cpp @@ -33,7 +33,7 @@ namespace qsyn::zx { */ ZXGraph::ZXGraph(ZXVertexList const& vertices, ZXVertexList const& inputs, - ZXVertexList const& outputs) : _next_v_id{0}, _inputs{inputs}, _outputs{outputs}, _vertices{vertices} { + ZXVertexList const& outputs) : _inputs{inputs}, _outputs{outputs}, _vertices{vertices} { for (auto v : _vertices) { v->set_id(_next_v_id); _next_v_id++; @@ -62,7 +62,7 @@ ZXGraph::ZXGraph(ZXGraph const& other) : _filename{other._filename}, _procedures } } - other.for_each_edge([&old_v2new_v_map, this](EdgePair&& epair) { + other.for_each_edge([&old_v2new_v_map, this](EdgePair const& epair) { this->add_edge(old_v2new_v_map[epair.first.first], old_v2new_v_map[epair.first.second], epair.second); }); } From 9344a34878db3de257f70650e6e56c4ed2230954 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 11 Dec 2023 13:55:23 +0800 Subject: [PATCH 02/20] :art: fix linting in clang-tidy-17 --- src/argparse/arg_parser_print.cpp | 2 +- src/device/device.hpp | 16 ++++++++-------- src/duostra/circuit_topology.cpp | 2 +- src/duostra/circuit_topology.hpp | 10 +++++----- src/duostra/router.cpp | 4 ++-- src/duostra/router.hpp | 2 +- src/duostra/scheduler.hpp | 22 +++++++++++----------- src/duostra/scheduler_search.cpp | 5 +++-- src/qcir/qcir.hpp | 19 +++++++++---------- src/qcir/qcir_gate.hpp | 6 +++--- src/tensor/qtensor.hpp | 9 +++------ src/tensor/tensor.hpp | 4 ++-- src/util/ordered_hashmap.hpp | 4 ++-- src/util/ordered_hashtable.hpp | 6 +++--- 14 files changed, 54 insertions(+), 57 deletions(-) diff --git a/src/argparse/arg_parser_print.cpp b/src/argparse/arg_parser_print.cpp index e60711982..36825a9b3 100644 --- a/src/argparse/arg_parser_print.cpp +++ b/src/argparse/arg_parser_print.cpp @@ -280,7 +280,7 @@ void ArgumentParser::print_help() const { fmt::println(" {}", get_description()); } - auto get_max_length = [this](std::function&& fn) { + auto get_max_length = [this](std::function const fn) { return _pimpl->arguments.empty() ? 0 : std::ranges::max(_pimpl->arguments | std::views::values | std::views::transform(fn)); }; diff --git a/src/device/device.hpp b/src/device/device.hpp index 7ad09dcf3..4c19c7ad2 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -66,11 +66,11 @@ class Topology { void print_single_edge(size_t a, size_t b) const; private: - std::string _name; - size_t _num_qubit = 0; - std::vector _gate_set; - PhysicalQubitInfo _qubit_info; - AdjacencyMap _adjacency_info; + std::string _name = ""; + size_t _num_qubit = 0; + std::vector _gate_set = {}; + PhysicalQubitInfo _qubit_info = {}; + AdjacencyMap _adjacency_info = {}; }; class PhysicalQubit { @@ -105,8 +105,8 @@ class PhysicalQubit { private: // NOTE - Device information - QubitIdType _id = max_qubit_id; - Adjacencies _adjacencies; + QubitIdType _id = max_qubit_id; + Adjacencies _adjacencies = {}; // NOTE - Duostra parameter std::optional _logical_qubit = std::nullopt; @@ -163,7 +163,7 @@ class Device { private: size_t _num_qubit = 0; std::shared_ptr _topology; - PhysicalQubitList _qubit_list; + PhysicalQubitList _qubit_list = {}; // NOTE - Internal functions only used in reader bool _parse_gate_set(std::string const& gate_set_str); diff --git a/src/duostra/circuit_topology.cpp b/src/duostra/circuit_topology.cpp index b86031450..a7d868619 100644 --- a/src/duostra/circuit_topology.cpp +++ b/src/duostra/circuit_topology.cpp @@ -27,7 +27,7 @@ namespace qsyn::duostra { * @param qs */ Gate::Gate(size_t id, GateRotationCategory type, dvlab::Phase ph, std::tuple qs) - : _id(id), _type(type), _phase(ph), _swap(false), _qubits(qs), _prevs({}), _nexts({}) { + : _id(id), _type(type), _phase(ph), _qubits(qs) { if (std::get<0>(_qubits) > std::get<1>(_qubits)) { _qubits = std::make_tuple(std::get<1>(_qubits), std::get<0>(_qubits)); _swap = true; diff --git a/src/duostra/circuit_topology.hpp b/src/duostra/circuit_topology.hpp index fb9123d04..ec79b4e78 100644 --- a/src/duostra/circuit_topology.hpp +++ b/src/duostra/circuit_topology.hpp @@ -59,15 +59,15 @@ class Gate { size_t _id; qcir::GateRotationCategory _type; dvlab::Phase _phase; // For saving phase information - bool _swap; // qubits is swapped for duostra + bool _swap = false; // qubits is swapped for duostra std::tuple _qubits; - std::vector _prevs; - std::vector _nexts; + std::vector _prevs = {}; + std::vector _nexts = {}; }; class DependencyGraph { public: - DependencyGraph(size_t n, std::vector&& gates) : _num_qubits(n), _gates(std::move(gates)) {} + DependencyGraph(size_t n, std::vector gates) : _num_qubits(n), _gates(std::move(gates)) {} ~DependencyGraph() = default; DependencyGraph(DependencyGraph const& other) = delete; DependencyGraph& operator=(DependencyGraph const& other) = delete; @@ -83,7 +83,7 @@ class DependencyGraph { private: size_t _num_qubits; - std::vector _gates; + std::vector _gates = {}; }; class CircuitTopology { diff --git a/src/duostra/router.cpp b/src/duostra/router.cpp index 9fe12aa1b..4aa0cba02 100644 --- a/src/duostra/router.cpp +++ b/src/duostra/router.cpp @@ -44,9 +44,9 @@ AStarNode::AStarNode(size_t cost, QubitIdType id, bool source) * @param cost * @param orient */ -Router::Router(Device&& device, Router::CostStrategyType cost_strategy, MinMaxOptionType tie_breaking_strategy) +Router::Router(Device device, Router::CostStrategyType cost_strategy, MinMaxOptionType tie_breaking_strategy) : _tie_breaking_strategy(tie_breaking_strategy), - _device(device), + _device(std::move(device)), _logical_to_physical({}), _apsp(DuostraConfig::ROUTER_TYPE == RouterType::shortest_path || cost_strategy == CostStrategyType::end), _duostra(DuostraConfig::ROUTER_TYPE == RouterType::duostra), diff --git a/src/duostra/router.hpp b/src/duostra/router.hpp index 0c4ad2318..d82cf9029 100644 --- a/src/duostra/router.hpp +++ b/src/duostra/router.hpp @@ -51,7 +51,7 @@ class Router { end }; using PriorityQueue = std::priority_queue, AStarComp>; - Router(Device&& device, CostStrategyType cost_strategy, MinMaxOptionType tie_breaking_strategy); + Router(Device device, CostStrategyType cost_strategy, MinMaxOptionType tie_breaking_strategy); std::unique_ptr clone() const; diff --git a/src/duostra/scheduler.hpp b/src/duostra/scheduler.hpp index b745e50bd..0880ce577 100644 --- a/src/duostra/scheduler.hpp +++ b/src/duostra/scheduler.hpp @@ -60,10 +60,10 @@ class BaseScheduler { protected: CircuitTopology _circuit_topology; - std::vector _operations; - std::vector _assign_order; - bool _sorted = false; - bool _tqdm = true; + std::vector _operations = {}; + std::vector _assign_order = {}; + bool _sorted = false; + bool _tqdm = true; virtual Device _assign_gates(std::unique_ptr); void _sort(); }; @@ -146,7 +146,7 @@ class TreeNode { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-sp public: TreeNode(TreeNodeConf, size_t, std::unique_ptr, std::unique_ptr, size_t); - TreeNode(TreeNodeConf, std::vector&&, std::unique_ptr, std::unique_ptr, size_t); + TreeNode(TreeNodeConf, std::vector, std::unique_ptr, std::unique_ptr, size_t); ~TreeNode() = default; @@ -186,19 +186,19 @@ class TreeNode { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-sp bool done() const { return scheduler().get_available_gates().empty(); } private: - TreeNodeConf _conf; + TreeNodeConf _conf = {}; // The head of the node. - std::vector _gate_ids; + std::vector _gate_ids = {}; // Using vector to pointer so that frequent cache misses // won't be as bad in parallel code. - std::vector _children; + std::vector _children = {}; // The state of duostra. - size_t _max_cost; - std::unique_ptr _router; - std::unique_ptr _scheduler; + size_t _max_cost = {}; + std::unique_ptr _router = {}; + std::unique_ptr _scheduler = {}; void _grow(); std::optional _immediate_next() const; diff --git a/src/duostra/scheduler_search.cpp b/src/duostra/scheduler_search.cpp index ecd0cdc76..489380804 100644 --- a/src/duostra/scheduler_search.cpp +++ b/src/duostra/scheduler_search.cpp @@ -54,7 +54,7 @@ TreeNode::TreeNode(TreeNodeConf conf, * @param maxCost */ TreeNode::TreeNode(TreeNodeConf conf, - std::vector&& gate_ids, + std::vector gate_ids, std::unique_ptr router, std::unique_ptr scheduler, size_t max_cost) @@ -142,7 +142,8 @@ void TreeNode::_route_internal_gates() { TreeNode TreeNode::best_child(size_t depth) { if (is_leaf()) _grow(); // NOTE - best_cost(depth) is computationally expensive, so we don't use std::min_element here to avoid calling it twice. - size_t best_idx = 0, best_cost = SIZE_MAX; + size_t best_idx = 0; + size_t best_cost = SIZE_MAX; for (auto&& [idx, node] : tl::views::enumerate(_children)) { assert(depth >= 1); auto cost = node.best_cost(depth); diff --git a/src/qcir/qcir.hpp b/src/qcir/qcir.hpp index 4e8a52759..0a2057814 100644 --- a/src/qcir/qcir.hpp +++ b/src/qcir/qcir.hpp @@ -189,16 +189,15 @@ class QCir { // NOLINT(hicpp-special-member-functions, cppcoreguidelines-specia void _set_next_gate_id(size_t id) { _gate_id = id; } void _set_next_qubit_id(QubitIdType qid) { _qubit_id = qid; } - size_t _gate_id = 0; - QubitIdType _qubit_id = 0; - bool mutable _dirty = true; - unsigned mutable _global_dfs_counter = 0; - std::string _filename; - std::vector _procedures; - - std::vector _qgates; - std::vector _qubits; - std::vector mutable _topological_order; + size_t _gate_id = 0; + QubitIdType _qubit_id = 0; + bool mutable _dirty = true; + unsigned mutable _global_dfs_counter = 0; + std::string _filename = ""; + std::vector _procedures = {}; + std::vector _qgates = {}; + std::vector _qubits = {}; + std::vector mutable _topological_order = {}; }; std::string to_qasm(QCir const& qcir); diff --git a/src/qcir/qcir_gate.hpp b/src/qcir/qcir_gate.hpp index 3871ac2a7..86371acdf 100644 --- a/src/qcir/qcir_gate.hpp +++ b/src/qcir/qcir_gate.hpp @@ -104,9 +104,9 @@ class QCirGate { protected: size_t _id; GateRotationCategory _rotation_category; - size_t _time = 0; - unsigned _dfs_counter = 0; - std::vector _qubits; + 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; diff --git a/src/tensor/qtensor.hpp b/src/tensor/qtensor.hpp index f75d2a913..d27f870e7 100644 --- a/src/tensor/qtensor.hpp +++ b/src/tensor/qtensor.hpp @@ -26,7 +26,7 @@ class QTensor : public Tensor> { public: QTensor() : Tensor(std::complex(1, 0)) {} QTensor(Tensor const& t) : Tensor(t) {} - QTensor(Tensor&& t) : Tensor(t) {} + QTensor(Tensor&& t) : Tensor(std::move(t)) {} QTensor(xt::nested_initializer_list_t il) : Tensor(il) {} QTensor(xt::nested_initializer_list_t il) : Tensor(il) {} @@ -38,13 +38,10 @@ class QTensor : public Tensor> { ~QTensor() override = default; QTensor(TensorShape const& shape) : Tensor(shape) {} - QTensor(TensorShape&& shape) : Tensor(shape) {} + QTensor(TensorShape&& shape) : Tensor(std::move(shape)) {} template requires std::convertible_to - QTensor(From const& internal) : Tensor(internal) {} - template - requires std::convertible_to - QTensor(From&& internal) : Tensor(internal) {} + QTensor(From&& internal) : Tensor(std::forward(internal)) {} static QTensor identity(size_t const& n_qubits); static QTensor zspider(size_t const& arity, dvlab::Phase const& phase = dvlab::Phase(0)); diff --git a/src/tensor/tensor.hpp b/src/tensor/tensor.hpp index 154a0aa95..00cbc29e7 100644 --- a/src/tensor/tensor.hpp +++ b/src/tensor/tensor.hpp @@ -44,7 +44,7 @@ class Tensor { virtual ~Tensor() = default; Tensor(TensorShape const& shape) : _tensor(shape) { reset_axis_history(); } - Tensor(TensorShape&& shape) : _tensor(shape) { reset_axis_history(); } + Tensor(TensorShape&& shape) : _tensor(std::move(shape)) { reset_axis_history(); } template requires std::convertible_to @@ -52,7 +52,7 @@ class Tensor { template requires std::convertible_to - Tensor(From&& internal) : _tensor(internal) { reset_axis_history(); } + Tensor(From&& internal) : _tensor(std::forward(internal)) { reset_axis_history(); } template DT& operator()(Args const&... args); diff --git a/src/util/ordered_hashmap.hpp b/src/util/ordered_hashmap.hpp index d734b2974..042a3e3af 100644 --- a/src/util/ordered_hashmap.hpp +++ b/src/util/ordered_hashmap.hpp @@ -230,8 +230,8 @@ T& ordered_hashmap::operator[](Key const& key) { */ template T& ordered_hashmap::operator[](Key&& key) { - if (!this->contains(key)) this->emplace(std::move(key), T()); - return at(key); + if (!this->contains(std::move(key))) this->emplace(std::move(key), T()); + return at(std::move(key)); } } // namespace utils diff --git a/src/util/ordered_hashtable.hpp b/src/util/ordered_hashtable.hpp index 911a04443..df569c22e 100644 --- a/src/util/ordered_hashtable.hpp +++ b/src/util/ordered_hashtable.hpp @@ -201,9 +201,9 @@ class ordered_hashtable { // NOLINT(readability-identifier-naming) : ordered_ha void sort(F lambda); protected: - std::unordered_map _key2id; - container _data; - size_t _size = 0; + std::unordered_map _key2id = {}; + container _data = {}; + size_t _size = 0; }; //------------------------------------------------------ From 91e51dca9dc82c283213074e86ace16d455258e2 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 11 Dec 2023 18:24:12 +0800 Subject: [PATCH 03/20] :bar_chart: delete unstable tab related dofiles --- tests/cli/dof/tab-file.dof | 9 --------- tests/cli/dof/tab-identifier.dof | 18 ------------------ tests/cli/dof/tab-variable.dof | 14 -------------- tests/cli/ref/tab-file.log | 9 --------- tests/cli/ref/tab-identifier.log | 7 ------- tests/cli/ref/tab-variable.log | 9 --------- 6 files changed, 66 deletions(-) delete mode 100644 tests/cli/dof/tab-file.dof delete mode 100644 tests/cli/dof/tab-identifier.dof delete mode 100644 tests/cli/dof/tab-variable.dof delete mode 100644 tests/cli/ref/tab-file.log delete mode 100644 tests/cli/ref/tab-identifier.log delete mode 100644 tests/cli/ref/tab-variable.log diff --git a/tests/cli/dof/tab-file.dof b/tests/cli/dof/tab-file.dof deleted file mode 100644 index 240412d5e..000000000 --- a/tests/cli/dof/tab-file.dof +++ /dev/null @@ -1,9 +0,0 @@ -// -------- FILE AUTOCOMPLETE -------- -// 1st tab: list help message to `source` command -// 2nd tab: list all files in the `tests/cli/` directory -// 3rd tab: autocomplete `h` to `helper/` -// 4th tab: autocomplete `e` to `empty_dof ` -source tests/cli/ h e -// tests when there are no matching files, nothing is printed -source tests/cli/g -quit -f \ No newline at end of file diff --git a/tests/cli/dof/tab-identifier.dof b/tests/cli/dof/tab-identifier.dof deleted file mode 100644 index c35e72c97..000000000 --- a/tests/cli/dof/tab-identifier.dof +++ /dev/null @@ -1,18 +0,0 @@ -// --------- COMMAND AUTOCOMPLETE -------- -// 1st tab: lists all commands and aliases. -// 2nd tab: list all commands starting with `h`. -// 3rd tab: autocomplete `hel` to `help` -// 4th tab: print usage to `help` -// quit: argument to `help` command - h el quit -// tests when there are no matching commands or aliases, nothing is printed -x -// ---------- ALIAS AUTOCOMPLETE --------- -alias dofile source -// 1st tab: complete to `dofile` (no space follows!) -// -f: argument to `quit` commands -do tests/cli/helper/empty_dof -// 2nd tab: replace `dofile` with `source` -// 3rd tab: add a space -do tests/cli/helper/empty_dof -quit -f diff --git a/tests/cli/dof/tab-variable.dof b/tests/cli/dof/tab-variable.dof deleted file mode 100644 index f70785bd6..000000000 --- a/tests/cli/dof/tab-variable.dof +++ /dev/null @@ -1,14 +0,0 @@ -// -------- VARIABLE TABBING -------- -set haha source -set happy source -// 1st tab: print all variables starting with `$ha` -// 2nd tab: autocomplete `$hap` to `$happy` -// 3rd tab replace `$happy` with source -$ha p tests/cli/helper/empty_dof -// 1st tab: print all variables starting with `$ha` -// 2nd tab: autocomplete `$hap` to `$happy` -${ha p tests/cli/helper/empty_dof -// 1st tab: replace `$happy` with source -// 2nd tab: add a space -${happy} tests/cli/helper/empty_dof -quit -f \ No newline at end of file diff --git a/tests/cli/ref/tab-file.log b/tests/cli/ref/tab-file.log deleted file mode 100644 index 063c7f699..000000000 --- a/tests/cli/ref/tab-file.log +++ /dev/null @@ -1,9 +0,0 @@ -qsyn> // -------- FILE AUTOCOMPLETE -------- -qsyn> // 1st tab: list help message to `source` command -qsyn> // 2nd tab: list all files in the `tests/cli/` directory -qsyn> // 3rd tab: autocomplete `h` to `helper/` -qsyn> // 4th tab: autocomplete `e` to `empty_dof ` -qsyn> source -Usage: source [-h] [-q] []... - -qsyn> source \ No newline at end of file diff --git a/tests/cli/ref/tab-identifier.log b/tests/cli/ref/tab-identifier.log deleted file mode 100644 index 78ec770b3..000000000 --- a/tests/cli/ref/tab-identifier.log +++ /dev/null @@ -1,7 +0,0 @@ -qsyn> // --------- COMMAND AUTOCOMPLETE -------- -qsyn> // 1st tab: lists all commands and aliases. -qsyn> // 2nd tab: list all commands starting with `h`. -qsyn> // 3rd tab: autocomplete `hel` to `help` -qsyn> // 4th tab: print usage to `help` -qsyn> // quit: argument to `help` command -qsyn> \ No newline at end of file diff --git a/tests/cli/ref/tab-variable.log b/tests/cli/ref/tab-variable.log deleted file mode 100644 index d6eff7fee..000000000 --- a/tests/cli/ref/tab-variable.log +++ /dev/null @@ -1,9 +0,0 @@ -qsyn> // -------- VARIABLE TABBING -------- -qsyn> set haha source - -qsyn> set happy source - -qsyn> // 1st tab: print all variables starting with `$ha` -qsyn> // 2nd tab: autocomplete `$hap` to `$happy` -qsyn> // 3rd tab replace `$happy` with source -qsyn> \ No newline at end of file From 747a17195b5dcb33626d67eabfeb3c95bf77587d Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 11 Dec 2023 18:48:26 +0800 Subject: [PATCH 04/20] :bar_chart: changed RUN_TESTS script to bash because python subprocess is weird --- docker/clang-test.Dockerfile | 3 +- docker/dev.Dockerfile | 3 +- docker/env.Dockerfile | 3 +- docker/gcc-test.Dockerfile | 3 +- docker/prod.Dockerfile | 3 +- scripts/RUN_TESTS | 533 +++++++++++++++-------------------- scripts/RUN_TESTS_PYTHON | 309 ++++++++++++++++++++ 7 files changed, 549 insertions(+), 308 deletions(-) create mode 100755 scripts/RUN_TESTS_PYTHON diff --git a/docker/clang-test.Dockerfile b/docker/clang-test.Dockerfile index a58fb9833..910ccc50d 100644 --- a/docker/clang-test.Dockerfile +++ b/docker/clang-test.Dockerfile @@ -2,7 +2,8 @@ FROM dvlab/qsyn-env:latest RUN dnf install -y \ diffutils \ - patch + patch \ + parallel COPY ./docker/test-entrypoint.sh /app/entrypoint.sh diff --git a/docker/dev.Dockerfile b/docker/dev.Dockerfile index e757f0c33..4418dab5f 100644 --- a/docker/dev.Dockerfile +++ b/docker/dev.Dockerfile @@ -14,7 +14,8 @@ FROM fedora:38 AS runner RUN dnf install -y \ lapack \ openblas-serial \ - libomp + libomp \ + parallel COPY --from=builder /app/build/qsyn /usr/local/bin/qsyn diff --git a/docker/env.Dockerfile b/docker/env.Dockerfile index 22f2b8887..af126a7e9 100644 --- a/docker/env.Dockerfile +++ b/docker/env.Dockerfile @@ -5,4 +5,5 @@ RUN dnf install -y \ clang \ cmake \ lapack-devel \ - openblas-devel + openblas-devel \ + parallel diff --git a/docker/gcc-test.Dockerfile b/docker/gcc-test.Dockerfile index 96a3697a5..c0c90a305 100644 --- a/docker/gcc-test.Dockerfile +++ b/docker/gcc-test.Dockerfile @@ -2,7 +2,8 @@ FROM dvlab/qsyn-env:latest RUN dnf install -y \ diffutils \ - patch + patch \ + parallel COPY ./docker/test-entrypoint.sh /app/entrypoint.sh diff --git a/docker/prod.Dockerfile b/docker/prod.Dockerfile index e5251a1f0..4e8d7480f 100644 --- a/docker/prod.Dockerfile +++ b/docker/prod.Dockerfile @@ -14,7 +14,8 @@ FROM fedora:38 AS runner RUN dnf install -y \ lapack \ openblas-serial \ - libomp + libomp \ + parallel COPY --from=builder /app/build/qsyn /usr/local/bin/qsyn diff --git a/scripts/RUN_TESTS b/scripts/RUN_TESTS index 6d7f409a1..75e005131 100755 --- a/scripts/RUN_TESTS +++ b/scripts/RUN_TESTS @@ -1,309 +1,236 @@ -#!/usr/bin/env python3 -import glob -import itertools -import multiprocessing -import os -import platform -import shutil -import subprocess -import sys -import textwrap -from argparse import ArgumentParser, RawDescriptionHelpFormatter - -qsyn_args = ["--no-version", "--qsynrc-path", "/dev/null", "--file"] - - -def cprint(*args, color=None, attrs=[], sep=" ", end="\n"): - """Print colored text""" - if not sys.stdout.isatty(): - print(*args, sep=sep, end=end) - return - if color is not None: - args = [getattr(Colors, color.upper()), *args, Colors.END] - if len(attrs) > 0: - args = [ - "".join(getattr(Colors, attr.upper()) for attr in attrs), - *args, - Colors.END, - ] - print(*args, sep=sep, end=end) - - -def pass_print(string, end="\n"): - cprint(string, color="green", attrs=["bold"], end=end) - - -def fail_print(string, end="\n"): - cprint(string, color="red", attrs=["bold"], end=end) - - -def unknown_print(string, end="\n"): - cprint(string, color="cyan", attrs=["bold"], end=end) - - -def dofile_result_same_with_ref(dofile, args): - from subprocess import PIPE, STDOUT - - if shutil.which("colordiff") is not None and sys.stdout.isatty(): - diff_cmd = "colordiff" - else: - diff_cmd = "diff" - - path, base = os.path.split(dofile) - pathpath, _ = os.path.split(path) - reffile = os.path.join(pathpath, "ref", os.path.splitext(base)[0] + ".log") - - if not os.path.isfile(reffile): - unknown_print(f" ? {dofile}: reference file not found") - return False, [dofile, " [Reference File Not Found]"] - - run_qsyn = subprocess.Popen( - [args.qsyn] + qsyn_args + [dofile], - stdout=PIPE, - stderr=STDOUT, - env={"OMP_WAIT_POLICY": "passive"}, - ) - - diff_result = subprocess.run( - [diff_cmd, "-", reffile], - stdin=run_qsyn.stdout, - stdout=PIPE, - stderr=STDOUT, - text=True, - ) - - if diff_result.returncode == 0: - if args.verbose: - pass_print(" ✓ ", end="") - print(f"{dofile}") - else: - fail_print(" ✗ ", end="") - print(f"{dofile}") - - return diff_result.returncode == 0, [dofile, diff_result.stdout] - - -def update_dofile_ref(dofile, args): - from subprocess import PIPE, STDOUT - - path, base = os.path.split(dofile) - pathpath, _ = os.path.split(path) - reffile = os.path.join(pathpath, "ref", os.path.splitext(base)[0] + ".log") - - if not os.path.isfile(reffile): - os.makedirs(os.path.join(pathpath, "ref"), exist_ok=True) - with open(reffile, "a"): - os.utime(reffile, None) - - run_qsyn = subprocess.Popen( - [args.qsyn] + qsyn_args + [dofile], - stdout=PIPE, - stderr=STDOUT, - env={"OMP_WAIT_POLICY": "passive"}, - ) - - diff_result = subprocess.run( - ["diff", reffile, "-"], stdin=run_qsyn.stdout, stdout=PIPE, text=True - ) - - if diff_result.returncode == 1: - patch = subprocess.run( - ["patch", reffile], input=diff_result.stdout, text=True, capture_output=True - ) - if patch.returncode == 0: - pass_print(" ↑ ", end="") - print(f"{dofile}") - else: - fail_print(" ! ", end="") - print(f"{dofile}") - return patch.returncode == 0, dofile - - elif args.verbose: - print(f" - {dofile}") - - return False, dofile - - -def update_test_refs(dofiles, args): - print("> Updating dofiles...") - - threads = multiprocessing.Pool(4) - - results = threads.starmap(update_dofile_ref, zip(dofiles, itertools.repeat(args))) - results = list(filter(lambda x: x[0] == True, results)) - - pass_print(f"Updated {len(results)} of {len(dofiles)} dofiles.") - - -def run_tests_and_compare_to_ref(dofiles, args): - print("> Testing dofiles...") - - threads = multiprocessing.Pool(4) - - results = threads.starmap( - dofile_result_same_with_ref, zip(dofiles, itertools.repeat(args)) - ) - results = filter(lambda x: x[0] == False, results) - fails = {} - - for result in results: - fails[result[1][0]] = result[1][1] - - if len(fails) == 0: - pass_print(f"Passed all {len(dofiles)} dofiles.") - else: - if not args.no_report: - print() - print(" DIFFERENCES ".center(50, "-")) - for file, diff in fails.items(): - fail_print("✗ ", end="") - print(f"{file}") - print(textwrap.indent(diff, " ")) - print("".ljust(50, "-")) - - fail_print(f"{len(fails)} out of {len(dofiles)} dofiles failed.") - - return len(fails) == 0 - - -def get_parser(): - descript = """ -Run test dofiles to qsyn, and help comparing to/updating the reference. When a reference is used, this function expects the following file structure and extensions: - some_folder/ - ├ dof/ - │ ├ test1.dof - │ └ test2.dof - └ ref/ - ├ test1.log - └ test2.log""" - parser = ArgumentParser( - description=descript, formatter_class=RawDescriptionHelpFormatter - ) - - default_qsyn = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "../tests/" - ) - parser.add_argument( - "paths", - nargs="*", - default=[default_qsyn], - help="run the provided dofiles. If directories are provided, tests all dofiles in tests/", - ) - - default_qsyn = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../qsyn") - parser.add_argument( - "-q", - "--qsyn", - default=default_qsyn, - help="path to qsyn executable", - ) - - mutex = parser.add_mutually_exclusive_group() - - mutex.add_argument( - "-d", - "--difference", - action="store_true", - help="(default) compare the dofile output and the reference", - ) - mutex.add_argument( - "-f", - "--file", - action="store_true", - help="simply run the dofile with qsyn. This argument serves as a shortcut to rerun failed dofiles", - ) - mutex.add_argument( - "-u", - "--update", - action="store_true", - help="update the references to dofiles, or create one if there is none", - ) - - parser.add_argument( - "-v", - "--verbose", - help="if specified, print all results; otherwise only the failed ones will be printed", - action="count", - ) - parser.add_argument( - "-n", - "--no-report", - help="do not report difference between execution result and reference", - action="store_true", - ) - - return parser - - -class Colors: - """ANSI color codes""" - - BLACK = "\033[30m" - RED = "\033[31m" - GREEN = "\033[32m" - BROWN = "\033[33m" - BLUE = "\033[34m" - PURPLE = "\033[35m" - CYAN = "\033[36m" - LIGHT_GRAY = "\033[37m" - DARK_GRAY = "\033[40m" - LIGHT_RED = "\033[41m" - LIGHT_GREEN = "\033[42m" - YELLOW = "\033[43m" - LIGHT_BLUE = "\033[44m" - LIGHT_PURPLE = "\033[45m" - LIGHT_CYAN = "\033[46m" - LIGHT_WHITE = "\033[47m" - BOLD = "\033[1m" - FAINT = "\033[2m" - ITALIC = "\033[3m" - UNDERLINE = "\033[4m" - BLINK = "\033[5m" - NEGATIVE = "\033[7m" - CROSSED = "\033[9m" - END = "\033[0m" - # cancel SGR codes if we don't write to a terminal - if not sys.stdout.isatty(): - for _ in dir(): - if isinstance(_, str) and _[0] != "_": - locals()[_] = "" - else: - # set Windows console in VT mode - if platform.system() == "Windows": - kernel32 = __import__("ctypes").windll.kernel32 - kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) - del kernel32 - - -def main(): - parser = get_parser() - - args = parser.parse_args() +#!/bin/bash + +# test if colordiff is installed; if not use diff +if [ -x "$(command -v colordiff)" ]; then + DIFF="colordiff" +else + DIFF="diff" +fi + +# return text in green, bold +pass-style() { + if [ $# -ne 1 ]; then + echo "Usage: pass-style " + else + echo -e "\033[1;32m$1\033[0m" + fi +} +export -f pass-style + +# return text in red, bold +fail-style() { + if [ $# -ne 1 ]; then + echo "Usage: fail-style " + else + echo -e "\033[1;31m$1\033[0m" + fi +} +export -f fail-style + +# return text in cyan, bold +unknown-style() { + if [ $# -ne 1 ]; then + echo "Usage: unknown-style " + else + echo -e "\033[1;36m$1\033[0m" + fi +} +export -f unknown-style + +# print usage message +usage() { + echo "Usage: $0 [-d|--diff] [-u|--update] [-q|--qsyn ]" + echo " -d, --diff diff the output of qsyn with the expected output" + echo " -u, --update update the expected output with the output of qsyn" + echo " -q, --qsyn path to qsyn executable" + echo " -v, --verbose also report tests that pass" + echo " -s, --silent do not report difference between execution result and reference" +} +export -f usage + +# parse arguments + +cd "$(git rev-parse --show-toplevel)" || exit 1 +QSYN=./qsyn +ACTION="" +SILENT=0 +VERBOSE=0 +POSITIONAL=() + +while [[ $# -gt 0 ]]; do + case $1 in + -d|--diff) + if [ -n "$ACTION" ]; then + echo "Error: cannot specify multiple actions!!" + usage + exit 1 + fi + ACTION="diff" + shift # argument + ;; + -u|--update) + if [ -n "$ACTION" ]; then + echo "Error: cannot specify multiple actions!!" + usage + exit 1 + fi + ACTION="update" + shift # argument + ;; + -q|--qsyn) + QSYN=$2; + shift # argument + shift # variable + ;; + -v|--verbose) + VERBOSE=1 + shift # argument + ;; + -s|--silent) + SILENT=1 + shift # argument + ;; + -h|--help) + usage + exit 0 + ;; + -*|--*) + echo "Error: unknown option $1!!" + usage + exit 1 + ;; + *) + POSITIONAL+=("$1") + shift # argument + esac +done + +# the default action is to diff +if [ ! -n "$ACTION" ]; then + ACTION="diff" +fi + +# if no positional argument is given, run all tests from the tests directory +if [ ${#POSITIONAL[@]} -eq 0 ]; then + POSITIONAL=(tests) +fi + +TESTS=() +# for each positional argument, if it is a file, run the test on that file +# if it is a directory, run the test on all files in that directory +for arg in "${POSITIONAL[@]}"; do + if [ -f "$arg" ]; then + TESTS+=("$arg") + elif [ -d "$arg" ]; then + for file in $(find $arg -regex ".*\.dof" -type f); do + if [ -f "$file" ]; then + TESTS+=("$file") + fi + done + else + echo "Error: $arg is not a file or directory!!" + usage + exit 1 + fi +done + +# run dofile with qsyn and diff the output with the expected output + +ref-path() { + local TEST=$1 + echo $(realpath --relative-to $(pwd) "$(dirname ${TEST})/../ref/$(basename ${TEST%.*}).log") +} +export -f ref-path + +# diff the output of qsyn with the reference output +dofile-result-same-with-ref() { + local TEST=$1 + local VERBOSE=$2 + local SILENT=$3 + local QSYN=$4 + local DIFF=$5 + local REF=$(ref-path $TEST) + + DIFF_OUTPUT=$(OMP_WAIT_POLICY=passive $QSYN --no-version --qsynrc-path /dev/null --file $TEST 2>&1 | $DIFF - $REF 2>&1) + if [ $? -eq 0 ]; then + if [[ $VERBOSE -eq 1 ]]; then + echo " $(pass-style '✓') $TEST" + return 0 + fi + else + if [ ! -f $REF ]; then + echo " $(unknown-style '?') $REF" + else + echo " $(fail-style '✗') $TEST" + fi + if [[ $SILENT -eq 0 ]]; then + # indent diff output + DIFF_OUTPUT=$(echo "$DIFF_OUTPUT" | sed 's/^/ /') + echo "$DIFF_OUTPUT" + fi + return 1 + fi +} +export -f dofile-result-same-with-ref + +update-dofile-ref() { + local TEST=$1 + local VERBOSE=$2 + local SILENT=$3 + local QSYN=$4 + local REF=$(ref-path $TEST) + + # if the reference file does not exist, create it + if [[ ! -f $REF ]]; then + mkdir -p $(dirname $REF) + touch $REF + fi + + DIFF_OUTPUT=$(OMP_WAIT_POLICY=passive $QSYN --no-version --qsynrc-path /dev/null --file $TEST 2>&1 | diff $REF -) + + # update reference file if the output of qsyn is different from the reference + if [[ $? -eq 0 ]]; then + if [[ $VERBOSE -eq 1 ]]; then + echo " - $TEST" + return 0 + fi + else + printf "%s\n" "$DIFF_OUTPUT" | patch -f $REF + if [[ $? -eq 1 ]]; then + echo " $(fail-style !) $TEST" + return 0 + else + echo " $(pass-style ↑) $TEST" + return 1 + fi + fi +} +export -f update-dofile-ref + +# diff dofiles and report result +if [[ "$ACTION" == 'diff' ]]; then + echo "> Testing dofiles..." + + # run in parallel and count failures + parallel -j"$(nproc)" "dofile-result-same-with-ref {} $VERBOSE $SILENT $QSYN $DIFF" ::: "${TESTS[@]}" - dofiles = [] + FAIL_COUNT=$? - args.qsyn = os.path.abspath(args.qsyn) + TESTS_COUNT=${#TESTS[@]} + if [ $FAIL_COUNT -eq 0 ]; then + echo "$(pass-style "Passed all $TESTS_COUNT tests.")" + else + echo "$(fail-style "$FAIL_COUNT out of $TESTS_COUNT tests failed.")" + fi +elif [[ "$ACTION" == 'update' ]]; then + echo "> Updating reference files..." - for path in args.paths: - if os.path.isfile(path): - dofiles.append(path) - elif os.path.isdir(path): - dofiles.extend(glob.glob(os.path.join(path, "**/*.dof"), recursive=True)) - else: - print(f"Warning: ignoring path '{path}': not a file nor a dir!!") + # run in parallel and count updates + parallel -j"$(nproc)" "update-dofile-ref {} $VERBOSE $SILENT $QSYN" ::: "${TESTS[@]}" - if args.file: - for dofile in dofiles: - subprocess.run( - [args.qsyn] + qsyn_args + [dofile], env={"OMP_WAIT_POLICY": "passive"} - ) - return 0 - if args.update: - update_test_refs(dofiles, args) - return 0 - - return 0 if run_tests_and_compare_to_ref(dofiles, args) else 1 + UPDATE_COUNT=$? + TESTS_COUNT=${#TESTS[@]} -if __name__ == "__main__": - exit(main()) + echo "$(pass-style "Updated $UPDATE_COUNT out of $TESTS_COUNT reference files.")" +fi \ No newline at end of file diff --git a/scripts/RUN_TESTS_PYTHON b/scripts/RUN_TESTS_PYTHON new file mode 100755 index 000000000..6d7f409a1 --- /dev/null +++ b/scripts/RUN_TESTS_PYTHON @@ -0,0 +1,309 @@ +#!/usr/bin/env python3 +import glob +import itertools +import multiprocessing +import os +import platform +import shutil +import subprocess +import sys +import textwrap +from argparse import ArgumentParser, RawDescriptionHelpFormatter + +qsyn_args = ["--no-version", "--qsynrc-path", "/dev/null", "--file"] + + +def cprint(*args, color=None, attrs=[], sep=" ", end="\n"): + """Print colored text""" + if not sys.stdout.isatty(): + print(*args, sep=sep, end=end) + return + if color is not None: + args = [getattr(Colors, color.upper()), *args, Colors.END] + if len(attrs) > 0: + args = [ + "".join(getattr(Colors, attr.upper()) for attr in attrs), + *args, + Colors.END, + ] + print(*args, sep=sep, end=end) + + +def pass_print(string, end="\n"): + cprint(string, color="green", attrs=["bold"], end=end) + + +def fail_print(string, end="\n"): + cprint(string, color="red", attrs=["bold"], end=end) + + +def unknown_print(string, end="\n"): + cprint(string, color="cyan", attrs=["bold"], end=end) + + +def dofile_result_same_with_ref(dofile, args): + from subprocess import PIPE, STDOUT + + if shutil.which("colordiff") is not None and sys.stdout.isatty(): + diff_cmd = "colordiff" + else: + diff_cmd = "diff" + + path, base = os.path.split(dofile) + pathpath, _ = os.path.split(path) + reffile = os.path.join(pathpath, "ref", os.path.splitext(base)[0] + ".log") + + if not os.path.isfile(reffile): + unknown_print(f" ? {dofile}: reference file not found") + return False, [dofile, " [Reference File Not Found]"] + + run_qsyn = subprocess.Popen( + [args.qsyn] + qsyn_args + [dofile], + stdout=PIPE, + stderr=STDOUT, + env={"OMP_WAIT_POLICY": "passive"}, + ) + + diff_result = subprocess.run( + [diff_cmd, "-", reffile], + stdin=run_qsyn.stdout, + stdout=PIPE, + stderr=STDOUT, + text=True, + ) + + if diff_result.returncode == 0: + if args.verbose: + pass_print(" ✓ ", end="") + print(f"{dofile}") + else: + fail_print(" ✗ ", end="") + print(f"{dofile}") + + return diff_result.returncode == 0, [dofile, diff_result.stdout] + + +def update_dofile_ref(dofile, args): + from subprocess import PIPE, STDOUT + + path, base = os.path.split(dofile) + pathpath, _ = os.path.split(path) + reffile = os.path.join(pathpath, "ref", os.path.splitext(base)[0] + ".log") + + if not os.path.isfile(reffile): + os.makedirs(os.path.join(pathpath, "ref"), exist_ok=True) + with open(reffile, "a"): + os.utime(reffile, None) + + run_qsyn = subprocess.Popen( + [args.qsyn] + qsyn_args + [dofile], + stdout=PIPE, + stderr=STDOUT, + env={"OMP_WAIT_POLICY": "passive"}, + ) + + diff_result = subprocess.run( + ["diff", reffile, "-"], stdin=run_qsyn.stdout, stdout=PIPE, text=True + ) + + if diff_result.returncode == 1: + patch = subprocess.run( + ["patch", reffile], input=diff_result.stdout, text=True, capture_output=True + ) + if patch.returncode == 0: + pass_print(" ↑ ", end="") + print(f"{dofile}") + else: + fail_print(" ! ", end="") + print(f"{dofile}") + return patch.returncode == 0, dofile + + elif args.verbose: + print(f" - {dofile}") + + return False, dofile + + +def update_test_refs(dofiles, args): + print("> Updating dofiles...") + + threads = multiprocessing.Pool(4) + + results = threads.starmap(update_dofile_ref, zip(dofiles, itertools.repeat(args))) + results = list(filter(lambda x: x[0] == True, results)) + + pass_print(f"Updated {len(results)} of {len(dofiles)} dofiles.") + + +def run_tests_and_compare_to_ref(dofiles, args): + print("> Testing dofiles...") + + threads = multiprocessing.Pool(4) + + results = threads.starmap( + dofile_result_same_with_ref, zip(dofiles, itertools.repeat(args)) + ) + results = filter(lambda x: x[0] == False, results) + fails = {} + + for result in results: + fails[result[1][0]] = result[1][1] + + if len(fails) == 0: + pass_print(f"Passed all {len(dofiles)} dofiles.") + else: + if not args.no_report: + print() + print(" DIFFERENCES ".center(50, "-")) + for file, diff in fails.items(): + fail_print("✗ ", end="") + print(f"{file}") + print(textwrap.indent(diff, " ")) + print("".ljust(50, "-")) + + fail_print(f"{len(fails)} out of {len(dofiles)} dofiles failed.") + + return len(fails) == 0 + + +def get_parser(): + descript = """ +Run test dofiles to qsyn, and help comparing to/updating the reference. When a reference is used, this function expects the following file structure and extensions: + some_folder/ + ├ dof/ + │ ├ test1.dof + │ └ test2.dof + └ ref/ + ├ test1.log + └ test2.log""" + parser = ArgumentParser( + description=descript, formatter_class=RawDescriptionHelpFormatter + ) + + default_qsyn = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "../tests/" + ) + parser.add_argument( + "paths", + nargs="*", + default=[default_qsyn], + help="run the provided dofiles. If directories are provided, tests all dofiles in tests/", + ) + + default_qsyn = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../qsyn") + parser.add_argument( + "-q", + "--qsyn", + default=default_qsyn, + help="path to qsyn executable", + ) + + mutex = parser.add_mutually_exclusive_group() + + mutex.add_argument( + "-d", + "--difference", + action="store_true", + help="(default) compare the dofile output and the reference", + ) + mutex.add_argument( + "-f", + "--file", + action="store_true", + help="simply run the dofile with qsyn. This argument serves as a shortcut to rerun failed dofiles", + ) + mutex.add_argument( + "-u", + "--update", + action="store_true", + help="update the references to dofiles, or create one if there is none", + ) + + parser.add_argument( + "-v", + "--verbose", + help="if specified, print all results; otherwise only the failed ones will be printed", + action="count", + ) + parser.add_argument( + "-n", + "--no-report", + help="do not report difference between execution result and reference", + action="store_true", + ) + + return parser + + +class Colors: + """ANSI color codes""" + + BLACK = "\033[30m" + RED = "\033[31m" + GREEN = "\033[32m" + BROWN = "\033[33m" + BLUE = "\033[34m" + PURPLE = "\033[35m" + CYAN = "\033[36m" + LIGHT_GRAY = "\033[37m" + DARK_GRAY = "\033[40m" + LIGHT_RED = "\033[41m" + LIGHT_GREEN = "\033[42m" + YELLOW = "\033[43m" + LIGHT_BLUE = "\033[44m" + LIGHT_PURPLE = "\033[45m" + LIGHT_CYAN = "\033[46m" + LIGHT_WHITE = "\033[47m" + BOLD = "\033[1m" + FAINT = "\033[2m" + ITALIC = "\033[3m" + UNDERLINE = "\033[4m" + BLINK = "\033[5m" + NEGATIVE = "\033[7m" + CROSSED = "\033[9m" + END = "\033[0m" + # cancel SGR codes if we don't write to a terminal + if not sys.stdout.isatty(): + for _ in dir(): + if isinstance(_, str) and _[0] != "_": + locals()[_] = "" + else: + # set Windows console in VT mode + if platform.system() == "Windows": + kernel32 = __import__("ctypes").windll.kernel32 + kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) + del kernel32 + + +def main(): + parser = get_parser() + + args = parser.parse_args() + + dofiles = [] + + args.qsyn = os.path.abspath(args.qsyn) + + for path in args.paths: + if os.path.isfile(path): + dofiles.append(path) + elif os.path.isdir(path): + dofiles.extend(glob.glob(os.path.join(path, "**/*.dof"), recursive=True)) + else: + print(f"Warning: ignoring path '{path}': not a file nor a dir!!") + + if args.file: + for dofile in dofiles: + subprocess.run( + [args.qsyn] + qsyn_args + [dofile], env={"OMP_WAIT_POLICY": "passive"} + ) + return 0 + if args.update: + update_test_refs(dofiles, args) + return 0 + + return 0 if run_tests_and_compare_to_ref(dofiles, args) else 1 + + +if __name__ == "__main__": + exit(main()) From e7552b3dd363ab14918df3afd226ef7f22810f03 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 11 Dec 2023 19:44:01 +0800 Subject: [PATCH 05/20] :bug: explicitly check if parallel is installed for scripts --- scripts/LINT | 6 ++++++ scripts/RUN_TESTS | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/scripts/LINT b/scripts/LINT index ba60a9e35..b41e7dbef 100755 --- a/scripts/LINT +++ b/scripts/LINT @@ -1,4 +1,10 @@ #!/usr/bin/env bash + +if [ ! -x "$(command -v parallel)" ]; then + echo "Error: GNU parallel is not installed!!" + exit 1 +fi + format_one() { clang-format -i "$1" } diff --git a/scripts/RUN_TESTS b/scripts/RUN_TESTS index 75e005131..94c70ac99 100755 --- a/scripts/RUN_TESTS +++ b/scripts/RUN_TESTS @@ -1,5 +1,10 @@ #!/bin/bash +if [ ! -x "$(command -v parallel)" ]; then + echo "Error: GNU parallel is not installed!!" + exit 1 +fi + # test if colordiff is installed; if not use diff if [ -x "$(command -v colordiff)" ]; then DIFF="colordiff" From 04b02ad4ace2380e5df6d724d82aabf745f31a73 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 11 Dec 2023 19:55:15 +0800 Subject: [PATCH 06/20] :fire: delete python-based RUN_TESTS scripts --- scripts/RUN_TESTS_PYTHON | 309 --------------------------------------- 1 file changed, 309 deletions(-) delete mode 100755 scripts/RUN_TESTS_PYTHON diff --git a/scripts/RUN_TESTS_PYTHON b/scripts/RUN_TESTS_PYTHON deleted file mode 100755 index 6d7f409a1..000000000 --- a/scripts/RUN_TESTS_PYTHON +++ /dev/null @@ -1,309 +0,0 @@ -#!/usr/bin/env python3 -import glob -import itertools -import multiprocessing -import os -import platform -import shutil -import subprocess -import sys -import textwrap -from argparse import ArgumentParser, RawDescriptionHelpFormatter - -qsyn_args = ["--no-version", "--qsynrc-path", "/dev/null", "--file"] - - -def cprint(*args, color=None, attrs=[], sep=" ", end="\n"): - """Print colored text""" - if not sys.stdout.isatty(): - print(*args, sep=sep, end=end) - return - if color is not None: - args = [getattr(Colors, color.upper()), *args, Colors.END] - if len(attrs) > 0: - args = [ - "".join(getattr(Colors, attr.upper()) for attr in attrs), - *args, - Colors.END, - ] - print(*args, sep=sep, end=end) - - -def pass_print(string, end="\n"): - cprint(string, color="green", attrs=["bold"], end=end) - - -def fail_print(string, end="\n"): - cprint(string, color="red", attrs=["bold"], end=end) - - -def unknown_print(string, end="\n"): - cprint(string, color="cyan", attrs=["bold"], end=end) - - -def dofile_result_same_with_ref(dofile, args): - from subprocess import PIPE, STDOUT - - if shutil.which("colordiff") is not None and sys.stdout.isatty(): - diff_cmd = "colordiff" - else: - diff_cmd = "diff" - - path, base = os.path.split(dofile) - pathpath, _ = os.path.split(path) - reffile = os.path.join(pathpath, "ref", os.path.splitext(base)[0] + ".log") - - if not os.path.isfile(reffile): - unknown_print(f" ? {dofile}: reference file not found") - return False, [dofile, " [Reference File Not Found]"] - - run_qsyn = subprocess.Popen( - [args.qsyn] + qsyn_args + [dofile], - stdout=PIPE, - stderr=STDOUT, - env={"OMP_WAIT_POLICY": "passive"}, - ) - - diff_result = subprocess.run( - [diff_cmd, "-", reffile], - stdin=run_qsyn.stdout, - stdout=PIPE, - stderr=STDOUT, - text=True, - ) - - if diff_result.returncode == 0: - if args.verbose: - pass_print(" ✓ ", end="") - print(f"{dofile}") - else: - fail_print(" ✗ ", end="") - print(f"{dofile}") - - return diff_result.returncode == 0, [dofile, diff_result.stdout] - - -def update_dofile_ref(dofile, args): - from subprocess import PIPE, STDOUT - - path, base = os.path.split(dofile) - pathpath, _ = os.path.split(path) - reffile = os.path.join(pathpath, "ref", os.path.splitext(base)[0] + ".log") - - if not os.path.isfile(reffile): - os.makedirs(os.path.join(pathpath, "ref"), exist_ok=True) - with open(reffile, "a"): - os.utime(reffile, None) - - run_qsyn = subprocess.Popen( - [args.qsyn] + qsyn_args + [dofile], - stdout=PIPE, - stderr=STDOUT, - env={"OMP_WAIT_POLICY": "passive"}, - ) - - diff_result = subprocess.run( - ["diff", reffile, "-"], stdin=run_qsyn.stdout, stdout=PIPE, text=True - ) - - if diff_result.returncode == 1: - patch = subprocess.run( - ["patch", reffile], input=diff_result.stdout, text=True, capture_output=True - ) - if patch.returncode == 0: - pass_print(" ↑ ", end="") - print(f"{dofile}") - else: - fail_print(" ! ", end="") - print(f"{dofile}") - return patch.returncode == 0, dofile - - elif args.verbose: - print(f" - {dofile}") - - return False, dofile - - -def update_test_refs(dofiles, args): - print("> Updating dofiles...") - - threads = multiprocessing.Pool(4) - - results = threads.starmap(update_dofile_ref, zip(dofiles, itertools.repeat(args))) - results = list(filter(lambda x: x[0] == True, results)) - - pass_print(f"Updated {len(results)} of {len(dofiles)} dofiles.") - - -def run_tests_and_compare_to_ref(dofiles, args): - print("> Testing dofiles...") - - threads = multiprocessing.Pool(4) - - results = threads.starmap( - dofile_result_same_with_ref, zip(dofiles, itertools.repeat(args)) - ) - results = filter(lambda x: x[0] == False, results) - fails = {} - - for result in results: - fails[result[1][0]] = result[1][1] - - if len(fails) == 0: - pass_print(f"Passed all {len(dofiles)} dofiles.") - else: - if not args.no_report: - print() - print(" DIFFERENCES ".center(50, "-")) - for file, diff in fails.items(): - fail_print("✗ ", end="") - print(f"{file}") - print(textwrap.indent(diff, " ")) - print("".ljust(50, "-")) - - fail_print(f"{len(fails)} out of {len(dofiles)} dofiles failed.") - - return len(fails) == 0 - - -def get_parser(): - descript = """ -Run test dofiles to qsyn, and help comparing to/updating the reference. When a reference is used, this function expects the following file structure and extensions: - some_folder/ - ├ dof/ - │ ├ test1.dof - │ └ test2.dof - └ ref/ - ├ test1.log - └ test2.log""" - parser = ArgumentParser( - description=descript, formatter_class=RawDescriptionHelpFormatter - ) - - default_qsyn = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "../tests/" - ) - parser.add_argument( - "paths", - nargs="*", - default=[default_qsyn], - help="run the provided dofiles. If directories are provided, tests all dofiles in tests/", - ) - - default_qsyn = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../qsyn") - parser.add_argument( - "-q", - "--qsyn", - default=default_qsyn, - help="path to qsyn executable", - ) - - mutex = parser.add_mutually_exclusive_group() - - mutex.add_argument( - "-d", - "--difference", - action="store_true", - help="(default) compare the dofile output and the reference", - ) - mutex.add_argument( - "-f", - "--file", - action="store_true", - help="simply run the dofile with qsyn. This argument serves as a shortcut to rerun failed dofiles", - ) - mutex.add_argument( - "-u", - "--update", - action="store_true", - help="update the references to dofiles, or create one if there is none", - ) - - parser.add_argument( - "-v", - "--verbose", - help="if specified, print all results; otherwise only the failed ones will be printed", - action="count", - ) - parser.add_argument( - "-n", - "--no-report", - help="do not report difference between execution result and reference", - action="store_true", - ) - - return parser - - -class Colors: - """ANSI color codes""" - - BLACK = "\033[30m" - RED = "\033[31m" - GREEN = "\033[32m" - BROWN = "\033[33m" - BLUE = "\033[34m" - PURPLE = "\033[35m" - CYAN = "\033[36m" - LIGHT_GRAY = "\033[37m" - DARK_GRAY = "\033[40m" - LIGHT_RED = "\033[41m" - LIGHT_GREEN = "\033[42m" - YELLOW = "\033[43m" - LIGHT_BLUE = "\033[44m" - LIGHT_PURPLE = "\033[45m" - LIGHT_CYAN = "\033[46m" - LIGHT_WHITE = "\033[47m" - BOLD = "\033[1m" - FAINT = "\033[2m" - ITALIC = "\033[3m" - UNDERLINE = "\033[4m" - BLINK = "\033[5m" - NEGATIVE = "\033[7m" - CROSSED = "\033[9m" - END = "\033[0m" - # cancel SGR codes if we don't write to a terminal - if not sys.stdout.isatty(): - for _ in dir(): - if isinstance(_, str) and _[0] != "_": - locals()[_] = "" - else: - # set Windows console in VT mode - if platform.system() == "Windows": - kernel32 = __import__("ctypes").windll.kernel32 - kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) - del kernel32 - - -def main(): - parser = get_parser() - - args = parser.parse_args() - - dofiles = [] - - args.qsyn = os.path.abspath(args.qsyn) - - for path in args.paths: - if os.path.isfile(path): - dofiles.append(path) - elif os.path.isdir(path): - dofiles.extend(glob.glob(os.path.join(path, "**/*.dof"), recursive=True)) - else: - print(f"Warning: ignoring path '{path}': not a file nor a dir!!") - - if args.file: - for dofile in dofiles: - subprocess.run( - [args.qsyn] + qsyn_args + [dofile], env={"OMP_WAIT_POLICY": "passive"} - ) - return 0 - if args.update: - update_test_refs(dofiles, args) - return 0 - - return 0 if run_tests_and_compare_to_ref(dofiles, args) else 1 - - -if __name__ == "__main__": - exit(main()) From 0fca210821b2fab35f4f451fff0c534729c873ef Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 11 Dec 2023 20:00:48 +0800 Subject: [PATCH 07/20] :bar_chart: updated testcases --- tests/conversion/duostra/dof/v0.6.2-bug.dof | 6 +++--- tests/conversion/duostra/ref/v0.6.2-bug.log | 18 ++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/conversion/duostra/dof/v0.6.2-bug.dof b/tests/conversion/duostra/dof/v0.6.2-bug.dof index d737b0c6e..3ac800b53 100644 --- a/tests/conversion/duostra/dof/v0.6.2-bug.dof +++ b/tests/conversion/duostra/dof/v0.6.2-bug.dof @@ -14,9 +14,9 @@ qcir new qcir qubit add 3 qcir gate add cx 2 1 qcir gate add cx 2 0 -qc2zx -zxopt -zx2qc +convert qcir zx +zx optimize +convert zx qcir qcir print -d duostra -c diff --git a/tests/conversion/duostra/ref/v0.6.2-bug.log b/tests/conversion/duostra/ref/v0.6.2-bug.log index e8f024ecc..ca9eaee88 100644 --- a/tests/conversion/duostra/ref/v0.6.2-bug.log +++ b/tests/conversion/duostra/ref/v0.6.2-bug.log @@ -47,18 +47,16 @@ qsyn> qcir gate add cx 2 1 qsyn> qcir gate add cx 2 0 -qsyn> qc2zx +qsyn> convert qcir zx -qsyn> zxopt -[error] Unknown command!! (zxopt) +qsyn> zx optimize -qsyn> zx2qc -[error] ZXGraph 0 is not extractable because it is not graph-like!! +qsyn> convert zx qcir qsyn> qcir print -d -Q 0 -------------------------cx( 1)- -Q 1 ---------cx( 0)- -Q 2 ---------cx( 0)----------cx( 1)- +Q 0 - h( 4)----------------------------------cz( 2)-- h( 0)- +Q 1 - h( 5)------------------cz( 3)-- h( 1)- +Q 2 - h( 7)-- h( 6)----------cz( 3)----------cz( 2)- qsyn> duostra -c Routing... @@ -71,8 +69,8 @@ Scheduler: search Router: duostra Placer: dfs -Mapping Depth: 10 -Total Time: 10 +Mapping Depth: 13 +Total Time: 16 #SWAP: 1 From 3a0ec59f8d1321e1e343d116e8f2c222d5d8556f Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Mon, 25 Dec 2023 22:12:00 +0800 Subject: [PATCH 08/20] :bug: trivial-optimization removes non-matching cz gates --- src/qcir/optimizer/trivial_optimization.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/qcir/optimizer/trivial_optimization.cpp b/src/qcir/optimizer/trivial_optimization.cpp index 71b7b5835..f4cf58998 100644 --- a/src/qcir/optimizer/trivial_optimization.cpp +++ b/src/qcir/optimizer/trivial_optimization.cpp @@ -127,6 +127,20 @@ void Optimizer::_fuse_z_phase(QCir& qcir, QCirGate* prev_gate, QCirGate* gate) { * @return modified circuit */ void Optimizer::_cancel_double_gate(QCir& qcir, QCirGate* prev_gate, QCirGate* gate) { + if (!is_double_qubit_gate(prev_gate) || !is_double_qubit_gate(gate)) { + Optimizer::_add_gate_to_circuit(qcir, gate, false); + return; + } + + auto const prev_qubits = prev_gate->get_qubits(); + auto const gate_qubits = gate->get_qubits(); + if (!( + prev_qubits[0]._qubit == gate_qubits[0]._qubit && prev_qubits[1]._qubit == gate_qubits[1]._qubit) && + !(prev_qubits[0]._qubit == gate_qubits[1]._qubit && prev_qubits[1]._qubit == gate_qubits[0]._qubit)) { + Optimizer::_add_gate_to_circuit(qcir, gate, false); + return; + } + if (prev_gate->get_rotation_category() != gate->get_rotation_category()) { Optimizer::_add_gate_to_circuit(qcir, gate, false); return; From a1456782c72a64a2b05f51b00c0705cd8dda0dcb Mon Sep 17 00:00:00 2001 From: chinyi0523 Date: Tue, 26 Dec 2023 16:07:22 +0800 Subject: [PATCH 09/20] =?UTF-8?q?=F0=9F=90=9B=20Fix=20filter=20cnots=20by?= =?UTF-8?q?=20checking=20matched=20control=20and=20target=20qubits=20(#458?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/boolean_matrix.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/util/boolean_matrix.cpp b/src/util/boolean_matrix.cpp index fe7894043..402038909 100644 --- a/src/util/boolean_matrix.cpp +++ b/src/util/boolean_matrix.cpp @@ -281,10 +281,12 @@ size_t BooleanMatrix::filter_duplicate_row_operations() { for (size_t ith_row_op = 0; ith_row_op < _row_operations.size(); ith_row_op++) { auto& [row_src, row_dest] = _row_operations[ith_row_op]; auto const first_match = last_used.contains(row_src) && - last_used[row_src].row_idx == row_dest; + last_used[row_src].row_idx == row_dest && + _row_operations[last_used[row_src].op_idx].first == row_src; // make sure the destinations are matched auto const second_match = last_used.contains(row_dest) && - last_used[row_dest].row_idx == row_src; + last_used[row_dest].row_idx == row_src && + _row_operations[last_used[row_dest].op_idx].second == row_dest; // make sure the destinations are matched if (first_match && second_match) { dups.emplace_back(ith_row_op); From 449785c77f3c0cc34213387291a1dbca7c04c0cf Mon Sep 17 00:00:00 2001 From: chinyi0523 Date: Tue, 26 Dec 2023 16:09:28 +0800 Subject: [PATCH 10/20] =?UTF-8?q?=F0=9F=90=9B=20Fix=20starting=20point=20i?= =?UTF-8?q?n=20back=20tracking=20Gaussian=20Elimination=20procedure=20(#45?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/boolean_matrix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/boolean_matrix.cpp b/src/util/boolean_matrix.cpp index 402038909..2626fc5e4 100644 --- a/src/util/boolean_matrix.cpp +++ b/src/util/boolean_matrix.cpp @@ -254,7 +254,7 @@ size_t BooleanMatrix::gaussian_elimination_skip(size_t block_size, bool do_fully auto last = pivots.back(); pivots.pop_back(); - clear_all_1s_in_column(pivots.size() - 1, last, std::views::iota(0u, pivots.size())); + clear_all_1s_in_column(pivots.size(), last, std::views::iota(0u, pivots.size())); if (pivots.empty()) return rank; } From fc29018d26344c5ff7d7d99a3ac306628e4bb66f Mon Sep 17 00:00:00 2001 From: chinyi0523 Date: Tue, 26 Dec 2023 16:52:24 +0800 Subject: [PATCH 11/20] =?UTF-8?q?=F0=9F=90=9B=20Fix=20initializing=20the?= =?UTF-8?q?=20extractor=20by=20not=20removing=20phase=20nodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/extractor/extract.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extractor/extract.cpp b/src/extractor/extract.cpp index 0d96263f6..57f357a21 100644 --- a/src/extractor/extract.cpp +++ b/src/extractor/extract.cpp @@ -771,7 +771,7 @@ void Extractor::update_neighbors() { if (num_boundaries != 2) continue; - if (_graph->get_num_neighbors(f) == 2) { + if (_graph->get_num_neighbors(f) == 2 && f->get_phase() == dvlab::Phase(0)) { // NOTE - Remove for (auto& [b, ep] : _graph->get_neighbors(f)) { if (_graph->get_inputs().contains(b)) { From c2413515e3f822f8d985e4ba2b1892958ffdd3cc Mon Sep 17 00:00:00 2001 From: chinyi0523 Date: Tue, 26 Dec 2023 17:16:53 +0800 Subject: [PATCH 12/20] =?UTF-8?q?=F0=9F=8E=A8=20Lint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/boolean_matrix.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/boolean_matrix.cpp b/src/util/boolean_matrix.cpp index 2626fc5e4..6ef38e234 100644 --- a/src/util/boolean_matrix.cpp +++ b/src/util/boolean_matrix.cpp @@ -282,11 +282,11 @@ size_t BooleanMatrix::filter_duplicate_row_operations() { auto& [row_src, row_dest] = _row_operations[ith_row_op]; auto const first_match = last_used.contains(row_src) && last_used[row_src].row_idx == row_dest && - _row_operations[last_used[row_src].op_idx].first == row_src; // make sure the destinations are matched + _row_operations[last_used[row_src].op_idx].first == row_src; // make sure the destinations are matched auto const second_match = last_used.contains(row_dest) && last_used[row_dest].row_idx == row_src && - _row_operations[last_used[row_dest].op_idx].second == row_dest; // make sure the destinations are matched + _row_operations[last_used[row_dest].op_idx].second == row_dest; // make sure the destinations are matched if (first_match && second_match) { dups.emplace_back(ith_row_op); From 3ee605eea9872a14d74bb056284bba3ab356ce24 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Tue, 26 Dec 2023 22:41:30 +0800 Subject: [PATCH 13/20] :children_crossing: add guards for tensor calculation --- src/convert/qcir_to_tensor.cpp | 57 +++++++++++++++++++------------ src/convert/zxgraph_to_tensor.cpp | 24 ++++++++++--- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/convert/qcir_to_tensor.cpp b/src/convert/qcir_to_tensor.cpp index a586bcc67..955fd0598 100644 --- a/src/convert/qcir_to_tensor.cpp +++ b/src/convert/qcir_to_tensor.cpp @@ -115,12 +115,17 @@ std::optional> to_tensor(QCir const &qcir) { // 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) { - if (stop_requested()) { - spdlog::warn("Conversion interrupted."); - return std::nullopt; + try { + for (size_t i = 0; i < qcir.get_qubits().size(); ++i) { + if (stop_requested()) { + spdlog::warn("Conversion interrupted."); + return std::nullopt; + } + tensor = tensordot(tensor, QTensor::identity(1)); } - tensor = tensordot(tensor, QTensor::identity(1)); + } catch (std::bad_alloc const &e) { + spdlog::error("Memory allocation failed!!"); + return std::nullopt; } Qubit2TensorPinMap qubit2pin; @@ -129,21 +134,26 @@ std::optional> to_tensor(QCir const &qcir) { spdlog::trace(" - Add Qubit {} input port: {}", qcir.get_qubits()[i]->get_id(), 2 * i); } - qcir.topological_traverse([&tensor, &qubit2pin](QCirGate *gate) { - if (stop_requested()) return; - spdlog::debug("Gate {} ({})", gate->get_id(), gate->get_type_str()); - auto tmp = to_tensor(gate); - assert(tmp.has_value()); - std::vector ori_pin; - std::vector new_pin; - for (size_t np = 0; np < gate->get_qubits().size(); np++) { - new_pin.emplace_back(2 * np); - auto const info = gate->get_qubits()[np]; - ori_pin.emplace_back(qubit2pin[info._qubit].second); - } - tensor = tensordot(tensor, *tmp, ori_pin, new_pin); - update_tensor_pin(qubit2pin, gate->get_qubits(), tensor, *tmp); - }); + try { + qcir.topological_traverse([&tensor, &qubit2pin](QCirGate *gate) { + if (stop_requested()) return; + spdlog::debug("Gate {} ({})", gate->get_id(), gate->get_type_str()); + auto tmp = to_tensor(gate); + assert(tmp.has_value()); + std::vector ori_pin; + std::vector new_pin; + for (size_t np = 0; np < gate->get_qubits().size(); np++) { + new_pin.emplace_back(2 * np); + auto const info = gate->get_qubits()[np]; + ori_pin.emplace_back(qubit2pin[info._qubit].second); + } + tensor = tensordot(tensor, *tmp, ori_pin, new_pin); + update_tensor_pin(qubit2pin, gate->get_qubits(), tensor, *tmp); + }); + } catch (std::bad_alloc const &e) { + spdlog::error("Memory allocation failed!!"); + return std::nullopt; + } if (stop_requested()) { spdlog::warn("Conversion interrupted."); @@ -155,7 +165,12 @@ std::optional> to_tensor(QCir const &qcir) { input_pin.emplace_back(qubit2pin[qcir.get_qubits()[i]->get_id()].first); output_pin.emplace_back(qubit2pin[qcir.get_qubits()[i]->get_id()].second); } - tensor = tensor.to_matrix(input_pin, output_pin); + try { + tensor = tensor.to_matrix(input_pin, output_pin); + } catch (std::bad_alloc const &e) { + spdlog::error("Memory allocation failed!!"); + return std::nullopt; + } return tensor; } diff --git a/src/convert/zxgraph_to_tensor.cpp b/src/convert/zxgraph_to_tensor.cpp index 4ba4c10e0..3a54ad5f5 100644 --- a/src/convert/zxgraph_to_tensor.cpp +++ b/src/convert/zxgraph_to_tensor.cpp @@ -109,7 +109,12 @@ std::optional> ZX2TSMapper::map(zx::ZXGraph const& graph return std::nullopt; } - graph.topological_traverse([&graph, this](zx::ZXVertex* v) { _map_one_vertex(graph, v); }); + try { + graph.topological_traverse([&graph, this](zx::ZXVertex* v) { _map_one_vertex(graph, v); }); + } catch (std::bad_alloc& e) { + spdlog::error("Memory allocation failed!!"); + return std::nullopt; + } if (stop_requested()) { spdlog::error("Conversion is interrupted!!"); @@ -117,8 +122,13 @@ std::optional> ZX2TSMapper::map(zx::ZXGraph const& graph } tensor::QTensor result; - for (size_t i = 0; i < _zx2ts_list.size(); ++i) { - result = tensordot(result, _zx2ts_list.tensor(i)); + try { + for (size_t i = 0; i < _zx2ts_list.size(); ++i) { + result = tensordot(result, _zx2ts_list.tensor(i)); + } + } catch (std::bad_alloc& e) { + spdlog::error("Memory allocation failed!!"); + return std::nullopt; } for (size_t i = 0; i < _boundary_edges.size(); ++i) { @@ -130,8 +140,12 @@ std::optional> ZX2TSMapper::map(zx::ZXGraph const& graph spdlog::trace("Input Axis IDs: {}", fmt::join(inputIds, " ")); spdlog::trace("Output Axis IDs: {}", fmt::join(outputIds, " ")); - - result = result.to_matrix(inputIds, outputIds); + try { + result = result.to_matrix(inputIds, outputIds); + } catch (std::bad_alloc& e) { + spdlog::error("Memory allocation failed!!"); + return std::nullopt; + } return result; } From 96fc2533c3188f1cb7ad0e4018b488a03572d702 Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Tue, 26 Dec 2023 22:45:25 +0800 Subject: [PATCH 14/20] :racehorse: improve make lint speed --- scripts/LINT | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/LINT b/scripts/LINT index b41e7dbef..761f9dd4d 100755 --- a/scripts/LINT +++ b/scripts/LINT @@ -11,15 +11,18 @@ format_one() { export -f format_one lint_one() { - clang-tidy "$1" --quiet 2>&1 | grep -v "warnings generated" | grep -v "warning generated" + clang-tidy -p build "$1" --quiet 2>&1 | grep -v -E "warnings? generated" } export -f lint_one FILES=$(find ./src -regex '.*\.[ch]pp' -type f) +CPPS=$(find ./src -regex '.*\.cpp' -type f) # Format all files first to avoid linting errors echo "Formatting files..." parallel -j"$(nproc)" format_one ::: "$FILES" +echo "Generating compile commands..." +cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=1 > /dev/null echo "Linting files..." -parallel -j"$(nproc)" lint_one ::: "$FILES" +parallel -j"$(nproc)" lint_one ::: "$CPPS" echo "Done" From 7abc506626b8e49b6b3470e5353de70fb912e80b Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Tue, 26 Dec 2023 22:57:03 +0800 Subject: [PATCH 15/20] :bug: qsyn does not guard unrecognized options --- src/argparse/arg_parser.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/argparse/arg_parser.cpp b/src/argparse/arg_parser.cpp index 2107daa48..b275b917d 100644 --- a/src/argparse/arg_parser.cpp +++ b/src/argparse/arg_parser.cpp @@ -385,6 +385,9 @@ bool ArgumentParser::_parse_options(TokensSpan tokens) { // among which only the last one can have an subsequent argument. auto const [single_char_options, remainder_token] = _explode_option(token); + if (single_char_options.empty()) { + return true; + } for (auto const& [j, option] : tl::views::enumerate(single_char_options)) { auto& arg = _get_arg(option); @@ -430,6 +433,8 @@ std::pair, std::string> ArgumentParser::_explode_option remainder_token = token.substr(j + 2); } } + fmt::println("single_char_options: {}", fmt::join(single_char_options, ", ")); + fmt::println("remainder_token: {}", remainder_token); return {single_char_options, remainder_token}; } From 2b7d3dc63c154c7f120041ac26c67ec3d57042d8 Mon Sep 17 00:00:00 2001 From: Mu-Te Joshua Lau <71618875+JoshuaLau0220@users.noreply.github.com> Date: Tue, 26 Dec 2023 23:11:41 +0800 Subject: [PATCH 16/20] :bug: fix #42 --- src/util/dvlab_string.hpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/util/dvlab_string.hpp b/src/util/dvlab_string.hpp index 905e7a1e5..075734dbc 100644 --- a/src/util/dvlab_string.hpp +++ b/src/util/dvlab_string.hpp @@ -227,15 +227,21 @@ template requires std::floating_point std::from_chars_result from_chars_wrapper(std::string_view str, T& val) { size_t pos = 0; - if constexpr (std::is_same_v) { - val = std::stof(std::string{str}, &pos); - return {str.data() + pos, std::errc{}}; - } else if constexpr (std::is_same_v) { - val = std::stod(std::string{str}, &pos); - return {str.data() + pos, std::errc{}}; - } else if constexpr (std::is_same_v) { - val = std::stold(std::string{str}, &pos); - return {str.data() + pos, std::errc{}}; + try { + if constexpr (std::is_same_v) { + val = std::stof(std::string{str}, &pos); + return {str.data() + pos, std::errc{}}; + } else if constexpr (std::is_same_v) { + val = std::stod(std::string{str}, &pos); + return {str.data() + pos, std::errc{}}; + } else if constexpr (std::is_same_v) { + val = std::stold(std::string{str}, &pos); + return {str.data() + pos, std::errc{}}; + } + } catch (std::invalid_argument const& e) { + return {str.data(), std::errc::invalid_argument}; + } catch (std::out_of_range const& e) { + return {str.data(), std::errc::result_out_of_range}; } DVLAB_UNREACHABLE("unsupported type"); } From de488707ef2926c96d60982b7f1745e3fc33f6fa Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Tue, 26 Dec 2023 23:37:13 +0800 Subject: [PATCH 17/20] :bug: qsyn panics on printing QCirs with SWAP gates --- src/qcir/qcir.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index 5d76c1cda..21a1f879c 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -318,6 +318,10 @@ QCirGateStatistics QCir::get_gate_statistics() const { stat.h++; stat.clifford++; break; + case GateRotationCategory::swap: + stat.clifford += 3; + stat.twoqubit += 3; + stat.cx += 3; case GateRotationCategory::pz: case GateRotationCategory::rz: if (get_num_qubits() == 1) { From 46cf3cd981ba5f0e53e865c862fe03d9b3a0396c Mon Sep 17 00:00:00 2001 From: JoshuaLau0220 Date: Wed, 27 Dec 2023 00:03:00 +0800 Subject: [PATCH 18/20] =?UTF-8?q?=F0=9F=90=B3=20remove=20unnecessary=20GNU?= =?UTF-8?q?=20parallel=20installation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/env.Dockerfile | 3 +-- docker/prod.Dockerfile | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docker/env.Dockerfile b/docker/env.Dockerfile index af126a7e9..22f2b8887 100644 --- a/docker/env.Dockerfile +++ b/docker/env.Dockerfile @@ -5,5 +5,4 @@ RUN dnf install -y \ clang \ cmake \ lapack-devel \ - openblas-devel \ - parallel + openblas-devel diff --git a/docker/prod.Dockerfile b/docker/prod.Dockerfile index 4e8d7480f..e5251a1f0 100644 --- a/docker/prod.Dockerfile +++ b/docker/prod.Dockerfile @@ -14,8 +14,7 @@ FROM fedora:38 AS runner RUN dnf install -y \ lapack \ openblas-serial \ - libomp \ - parallel + libomp COPY --from=builder /app/build/qsyn /usr/local/bin/qsyn From add47f76aba1c5b0eef738ebb561807e30328111 Mon Sep 17 00:00:00 2001 From: chinyi0523 Date: Wed, 27 Dec 2023 00:10:38 +0800 Subject: [PATCH 19/20] =?UTF-8?q?=F0=9F=90=9B=20Break=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/qcir/qcir.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qcir/qcir.cpp b/src/qcir/qcir.cpp index 21a1f879c..7e6731cfa 100644 --- a/src/qcir/qcir.cpp +++ b/src/qcir/qcir.cpp @@ -322,6 +322,7 @@ QCirGateStatistics QCir::get_gate_statistics() const { stat.clifford += 3; stat.twoqubit += 3; stat.cx += 3; + break; case GateRotationCategory::pz: case GateRotationCategory::rz: if (get_num_qubits() == 1) { From 50b198294d76c032f9fa3b0170ed560ce1e5ba00 Mon Sep 17 00:00:00 2001 From: chinyi0523 Date: Wed, 27 Dec 2023 00:11:47 +0800 Subject: [PATCH 20/20] =?UTF-8?q?=F0=9F=93=8A=20Add=20optimization=20flow?= =?UTF-8?q?=20testcase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/optimization/dof/opt_lv0.dof | 14 +++++++++++++ tests/optimization/dof/opt_lv1.dof | 14 +++++++++++++ tests/optimization/dof/opt_lv2.dof | 14 +++++++++++++ tests/optimization/ref/opt_lv0.log | 32 ++++++++++++++++++++++++++++++ tests/optimization/ref/opt_lv1.log | 32 ++++++++++++++++++++++++++++++ tests/optimization/ref/opt_lv2.log | 32 ++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 tests/optimization/dof/opt_lv0.dof create mode 100644 tests/optimization/dof/opt_lv1.dof create mode 100644 tests/optimization/dof/opt_lv2.dof create mode 100644 tests/optimization/ref/opt_lv0.log create mode 100644 tests/optimization/ref/opt_lv1.log create mode 100644 tests/optimization/ref/opt_lv2.log diff --git a/tests/optimization/dof/opt_lv0.dof b/tests/optimization/dof/opt_lv0.dof new file mode 100644 index 000000000..bac7d4f51 --- /dev/null +++ b/tests/optimization/dof/opt_lv0.dof @@ -0,0 +1,14 @@ +extract config --optimize-level 0 +qcir read benchmark/SABRE/large/cm82a_208.qasm +convert qcir zx +zx optimize +convert zx qcir +qcir optimize +qcir print +convert qcir zx +zx adjoint +zx list +zx compose 0 +zx optimize +zx test --identity +quit -f \ No newline at end of file diff --git a/tests/optimization/dof/opt_lv1.dof b/tests/optimization/dof/opt_lv1.dof new file mode 100644 index 000000000..154aa9c2b --- /dev/null +++ b/tests/optimization/dof/opt_lv1.dof @@ -0,0 +1,14 @@ +extract config --optimize-level 1 +qcir read benchmark/SABRE/large/cm82a_208.qasm +convert qcir zx +zx optimize +convert zx qcir +qcir optimize +qcir print +convert qcir zx +zx adjoint +zx list +zx compose 0 +zx optimize +zx test --identity +quit -f \ No newline at end of file diff --git a/tests/optimization/dof/opt_lv2.dof b/tests/optimization/dof/opt_lv2.dof new file mode 100644 index 000000000..f6a381e38 --- /dev/null +++ b/tests/optimization/dof/opt_lv2.dof @@ -0,0 +1,14 @@ +extract config --optimize-level 2 +qcir read benchmark/SABRE/large/cm82a_208.qasm +convert qcir zx +zx optimize +convert zx qcir +qcir optimize +qcir print +convert qcir zx +zx adjoint +zx list +zx compose 0 +zx optimize +zx test --identity +quit -f diff --git a/tests/optimization/ref/opt_lv0.log b/tests/optimization/ref/opt_lv0.log new file mode 100644 index 000000000..059eefc40 --- /dev/null +++ b/tests/optimization/ref/opt_lv0.log @@ -0,0 +1,32 @@ +qsyn> extract config --optimize-level 0 + +qsyn> qcir read benchmark/SABRE/large/cm82a_208.qasm + +qsyn> convert qcir zx + +qsyn> zx optimize + +qsyn> convert zx qcir + +qsyn> qcir optimize + +qsyn> qcir print +QCir (16 qubits, 404 gates, 231 2-qubits gates, 122 T-gates, 516 depths) + +qsyn> convert qcir zx + +qsyn> zx adjoint + +qsyn> zx list + 0 cm82a_208 QC2ZX ➔ FR +★ 2 cm82a_208 QC2ZX ➔ FR ➔ ZX2QC ➔ Optimize ➔ QC2ZX + +qsyn> zx compose 0 + +qsyn> zx optimize + +qsyn> zx test --identity +The graph is an identity! + +qsyn> quit -f + diff --git a/tests/optimization/ref/opt_lv1.log b/tests/optimization/ref/opt_lv1.log new file mode 100644 index 000000000..47cbac35e --- /dev/null +++ b/tests/optimization/ref/opt_lv1.log @@ -0,0 +1,32 @@ +qsyn> extract config --optimize-level 1 + +qsyn> qcir read benchmark/SABRE/large/cm82a_208.qasm + +qsyn> convert qcir zx + +qsyn> zx optimize + +qsyn> convert zx qcir + +qsyn> qcir optimize + +qsyn> qcir print +QCir (16 qubits, 403 gates, 227 2-qubits gates, 122 T-gates, 509 depths) + +qsyn> convert qcir zx + +qsyn> zx adjoint + +qsyn> zx list + 0 cm82a_208 QC2ZX ➔ FR +★ 2 cm82a_208 QC2ZX ➔ FR ➔ ZX2QC ➔ Optimize ➔ QC2ZX + +qsyn> zx compose 0 + +qsyn> zx optimize + +qsyn> zx test --identity +The graph is an identity! + +qsyn> quit -f + diff --git a/tests/optimization/ref/opt_lv2.log b/tests/optimization/ref/opt_lv2.log new file mode 100644 index 000000000..3f40b0fae --- /dev/null +++ b/tests/optimization/ref/opt_lv2.log @@ -0,0 +1,32 @@ +qsyn> extract config --optimize-level 2 + +qsyn> qcir read benchmark/SABRE/large/cm82a_208.qasm + +qsyn> convert qcir zx + +qsyn> zx optimize + +qsyn> convert zx qcir + +qsyn> qcir optimize + +qsyn> qcir print +QCir (16 qubits, 384 gates, 217 2-qubits gates, 122 T-gates, 477 depths) + +qsyn> convert qcir zx + +qsyn> zx adjoint + +qsyn> zx list + 0 cm82a_208 QC2ZX ➔ FR +★ 2 cm82a_208 QC2ZX ➔ FR ➔ ZX2QC ➔ Optimize ➔ QC2ZX + +qsyn> zx compose 0 + +qsyn> zx optimize + +qsyn> zx test --identity +The graph is an identity! + +qsyn> quit -f +