diff --git a/CMakeLists.txt b/CMakeLists.txt index 785c71da..e2c0ce2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,10 @@ cmake_minimum_required(VERSION 3.16) project(Quartz) - +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) SET(CMAKE_BUILD_TYPE "Release") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") # python set(Python_FIND_VIRTUALENV FIRST) @@ -157,4 +160,5 @@ else() endif() add_subdirectory(src/test) +add_subdirectory(src/wrapper) add_subdirectory(src/benchmark) diff --git a/src/quartz/gate/all_gates.h b/src/quartz/gate/all_gates.h index 55f1d905..2987db19 100644 --- a/src/quartz/gate/all_gates.h +++ b/src/quartz/gate/all_gates.h @@ -1,6 +1,7 @@ #pragma once #include "add.h" +#include "b2.h" #include "ccx.h" #include "ccz.h" #include "ch.h" @@ -36,4 +37,4 @@ #include "u3.h" #include "x.h" #include "y.h" -#include "z.h" +#include "z.h" \ No newline at end of file diff --git a/src/quartz/gate/b2.h b/src/quartz/gate/b2.h new file mode 100644 index 00000000..96a7f499 --- /dev/null +++ b/src/quartz/gate/b2.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../math/matrix.h" +#include "gate.h" + +#include + +namespace quartz { +class B2Gate : public Gate { + public: + B2Gate() : Gate(GateType::b2, 2 /*num_qubits*/, 1 /*num_parameters*/) {} + MatrixBase *get_matrix() override { + assert(false); + auto mat = std::make_unique>( + Matrix<4>({{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}})); + + return mat.get(); + } + bool is_symmetric() const override { return true; } + bool is_sparse() const override { return true; } + bool is_diagonal() const override { return true; } + int get_num_control_qubits() const override { return 1; } +}; + +} // namespace quartz diff --git a/src/quartz/gate/gates.inc.h b/src/quartz/gate/gates.inc.h index bb5d7337..839f7475 100644 --- a/src/quartz/gate/gates.inc.h +++ b/src/quartz/gate/gates.inc.h @@ -36,3 +36,4 @@ PER_GATE(ry3, RY3Gate) PER_GATE(rxx1, RXX1Gate) PER_GATE(rxx3, RXX3Gate) PER_GATE(sx, SXGate) +PER_GATE(b2, B2Gate) \ No newline at end of file diff --git a/src/quartz/tasograph/tasograph.cpp b/src/quartz/tasograph/tasograph.cpp index e3cd1e1d..0d0d47ad 100644 --- a/src/quartz/tasograph/tasograph.cpp +++ b/src/quartz/tasograph/tasograph.cpp @@ -1470,9 +1470,9 @@ Graph::_from_qasm_stream(Context *ctx, while (p < 0) { p += 2 * PI; } - while (p >= 2 * PI) { - p -= 2 * PI; - } + // while (p >= 2 * PI) { + // p -= 2 * PI; + // } auto src_op = graph->add_parameter(p); int src_idx = 0; auto dst_op = op; @@ -1554,6 +1554,72 @@ void Graph::draw_circuit(const std::string &src_file_name, .c_str()); } +std::shared_ptr +Graph::greedy_optimize_with_xfer(const std::vector &xfers, + bool print_message, + std::function cost_function) { + // std::cout << "Number of xfers:" << xfers.size() << std::endl; + + if (cost_function == nullptr) { + cost_function = [](Graph *graph) { return graph->total_cost(); }; + } + + EquivalenceSet eqs; + // Load equivalent dags from file + + auto original_cost = cost_function(this); + auto current_cost = original_cost; + // Get xfers that strictly reduce the cost from the ECC set + + if (print_message) { + std::cout << "greedy_optimize(): Number of xfers that reduce cost: " + << xfers.size() << std::endl; + } + + std::shared_ptr optimized_graph = std::make_shared(*this); + bool optimized_in_this_iteration; + std::vector all_nodes; + optimized_graph->topology_order_ops(all_nodes); + do { + optimized_in_this_iteration = false; + for (auto xfer : xfers) { + bool optimized_this_xfer; + do { + optimized_this_xfer = false; + for (auto const &node : all_nodes) { + auto new_graph = optimized_graph->apply_xfer( + xfer, node, context->has_parameterized_gate()); + if (!new_graph) { + continue; + } + auto new_cost = cost_function(new_graph.get()); + if (new_cost < current_cost) { + current_cost = cost_function(new_graph.get()); + + optimized_graph.swap(new_graph); + // Update the wires after applying a transformation. + all_nodes.clear(); + optimized_graph->topology_order_ops(all_nodes); + optimized_this_xfer = true; + optimized_in_this_iteration = true; + // Since |all_nodes| has changed, we cannot continue this loop. + break; + } + } + } while (optimized_this_xfer); + } + } while (optimized_in_this_iteration); + + auto optimized_cost = cost_function(optimized_graph.get()); + + if (print_message) { + std::cout << "greedy_optimize(): cost optimized from " << original_cost + << " to " << optimized_cost << std::endl; + } + + return optimized_graph; +} + std::shared_ptr Graph::greedy_optimize(Context *ctx, const std::string &equiv_file_name, bool print_message, @@ -1562,7 +1628,6 @@ Graph::greedy_optimize(Context *ctx, const std::string &equiv_file_name, if (cost_function == nullptr) { cost_function = [](Graph *graph) { return graph->total_cost(); }; } - EquivalenceSet eqs; // Load equivalent dags from file if (!eqs.load_json(ctx, equiv_file_name, /*from_verifier=*/false)) { @@ -1876,8 +1941,8 @@ std::shared_ptr Graph::optimize_legacy( .count() / 1000.0 > timeout) { - std::cout << "Timeout. Program terminated. Best cost is " << bestCost - << std::endl; + // std::cout << "Timeout. Program terminated. Best cost is " << bestCost + // << std::endl; bestGraph->constant_and_rotation_elimination(); return bestGraph; } @@ -2088,8 +2153,8 @@ Graph::optimize(const std::vector &xfers, double cost_upper_bound, .count() / 1000.0 > timeout) { - std::cout << "Timeout. Program terminated. Best cost is " << best_cost - << std::endl; + // std::cout << "Timeout. Program terminated. Best cost is " << best_cost + // << std::endl; hit_timeout = true; break; } diff --git a/src/quartz/tasograph/tasograph.h b/src/quartz/tasograph/tasograph.h index c391b676..02dfbd1f 100644 --- a/src/quartz/tasograph/tasograph.h +++ b/src/quartz/tasograph/tasograph.h @@ -217,6 +217,10 @@ class Graph { std::function cost_function = nullptr, const std::string &store_all_steps_file_prefix = std::string()); std::shared_ptr + greedy_optimize_with_xfer(const std::vector &xfers, + bool print_message, + std::function cost_function); + std::shared_ptr optimize_legacy(float alpha, int budget, bool print_subst, Context *ctx, const std::string &equiv_file_name, bool use_simulated_annealing, bool enable_early_stop, diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 99eaf718..c3996d37 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -31,6 +31,7 @@ file(GLOB_RECURSE TEST_FROM_AND_TO_QASM "test_from_and_to_qasm.cpp") file(GLOB_RECURSE TEST_OPTIMIZE "test_optimize.cpp") file(GLOB_RECURSE TEST_CREATE_GRAPHXFER_FROM_QASM "test_create_graphXfer_from_qasm.cpp") file(GLOB_RECURSE TEST_PARTITION "test_partition.cpp") + if(USE_ARBLIB) file(GLOB_RECURSE TEST_ARB "test_arb.cpp") endif() diff --git a/src/wrapper/CMakeLists.txt b/src/wrapper/CMakeLists.txt new file mode 100644 index 00000000..7805d6e0 --- /dev/null +++ b/src/wrapper/CMakeLists.txt @@ -0,0 +1,36 @@ +include_directories(${CMAKE_INCLUDE_PATH}) +include_directories("..") + +link_directories("../..") + +file(GLOB_RECURSE WRAPPER_RPC "rpc/wrapper_rpc.cpp") +file(GLOB_RECURSE TEST_RPC "rpc/test_rpc.cpp") +file(GLOB_RECURSE TEST_ORACLE "test_oracle.cpp") +file(GLOB_RECURSE ORACLE "oracle.cpp") + +add_library(oracle SHARED ${ORACLE}) +target_link_libraries(oracle quartz_runtime) + +add_executable(wrapper_rpc ${WRAPPER_RPC}) +add_executable(test_rpc ${TEST_RPC}) +add_executable(test_oracle ${TEST_ORACLE}) + +target_link_libraries(wrapper_rpc oracle) +target_link_libraries(wrapper_rpc quartz_runtime) + +target_link_libraries(test_rpc oracle) +target_link_libraries(test_rpc quartz_runtime) + +target_link_libraries(test_oracle oracle) +target_link_libraries(test_oracle quartz_runtime) + +cmake_path(SET LIBRPC_PATH "$ENV{LIBRPC_PATH}") +cmake_path(GET LIBRPC_PATH PARENT_PATH LIBRPC_PARENT_PATH) +cmake_path(GET LIBRPC_PARENT_PATH PARENT_PATH LIBRPC_PARENT_PARENT_PATH) +cmake_path(SET LIBRPC_INCLUDE_DIR "${LIBRPC_PARENT_PARENT_PATH}/include") +include_directories(${LIBRPC_INCLUDE_DIR}) + +add_library(librpc STATIC IMPORTED) +set_property(TARGET librpc PROPERTY IMPORTED_LOCATION $ENV{LIBRPC_PATH}) +target_link_libraries(wrapper_rpc librpc) +target_link_libraries(test_rpc librpc) diff --git a/src/wrapper/oracle.cpp b/src/wrapper/oracle.cpp new file mode 100644 index 00000000..0a643c20 --- /dev/null +++ b/src/wrapper/oracle.cpp @@ -0,0 +1,107 @@ +#include "oracle.h" + +using namespace quartz; +std::shared_ptr get_context_(const std::string gate_set, + int n_qubits, + const std::string ecc_path) { + auto super_context = + std::make_shared(gate_set, n_qubits, ecc_path); + return super_context; +} + +std::string optimize_(std::string circ_string, std::string cost_func, + std::string timeout_type, float timeout_value, + std::shared_ptr super_context + +) { + auto graph = Graph::from_qasm_str(&super_context->ctx, circ_string); + + std::function cost_function; + std::vector greedy_xfers; + if (cost_func == "Gate") { + cost_function = [](Graph *graph) { return graph->total_cost(); }; + greedy_xfers = super_context->xfers_greedy_gate; + } else if (cost_func == "Depth") { + cost_function = [](Graph *graph) { return graph->circuit_depth(); }; + greedy_xfers = super_context->xfers_greedy_gate; + + } else if (cost_func == "Mixed") { + cost_function = [](Graph *graph) { + return 10 * graph->circuit_depth() + graph->total_cost(); + }; + greedy_xfers = super_context->xfers_greedy_gate; + + } else { + std::cout << "Invalid cost function." << std::endl; + assert(false); + } + float timeout = 0; + if (timeout_type == "PerSegment") { + timeout = timeout_value; + } else if (timeout_type == "PerGate") { + timeout = timeout_value * graph->total_cost(); + } else { + std::cout << "Invalid timeout type." << std::endl; + assert(false); + } + + float init_cost = cost_function(graph.get()); + auto start = std::chrono::steady_clock::now(); + auto graph_after_greedy = + graph->greedy_optimize_with_xfer(greedy_xfers, false, cost_function); + + auto end = std::chrono::steady_clock::now(); + double remaining_time = + timeout - + std::chrono::duration_cast(end - start) + .count() / + 1000.0; + if (remaining_time < 0.01) { + return graph_after_greedy->to_qasm(false, false); + } else { + auto graph_after_search = graph_after_greedy->optimize( + super_context->xfers, init_cost * 1.05, "barenco_tof_3", "", false, + cost_function, remaining_time); + return graph_after_search->to_qasm(false, false); + } +} +std::string clifford_decomposition_(std::string circ) { + auto param_info = ParamInfo(0); + + Context src_ctx(std::vector{GateType::h, GateType::ccz, GateType::x, + GateType::cx, GateType::add, + GateType::s, GateType::sdg, GateType::t, + GateType::tdg, GateType::input_qubit, + GateType::input_param, GateType::rz}, + 3, ¶m_info); + Context dst_ctx({GateType::h, GateType::x, GateType::rz, GateType::add, + GateType::cx, GateType::input_qubit, GateType::input_param}, + 3, ¶m_info); + + auto union_ctx = union_contexts(&src_ctx, &dst_ctx); + + auto graph = Graph::from_qasm_str(&src_ctx, circ); + + std::shared_ptr newGraph; + + auto xfer_pair = GraphXfer::ccz_cx_rz_xfer(&src_ctx, &dst_ctx, &union_ctx); + + newGraph = graph->toffoli_flip_greedy(GateType::rz, xfer_pair.first, + xfer_pair.second); + return newGraph->to_qasm(false, false); +} + +std::string rotation_merging_(std::string circ_string) + +{ + auto param_info = ParamInfo(0); + Context src_ctx(std::vector{GateType::h, GateType::x, GateType::rz, + GateType::add, GateType::cx, + GateType::input_qubit, + GateType::input_param}, + 3, ¶m_info); + + auto graph = Graph::from_qasm_str(&src_ctx, circ_string); + graph->rotation_merging(GateType::rz); + return graph->to_qasm(false, false); +} \ No newline at end of file diff --git a/src/wrapper/oracle.h b/src/wrapper/oracle.h new file mode 100644 index 00000000..4f6d4d63 --- /dev/null +++ b/src/wrapper/oracle.h @@ -0,0 +1,99 @@ +#include "quartz/tasograph/substitution.h" +#include "quartz/tasograph/tasograph.h" + +#include +#include +#include +using namespace quartz; + +class SuperContext { + public: + ParamInfo param_info; + Context ctx; + std::vector xfers; + std::vector xfers_greedy_gate; + + static Context createContext(const std::string &gate_set, int n_qubits, + ParamInfo ¶m_info) { + std::vector gates; + if (gate_set == "Nam") { + gates = {GateType::input_qubit, GateType::input_param, GateType::cx, + GateType::h, GateType::rz, GateType::x, + GateType::add}; + } else if (gate_set == "CliffordT") { + gates = {GateType::input_qubit, GateType::input_param, GateType::h, + GateType::x, GateType::t, GateType::tdg, + GateType::s, GateType::sdg, GateType::z, + GateType::cx, GateType::add}; + } else if (gate_set == "Nam_B") { + gates = { + GateType::input_qubit, GateType::input_param, GateType::cx, + GateType::h, GateType::rz, GateType::x, + GateType::add, GateType::b2, + }; + } else { + std::cerr << "Invalid gate set." << std::endl; + assert(false); + } + + return Context(gates, n_qubits, ¶m_info); + } + SuperContext(const std::string &gate_set, int n_qubits, + const std::string &ecc_path) + : param_info(0), ctx(createContext(gate_set, n_qubits, param_info)) { + xfers = std::vector(); + xfers_greedy_gate = std::vector(); + EquivalenceSet eqs; + if (!eqs.load_json(&ctx, ecc_path, false)) { + std::cout << "Failed to load equivalence file." << std::endl; + assert(false); + } + int n_xfer_1q = 0; + auto ecc = eqs.get_all_equivalence_sets(); + for (auto &eqcs : ecc) { + for (auto &circ_0 : eqcs) { + for (auto &circ_1 : eqcs) { + if (circ_0 != circ_1) { + if (circ_0->get_num_qubits() == 1 && + circ_1->get_num_qubits() == 1) { + n_xfer_1q++; + } + auto xfer = GraphXfer::create_GraphXfer(&ctx, circ_0, circ_1, true); + if (xfer != nullptr) { + xfers.push_back(xfer); + } + } + } + } + } + // Only representatives. + // std::cout << "Number of 1q xfers: " << n_xfer_1q << std::endl; + // std::cout << "Number of xfers: " << xfers.size() << std::endl; + + for (auto &eqcs : ecc) { + for (auto &circ_0 : eqcs) { + for (auto &circ_1 : eqcs) { + if (circ_0->get_num_gates() < circ_1->get_num_gates()) { + auto xfer = GraphXfer::create_GraphXfer(&ctx, circ_1, circ_0, true); + if (xfer != nullptr) { + xfers_greedy_gate.push_back(xfer); + } + } + } + } + } + }; + + ~SuperContext() = default; +}; + +std::shared_ptr get_context_(const std::string gate_set, + int n_qubits, + const std::string ecc_path); +std::string optimize_(std::string circ_string, std::string cost_func, + std::string timeout_type, float timeout_value, + std::shared_ptr super_context + +); +std::string rotation_merging_(std::string circ_string); +std::string clifford_decomposition_(std::string circ); \ No newline at end of file diff --git a/src/wrapper/rpc/test_rpc.cpp b/src/wrapper/rpc/test_rpc.cpp new file mode 100644 index 00000000..2f4b8cfb --- /dev/null +++ b/src/wrapper/rpc/test_rpc.cpp @@ -0,0 +1,11 @@ +#include "quartz/tasograph/substitution.h" +#include "quartz/tasograph/tasograph.h" +#include "wrapper/oracle.h" +using namespace quartz; + +int main() { + auto supercontext = + get_context_("Nam", 48, "resources/Nam_4_3_complete_ECC_set.json"); + + return 0; +} \ No newline at end of file diff --git a/src/wrapper/rpc/wrapper_rpc.cpp b/src/wrapper/rpc/wrapper_rpc.cpp new file mode 100644 index 00000000..e7744646 --- /dev/null +++ b/src/wrapper/rpc/wrapper_rpc.cpp @@ -0,0 +1,38 @@ +#include "quartz/tasograph/substitution.h" +#include "quartz/tasograph/tasograph.h" +#include "rpc/server.h" +#include "wrapper/oracle.h" + +#include +using namespace quartz; + +int main(int argc, char **argv) { + if (argc != 7) { + std::cerr << "Usage: " << argv[0] + << " " + " "; + return 1; + } + int port = atoi(argv[1]); + std::string optimization_gateset = argv[2]; + std::string eccfile = argv[3]; + std::string cost = argv[4]; + std::string timeout_type = argv[5]; + float timeout_value = atof(argv[6]); + rpc::server srv(port); + + auto supercontext = get_context_(optimization_gateset, 1, eccfile); + srv.bind("optimize", [supercontext, cost, timeout_type, + timeout_value](std::string my_circ) { + return optimize_(my_circ, cost, timeout_type, timeout_value, supercontext); + }); + srv.bind("rotation_merging", + [](std::string my_circ) { return rotation_merging_(my_circ); }); + srv.bind("clifford_decomposition", [](std::string my_circ) { + return clifford_decomposition_(my_circ); + }); + // std::cout << "Server started on port " << port << std::endl; + srv.run(); + + return 0; +} \ No newline at end of file diff --git a/src/wrapper/rust/wrapper_rust.cpp b/src/wrapper/rust/wrapper_rust.cpp new file mode 100644 index 00000000..459614af --- /dev/null +++ b/src/wrapper/rust/wrapper_rust.cpp @@ -0,0 +1,14 @@ +#include "wrapper.h" + +#include +rust::String optimize(rust::String circ_string, rust::String cost_func, + float timeout, + std::shared_ptr super_context) { + return optimize_(std::string(circ_string), std::string(cost_func), timeout, + super_context); +} + +std::shared_ptr get_context(rust::String gate_set, int n_qubits, + rust::String ecc_path) { + return get_context_(std::string(gate_set), n_qubits, std::string(ecc_path)); +} \ No newline at end of file diff --git a/src/wrapper/rust/wrapper_rust.h b/src/wrapper/rust/wrapper_rust.h new file mode 100644 index 00000000..0ad95ba1 --- /dev/null +++ b/src/wrapper/rust/wrapper_rust.h @@ -0,0 +1,8 @@ +#include "oracle.h" +#include "rust/cxx.h" +rust::String optimize(rust::String circ_string, rust::String cost_func, + float timeout, + std::shared_ptr super_context); + +std::shared_ptr get_context(rust::String gate_set, int n_qubits, + rust::String ecc_path); \ No newline at end of file diff --git a/src/wrapper/test_oracle.cpp b/src/wrapper/test_oracle.cpp new file mode 100644 index 00000000..00e1421b --- /dev/null +++ b/src/wrapper/test_oracle.cpp @@ -0,0 +1,29 @@ +#include "quartz/tasograph/substitution.h" +#include "quartz/tasograph/tasograph.h" +#include "wrapper/oracle.h" + +#include +using namespace quartz; + +int main(int argc, char **argv) { + if (argc != 7) { + std::cerr << "Usage: " << argv[0] + << " " + " "; + return 1; + } + std::string optimization_gateset = argv[2]; + std::string eccfile = argv[3]; + std::string cost = argv[4]; + std::string timeout_type = argv[5]; + std::string circ_path = argv[1]; + std::ifstream circ_file(circ_path); + std::string my_circ((std::istreambuf_iterator(circ_file)), + std::istreambuf_iterator()); + float timeout_value = atof(argv[6]); + + auto supercontext = get_context_(optimization_gateset, 1, eccfile); + std::string new_circ = + optimize_(my_circ, cost, timeout_type, timeout_value, supercontext); + std::cout << new_circ << std::endl; +} \ No newline at end of file