diff --git a/python/quartz/_cython/CCore.pxd b/python/quartz/_cython/CCore.pxd index 7122f08c..e2cddf25 100644 --- a/python/quartz/_cython/CCore.pxd +++ b/python/quartz/_cython/CCore.pxd @@ -62,9 +62,15 @@ cdef extern from "gate/gate.h" namespace "quartz": GateType tp int num_qubits, num_parameters +cdef extern from "context/param_info.h" namespace "quartz": + cdef cppclass ParamInfo: + ParamInfo() except + + +ctypedef ParamInfo* ParamInfo_ptr + cdef extern from "context/context.h" namespace "quartz": cdef cppclass Context: - Context(const vector[GateType]) except + + Context(const vector[GateType], ParamInfo_ptr) except + size_t next_global_unique_id() bool has_parameterized_gate() const diff --git a/python/quartz/_cython/core.pyx b/python/quartz/_cython/core.pyx index 8f4a0ff4..ca4afc79 100644 --- a/python/quartz/_cython/core.pyx +++ b/python/quartz/_cython/core.pyx @@ -11,6 +11,7 @@ from CCore cimport ( Graph, GraphXfer, Op, + ParamInfo, QASMParser, ) from cython.operator cimport dereference as deref @@ -228,6 +229,7 @@ cdef class PyXfer: return self.graphXfer.dst_str().decode('utf-8') cdef class QuartzContext: + cdef ParamInfo *param_info cdef Context *context cdef EquivalenceSet *eqs cdef vector[GraphXfer *] v_xfers @@ -241,7 +243,8 @@ cdef class QuartzContext: gate_type_list.append(GateType.input_param) if GateType.input_qubit not in gate_type_list: gate_type_list.append(GateType.input_qubit) - self.context = new Context(gate_type_list) + self.param_info = new ParamInfo() + self.context = new Context(gate_type_list, self.param_info) self.eqs = new EquivalenceSet() self.load_json(filename) diff --git a/src/benchmark/dp.cpp b/src/benchmark/dp.cpp index a9d599f5..e4d22bb3 100644 --- a/src/benchmark/dp.cpp +++ b/src/benchmark/dp.cpp @@ -16,10 +16,12 @@ int main() { auto start = std::chrono::steady_clock::now(); init_python_interpreter(); PythonInterpreter interpreter; + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, GateType::x, GateType::ry, GateType::u2, GateType::u3, GateType::cx, GateType::cz, GateType::cp, GateType::swap, - GateType::rz, GateType::p, GateType::ccx, GateType::rx}); + GateType::rz, GateType::p, GateType::ccx, GateType::rx}, + ¶m_info); std::vector circuit_names = { "ae", "dj", "ghz", "graphstate", "qft", "qftentangled", "qpeexact", "su2random", "wstate"}; diff --git a/src/benchmark/ilp_num_stages.cpp b/src/benchmark/ilp_num_stages.cpp index 8dfe41a0..d1aa90e9 100644 --- a/src/benchmark/ilp_num_stages.cpp +++ b/src/benchmark/ilp_num_stages.cpp @@ -145,10 +145,12 @@ int main() { auto start = std::chrono::steady_clock::now(); init_python_interpreter(); PythonInterpreter interpreter; + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, GateType::x, GateType::ry, GateType::u2, GateType::u3, GateType::cx, GateType::cz, GateType::cp, GateType::swap, - GateType::rz, GateType::p, GateType::ccx, GateType::rx}); + GateType::rz, GateType::p, GateType::ccx, GateType::rx}, + ¶m_info); std::vector circuit_names = {"ae", "dj", "ghz", "graphstate", "qft", "qpeexact", "su2random", "wstate"}; diff --git a/src/benchmark/nam_middle_circuits.cpp b/src/benchmark/nam_middle_circuits.cpp index bb6c0081..ed55ad21 100644 --- a/src/benchmark/nam_middle_circuits.cpp +++ b/src/benchmark/nam_middle_circuits.cpp @@ -19,10 +19,13 @@ static std::stringstream summary_result; void benchmark_nam(const std::string &circuit_name) { std::string circuit_path = "circuit/nam-benchmarks/" + circuit_name + ".qasm"; // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::x, GateType::cx, - GateType::input_qubit, GateType::input_param}); + GateType::add, GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::h, GateType::x, GateType::rz, GateType::add, - GateType::cx, GateType::input_qubit, GateType::input_param}); + GateType::cx, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); auto xfer_pair = GraphXfer::ccz_cx_rz_xfer(&union_ctx); diff --git a/src/benchmark/nam_small_circuits.cpp b/src/benchmark/nam_small_circuits.cpp index 08cba1ae..bd54e957 100644 --- a/src/benchmark/nam_small_circuits.cpp +++ b/src/benchmark/nam_small_circuits.cpp @@ -19,10 +19,13 @@ static std::stringstream summary_result; void benchmark_nam(const std::string &circuit_name) { std::string circuit_path = "circuit/nam-benchmarks/" + circuit_name + ".qasm"; // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::x, GateType::cx, - GateType::input_qubit, GateType::input_param}); + GateType::add, GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::h, GateType::x, GateType::rz, GateType::add, - GateType::cx, GateType::input_qubit, GateType::input_param}); + GateType::cx, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); auto xfer_pair = GraphXfer::ccz_cx_rz_xfer(&union_ctx); diff --git a/src/quartz/circuitseq/circuitseq.cpp b/src/quartz/circuitseq/circuitseq.cpp index cd78a732..70fcd597 100644 --- a/src/quartz/circuitseq/circuitseq.cpp +++ b/src/quartz/circuitseq/circuitseq.cpp @@ -560,13 +560,13 @@ CircuitSeqHashType CircuitSeq::hash(Context *ctx) { if (hash_value_valid_) { return hash_value_; } - const Vector &input_dis = ctx->get_generated_input_dis(get_num_qubits()); + const Vector &input_dis = ctx->get_and_gen_input_dis(get_num_qubits()); Vector output_dis; auto input_parameters = ctx->get_all_generated_parameters(); auto all_parameters = ctx->compute_parameters(input_parameters); evaluate(input_dis, all_parameters, output_dis); ComplexType dot_product = - output_dis.dot(ctx->get_generated_hashing_dis(get_num_qubits())); + output_dis.dot(ctx->get_and_gen_hashing_dis(get_num_qubits())); original_fingerprint_ = dot_product; diff --git a/src/quartz/context/context.cpp b/src/quartz/context/context.cpp index 0377d46f..d0e1239a 100644 --- a/src/quartz/context/context.cpp +++ b/src/quartz/context/context.cpp @@ -5,14 +5,16 @@ #include "quartz/utils/string_utils.h" #include -#include #include #include #include +#include namespace quartz { -Context::Context(const std::vector &supported_gates) - : global_unique_id(16384), supported_gates_(supported_gates) { +Context::Context(const std::vector &supported_gates, + ParamInfo *param_info) + : global_unique_id(16384), supported_gates_(supported_gates), + param_info_(param_info) { gates_.reserve(supported_gates.size()); for (const auto &gate : supported_gates) { insert_gate(gate); @@ -25,13 +27,9 @@ Context::Context(const std::vector &supported_gates) } Context::Context(const std::vector &supported_gates, int num_qubits, - int num_input_symbolic_params) - : Context(supported_gates) { + ParamInfo *param_info) + : Context(supported_gates, param_info) { gen_input_and_hashing_dis(num_qubits); - gen_random_parameters(num_input_symbolic_params); - for (int i = 0; i < num_input_symbolic_params; i++) { - get_new_param_id(); - } generate_parameter_expressions(); } @@ -41,10 +39,6 @@ size_t Context::next_global_unique_id(void) { return global_unique_id++; } -void Context::set_generated_parameter(int id, ParamType param) { - random_parameters_[id] = param; -} - Gate *Context::get_gate(GateType tp) { const auto it = gates_.find(tp); if (it != gates_.end()) @@ -57,6 +51,10 @@ Gate *Context::get_gate(GateType tp) { } } +bool Context::has_gate(GateType tp) const { + return gates_.find(tp) != gates_.end(); +} + Gate *Context::get_general_controlled_gate(GateType tp, const std::vector &state) { auto it1 = general_controlled_gates_.find(tp); @@ -103,17 +101,6 @@ bool Context::insert_gate(GateType tp) { return true; } -void Context::gen_random_parameters(const int num_params) { - assert(num_params >= 0); - if (random_parameters_.size() < num_params) { - static ParamType pi = std::acos((ParamType)-1.0); - static std::uniform_real_distribution dis_real(-pi, pi); - while (random_parameters_.size() < num_params) { - random_parameters_.emplace_back(dis_real(gen)); - } - } -} - const std::vector &Context::get_supported_gates() const { return supported_gates_; } @@ -153,6 +140,11 @@ const Vector &Context::get_generated_input_dis(int num_qubits) const { } } +const Vector &Context::get_and_gen_input_dis(int num_qubits) { + gen_input_and_hashing_dis(num_qubits); + return get_generated_input_dis(num_qubits); +} + const Vector &Context::get_generated_hashing_dis(int num_qubits) const { if (0 <= num_qubits && num_qubits < random_hashing_distribution_.size()) return random_hashing_distribution_[num_qubits]; @@ -169,8 +161,13 @@ const Vector &Context::get_generated_hashing_dis(int num_qubits) const { } } +const Vector &Context::get_and_gen_hashing_dis(int num_qubits) { + gen_input_and_hashing_dis(num_qubits); + return get_generated_hashing_dis(num_qubits); +} + std::vector Context::get_all_generated_parameters() const { - return random_parameters_; + return param_info_->get_all_generated_parameters(); } void Context::set_representative(std::unique_ptr seq) { @@ -194,132 +191,60 @@ bool Context::get_possible_representative(const CircuitSeqHashType &hash_value, } ParamType Context::get_param_value(int id) const { - assert(id >= 0 && id < (int)parameter_values_.size()); - assert(!is_parameter_symbolic_[id]); - return parameter_values_[id]; + return param_info_->get_param_value(id); } void Context::set_param_value(int id, const ParamType ¶m) { - assert(id >= 0 && id < (int)is_parameter_symbolic_.size()); - assert(!is_parameter_symbolic_[id]); - while (id >= (int)parameter_values_.size()) { - parameter_values_.emplace_back(); - } - parameter_values_[id] = param; + return param_info_->set_param_value(id, param); } std::vector Context::get_all_input_param_values() const { - return parameter_values_; + return param_info_->get_all_input_param_values(); } int Context::get_new_param_id(const ParamType ¶m) { - int id = (int)is_parameter_symbolic_.size(); - is_parameter_symbolic_.push_back(false); - auto wire = std::make_unique(); - wire->type = CircuitWire::input_param; - wire->index = id; - parameter_wires_.push_back(std::move(wire)); - set_param_value(id, param); - return id; -} - -int Context::get_new_param_id() { - int id = (int)is_parameter_symbolic_.size(); - is_parameter_symbolic_.push_back(true); - auto wire = std::make_unique(); - wire->type = CircuitWire::input_param; - wire->index = id; - parameter_wires_.push_back(std::move(wire)); - return id; + return param_info_->get_new_param_id(param); } +int Context::get_new_param_id() { return param_info_->get_new_param_id(); } + int Context::get_new_param_expression_id( const std::vector ¶meter_indices, Gate *op) { - bool is_symbolic = false; - for (auto &input_id : parameter_indices) { - assert(input_id >= 0 && input_id < (int)is_parameter_symbolic_.size()); - if (param_is_symbolic(input_id)) { - is_symbolic = true; - } - } - if (!is_symbolic) { - // A concrete parameter, no need to create an expression. - // Compute the value directly. - std::vector input_params; - input_params.reserve(parameter_indices.size()); - for (auto &input_id : parameter_indices) { - input_params.push_back(get_param_value(input_id)); - } - return get_new_param_id(op->compute(input_params)); - } - int id = (int)is_parameter_symbolic_.size(); - is_parameter_symbolic_.push_back(true); - auto circuit_gate = std::make_unique(); - circuit_gate->gate = op; - for (auto &input_id : parameter_indices) { - circuit_gate->input_wires.push_back(parameter_wires_[input_id].get()); - parameter_wires_[input_id]->output_gates.push_back(circuit_gate.get()); - } - auto wire = std::make_unique(); - wire->type = CircuitWire::internal_param; - wire->index = id; - wire->input_gates.push_back(circuit_gate.get()); - circuit_gate->output_wires.push_back(wire.get()); - parameter_wires_.push_back(std::move(wire)); - parameter_expressions_.push_back(std::move(circuit_gate)); - return id; + return param_info_->get_new_param_expression_id(parameter_indices, op); } int Context::get_num_parameters() const { - return (int)is_parameter_symbolic_.size(); + return param_info_->get_num_parameters(); } int Context::get_num_input_symbolic_parameters() const { - return (int)random_parameters_.size(); + return param_info_->get_num_input_symbolic_parameters(); } bool Context::param_is_symbolic(int id) const { - return id >= 0 && id < (int)is_parameter_symbolic_.size() && - is_parameter_symbolic_[id]; + return param_info_->param_is_symbolic(id); } bool Context::param_has_value(int id) const { - return id >= 0 && id < (int)is_parameter_symbolic_.size() && - !is_parameter_symbolic_[id]; + return param_info_->param_has_value(id); } bool Context::param_is_expression(int id) const { - return id >= 0 && id < (int)parameter_wires_.size() && - !parameter_wires_[id]->input_gates.empty(); + return param_info_->param_is_expression(id); } CircuitWire *Context::get_param_wire(int id) const { - if (id >= 0 && id < (int)parameter_wires_.size()) { - return parameter_wires_[id].get(); - } else { - return nullptr; // out of range - } + return param_info_->get_param_wire(id); } std::vector Context::compute_parameters(const std::vector &input_parameters) { - auto result = input_parameters; - result.resize(is_parameter_symbolic_.size()); - for (auto &expr : parameter_expressions_) { - std::vector params; - for (const auto &input_wire : expr->input_wires) { - params.push_back(result[input_wire->index]); - } - assert(expr->output_wires.size() == 1); - const auto &output_wire = expr->output_wires[0]; - result[output_wire->index] = expr->gate->compute(params); - } - return result; + return param_info_->compute_parameters(input_parameters); } std::vector Context::get_param_permutation( const std::vector &input_param_permutation) { - int num_parameters = (int)is_parameter_symbolic_.size(); + int num_parameters = (int)param_info_->is_parameter_symbolic_.size(); std::vector result = input_param_permutation; result.resize(num_parameters, -1); // fill with -1 for (int i = (int)input_param_permutation.size(); i < num_parameters; i++) { @@ -365,8 +290,15 @@ std::vector Context::get_param_permutation( void Context::generate_parameter_expressions( int max_num_operators_per_expression) { assert(max_num_operators_per_expression == 1); - int num_input_parameters = (int)is_parameter_symbolic_.size(); + int num_input_parameters = (int)param_info_->is_parameter_symbolic_.size(); assert(num_input_parameters > 0); + if (!param_info_->parameter_expressions_.empty()) { + std::cerr << "Context::generate_parameter_expressions() called twice for a " + "single ParamInfo object. Please use different ParamInfo " + "objects for different Context objects." + << std::endl; + assert(false); + } for (const auto &idx : get_supported_parameter_ops()) { Gate *op = get_gate(idx); if (op->get_num_parameters() == 1) { @@ -397,40 +329,27 @@ void Context::generate_parameter_expressions( } std::vector Context::get_param_masks() const { - std::vector param_mask(is_parameter_symbolic_.size()); - for (int i = 0; i < (int)param_mask.size(); i++) { - if (!param_is_expression(i)) { - param_mask[i] = ((InputParamMaskType)1) << i; - } - } - for (auto &expr : parameter_expressions_) { - const auto &output_wire = expr->output_wires[0]; - param_mask[output_wire->index] = 0; - for (const auto &input_wire : expr->input_wires) { - param_mask[output_wire->index] |= param_mask[input_wire->index]; - } - } - return param_mask; + return param_info_->get_param_masks(); } std::string Context::param_info_to_json() const { std::string result = "["; result += "["; - result += std::to_string(is_parameter_symbolic_.size()); - for (int i = 0; i < (int)is_parameter_symbolic_.size(); i++) { + result += std::to_string(param_info_->is_parameter_symbolic_.size()); + for (int i = 0; i < (int)param_info_->is_parameter_symbolic_.size(); i++) { result += ", "; if (param_is_expression(i)) { - result += parameter_wires_[i]->input_gates[0]->to_json(); - } else if (is_parameter_symbolic_[i]) { + result += param_info_->parameter_wires_[i]->input_gates[0]->to_json(); + } else if (param_info_->is_parameter_symbolic_[i]) { result += "\"\""; } else { - result += - to_string_with_precision(parameter_values_[i], /*precision=*/17); + result += to_string_with_precision(param_info_->parameter_values_[i], + /*precision=*/17); } } result += "], "; - result += - to_json_style_string_with_precision(random_parameters_, /*precision=*/17); + result += to_json_style_string_with_precision(param_info_->random_parameters_, + /*precision=*/17); result += "]"; return result; } @@ -440,13 +359,13 @@ void Context::load_param_info_from_json(std::istream &fin) { fin.ignore(std::numeric_limits::max(), '['); int num_params; fin >> num_params; - is_parameter_symbolic_.clear(); - is_parameter_symbolic_.reserve(num_params); - parameter_wires_.clear(); - parameter_wires_.reserve(num_params); - parameter_values_.clear(); - parameter_values_.reserve(num_params); - parameter_expressions_.clear(); + param_info_->is_parameter_symbolic_.clear(); + param_info_->is_parameter_symbolic_.reserve(num_params); + param_info_->parameter_wires_.clear(); + param_info_->parameter_wires_.reserve(num_params); + param_info_->parameter_values_.clear(); + param_info_->parameter_values_.reserve(num_params); + param_info_->parameter_expressions_.clear(); for (int i = 0; i < num_params; i++) { char ch; fin >> ch; @@ -479,10 +398,12 @@ void Context::load_param_info_from_json(std::istream &fin) { } } fin.ignore(std::numeric_limits::max(), ','); - bool ret = read_json_style_vector(fin, random_parameters_); + bool ret = read_json_style_vector(fin, param_info_->random_parameters_); assert(ret); } +ParamInfo *Context::get_param_info() const { return param_info_; } + double Context::random_number() { static std::uniform_real_distribution dis_real(0, 1); return dis_real(gen); @@ -497,6 +418,7 @@ bool Context::has_parameterized_gate() const { } Context union_contexts(Context *ctx_0, Context *ctx_1) { + assert(ctx_0->get_param_info() == ctx_1->get_param_info()); std::vector union_vector; std::set gate_set_0(ctx_0->get_supported_gates().begin(), ctx_0->get_supported_gates().end()); @@ -509,7 +431,7 @@ Context union_contexts(Context *ctx_0, Context *ctx_1) { union_vector.push_back(tp); } } - return Context(union_vector); + return Context(union_vector, ctx_0->get_param_info()); } } // namespace quartz diff --git a/src/quartz/context/context.h b/src/quartz/context/context.h index e4aa3676..7f652b8a 100644 --- a/src/quartz/context/context.h +++ b/src/quartz/context/context.h @@ -1,6 +1,7 @@ #pragma once #include "quartz/circuitseq/circuitwire.h" +#include "quartz/context/param_info.h" #include "quartz/gate/gate_utils.h" #include "quartz/math/vector.h" #include "quartz/utils/utils.h" @@ -8,7 +9,6 @@ #include #include #include -#include #include #include @@ -19,18 +19,22 @@ class CircuitGate; class Context { public: /** - * A constructor without parameters. + * A simple constructor, should be used when the generator is not needed. + * - Does not generate the random testing values. + * - Does not initialize the ParamInfo object for the generator. */ - explicit Context(const std::vector &supported_gates); + explicit Context(const std::vector &supported_gates, + ParamInfo *param_info); /** - * Generates the random testing values for 2^|num_qubits| coefficients - * and |num_input_symbolic_params| parameters. - * The constructor then calls |generate_parameter_expressions()|. + * Generates the random testing values for 2^|num_qubits| coefficients. + * The constructor then calls |generate_parameter_expressions()|, + * which initializes the ParamInfo object for the generator. */ Context(const std::vector &supported_gates, int num_qubits, - int num_input_symbolic_params); + ParamInfo *param_info); Gate *get_gate(GateType tp); + [[nodiscard]] bool has_gate(GateType tp) const; Gate *get_general_controlled_gate(GateType tp, const std::vector &state); [[nodiscard]] const std::vector &get_supported_gates() const; @@ -43,15 +47,14 @@ class Context { */ void gen_input_and_hashing_dis(int num_qubits); [[nodiscard]] const Vector &get_generated_input_dis(int num_qubits) const; + [[nodiscard]] const Vector &get_and_gen_input_dis(int num_qubits); [[nodiscard]] const Vector &get_generated_hashing_dis(int num_qubits) const; + [[nodiscard]] const Vector &get_and_gen_hashing_dis(int num_qubits); [[nodiscard]] std::vector get_all_generated_parameters() const; size_t next_global_unique_id(); [[nodiscard]] bool has_parameterized_gate() const; - // A hacky function: set a generated parameter. - void set_generated_parameter(int id, ParamType param); - // These three functions are used in |Verifier::redundant()| for a version // of RepGen algorithm that does not invoke Python verifier. void set_representative(std::unique_ptr seq); @@ -147,6 +150,11 @@ class Context { */ void load_param_info_from_json(std::istream &fin); + /** + * Return the pointer to the ParamInfo object. + */ + [[nodiscard]] ParamInfo *get_param_info() const; + // This function generates a deterministic series of random numbers // ranging [0, 1]. double random_number(); @@ -154,15 +162,6 @@ class Context { private: bool insert_gate(GateType tp); - /** - * Generate random values for random testing for input symbolic parameters. - * The results are stored in |random_parameters_|. - * The size of |random_parameters_| should be equal to the number of input - * symbolic parameters. - * @param num_params The number of input symbolic parameters. - */ - void gen_random_parameters(int num_params); - size_t global_unique_id; std::unordered_map> gates_; std::unordered_map< @@ -173,7 +172,6 @@ class Context { std::vector supported_quantum_gates_; std::vector random_input_distribution_; std::vector random_hashing_distribution_; - std::vector random_parameters_; // A vector to store the representative circuit sequences. std::vector> representative_seqs_; @@ -181,19 +179,15 @@ class Context { // Standard mersenne_twister_engine seeded with 0 std::mt19937 gen{0}; - // Each parameter can be either a concrete parameter (with a value), - // an input symbolic parameter, or a symbolic parameter expression. - // The concrete parameters are from the input QASM file, - // written by QASMParser. - // These three vectors should always have the same size. - std::vector parameter_values_; - std::vector> parameter_wires_; - std::vector is_parameter_symbolic_; - // A holder for parameter expressions. - std::vector> parameter_expressions_; + // A (shared within a single thread) pointer to a (mutable) parameter info + // object. + ParamInfo *param_info_; }; -// TODO: This function does not consider the parameters +/** + * Union two contexts with potentially different gate sets. + * Require the ParamInfo object of both contexts to be the same. + */ Context union_contexts(Context *ctx_0, Context *ctx_1); } // namespace quartz diff --git a/src/quartz/context/param_info.cpp b/src/quartz/context/param_info.cpp new file mode 100644 index 00000000..d13779af --- /dev/null +++ b/src/quartz/context/param_info.cpp @@ -0,0 +1,170 @@ +#include "param_info.h" + +#include + +namespace quartz { + +ParamInfo::ParamInfo(int num_input_symbolic_params) { + gen_random_parameters(num_input_symbolic_params); + for (int i = 0; i < num_input_symbolic_params; i++) { + get_new_param_id(); + } +} + +void ParamInfo::gen_random_parameters(int num_params) { + assert(num_params >= 0); + if (random_parameters_.size() < num_params) { + random_parameters_.reserve(num_params); + static const ParamType pi = std::acos((ParamType)-1.0); + static std::uniform_real_distribution dis_real(-pi, pi); + while (random_parameters_.size() < num_params) { + random_parameters_.emplace_back(dis_real(gen)); + } + } +} + +std::vector ParamInfo::get_all_generated_parameters() const { + return random_parameters_; +} + +ParamType ParamInfo::get_param_value(int id) const { + assert(id >= 0 && id < (int)parameter_values_.size()); + assert(!is_parameter_symbolic_[id]); + return parameter_values_[id]; +} + +void ParamInfo::set_param_value(int id, const ParamType ¶m) { + assert(id >= 0 && id < (int)is_parameter_symbolic_.size()); + assert(!is_parameter_symbolic_[id]); + while (id >= (int)parameter_values_.size()) { + parameter_values_.emplace_back(); + } + parameter_values_[id] = param; +} + +std::vector ParamInfo::get_all_input_param_values() const { + return parameter_values_; +} + +int ParamInfo::get_new_param_id(const ParamType ¶m) { + int id = (int)is_parameter_symbolic_.size(); + is_parameter_symbolic_.push_back(false); + auto wire = std::make_unique(); + wire->type = CircuitWire::input_param; + wire->index = id; + parameter_wires_.push_back(std::move(wire)); + set_param_value(id, param); + return id; +} + +int ParamInfo::get_new_param_id() { + int id = (int)is_parameter_symbolic_.size(); + is_parameter_symbolic_.push_back(true); + auto wire = std::make_unique(); + wire->type = CircuitWire::input_param; + wire->index = id; + parameter_wires_.push_back(std::move(wire)); + return id; +} + +int ParamInfo::get_new_param_expression_id( + const std::vector ¶meter_indices, Gate *op) { + bool is_symbolic = false; + for (auto &input_id : parameter_indices) { + assert(input_id >= 0 && input_id < (int)is_parameter_symbolic_.size()); + if (param_is_symbolic(input_id)) { + is_symbolic = true; + } + } + if (!is_symbolic) { + // A concrete parameter, no need to create an expression. + // Compute the value directly. + std::vector input_params; + input_params.reserve(parameter_indices.size()); + for (auto &input_id : parameter_indices) { + input_params.push_back(get_param_value(input_id)); + } + return get_new_param_id(op->compute(input_params)); + } + int id = (int)is_parameter_symbolic_.size(); + is_parameter_symbolic_.push_back(true); + auto circuit_gate = std::make_unique(); + circuit_gate->gate = op; + for (auto &input_id : parameter_indices) { + circuit_gate->input_wires.push_back(parameter_wires_[input_id].get()); + parameter_wires_[input_id]->output_gates.push_back(circuit_gate.get()); + } + auto wire = std::make_unique(); + wire->type = CircuitWire::internal_param; + wire->index = id; + wire->input_gates.push_back(circuit_gate.get()); + circuit_gate->output_wires.push_back(wire.get()); + parameter_wires_.push_back(std::move(wire)); + parameter_expressions_.push_back(std::move(circuit_gate)); + return id; +} + +int ParamInfo::get_num_parameters() const { + return (int)is_parameter_symbolic_.size(); +} + +int ParamInfo::get_num_input_symbolic_parameters() const { + return (int)random_parameters_.size(); +} + +bool ParamInfo::param_is_symbolic(int id) const { + return id >= 0 && id < (int)is_parameter_symbolic_.size() && + is_parameter_symbolic_[id]; +} + +bool ParamInfo::param_has_value(int id) const { + return id >= 0 && id < (int)is_parameter_symbolic_.size() && + !is_parameter_symbolic_[id]; +} + +bool ParamInfo::param_is_expression(int id) const { + return id >= 0 && id < (int)parameter_wires_.size() && + !parameter_wires_[id]->input_gates.empty(); +} + +CircuitWire *ParamInfo::get_param_wire(int id) const { + if (id >= 0 && id < (int)parameter_wires_.size()) { + return parameter_wires_[id].get(); + } else { + return nullptr; // out of range + } +} + +std::vector +ParamInfo::compute_parameters(const std::vector &input_parameters) { + auto result = input_parameters; + result.resize(is_parameter_symbolic_.size()); + for (auto &expr : parameter_expressions_) { + std::vector params; + for (const auto &input_wire : expr->input_wires) { + params.push_back(result[input_wire->index]); + } + assert(expr->output_wires.size() == 1); + const auto &output_wire = expr->output_wires[0]; + result[output_wire->index] = expr->gate->compute(params); + } + return result; +} + +std::vector ParamInfo::get_param_masks() const { + std::vector param_mask(is_parameter_symbolic_.size()); + for (int i = 0; i < (int)param_mask.size(); i++) { + if (!param_is_expression(i)) { + param_mask[i] = ((InputParamMaskType)1) << i; + } + } + for (auto &expr : parameter_expressions_) { + const auto &output_wire = expr->output_wires[0]; + param_mask[output_wire->index] = 0; + for (const auto &input_wire : expr->input_wires) { + param_mask[output_wire->index] |= param_mask[input_wire->index]; + } + } + return param_mask; +} +} // namespace quartz diff --git a/src/quartz/context/param_info.h b/src/quartz/context/param_info.h new file mode 100644 index 00000000..9653e0e3 --- /dev/null +++ b/src/quartz/context/param_info.h @@ -0,0 +1,107 @@ +#pragma once + +#include "quartz/circuitseq/circuitgate.h" +#include "quartz/circuitseq/circuitwire.h" +#include "quartz/gate/gate_utils.h" +#include "quartz/utils/utils.h" + +#include +#include + +namespace quartz { + +class ParamInfo { + public: + /** + * Default constructor: initialize 0 parameters. + */ + ParamInfo() : ParamInfo(0) {} + /** + * Initialize |num_input_symbolic_params| input symbolic parameters. + */ + explicit ParamInfo(int num_input_symbolic_params); + + /** + * Generate random values for random testing for input symbolic parameters. + * The results are stored in |random_parameters_|. + * The size of |random_parameters_| should be equal to the number of input + * symbolic parameters. + * @param num_params The number of input symbolic parameters. + */ + void gen_random_parameters(int num_params); + + [[nodiscard]] std::vector get_all_generated_parameters() const; + + /** + * Get the value of a concrete parameter. + */ + [[nodiscard]] ParamType get_param_value(int id) const; + /** + * Set the value of a concrete parameter. + */ + void set_param_value(int id, const ParamType ¶m); + /** + * A convenient method to return all concrete parameter values. + * @return A vector such that the |i|-th index stores the value of the + * parameter with index |i|. + */ + [[nodiscard]] std::vector get_all_input_param_values() const; + /** + * Create a new concrete parameter. + * @return The index of the new concrete parameter. + */ + int get_new_param_id(const ParamType ¶m); + /** + * Create a new symbolic parameter. + * @return The index of the new symbolic parameter. + */ + int get_new_param_id(); + /** + * Create a new parameter expression. If all input parameters are concrete, + * compute the result directly instead of creating the expression. + * @param parameter_indices The input parameter indices. + * @param op The operator of the parameter expression. + * @return The index of the new parameter expression. + */ + int get_new_param_expression_id(const std::vector ¶meter_indices, + Gate *op); + [[nodiscard]] int get_num_parameters() const; + [[nodiscard]] int get_num_input_symbolic_parameters() const; + [[nodiscard]] bool param_is_symbolic(int id) const; + [[nodiscard]] bool param_has_value(int id) const; + [[nodiscard]] bool param_is_expression(int id) const; + + [[nodiscard]] CircuitWire *get_param_wire(int id) const; + + /** + * Compute all parameters given the input symbolic parameter values. + * @param input_parameters The input symbolic parameter values. + * @return A vector containing all parameters. + */ + [[nodiscard]] std::vector + compute_parameters(const std::vector &input_parameters); + + /** + * Compute and return which input parameters are used in each of the + * parameter expressions. + * @return The mask for each parameter expression. + */ + [[nodiscard]] std::vector get_param_masks() const; + + // Each parameter can be either a concrete parameter (with a value), + // an input symbolic parameter, or a symbolic parameter expression. + // The concrete parameters are from the input QASM file, + // written by QASMParser. + // These three vectors should always have the same size. + std::vector parameter_values_; + std::vector> parameter_wires_; + std::vector is_parameter_symbolic_; + // A holder for parameter expressions. + std::vector> parameter_expressions_; + + // For random testing. + std::vector random_parameters_; + // Standard mersenne_twister_engine seeded with 0 for random testing. + std::mt19937 gen{0}; +}; +} // namespace quartz diff --git a/src/quartz/parser/qasm_parser.h b/src/quartz/parser/qasm_parser.h index 6b522417..6d81f114 100644 --- a/src/quartz/parser/qasm_parser.h +++ b/src/quartz/parser/qasm_parser.h @@ -160,6 +160,7 @@ bool QASMParser::load_qasm_stream( int num_params = ctx_->get_gate(gate_type)->num_parameters; std::vector qubit_indices(num_qubits); std::vector param_indices(num_params); + std::cout << "num_param " << num_params << std::endl; for (int i = 0; i < num_params; ++i) { assert(ss.good()); std::string token; @@ -219,9 +220,12 @@ bool QASMParser::load_qasm_stream( if (negative) p = -p; if (parameters.count(p) == 0) { + std::cout << "P1 " << ctx_->get_num_parameters() << std::endl; int param_id = ctx_->get_new_param_id(p); + std::cout << "P2 " << ctx_->get_num_parameters() << std::endl; parameters[p] = param_id; } + std::cout << "P= " << ctx_->get_num_parameters() << std::endl; param_indices[i] = parameters[p]; } for (int i = 0; i < num_qubits; ++i) { diff --git a/src/quartz/tasograph/substitution.cpp b/src/quartz/tasograph/substitution.cpp index b2ee315f..f9c96bef 100644 --- a/src/quartz/tasograph/substitution.cpp +++ b/src/quartz/tasograph/substitution.cpp @@ -489,7 +489,8 @@ GraphXfer::create_single_gate_GraphXfer(Context *union_ctx, Command src_cmd, std::pair GraphXfer::ccz_cx_rz_xfer(Context *ctx) { Context dst_ctx({GateType::rz, GateType::cx, GateType::input_qubit, - GateType::input_param}); + GateType::input_param}, + ctx->get_param_info()); std::pair toffoli_rules = RuleParser::ccz_cx_rz_rules(); std::vector cmds; @@ -507,7 +508,8 @@ std::pair GraphXfer::ccz_cx_rz_xfer(Context *ctx) { std::pair GraphXfer::ccz_cx_u1_xfer(Context *ctx) { Context dst_ctx({GateType::u1, GateType::cx, GateType::input_qubit, - GateType::input_param}); + GateType::input_param}, + ctx->get_param_info()); std::pair toffoli_rules = RuleParser::ccz_cx_u1_rules(); std::vector cmds; @@ -525,7 +527,8 @@ std::pair GraphXfer::ccz_cx_u1_xfer(Context *ctx) { std::pair GraphXfer::ccz_cx_t_xfer(Context *ctx) { Context dst_ctx({GateType::t, GateType::tdg, GateType::cx, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ctx->get_param_info()); std::pair toffoli_rules = RuleParser::ccz_cx_t_rules(); std::vector cmds; diff --git a/src/quartz/tasograph/tasograph.cpp b/src/quartz/tasograph/tasograph.cpp index fafddb76..f636178d 100644 --- a/src/quartz/tasograph/tasograph.cpp +++ b/src/quartz/tasograph/tasograph.cpp @@ -33,15 +33,15 @@ void Graph::_construct_pos_2_logical_qubit() { // Construct pos_2_logical_qubit std::unordered_map op_in_degree; std::queue op_q; - for (auto it = outEdges.cbegin(); it != outEdges.cend(); ++it) { - if (it->first.ptr->tp == GateType::input_qubit || - it->first.ptr->tp == GateType::input_param) { - op_q.push(it->first); + for (const auto &outEdge : outEdges) { + if (outEdge.first.ptr->tp == GateType::input_qubit || + outEdge.first.ptr->tp == GateType::input_param) { + op_q.push(outEdge.first); } } - for (auto it = inEdges.cbegin(); it != inEdges.cend(); ++it) { - op_in_degree[it->first] = it->second.size(); + for (const auto &inEdge : inEdges) { + op_in_degree[inEdge.first] = (int)inEdge.second.size(); } while (!op_q.empty()) { @@ -93,8 +93,8 @@ Graph::Graph(Context *ctx, const CircuitSeq *seq) // Map all gates in circuitseq to Op std::map edge_2_op; - auto search_for_params = [this, &edge_2_op](auto &this_ref, - CircuitGate *e) -> void { + auto search_for_params = [this, &edge_2_op, &ctx](auto &this_ref, + CircuitGate *e) -> void { auto dstOp = edge_2_op[e]; for (int dstIdx = 0; dstIdx < (int)e->input_wires.size(); dstIdx++) { auto &input_node = e->input_wires[dstIdx]; @@ -113,6 +113,7 @@ Graph::Graph(Context *ctx, const CircuitSeq *seq) auto ex = input_node->input_gates[0]; if (edge_2_op.find(ex) == edge_2_op.end()) { // consider expressions recursively + edge_2_op[ex] = Op(ctx->next_global_unique_id(), ex->gate); this_ref(this_ref, ex); } auto srcOp = edge_2_op[ex]; @@ -257,6 +258,8 @@ void Graph::set_special_op_guid(size_t _special_op_guid) { } void Graph::add_edge(const Op &srcOp, const Op &dstOp, int srcIdx, int dstIdx) { + assert(srcOp.ptr); + assert(dstOp.ptr); if (inEdges.find(dstOp) == inEdges.end()) { inEdges[dstOp]; } @@ -897,9 +900,17 @@ void Graph::remove(Pos pos, bool left, } bool Graph::merge_2_rotation_op(Op op_0, Op op_1) { + if (!context->has_gate(GateType::add)) { + std::cerr << "Graph::merge_2_rotation_op requires the context to have " + "GateType::add in the gate set." + << std::endl; + assert(false); + } // Marge rotation op_1 to rotation op_0 int num_qubits = op_0.ptr->get_num_qubits(); int num_params = op_0.ptr->get_num_parameters(); + assert(op_1.ptr->get_num_qubits() == num_qubits); + assert(op_1.ptr->get_num_parameters() == num_params); std::map param_idx_2_op_0; std::map param_idx_2_op_1; @@ -907,15 +918,13 @@ bool Graph::merge_2_rotation_op(Op op_0, Op op_1) { assert(inEdges.find(op_0) != inEdges.end()); assert(inEdges.find(op_1) != inEdges.end()); auto input_edges_0 = inEdges[op_0]; - for (auto it = input_edges_0.begin(); it != input_edges_0.end(); ++it) { - auto edge_0 = *it; + for (auto edge_0 : input_edges_0) { if (edge_0.dstIdx >= num_qubits) { param_idx_2_op_0[edge_0.dstIdx] = edge_0.srcOp; } } auto input_edges_1 = inEdges[op_1]; - for (auto it = input_edges_1.begin(); it != input_edges_1.end(); ++it) { - auto edge_1 = *it; + for (auto edge_1 : input_edges_1) { if (edge_1.dstIdx >= num_qubits) { // Which means that it is a parameter input param_idx_2_op_1[edge_1.dstIdx] = edge_1.srcOp; @@ -952,6 +961,12 @@ bool Graph::merge_2_rotation_op(Op op_0, Op op_1) { } void Graph::rotation_merging(GateType target_rotation) { + if (!context->has_gate(GateType::add)) { + std::cerr << "Rotation merging requires the context to have GateType::add" + " in the gate set." + << std::endl; + assert(false); + } // Step 1: calculate the bitmask of each operator std::unordered_map bitmasks; std::unordered_map pos_to_qubits; @@ -1027,10 +1042,11 @@ void Graph::rotation_merging(GateType target_rotation) { // Step 2: Propagate all CNOTs std::queue todo_cx; std::unordered_set visited_cx; - for (const auto &it : inEdges) + for (const auto &it : inEdges) { if (it.first.ptr->tp == GateType::cx) { todo_cx.push(it.first); } + } while (!todo_cx.empty()) { const auto cx = todo_cx.front(); todo_cx.pop(); @@ -1091,7 +1107,7 @@ void Graph::rotation_merging(GateType target_rotation) { assert(pos_set.size() >= 1); Pos first; bool is_first = true; - for (auto pos : pos_set) { + for (const auto &pos : pos_set) { if (is_first) { first = pos; is_first = false; @@ -1103,6 +1119,7 @@ void Graph::rotation_merging(GateType target_rotation) { // << pos.op.guid << ")" << std::endl; } } + std::cout << __LINE__ << std::endl; auto op = first.op; auto in_edges = inEdges[op]; diff --git a/src/test/gen_ecc_set.h b/src/test/gen_ecc_set.h index eedbb00c..4487e02d 100644 --- a/src/test/gen_ecc_set.h +++ b/src/test/gen_ecc_set.h @@ -13,7 +13,8 @@ void gen_ecc_set(const std::vector &supported_gates, const std::string &file_prefix, bool unique_parameters, bool generate_representative_set, int num_qubits, int num_input_parameters, int max_num_quantum_gates) { - Context ctx(supported_gates, num_qubits, num_input_parameters); + ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + Context ctx(supported_gates, num_qubits, ¶m_info); Generator gen(&ctx); EquivalenceSet equiv_set; diff --git a/src/test/test_all.cpp b/src/test/test_all.cpp index 7cbd07b2..fca3c80e 100644 --- a/src/test/test_all.cpp +++ b/src/test/test_all.cpp @@ -11,9 +11,10 @@ using namespace quartz; int main() { std::cout << "Hello, World!" << std::endl; + ParamInfo param_info(/*num_input_symbolic_params=*/2); Context ctx({GateType::x, GateType::y, GateType::add, GateType::neg, GateType::u2, GateType::u3, GateType::cx}, - 2, 2); + 2, ¶m_info); auto y = ctx.get_gate(GateType::y); y->get_matrix()->print(); diff --git a/src/test/test_appliable_xfer.cpp b/src/test/test_appliable_xfer.cpp index 72f29741..ccb90730 100644 --- a/src/test/test_appliable_xfer.cpp +++ b/src/test/test_appliable_xfer.cpp @@ -22,8 +22,10 @@ template std::string output_vec(const std::vector &vec) { } int main() { + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, - GateType::cx, GateType::t, GateType::tdg}); + GateType::cx, GateType::t, GateType::tdg}, + ¶m_info); EquivalenceSet eqs; if (!eqs.load_json(&ctx, "../bfs_verified_simplified.json", /*from_verifier=*/false)) { diff --git a/src/test/test_apply_xfer.cpp b/src/test/test_apply_xfer.cpp index 05acfe6c..8977e258 100644 --- a/src/test/test_apply_xfer.cpp +++ b/src/test/test_apply_xfer.cpp @@ -12,8 +12,10 @@ bool graph_cmp(std::shared_ptr a, std::shared_ptr b) { int main() { // Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, // GateType::cx, GateType::x, GateType::rz, GateType::add}); + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, - GateType::cx, GateType::x, GateType::t, GateType::tdg}); + GateType::cx, GateType::x, GateType::t, GateType::tdg}, + ¶m_info); auto graph = Graph::from_qasm_file( &ctx, "../experiment/circs/t_tdg_circs/barenco_tof_3.qasm"); diff --git a/src/test/test_bfs.cpp b/src/test/test_bfs.cpp index 55f7a2b4..922552ed 100644 --- a/src/test/test_bfs.cpp +++ b/src/test/test_bfs.cpp @@ -9,7 +9,8 @@ int main() { const bool run_bfs_unverified = false; const bool run_bfs_verified = true; // with representative pruning - Context ctx({GateType::h}, num_qubits, num_input_parameters); + ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + Context ctx({GateType::h}, num_qubits, ¶m_info); Generator gen(&ctx); EquivalenceSet equiv_set; diff --git a/src/test/test_context_shift.cpp b/src/test/test_context_shift.cpp index 7f4c8623..59f8992e 100644 --- a/src/test/test_context_shift.cpp +++ b/src/test/test_context_shift.cpp @@ -3,13 +3,17 @@ using namespace quartz; int main() { + ParamInfo param_info; Context src_ctx({GateType::input_qubit, GateType::input_param, GateType::t, - GateType::tdg}); - Context dst_ctx({ - GateType::input_qubit, - GateType::input_param, - GateType::u1, - }); + GateType::tdg}, + ¶m_info); + Context dst_ctx( + { + GateType::input_qubit, + GateType::input_param, + GateType::u1, + }, + ¶m_info); RuleParser rule_parser({ "t q0 = u1 q0 0.25pi", // t -> u1 "h q0 = u2 q0 0 pi", // h-> u2 diff --git a/src/test/test_create_graphXfer_from_qasm.cpp b/src/test/test_create_graphXfer_from_qasm.cpp index d16f57f3..d0352e79 100644 --- a/src/test/test_create_graphXfer_from_qasm.cpp +++ b/src/test/test_create_graphXfer_from_qasm.cpp @@ -7,8 +7,10 @@ using namespace quartz; int main() { - Context ctx({GateType::input_qubit, GateType::input_param, GateType::rz, - GateType::z}); + ParamInfo param_info; + Context ctx( + {GateType::input_qubit, GateType::input_param, GateType::rz, GateType::z}, + ¶m_info); std::string src_str = "OPENQASM 2.0;\n" "include \"qelib1.inc\";\n" "qreg q[1];\n" diff --git a/src/test/test_dataset.h b/src/test/test_dataset.h index 57908ff3..7eb32345 100644 --- a/src/test/test_dataset.h +++ b/src/test/test_dataset.h @@ -10,7 +10,8 @@ using namespace quartz; void test_equivalence_set(const std::vector &support_gates, const std::string &file_name, const std::string &save_file_name) { - Context ctx(support_gates); + ParamInfo param_info; + Context ctx(support_gates, ¶m_info); EquivalenceSet eqs; auto start = std::chrono::steady_clock::now(); if (!eqs.load_json(&ctx, file_name, /*from_verifier=*/false)) { diff --git a/src/test/test_from_and_to_qasm.cpp b/src/test/test_from_and_to_qasm.cpp index d4bdd55e..241adecd 100644 --- a/src/test/test_from_and_to_qasm.cpp +++ b/src/test/test_from_and_to_qasm.cpp @@ -3,8 +3,10 @@ using namespace quartz; int main() { + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::add, - GateType::cx, GateType::rz, GateType::x, GateType::h}); + GateType::cx, GateType::rz, GateType::x, GateType::h}, + ¶m_info); // Context ctx({GateType::input_qubit, GateType::input_param, GateType::add, // GateType::cz, GateType::rz, GateType::x, GateType::rx1, // GateType::rx3}); diff --git a/src/test/test_generator.cpp b/src/test/test_generator.cpp index a3b90df6..4b895975 100644 --- a/src/test/test_generator.cpp +++ b/src/test/test_generator.cpp @@ -6,7 +6,9 @@ using namespace quartz; int main() { - Context ctx({GateType::x, GateType::y, GateType::cx, GateType::h}, 3, 3); + ParamInfo param_info(/*num_input_symbolic_params=*/3); + Context ctx({GateType::x, GateType::y, GateType::cx, GateType::h}, 3, + ¶m_info); Generator gen(&ctx); Dataset dataset; EquivalenceSet ecc; diff --git a/src/test/test_generator.h b/src/test/test_generator.h index 722d551a..fa1a8808 100644 --- a/src/test/test_generator.h +++ b/src/test/test_generator.h @@ -11,7 +11,8 @@ void test_generator(const std::vector &support_gates, int num_qubits, int max_num_input_parameters, int max_num_gates, bool verbose, const std::string &save_file_name, bool count_minimal_representations = false) { - Context ctx(support_gates, num_qubits, max_num_input_parameters); + ParamInfo param_info(/*num_input_symbolic_params=*/max_num_input_parameters); + Context ctx(support_gates, num_qubits, ¶m_info); Generator generator(&ctx); Dataset dataset; auto start = std::chrono::steady_clock::now(); diff --git a/src/test/test_graph_to_qasm.cpp b/src/test/test_graph_to_qasm.cpp index d6785454..b0bcfc70 100644 --- a/src/test/test_graph_to_qasm.cpp +++ b/src/test/test_graph_to_qasm.cpp @@ -6,15 +6,17 @@ using namespace quartz; #include int main() { - Context *ctx = new Context({GateType::input_qubit, GateType::input_param, - GateType::t, GateType::tdg, GateType::cx}); - QASMParser qasm_parser(ctx); + ParamInfo param_info; + Context ctx({GateType::input_qubit, GateType::input_param, GateType::t, + GateType::tdg, GateType::cx}, + ¶m_info); + QASMParser qasm_parser(&ctx); CircuitSeq *dag = nullptr; if (!qasm_parser.load_qasm("circuit/example-circuits/t_cx_tdg.qasm", dag)) { std::cout << "Parser failed" << std::endl; } - Graph graph(ctx, dag); + Graph graph(&ctx, dag); graph.to_qasm("temp.qasm", /*print_result=*/true, true); graph.draw_circuit("temp.qasm", "temp.png"); } diff --git a/src/test/test_ibmq.cpp b/src/test/test_ibmq.cpp index 955fb5a5..f86f4cc8 100644 --- a/src/test/test_ibmq.cpp +++ b/src/test/test_ibmq.cpp @@ -37,10 +37,13 @@ int main(int argc, char **argv) { input_fn, output_fn, eqset_fn); auto fn = input_fn.substr(input_fn.rfind('/') + 1); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::x, GateType::cx, - GateType::t, GateType::input_qubit, GateType::input_param}); + GateType::t, GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::u1, GateType::u2, GateType::u3, GateType::add, - GateType::cx, GateType::input_qubit, GateType::input_param}); + GateType::cx, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); // Construct GraphXfers for toffoli flip diff --git a/src/test/test_ibmq_td_disabled.cpp b/src/test/test_ibmq_td_disabled.cpp index 984d399b..ebe4a606 100644 --- a/src/test/test_ibmq_td_disabled.cpp +++ b/src/test/test_ibmq_td_disabled.cpp @@ -26,10 +26,13 @@ int main(int argc, char **argv) { parse_args(argv, argc, simulated_annealing, early_stop, input_fn, output_fn, eqset_fn); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::x, GateType::cx, - GateType::t, GateType::input_qubit, GateType::input_param}); + GateType::t, GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::u1, GateType::u2, GateType::u3, GateType::add, - GateType::cx, GateType::input_qubit, GateType::input_param}); + GateType::cx, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); // Construct GraphXfers for toffoli flip diff --git a/src/test/test_nam.cpp b/src/test/test_nam.cpp index 5e14cd8b..c8befb26 100644 --- a/src/test/test_nam.cpp +++ b/src/test/test_nam.cpp @@ -38,10 +38,13 @@ int main(int argc, char **argv) { auto fn = input_fn.substr(input_fn.rfind('/') + 1); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::x, GateType::cx, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::h, GateType::x, GateType::rz, GateType::add, - GateType::cx, GateType::input_qubit, GateType::input_param}); + GateType::cx, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); auto xfer_pair = GraphXfer::ccz_cx_rz_xfer(&union_ctx); diff --git a/src/test/test_nam_td_disabled.cpp b/src/test/test_nam_td_disabled.cpp index 3c980c31..55814a9b 100644 --- a/src/test/test_nam_td_disabled.cpp +++ b/src/test/test_nam_td_disabled.cpp @@ -27,10 +27,13 @@ int main(int argc, char **argv) { eqset_fn); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::x, GateType::cx, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::h, GateType::x, GateType::rz, GateType::add, - GateType::cx, GateType::input_qubit, GateType::input_param}); + GateType::cx, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); auto xfer_pair = GraphXfer::ccz_cx_rz_xfer(&union_ctx); diff --git a/src/test/test_optimization.cpp b/src/test/test_optimization.cpp index 17d25372..068c990b 100644 --- a/src/test/test_optimization.cpp +++ b/src/test/test_optimization.cpp @@ -7,9 +7,11 @@ using namespace quartz; int main() { + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::cx, GateType::h, GateType::s, GateType::t, GateType::tdg, - GateType::x, GateType::add, GateType::z}); + GateType::x, GateType::add, GateType::z}, + ¶m_info); // test_optimization(&ctx, "circuit/example-circuits/voqc_fig5.qasm", // "cmake-build-debug/bfs_verified.json"); test_optimization(&ctx, "circuit/example-circuits/barenco_tof_3.qasm", diff --git a/src/test/test_optimization_sa.cpp b/src/test/test_optimization_sa.cpp index d9b6725b..f0440c64 100644 --- a/src/test/test_optimization_sa.cpp +++ b/src/test/test_optimization_sa.cpp @@ -6,8 +6,10 @@ using namespace quartz; #include int main() { + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, - GateType::t, GateType::cx, GateType::tdg}); + GateType::t, GateType::cx, GateType::tdg}, + ¶m_info); // test_optimization(&ctx, "circuit/example-circuits/voqc_fig5.qasm", // "cmake-build-debug/bfs_verified.json"); #ifdef __linux diff --git a/src/test/test_optimize.cpp b/src/test/test_optimize.cpp index 7b5b56ab..eb89382a 100644 --- a/src/test/test_optimize.cpp +++ b/src/test/test_optimize.cpp @@ -5,9 +5,10 @@ using namespace quartz; int main() { + ParamInfo param_info(/*num_input_symbolic_params=*/2); Context ctx({GateType::input_qubit, GateType::input_param, GateType::cx, GateType::h, GateType::rz, GateType::x, GateType::add}, - /*num_qubits=*/3, /*num_input_symbolic_params=*/2); + /*num_qubits=*/3, ¶m_info); auto graph = Graph::from_qasm_file( &ctx, "experiment/circs/nam_circs/barenco_tof_3.qasm"); diff --git a/src/test/test_partition.cpp b/src/test/test_partition.cpp index fa72d4d0..f657d514 100644 --- a/src/test/test_partition.cpp +++ b/src/test/test_partition.cpp @@ -5,9 +5,11 @@ using namespace quartz; int main() { + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, GateType::cx, GateType::x, GateType::rz, GateType::add, - GateType::neg}); + GateType::neg}, + ¶m_info); auto graph = Graph::from_qasm_file( &ctx, "../experiment/circs/scalability_study/adder_64/adder_64.qasm"); auto subgraphs = graph->topology_partition(512); diff --git a/src/test/test_phase_shift.cpp b/src/test/test_phase_shift.cpp index 16a3ffbf..1eebcf9d 100644 --- a/src/test/test_phase_shift.cpp +++ b/src/test/test_phase_shift.cpp @@ -15,8 +15,9 @@ int main() { const int num_input_parameters = 1; const int max_num_gates = 2; const int max_num_param_gates = 1; + ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); Context ctx({GateType::rz, GateType::u1, GateType::add}, num_qubits, - num_input_parameters); + ¶m_info); Generator gen(&ctx); Dataset dataset; diff --git a/src/test/test_pruning.h b/src/test/test_pruning.h index 01674502..3945c42c 100644 --- a/src/test/test_pruning.h +++ b/src/test/test_pruning.h @@ -16,7 +16,8 @@ void test_pruning( int max_num_param_gates = 1, bool run_representative_pruning = true, bool run_original = true, bool run_original_unverified = false, bool run_original_verified = true, bool unique_parameters = false) { - Context ctx(supported_gates, num_qubits, num_input_parameters); + ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + Context ctx(supported_gates, num_qubits, ¶m_info); Generator gen(&ctx); EquivalenceSet equiv_set; diff --git a/src/test/test_quartz.cpp b/src/test/test_quartz.cpp index 1eab126a..a5b3b36d 100644 --- a/src/test/test_quartz.cpp +++ b/src/test/test_quartz.cpp @@ -44,10 +44,13 @@ int main(int argc, char **argv) { fprintf(stderr, "Input qasm file: %s\n", input_fn.c_str()); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::x, GateType::cx, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::h, GateType::x, GateType::rz, GateType::add, - GateType::cx, GateType::input_qubit, GateType::input_param}); + GateType::cx, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); // Construct GraphXfers for toffoli flip diff --git a/src/test/test_remove_swap.cpp b/src/test/test_remove_swap.cpp index f0b88d2d..e6c336d8 100644 --- a/src/test/test_remove_swap.cpp +++ b/src/test/test_remove_swap.cpp @@ -50,10 +50,12 @@ void remove_swap_for_nwq(Context *ctx) { } } int main() { + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, GateType::x, GateType::ry, GateType::u2, GateType::u3, GateType::cx, GateType::cz, GateType::cp, GateType::swap, - GateType::rz, GateType::rx, GateType::p}); + GateType::rz, GateType::rx, GateType::p}, + ¶m_info); std::vector circuit_names = {"ae", "dj", "ghz", diff --git a/src/test/test_rigetti.cpp b/src/test/test_rigetti.cpp index d9410ba7..f4043fdb 100644 --- a/src/test/test_rigetti.cpp +++ b/src/test/test_rigetti.cpp @@ -38,11 +38,13 @@ int main(int argc, char **argv) { auto fn = input_fn.substr(input_fn.rfind('/') + 1); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::cx, GateType::x, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::rz, GateType::h, GateType::cx, GateType::x, - GateType::add, GateType::input_qubit, - GateType::input_param}); + GateType::add, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); // Construct GraphXfers for toffoli flip @@ -69,7 +71,8 @@ int main(int argc, char **argv) { "x q0 = rx q0 pi;"}); Context rigetti_ctx({GateType::rx, GateType::rz, GateType::cz, GateType::add, GateType::input_qubit, - GateType::input_param}); + GateType::input_param}, + ¶m_info); auto union_ctx_0 = union_contexts(&dst_ctx, &rigetti_ctx); auto graph_before_search = new_graph->context_shift(&dst_ctx, &rigetti_ctx, &union_ctx_0, &rules); @@ -89,7 +92,8 @@ int main(int argc, char **argv) { // Convert cx to cz and merge h gates RuleParser cx_2_cz({"cx q0 q1 = h q1; cz q0 q1; h q1;"}); Context cz_ctx({GateType::rz, GateType::h, GateType::x, GateType::cz, - GateType::add, GateType::input_qubit, GateType::input_param}); + GateType::add, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx_0 = union_contexts(&cz_ctx, &dst_ctx); auto graph_before_h_cz_merge = new_graph->context_shift( &dst_ctx, &cz_ctx, &union_ctx_0, &cx_2_cz, false); @@ -104,7 +108,8 @@ int main(int argc, char **argv) { RuleParser rules({"h q0 = rx q0 pi; rz q0 0.5pi; rx q0 0.5pi; rz q0 -0.5pi;", "x q0 = rx q0 pi;"}); Context rigetti_ctx({GateType::rx, GateType::rz, GateType::cz, GateType::add, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx_1 = union_contexts(&rigetti_ctx, &union_ctx_0); auto graph_rigetti = graph_after_h_cz_merge->context_shift( &cz_ctx, &rigetti_ctx, &union_ctx_1, &rules, false); diff --git a/src/test/test_rigetti_td_disabled.cpp b/src/test/test_rigetti_td_disabled.cpp index dd65b98d..879ba34d 100644 --- a/src/test/test_rigetti_td_disabled.cpp +++ b/src/test/test_rigetti_td_disabled.cpp @@ -28,11 +28,13 @@ int main(int argc, char **argv) { auto fn = input_fn.substr(input_fn.rfind('/') + 1); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::cx, GateType::x, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::rz, GateType::h, GateType::cx, GateType::x, - GateType::add, GateType::input_qubit, - GateType::input_param}); + GateType::add, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx = union_contexts(&src_ctx, &dst_ctx); // Construct GraphXfers for toffoli flip @@ -55,7 +57,8 @@ int main(int argc, char **argv) { // Convert cx to cz and merge h gates RuleParser cx_2_cz({"cx q0 q1 = h q1; cz q0 q1; h q1;"}); Context cz_ctx({GateType::rz, GateType::h, GateType::x, GateType::cz, - GateType::add, GateType::input_qubit, GateType::input_param}); + GateType::add, GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx_0 = union_contexts(&cz_ctx, &dst_ctx); auto graph_before_h_cz_merge = new_graph->context_shift( &dst_ctx, &cz_ctx, &union_ctx_0, &cx_2_cz, false); @@ -70,7 +73,8 @@ int main(int argc, char **argv) { RuleParser rules({"h q0 = rx q0 pi; rz q0 0.5pi; rx q0 0.5pi; rz q0 -0.5pi;", "x q0 = rx q0 pi;"}); Context rigetti_ctx({GateType::rx, GateType::rz, GateType::cz, GateType::add, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); auto union_ctx_1 = union_contexts(&rigetti_ctx, &union_ctx_0); auto graph_rigetti = graph_after_h_cz_merge->context_shift( &cz_ctx, &rigetti_ctx, &union_ctx_1, &rules, false); diff --git a/src/test/test_rotation_merging.cpp b/src/test/test_rotation_merging.cpp index b4cad7b4..5fe07ef9 100644 --- a/src/test/test_rotation_merging.cpp +++ b/src/test/test_rotation_merging.cpp @@ -12,10 +12,13 @@ int main() { "circuit/voqc-benchmarks/barenco_tof_10.qasm"; std::string result_qasm_filename = "circuit/voqc-benchmarks/barenco_tof_10_rotation_merging.qasm"; + ParamInfo param_info; Context src_ctx({GateType::input_param, GateType::input_qubit, GateType::ccz, - GateType::h}); + GateType::h}, + ¶m_info); Context dst_ctx({GateType::input_param, GateType::input_qubit, GateType::h, - GateType::rz, GateType::cx}); + GateType::rz, GateType::cx}, + ¶m_info); // Context union_ctx({GateType::input_param, GateType::input_qubit, // GateType::t, // GateType::tdg, GateType::cx, GateType::rz}); diff --git a/src/test/test_simulation.cpp b/src/test/test_simulation.cpp index e20368c6..f6013538 100644 --- a/src/test/test_simulation.cpp +++ b/src/test/test_simulation.cpp @@ -125,10 +125,12 @@ int main() { auto start = std::chrono::steady_clock::now(); init_python_interpreter(); PythonInterpreter interpreter; + ParamInfo param_info; Context ctx({GateType::input_qubit, GateType::input_param, GateType::h, GateType::x, GateType::ry, GateType::u2, GateType::u3, GateType::cx, GateType::cz, GateType::cp, GateType::swap, - GateType::rz, GateType::ccz}); + GateType::rz, GateType::ccz}, + ¶m_info); std::vector circuit_names = { "nam-benchmarks/adder_8.qasm" // "nam-benchmarks/csla_mux_3.qasm" diff --git a/src/test/test_sparsity.cpp b/src/test/test_sparsity.cpp index bf87583d..6255f641 100644 --- a/src/test/test_sparsity.cpp +++ b/src/test/test_sparsity.cpp @@ -26,7 +26,8 @@ int nnz(const std::vector &mat, double eps) { void test_sparsity(const std::vector &supported_gates, const std::string &file_prefix, int num_qubits, int num_input_parameters, int max_num_quantum_gates) { - Context ctx(supported_gates, num_qubits, num_input_parameters); + ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + Context ctx(supported_gates, num_qubits, ¶m_info); Generator gen(&ctx); EquivalenceSet equiv_set; diff --git a/src/test/test_toffoli_flip.cpp b/src/test/test_toffoli_flip.cpp index e8aaab30..7e6c2299 100644 --- a/src/test/test_toffoli_flip.cpp +++ b/src/test/test_toffoli_flip.cpp @@ -22,13 +22,16 @@ int main() { // "-0.25pi; rz q1 -0.25pi; rz q2 -0.25pi;"}); auto rules = RuleParser::ccz_cx_t_rules(); // Construct contexts + ParamInfo param_info; Context src_ctx({GateType::h, GateType::ccz, GateType::input_qubit, - GateType::input_param}); + GateType::input_param}, + ¶m_info); Context dst_ctx({GateType::t, GateType::tdg, GateType::cx, GateType::h, - GateType::input_qubit, GateType::input_param}); + GateType::input_qubit, GateType::input_param}, + ¶m_info); Context union_ctx({GateType::ccz, GateType::t, GateType::tdg, GateType::cx, - GateType::h, GateType::input_qubit, - GateType::input_param}); + GateType::h, GateType::input_qubit, GateType::input_param}, + ¶m_info); // Construct GraphXfers std::vector cmds; Command cmd;