diff --git a/src/quartz/context/context.cpp b/src/quartz/context/context.cpp index 89491c5e..579d376c 100644 --- a/src/quartz/context/context.cpp +++ b/src/quartz/context/context.cpp @@ -15,6 +15,7 @@ Context::Context(const std::vector &supported_gates, ParamInfo *param_info) : global_unique_id(16384), supported_gates_(supported_gates), param_info_(param_info) { + // Precomputes the supported gates. gates_.reserve(supported_gates.size()); for (const auto &gate : supported_gates) { insert_gate(gate); @@ -24,6 +25,16 @@ Context::Context(const std::vector &supported_gates, supported_quantum_gates_.emplace_back(gate); } } + + // Precomputes whether any of the gates use halved parameters. + may_use_halved_params_ = false; + for (const auto &g : gates_) { + for (int i = 0; i < g.second->get_num_parameters(); ++i) { + if (g.second->is_param_halved(i)) { + may_use_halved_params_ = true; + } + } + } } Context::Context(const std::vector &supported_gates, int num_qubits, @@ -206,7 +217,9 @@ int Context::get_new_param_id(const ParamType ¶m) { 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_id() { + return param_info_->get_new_param_id(may_use_halved_params_); +} int Context::get_new_param_expression_id( const std::vector ¶meter_indices, Gate *op) { @@ -233,6 +246,10 @@ bool Context::param_is_expression(int id) const { return param_info_->param_is_expression(id); } +bool Context::param_is_halved(int id) const { + return param_info_->param_is_halved(id); +} + CircuitWire *Context::get_param_wire(int id) const { return param_info_->get_param_wire(id); } diff --git a/src/quartz/context/context.h b/src/quartz/context/context.h index 7f652b8a..5f118f5d 100644 --- a/src/quartz/context/context.h +++ b/src/quartz/context/context.h @@ -100,6 +100,7 @@ class Context { [[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]] bool param_is_halved(int id) const; [[nodiscard]] CircuitWire *get_param_wire(int id) const; @@ -163,6 +164,7 @@ class Context { bool insert_gate(GateType tp); size_t global_unique_id; + bool may_use_halved_params_; std::unordered_map> gates_; std::unordered_map< GateType, std::unordered_map, std::unique_ptr>> diff --git a/src/quartz/context/param_info.cpp b/src/quartz/context/param_info.cpp index cbd5515c..77ec3cac 100644 --- a/src/quartz/context/param_info.cpp +++ b/src/quartz/context/param_info.cpp @@ -12,10 +12,10 @@ bool is_symbolic_constant(Gate *op) { return rv; } -ParamInfo::ParamInfo(int num_input_symbolic_params) { +ParamInfo::ParamInfo(int num_input_symbolic_params, bool is_halved) { gen_random_parameters(num_input_symbolic_params); for (int i = 0; i < num_input_symbolic_params; i++) { - get_new_param_id(); + get_new_param_id(is_halved); } } @@ -56,7 +56,9 @@ std::vector ParamInfo::get_all_input_param_values() const { int ParamInfo::get_new_param_id(const ParamType ¶m) { int id = (int)is_parameter_symbolic_.size(); + assert(id == (int)is_parameter_halved_.size()); is_parameter_symbolic_.push_back(false); + is_parameter_halved_.push_back(false); auto wire = std::make_unique(); wire->type = CircuitWire::input_param; wire->index = id; @@ -65,9 +67,10 @@ int ParamInfo::get_new_param_id(const ParamType ¶m) { return id; } -int ParamInfo::get_new_param_id() { +int ParamInfo::get_new_param_id(bool is_halved) { int id = (int)is_parameter_symbolic_.size(); is_parameter_symbolic_.push_back(true); + is_parameter_halved_.push_back(is_halved); // Make sure to generate a random parameter for each symbolic parameter. gen_random_parameters(id + 1); auto wire = std::make_unique(); @@ -98,6 +101,7 @@ int ParamInfo::get_new_param_expression_id( } int id = (int)is_parameter_symbolic_.size(); is_parameter_symbolic_.push_back(true); + is_parameter_halved_.push_back(false); auto circuit_gate = std::make_unique(); circuit_gate->gate = op; for (auto &input_id : parameter_indices) { @@ -137,6 +141,11 @@ bool ParamInfo::param_is_expression(int id) const { !parameter_wires_[id]->input_gates.empty(); } +bool ParamInfo::param_is_halved(int id) const { + return id >= 0 && id < (int)is_parameter_halved_.size() && + is_parameter_halved_[id]; +} + CircuitWire *ParamInfo::get_param_wire(int id) const { if (id >= 0 && id < (int)parameter_wires_.size()) { return parameter_wires_[id].get(); diff --git a/src/quartz/context/param_info.h b/src/quartz/context/param_info.h index f6d93e1c..cddbd176 100644 --- a/src/quartz/context/param_info.h +++ b/src/quartz/context/param_info.h @@ -20,11 +20,11 @@ class ParamInfo { /** * Default constructor: initialize 0 parameters. */ - ParamInfo() : ParamInfo(0) {} + ParamInfo() : ParamInfo(0, false) {} /** * Initialize |num_input_symbolic_params| input symbolic parameters. */ - explicit ParamInfo(int num_input_symbolic_params); + explicit ParamInfo(int num_input_symbolic_params, bool is_halved); /** * Generate random values for random testing for input symbolic parameters. @@ -58,9 +58,10 @@ class ParamInfo { int get_new_param_id(const ParamType ¶m); /** * Create a new symbolic parameter. + * @param is_halved If true, then used by a gate with period 4*pi. * @return The index of the new symbolic parameter. */ - int get_new_param_id(); + int get_new_param_id(bool is_halved); /** * Create a new parameter expression. If all input parameters are concrete, * compute the result directly instead of creating the expression. @@ -75,6 +76,7 @@ class ParamInfo { [[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]] bool param_is_halved(int id) const; [[nodiscard]] CircuitWire *get_param_wire(int id) const; @@ -101,6 +103,7 @@ class ParamInfo { std::vector parameter_values_; std::vector> parameter_wires_; std::vector is_parameter_symbolic_; + std::vector is_parameter_halved_; // A holder for parameter expressions. std::vector> parameter_expressions_; diff --git a/src/quartz/gate/gate.cpp b/src/quartz/gate/gate.cpp index 129e291d..a83a1111 100644 --- a/src/quartz/gate/gate.cpp +++ b/src/quartz/gate/gate.cpp @@ -44,6 +44,8 @@ bool Gate::is_sparse() const { return false; } bool Gate::is_diagonal() const { return false; } +bool Gate::is_param_halved(int i) const { return false; } + int Gate::get_num_control_qubits() const { return 0; } std::vector Gate::get_control_state() const { diff --git a/src/quartz/gate/gate.h b/src/quartz/gate/gate.h index 56492274..95ff3440 100644 --- a/src/quartz/gate/gate.h +++ b/src/quartz/gate/gate.h @@ -40,6 +40,13 @@ class Gate { * Default value is false. */ [[nodiscard]] virtual bool is_diagonal() const; + /** + * @param i the index of the parameter to check + * @return True if this gate is parameterized and parameter i has a period of + * 4*pi as opposed to 2*pi (e.g., rx, ry, rz). + * Default value is false. + */ + [[nodiscard]] virtual bool is_param_halved(int i) const; /** * @return The number of control qubits for controlled gates; or 0 if it is * not a controlled gate. diff --git a/src/quartz/gate/rx.h b/src/quartz/gate/rx.h index 300afe8d..ec600b65 100644 --- a/src/quartz/gate/rx.h +++ b/src/quartz/gate/rx.h @@ -20,6 +20,7 @@ class RXGate : public Gate { } return cached_matrices[theta].get(); } + bool is_param_halved(int i) const override { return true; } std::unordered_map>> cached_matrices; }; diff --git a/src/quartz/gate/ry.h b/src/quartz/gate/ry.h index ffcc8d0f..0fdbdc60 100644 --- a/src/quartz/gate/ry.h +++ b/src/quartz/gate/ry.h @@ -19,6 +19,7 @@ class RYGate : public Gate { } return cached_matrices[theta].get(); } + bool is_param_halved(int i) const override { return true; } std::unordered_map>> cached_matrices; }; diff --git a/src/quartz/gate/rz.h b/src/quartz/gate/rz.h index a4bbdde5..de191d10 100644 --- a/src/quartz/gate/rz.h +++ b/src/quartz/gate/rz.h @@ -21,6 +21,7 @@ class RZGate : public Gate { return cached_matrices[theta].get(); } bool is_sparse() const override { return true; } + bool is_param_halved(int i) const override { return true; } std::unordered_map>> cached_matrices; }; diff --git a/src/quartz/gate/u3.h b/src/quartz/gate/u3.h index aa247242..b31fec48 100644 --- a/src/quartz/gate/u3.h +++ b/src/quartz/gate/u3.h @@ -26,6 +26,7 @@ class U3Gate : public Gate { } return cached_matrices[theta][phi][lambda].get(); } + bool is_param_halved(int i) const override { return i == 0; } std::unordered_map< float, std::unordered_map< float, std::unordered_map>>>> diff --git a/src/test/gen_ecc_set.h b/src/test/gen_ecc_set.h index 13a8e333..f7797236 100644 --- a/src/test/gen_ecc_set.h +++ b/src/test/gen_ecc_set.h @@ -15,7 +15,7 @@ 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) { - ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + ParamInfo param_info(num_input_parameters, false); Context ctx(supported_gates, num_qubits, ¶m_info); Generator gen(&ctx); diff --git a/src/test/test_all.cpp b/src/test/test_all.cpp index fca3c80e..de0f8af3 100644 --- a/src/test/test_all.cpp +++ b/src/test/test_all.cpp @@ -11,7 +11,7 @@ using namespace quartz; int main() { std::cout << "Hello, World!" << std::endl; - ParamInfo param_info(/*num_input_symbolic_params=*/2); + ParamInfo param_info(/*num_input_symbolic_params=*/2, false); Context ctx({GateType::x, GateType::y, GateType::add, GateType::neg, GateType::u2, GateType::u3, GateType::cx}, 2, ¶m_info); diff --git a/src/test/test_bfs.cpp b/src/test/test_bfs.cpp index 922552ed..b4fd2c91 100644 --- a/src/test/test_bfs.cpp +++ b/src/test/test_bfs.cpp @@ -9,7 +9,7 @@ int main() { const bool run_bfs_unverified = false; const bool run_bfs_verified = true; // with representative pruning - ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + ParamInfo param_info(num_input_parameters, false); Context ctx({GateType::h}, num_qubits, ¶m_info); Generator gen(&ctx); diff --git a/src/test/test_constants.cpp b/src/test/test_constants.cpp index 010cf724..8e86d41e 100644 --- a/src/test/test_constants.cpp +++ b/src/test/test_constants.cpp @@ -7,7 +7,7 @@ using namespace quartz; int main() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::rx}, 2, ¶m_info); QASMParser parser(&ctx); diff --git a/src/test/test_generator.cpp b/src/test/test_generator.cpp index 3af7c438..34c8cfeb 100644 --- a/src/test/test_generator.cpp +++ b/src/test/test_generator.cpp @@ -6,7 +6,7 @@ using namespace quartz; int main() { - ParamInfo param_info(/*num_input_symbolic_params=*/3); + ParamInfo param_info(/*num_input_symbolic_params=*/3, false); Context ctx({GateType::x, GateType::y, GateType::cx, GateType::h}, 3, ¶m_info); Generator gen(&ctx); diff --git a/src/test/test_generator.h b/src/test/test_generator.h index fa1a8808..c4f3b55d 100644 --- a/src/test/test_generator.h +++ b/src/test/test_generator.h @@ -11,7 +11,7 @@ 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) { - ParamInfo param_info(/*num_input_symbolic_params=*/max_num_input_parameters); + ParamInfo param_info(max_num_input_parameters, false); Context ctx(support_gates, num_qubits, ¶m_info); Generator generator(&ctx); Dataset dataset; diff --git a/src/test/test_mult.cpp b/src/test/test_mult.cpp index 913b7e54..cc63e6db 100644 --- a/src/test/test_mult.cpp +++ b/src/test/test_mult.cpp @@ -7,7 +7,7 @@ using namespace quartz; int main() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::rx, GateType::mult}, 1, ¶m_info); auto p0 = ctx.get_new_param_id(2.0); diff --git a/src/test/test_optimize.cpp b/src/test/test_optimize.cpp index 2cde9b0d..ec95ddaa 100644 --- a/src/test/test_optimize.cpp +++ b/src/test/test_optimize.cpp @@ -5,7 +5,7 @@ using namespace quartz; int main() { - ParamInfo param_info(/*num_input_symbolic_params=*/2); + ParamInfo param_info(/*num_input_symbolic_params=*/2, false); Context ctx({GateType::input_qubit, GateType::input_param, GateType::cx, GateType::h, GateType::rz, GateType::x, GateType::add}, /*num_qubits=*/3, ¶m_info); diff --git a/src/test/test_phase_shift.cpp b/src/test/test_phase_shift.cpp index 1eebcf9d..44ecbe7d 100644 --- a/src/test/test_phase_shift.cpp +++ b/src/test/test_phase_shift.cpp @@ -15,7 +15,7 @@ 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); + ParamInfo param_info(num_input_parameters, false); Context ctx({GateType::rz, GateType::u1, GateType::add}, num_qubits, ¶m_info); diff --git a/src/test/test_pi.cpp b/src/test/test_pi.cpp index 8c85657e..08153f94 100644 --- a/src/test/test_pi.cpp +++ b/src/test/test_pi.cpp @@ -7,7 +7,7 @@ using namespace quartz; int main() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::rx, GateType::pi}, 1, ¶m_info); auto p0 = ctx.get_new_param_id(PI / 2); diff --git a/src/test/test_pruning.h b/src/test/test_pruning.h index 3945c42c..51210158 100644 --- a/src/test/test_pruning.h +++ b/src/test/test_pruning.h @@ -16,7 +16,7 @@ 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) { - ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + ParamInfo param_info(num_input_parameters, false); Context ctx(supported_gates, num_qubits, ¶m_info); Generator gen(&ctx); diff --git a/src/test/test_qasm_parser.cpp b/src/test/test_qasm_parser.cpp index 4b006f2a..17bfbb6e 100644 --- a/src/test/test_qasm_parser.cpp +++ b/src/test/test_qasm_parser.cpp @@ -16,7 +16,7 @@ bool has_exprs(Context &ctx, CircuitSeq *seq) { } void test_symbolic_exprs() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::rx, GateType::ry, GateType::rz, GateType::cx, GateType::mult, GateType::pi}, 2, ¶m_info); @@ -87,7 +87,7 @@ void test_symbolic_exprs() { } void test_qasm2_qubits() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::cx}, 5, ¶m_info); QASMParser parser(&ctx); @@ -116,7 +116,7 @@ void test_qasm2_qubits() { } void test_qasm3_qubits() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::cx}, 7, ¶m_info); QASMParser parser(&ctx); @@ -147,7 +147,7 @@ void test_qasm3_qubits() { } void test_param_parsing() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::cx, GateType::rx}, 2, ¶m_info); QASMParser parser(&ctx); @@ -224,7 +224,7 @@ void test_param_parsing() { } void test_sum_parsing() { - ParamInfo param_info(0); + ParamInfo param_info; Context ctx({GateType::rx, GateType::mult, GateType::add, GateType::pi}, 2, ¶m_info); @@ -284,6 +284,63 @@ void test_sum_parsing() { } } +bool test_halved_param_context(Context &ctx, bool is_halved) { + auto g = ctx.get_gate(GateType::neg); + + auto param_id = ctx.get_new_param_id(); + auto const_id = ctx.get_new_param_id(0.5); + auto exprs_id = ctx.get_new_param_expression_id({param_id}, g); + + if (ctx.param_is_halved(param_id) != is_halved) { + std::cout << "is_param_halved returned wrong val for symb." << std::endl; + return false; + } + + if (ctx.param_is_halved(const_id)) { + std::cout << "is_param_halved returned wrong val for const." << std::endl; + return false; + } + + if (ctx.param_is_halved(exprs_id)) { + std::cout << "is_param_halved returned wrong val for expr." << std::endl; + return false; + } + + return true; +} + +void test_halved_param_ids() { + // Default parameters constructed by ParamInfo. + ParamInfo param_info_1(4, false); + if (param_info_1.param_is_halved(2)) { + std::cout << "ParamInfo(4,false): param_is_halved(2) == true" << std::endl; + assert(false); + } + + // Halved parameters constructed by ParamInfo. + ParamInfo param_info_2(4, true); + if (!param_info_2.param_is_halved(2)) { + std::cout << "ParamInfo(4,true): param_is_halved(2) == false" << std::endl; + assert(false); + } + + // Default parameters constructed by a Context. + ParamInfo param_info_3; + Context ctx_3({GateType::x, GateType::ry, GateType::neg}, 2, ¶m_info_3); + if (!test_halved_param_context(ctx_3, true)) { + std::cout << "Context failed to handle halved param gate." << std::endl; + assert(false); + } + + // Halved parameters constructed by a Context. + ParamInfo param_info_4; + Context ctx_4({GateType::x, GateType::y, GateType::neg}, 2, ¶m_info_4); + if (!test_halved_param_context(ctx_4, false)) { + std::cout << "Context failed to handle standard gates." << std::endl; + assert(false); + } +} + int main() { std::cout << "[Symbolic Expression Tests]" << std::endl; test_symbolic_exprs(); @@ -293,6 +350,8 @@ int main() { test_qasm3_qubits(); std::cout << "[Sybmolic Parameter Parsing Tests]" << std::endl; test_param_parsing(); - std::cout << "[Sybmolic Summation Parsing Tests]" << std::endl; + std::cout << "[Symbolic Summation Parsing Tests]" << std::endl; test_sum_parsing(); + std::cout << "[Halved Symbolic Parameters]" << std::endl; + test_halved_param_ids(); } diff --git a/src/test/test_sparsity.cpp b/src/test/test_sparsity.cpp index 6255f641..2b88f4c7 100644 --- a/src/test/test_sparsity.cpp +++ b/src/test/test_sparsity.cpp @@ -26,7 +26,7 @@ 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) { - ParamInfo param_info(/*num_input_symbolic_params=*/num_input_parameters); + ParamInfo param_info(num_input_parameters, false); Context ctx(supported_gates, num_qubits, ¶m_info); Generator gen(&ctx);