diff --git a/examples/shors_algorithm.hpp b/examples/shors_algorithm.hpp index 9e41b6e..7a0985f 100644 --- a/examples/shors_algorithm.hpp +++ b/examples/shors_algorithm.hpp @@ -4,17 +4,15 @@ #include -using namespace QPP; - /// @brief U gate for N = 15 -Circuit::CircuitGate UGate(const size_t& a, const unsigned long& power){ +QPP::Circuit::CircuitGate UGate(const size_t& a, const unsigned long& power){ // Controlled multiplication by a mod 15 // Check if a is not coprime to N (15) if(a != 2 && a != 4 && a != 7 && a != 8 && a != 11 && a != 13){ throw std::invalid_argument("a must be coprime to N (15)"); } - auto circuit = Circuit(std::make_shared>(), 4); + auto circuit = Circuit(std::make_shared>(), 4); for (size_t iteration = 0; iteration < power; iteration++) { if(a == 2 || a == 13){ circuit.addSwapGate(2, 3); @@ -41,8 +39,8 @@ Circuit::CircuitGate UGate(const size_t& a, const unsigned long& power){ } /// @brief Quantum Fourier Transform -Circuit CQFT(const std::shared_ptr>& probabilityEngine, const size_t &n) { - Circuit circuit(probabilityEngine, n, n); +QPP::Circuit CQFT(const std::shared_ptr>& probabilityEngine, const size_t &n) { + QPP::Circuit circuit(probabilityEngine, n, n); // Apply inverse quantum Fourier transform for (size_t index = 0; index < n / 2; index++) { @@ -64,12 +62,12 @@ Circuit CQFT(const std::shared_ptr>& probabili /// f(x) = a^x mod N /// where a and N are integers and a is coprime to N /// The period of the function is the smallest positive integer r such that f(x) = f(x + r) = 1 -Circuit::CompoundResult shorsAlgorithm(unsigned long a, unsigned long countingQubits, unsigned long repetitions = 1000) { +inline QPP::Circuit::CompoundResult shorsAlgorithm(unsigned long a, unsigned long countingQubits, unsigned long repetitions = 1000) { // Create a Quantum Circuit with N counting qubits plus 4 qubits for U to act on. - const std::shared_ptr> &probabilityEngine = std::make_shared>(); + const std::shared_ptr> &probabilityEngine = std::make_shared>(); - auto circuit = Circuit(probabilityEngine, countingQubits + 4, countingQubits); + auto circuit = QPP::Circuit(probabilityEngine, countingQubits + 4, countingQubits); // Initialize counting qubits to |+> for (size_t i = 0; i < countingQubits; i++) { diff --git a/examples/teleportation.hpp b/examples/teleportation.hpp new file mode 100644 index 0000000..1ce292c --- /dev/null +++ b/examples/teleportation.hpp @@ -0,0 +1,47 @@ +#include + +#include "../include/circuit.hpp" + +inline void runQuantumTeleportationExample(){ + // Create a Quantum Circuit with 3 qubits and 2 classical bits + const std::shared_ptr> &probabilityEngine = std::make_shared>(); + auto circuit = QPP::Circuit(probabilityEngine, 3, 2); + + std::cout << "Quantum Teleportation Example" << std::endl << std::endl; + + std::cout << "First step: Initialize the qubit we want to teleport to a random state" << std::endl << std::endl; + + // Initialize the first qubit in a random state + circuit.addInitGate(0, QPP::Qubit::State::Random(probabilityEngine)); + + std::cout << circuit << std::endl << std::endl; + + std::cout << "Second step: Create an entangled pair of qubits" << std::endl << std::endl; + + circuit.addHadamardGate(1); + circuit.addCXGate(1, 2); + + std::cout << circuit << std::endl << std::endl; + + std::cout << "Third step: Entangle the teleportation qubit to the second qubit" << std::endl << std::endl; + + circuit.addCXGate(0, 1); + circuit.addHadamardGate(0); + + std::cout << circuit << std::endl << std::endl; + + std::cout << "Fourth step: Measure the first two qubits" << std::endl << std::endl; + + // Measure the first two qubits + circuit.addMeasureGate({{0, 0}, {1, 1}}); + + std::cout << circuit << std::endl << std::endl; + + std::cout << "Fifth step: Apply the appropriate gates based on the measurement results" << std::endl << std::endl; + + // Apply the appropriate gates based on the measurement results + circuit.addGate(std::make_unique::XGate>(2)->makeControlled(1, true)); + circuit.addGate(std::make_unique::ZGate>(2)->makeControlled(0, true)); + + std::cout << circuit << std::endl << std::endl; +} \ No newline at end of file diff --git a/include/circuit.hpp b/include/circuit.hpp index b76e4d0..11125af 100644 --- a/include/circuit.hpp +++ b/include/circuit.hpp @@ -32,12 +32,13 @@ namespace QPP { /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. template class Circuit : public Representable { - private: + public: class Gate : public Representable { friend class Circuit; public : + [[nodiscard]] constexpr virtual const char* getSymbol() const = 0; typedef std::vector> Drawings; /// @brief Applies the gate to the given circuitPointer. @@ -57,7 +58,8 @@ namespace QPP { /// @brief Get a controlled version of the gate. /// @param controlIndex The index of the control qubit. /// @return A pointer to the controlled gate. - [[nodiscard]] std::unique_ptr makeControlled(const size_t &controlIndex) const; + [[nodiscard]] std::unique_ptr + makeControlled(const size_t &controlIndex, const bool &classic = false) const; protected: /// @brief Returns a standard string representation of the gate based on an identifier. @@ -85,12 +87,17 @@ namespace QPP { [[nodiscard]] bool getControlState(Circuit *circuit) const; + [[nodiscard]] size_t getControlIndex() const; + void verify(const Circuit *circuit) const override; }; + private: + class InvalidQubitIndexException : public std::runtime_error { public: explicit InvalidQubitIndexException(const size_t &qubitIndex); + private: const size_t index; }; @@ -98,6 +105,7 @@ namespace QPP { class InvalidClassicBitIndexException : public std::runtime_error { public: explicit InvalidClassicBitIndexException(const size_t &classicIndex); + private: const size_t index; }; @@ -154,6 +162,19 @@ namespace QPP { void addResult(const Result &result); }; + //#region Gates + + class SingleTargetGate : public virtual Gate { + protected: + const size_t qubitIndex; + + explicit SingleTargetGate(const size_t &targetIndex); + + void verify(const Circuit *circuit) const override; + public: + [[nodiscard]] size_t getTargetIndex() const; + }; + /// @class MeasureGate /// @brief A class representing a measure gate. /// @@ -173,6 +194,10 @@ namespace QPP { public: + [[nodiscard]] constexpr const char* getSymbol() const override { + return "H"; + } + /// @brief Creates a MeasureGate with the given vector of qubit-classic bit pairs. /// @param qubitClassicBitPairs The vector of qubit-classic bit pairs. explicit MeasureGate(const std::vector> &qubitClassicBitPairs); @@ -195,16 +220,18 @@ namespace QPP { /// /// A Hadamard gate is a gate that applies a Hadamard transformation to a qubit. /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. - class HadamardGate : public virtual Gate { + class HadamardGate : public virtual SingleTargetGate { private: protected: - size_t qubitIndex; - [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; public: + [[nodiscard]] constexpr const char* getSymbol() const override { + return "H"; + } + /// @brief Creates a HadamardGate with the given qubit index. /// @param qubitIndex The qubit index. explicit HadamardGate(const size_t &qubitIndex); @@ -234,6 +261,10 @@ namespace QPP { public: + [[nodiscard]] constexpr const char* getSymbol() const override { + return "CH"; + } + /// @brief Creates a CXGate with the given control qubit index and target qubit index. /// @param controlQubitIndex The control qubit index. /// @param targetQubitIndex The target qubit index. @@ -257,14 +288,17 @@ namespace QPP { /// /// A NOT gate is a gate that applies a NOT transformation to a qubit. /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. - class XGate : public virtual Gate { + class XGate : public virtual SingleTargetGate { protected: - size_t qubitIndex; - [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "X"; + } + /// @brief Creates a XGate with the given qubit index. /// @param qubitIndex The qubit index. explicit XGate(const size_t &qubitIndex); @@ -294,6 +328,10 @@ namespace QPP { public: + [[nodiscard]] constexpr const char* getSymbol() const override { + return "CX"; + } + /// @brief Creates a CXGate with the given control qubit index and target qubit index. /// @param controlQubitIndex The control qubit index. /// @param targetQubitIndex The target qubit index. @@ -317,14 +355,17 @@ namespace QPP { /// /// A Y gate is a gate that applies a Y transformation to a qubit. /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. - class YGate : public virtual Gate { + class YGate : public virtual SingleTargetGate { protected: - size_t qubitIndex; - [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "Y"; + } + /// @brief Creates a YGate with the given qubit index. /// @param qubitIndex The qubit index. explicit YGate(const size_t &qubitIndex); @@ -354,6 +395,10 @@ namespace QPP { public: + [[nodiscard]] constexpr const char* getSymbol() const override { + return "CY"; + } + /// @brief Creates a CYGate with the given control qubit index and target qubit index. /// @param controlQubitIndex The control qubit index. /// @param targetQubitIndex The target qubit index. @@ -377,14 +422,17 @@ namespace QPP { /// /// A Z gate is a gate that applies a Z transformation to a qubit. /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. - class ZGate : public virtual Gate { + class ZGate : public virtual SingleTargetGate { protected: - size_t qubitIndex; - [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "Z"; + } + /// @brief Creates a ZGate with the given qubit index. /// @param qubitIndex The qubit index. explicit ZGate(const size_t &qubitIndex); @@ -413,6 +461,11 @@ namespace QPP { getDrawings(const Circuit *circuit) const override; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "CZ"; + } + /// @brief Creates a CZGate with the given control qubit index and target qubit index. /// @param controlQubitIndex The control qubit index. /// @param targetQubitIndex The target qubit index. @@ -438,20 +491,25 @@ namespace QPP { /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. class SwapGate : public virtual Gate { protected: - size_t qubitIndex1; - size_t qubitIndex2; + const size_t qubitIndex1; + const size_t qubitIndex2; [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; class SwapSameQubitException : public std::runtime_error { protected: - size_t qubitIndex; + const size_t qubitIndex; public: explicit SwapSameQubitException(const size_t &qubitIndex); }; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "SWAP"; + } + /// @brief Creates a SwapGate with the given qubit indices. /// @param qubitIndex1 The first qubit index. /// @param qubitIndex2 The second qubit index. @@ -477,17 +535,24 @@ namespace QPP { /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. class CustomControlledGate : public Gate { protected: - size_t controlIndex; + const size_t controlIndex; + const bool classic; std::unique_ptr gatePointer; [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "C[]"; + } + /// @brief Creates a CustomControlledGate with the given control qubit index and gate. /// @param controlQubitIndex The control qubit index. /// @param gate The gate to apply to the target qubit if the control qubit is 1. - CustomControlledGate(const size_t &controlQubitIndex, std::unique_ptr gate); + CustomControlledGate(const size_t &controlQubitIndex, std::unique_ptr gate, + const bool &classic = false); /// @brief Copy constructor. /// @param other The CustomControlledGate to copy. @@ -525,6 +590,7 @@ namespace QPP { class InvalidQubitIndicesException : public std::runtime_error { public: explicit InvalidQubitIndicesException(const CircuitGate &gate, const std::vector &indices); + private: const size_t qubitCount; const size_t indexCount; @@ -532,6 +598,10 @@ namespace QPP { public: + [[nodiscard]] constexpr const char* getSymbol() const override { + return "CG"; + } + /// @brief The name of the CircuitGate. /// @details The name of the CircuitGate is used to identify the gate in the circuitPointer. /// Has default value "NEW_CIRCUIT_GATE". @@ -583,15 +653,19 @@ namespace QPP { /// /// A Phase gate is a gate that applies a phase transformation to a qubit. /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. - class PhaseGate : public virtual Gate { + class PhaseGate : public virtual SingleTargetGate { protected: - size_t qubitIndex; double angle; [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "P"; + } + /// @brief Creates a PhaseGate with the given qubit index. /// @param qubitIndex The qubit index. explicit PhaseGate(const size_t &qubitIndex, const double &angle); @@ -616,12 +690,17 @@ namespace QPP { /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. class ControlledPhaseGate : public ControlledGate, public PhaseGate { protected: - size_t controlQubitIndex{}; + const size_t controlQubitIndex; [[nodiscard]] typename Gate::Drawings getDrawings(const Circuit *circuit) const override; public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "CP"; + } + /// @brief Creates a ControlledPhaseGate with the given control qubit index. /// @param controlQubitIndex The control qubit index. explicit ControlledPhaseGate(const size_t &controlQubitIndex, const size_t &qubitIndex, @@ -640,6 +719,83 @@ namespace QPP { std::unique_ptr clone() const override; }; + /// @class InitGate + /// @brief A class representing an Init gate. + /// + /// An Init gate is a gate that initializes a qubit to a given state. + /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. + class InitGate : public virtual SingleTargetGate { + protected: + const typename Qubit::State state; + + [[nodiscard]] typename Gate::Drawings + getDrawings(const Circuit *circuit) const override; + + public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "INIT"; + } + + /// @brief Creates an InitGate with the given qubit index. + /// @param qubitIndex The qubit index. + InitGate(const size_t &qubitIndex, const typename Qubit::State &state); + + /// @brief Creates an InitGate from another InitGate. + /// @param other The other InitGate. + InitGate(const InitGate &other); + + /// @brief Returns a string representation of the Init gate. + /// @return A string representation of the Init gate. + [[nodiscard]] std::string getRepresentation() const override; + + /// @brief Applies the Init gate to the given circuitPointer. + /// @param circuit The circuitPointer to apply the Init gate to. + void apply(Circuit *circuit) override; + + void verify(const Circuit *circuit) const override; + + std::unique_ptr clone() const override; + }; + + /// @class PrintGate + /// @brief A class representing a Print gate. + /// + /// A Print gate is a gate that prints the state of a qubit. + /// @tparam FloatingNumberType The type of the floating-point number used to represent the probabilities. + class PrintGate : public virtual SingleTargetGate { + protected: + std::ostream *outputStream; + + [[nodiscard]] typename Gate::Drawings + getDrawings(const Circuit *circuit) const override; + + public: + + [[nodiscard]] constexpr const char* getSymbol() const override { + return "PRINT"; + } + + /// @brief Creates a PrintGate with the given qubit index. + /// @param qubitIndex The qubit index. + [[deprecated("PrintGate gives unreliable results.")]] + explicit PrintGate(const size_t &qubitIndex, std::ostream *outputStream = &std::cout); + + /// @brief Returns a string representation of the Print gate. + /// @return A string representation of the Print gate. + [[nodiscard]] std::string getRepresentation() const override; + + /// @brief Applies the Print gate to the given circuitPointer. + /// @param circuit The circuitPointer to apply the Print gate to. + void apply(Circuit *circuit) override; + + void verify(const Circuit *circuit) const override; + + std::unique_ptr clone() const override; + }; + + //#endregion + /// @brief Creates a Circuit with the given probability engine, qubit count and classic bit count. /// @param probabilityEngine The probability engine to use. /// @param qubitCount The qubit count. @@ -665,6 +821,8 @@ namespace QPP { /// @details Resets the circuit by resetting the qubits. void reset(); + //#region Gate Adders + /// @brief Adds an already constructed CircuitGate to the circuit. /// @param gate The CircuitGate to add. void addGate(std::unique_ptr gate, const std::vector &qubitIndices); @@ -733,6 +891,28 @@ namespace QPP { void addControlledPhaseGate(const size_t &controlQubitIndex, const size_t &targetQubitIndex, const FloatingNumberType &angle); + void addInitGate(const size_t &qubitIndex, const typename Qubit::State &state); + + void addPrintGate(const size_t &qubitIndex); + + //#endregion + + //#region Getters + + /// @brief Returns the qubit count. + /// @return The qubit count. + [[nodiscard]] size_t getQubitCount() const; + + /// @brief Returns the classic bit count. + /// @return The classic bit count. + [[nodiscard]] size_t getClassicBitCount() const; + + /// @brief Returns the gates. + /// @return The gates. + [[nodiscard]] const std::vector> &getGates() const; + + //#endregion + Circuit &operator+=(const Circuit &other); CircuitGate toGate() const; @@ -749,7 +929,10 @@ namespace QPP { #include "templates/circuit.tpp" +#include "templates/single_target.tpp" #include "templates/measure.tpp" +#include "templates/init.tpp" +#include "templates/print.tpp" #include "templates/xgate.tpp" #include "templates/ygate.tpp" #include "templates/zgate.tpp" diff --git a/include/qubit.hpp b/include/qubit.hpp index 0907443..367b86f 100644 --- a/include/qubit.hpp +++ b/include/qubit.hpp @@ -10,6 +10,7 @@ #include #include +#include #include "classic_bit.hpp" #include "probability.hpp" #include "representable.hpp" @@ -86,6 +87,11 @@ namespace QPP { ///@brief Gets the representation of the state. ///@return The representation of the state as the sum of the alpha and beta amplitudes multiplied with the respective kets. [[nodiscard]] std::string getRepresentation() const override; + + ///@brief Returns a random state. + ///@param probabilityEngine The probability engine to use. + ///@return A random state. + static State Random(std::shared_ptr> probabilityEngine); }; ///@brief Creates a Qubit with the given probability engine. diff --git a/include/templates/circuit.tpp b/include/templates/circuit.tpp index f25a377..c8e403e 100644 --- a/include/templates/circuit.tpp +++ b/include/templates/circuit.tpp @@ -284,6 +284,17 @@ void Circuit::addControlledPhaseGate(const size_t &controlQu addGate(static_cast>(std::make_unique(controlQubitIndex, targetQubitIndex, angle))); } +template +void Circuit::addInitGate(const size_t &qubitIndex, + const typename Qubit::State &state) { + addGate(std::make_unique(qubitIndex, state)); +} + +template +void Circuit::addPrintGate(const size_t &qubitIndex) { + addGate(std::make_unique(qubitIndex)); +} + template void Circuit::reset() { for(auto& qubit : qubits){ @@ -327,8 +338,8 @@ typename Circuit::CircuitGate Circuit::t } template -std::unique_ptr::Gate> Circuit::Gate::makeControlled(const size_t& controlIndex) const { - return std::make_unique(controlIndex, clone()); +std::unique_ptr::Gate> Circuit::Gate::makeControlled(const size_t& controlIndex, const bool& classic) const { + return std::make_unique(controlIndex, clone(), classic); } template @@ -350,4 +361,24 @@ Circuit &Circuit::operator=(const Circui std::swap(temp.gates, gates); } return *this; +} + +template +size_t Circuit::getQubitCount() const { + return qubits.size(); +} + +template +size_t Circuit::getClassicBitCount() const { + return classicBits.size(); +} + +template +const std::vector::Gate>> &Circuit::getGates() const{ + return gates; +} + +template +size_t Circuit::ControlledGate::getControlIndex() const { + return controlIndex; } \ No newline at end of file diff --git a/include/templates/control.tpp b/include/templates/control.tpp index 8fe01d6..247836f 100644 --- a/include/templates/control.tpp +++ b/include/templates/control.tpp @@ -1,6 +1,7 @@ template typename Circuit::Gate::Drawings Circuit::CustomControlledGate::getDrawings(const Circuit *circuit) const { + // TODO: Drawing for classic controlled gates typename Gate::Drawings drawings = gatePointer->getDrawings(circuit); const size_t drawingWidth = drawings[controlIndex][1].length(); const size_t lineLength = std::string("─").length(); @@ -13,14 +14,17 @@ Circuit::CustomControlledGate::getDrawings(const Circuit Circuit::CustomControlledGate::CustomControlledGate(const size_t &controlQubitIndex, - std::unique_ptr gate): + std::unique_ptr gate, + const bool& classic): controlIndex(controlQubitIndex), + classic(classic), gatePointer(gate->clone()){} template Circuit::CustomControlledGate::CustomControlledGate(const Circuit::CustomControlledGate &other): controlIndex(other.controlIndex), + classic(other.classic), gatePointer(other.gatePointer->clone()){} template @@ -30,15 +34,22 @@ std::string Circuit::CustomControlledGate::getRepresentation template void Circuit::CustomControlledGate::apply(Circuit *circuit) { - auto& controlQubit = circuit->qubits[controlIndex]; - if(controlQubit.measure().getState() == ClassicBit::State::ONE){ - gatePointer->apply(circuit); + if(classic){ + auto& controlQubit = circuit->classicBits[controlIndex]; + if(controlQubit.getState() == ClassicBit::State::ONE){ + gatePointer->apply(circuit); + } + } else { + auto& controlQubit = circuit->qubits[controlIndex]; + if(controlQubit.measure().getState() == ClassicBit::State::ONE){ + gatePointer->apply(circuit); + } } } template void Circuit::CustomControlledGate::verify(const Circuit* circuit) const { - if(controlIndex >= circuit->qubits.size()){ + if(controlIndex >= (classic ? circuit->classicBits.size() : circuit->qubits.size())){ throw Circuit::InvalidQubitIndexException(controlIndex); } gatePointer->verify(circuit); diff --git a/include/templates/hadamard.tpp b/include/templates/hadamard.tpp index f2c2d6d..8acd2ea 100644 --- a/include/templates/hadamard.tpp +++ b/include/templates/hadamard.tpp @@ -1,7 +1,7 @@ template typename Circuit::Gate::Drawings Circuit::HadamardGate::getDrawings( const Circuit *circuit) const { - return Circuit::Gate::getStandardDrawing(circuit, "H", qubitIndex); + return Circuit::Gate::getStandardDrawing(circuit, "H", SingleTargetGate::qubitIndex); } template @@ -11,7 +11,7 @@ typename Circuit::Gate::Drawings Circuit } template -Circuit::HadamardGate::HadamardGate(const size_t &qubitIndex): qubitIndex(qubitIndex) {} +Circuit::HadamardGate::HadamardGate(const size_t &qubitIndex): SingleTargetGate(qubitIndex) {} template Circuit::ControlledHadamardGate::ControlledHadamardGate(const size_t &controlIndex, const size_t &targetIndex): ControlledGate(controlIndex), @@ -19,7 +19,7 @@ Circuit::ControlledHadamardGate::ControlledHadamardGate(cons template std::string Circuit::HadamardGate::getRepresentation() const { - return "H[Q#" + std::to_string(qubitIndex) + "]"; + return "H[Q#" + std::to_string(SingleTargetGate::qubitIndex) + "]"; } template @@ -29,7 +29,7 @@ std::string Circuit::ControlledHadamardGate::getRepresentati template void Circuit::HadamardGate::apply(Circuit *circuit) { - auto& qubit = circuit->qubits[qubitIndex]; + auto& qubit = circuit->qubits[SingleTargetGate::qubitIndex]; const auto alpha = qubit.getState().getAlpha(); const auto beta = qubit.getState().getBeta(); const auto newAlpha = (alpha + beta) / (FloatingNumberType)std::sqrt(2); @@ -57,8 +57,8 @@ std::unique_ptr::Gate> Circuit void Circuit::HadamardGate::verify(const Circuit* circuit) const { - if(qubitIndex >= circuit->qubits.size()){ - throw Circuit::InvalidQubitIndexException(qubitIndex); + if(SingleTargetGate::qubitIndex >= circuit->qubits.size()){ + throw Circuit::InvalidQubitIndexException(SingleTargetGate::qubitIndex); } } diff --git a/include/templates/init.tpp b/include/templates/init.tpp new file mode 100644 index 0000000..e82c2f1 --- /dev/null +++ b/include/templates/init.tpp @@ -0,0 +1,36 @@ +template +Circuit::InitGate::InitGate(const size_t &qubitIndex, + const typename Qubit::State &state): + SingleTargetGate(qubitIndex), + state(state) {} + +template +Circuit::InitGate::InitGate(const Circuit::InitGate &other): + SingleTargetGate(other.qubitIndex), + state(other.state) {} + +template +void Circuit::InitGate::apply(Circuit *circuit) { + circuit->qubits[SingleTargetGate::qubitIndex].setState(state); +} + +template +void Circuit::InitGate::verify(const Circuit *circuit) const { + SingleTargetGate::verify(circuit); +} + +template +std::unique_ptr::Gate> Circuit::InitGate::clone() const { + return std::make_unique(*this); +} + +template +typename Circuit::Gate::Drawings Circuit::InitGate::getDrawings( + const Circuit *circuit) const { + return Circuit::Gate::getStandardDrawing(circuit, "INIT", SingleTargetGate::qubitIndex); +} + +template +std::string Circuit::InitGate::getRepresentation() const { + return "INIT[Q#" + std::to_string(SingleTargetGate::qubitIndex) + "]"; +} \ No newline at end of file diff --git a/include/templates/phase.tpp b/include/templates/phase.tpp index 72cfc84..578c5c2 100644 --- a/include/templates/phase.tpp +++ b/include/templates/phase.tpp @@ -7,18 +7,18 @@ typename Circuit::Gate::Drawings Circuit template typename Circuit::Gate::Drawings Circuit::PhaseGate::getDrawings( const Circuit *circuit) const { - return Circuit::Gate::getStandardDrawing(circuit, "P", qubitIndex); + return Circuit::Gate::getStandardDrawing(circuit, "P", SingleTargetGate::qubitIndex); } template -Circuit::PhaseGate::PhaseGate(const size_t &qubitIndex, const double& angle): qubitIndex(qubitIndex), angle(angle) {} +Circuit::PhaseGate::PhaseGate(const size_t &qubitIndex, const double& angle): SingleTargetGate(qubitIndex), angle(angle) {} template Circuit::ControlledPhaseGate::ControlledPhaseGate(const size_t &controlQubitIndex, const size_t &qubitIndex, const double& angle): Circuit::ControlledGate(controlQubitIndex), Circuit::PhaseGate(qubitIndex, angle) {} template std::string Circuit::PhaseGate::getRepresentation() const { - return "P[Q#" + std::to_string(qubitIndex) + "]"; + return "P[Q#" + std::to_string(SingleTargetGate::qubitIndex) + "]"; } template @@ -28,7 +28,7 @@ std::string Circuit::ControlledPhaseGate::getRepresentation( template void Circuit::PhaseGate::apply(Circuit *circuit) { - auto& targetQubit = circuit->qubits[qubitIndex]; + auto& targetQubit = circuit->qubits[SingleTargetGate::qubitIndex]; const auto newAlpha = targetQubit.getState().getAlpha(); const auto newBeta = targetQubit.getState().getBeta() * std::exp(std::complex(0, angle)); targetQubit.setState(newAlpha, newBeta); @@ -53,8 +53,8 @@ std::unique_ptr::Gate> Circuit void Circuit::PhaseGate::verify(const Circuit* circuit) const { - if(qubitIndex >= circuit->qubits.size()){ - throw Circuit::InvalidQubitIndexException(qubitIndex); + if(SingleTargetGate::qubitIndex >= circuit->qubits.size()){ + throw Circuit::InvalidQubitIndexException(SingleTargetGate::qubitIndex); } } diff --git a/include/templates/print.tpp b/include/templates/print.tpp new file mode 100644 index 0000000..2039d86 --- /dev/null +++ b/include/templates/print.tpp @@ -0,0 +1,30 @@ +template +Circuit::PrintGate::PrintGate(const size_t &qubitIndex, std::ostream *outputStream) : + SingleTargetGate(qubitIndex), + outputStream(outputStream){} + +template +void Circuit::PrintGate::apply(Circuit *circuit) { + *outputStream << circuit->qubits[SingleTargetGate::qubitIndex].getState() << std::endl; +} + +template +void Circuit::PrintGate::verify(const Circuit* circuit) const { + SingleTargetGate::verify(circuit); +} + +template +std::unique_ptr::Gate> Circuit::PrintGate::clone() const { + return std::make_unique(*this); +} + +template +typename Circuit::Gate::Drawings Circuit::PrintGate::getDrawings( + const Circuit *circuit) const { + return Circuit::Gate::getStandardDrawing(circuit, "PRINT", SingleTargetGate::qubitIndex); +} + +template +std::string Circuit::PrintGate::getRepresentation() const { + return "PRINT[Q#" + std::to_string(SingleTargetGate::qubitIndex) + "]"; +} \ No newline at end of file diff --git a/include/templates/qubit.tpp b/include/templates/qubit.tpp index cc20730..24497fd 100644 --- a/include/templates/qubit.tpp +++ b/include/templates/qubit.tpp @@ -119,3 +119,18 @@ Qubit::State::InvalidStateException::InvalidStateException(c norm(state.getAlpha()) + norm(state.getBeta()))), state(state) {} + +template +typename Qubit::State Qubit::State::Random( + std::shared_ptr> probabilityEngine) { + const double p0 = probabilityEngine->getProbability(); + const double p1 = 1.0 - p0; + + const double phi0 = 2.0 * std::numbers::pi * probabilityEngine->getProbability(); + const double phi1 = 2.0 * std::numbers::pi * probabilityEngine->getProbability(); + + const auto alpha = std::sqrt(p0) * std::complex(std::cos(phi0), std::sin(phi0)); + const auto beta = std::sqrt(p1) * std::complex(std::cos(phi1), std::sin(phi1)); + + return State(probabilityEngine, alpha, beta); +} diff --git a/include/templates/single_target.tpp b/include/templates/single_target.tpp new file mode 100644 index 0000000..092f5cc --- /dev/null +++ b/include/templates/single_target.tpp @@ -0,0 +1,16 @@ +#pragma once + +template +size_t Circuit::SingleTargetGate::getTargetIndex() const { + return qubitIndex; +} + +template +Circuit::SingleTargetGate::SingleTargetGate(const size_t &targetIndex): qubitIndex(targetIndex) {} + +template +void Circuit::SingleTargetGate::verify(const Circuit *circuit) const { + if(qubitIndex >= circuit->qubits.size()){ + throw Circuit::InvalidQubitIndexException(qubitIndex); + } +} \ No newline at end of file diff --git a/include/templates/xgate.tpp b/include/templates/xgate.tpp index af60da5..45f14cb 100644 --- a/include/templates/xgate.tpp +++ b/include/templates/xgate.tpp @@ -7,21 +7,21 @@ typename Circuit::Gate::Drawings Circuit template typename Circuit::Gate::Drawings Circuit::XGate::getDrawings( const Circuit *circuit) const { - return Circuit::Gate::getStandardDrawing(circuit, "X", qubitIndex); + return Circuit::Gate::getStandardDrawing(circuit, "X", SingleTargetGate::qubitIndex); } template -Circuit::XGate::XGate(const size_t &qubitIndex): qubitIndex(qubitIndex) {} +Circuit::XGate::XGate(const size_t &qubitIndex): SingleTargetGate(qubitIndex) {} template Circuit::CXGate::CXGate(const size_t &controlQubitIndex, const size_t &qubitIndex): -Circuit::Gate(), +Circuit::SingleTargetGate(qubitIndex), Circuit::ControlledGate(controlQubitIndex), Circuit::XGate(qubitIndex) {} template std::string Circuit::XGate::getRepresentation() const { - return "X[Q#" + std::to_string(qubitIndex) + "]"; + return "X[Q#" + std::to_string(SingleTargetGate::qubitIndex) + "]"; } template @@ -31,7 +31,7 @@ std::string Circuit::CXGate::getRepresentation() const { template void Circuit::XGate::apply(Circuit *circuit) { - auto& targetQubit = circuit->qubits[qubitIndex]; + auto& targetQubit = circuit->qubits[SingleTargetGate::qubitIndex]; const auto newAlpha = targetQubit.getState().getBeta(); const auto newBeta = targetQubit.getState().getAlpha(); targetQubit.setState(newAlpha, newBeta); @@ -56,8 +56,8 @@ std::unique_ptr::Gate> Circuit void Circuit::XGate::verify(const Circuit* circuit) const { - if(qubitIndex >= circuit->qubits.size()){ - throw Circuit::InvalidQubitIndexException(qubitIndex); + if(SingleTargetGate::qubitIndex >= circuit->qubits.size()){ + throw Circuit::InvalidQubitIndexException(SingleTargetGate::qubitIndex); } } diff --git a/include/templates/ygate.tpp b/include/templates/ygate.tpp index 80f1b5b..db4c223 100644 --- a/include/templates/ygate.tpp +++ b/include/templates/ygate.tpp @@ -7,18 +7,18 @@ typename Circuit::Gate::Drawings Circuit template typename Circuit::Gate::Drawings Circuit::YGate::getDrawings( const Circuit *circuit) const { - return Circuit::Gate::getStandardDrawing(circuit, "Y", qubitIndex); + return Circuit::Gate::getStandardDrawing(circuit, "Y", SingleTargetGate::qubitIndex); } template -Circuit::YGate::YGate(const size_t &qubitIndex): qubitIndex(qubitIndex) {} +Circuit::YGate::YGate(const size_t &qubitIndex): SingleTargetGate(qubitIndex) {} template Circuit::CYGate::CYGate(const size_t &controlQubitIndex, const size_t &qubitIndex): Circuit::ControlledGate(controlQubitIndex), Circuit::XGate(qubitIndex) {} template std::string Circuit::YGate::getRepresentation() const { - return "Y[Q#" + std::to_string(qubitIndex) + "]"; + return "Y[Q#" + std::to_string(SingleTargetGate::qubitIndex) + "]"; } template @@ -28,7 +28,7 @@ std::string Circuit::CYGate::getRepresentation() const { template void Circuit::YGate::apply(Circuit *circuit) { - auto& targetQubit = circuit->qubits[qubitIndex]; + auto& targetQubit = circuit->qubits[SingleTargetGate::qubitIndex]; const auto newAlpha = targetQubit.getState().getBeta(); const auto newBeta = -targetQubit.getState().getAlpha(); targetQubit.setState(newAlpha, newBeta); @@ -53,8 +53,8 @@ std::unique_ptr::Gate> Circuit void Circuit::YGate::verify(const Circuit* circuit) const { - if(qubitIndex >= circuit->qubits.size()){ - throw Circuit::InvalidQubitIndexException(qubitIndex); + if(SingleTargetGate::qubitIndex >= circuit->qubits.size()){ + throw Circuit::InvalidQubitIndexException(SingleTargetGate::qubitIndex); } } diff --git a/include/templates/zgate.tpp b/include/templates/zgate.tpp index 95dbb06..38a416b 100644 --- a/include/templates/zgate.tpp +++ b/include/templates/zgate.tpp @@ -7,18 +7,18 @@ typename Circuit::Gate::Drawings Circuit template typename Circuit::Gate::Drawings Circuit::ZGate::getDrawings( const Circuit *circuit) const { - return Circuit::Gate::getStandardDrawing(circuit, "Z", qubitIndex); + return Circuit::Gate::getStandardDrawing(circuit, "Z", SingleTargetGate::qubitIndex); } template -Circuit::ZGate::ZGate(const size_t &qubitIndex): qubitIndex(qubitIndex) {} +Circuit::ZGate::ZGate(const size_t &qubitIndex): SingleTargetGate(qubitIndex) {} template Circuit::CZGate::CZGate(const size_t &controlQubitIndex, const size_t &qubitIndex): Circuit::ControlledGate(controlQubitIndex), Circuit::ZGate(qubitIndex) {} template std::string Circuit::ZGate::getRepresentation() const { - return "Z[Q#" + std::to_string(qubitIndex) + "]"; + return "Z[Q#" + std::to_string(SingleTargetGate::qubitIndex) + "]"; } template @@ -28,7 +28,7 @@ std::string Circuit::CZGate::getRepresentation() const { template void Circuit::ZGate::apply(Circuit *circuit) { - auto& targetQubit = circuit->qubits[qubitIndex]; + auto& targetQubit = circuit->qubits[SingleTargetGate::qubitIndex]; const auto newBeta = -targetQubit.getState().getBeta(); targetQubit.setState(targetQubit.getState().getAlpha(), newBeta); } @@ -52,8 +52,8 @@ std::unique_ptr::Gate> Circuit void Circuit::ZGate::verify(const Circuit* circuit) const { - if(qubitIndex >= circuit->qubits.size()){ - throw Circuit::InvalidQubitIndexException(qubitIndex); + if(SingleTargetGate::qubitIndex >= circuit->qubits.size()){ + throw Circuit::InvalidQubitIndexException(SingleTargetGate::qubitIndex); } }