From bbc75d86515a629cef3a7054bc5b34e36625c7ba Mon Sep 17 00:00:00 2001 From: Vlad Gheorghiu Date: Fri, 6 Sep 2024 15:08:29 -0400 Subject: [PATCH] Initial stab at conditionals Signed-off-by: Vlad Gheorghiu --- include/qpp/classes/qcircuit.hpp | 105 +++++++++--- include/qpp/classes/qengine.hpp | 16 +- .../classes/qcircuit_conditional_step.hpp | 155 ++++++++++++++++++ include/qpp/types.hpp | 5 + 4 files changed, 253 insertions(+), 28 deletions(-) create mode 100644 include/qpp/internal/classes/qcircuit_conditional_step.hpp diff --git a/include/qpp/classes/qcircuit.hpp b/include/qpp/classes/qcircuit.hpp index f9164c14b..d025244ca 100644 --- a/include/qpp/classes/qcircuit.hpp +++ b/include/qpp/classes/qcircuit.hpp @@ -55,6 +55,7 @@ #include "qpp/classes/idisplay.hpp" #include "qpp/classes/ijson.hpp" +#include "qpp/internal/classes/qcircuit_conditional_step.hpp" #include "qpp/internal/classes/qcircuit_gate_step.hpp" #include "qpp/internal/classes/qcircuit_measurement_step.hpp" #include "qpp/internal/classes/qcircuit_nop_step.hpp" @@ -121,7 +122,8 @@ class QCircuit : public IDisplay, public IJSON { } using VarStep = - std::variant; ///< circuit step variant std::vector circuit_; ///<< quantum circuit representation @@ -148,6 +150,7 @@ class QCircuit : public IDisplay, public IJSON { */ template void visit_circuit_step_(const VarStep& circuit_step, Fns&&... fns) const; + /** * \brief Visits a vector of qpp::QCircuit::VarStep variants * @@ -801,9 +804,11 @@ class QCircuit : public IDisplay, public IJSON { } }; auto nop_step_visitor = [&](internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(circuit_, gate_step_visitor, measurement_step_visitor, - nop_step_visitor); + visit_circuit_(circuit_, conditional_step_visitor, gate_step_visitor, + measurement_step_visitor, nop_step_visitor); // update the destructively measured qudits measured_d_.insert( @@ -868,9 +873,11 @@ class QCircuit : public IDisplay, public IJSON { } }; auto nop_step_visitor = [&](internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(circuit_, gate_step_visitor, measurement_step_visitor, - nop_step_visitor); + visit_circuit_(circuit_, conditional_step_visitor, gate_step_visitor, + measurement_step_visitor, nop_step_visitor); // update (enlarge) the clean dits vector clean_dits_.insert( @@ -893,6 +900,27 @@ class QCircuit : public IDisplay, public IJSON { */ QCircuit& add_dit(idx n = 1) { return add_dit(n, nc_); } + QCircuit& cond_if(std::function)> cond_func) { + circuit_.emplace_back(internal::QCircuitConditionalStep{ + internal::QCircuitConditionalStep::Type::IF, cond_func}); + + return *this; + } + + QCircuit& cond_else() { + circuit_.emplace_back(internal::QCircuitConditionalStep{ + internal::QCircuitConditionalStep::Type::ELSE}); + + return *this; + } + + QCircuit& cond_endif() { + circuit_.emplace_back(internal::QCircuitConditionalStep{ + internal::QCircuitConditionalStep::Type::ENDIF}); + + return *this; + } + /** * \brief Applies the single qudit gate \a U on single qudit \a i * @@ -3778,9 +3806,12 @@ class QCircuit : public IDisplay, public IJSON { } }; auto nop_step_visitor = [&](internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(other.circuit_, gate_step_visitor, - measurement_step_visitor, nop_step_visitor); + visit_circuit_(other.circuit_, conditional_step_visitor, + gate_step_visitor, measurement_step_visitor, + nop_step_visitor); // STEP 2 // replace the corresponding elements of measured_, measured_nd_, @@ -3948,9 +3979,12 @@ class QCircuit : public IDisplay, public IJSON { } }; auto nop_step_visitor = [&](internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(other.circuit_, gate_step_visitor, - measurement_step_visitor, nop_step_visitor); + visit_circuit_(other.circuit_, conditional_step_visitor, + gate_step_visitor, measurement_step_visitor, + nop_step_visitor); // TODO check this @@ -4114,9 +4148,12 @@ class QCircuit : public IDisplay, public IJSON { } }; auto nop_step_visitor = [&](internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(other.circuit_, gate_step_visitor, - measurement_step_visitor, nop_step_visitor); + visit_circuit_(other.circuit_, conditional_step_visitor, + gate_step_visitor, measurement_step_visitor, + nop_step_visitor); // TODO check this @@ -4390,9 +4427,12 @@ class QCircuit : public IDisplay, public IJSON { [&](const internal::QCircuitMeasurementStep&) {}; // NOPs are left unchanged auto nop_step_visitor = [&](const internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; for (idx i = end_ctrl_circuit; i < end_composed_circuit; ++i) { - visit_circuit_step_(circuit_[i], gate_step_visitor, - measurement_step_visitor, nop_step_visitor); + visit_circuit_step_(circuit_[i], conditional_step_visitor, + gate_step_visitor, measurement_step_visitor, + nop_step_visitor); } return *this; @@ -4507,9 +4547,11 @@ class QCircuit : public IDisplay, public IJSON { auto measurement_step_visitor = [&](const internal::QCircuitMeasurementStep&) {}; auto nop_step_visitor = [&](const internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(circuit_, gate_step_visitor, measurement_step_visitor, - nop_step_visitor); + visit_circuit_(circuit_, conditional_step_visitor, gate_step_visitor, + measurement_step_visitor, nop_step_visitor); return *this; } @@ -4678,9 +4720,11 @@ class QCircuit : public IDisplay, public IJSON { } }; auto nop_step_visitor = [&](const internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(circuit_, gate_step_visitor, measurement_step_visitor, - nop_step_visitor); + visit_circuit_(circuit_, conditional_step_visitor, gate_step_visitor, + measurement_step_visitor, nop_step_visitor); clean_qudits_.erase(std::next(clean_qudits_.begin(), static_cast(target))); @@ -4723,9 +4767,11 @@ class QCircuit : public IDisplay, public IJSON { } }; auto nop_step_visitor = [&](const internal::QCircuitNOPStep&) {}; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_(circuit_, gate_step_visitor, measurement_step_visitor, - nop_step_visitor); + visit_circuit_(circuit_, conditional_step_visitor, gate_step_visitor, + measurement_step_visitor, nop_step_visitor); clean_dits_.erase(std::next(clean_dits_.begin(), static_cast(target))); @@ -5056,10 +5102,14 @@ class QCircuitIterator { [&](const internal::QCircuitNOPStep& nop_step) { os << nop_step; }; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep& conditional_step) { + os << conditional_step; + }; qc_ptr_->visit_circuit_step_( - qc_ptr_->circuit_[ip_], gate_step_visitor, - measurement_step_visitor, nop_step_visitor); + qc_ptr_->circuit_[ip_], conditional_step_visitor, + gate_step_visitor, measurement_step_visitor, nop_step_visitor); return os; } @@ -5363,9 +5413,12 @@ inline std::string QCircuit::to_JSON(bool enclosed_in_curly_brackets) const { auto nop_step_visitor = [&](const internal::QCircuitNOPStep&) { result += std::string{"\"NOP\""} + "}"; }; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep&) {}; - visit_circuit_step_(elem.get_step(), gate_step_visitor, - measurement_step_visitor, nop_step_visitor); + visit_circuit_step_(elem.get_step(), conditional_step_visitor, + gate_step_visitor, measurement_step_visitor, + nop_step_visitor); } // end for result += "], "; // end steps @@ -6889,9 +6942,9 @@ circuit_as_iterators(const QCircuit& qc) { /** * \brief Puts a quantum (sub)-circuit description in the canonical form, - * i.e., starting with the first measurement step from the circuit range - * [start, finish), pushes all measurements and cCTRLs to the end of the - * circuit + * i.e., starting with the first measurement step in the circuit range + * [start, finish), pushes all cCTRLs and measurements to the end of the + * circuit, so the circuit will be of the form [Gates, cCTRLs, Measurements] * * \note This function does not interchange measurements, i.e., the re-ordering * is stable diff --git a/include/qpp/classes/qengine.hpp b/include/qpp/classes/qengine.hpp index 5b7fb4df7..050f3fc2b 100644 --- a/include/qpp/classes/qengine.hpp +++ b/include/qpp/classes/qengine.hpp @@ -32,6 +32,8 @@ #ifndef QPP_CLASSES_QENGINE_HPP_ #define QPP_CLASSES_QENGINE_HPP_ +#include + #include #include #include @@ -1123,8 +1125,8 @@ class QEngineT : public QBaseEngine { /** * \brief Executes one step in the quantum circuit description * - * \note Override only this QEngine::execute() member function in every - * derived class to achieve the desired behaviour + * \note Override only this member function in every derived class to + * achieve the desired behaviour * * \param elem Step to be executed * \return Reference to the current instance @@ -1153,9 +1155,19 @@ class QEngineT : public QBaseEngine { auto nop_step_visitor = [&](const internal::QCircuitNOPStep& nop_step) { return this->execute_nop_step_(nop_step); }; + auto conditional_step_visitor = + [&](const internal::QCircuitConditionalStep& conditional_step) { + auto func = conditional_step.func_; + if (func.has_value()) { + // simply call the functor + func.value()(qeng_st_.dits_); + } + return; + }; std::visit( overloaded{ + conditional_step_visitor, gate_step_visitor, measurement_step_visitor, nop_step_visitor, diff --git a/include/qpp/internal/classes/qcircuit_conditional_step.hpp b/include/qpp/internal/classes/qcircuit_conditional_step.hpp new file mode 100644 index 000000000..c09e67538 --- /dev/null +++ b/include/qpp/internal/classes/qcircuit_conditional_step.hpp @@ -0,0 +1,155 @@ +/* + * This file is part of Quantum++. + * + * Copyright (c) 2017 - 2024 softwareQ Inc. All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * \file qpp/internal/classes/qcircuit_conditional_step.hpp + * \brief qpp::internal::QCircuitConditionalStep + */ + +#ifndef QPP_INTERNAL_CLASSES_QCIRCUIT_CONDITIONAL_STEP_HPP_ +#define QPP_INTERNAL_CLASSES_QCIRCUIT_CONDITIONAL_STEP_HPP_ + +#include + +#include "qpp/input_output.hpp" + +#include "qpp/classes/idisplay.hpp" + +namespace qpp { +namespace internal { +/** + * \brief One step consisting only of measurements in the circuit + */ +struct QCircuitConditionalStep : IDisplay { + /** + * \brief Type of measurement being executed in a measurement step + */ + enum class Type { + NONE, ///< no condition + + IF, ///< if branch statement + + ELSE, ///< else branch statement + + ENDIF, ///< end if statement + }; + + /** + * \brief Extraction operator overload for + * qpp::internal::QCircuitConditionalStep::Type enum class + * + * \param os Output stream passed by reference + * \param measure_type qpp::internal::QCircuitConditionalStep::Type enum + * class + * \return Reference to the output stream + */ + friend std::ostream& operator<<(std::ostream& os, + const Type& condition_type) { + switch (condition_type) { + case Type::NONE: + os << "CONDITIONAL NONE"; + break; + case Type::IF: + os << "IF"; + break; + case Type::ELSE: + os << "ELSE"; + break; + case Type::ENDIF: + os << "ENDIF"; + break; + } + + return os; + } + + Type condition_type_ = Type::NONE; ///< condition type + std::optional + jump_if_false_; ///< where to jump (relative) when condition is false + std::optional func_{}; ///< condition function + + /** + * \brief Default constructor + */ + QCircuitConditionalStep() = default; + + /** + * \brief Constructs a conditional step instance + * + * \tparam Func Conditional functor + * \param condition_type Measurement type + */ + explicit QCircuitConditionalStep( + Type condition_type, std::optional func = std::nullopt) + : condition_type_{condition_type}, func_{func} {} + + /** + * \brief Equality operator + * + * \param rhs qpp::internal::QCircuitConditionalStep against which the + * equality is being tested \return True if the + * qpp::internal::QCircuitConditionalStep(s) are equal, false otherwise + */ + bool operator==(const QCircuitConditionalStep& rhs) const noexcept { + return std::tie(rhs.condition_type_) == std::tie(condition_type_); + } + + /** + * \brief Inequality operator + * + * \param rhs qpp::internal::QCircuitConditionalStep against which the + * inequality is being tested \return True if the + * qpp::internal::QCircuitConditionalStep(s) are not equal, false + otherwise + */ + bool operator!=(const QCircuitConditionalStep& rhs) const noexcept { + return !(*this == rhs); + } + + private: + /** + * \brief qpp::IDisplay::display() override + * + * Writes to the output stream a textual representation of the + * \a qpp::internal::QCircuitConditionalStep instance + * + * \param os Output stream passed by reference + * \return Reference to the output stream + */ + std::ostream& display(std::ostream& os) const override { + os << condition_type_; + if (condition_type_ == Type::IF) { + os << ", "; + } + + return os; + } +}; + +} /* namespace internal */ +} /* namespace qpp */ + +#endif /* QPP_INTERNAL_CLASSES_QCIRCUIT_CONDITIONAL_STEP_HPP_ */ diff --git a/include/qpp/types.hpp b/include/qpp/types.hpp index d678b448e..72e6d839c 100644 --- a/include/qpp/types.hpp +++ b/include/qpp/types.hpp @@ -238,6 +238,11 @@ using expr_t = Eigen::Matrix< internal::eval_t::RowsAtCompileTime == 1 ? 1 : Eigen::Dynamic, internal::eval_t::ColsAtCompileTime == 1 ? 1 : Eigen::Dynamic>; +/** + * \brief Conditional functor type in qpp::QCircuit conditional statements + */ +using cond_func_t = std::function)>; + /** * \brief Variant type-matching utility for std::visit * \tparam Ts Type list