From e66175979754944139de96c94188e9ec16b826f1 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Fri, 31 Jan 2025 23:11:26 +0100 Subject: [PATCH 1/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20QASM=20impo?= =?UTF-8?q?rt=20functionality=20and=20remove=20deprecated=20formats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ♻️ turn QASM parser into its own library - 🔥 remove import functionality from `QuantumComputation` the respective functionality is now provided by a separate `MQT::CoreQASM` library - ♻️ refactor QASM parser and improve imports Signed-off-by: burgholzer --- docs/mqt_core_ir.md | 33 +- include/mqt-core/Definitions.hpp | 2 +- include/mqt-core/ir/QuantumComputation.hpp | 41 +- .../ir/parsers/qasm3_parser/Token.hpp | 486 ----- .../qasm3_parser => qasm3}/Exception.hpp | 0 .../parsers/qasm3_parser => qasm3}/Gate.hpp | 2 +- include/mqt-core/qasm3/Importer.hpp | 178 ++ .../qasm3_parser => qasm3}/InstVisitor.hpp | 20 +- .../NestedEnvironment.hpp | 0 .../parsers/qasm3_parser => qasm3}/Parser.hpp | 87 +- .../qasm3_parser => qasm3}/Scanner.hpp | 32 +- .../qasm3_parser => qasm3}/Statement.hpp | 63 +- include/mqt-core/qasm3/Statement_fwd.hpp | 36 + .../qasm3_parser => qasm3}/StdGates.hpp | 0 include/mqt-core/qasm3/Token.hpp | 196 ++ .../parsers/qasm3_parser => qasm3}/Types.hpp | 6 +- include/mqt-core/qasm3/Types_fwd.hpp | 21 + .../passes/CompilerPass.hpp | 4 +- .../passes/ConstEvalPass.hpp | 67 +- .../passes/TypeCheckPass.hpp | 45 +- src/CMakeLists.txt | 3 + src/ir/QuantumComputation.cpp | 134 +- src/ir/parsers/QASM3Parser.cpp | 871 --------- src/ir/parsers/QCParser.cpp | 332 ---- src/ir/parsers/RealParser.cpp | 866 --------- src/ir/parsers/TFCParser.cpp | 272 --- src/ir/parsers/qasm3_parser/Statement.cpp | 36 - src/mqt/core/__init__.py | 4 +- src/mqt/core/ir/__init__.pyi | 22 +- src/python/ir/CMakeLists.txt | 3 +- .../ir/register_quantum_computation.cpp | 12 +- src/qasm3/CMakeLists.txt | 59 + src/qasm3/Importer.cpp | 876 +++++++++ .../parsers/qasm3_parser => qasm3}/Parser.cpp | 78 +- .../qasm3_parser => qasm3}/Scanner.cpp | 33 +- src/qasm3/Statement.cpp | 87 + src/qasm3/Token.cpp | 311 ++++ .../parsers/qasm3_parser => qasm3}/Types.cpp | 2 +- .../passes/ConstEvalPass.cpp | 72 +- .../passes/TypeCheckPass.cpp | 32 +- test/circuit_optimizer/CMakeLists.txt | 2 +- .../test_remove_final_measurements.cpp | 6 +- test/circuits/test.qc | 29 - test/circuits/test.real | 42 - test/circuits/test.tfc | 50 - test/dd/CMakeLists.txt | 1 + test/dd/test_dd_functionality.cpp | 3 +- test/ir/CMakeLists.txt | 1 + test/ir/test_io.cpp | 506 +++--- test/ir/test_qasm3_parser.cpp | 161 +- test/ir/test_qfr_functionality.cpp | 24 +- test/ir/test_real_parser.cpp | 1570 ----------------- test/na/CMakeLists.txt | 1 + test/na/test_nadefinitions.cpp | 3 +- test/zx/CMakeLists.txt | 1 + test/zx/test_zx_functionality.cpp | 7 +- 56 files changed, 2428 insertions(+), 5403 deletions(-) delete mode 100644 include/mqt-core/ir/parsers/qasm3_parser/Token.hpp rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/Exception.hpp (100%) rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/Gate.hpp (98%) create mode 100644 include/mqt-core/qasm3/Importer.hpp rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/InstVisitor.hpp (92%) rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/NestedEnvironment.hpp (100%) rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/Parser.hpp (65%) rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/Scanner.hpp (58%) rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/Statement.hpp (88%) create mode 100644 include/mqt-core/qasm3/Statement_fwd.hpp rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/StdGates.hpp (100%) create mode 100644 include/mqt-core/qasm3/Token.hpp rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/Types.hpp (97%) create mode 100644 include/mqt-core/qasm3/Types_fwd.hpp rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/passes/CompilerPass.hpp (92%) rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/passes/ConstEvalPass.hpp (58%) rename include/mqt-core/{ir/parsers/qasm3_parser => qasm3}/passes/TypeCheckPass.hpp (77%) delete mode 100644 src/ir/parsers/QASM3Parser.cpp delete mode 100644 src/ir/parsers/QCParser.cpp delete mode 100644 src/ir/parsers/RealParser.cpp delete mode 100644 src/ir/parsers/TFCParser.cpp delete mode 100644 src/ir/parsers/qasm3_parser/Statement.cpp create mode 100644 src/qasm3/CMakeLists.txt create mode 100644 src/qasm3/Importer.cpp rename src/{ir/parsers/qasm3_parser => qasm3}/Parser.cpp (92%) rename src/{ir/parsers/qasm3_parser => qasm3}/Scanner.cpp (94%) create mode 100644 src/qasm3/Statement.cpp create mode 100644 src/qasm3/Token.cpp rename src/{ir/parsers/qasm3_parser => qasm3}/Types.cpp (97%) rename src/{ir/parsers/qasm3_parser => qasm3}/passes/ConstEvalPass.cpp (90%) rename src/{ir/parsers/qasm3_parser => qasm3}/passes/TypeCheckPass.cpp (93%) delete mode 100755 test/circuits/test.qc delete mode 100755 test/circuits/test.real delete mode 100644 test/circuits/test.tfc delete mode 100644 test/ir/test_real_parser.cpp diff --git a/docs/mqt_core_ir.md b/docs/mqt_core_ir.md index 1457677ad..813d66539 100644 --- a/docs/mqt_core_ir.md +++ b/docs/mqt_core_ir.md @@ -298,16 +298,37 @@ qc.append(classic_controlled) print(qc) ``` -## Interfacing with other SDKs +## Interfacing with other SDKs and Formats -Since a {py:class}`~mqt.core.ir.QuantumComputation` can be imported from and exported to an OpenQASM 3.0 (or OpenQASM 2.0) string, any library that can work with OpenQASM is easy to use in conjunction with the {py:class}`~mqt.core.ir.QuantumComputation` class. +### OpenQASM -In addition, `mqt-core` can import [Qiskit](https://qiskit.org/) {py:class}`~qiskit.circuit.QuantumCircuit` objects directly. +OpenQASM is a widely used format for representing quantum circuits. +Its latest version, [OpenQASM 3](https://openqasm.com/index.html), is a powerful language that can express a wide range of quantum circuits. +MQT Core supports the full functionality of OpenQASM 2.0 (including classically controlled operations) and a growing subset of OpenQASM 3. ```{code-cell} ipython3 -from qiskit import QuantumCircuit +from mqt.core.ir import QuantumComputation -from mqt.core.plugins.qiskit import qiskit_to_mqt +qasm_str = """ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +h q[0]; +cx q[0], q[1]; +cx q[0], q[2]; +""" + +qc = QuantumComputation.from_qasm_str(qasm_str) + +print(qc) +``` + +### Qiskit + +In addition to OpenQASM, `mqt-core` can natively import [Qiskit](https://qiskit.org/) {py:class}`~qiskit.circuit.QuantumCircuit` objects. + +```{code-cell} ipython3 +from qiskit import QuantumCircuit # GHZ circuit in qiskit qiskit_qc = QuantumCircuit(3) @@ -319,6 +340,8 @@ qiskit_qc.draw(output="mpl", style="iqp") ``` ```{code-cell} ipython3 +from mqt.core.plugins.qiskit import qiskit_to_mqt + mqt_qc = qiskit_to_mqt(qiskit_qc) print(mqt_qc) ``` diff --git a/include/mqt-core/Definitions.hpp b/include/mqt-core/Definitions.hpp index 7ad594ea3..3bedfd691 100644 --- a/include/mqt-core/Definitions.hpp +++ b/include/mqt-core/Definitions.hpp @@ -50,7 +50,7 @@ static constexpr fp E = static_cast( 2.718281828459045235360287471352662497757247093699959574967L); // supported file formats -enum class Format : uint8_t { Real, OpenQASM2, OpenQASM3, TFC, QC, Tensor }; +enum class Format : uint8_t { OpenQASM2, OpenQASM3 }; /** * @brief 64bit mixing hash (from MurmurHash3) diff --git a/include/mqt-core/ir/QuantumComputation.hpp b/include/mqt-core/ir/QuantumComputation.hpp index 0c1d352fd..30f67bb7d 100644 --- a/include/mqt-core/ir/QuantumComputation.hpp +++ b/include/mqt-core/ir/QuantumComputation.hpp @@ -68,10 +68,8 @@ class QuantumComputation { std::unordered_set occurringVariables; public: - QuantumComputation() = default; - explicit QuantumComputation(std::size_t nq, std::size_t nc = 0U, + explicit QuantumComputation(std::size_t nq = 0, std::size_t nc = 0U, std::size_t s = 0); - explicit QuantumComputation(const std::string& filename, std::size_t s = 0U); QuantumComputation(QuantumComputation&& qc) noexcept = default; QuantumComputation& operator=(QuantumComputation&& qc) noexcept = default; QuantumComputation(const QuantumComputation& qc); @@ -82,13 +80,6 @@ class QuantumComputation { Permutation initialLayout{}; Permutation outputPermutation{}; - /** - * @brief Construct a QuantumComputation from an OpenQASM string - * @param qasm The OpenQASM 2.0 or 3.0 string - * @return The constructed QuantumComputation - */ - [[nodiscard]] static QuantumComputation fromQASM(const std::string& qasm); - /** * @brief Construct a QuantumComputation from CompoundOperation object * @details The function creates a copy of each operation in the compound @@ -372,9 +363,6 @@ class QuantumComputation { /// qubits occurring in the output permutation void stripIdleQubits(bool force = false); - void import(const std::string& filename); - void import(const std::string& filename, Format format); - void import(std::istream& is, Format format); void initializeIOMapping(); // append measurements to the end of the circuit according to the tracked // output permutation @@ -408,7 +396,8 @@ class QuantumComputation { void addQubit(Qubit logicalQubitIndex, Qubit physicalQubitIndex, std::optional outputQubitIndex); - QuantumComputation instantiate(const VariableAssignment& assignment) const; + [[nodiscard]] QuantumComputation + instantiate(const VariableAssignment& assignment) const; void instantiateInplace(const VariableAssignment& assignment); void addVariable(const SymbolOrNumber& expr); @@ -449,20 +438,15 @@ class QuantumComputation { static std::ostream& printPermutation(const Permutation& permutation, std::ostream& os = std::cout); - void dump(const std::string& filename, Format format) const; - void dump(const std::string& filename) const; - void dump(std::ostream& of, Format format) const; - void dumpOpenQASM2(std::ostream& of) const { dumpOpenQASM(of, false); } - void dumpOpenQASM3(std::ostream& of) const { dumpOpenQASM(of, true); } + void dump(const std::string& filename, + Format format = Format::OpenQASM3) const; /** * @brief Dumps the circuit in OpenQASM format to the given output stream - * @details One might want to call `ensureContiguousInitialLayout` before - * calling this function to ensure full layout information is available. * @param of The output stream to write the OpenQASM representation to * @param openQasm3 Whether to use OpenQASM 3.0 or 2.0 */ - void dumpOpenQASM(std::ostream& of, bool openQasm3) const; + void dumpOpenQASM(std::ostream& of, bool openQasm3 = true) const; /** * @brief Returns the OpenQASM representation of the circuit @@ -500,19 +484,6 @@ class QuantumComputation { [[nodiscard]] bool isDynamic() const; protected: - void importOpenQASM3(std::istream& is); - void importReal(std::istream& is); - int readRealHeader(std::istream& is); - void readRealGateDescriptions(std::istream& is, int line); - void importTFC(std::istream& is); - int readTFCHeader(std::istream& is, std::map& varMap); - void readTFCGateDescriptions(std::istream& is, int line, - std::map& varMap); - void importQC(std::istream& is); - int readQCHeader(std::istream& is, std::map& varMap); - void readQCGateDescriptions(std::istream& is, int line, - std::map& varMap); - [[nodiscard]] std::size_t getSmallestAncillary() const { for (std::size_t i = 0; i < ancillary.size(); ++i) { if (ancillary[i]) { diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Token.hpp b/include/mqt-core/ir/parsers/qasm3_parser/Token.hpp deleted file mode 100644 index ad8d248e1..000000000 --- a/include/mqt-core/ir/parsers/qasm3_parser/Token.hpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (c) 2025 Chair for Design Automation, TUM - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "Definitions.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace qasm3 { - -struct Token { - enum class Kind : uint8_t { - None, - - OpenQasm, - Include, - DefCalGrammar, - Def, - Cal, - DefCal, - Gate, - Opaque, - Extern, - Box, - Let, - - Break, - Continue, - If, - Else, - End, - Return, - For, - While, - In, - - Pragma, - - // types - Input, - Output, - Const, - ReadOnly, - Mutable, - - Qreg, - Qubit, - - CReg, - Bool, - Bit, - Int, - Uint, - Float, - Angle, - Complex, - Array, - Void, - - Duration, - Stretch, - - // builtin identifiers - Gphase, - Inv, - Pow, - Ctrl, - NegCtrl, - - Dim, - - DurationOf, - - Delay, - Reset, - Measure, - Barrier, - - True, - False, - - LBracket, - RBracket, - LBrace, - RBrace, - LParen, - RParen, - - Colon, - Semicolon, - Eof, - - Dot, - Comma, - - Equals, - Arrow, - Plus, - DoublePlus, - Minus, - Asterisk, - DoubleAsterisk, - Slash, - Percent, - Pipe, - DoublePipe, - Ampersand, - DoubleAmpersand, - Caret, - At, - Tilde, - ExclamationPoint, - - DoubleEquals, - NotEquals, - PlusEquals, - MinusEquals, - AsteriskEquals, - SlashEquals, - AmpersandEquals, - PipeEquals, - TildeEquals, - CaretEquals, - LeftShitEquals, - RightShiftEquals, - PercentEquals, - DoubleAsteriskEquals, - - LessThan, - LessThanEquals, - GreaterThan, - GreaterThanEquals, - LeftShift, - RightShift, - - Imag, - - Underscore, - - DoubleQuote, - SingleQuote, - BackSlash, - - Identifier, - - HardwareQubit, - StringLiteral, - IntegerLiteral, - FloatLiteral, - TimingLiteral, - - Sin, - Cos, - Tan, - Exp, - Ln, - Sqrt, - - InitialLayout, - OutputPermutation, - }; - - Kind kind = Kind::None; - size_t line = 0; - size_t col = 0; - size_t endLine = 0; - size_t endCol = 0; - int64_t val{}; - bool isSigned{false}; - qc::fp valReal{}; - std::string str; - - Token(const size_t l, const size_t c) - : line(l), col(c), endLine(l), endCol(c) {} - Token(const Kind k, const size_t l, const size_t c) - : kind(k), line(l), col(c), endLine(l), endCol(c) {} - Token(const Kind k, const size_t l, const size_t c, std::string s) - : kind(k), line(l), col(c), endLine(l), endCol(c), str(std::move(s)) {} - - static std::string kindToString(const Kind kind) { - // Print a token kind string representation. - // This is the representation used in the error messages. - switch (kind) { - case Kind::None: - return "None"; - case Kind::OpenQasm: - return "OPENQASM"; - case Kind::Include: - return "include"; - case Kind::DefCalGrammar: - return "DefCalGrammar"; - case Kind::Def: - return "Def"; - case Kind::Cal: - return "Cal"; - case Kind::DefCal: - return "DefCal"; - case Kind::Gate: - return "gate"; - case Kind::Opaque: - return "opaque"; - case Kind::Extern: - return "extern"; - case Kind::Box: - return "box"; - case Kind::Let: - return "let"; - case Kind::Break: - return "break"; - case Kind::Continue: - return "continue"; - case Kind::If: - return "if"; - case Kind::Else: - return "else"; - case Kind::End: - return "end"; - case Kind::Return: - return "return"; - case Kind::For: - return "for"; - case Kind::While: - return "while"; - case Kind::In: - return "in"; - case Kind::Pragma: - return "pragma"; - case Kind::Input: - return "input"; - case Kind::Output: - return "output"; - case Kind::Const: - return "const"; - case Kind::ReadOnly: - return "readOnly"; - case Kind::Mutable: - return "mutable"; - case Kind::Qreg: - return "qreg"; - case Kind::Qubit: - return "qubit"; - case Kind::CReg: - return "cReg"; - case Kind::Bool: - return "bool"; - case Kind::Bit: - return "bit"; - case Kind::Int: - return "int"; - case Kind::Uint: - return "uint"; - case Kind::Float: - return "float"; - case Kind::Angle: - return "angle"; - case Kind::Complex: - return "complex"; - case Kind::Array: - return "array"; - case Kind::Void: - return "void"; - case Kind::Duration: - return "duration"; - case Kind::Stretch: - return "stretch"; - case Kind::Gphase: - return "gphase"; - case Kind::Inv: - return "inv"; - case Kind::Pow: - return "pow"; - case Kind::Ctrl: - return "ctrl"; - case Kind::NegCtrl: - return "negCtrl"; - case Kind::Dim: - return "#dim"; - case Kind::DurationOf: - return "durationof"; - case Kind::Delay: - return "delay"; - case Kind::Reset: - return "reset"; - case Kind::Measure: - return "measure"; - case Kind::Barrier: - return "barrier"; - case Kind::True: - return "true"; - case Kind::False: - return "false"; - case Kind::LBracket: - return "["; - case Kind::RBracket: - return "]"; - case Kind::LBrace: - return "{"; - case Kind::RBrace: - return "}"; - case Kind::LParen: - return "("; - case Kind::RParen: - return ")"; - case Kind::Colon: - return ":"; - case Kind::Semicolon: - return ";"; - case Kind::Eof: - return "Eof"; - case Kind::Dot: - return "."; - case Kind::Comma: - return ","; - case Kind::Equals: - return "="; - case Kind::Arrow: - return "->"; - case Kind::Plus: - return "+"; - case Kind::DoublePlus: - return "++"; - case Kind::Minus: - return "-"; - case Kind::Asterisk: - return "*"; - case Kind::DoubleAsterisk: - return "**"; - case Kind::Slash: - return "/"; - case Kind::Percent: - return "%"; - case Kind::Pipe: - return "|"; - case Kind::DoublePipe: - return "||"; - case Kind::Ampersand: - return "&"; - case Kind::DoubleAmpersand: - return "&&"; - case Kind::Caret: - return "^"; - case Kind::At: - return "@"; - case Kind::Tilde: - return "~"; - case Kind::ExclamationPoint: - return "!"; - case Kind::DoubleEquals: - return "=="; - case Kind::NotEquals: - return "!="; - case Kind::PlusEquals: - return "+="; - case Kind::MinusEquals: - return "-="; - case Kind::AsteriskEquals: - return "*="; - case Kind::SlashEquals: - return "/="; - case Kind::AmpersandEquals: - return "&="; - case Kind::PipeEquals: - return "|="; - case Kind::TildeEquals: - return "~="; - case Kind::CaretEquals: - return "^="; - case Kind::LeftShitEquals: - return "<<="; - case Kind::RightShiftEquals: - return ">>="; - case Kind::PercentEquals: - return "%="; - case Kind::DoubleAsteriskEquals: - return "**="; - case Kind::LessThan: - return "<"; - case Kind::LessThanEquals: - return "<="; - case Kind::GreaterThan: - return ">"; - case Kind::GreaterThanEquals: - return ">="; - case Kind::LeftShift: - return "<<"; - case Kind::RightShift: - return ">>"; - case Kind::Imag: - return "imag"; - case Kind::Underscore: - return "underscore"; - case Kind::DoubleQuote: - return "\""; - case Kind::SingleQuote: - return "'"; - case Kind::BackSlash: - return "\\"; - case Kind::Identifier: - return "Identifier"; - case Kind::HardwareQubit: - return "HardwareQubit"; - case Kind::StringLiteral: - return "StringLiteral"; - case Kind::IntegerLiteral: - return "IntegerLiteral"; - case Kind::FloatLiteral: - return "FloatLiteral"; - case Kind::TimingLiteral: - return "TimingLiteral"; - case Kind::Sin: - return "sin"; - case Kind::Cos: - return "cos"; - case Kind::Tan: - return "tan"; - case Kind::Exp: - return "exp"; - case Kind::Ln: - return "ln"; - case Kind::Sqrt: - return "sqrt"; - case Kind::InitialLayout: - return "InitialLayout"; - case Kind::OutputPermutation: - return "OutputPermutation"; - - default: - // This cannot happen, as we have a case for every enum value. - // The default case is only here to silence compiler warnings. - throw std::runtime_error("Unknown token kind"); - } - } - - [[nodiscard]] std::string toString() const { - std::stringstream ss; - ss << kindToString(kind); - switch (kind) { - case Kind::Identifier: - ss << " (" << str << ")"; - break; - case Kind::StringLiteral: - ss << " (\"" << str << "\")"; - break; - case Kind::InitialLayout: - case Kind::OutputPermutation: - ss << " (" << str << ")"; - break; - case Kind::IntegerLiteral: - ss << " (" << val << ")"; - break; - case Kind::FloatLiteral: - ss << " (" << valReal << ")"; - break; - case Kind::TimingLiteral: - ss << " (" << valReal << " [s])"; - break; - default: - break; - } - return ss.str(); - } - - friend std::ostream& operator<<(std::ostream& os, const Kind& k) { - os << kindToString(k); - return os; - } - - friend std::ostream& operator<<(std::ostream& os, const Token& t) { - os << t.toString(); - return os; - } -}; -} // namespace qasm3 diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Exception.hpp b/include/mqt-core/qasm3/Exception.hpp similarity index 100% rename from include/mqt-core/ir/parsers/qasm3_parser/Exception.hpp rename to include/mqt-core/qasm3/Exception.hpp diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Gate.hpp b/include/mqt-core/qasm3/Gate.hpp similarity index 98% rename from include/mqt-core/ir/parsers/qasm3_parser/Gate.hpp rename to include/mqt-core/qasm3/Gate.hpp index 805524412..57ce5a529 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/Gate.hpp +++ b/include/mqt-core/qasm3/Gate.hpp @@ -9,7 +9,7 @@ #pragma once -#include "Statement.hpp" +#include "Statement_fwd.hpp" #include "ir/operations/OpType.hpp" #include diff --git a/include/mqt-core/qasm3/Importer.hpp b/include/mqt-core/qasm3/Importer.hpp new file mode 100644 index 000000000..e13f39ee5 --- /dev/null +++ b/include/mqt-core/qasm3/Importer.hpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2025 Chair for Design Automation, TUM + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "Definitions.hpp" +#include "InstVisitor.hpp" +#include "NestedEnvironment.hpp" +#include "ir/Permutation.hpp" +#include "passes/ConstEvalPass.hpp" +#include "passes/TypeCheckPass.hpp" + +#include +#include +#include +#include +#include +#include + +namespace qc { +struct Control; +class QuantumRegister; +// forward declarations +class Operation; +class QuantumComputation; +using QuantumRegisterMap = std::unordered_map; +} // namespace qc +namespace qasm3 { +// forward declarations +class Statement; +struct Gate; + +class Importer final : public InstVisitor { +public: + /** + * Imports a QASM3 file into a @ref QuantumComputation instance + * @param filename The path to the QASM3 file to import + * @return The imported @ref QuantumComputation instance + */ + [[nodiscard]] static auto importf(const std::string& filename) + -> qc::QuantumComputation; + + /** + * Imports a QASM3 program from a string into a @ref QuantumComputation + * @param qasm The QASM3 program to import + * @return The imported @ref QuantumComputation instance + */ + [[nodiscard]] static auto imports(const std::string& qasm) + -> qc::QuantumComputation; + + /** + * Imports a QASM3 program from a stream into a @ref QuantumComputation + * @param is The input stream to read the QASM3 program from + * @return The imported @ref QuantumComputation instance + */ + [[nodiscard]] static auto import(std::istream& is) -> qc::QuantumComputation; + +private: + /** + * @brief Construct a new instance for importing QASM3 code + * @param quantumComputation The @ref qc::QuantumComputation to import the + * QASM3 code into. + */ + explicit Importer(qc::QuantumComputation& quantumComputation); + + /** + * @brief Import the given QASM3 program into the quantum computation + * @param program The parsed QASM3 program AST + */ + void visitProgram(const std::vector>& program); + + const_eval::ConstEvalPass constEvalPass; + type_checking::TypeCheckPass typeCheckPass; + + NestedEnvironment> declarations; + qc::QuantumComputation* qc{}; + + std::map> gates; + + bool openQASM2CompatMode{false}; + + qc::Permutation initialLayout{}; + qc::Permutation outputPermutation{}; + + static std::map> + initializeBuiltins(); + + static void + translateGateOperand(const std::shared_ptr& gateOperand, + std::vector& qubits, + const qc::QuantumRegisterMap& qregs, + const std::shared_ptr& debugInfo); + + static void translateGateOperand(const std::string& gateIdentifier, + const std::shared_ptr& indexExpr, + std::vector& qubits, + const qc::QuantumRegisterMap& qregs, + const std::shared_ptr& debugInfo); + + void translateBitOperand(const std::string& bitIdentifier, + const std::shared_ptr& indexExpr, + std::vector& bits, + const std::shared_ptr& debugInfo) const; + + static uint64_t + evaluatePositiveConstant(const std::shared_ptr& expr, + const std::shared_ptr& debugInfo, + uint64_t defaultValue = 0); + + void visitVersionDeclaration( + std::shared_ptr versionDeclaration) override; + + void visitDeclarationStatement( + std::shared_ptr declarationStatement) override; + + void visitAssignmentStatement( + std::shared_ptr assignmentStatement) override; + + void visitInitialLayout(std::shared_ptr layout) override; + + void visitOutputPermutation( + std::shared_ptr permutation) override; + + void + visitGateStatement(std::shared_ptr gateStatement) override; + + void visitGateCallStatement( + std::shared_ptr gateCallStatement) override; + + auto + evaluateGateCall(const std::shared_ptr& gateCallStatement, + const std::string& identifier, + const std::vector>& parameters, + std::vector> targets, + const qc::QuantumRegisterMap& qregs) + -> std::unique_ptr; + + static std::shared_ptr + getMcGateDefinition(const std::string& identifier, size_t operandSize, + const std::shared_ptr& debugInfo); + + auto applyQuantumOperation(const std::shared_ptr& gate, + const qc::Targets& targetBits, + const std::vector& controlBits, + const std::vector& evaluatedParameters, + bool invertOperation, + const std::shared_ptr& debugInfo) + -> std::unique_ptr; + + void visitMeasureAssignment( + const std::string& identifier, + const std::shared_ptr& indexExpression, + const std::shared_ptr& measureExpression, + const std::shared_ptr& debugInfo); + + void visitBarrierStatement( + std::shared_ptr barrierStatement) override; + + void + visitResetStatement(std::shared_ptr resetStatement) override; + + void visitIfStatement(std::shared_ptr ifStatement) override; + + [[nodiscard]] auto translateBlockOperations( + const std::vector>& statements) + -> std::unique_ptr; + + std::pair + parseGateIdentifierCompatMode(const std::string& identifier); +}; +} // namespace qasm3 diff --git a/include/mqt-core/ir/parsers/qasm3_parser/InstVisitor.hpp b/include/mqt-core/qasm3/InstVisitor.hpp similarity index 92% rename from include/mqt-core/ir/parsers/qasm3_parser/InstVisitor.hpp rename to include/mqt-core/qasm3/InstVisitor.hpp index 7eca6a808..1d3b3aabb 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/InstVisitor.hpp +++ b/include/mqt-core/qasm3/InstVisitor.hpp @@ -9,29 +9,13 @@ #pragma once +#include "Statement_fwd.hpp" + #include #include #include namespace qasm3 { -class GateDeclaration; -class GateCallStatement; -class VersionDeclaration; -class DeclarationStatement; -class InitialLayout; -class OutputPermutation; -class AssignmentStatement; -class BarrierStatement; -class ResetStatement; - -class Expression; -class BinaryExpression; -class UnaryExpression; -class IdentifierExpression; -class IdentifierList; -class Constant; -class MeasureExpression; -class IfStatement; class InstVisitor { public: diff --git a/include/mqt-core/ir/parsers/qasm3_parser/NestedEnvironment.hpp b/include/mqt-core/qasm3/NestedEnvironment.hpp similarity index 100% rename from include/mqt-core/ir/parsers/qasm3_parser/NestedEnvironment.hpp rename to include/mqt-core/qasm3/NestedEnvironment.hpp diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Parser.hpp b/include/mqt-core/qasm3/Parser.hpp similarity index 65% rename from include/mqt-core/ir/parsers/qasm3_parser/Parser.hpp rename to include/mqt-core/qasm3/Parser.hpp index ef21a8161..e5cbedfbd 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/Parser.hpp +++ b/include/mqt-core/qasm3/Parser.hpp @@ -15,26 +15,26 @@ #pragma once -#include "Exception.hpp" #include "Scanner.hpp" -#include "Statement.hpp" -#include "StdGates.hpp" +#include "Statement_fwd.hpp" #include "Token.hpp" -#include "Types.hpp" -#include "ir/Permutation.hpp" +#include "Types_fwd.hpp" -#include +#include #include #include -#include #include -#include #include #include #include +namespace qc { +// forward declarations +class Permutation; +} // namespace qc + namespace qasm3 { -class Parser { +class Parser final { struct ScannerState { private: std::unique_ptr is; @@ -79,60 +79,21 @@ class Parser { std::stack scanner; std::shared_ptr includeDebugInfo{nullptr}; - [[noreturn]] void error(const Token& token, const std::string& msg) { - throw CompilerError(msg, makeDebugInfo(token)); - } + [[noreturn]] void error(const Token& token, const std::string& msg); - [[nodiscard]] Token last() const { - if (scanner.empty()) { - throw std::runtime_error("No scanner available"); - } - return scanner.top().last; - } + [[nodiscard]] Token last() const; - [[nodiscard]] Token current() const { - if (scanner.empty()) { - throw std::runtime_error("No scanner available"); - } - return scanner.top().t; - } + [[nodiscard]] Token current() const; - [[nodiscard]] Token peek() const { - if (scanner.empty()) { - throw std::runtime_error("No scanner available"); - } - return scanner.top().next; - } + [[nodiscard]] Token peek() const; Token expect(const Token::Kind& expected, - const std::optional& context = std::nullopt) { - if (current().kind != expected) { - std::string message = "Expected '" + Token::kindToString(expected) + - "', got '" + Token::kindToString(current().kind) + - "'."; - if (context.has_value()) { - message += " " + context.value(); - } - error(current(), message); - } - - auto token = current(); - scan(); - return token; - } + const std::optional& context = std::nullopt); public: - explicit Parser(std::istream* is, bool implicitlyIncludeStdgates = true) { - scanner.emplace(is); - scan(); - if (implicitlyIncludeStdgates) { - scanner.emplace(std::make_unique(STDGATES), - "stdgates.inc", true); - scan(); - } - } + explicit Parser(std::istream& is, bool implicitlyIncludeStdgates = true); - virtual ~Parser() = default; + ~Parser() = default; std::shared_ptr parseVersionDeclaration(); @@ -180,7 +141,8 @@ class Parser { std::shared_ptr parseIdentifierList(); - std::pair, bool> parseType(); + std::pair>>, bool> + parseType(); std::shared_ptr parseTypeDesignator(); @@ -189,18 +151,9 @@ class Parser { void scan(); std::shared_ptr makeDebugInfo(Token const& begin, - Token const& /*end*/) { - // Parameter `end` is currently not used. - return std::make_shared( - begin.line, begin.col, scanner.top().filename.value_or(""), - includeDebugInfo); - } + Token const& /*end*/); - std::shared_ptr makeDebugInfo(Token const& token) { - return std::make_shared( - token.line, token.col, scanner.top().filename.value_or(""), - includeDebugInfo); - } + std::shared_ptr makeDebugInfo(Token const& token); [[nodiscard]] bool isAtEnd() const { return current().kind == Token::Kind::Eof; diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp b/include/mqt-core/qasm3/Scanner.hpp similarity index 58% rename from include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp rename to include/mqt-core/qasm3/Scanner.hpp index d5fc7856c..63ea0fc72 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp +++ b/include/mqt-core/qasm3/Scanner.hpp @@ -11,10 +11,9 @@ #include "Token.hpp" -#include #include #include -#include +#include #include #include #include @@ -27,19 +26,13 @@ class Scanner { size_t line = 1; size_t col = 0; - [[nodiscard]] static bool isSpace(const char c) { - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; - } + [[nodiscard]] static bool isSpace(char c); - [[nodiscard]] static bool isFirstIdChar(const char c) { - return isalpha(c) != 0 || c == '_'; - } + [[nodiscard]] static bool isFirstIdChar(char c); - [[nodiscard]] static bool isNum(const char c) { return c >= '0' && c <= '9'; } + [[nodiscard]] static bool isNum(char c); - [[nodiscard]] static bool isHex(const char c) { - return isNum(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); - } + [[nodiscard]] static bool isHex(char c); [[nodiscard]] static bool hasTimingSuffix(char first, char second); @@ -65,18 +58,9 @@ class Scanner { Token consumeName(); - void error(const std::string& msg) const { - std::cerr << "Error at line " << line << ", column " << col << ": " << msg - << '\n'; - } - - void expect(const char expected) { - if (ch != expected) { - error("Expected '" + std::to_string(expected) + "', got '" + ch + "'"); - } else { - nextCh(); - } - } + void error(const std::string& msg) const; + + void expect(char expected); public: explicit Scanner(std::istream* in); diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Statement.hpp b/include/mqt-core/qasm3/Statement.hpp similarity index 88% rename from include/mqt-core/ir/parsers/qasm3_parser/Statement.hpp rename to include/mqt-core/qasm3/Statement.hpp index 5aa030927..418732c5d 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/Statement.hpp +++ b/include/mqt-core/qasm3/Statement.hpp @@ -9,12 +9,10 @@ #pragma once -#include "InstVisitor.hpp" -#include "Types.hpp" +#include "Statement_fwd.hpp" // IWYU pragma: export +#include "Types_fwd.hpp" #include "ir/Permutation.hpp" -#include "ir/operations/ClassicControlledOperation.hpp" -#include #include #include #include @@ -24,7 +22,13 @@ #include #include +namespace qc { +// forward declarations +enum ComparisonKind : std::uint8_t; +} // namespace qc + namespace qasm3 { +class InstVisitor; struct DebugInfo { size_t line; @@ -57,7 +61,7 @@ class DeclarationExpression final { explicit DeclarationExpression(std::shared_ptr expr) : expression(std::move(expr)) {} - virtual ~DeclarationExpression() = default; + ~DeclarationExpression() = default; }; class Constant final : public Expression { @@ -246,18 +250,9 @@ class GateDeclaration final std::shared_ptr params, std::shared_ptr qbits, std::vector> stmts, - const bool opaque = false) - : Statement(std::move(debug)), identifier(std::move(id)), - parameters(std::move(params)), qubits(std::move(qbits)), - statements(std::move(stmts)), isOpaque(opaque) { - if (opaque) { - assert(statements.empty() && "Opaque gate should not have statements."); - } - } + const bool opaque = false); - void accept(InstVisitor* visitor) override { - visitor->visitGateStatement(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class VersionDeclaration final @@ -270,9 +265,7 @@ class VersionDeclaration final const double versionNum) : Statement(std::move(debug)), version(versionNum) {} - void accept(InstVisitor* visitor) override { - visitor->visitVersionDeclaration(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class InitialLayout final : public Statement, @@ -284,9 +277,7 @@ class InitialLayout final : public Statement, : Statement(std::move(debug)), permutation(std::move(perm)) {} private: - void accept(InstVisitor* visitor) override { - visitor->visitInitialLayout(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class OutputPermutation final @@ -300,9 +291,7 @@ class OutputPermutation final : Statement(std::move(debug)), permutation(std::move(perm)) {} private: - void accept(InstVisitor* visitor) override { - visitor->visitOutputPermutation(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class DeclarationStatement final @@ -320,9 +309,7 @@ class DeclarationStatement final : Statement(std::move(debug)), isConst(declIsConst), type(ty), identifier(std::move(id)), expression(std::move(expr)) {} - void accept(InstVisitor* visitor) override { - visitor->visitDeclarationStatement(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class GateModifier : public std::enable_shared_from_this { @@ -372,9 +359,7 @@ class GateCallStatement final modifiers(std::move(modifierList)), arguments(std::move(argumentList)), operands(std::move(operandList)) {} - void accept(InstVisitor* visitor) override { - visitor->visitGateCallStatement(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class AssignmentStatement final @@ -407,9 +392,7 @@ class AssignmentStatement final : Statement(std::move(debug)), type(ty), identifier(std::move(id)), indexExpression(std::move(indexExpr)), expression(std::move(expr)) {} - void accept(InstVisitor* visitor) override { - visitor->visitAssignmentStatement(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class BarrierStatement final @@ -422,9 +405,7 @@ class BarrierStatement final std::vector> gateList) : QuantumStatement(std::move(debug)), gates(std::move(gateList)) {} - void accept(InstVisitor* visitor) override { - visitor->visitBarrierStatement(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class ResetStatement final @@ -437,9 +418,7 @@ class ResetStatement final std::shared_ptr g) : QuantumStatement(std::move(debug)), gate(std::move(g)) {} - void accept(InstVisitor* visitor) override { - visitor->visitResetStatement(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; class IfStatement final : public Statement, @@ -456,8 +435,6 @@ class IfStatement final : public Statement, : Statement(std::move(debug)), condition(cond), thenStatements(thenStmts), elseStatements(elseStmts) {} - void accept(InstVisitor* visitor) override { - visitor->visitIfStatement(shared_from_this()); - } + void accept(InstVisitor* visitor) override; }; } // namespace qasm3 diff --git a/include/mqt-core/qasm3/Statement_fwd.hpp b/include/mqt-core/qasm3/Statement_fwd.hpp new file mode 100644 index 000000000..a67796650 --- /dev/null +++ b/include/mqt-core/qasm3/Statement_fwd.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Chair for Design Automation, TUM + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +namespace qasm3 { +struct DebugInfo; +class Statement; +class IfStatement; +class IdentifierList; +class MeasureExpression; +class GateCallStatement; +class GateDeclaration; +class BarrierStatement; +class ResetStatement; +class AssignmentStatement; +class VersionDeclaration; +class DeclarationExpression; +class DeclarationStatement; +class GateOperand; +class GateModifier; +class QuantumStatement; +class InitialLayout; +class OutputPermutation; +class Expression; +class Constant; +class BinaryExpression; +class UnaryExpression; +class IdentifierExpression; +} // namespace qasm3 diff --git a/include/mqt-core/ir/parsers/qasm3_parser/StdGates.hpp b/include/mqt-core/qasm3/StdGates.hpp similarity index 100% rename from include/mqt-core/ir/parsers/qasm3_parser/StdGates.hpp rename to include/mqt-core/qasm3/StdGates.hpp diff --git a/include/mqt-core/qasm3/Token.hpp b/include/mqt-core/qasm3/Token.hpp new file mode 100644 index 000000000..03b526306 --- /dev/null +++ b/include/mqt-core/qasm3/Token.hpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2025 Chair for Design Automation, TUM + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include +#include +#include +#include + +namespace qasm3 { + +struct Token { + enum class Kind : uint8_t { + None, + + OpenQasm, + Include, + DefCalGrammar, + Def, + Cal, + DefCal, + Gate, + Opaque, + Extern, + Box, + Let, + + Break, + Continue, + If, + Else, + End, + Return, + For, + While, + In, + + Pragma, + + // types + Input, + Output, + Const, + ReadOnly, + Mutable, + + Qreg, + Qubit, + + CReg, + Bool, + Bit, + Int, + Uint, + Float, + Angle, + Complex, + Array, + Void, + + Duration, + Stretch, + + // builtin identifiers + Gphase, + Inv, + Pow, + Ctrl, + NegCtrl, + + Dim, + + DurationOf, + + Delay, + Reset, + Measure, + Barrier, + + True, + False, + + LBracket, + RBracket, + LBrace, + RBrace, + LParen, + RParen, + + Colon, + Semicolon, + Eof, + + Dot, + Comma, + + Equals, + Arrow, + Plus, + DoublePlus, + Minus, + Asterisk, + DoubleAsterisk, + Slash, + Percent, + Pipe, + DoublePipe, + Ampersand, + DoubleAmpersand, + Caret, + At, + Tilde, + ExclamationPoint, + + DoubleEquals, + NotEquals, + PlusEquals, + MinusEquals, + AsteriskEquals, + SlashEquals, + AmpersandEquals, + PipeEquals, + TildeEquals, + CaretEquals, + LeftShitEquals, + RightShiftEquals, + PercentEquals, + DoubleAsteriskEquals, + + LessThan, + LessThanEquals, + GreaterThan, + GreaterThanEquals, + LeftShift, + RightShift, + + Imag, + + Underscore, + + DoubleQuote, + SingleQuote, + BackSlash, + + Identifier, + + HardwareQubit, + StringLiteral, + IntegerLiteral, + FloatLiteral, + TimingLiteral, + + Sin, + Cos, + Tan, + Exp, + Ln, + Sqrt, + + InitialLayout, + OutputPermutation, + }; + + Kind kind = Kind::None; + size_t line = 0; + size_t col = 0; + size_t endLine = 0; + size_t endCol = 0; + int64_t val{}; + bool isSigned{false}; + double valReal{}; + std::string str; + + Token(const size_t l, const size_t c) + : line(l), col(c), endLine(l), endCol(c) {} + Token(const Kind k, const size_t l, const size_t c) + : kind(k), line(l), col(c), endLine(l), endCol(c) {} + Token(const Kind k, const size_t l, const size_t c, std::string s) + : kind(k), line(l), col(c), endLine(l), endCol(c), str(std::move(s)) {} + + static std::string kindToString(Kind kind); + + [[nodiscard]] std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const Kind& k); + + friend std::ostream& operator<<(std::ostream& os, const Token& t); +}; +} // namespace qasm3 diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Types.hpp b/include/mqt-core/qasm3/Types.hpp similarity index 97% rename from include/mqt-core/ir/parsers/qasm3_parser/Types.hpp rename to include/mqt-core/qasm3/Types.hpp index e5305a24f..7d461343c 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/Types.hpp +++ b/include/mqt-core/qasm3/Types.hpp @@ -10,6 +10,7 @@ #pragma once #include "InstVisitor.hpp" +#include "Types_fwd.hpp" // IWYU pragma: export #include #include @@ -18,11 +19,6 @@ #include namespace qasm3 { -class Expression; - -template class Type; -using TypeExpr = Type>; -using ResolvedType = Type; template class Type { public: diff --git a/include/mqt-core/qasm3/Types_fwd.hpp b/include/mqt-core/qasm3/Types_fwd.hpp new file mode 100644 index 000000000..fbf5f218e --- /dev/null +++ b/include/mqt-core/qasm3/Types_fwd.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Chair for Design Automation, TUM + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include +#include + +namespace qasm3 { +class Expression; + +template class Type; +using TypeExpr = Type>; +using ResolvedType = Type; +} // namespace qasm3 diff --git a/include/mqt-core/ir/parsers/qasm3_parser/passes/CompilerPass.hpp b/include/mqt-core/qasm3/passes/CompilerPass.hpp similarity index 92% rename from include/mqt-core/ir/parsers/qasm3_parser/passes/CompilerPass.hpp rename to include/mqt-core/qasm3/passes/CompilerPass.hpp index 874a243d3..d47465ef3 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/passes/CompilerPass.hpp +++ b/include/mqt-core/qasm3/passes/CompilerPass.hpp @@ -9,9 +9,9 @@ #pragma once -#include "../Statement.hpp" - namespace qasm3 { +class Statement; + class CompilerPass { public: virtual ~CompilerPass() = default; diff --git a/include/mqt-core/ir/parsers/qasm3_parser/passes/ConstEvalPass.hpp b/include/mqt-core/qasm3/passes/ConstEvalPass.hpp similarity index 58% rename from include/mqt-core/ir/parsers/qasm3_parser/passes/ConstEvalPass.hpp rename to include/mqt-core/qasm3/passes/ConstEvalPass.hpp index ee5901d5b..5ec111d9d 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/passes/ConstEvalPass.hpp +++ b/include/mqt-core/qasm3/passes/ConstEvalPass.hpp @@ -9,17 +9,12 @@ #pragma once -#include "CompilerPass.hpp" -#include "Definitions.hpp" -#include "ir/parsers/qasm3_parser/Exception.hpp" -#include "ir/parsers/qasm3_parser/InstVisitor.hpp" -#include "ir/parsers/qasm3_parser/NestedEnvironment.hpp" -#include "ir/parsers/qasm3_parser/Statement.hpp" - -#include +#include "qasm3/InstVisitor.hpp" +#include "qasm3/NestedEnvironment.hpp" +#include "qasm3/passes/CompilerPass.hpp" + #include #include -#include #include #include #include @@ -42,39 +37,9 @@ struct ConstEvalValue { : type(isSigned ? ConstInt : ConstUint), value(val), width(w) {} explicit ConstEvalValue(bool val) : type(ConstBool), value(val), width(1) {} - [[nodiscard]] std::shared_ptr toExpr() const { - switch (type) { - case ConstInt: - return std::make_shared(Constant(std::get<0>(value), true)); - case ConstUint: - return std::make_shared(Constant(std::get<0>(value), false)); - case ConstFloat: - return std::make_shared(Constant(std::get<1>(value))); - case ConstBool: - return std::make_shared(Constant(std::get<2>(value))); - default: - qc::unreachable(); - } - } + [[nodiscard]] std::shared_ptr toExpr() const; - bool operator==(const ConstEvalValue& rhs) const { - if (type != rhs.type) { - return false; - } - - switch (type) { - case ConstInt: - case ConstUint: - return std::get<0>(value) == std::get<0>(rhs.value); - case ConstFloat: - return std::abs(std::get<1>(value) - std::get<1>(rhs.value)) < - std::numeric_limits::epsilon() * 1024; - case ConstBool: - return std::get<2>(value) == std::get<2>(rhs.value); - } - - return false; - } + bool operator==(const ConstEvalValue& rhs) const; bool operator!=(const ConstEvalValue& rhs) const { return !(*this == rhs); } @@ -88,18 +53,6 @@ class ConstEvalPass final public TypeVisitor> { NestedEnvironment env; - template static int64_t castToWidth(int64_t value) { - return static_cast(static_cast(value)); - } - - static ConstEvalValue evalIntExpression(BinaryExpression::Op op, int64_t lhs, - int64_t rhs, size_t width, - bool isSigned); - static ConstEvalValue evalFloatExpression(BinaryExpression::Op op, double lhs, - double rhs); - static ConstEvalValue evalBoolExpression(BinaryExpression::Op op, bool lhs, - bool rhs); - public: ConstEvalPass() = default; ~ConstEvalPass() override = default; @@ -112,13 +65,7 @@ class ConstEvalPass final env.emplace(identifier, ConstEvalValue(val)); } - void processStatement(Statement& statement) override { - try { - statement.accept(this); - } catch (const ConstEvalError& e) { - throw CompilerError(e.what(), statement.debugInfo); - } - } + void processStatement(Statement& statement) override; void pushEnv() { env.push(); } void popEnv() { env.pop(); } diff --git a/include/mqt-core/ir/parsers/qasm3_parser/passes/TypeCheckPass.hpp b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp similarity index 77% rename from include/mqt-core/ir/parsers/qasm3_parser/passes/TypeCheckPass.hpp rename to include/mqt-core/qasm3/passes/TypeCheckPass.hpp index cb15dff83..296f2e2a0 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/passes/TypeCheckPass.hpp +++ b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp @@ -10,21 +10,24 @@ #pragma once #include "CompilerPass.hpp" -#include "ConstEvalPass.hpp" -#include "ir/parsers/qasm3_parser/Exception.hpp" -#include "ir/parsers/qasm3_parser/InstVisitor.hpp" -#include "ir/parsers/qasm3_parser/Statement.hpp" -#include "ir/parsers/qasm3_parser/Types.hpp" +#include "qasm3/InstVisitor.hpp" +#include "qasm3/Types.hpp" #include #include #include #include -namespace qasm3::type_checking { +namespace qasm3 { +class GateOperand; +struct DebugInfo; +} // namespace qasm3 -using const_eval::ConstEvalPass; +namespace qasm3::const_eval { +class ConstEvalPass; +} +namespace qasm3::type_checking { struct InferredType { bool isError; std::shared_ptr type; @@ -60,13 +63,14 @@ class TypeCheckPass final : public CompilerPass, std::map env; // We need a reference to a const eval pass to evaluate types before type // checking. - ConstEvalPass* constEvalPass; + const_eval::ConstEvalPass* constEvalPass; InferredType error(const std::string& msg, const std::shared_ptr& debugInfo = nullptr); public: - explicit TypeCheckPass(ConstEvalPass* pass) : constEvalPass(pass) {} + explicit TypeCheckPass(const_eval::ConstEvalPass* pass) + : constEvalPass(pass) {} ~TypeCheckPass() override = default; @@ -74,28 +78,9 @@ class TypeCheckPass final : public CompilerPass, env.emplace(identifier, ty); } - void processStatement(Statement& statement) override { - try { - statement.accept(this); - - if (hasError) { - throw TypeCheckError("Type check failed."); - } - } catch (const TypeCheckError& e) { - throw CompilerError(e.what(), statement.debugInfo); - } - } + void processStatement(Statement& statement) override; - void checkGateOperand(const GateOperand& operand) { - if (operand.expression == nullptr) { - return; - } - - if (const auto type = visit(operand.expression); - !type.isError && !type.type->isUint()) { - error("Index must be an unsigned integer"); - } - } + void checkGateOperand(const GateOperand& operand); // Types void diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5259d77a1..5b90b5a78 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -69,6 +69,9 @@ endif() # add the IR package add_subdirectory(ir) +# add the QASM package +add_subdirectory(qasm3) + # add the algorithms package add_subdirectory(algorithms) diff --git a/src/ir/QuantumComputation.cpp b/src/ir/QuantumComputation.cpp index 5826b2457..4e534dd19 100644 --- a/src/ir/QuantumComputation.cpp +++ b/src/ir/QuantumComputation.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -31,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -248,64 +246,6 @@ std::size_t QuantumComputation::getDepth() const { return *std::max_element(depths.begin(), depths.end()); } -void QuantumComputation::import(const std::string& filename) { - const std::size_t dot = filename.find_last_of('.'); - std::string extension = filename.substr(dot + 1); - std::transform( - extension.begin(), extension.end(), extension.begin(), - [](unsigned char ch) { return static_cast(::tolower(ch)); }); - if (extension == "real") { - import(filename, Format::Real); - } else if (extension == "qasm") { - import(filename, Format::OpenQASM3); - } else if (extension == "tfc") { - import(filename, Format::TFC); - } else if (extension == "qc") { - import(filename, Format::QC); - } else { - throw QFRException("[import] extension " + extension + " not recognized"); - } -} - -void QuantumComputation::import(const std::string& filename, Format format) { - const std::size_t slash = filename.find_last_of('/'); - const std::size_t dot = filename.find_last_of('.'); - name = filename.substr(slash + 1, dot - slash - 1); - - auto ifs = std::ifstream(filename); - if (ifs.good()) { - import(ifs, format); - } else { - throw QFRException("[import] Error processing input stream: " + name); - } -} - -void QuantumComputation::import(std::istream& is, Format format) { - // reset circuit before importing - reset(); - - switch (format) { - case Format::Real: - importReal(is); - break; - case Format::OpenQASM2: - case Format::OpenQASM3: - importOpenQASM3(is); - break; - case Format::TFC: - importTFC(is); - break; - case Format::QC: - importQC(is); - break; - default: - throw QFRException("[import] format not recognized"); - } - - // initialize the initial layout and output permutation - initializeIOMapping(); -} - void QuantumComputation::initializeIOMapping() { // if no initial layout was found during parsing the identity mapping is // assumed @@ -696,29 +636,6 @@ std::ostream& QuantumComputation::printStatistics(std::ostream& os) const { return os; } -void QuantumComputation::dump(const std::string& filename) const { - const std::size_t dot = filename.find_last_of('.'); - assert(dot != std::string::npos); - std::string extension = filename.substr(dot + 1); - std::transform( - extension.begin(), extension.end(), extension.begin(), - [](unsigned char c) { return static_cast(::tolower(c)); }); - if (extension == "real") { - dump(filename, Format::Real); - } else if (extension == "qasm") { - dump(filename, Format::OpenQASM3); - } else if (extension == "qc") { - dump(filename, Format::QC); - } else if (extension == "tfc") { - dump(filename, Format::TFC); - } else if (extension == "tensor") { - dump(filename, Format::Tensor); - } else { - throw QFRException("[dump] Extension " + extension + - " not recognized/supported for dumping."); - } -} - void QuantumComputation::dumpOpenQASM(std::ostream& of, bool openQASM3) const { // dump initial layout and output permutation @@ -824,33 +741,15 @@ void QuantumComputation::reset() { } void QuantumComputation::dump(const std::string& filename, - Format format) const { + const Format format) const { auto of = std::ofstream(filename); if (!of.good()) { throw QFRException("[dump] Error opening file: " + filename); } - dump(of, format); -} - -void QuantumComputation::dump(std::ostream& of, Format format) const { - switch (format) { - case Format::OpenQASM3: + if (format == Format::OpenQASM3) { dumpOpenQASM(of, true); - break; - case Format::OpenQASM2: + } else { dumpOpenQASM(of, false); - break; - case Format::Real: - std::cerr << "Dumping in real format currently not supported\n"; - break; - case Format::TFC: - std::cerr << "Dumping in TFC format currently not supported\n"; - break; - case Format::QC: - std::cerr << "Dumping in QC format currently not supported\n"; - break; - default: - throw QFRException("[dump] Format not recognized/supported for dumping."); } } @@ -1144,23 +1043,7 @@ QuantumComputation::QuantumComputation(const std::size_t nq, mt.seed(seeds); } } -QuantumComputation::QuantumComputation(const std::string& filename, - const std::size_t s) - : seed(s) { - import(filename); - if (seed != 0U) { - mt.seed(seed); - } else { - // create and properly seed rng - std::array - randomData{}; - std::random_device rd; - std::generate(std::begin(randomData), std::end(randomData), - [&rd]() { return rd(); }); - std::seed_seq seeds(std::begin(randomData), std::end(randomData)); - mt.seed(seeds); - } -} + QuantumComputation::QuantumComputation(const QuantumComputation& qc) : nqubits(qc.nqubits), nclassics(qc.nclassics), nancillae(qc.nancillae), name(qc.name), quantumRegisters(qc.quantumRegisters), @@ -1376,15 +1259,6 @@ bool QuantumComputation::isDynamic() const { }); } -QuantumComputation QuantumComputation::fromQASM(const std::string& qasm) { - std::stringstream ss{}; - ss << qasm; - QuantumComputation qc{}; - qc.importOpenQASM3(ss); - qc.initializeIOMapping(); - return qc; -} - QuantumComputation QuantumComputation::fromCompoundOperation(const CompoundOperation& op) { QuantumComputation qc{}; diff --git a/src/ir/parsers/QASM3Parser.cpp b/src/ir/parsers/QASM3Parser.cpp deleted file mode 100644 index 0d1b76108..000000000 --- a/src/ir/parsers/QASM3Parser.cpp +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Copyright (c) 2025 Chair for Design Automation, TUM - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "Definitions.hpp" -#include "ir/Permutation.hpp" -#include "ir/QuantumComputation.hpp" -#include "ir/operations/ClassicControlledOperation.hpp" -#include "ir/operations/CompoundOperation.hpp" -#include "ir/operations/Control.hpp" -#include "ir/operations/NonUnitaryOperation.hpp" -#include "ir/operations/OpType.hpp" -#include "ir/operations/Operation.hpp" -#include "ir/operations/StandardOperation.hpp" -#include "ir/parsers/qasm3_parser/Exception.hpp" -#include "ir/parsers/qasm3_parser/Gate.hpp" -#include "ir/parsers/qasm3_parser/InstVisitor.hpp" -#include "ir/parsers/qasm3_parser/NestedEnvironment.hpp" -#include "ir/parsers/qasm3_parser/Parser.hpp" -#include "ir/parsers/qasm3_parser/Statement.hpp" -#include "ir/parsers/qasm3_parser/StdGates.hpp" -#include "ir/parsers/qasm3_parser/Types.hpp" -#include "ir/parsers/qasm3_parser/passes/ConstEvalPass.hpp" -#include "ir/parsers/qasm3_parser/passes/TypeCheckPass.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace qasm3; -using const_eval::ConstEvalPass; -using const_eval::ConstEvalValue; -using type_checking::InferredType; -using type_checking::TypeCheckPass; - -class OpenQasm3Parser final : public InstVisitor { - ConstEvalPass constEvalPass; - TypeCheckPass typeCheckPass; - - NestedEnvironment> declarations; - qc::QuantumComputation* qc{}; - - std::vector> ops; - - std::map> gates = STANDARD_GATES; - - bool openQASM2CompatMode{false}; - - qc::Permutation initialLayout{}; - qc::Permutation outputPermutation{}; - - [[noreturn]] static void error(const std::string& message, - const std::shared_ptr& debugInfo) { - throw CompilerError(message, debugInfo); - } - - static std::map> - initializeBuiltins() { - std::map> builtins{}; - - InferredType const floatTy{std::dynamic_pointer_cast( - std::make_shared>(Float, 64))}; - - builtins.emplace("pi", std::pair{ConstEvalValue(qc::PI), floatTy}); - builtins.emplace("π", std::pair{ConstEvalValue(qc::PI), floatTy}); - builtins.emplace("tau", std::pair{ConstEvalValue(qc::TAU), floatTy}); - builtins.emplace("τ", std::pair{ConstEvalValue(qc::TAU), floatTy}); - builtins.emplace("euler", std::pair{ConstEvalValue(qc::E), floatTy}); - builtins.emplace("ℇ", std::pair{ConstEvalValue(qc::E), floatTy}); - - return builtins; - } - - static void - translateGateOperand(const std::shared_ptr& gateOperand, - std::vector& qubits, - const qc::QuantumRegisterMap& qregs, - const std::shared_ptr& debugInfo) { - translateGateOperand(gateOperand->identifier, gateOperand->expression, - qubits, qregs, debugInfo); - } - - static void - translateGateOperand(const std::string& gateIdentifier, - const std::shared_ptr& indexExpr, - std::vector& qubits, - const qc::QuantumRegisterMap& qregs, - const std::shared_ptr& debugInfo) { - const auto qubitIter = qregs.find(gateIdentifier); - if (qubitIter == qregs.end()) { - error("Usage of unknown quantum register.", debugInfo); - } - auto qubit = qubitIter->second; - - if (indexExpr != nullptr) { - const auto result = evaluatePositiveConstant(indexExpr, debugInfo); - - if (result >= qubit.getSize()) { - error("Index expression must be smaller than the width of the " - "quantum register.", - debugInfo); - } - qubit.getStartIndex() += static_cast(result); - qubit.getSize() = 1; - } - - for (uint64_t i = 0; i < qubit.getSize(); ++i) { - qubits.emplace_back(static_cast(qubit.getStartIndex() + i)); - } - } - - void translateBitOperand(const std::string& bitIdentifier, - const std::shared_ptr& indexExpr, - std::vector& bits, - const std::shared_ptr& debugInfo) const { - const auto iter = qc->getClassicalRegisters().find(bitIdentifier); - if (iter == qc->getClassicalRegisters().end()) { - error("Usage of unknown classical register.", debugInfo); - } - auto creg = iter->second; - - if (indexExpr != nullptr) { - const auto index = evaluatePositiveConstant(indexExpr, debugInfo); - if (index >= creg.getSize()) { - error("Index expression must be smaller than the width of the " - "classical register.", - debugInfo); - } - - creg.getStartIndex() += index; - creg.getSize() = 1; - } - - for (uint64_t i = 0; i < creg.getSize(); ++i) { - bits.emplace_back(creg.getStartIndex() + i); - } - } - - static uint64_t - evaluatePositiveConstant(const std::shared_ptr& expr, - const std::shared_ptr& debugInfo, - const uint64_t defaultValue = 0) { - if (expr == nullptr) { - return defaultValue; - } - - const auto constInt = std::dynamic_pointer_cast(expr); - if (!constInt) { - error("Expected a constant integer expression.", debugInfo); - } - - return constInt->getUInt(); - } - -public: - explicit OpenQasm3Parser(qc::QuantumComputation* quantumComputation) - : typeCheckPass(&constEvalPass), qc(quantumComputation) { - for (const auto& [identifier, builtin] : initializeBuiltins()) { - constEvalPass.addConst(identifier, builtin.first); - typeCheckPass.addBuiltin(identifier, builtin.second); - } - } - - ~OpenQasm3Parser() override = default; - - void visitProgram(const std::vector>& program) { - // TODO: in the future, don't exit early, but collect all errors - // To do this, we need to insert make sure that erroneous declarations - // actually insert a dummy entry; also, we need to synchronize to the next - // semicolon, to make sure we don't do some weird stuff and report false - // errors. - for (const auto& statement : program) { - try { - constEvalPass.processStatement(*statement); - typeCheckPass.processStatement(*statement); - statement->accept(this); - } catch (CompilerError& e) { - std::cerr << e.what() << '\n'; - throw; - } - } - - // Finally, if we have a initial layout and output permutation specified, - // apply them. - if (!initialLayout.empty()) { - qc->initialLayout = initialLayout; - } - if (!outputPermutation.empty()) { - qc->outputPermutation = outputPermutation; - } - } - - void visitVersionDeclaration( - const std::shared_ptr versionDeclaration) override { - if (versionDeclaration->version < 3) { - openQASM2CompatMode = true; - } - } - - void visitDeclarationStatement(const std::shared_ptr - declarationStatement) override { - const auto identifier = declarationStatement->identifier; - if (declarations.find(identifier).has_value()) { - // TODO: show the location of the previous declaration - error("Identifier '" + identifier + "' already declared.", - declarationStatement->debugInfo); - } - - std::shared_ptr const ty = - std::get<1>(declarationStatement->type); - - if (const auto sizedTy = - std::dynamic_pointer_cast>(ty)) { - const auto designator = sizedTy->getDesignator(); - switch (sizedTy->type) { - case Qubit: - qc->addQubitRegister(designator, identifier); - break; - case Bit: - case Int: - case Uint: - qc->addClassicalRegister(designator, identifier); - break; - case Float: - // not adding to qc - break; - case Angle: - error("Angle type is currently not supported.", - declarationStatement->debugInfo); - } - } else { - error("Only sized types are supported.", declarationStatement->debugInfo); - } - declarations.emplace(identifier, declarationStatement); - - if (declarationStatement->expression == nullptr) { - // value is uninitialized - return; - } - if (const auto measureExpression = - std::dynamic_pointer_cast( - declarationStatement->expression->expression)) { - assert(!declarationStatement->isConst && - "Type check pass should catch this"); - visitMeasureAssignment(identifier, nullptr, measureExpression, - declarationStatement->debugInfo); - return; - } - if (declarationStatement->isConst) { - // nothing to do - return; - } - - error("Only measure statements are supported for initialization.", - declarationStatement->debugInfo); - } - - void visitAssignmentStatement( - const std::shared_ptr assignmentStatement) override { - const auto identifier = assignmentStatement->identifier->identifier; - const auto declaration = declarations.find(identifier); - assert(declaration.has_value() && "Checked by type check pass"); - assert(!declaration->get()->isConst && "Checked by type check pass"); - - if (const auto measureExpression = - std::dynamic_pointer_cast( - assignmentStatement->expression->expression)) { - visitMeasureAssignment(identifier, assignmentStatement->indexExpression, - measureExpression, assignmentStatement->debugInfo); - return; - } - - // In the future, handle classical computation. - error("Classical computation not supported.", - assignmentStatement->debugInfo); - } - - void - visitInitialLayout(const std::shared_ptr layout) override { - if (!initialLayout.empty()) { - error("Multiple initial layout specifications found.", layout->debugInfo); - } - initialLayout = layout->permutation; - } - - void visitOutputPermutation( - const std::shared_ptr permutation) override { - if (!outputPermutation.empty()) { - error("Multiple output permutation specifications found.", - permutation->debugInfo); - } - outputPermutation = permutation->permutation; - } - - void visitGateStatement( - const std::shared_ptr gateStatement) override { - auto identifier = gateStatement->identifier; - if (gateStatement->isOpaque) { - if (gates.find(identifier) == gates.end()) { - // only builtin gates may be declared as opaque. - error("Unsupported opaque gate '" + identifier + "'.", - gateStatement->debugInfo); - } - - return; - } - - if (openQASM2CompatMode) { - // we need to check if this is a standard gate - identifier = parseGateIdentifierCompatMode(identifier).first; - } - - if (auto prevDeclaration = gates.find(identifier); - prevDeclaration != gates.end()) { - if (std::dynamic_pointer_cast(prevDeclaration->second)) { - // we ignore redeclarations of standard gates - return; - } - // TODO: print location of previous declaration - error("Gate '" + identifier + "' already declared.", - gateStatement->debugInfo); - } - - const auto parameters = gateStatement->parameters; - const auto qubits = gateStatement->qubits; - - // first we check that all parameters and qubits are unique - std::vector parameterIdentifiers{}; - for (const auto& parameter : parameters->identifiers) { - if (std::find(parameterIdentifiers.begin(), parameterIdentifiers.end(), - parameter->identifier) != parameterIdentifiers.end()) { - error("Parameter '" + parameter->identifier + "' already declared.", - gateStatement->debugInfo); - } - parameterIdentifiers.emplace_back(parameter->identifier); - } - std::vector qubitIdentifiers{}; - for (const auto& qubit : qubits->identifiers) { - if (std::find(qubitIdentifiers.begin(), qubitIdentifiers.end(), - qubit->identifier) != qubitIdentifiers.end()) { - error("Qubit '" + qubit->identifier + "' already declared.", - gateStatement->debugInfo); - } - qubitIdentifiers.emplace_back(qubit->identifier); - } - - auto compoundGate = std::make_shared(CompoundGate( - parameterIdentifiers, qubitIdentifiers, gateStatement->statements)); - - gates.emplace(identifier, compoundGate); - } - - void visitGateCallStatement( - const std::shared_ptr gateCallStatement) override { - const auto& qregs = qc->getQuantumRegisters(); - if (auto op = evaluateGateCall( - gateCallStatement, gateCallStatement->identifier, - gateCallStatement->arguments, gateCallStatement->operands, qregs); - op != nullptr) { - qc->emplace_back(std::move(op)); - } - } - - std::unique_ptr - evaluateGateCall(const std::shared_ptr& gateCallStatement, - const std::string& identifier, - const std::vector>& parameters, - std::vector> targets, - const qc::QuantumRegisterMap& qregs) { - auto iter = gates.find(identifier); - std::shared_ptr gate; - size_t implicitControls{0}; - - if (iter == gates.end()) { - if (identifier == "mcx" || identifier == "mcx_gray" || - identifier == "mcx_vchain" || identifier == "mcx_recursive" || - identifier == "mcphase") { - // we create a temp gate definition for these gates - gate = - getMcGateDefinition(identifier, gateCallStatement->operands.size(), - gateCallStatement->debugInfo); - } else if (openQASM2CompatMode) { - auto [updatedIdentifier, nControls] = - parseGateIdentifierCompatMode(identifier); - - iter = gates.find(updatedIdentifier); - if (iter == gates.end()) { - error("Usage of unknown gate '" + identifier + "'.", - gateCallStatement->debugInfo); - } - gate = iter->second; - implicitControls = nControls; - } else { - error("Usage of unknown gate '" + identifier + "'.", - gateCallStatement->debugInfo); - } - } else { - gate = iter->second; - } - - if (gate->getNParameters() != parameters.size()) { - error("Gate '" + identifier + "' takes " + - std::to_string(gate->getNParameters()) + " parameters, but " + - std::to_string(parameters.size()) + " were supplied.", - gateCallStatement->debugInfo); - } - - // here we count the number of controls - std::vector, bool>> controls{}; - // since standard gates may define a number of control targets, we first - // need to handle those - size_t nControls{gate->getNControls() + implicitControls}; - if (targets.size() < nControls) { - error("Gate '" + identifier + "' takes " + std::to_string(nControls) + - " controls, but only " + std::to_string(targets.size()) + - " qubits were supplied.", - gateCallStatement->debugInfo); - } - - for (size_t i = 0; i < nControls; ++i) { - controls.emplace_back(targets[i], true); - } - - bool invertOperation = false; - for (const auto& modifier : gateCallStatement->modifiers) { - if (auto ctrlModifier = - std::dynamic_pointer_cast(modifier); - ctrlModifier != nullptr) { - size_t const n = evaluatePositiveConstant(ctrlModifier->expression, - gateCallStatement->debugInfo, - /*defaultValue=*/1); - if (targets.size() < n + nControls) { - error("Gate '" + identifier + "' takes " + - std::to_string(n + nControls) + " controls, but only " + - std::to_string(targets.size()) + " were supplied.", - gateCallStatement->debugInfo); - } - - for (size_t i = 0; i < n; ++i) { - controls.emplace_back(targets[nControls + i], ctrlModifier->ctrlType); - } - nControls += n; - } else if (auto invModifier = - std::dynamic_pointer_cast(modifier); - invModifier != nullptr) { - // if we have an even number of inv modifiers, they cancel each other - // out - invertOperation = !invertOperation; - } else { - error("Only ctrl/negctrl/inv modifiers are supported.", - gateCallStatement->debugInfo); - } - } - targets.erase(targets.begin(), - targets.begin() + static_cast(nControls)); - - if (gate->getNTargets() != targets.size()) { - error("Gate '" + identifier + "' takes " + - std::to_string(gate->getNTargets()) + " targets, but " + - std::to_string(targets.size()) + " were supplied.", - gateCallStatement->debugInfo); - } - - // now evaluate all arguments; we only support const arguments. - std::vector evaluatedParameters{}; - for (const auto& param : parameters) { - auto result = constEvalPass.visit(param); - if (!result.has_value()) { - error("Only const expressions are supported as gate parameters, but " - "found '" + - param->getName() + "'.", - gateCallStatement->debugInfo); - } - - evaluatedParameters.emplace_back(result->toExpr()->asFP()); - } - - size_t broadcastingWidth{1}; - qc::Targets targetBits{}; - std::vector targetBroadcastingIndices{}; - size_t i{0}; - for (const auto& target : targets) { - qc::Targets t{}; - translateGateOperand(target, t, qregs, gateCallStatement->debugInfo); - - targetBits.emplace_back(t[0]); - - if (t.size() > 1) { - if (broadcastingWidth != 1 && t.size() != broadcastingWidth) { - error("When broadcasting, all registers must be of the same width.", - gateCallStatement->debugInfo); - } - broadcastingWidth = t.size(); - - targetBroadcastingIndices.emplace_back(i); - } - - i++; - } - - std::vector controlBits{}; - std::vector controlBroadcastingIndices{}; - i = 0; - for (const auto& [control, type] : controls) { - qc::Targets c{}; - translateGateOperand(control, c, qregs, gateCallStatement->debugInfo); - - controlBits.emplace_back(c[0], type ? qc::Control::Type::Pos - : qc::Control::Type::Neg); - - if (c.size() > 1) { - if (broadcastingWidth != 1 && c.size() != broadcastingWidth) { - error("When broadcasting, all registers must be of the same width.", - gateCallStatement->debugInfo); - } - broadcastingWidth = c.size(); - - controlBroadcastingIndices.emplace_back(i); - } - - i++; - } - - // check if any of the bits are duplicate - std::unordered_set allQubits; - for (const auto& control : controlBits) { - if (allQubits.find(control.qubit) != allQubits.end()) { - error("Duplicate qubit in control list.", gateCallStatement->debugInfo); - } - allQubits.emplace(control.qubit); - } - for (const auto& qubit : targetBits) { - if (allQubits.find(qubit) != allQubits.end()) { - error("Duplicate qubit in target list.", gateCallStatement->debugInfo); - } - allQubits.emplace(qubit); - } - - if (broadcastingWidth == 1) { - return applyQuantumOperation(gate, targetBits, controlBits, - evaluatedParameters, invertOperation, - gateCallStatement->debugInfo); - } - - // if we are broadcasting, we need to create a compound operation - auto op = std::make_unique(); - for (size_t j = 0; j < broadcastingWidth; ++j) { - // first we apply the operation - auto nestedOp = applyQuantumOperation( - gate, targetBits, controlBits, evaluatedParameters, invertOperation, - gateCallStatement->debugInfo); - if (nestedOp == nullptr) { - return nullptr; - } - op->getOps().emplace_back(std::move(nestedOp)); - - // after applying the operation, we update the broadcast bits - for (auto index : targetBroadcastingIndices) { - targetBits[index] = qc::Qubit{targetBits[index] + 1}; - } - for (auto index : controlBroadcastingIndices) { - controlBits[index].qubit = qc::Qubit{controlBits[index].qubit + 1}; - } - } - return op; - } - - static std::shared_ptr - getMcGateDefinition(const std::string& identifier, size_t operandSize, - const std::shared_ptr& debugInfo) { - std::vector targetParams{}; - std::vector> operands; - size_t nTargets = operandSize; - if (identifier == "mcx_vchain") { - nTargets -= (nTargets + 1) / 2 - 2; - } else if (identifier == "mcx_recursive" && nTargets > 5) { - nTargets -= 1; - } - for (size_t i = 0; i < operandSize; ++i) { - targetParams.emplace_back("q" + std::to_string(i)); - if (i < nTargets) { - operands.emplace_back( - std::make_shared("q" + std::to_string(i), nullptr)); - } - } - const size_t nControls = nTargets - 1; - - std::string nestedGateIdentifier = "x"; - std::vector> nestedParameters{}; - std::vector nestedParameterNames{}; - if (identifier == "mcphase") { - nestedGateIdentifier = "p"; - nestedParameters.emplace_back( - std::make_shared("x")); - nestedParameterNames.emplace_back("x"); - } - - // ctrl(nTargets - 1) @ x q0, ..., q(nTargets - 1) - const auto gateCall = GateCallStatement( - debugInfo, nestedGateIdentifier, - std::vector>{ - std::make_shared( - true, std::make_shared(nControls, false))}, - nestedParameters, operands); - const auto inner = std::make_shared(gateCall); - - const CompoundGate g{nestedParameterNames, targetParams, {inner}}; - return std::make_shared(g); - } - - std::unique_ptr applyQuantumOperation( - const std::shared_ptr& gate, qc::Targets targetBits, - std::vector controlBits, - std::vector evaluatedParameters, bool invertOperation, - const std::shared_ptr& debugInfo) { - if (auto* standardGate = dynamic_cast(gate.get())) { - auto op = std::make_unique( - qc::Controls{}, targetBits, standardGate->info.type, - evaluatedParameters); - if (invertOperation) { - op->invert(); - } - op->setControls(qc::Controls{controlBits.begin(), controlBits.end()}); - return op; - } - if (auto* compoundGate = dynamic_cast(gate.get())) { - constEvalPass.pushEnv(); - - for (size_t i = 0; i < compoundGate->parameterNames.size(); ++i) { - constEvalPass.addConst(compoundGate->parameterNames[i], - evaluatedParameters[i]); - } - - auto nestedQubits = qc::QuantumRegisterMap{}; - size_t index = 0; - for (const auto& qubitIdentifier : compoundGate->targetNames) { - nestedQubits.try_emplace(qubitIdentifier, targetBits[index], 1, - qubitIdentifier); - index++; - } - - auto op = std::make_unique(true); - for (const auto& nestedGate : compoundGate->body) { - if (auto barrierStatement = - std::dynamic_pointer_cast(nestedGate); - barrierStatement != nullptr) { - // nothing to do here for the simulator. - } else if (auto resetStatement = - std::dynamic_pointer_cast(nestedGate); - resetStatement != nullptr) { - op->emplace_back(getResetOp(resetStatement, nestedQubits)); - } else if (auto gateCallStatement = - std::dynamic_pointer_cast(nestedGate); - gateCallStatement != nullptr) { - for (const auto& operand : gateCallStatement->operands) { - // OpenQASM 3.0 doesn't support indexing of gate arguments. - if (operand->expression != nullptr && - std::find(compoundGate->targetNames.begin(), - compoundGate->targetNames.end(), - operand->identifier) != - compoundGate->targetNames.end()) { - error("Gate arguments cannot be indexed within gate body.", - debugInfo); - } - } - - auto nestedOp = - evaluateGateCall(gateCallStatement, gateCallStatement->identifier, - gateCallStatement->arguments, - gateCallStatement->operands, nestedQubits); - if (nestedOp == nullptr) { - return nullptr; - } - op->getOps().emplace_back(std::move(nestedOp)); - } else { - error("Unhandled quantum statement.", debugInfo); - } - } - op->setControls(qc::Controls{controlBits.begin(), controlBits.end()}); - if (invertOperation) { - op->invert(); - } - - constEvalPass.popEnv(); - - if (op->getOps().size() == 1) { - return std::move(op->getOps()[0]); - } - - return op; - } - - error("Unknown gate type.", debugInfo); - } - - void visitMeasureAssignment( - const std::string& identifier, - const std::shared_ptr& indexExpression, - const std::shared_ptr& measureExpression, - const std::shared_ptr& debugInfo) { - const auto decl = declarations.find(identifier); - if (!decl.has_value()) { - error("Usage of unknown identifier '" + identifier + "'.", debugInfo); - } - - if (!std::get<1>(decl.value()->type)->isBit()) { - error("Measure expression can only be assigned to a bit register.", - debugInfo); - } - - std::vector qubits{}; - std::vector bits{}; - translateGateOperand(measureExpression->gate, qubits, - qc->getQuantumRegisters(), debugInfo); - translateBitOperand(identifier, indexExpression, bits, debugInfo); - - if (qubits.size() != bits.size()) { - error("Classical and quantum register must have the same width in " - "measure statement. Classical register '" + - identifier + "' has " + std::to_string(bits.size()) + - " bits, but quantum register '" + - measureExpression->gate->identifier + "' has " + - std::to_string(qubits.size()) + " qubits.", - debugInfo); - } - - auto op = std::make_unique(qubits, bits); - qc->emplace_back(std::move(op)); - } - - void visitBarrierStatement( - const std::shared_ptr barrierStatement) override { - qc->emplace_back(getBarrierOp(barrierStatement, qc->getQuantumRegisters())); - } - - void - visitResetStatement(std::shared_ptr resetStatement) override { - qc->emplace_back(getResetOp(resetStatement, qc->getQuantumRegisters())); - } - - void visitIfStatement(std::shared_ptr ifStatement) override { - // TODO: for now we only support statements comparing a classical bit reg - // to a constant. - const auto condition = - std::dynamic_pointer_cast(ifStatement->condition); - if (condition == nullptr) { - error("Condition not supported for if statement.", - ifStatement->debugInfo); - } - - const auto comparisonKind = getComparisonKind(condition->op); - if (!comparisonKind) { - error("Unsupported comparison operator.", ifStatement->debugInfo); - } - - const auto lhs = - std::dynamic_pointer_cast(condition->lhs); - const auto rhs = std::dynamic_pointer_cast(condition->rhs); - - if (lhs == nullptr) { - error("Only classical registers are supported in conditions.", - ifStatement->debugInfo); - } - if (rhs == nullptr) { - error("Can only compare to constants.", ifStatement->debugInfo); - } - - const auto creg = qc->getClassicalRegisters().find(lhs->identifier); - if (creg == qc->getClassicalRegisters().end()) { - error("Usage of unknown or invalid identifier '" + lhs->identifier + - "' in condition.", - ifStatement->debugInfo); - } - - // translate statements in then/else blocks - if (!ifStatement->thenStatements.empty()) { - auto thenOps = translateBlockOperations(ifStatement->thenStatements); - qc->emplace_back(std::make_unique( - std::move(thenOps), creg->second, rhs->getUInt(), *comparisonKind)); - } - if (!ifStatement->elseStatements.empty()) { - const auto invertedComparisonKind = - qc::getInvertedComparisonKind(*comparisonKind); - auto elseOps = translateBlockOperations(ifStatement->elseStatements); - qc->emplace_back(std::make_unique( - std::move(elseOps), creg->second, rhs->getUInt(), - invertedComparisonKind)); - } - } - - [[nodiscard]] std::unique_ptr translateBlockOperations( - const std::vector>& statements) { - auto blockOps = std::make_unique(); - for (const auto& statement : statements) { - auto gateCall = std::dynamic_pointer_cast(statement); - if (gateCall == nullptr) { - error("Only quantum statements are supported in blocks.", - statement->debugInfo); - } - const auto& qregs = qc->getQuantumRegisters(); - - auto op = - evaluateGateCall(gateCall, gateCall->identifier, gateCall->arguments, - gateCall->operands, qregs); - - blockOps->emplace_back(std::move(op)); - } - - return blockOps; - } - - [[nodiscard]] static std::unique_ptr - getBarrierOp(const std::shared_ptr& barrierStatement, - const qc::QuantumRegisterMap& qregs) { - std::vector qubits{}; - for (const auto& gate : barrierStatement->gates) { - translateGateOperand(gate, qubits, qregs, barrierStatement->debugInfo); - } - - return std::make_unique(qubits, qc::Barrier); - } - - [[nodiscard]] static std::unique_ptr - getResetOp(const std::shared_ptr& resetStatement, - const qc::QuantumRegisterMap& qregs) { - std::vector qubits{}; - translateGateOperand(resetStatement->gate, qubits, qregs, - resetStatement->debugInfo); - return std::make_unique(qubits, qc::Reset); - } - - std::pair - parseGateIdentifierCompatMode(const std::string& identifier) { - // we need to copy as we modify the string and need to return the original - // string if we don't find a match. - std::string gateIdentifier = identifier; - size_t implicitControls = 0; - while (!gateIdentifier.empty() && gateIdentifier[0] == 'c') { - gateIdentifier = gateIdentifier.substr(1); - implicitControls++; - } - - if (gates.find(gateIdentifier) == gates.end()) { - return std::pair{identifier, 0}; - } - return std::pair{gateIdentifier, implicitControls}; - } -}; - -void qc::QuantumComputation::importOpenQASM3(std::istream& is) { - using namespace qasm3; - - Parser p(&is); - - const auto program = p.parseProgram(); - OpenQasm3Parser parser{this}; - parser.visitProgram(program); -} diff --git a/src/ir/parsers/QCParser.cpp b/src/ir/parsers/QCParser.cpp deleted file mode 100644 index 31a37dc28..000000000 --- a/src/ir/parsers/QCParser.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (c) 2025 Chair for Design Automation, TUM - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "Definitions.hpp" -#include "ir/QuantumComputation.hpp" -#include "ir/operations/Control.hpp" -#include "ir/operations/OpType.hpp" -#include "ir/operations/StandardOperation.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void qc::QuantumComputation::importQC(std::istream& is) { - std::map varMap{}; - auto line = readQCHeader(is, varMap); - readQCGateDescriptions(is, line, varMap); -} - -int qc::QuantumComputation::readQCHeader(std::istream& is, - std::map& varMap) { - std::string cmd; - std::string variable; - std::string identifier; - int line = 0; - - const std::string delimiter = " "; - std::size_t pos{}; - - std::vector variables{}; - std::vector inputs{}; - std::vector outputs{}; - std::vector constants{}; - - while (true) { - if (!static_cast(is >> cmd)) { - throw QFRException("[qc parser] l:" + std::to_string(line) + - " msg: Invalid file header"); - } - ++line; - - // skip comments - if (cmd.front() == '#') { - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } - - // valid header commands start with '.' or end the header with BEGIN - if (cmd.front() != '.' && cmd != "BEGIN" && cmd != "begin") { - throw QFRException("[qc parser] l:" + std::to_string(line) + - " msg: Invalid file header"); - } - - // header read complete - if (cmd == "BEGIN" || cmd == "begin") { - break; - } - - if (cmd == ".v") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - variables.emplace_back(variable); - identifier.erase(0, pos + 1); - } - variables.emplace_back(identifier); - } else if (cmd == ".i") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - if (std::find(variables.begin(), variables.end(), variable) != - variables.end()) { - inputs.emplace_back(variable); - } else { - throw QFRException( - "[qc parser] l:" + std::to_string(line) + - " msg: Unknown variable in input statement: " + cmd); - } - identifier.erase(0, pos + 1); - } - if (std::find(variables.begin(), variables.end(), identifier) != - variables.end()) { - inputs.emplace_back(identifier); - } else { - throw QFRException("[qc parser] l:" + std::to_string(line) + - " msg: Unknown variable in input statement: " + cmd); - } - } else if (cmd == ".o") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - if (std::find(variables.begin(), variables.end(), variable) != - variables.end()) { - outputs.emplace_back(variable); - } else { - throw QFRException( - "[qc parser] l:" + std::to_string(line) + - " msg: Unknown variable in output statement: " + cmd); - } - identifier.erase(0, pos + 1); - } - if (std::find(variables.begin(), variables.end(), identifier) != - variables.end()) { - outputs.emplace_back(identifier); - } else { - throw QFRException( - "[qc parser] l:" + std::to_string(line) + - " msg: Unknown variable in output statement: " + cmd); - } - } else if (cmd == ".c") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - constants.emplace_back(variable); - identifier.erase(0, pos + 1); - } - constants.emplace_back(identifier); - } else if (cmd == ".ol") { // ignore output labels - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } else { - throw QFRException("[qc parser] l:" + std::to_string(line) + - " msg: Unknown command: " + cmd); - } - } - addQubitRegister(inputs.size()); - auto nconstants = variables.size() - inputs.size(); - if (nconstants > 0) { - addAncillaryRegister(nconstants); - } - - auto qidx = 0; - auto constidx = inputs.size(); - for (auto& var : variables) { - // check if variable is input - if (std::count(inputs.begin(), inputs.end(), var) != 0) { - varMap.insert({var, qidx++}); - } else { - if (!constants.empty()) { - if (constants.at(constidx - inputs.size()) == "0" || - constants.at(constidx - inputs.size()) == "1") { - // add X operation in case of initial value 1 - if (constants.at(constidx - inputs.size()) == "1") { - x(static_cast(constidx)); - } - varMap.insert({var, static_cast(constidx++)}); - } else { - throw QFRException("[qc parser] l:" + std::to_string(line) + - " msg: Non-binary constant specified: " + cmd); - } - } else { - // variable does not occur in input statement --> assumed to be |0> - // ancillary - varMap.insert({var, static_cast(constidx++)}); - } - } - } - - for (std::size_t q = 0; q < variables.size(); ++q) { - variable = variables.at(q); - auto p = varMap.at(variable); - initialLayout[static_cast(q)] = p; - if (!outputs.empty()) { - if (std::count(outputs.begin(), outputs.end(), variable) != 0) { - outputPermutation[static_cast(q)] = p; - } else { - outputPermutation.erase(static_cast(q)); - garbage.at(p) = true; - } - } else { - // no output statement given --> assume all outputs are relevant - outputPermutation[static_cast(q)] = p; - } - } - - return line; -} - -void qc::QuantumComputation::readQCGateDescriptions( - std::istream& is, int line, std::map& varMap) { - const std::regex gateRegex = std::regex( - R"((H|X|Y|Zd?|[SPT]\*?|tof|cnot|swap|R[xyz])(?:\((pi\/2\^(\d+)|(?:[-+]?[0-9]+[.]?[0-9]*(?:[eE][-+]?[0-9]+)?))\))?)"); - std::smatch m; - std::string cmd; - - while (!is.eof()) { - if (!static_cast(is >> cmd)) { - throw QFRException("[qc parser] l:" + std::to_string(line) + - " msg: Failed to read command"); - } - ++line; - - if (cmd.front() == '#') { - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } - - if (cmd == "END" || cmd == "end") { - break; - } - - // match gate declaration - if (!std::regex_match(cmd, m, gateRegex)) { - throw QFRException("[qc parser] l:" + std::to_string(line) + - " msg: Unsupported gate detected: " + cmd); - } - - // extract gate information (identifier, #controls, divisor) - auto lambda = static_cast(0L); - OpType gate = None; - const std::string gateType = m.str(1); - if (gateType == "H") { - gate = H; - } else if (gateType == "X" || gateType == "cnot" || gateType == "tof") { - gate = X; - } else if (gateType == "Y") { - gate = Y; - } else if (gateType == "Z" || gateType == "Zd") { - gate = Z; - } else if (gateType == "P" || gateType == "S") { - gate = S; - } else if (gateType == "P*" || gateType == "S*") { - gate = Sdg; - } else if (gateType == "T") { - gate = T; - } else if (gateType == "T*") { - gate = Tdg; - } else if (gateType == "swap") { - gate = SWAP; - } else if (gateType == "Rx") { - gate = RX; - } else if (gateType == "Ry") { - gate = RY; - } else if (gateType == "Rz") { - gate = RZ; - } - - if (gate == RX || gate == RY || gate == RZ) { - if (m.str(3).empty()) { - // float definition - lambda = static_cast(std::stold(m.str(2))); - } else if (!m.str(2).empty()) { - // pi/2^x definition - auto power = std::stoul(m.str(3)); - if (power == 0UL) { - lambda = PI; - } else if (power == 1UL) { - lambda = PI_2; - } else if (power == 2UL) { - lambda = PI_4; - } else { - lambda = PI_4 / (std::pow(static_cast(2), power - 2UL)); - } - } else { - throw QFRException("Rotation gate without angle detected"); - } - } - - std::string qubits; - std::string label; - is >> std::ws; - getline(is, qubits); - - std::vector controls{}; - - auto delimiter = ' '; - std::size_t pos{}; - - while ((pos = qubits.find(delimiter)) != std::string::npos) { - label = qubits.substr(0, pos); - if (label.back() == '\'') { - label.erase(label.size() - 1); - controls.emplace_back(varMap.at(label), Control::Type::Neg); - } else { - controls.emplace_back(varMap.at(label)); - } - qubits.erase(0, pos + 1); - } - // delete whitespace at the end - qubits.erase(std::remove(qubits.begin(), qubits.end(), delimiter), - qubits.end()); - controls.emplace_back(varMap.at(qubits)); - - if (controls.size() > nqubits + nancillae) { - throw QFRException( - "[qc parser] l:" + std::to_string(line) + " msg: Gate acts on " + - std::to_string(controls.size()) + " qubits, but only " + - std::to_string(nqubits + nancillae) + " qubits are available."); - } - - if (gate == X) { - const Qubit target = controls.back().qubit; - controls.pop_back(); - mcx(Controls{controls.cbegin(), controls.cend()}, target); - } else if (gate == H || gate == Y || gate == Z || gate == S || - gate == Sdg || gate == T || gate == Tdg) { - const Qubit target = controls.back().qubit; - controls.pop_back(); - emplace_back( - Controls{controls.cbegin(), controls.cend()}, target, gate); - } else if (gate == SWAP) { - const Qubit target0 = controls.back().qubit; - controls.pop_back(); - const Qubit target1 = controls.back().qubit; - controls.pop_back(); - mcswap(Controls{controls.cbegin(), controls.cend()}, target0, target1); - } else if (gate == RX || gate == RY || gate == RZ) { - const Qubit target = controls.back().qubit; - controls.pop_back(); - emplace_back( - Controls{controls.cbegin(), controls.cend()}, target, gate, - std::vector{lambda}); - } - } -} diff --git a/src/ir/parsers/RealParser.cpp b/src/ir/parsers/RealParser.cpp deleted file mode 100644 index bee0617e7..000000000 --- a/src/ir/parsers/RealParser.cpp +++ /dev/null @@ -1,866 +0,0 @@ -/* - * Copyright (c) 2025 Chair for Design Automation, TUM - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "Definitions.hpp" -#include "ir/QuantumComputation.hpp" -#include "ir/Register.hpp" -#include "ir/operations/Control.hpp" -#include "ir/operations/OpType.hpp" -#include "ir/operations/StandardOperation.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { -std::optional getQubitForVariableIdentFromAnyLookup( - const std::string& variableIdent, const qc::QuantumRegisterMap& dataQubits, - const qc::QuantumRegisterMap& ancillaryQubits) { - if (const auto& matchingEntryInDataQubits = dataQubits.find(variableIdent); - matchingEntryInDataQubits != dataQubits.end()) { - return matchingEntryInDataQubits->second.getStartIndex(); - } - - if (const auto& matchingEntryInAncillaryQubits = - ancillaryQubits.find(variableIdent); - matchingEntryInAncillaryQubits != ancillaryQubits.end()) { - return matchingEntryInAncillaryQubits->second.getStartIndex(); - } - - return std::nullopt; -} - -/// Determine whether the given io name value, which is not enclosed in quotes, -/// consists of only letters, digits, and underscore characters. -/// @param ioName The name to validate -/// @return Whether the given io name is valid -bool isValidIoName(const std::string_view& ioName) noexcept { - return !ioName.empty() && - std::all_of( - ioName.cbegin(), ioName.cend(), [](const char ioNameCharacter) { - return static_cast(std::isalnum( - static_cast(ioNameCharacter))) || - ioNameCharacter == '_'; - }); -} - -std::vector -parseVariableNames(const int processedLineNumberInRealFile, - const std::size_t expectedNumberOfVariables, - const std::string& readInRawVariableIdentValues, - const std::unordered_set& variableIdentsLookup, - const std::string_view& trimableVariableIdentPrefix) { - std::vector variableNames; - variableNames.reserve(expectedNumberOfVariables); - - std::unordered_set processedVariableIdents; - std::size_t variableIdentStartIdx = 0; - std::size_t variableIdentEndIdx = 0; - - while (variableIdentStartIdx < readInRawVariableIdentValues.size() && - variableNames.size() < expectedNumberOfVariables) { - variableIdentEndIdx = - readInRawVariableIdentValues.find_first_of(' ', variableIdentStartIdx); - - if (variableIdentEndIdx == std::string::npos) { - variableIdentEndIdx = readInRawVariableIdentValues.size(); - } - - std::size_t variableIdentLength = - variableIdentEndIdx - variableIdentStartIdx; - - // Remove carriage return character if present - if (variableIdentLength > 0 && - readInRawVariableIdentValues.at(variableIdentEndIdx - 1) == '\r') { - --variableIdentLength; - } - - auto variableIdent = readInRawVariableIdentValues.substr( - variableIdentStartIdx, variableIdentLength); - const bool trimVariableIdent = - variableIdent.find_first_of(trimableVariableIdentPrefix) == 0; - if (trimVariableIdent) { - variableIdent = - variableIdent.replace(0, trimableVariableIdentPrefix.size(), ""); - } - - if (!isValidIoName(variableIdent)) { - throw qc::QFRException( - "[real parser] l: " + std::to_string(processedLineNumberInRealFile) + - " msg: invalid variable name: " + variableIdent); - } - - if (processedVariableIdents.count(variableIdent) > 0) { - throw qc::QFRException( - "[real parser] l: " + std::to_string(processedLineNumberInRealFile) + - " msg: duplicate variable name: " + variableIdent); - } - - if (!variableIdentsLookup.empty() && - variableIdentsLookup.count(variableIdent) == 0) { - throw qc::QFRException( - "[real parser] l: " + std::to_string(processedLineNumberInRealFile) + - " msg: given variable name " + variableIdent + - " was not declared in .variables entry"); - } - processedVariableIdents.emplace(variableIdent); - variableNames.emplace_back(trimVariableIdent - ? std::string(trimableVariableIdentPrefix) + - variableIdent - : variableIdent); - variableIdentStartIdx = variableIdentEndIdx + 1; - } - - if (variableIdentEndIdx < readInRawVariableIdentValues.size() && - readInRawVariableIdentValues.at(variableIdentEndIdx) == ' ') { - throw qc::QFRException( - "[real parser] l: " + std::to_string(processedLineNumberInRealFile) + - " msg: expected only " + std::to_string(expectedNumberOfVariables) + - " variable identifiers to be declared but variable identifier " - "delimiter was found" - " after " + - std::to_string(expectedNumberOfVariables) + - " identifiers were detected (which we assume will be followed by " - "another io identifier)!"); - } - - if (variableNames.size() < expectedNumberOfVariables) { - throw qc::QFRException( - "[real parser] l:" + std::to_string(processedLineNumberInRealFile) + - " msg: Expected " + std::to_string(expectedNumberOfVariables) + - " variable idents but only " + std::to_string(variableNames.size()) + - " were declared!"); - } - return variableNames; -} - -std::unordered_map -parseIoNames(const int lineInRealFileDefiningIoNames, - const std::size_t expectedNumberOfIos, - const std::string& ioNameIdentsRawValues, - const std::unordered_set& variableIdentLookup) { - std::unordered_map foundIoNames; - std::size_t ioNameStartIdx = 0; - std::size_t ioNameEndIdx = 0; - std::size_t ioIdx = 0; - - bool searchingForWhitespaceCharacter = false; - while (ioNameStartIdx < ioNameIdentsRawValues.size() && - foundIoNames.size() <= expectedNumberOfIos) { - searchingForWhitespaceCharacter = - ioNameIdentsRawValues.at(ioNameStartIdx) != '"'; - if (searchingForWhitespaceCharacter) { - ioNameEndIdx = ioNameIdentsRawValues.find_first_of(' ', ioNameStartIdx); - } else { - ioNameEndIdx = - ioNameIdentsRawValues.find_first_of('"', ioNameStartIdx + 1); - } - - if (ioNameEndIdx == std::string::npos) { - ioNameEndIdx = ioNameIdentsRawValues.size(); - if (!searchingForWhitespaceCharacter) { - throw qc::QFRException( - "[real parser] l: " + - std::to_string(lineInRealFileDefiningIoNames) + - " no matching closing quote found for name of io: " + - std::to_string(ioIdx)); - } - } else { - ioNameEndIdx += - static_cast(!searchingForWhitespaceCharacter); - } - - std::size_t ioNameLength = ioNameEndIdx - ioNameStartIdx; - // Remove carriage return character if present - if (ioNameLength > 0 && - ioNameIdentsRawValues.at(ioNameEndIdx - 1) == '\r') { - --ioNameLength; - } - - const auto& ioName = - ioNameIdentsRawValues.substr(ioNameStartIdx, ioNameLength); - - std::string_view ioNameToValidate = ioName; - if (!searchingForWhitespaceCharacter) { - ioNameToValidate = - ioNameToValidate.substr(1, ioNameToValidate.size() - 2); - } - - if (!isValidIoName(ioNameToValidate)) { - throw qc::QFRException( - "[real parser] l: " + std::to_string(lineInRealFileDefiningIoNames) + - " msg: invalid io name: " + ioName); - } - - if (variableIdentLookup.count(ioName) > 0) { - throw qc::QFRException( - "[real parser] l: " + std::to_string(lineInRealFileDefiningIoNames) + - " msg: IO ident matched already declared variable with name " + - ioName); - } - - ioNameStartIdx = ioNameEndIdx + 1; - if (const auto& ioNameInsertionIntoLookupResult = - foundIoNames.emplace(ioName, static_cast(ioIdx++)); - !ioNameInsertionIntoLookupResult.second) { - throw qc::QFRException( - "[real parser] l:" + std::to_string(lineInRealFileDefiningIoNames) + - " msg: duplicate io name: " + ioName); - } - } - - if (searchingForWhitespaceCharacter && - ioNameEndIdx + 1 < ioNameIdentsRawValues.size() && - ioNameIdentsRawValues.at(ioNameEndIdx + 1) == ' ') { - throw qc::QFRException( - "[real parser] l:" + std::to_string(lineInRealFileDefiningIoNames) + - " msg: expected only " + std::to_string(expectedNumberOfIos) + - " io identifiers to be declared but io identifier delimiter was found" - " after " + - std::to_string(expectedNumberOfIos) + - " identifiers were detected (which we assume will be followed by " - "another io identifier)!"); - } - return foundIoNames; -} - -void assertRequiredHeaderComponentsAreDefined( - const int processedLine, - std::initializer_list requiredHeaderComponentPrefixes, - const std::set>& - currentUserDeclaredHeaderComponents) { - - for (const auto& requiredHeaderComponentPrefix : - requiredHeaderComponentPrefixes) { - if (currentUserDeclaredHeaderComponents.count( - requiredHeaderComponentPrefix) == 0) { - throw qc::QFRException( - "[real parser] l:" + std::to_string(processedLine) + - " msg: Expected " + std::string(requiredHeaderComponentPrefix) + - " to have been already defined"); - } - } -} - -void trimCommentAndTrailingWhitespaceData(std::string& lineToProcess) { - if (const auto commentLinePrefixPosition = lineToProcess.find_first_of('#'); - commentLinePrefixPosition != std::string::npos) { - if (commentLinePrefixPosition != 0) { - lineToProcess = lineToProcess.substr(0, commentLinePrefixPosition); - } else { - lineToProcess = ""; - } - } - - if (lineToProcess.empty()) { - return; - } - - if (const std::size_t positionOfLastDataCharacter = - lineToProcess.find_last_not_of(" \t"); - positionOfLastDataCharacter != std::string::npos && - positionOfLastDataCharacter != lineToProcess.size() - 1) { - lineToProcess = lineToProcess.substr(0, positionOfLastDataCharacter + 1); - } -} -} // namespace - -void qc::QuantumComputation::importReal(std::istream& is) { - const auto line = readRealHeader(is); - readRealGateDescriptions(is, line); -} - -int qc::QuantumComputation::readRealHeader(std::istream& is) { - std::string cmd; - int line = 0; - - // We could reuse the QuantumRegisterMap type defined in the qc namespace but - // to avoid potential errors due to any future refactoring of said type, we - // use an std::unordered_map instead - std::unordered_map userDefinedInputIdents; - std::unordered_map userDefinedOutputIdents; - std::unordered_set userDeclaredVariableIdents; - std::unordered_set outputQubitsMarkedAsGarbage; - - constexpr std::string_view numVariablesHeaderComponentPrefix = ".NUMVARS"; - constexpr std::string_view variablesHeaderComponentPrefix = ".VARIABLES"; - constexpr std::string_view outputsHeaderComponentPrefix = ".OUTPUTS"; - - std::set> definedHeaderComponents; - - while (true) { - if (!static_cast(is >> cmd)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Invalid file header"); - } - std::transform(cmd.begin(), cmd.end(), cmd.begin(), [](unsigned char ch) { - return static_cast(toupper(ch)); - }); - ++line; - - // skip comments - if (cmd.front() == '#') { - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } - - // valid header commands start with '.' - if (cmd.front() != '.') { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Invalid file header"); - } - - if (definedHeaderComponents.count(cmd) != 0) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Duplicate definition of header component " + - cmd); - } - - definedHeaderComponents.emplace(cmd); - if (cmd == ".BEGIN") { - // Entries .numvars and .variables must be declared in all .real files - assertRequiredHeaderComponentsAreDefined( - line, - {numVariablesHeaderComponentPrefix, variablesHeaderComponentPrefix}, - definedHeaderComponents); - - // The garbage declarations in the .real file are defined on the outputs - // while the garbage state of the quantum computation operates on the - // defined inputs, thus we perform a mapping from the output marked as - // garbage back to the input using the output permutation. - for (const auto& outputQubitMarkedAsGarbage : - outputQubitsMarkedAsGarbage) { - - // Since the call setLogicalQubitAsGarbage(...) assumes that the qubit - // parameter is an input qubit, we need to manually mark the output - // qubit as garbage by using the output qubit instead. - garbage[outputQubitMarkedAsGarbage] = true; - outputPermutation.erase(outputQubitMarkedAsGarbage); - } - - // header read complete - return line; - } - - if (cmd == ".NUMVARS") { - if (std::size_t nq{}; !static_cast(is >> nq)) { - nqubits = 0; - } else { - nqubits = nq; - } - nclassics = nqubits; - } else if (cmd == ".VARIABLES") { - is >> std::ws; - assertRequiredHeaderComponentsAreDefined( - line, {numVariablesHeaderComponentPrefix}, definedHeaderComponents); - userDeclaredVariableIdents.reserve(nclassics); - - std::string variableDefinitionEntry; - if (!std::getline(is, variableDefinitionEntry)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed read in '.variables' line"); - } - - trimCommentAndTrailingWhitespaceData(variableDefinitionEntry); - const auto& processedVariableIdents = - parseVariableNames(line, nclassics, variableDefinitionEntry, {}, ""); - userDeclaredVariableIdents.insert(processedVariableIdents.cbegin(), - processedVariableIdents.cend()); - - ancillary.resize(nqubits); - garbage.resize(nqubits); - for (std::size_t i = 0; i < nclassics; ++i) { - const auto qubit = static_cast(i); - const std::string& quantumRegisterIdentifier = - processedVariableIdents.at(i); - quantumRegisters.emplace( - quantumRegisterIdentifier, - QuantumRegister(qubit, 1, quantumRegisterIdentifier)); - - const std::string& classicalRegisterIdentifier = - "c_" + quantumRegisterIdentifier; - classicalRegisters.emplace( - classicalRegisterIdentifier, - ClassicalRegister(qubit, 1, classicalRegisterIdentifier)); - initialLayout.emplace(qubit, qubit); - outputPermutation.emplace(qubit, qubit); - } - } else if (cmd == ".INITIAL_LAYOUT") { - is >> std::ws; - assertRequiredHeaderComponentsAreDefined( - line, - {numVariablesHeaderComponentPrefix, variablesHeaderComponentPrefix}, - definedHeaderComponents); - - std::string initialLayoutDefinitionEntry; - if (!std::getline(is, initialLayoutDefinitionEntry)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed read in '.initial_layout' line"); - } - - trimCommentAndTrailingWhitespaceData(initialLayoutDefinitionEntry); - const auto& processedVariableIdents = - parseVariableNames(line, nclassics, initialLayoutDefinitionEntry, - userDeclaredVariableIdents, ""); - - // Map the user declared variable idents in the .variable entry to the - // ones declared in the .initial_layout as explained in - // https://mqt.readthedocs.io/projects/core/en/latest/mqt_core_ir.html#layout-information - for (std::size_t i = 0; i < nclassics; ++i) { - const auto algorithmicQubit = static_cast(i); - const auto deviceQubitForVariableIdentInInitialLayout = - quantumRegisters.at(processedVariableIdents.at(i)).getStartIndex(); - initialLayout[deviceQubitForVariableIdentInInitialLayout] = - algorithmicQubit; - } - } else if (cmd == ".CONSTANTS") { - is >> std::ws; - assertRequiredHeaderComponentsAreDefined( - line, {numVariablesHeaderComponentPrefix}, definedHeaderComponents); - - std::string constantsValuePerIoDefinition; - if (!std::getline(is, constantsValuePerIoDefinition)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed read in '.constants' line"); - } - - trimCommentAndTrailingWhitespaceData(constantsValuePerIoDefinition); - if (constantsValuePerIoDefinition.size() != nclassics) { - throw QFRException( - "[real parser] l:" + std::to_string(line) + " msg: Expected " + - std::to_string(nclassics) + " constant values but " + - std::to_string(constantsValuePerIoDefinition.size()) + - " were declared!"); - } - - std::size_t constantValueIdx = 0; - for (const auto constantValuePerIo : constantsValuePerIoDefinition) { - if (const bool isCurrentQubitMarkedAsAncillary = - constantValuePerIo == '0' || constantValuePerIo == '1'; - isCurrentQubitMarkedAsAncillary) { - const auto& ancillaryQubit = static_cast(constantValueIdx); - // Since ancillary qubits are assumed to have an initial value of - // zero, we need to add an inversion gate to derive the correct - // initial value of 1. - if (constantValuePerIo == '1') { - x(ancillaryQubit); - } - - setLogicalQubitAncillary(ancillaryQubit); - - // Since the call to setLogicalQubitAncillary does not actually - // transfer the qubit from the data qubit register into the ancillary - // register we will 'manually' perform this transfer. - const std::string associatedVariableNameForQubitRegister = - getQubitRegister(ancillaryQubit).getName(); - quantumRegisters.erase(associatedVariableNameForQubitRegister); - ancillaRegisters.insert_or_assign( - associatedVariableNameForQubitRegister, - QuantumRegister(ancillaryQubit, 1, - associatedVariableNameForQubitRegister)); - } else if (constantValuePerIo != '-') { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Invalid value in '.constants' header: '" + - std::to_string(constantValuePerIo) + "'"); - } - ++constantValueIdx; - } - } else if (cmd == ".GARBAGE") { - is >> std::ws; - assertRequiredHeaderComponentsAreDefined( - line, {numVariablesHeaderComponentPrefix}, definedHeaderComponents); - - std::string garbageStatePerIoDefinition; - if (!std::getline(is, garbageStatePerIoDefinition)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed read in '.garbage' line"); - } - - trimCommentAndTrailingWhitespaceData(garbageStatePerIoDefinition); - if (garbageStatePerIoDefinition.size() != nclassics) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Expected " + std::to_string(nclassics) + - " garbage state values but " + - std::to_string(garbageStatePerIoDefinition.size()) + - " were declared!"); - } - - std::size_t garbageStateIdx = 0; - for (const auto garbageStateValue : garbageStatePerIoDefinition) { - if (const bool isCurrentQubitMarkedAsGarbage = garbageStateValue == '1'; - isCurrentQubitMarkedAsGarbage) { - outputQubitsMarkedAsGarbage.emplace( - static_cast(garbageStateIdx)); - } else if (garbageStateValue != '-') { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Invalid value in '.garbage' header: '" + - std::to_string(garbageStateValue) + "'"); - } - garbageStateIdx++; - } - } else if (cmd == ".INPUTS") { - // .INPUT: specifies initial layout - is >> std::ws; - assertRequiredHeaderComponentsAreDefined( - line, - {numVariablesHeaderComponentPrefix, variablesHeaderComponentPrefix}, - definedHeaderComponents); - - if (definedHeaderComponents.count(outputsHeaderComponentPrefix) > 0) { - throw QFRException( - "[real parser] l:" + std::to_string(line) + - " msg: .inputs entry must be declared prior to the .outputs entry"); - } - - const std::size_t expectedNumInputIos = nclassics; - std::string ioNameIdentsLine; - if (!std::getline(is, ioNameIdentsLine)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed read in '.inputs' line"); - } - - trimCommentAndTrailingWhitespaceData(ioNameIdentsLine); - userDefinedInputIdents = - parseIoNames(line, expectedNumInputIos, ioNameIdentsLine, - userDeclaredVariableIdents); - - if (userDefinedInputIdents.size() != expectedNumInputIos) { - throw QFRException( - "[real parser] l:" + std::to_string(line) + "msg: Expected " + - std::to_string(expectedNumInputIos) + " inputs to be declared!"); - } - } else if (cmd == ".OUTPUTS") { - // .OUTPUTS: specifies output permutation - is >> std::ws; - assertRequiredHeaderComponentsAreDefined( - line, - {numVariablesHeaderComponentPrefix, variablesHeaderComponentPrefix}, - definedHeaderComponents); - - const std::size_t expectedNumOutputIos = nclassics; - std::string ioNameIdentsLine; - if (!std::getline(is, ioNameIdentsLine)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed read in '.outputs' line"); - } - - trimCommentAndTrailingWhitespaceData(ioNameIdentsLine); - userDefinedOutputIdents = - parseIoNames(line, expectedNumOutputIos, ioNameIdentsLine, - userDeclaredVariableIdents); - - if (userDefinedOutputIdents.size() != expectedNumOutputIos) { - throw QFRException( - "[real parser] l:" + std::to_string(line) + "msg: Expected " + - std::to_string(expectedNumOutputIos) + " outputs to be declared!"); - } - - if (userDefinedInputIdents.empty()) { - continue; - } - - for (const auto& [outputIoIdent, outputIoQubit] : - userDefinedOutputIdents) { - - // We assume that a permutation of a given input qubit Q at index i - // is performed in the circuit if an entry in both in the .output - // as well as the .input definition using the same literal is found, - // with the input literal being defined at position i in the .input - // definition. If no such matching is found, we require that the output - // is marked as garbage. - // - // The outputPermutation map will use be structured as shown in the - // documentation - // (https://mqt.readthedocs.io/projects/core/en/latest/mqt_core_ir.html#layout-information) - // with the output qubit being used as the key while the input qubit - // serves as the map entries value. - // - if (userDefinedInputIdents.count(outputIoIdent) == 0) { - // The current implementation requires that the .garbage definition is - // define prior to the .output one. - if (outputQubitsMarkedAsGarbage.count(outputIoQubit) == 0) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: outputs without matching inputs are " - "expected to be marked as garbage"); - } - } else if (const Qubit matchingInputQubitForOutputLiteral = - userDefinedInputIdents.at(outputIoIdent); - matchingInputQubitForOutputLiteral != outputIoQubit && - !logicalQubitIsGarbage(outputIoQubit)) { - // We do not need to check whether a mapping from one input to any - // output exists, since we require that the idents defined in either - // of the .input as well as the .output definition are unique in their - // definition. - // - // Only if the matching entries where defined at different indices - // in their respective IO declaration do we update the existing - // identity mapping for the given output qubit - outputPermutation.insert_or_assign( - outputIoQubit, matchingInputQubitForOutputLiteral); - - // If we have determined a non-identity permutation of an input qubit, - // (i.e. output 2 <- input 1) any existing identity permutation - // of the input qubit will be removed since the previously mapped to - // output (output 1) of the identity permutation must have another - // non-identity permutation defined or must be declared as a garbage - // output. - if (outputPermutation.count(matchingInputQubitForOutputLiteral) > 0 && - outputPermutation[matchingInputQubitForOutputLiteral] == - matchingInputQubitForOutputLiteral) { - outputPermutation.erase(matchingInputQubitForOutputLiteral); - } - } - } - } else if (cmd == ".VERSION" || cmd == ".INPUTBUS" || cmd == ".OUTPUTBUS") { - is.ignore(std::numeric_limits::max(), '\n'); - } else if (cmd == ".DEFINE") { - // TODO: Defines currently not supported - std::cerr << "[WARN] File contains 'define' statement, which is " - "currently not supported and thus simply skipped.\n"; - while (cmd != ".ENDDEFINE") { - is.ignore(std::numeric_limits::max(), '\n'); - is >> cmd; - std::transform(cmd.begin(), cmd.end(), cmd.begin(), - [](const unsigned char c) { - return static_cast(toupper(c)); - }); - } - } else { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Unknown command: " + cmd); - } - } -} - -void qc::QuantumComputation::readRealGateDescriptions(std::istream& is, - int line) { - const std::regex gateRegex = - std::regex("(r[xyz]|i[df]|q|[0a-z](?:[+ip])?)(\\d+)?(?::([-+]?[0-9]+[.]?[" - "0-9]*(?:[eE][-+]?[0-9]+)?))?"); - std::smatch m; - std::string cmd; - - static const std::map IDENTIFIER_MAP{ - {"0", I}, {"id", I}, {"h", H}, {"n", X}, {"c", X}, - {"x", X}, {"y", Y}, {"z", Z}, {"s", S}, {"si", Sdg}, - {"sp", Sdg}, {"s+", Sdg}, {"v", V}, {"vi", Vdg}, {"vp", Vdg}, - {"v+", Vdg}, {"rx", RX}, {"ry", RY}, {"rz", RZ}, {"f", SWAP}, - {"if", SWAP}, {"p", Peres}, {"pi", Peresdg}, {"p+", Peresdg}, {"q", P}}; - - while (!is.eof()) { - if (!static_cast(is >> cmd)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed to read command"); - } - std::transform( - cmd.begin(), cmd.end(), cmd.begin(), - [](const unsigned char c) { return static_cast(tolower(c)); }); - ++line; - - if (cmd.front() == '#') { - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } - - if (cmd == ".end") { - break; - } - - // match gate declaration - if (!std::regex_match(cmd, m, gateRegex)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Unsupported gate detected: " + cmd); - } - - // extract gate information (identifier, #controls, divisor) - OpType gate{}; - if (m.str(1) == "t") { // special treatment of t(offoli) for real format - gate = X; - } else { - auto it = IDENTIFIER_MAP.find(m.str(1)); - if (it == IDENTIFIER_MAP.end()) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Unknown gate identifier: " + m.str(1)); - } - gate = (*it).second; - } - auto ncontrols = - m.str(2).empty() ? 0 : std::stoul(m.str(2), nullptr, 0) - 1; - const fp lambda = m.str(3).empty() ? static_cast(0L) - : static_cast(std::stold(m.str(3))); - - if (gate == V || gate == Vdg || m.str(1) == "c") { - ncontrols = 1; - } else if (gate == Peres || gate == Peresdg) { - ncontrols = 2; - } - - if (ncontrols >= getNqubits()) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Gate acts on " + std::to_string(ncontrols + 1) + - " qubits, but only " + std::to_string(getNqubits()) + - " qubits are available."); - } - - std::string qubits; - if (!getline(is, qubits)) { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Failed read in gate definition"); - } - trimCommentAndTrailingWhitespaceData(qubits); - - // If we cannot determine how many gate lines are to be expected from the - // gate definition (i.e. the gate definition 'c a b' does not define the - // number of gate lines) we assume that the number of whitespaces left of - // the gate type define the number of gate lines. - std::size_t numberOfGateLines = 0; - if (const std::string& stringifiedNumberOfGateLines = m.str(2); - !stringifiedNumberOfGateLines.empty()) { - numberOfGateLines = static_cast( - std::stoul(stringifiedNumberOfGateLines, nullptr, 0)); - } else { - numberOfGateLines = static_cast( - std::count(qubits.cbegin(), qubits.cend(), ' ')); - } - - // Current parser implementation defines number of expected control lines - // (nControl) as nLines (of gate definition) - 1. Controlled swap gate has - // at most two target lines so we define the number of control lines as - // nLines - 2. - if (gate == SWAP) { - if (numberOfGateLines < 2) { - throw QFRException("[real parser] l:" + std::to_string(line) + - "msg: SWAP gate is expected to operate on at least " - "two qubits but only " + - std::to_string(ncontrols) + " were defined"); - } - ncontrols = static_cast(numberOfGateLines - 2); - } - - std::vector controls(ncontrols, Qubit()); - const auto& gateLines = qubits.empty() ? "" : qubits.substr(1); - std::unordered_set validVariableIdentLookup; - - // Use the entries of the creg register map prefixed with 'c_' to determine - // the declared variable idents in the .variable entry - for (const auto& qregNameAndQubitIndexPair : classicalRegisters) { - validVariableIdentLookup.emplace( - qregNameAndQubitIndexPair.first.substr(2)); - } - - // We will ignore the prefix '-' when validating a given gate line ident - auto processedGateLines = parseVariableNames( - line, numberOfGateLines, gateLines, validVariableIdentLookup, "-"); - - std::size_t lineIdx = 0; - // get controls and target - for (std::size_t i = 0; i < ncontrols; ++i) { - std::string_view gateIdent = processedGateLines.at(lineIdx++); - const bool negativeControl = gateIdent.front() == '-'; - if (negativeControl) { - gateIdent = gateIdent.substr(1); - } - - // Since variable qubits can either be data or ancillary qubits our search - // will have to be conducted in both lookups - if (const std::optional controlLineQubit = - getQubitForVariableIdentFromAnyLookup( - std::string(gateIdent), quantumRegisters, ancillaRegisters); - controlLineQubit.has_value()) { - controls[i] = - Control(*controlLineQubit, - negativeControl ? Control::Type::Neg : Control::Type::Pos); - } else { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Matching qubit for control line " + - std::string(gateIdent) + " not found!"); - } - } - - const auto numberOfTargetLines = numberOfGateLines - ncontrols; - std::vector targetLineQubits(numberOfTargetLines, Qubit()); - for (std::size_t i = 0; i < numberOfTargetLines; ++i) { - const auto& targetLineIdent = processedGateLines.at(lineIdx++); - // Since variable qubits can either be data or ancillary qubits our search - // will have to be conducted in both lookups - if (const std::optional targetLineQubit = - getQubitForVariableIdentFromAnyLookup( - targetLineIdent, quantumRegisters, ancillaRegisters); - targetLineQubit.has_value()) { - targetLineQubits[i] = *targetLineQubit; - } else { - throw QFRException("[real parser] l:" + std::to_string(line) + - " msg: Matching qubit for target line " + - targetLineIdent + " not found!"); - } - } - - switch (gate) { - case I: - case H: - case Y: - case Z: - case S: - case Sdg: - case T: - case Tdg: - case V: - case Vdg: - emplace_back( - Controls{controls.cbegin(), controls.cend()}, - targetLineQubits.front(), gate); - break; - case X: - mcx(Controls{controls.cbegin(), controls.cend()}, - targetLineQubits.front()); - break; - case RX: - case RY: - case RZ: - case P: - emplace_back( - Controls{controls.cbegin(), controls.cend()}, - targetLineQubits.front(), gate, std::vector{PI / (lambda)}); - break; - case SWAP: - case iSWAP: - emplace_back( - Controls{controls.cbegin(), controls.cend()}, - Targets{targetLineQubits.cbegin(), targetLineQubits.cend()}, gate); - break; - case Peres: - case Peresdg: { - const auto target1 = controls.back().qubit; - controls.pop_back(); - emplace_back( - Controls{controls.cbegin(), controls.cend()}, target1, - targetLineQubits.front(), gate); - break; - } - default: - std::cerr << "Unsupported operation encountered: " << gate << "!\n"; - break; - } - } -} diff --git a/src/ir/parsers/TFCParser.cpp b/src/ir/parsers/TFCParser.cpp deleted file mode 100644 index ac48713ef..000000000 --- a/src/ir/parsers/TFCParser.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2025 Chair for Design Automation, TUM - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "Definitions.hpp" -#include "ir/QuantumComputation.hpp" -#include "ir/operations/Control.hpp" -#include "ir/operations/OpType.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void qc::QuantumComputation::importTFC(std::istream& is) { - std::map varMap{}; - auto line = readTFCHeader(is, varMap); - readTFCGateDescriptions(is, line, varMap); -} - -int qc::QuantumComputation::readTFCHeader( - std::istream& is, std::map& varMap) { - std::string cmd; - std::string variable; - std::string identifier; - int line = 0; - - const std::string delimiter = ","; - size_t pos{}; - - std::vector variables{}; - std::vector inputs{}; - std::vector outputs{}; - std::vector constants{}; - - while (true) { - if (!static_cast(is >> cmd)) { - throw QFRException("[tfc parser] l:" + std::to_string(line) + - " msg: Invalid file header"); - } - ++line; - - // skip comments - if (cmd.front() == '#') { - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } - - // valid header commands start with '.' or end the header with BEGIN - if (cmd.front() != '.' && cmd != "BEGIN" && cmd != "begin") { - throw QFRException("[tfc parser] l:" + std::to_string(line) + - " msg: Invalid file header"); - } - - // header read complete - if (cmd == "BEGIN" || cmd == "begin") { - break; - } - - if (cmd == ".v") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - variables.emplace_back(variable); - identifier.erase(0, pos + 1); - } - variables.emplace_back(identifier); - } else if (cmd == ".i") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - if (std::find(variables.begin(), variables.end(), variable) != - variables.end()) { - inputs.emplace_back(variable); - } else { - throw QFRException( - "[tfc parser] l:" + std::to_string(line) + - " msg: Unknown variable in input statement: " + cmd); - } - identifier.erase(0, pos + 1); - } - if (std::find(variables.begin(), variables.end(), identifier) != - variables.end()) { - inputs.emplace_back(identifier); - } else { - throw QFRException("[tfc parser] l:" + std::to_string(line) + - " msg: Unknown variable in input statement: " + cmd); - } - } else if (cmd == ".o") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - if (std::find(variables.begin(), variables.end(), variable) != - variables.end()) { - outputs.emplace_back(variable); - } else { - throw QFRException( - "[tfc parser] l:" + std::to_string(line) + - " msg: Unknown variable in output statement: " + cmd); - } - identifier.erase(0, pos + 1); - } - if (std::find(variables.begin(), variables.end(), identifier) != - variables.end()) { - outputs.emplace_back(identifier); - } else { - throw QFRException( - "[tfc parser] l:" + std::to_string(line) + - " msg: Unknown variable in output statement: " + cmd); - } - } else if (cmd == ".c") { - is >> std::ws; - std::getline(is, identifier); - while ((pos = identifier.find(delimiter)) != std::string::npos) { - variable = identifier.substr(0, pos); - constants.emplace_back(variable); - identifier.erase(0, pos + 1); - } - constants.emplace_back(identifier); - } else if (cmd == ".ol") { // ignore output labels - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } else { - throw QFRException("[tfc parser] l:" + std::to_string(line) + - " msg: Unknown command: " + cmd); - } - } - addQubitRegister(inputs.size()); - auto nconstants = variables.size() - inputs.size(); - if (nconstants > 0) { - addAncillaryRegister(nconstants); - } - - auto qidx = 0; - auto constidx = inputs.size(); - for (auto& var : variables) { - // check if variable is input - if (std::count(inputs.begin(), inputs.end(), var) != 0) { - varMap.insert({var, qidx++}); - } else { - if (!constants.empty()) { - if (constants.at(constidx - inputs.size()) == "0" || - constants.at(constidx - inputs.size()) == "1") { - // add X operation in case of initial value 1 - if (constants.at(constidx - inputs.size()) == "1") { - x(static_cast(constidx)); - } - varMap.insert({var, static_cast(constidx++)}); - } else { - throw QFRException("[tfc parser] l:" + std::to_string(line) + - " msg: Non-binary constant specified: " + cmd); - } - } else { - // variable does not occur in input statement --> assumed to be |0> - // ancillary - varMap.insert({var, static_cast(constidx++)}); - } - } - } - - for (size_t q = 0; q < variables.size(); ++q) { - variable = variables.at(q); - auto p = varMap.at(variable); - initialLayout[static_cast(q)] = p; - if (!outputs.empty()) { - if (std::count(outputs.begin(), outputs.end(), variable) != 0) { - outputPermutation[static_cast(q)] = p; - } else { - outputPermutation.erase(static_cast(q)); - garbage.at(p) = true; - } - } else { - // no output statement given --> assume all outputs are relevant - outputPermutation[static_cast(q)] = p; - } - } - - return line; -} - -void qc::QuantumComputation::readTFCGateDescriptions( - std::istream& is, int line, std::map& varMap) { - const std::regex gateRegex = std::regex("([tTfF])(\\d+)"); - std::smatch m; - std::string cmd; - - while (!is.eof()) { - if (!static_cast(is >> cmd)) { - throw QFRException("[tfc parser] l:" + std::to_string(line) + - " msg: Failed to read command"); - } - ++line; - - if (cmd.front() == '#') { - is.ignore(std::numeric_limits::max(), '\n'); - continue; - } - - if (cmd == "END" || cmd == "end") { - break; - } - - // match gate declaration - if (!std::regex_match(cmd, m, gateRegex)) { - throw QFRException("[tfc parser] l:" + std::to_string(line) + - " msg: Unsupported gate detected: " + cmd); - } - - // extract gate information (identifier, #controls, divisor) - OpType gate = SWAP; - // special treatment of t(offoli) for real format - if (m.str(1) == "t" || m.str(1) == "T") { - gate = X; - } - const std::size_t ncontrols = - m.str(2).empty() ? 0 : std::stoul(m.str(2), nullptr, 0) - 1; - - if (ncontrols >= nqubits + nancillae) { - throw QFRException( - "[tfc parser] l:" + std::to_string(line) + " msg: Gate acts on " + - std::to_string(ncontrols + 1) + " qubits, but only " + - std::to_string(nqubits + nancillae) + " qubits are available."); - } - - std::string qubits; - std::string label; - is >> std::ws; - getline(is, qubits); - - std::vector controls{}; - - const std::string delimiter = ","; - size_t pos{}; - - while ((pos = qubits.find(delimiter)) != std::string::npos) { - label = qubits.substr(0, pos); - if (label.back() == '\'') { - label.erase(label.size() - 1); - controls.emplace_back(varMap.at(label), Control::Type::Neg); - } else { - controls.emplace_back(varMap.at(label)); - } - qubits.erase(0, pos + 1); - } - controls.emplace_back(varMap.at(qubits)); - - if (gate == X) { - const Qubit target = controls.back().qubit; - controls.pop_back(); - mcx(Controls{controls.cbegin(), controls.cend()}, target); - } else { - const Qubit target0 = controls.back().qubit; - controls.pop_back(); - const Qubit target1 = controls.back().qubit; - controls.pop_back(); - mcswap(Controls{controls.cbegin(), controls.cend()}, target0, target1); - } - } -} diff --git a/src/ir/parsers/qasm3_parser/Statement.cpp b/src/ir/parsers/qasm3_parser/Statement.cpp deleted file mode 100644 index 457cdf4cf..000000000 --- a/src/ir/parsers/qasm3_parser/Statement.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2025 Chair for Design Automation, TUM - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "ir/parsers/qasm3_parser/Statement.hpp" - -#include "ir/operations/ClassicControlledOperation.hpp" - -#include - -namespace qasm3 { - -std::optional getComparisonKind(BinaryExpression::Op op) { - switch (op) { - case BinaryExpression::Op::LessThan: - return qc::ComparisonKind::Lt; - case BinaryExpression::Op::LessThanOrEqual: - return qc::ComparisonKind::Leq; - case BinaryExpression::Op::GreaterThan: - return qc::ComparisonKind::Gt; - case BinaryExpression::Op::GreaterThanOrEqual: - return qc::ComparisonKind::Geq; - case BinaryExpression::Op::Equal: - return qc::ComparisonKind::Eq; - case BinaryExpression::Op::NotEqual: - return qc::ComparisonKind::Neq; - default: - return std::nullopt; - } -} -} // namespace qasm3 diff --git a/src/mqt/core/__init__.py b/src/mqt/core/__init__.py index 94b477e53..9257e711e 100644 --- a/src/mqt/core/__init__.py +++ b/src/mqt/core/__init__.py @@ -63,9 +63,9 @@ def load(input_circuit: QuantumComputation | str | os.PathLike[str] | QuantumCir msg = f"File {input_circuit} does not exist." raise FileNotFoundError(msg) # otherwise, we assume that this is a QASM string - return QuantumComputation.from_qasm(input_str) + return QuantumComputation.from_qasm_str(input_str) - return QuantumComputation(input_str) + return QuantumComputation.from_qasm(input_str) # At this point, we know that the input is a Qiskit QuantumCircuit from .plugins.qiskit import qiskit_to_mqt diff --git a/src/mqt/core/ir/__init__.pyi b/src/mqt/core/ir/__init__.pyi index 6d9f2f22e..6d05daef4 100644 --- a/src/mqt/core/ir/__init__.pyi +++ b/src/mqt/core/ir/__init__.pyi @@ -102,21 +102,15 @@ class QuantumComputation(MutableSequence[Operation]): Args: nq: The number of qubits in the quantum computation. nc: The number of classical bits in the quantum computation. - filename: The filename of the file to load the quantum computation from. - Supported formats are OpenQASM2, OpenQASM3, Real, GRCS, TFC, QC. + seed: The seed to use for the internal random number generator. """ # -------------------------------------------------------------------------- # Constructors # -------------------------------------------------------------------------- - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, nq: int, nc: int = 0) -> None: ... - @overload - def __init__(self, filename: str | PathLike[str]) -> None: ... + def __init__(self, nq: int = 0, nc: int = 0, seed: int = 0) -> None: ... @staticmethod - def from_qasm(qasm: str) -> QuantumComputation: + def from_qasm_str(qasm: str) -> QuantumComputation: """Create a QuantumComputation object from an OpenQASM string. Args: @@ -125,6 +119,16 @@ class QuantumComputation(MutableSequence[Operation]): Returns: The QuantumComputation object created from the OpenQASM string. """ + @staticmethod + def from_qasm(filename: str) -> QuantumComputation: + """Create a QuantumComputation object from an OpenQASM file. + + Args: + filename: The filename of the OpenQASM file to create the QuantumComputation object from. + + Returns: + The QuantumComputation object created from the OpenQASM file. + """ # -------------------------------------------------------------------------- # General Properties diff --git a/src/python/ir/CMakeLists.txt b/src/python/ir/CMakeLists.txt index a2e056ab7..a516647da 100644 --- a/src/python/ir/CMakeLists.txt +++ b/src/python/ir/CMakeLists.txt @@ -20,7 +20,8 @@ if(NOT TARGET ir) # Source code goes here ${MQT_CORE_INCLUDE_BUILD_DIR}/python/pybind11.hpp ${IR_SOURCES}) - target_link_libraries(ir PRIVATE MQT::CoreIR MQT::ProjectOptions MQT::ProjectWarnings) + target_link_libraries(ir PRIVATE MQT::CoreIR MQT::CoreQASM MQT::ProjectOptions + MQT::ProjectWarnings) # Install directive for scikit-build-core install( diff --git a/src/python/ir/register_quantum_computation.cpp b/src/python/ir/register_quantum_computation.cpp index 97a85a237..243840a46 100644 --- a/src/python/ir/register_quantum_computation.cpp +++ b/src/python/ir/register_quantum_computation.cpp @@ -15,6 +15,7 @@ #include "ir/operations/OpType.hpp" #include "ir/operations/Operation.hpp" #include "python/pybind11.hpp" +#include "qasm3/Importer.hpp" #include #include @@ -45,13 +46,12 @@ void registerQuantumComputation(py::module& m) { ///--------------------------------------------------------------------------- /// \n Constructors \n ///--------------------------------------------------------------------------- + qc.def(py::init(), "nq"_a = 0U, + "nc"_a = 0U, "seed"_a = 0U); - qc.def(py::init<>(), "Constructs an empty QuantumComputation."); - qc.def(py::init(), "nq"_a, "nc"_a = 0U); - qc.def(py::init(), "filename"_a); - - // expose the static constructor from qasm strings - qc.def_static("from_qasm", &qc::QuantumComputation::fromQASM, "qasm"_a); + // expose the static constructor from qasm strings or files + qc.def_static("from_qasm_str", &qasm3::Importer::imports, "qasm"_a); + qc.def_static("from_qasm", &qasm3::Importer::importf, "filename"_a); ///--------------------------------------------------------------------------- /// \n General Properties \n diff --git a/src/qasm3/CMakeLists.txt b/src/qasm3/CMakeLists.txt new file mode 100644 index 000000000..74c9a8046 --- /dev/null +++ b/src/qasm3/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright (c) 2025 Chair for Design Automation, TUM +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +# +# This file is part of the MQT CORE library released under the MIT license. See README.md or go to +# https://github.com/cda-tum/mqt-core for more information. +# + +if(NOT TARGET MQT::CoreQASM) + # collect headers and source files + file(GLOB_RECURSE QASM_HEADERS ${MQT_CORE_INCLUDE_BUILD_DIR}/qasm3/*.hpp) + file(GLOB_RECURSE QASM_SOURCES **.cpp) + + # add OpenQASM Package library + add_library(${MQT_CORE_TARGET_NAME}-qasm ${QASM_HEADERS} ${QASM_SOURCES}) + + # add link libraries + target_link_libraries( + ${MQT_CORE_TARGET_NAME}-qasm + PRIVATE MQT::ProjectOptions MQT::ProjectWarnings + PUBLIC MQT::CoreIR) + + # set include directories + target_include_directories( + ${MQT_CORE_TARGET_NAME}-qasm PUBLIC $ + $) + + # add MQT alias + add_library(MQT::CoreQASM ALIAS ${MQT_CORE_TARGET_NAME}-qasm) + + # set versioning information + set_target_properties( + ${MQT_CORE_TARGET_NAME}-qasm + PROPERTIES VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + EXPORT_NAME CoreQASM) + + # generate export header + include(GenerateExportHeader) + generate_export_header(${MQT_CORE_TARGET_NAME}-qasm BASE_NAME mqt_core_qasm) + if(NOT BUILD_SHARED_LIBS) + target_compile_definitions(${MQT_CORE_TARGET_NAME}-qasm PUBLIC MQT_CORE_QASM_STATIC_DEFINE) + endif() + + # install export header + if(MQT_CORE_INSTALL) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mqt_core_qasm_export.h + DESTINATION ${MQT_CORE_INCLUDE_INSTALL_DIR}) + endif() + + # add to list of MQT core target + set(MQT_CORE_TARGETS + ${MQT_CORE_TARGETS} ${MQT_CORE_TARGET_NAME}-qasm + PARENT_SCOPE) +endif() diff --git a/src/qasm3/Importer.cpp b/src/qasm3/Importer.cpp new file mode 100644 index 000000000..c6dd082a5 --- /dev/null +++ b/src/qasm3/Importer.cpp @@ -0,0 +1,876 @@ +/* + * Copyright (c) 2025 Chair for Design Automation, TUM + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "qasm3/Importer.hpp" + +#include "Definitions.hpp" +#include "ir/QuantumComputation.hpp" +#include "ir/operations/ClassicControlledOperation.hpp" +#include "ir/operations/CompoundOperation.hpp" +#include "ir/operations/Control.hpp" +#include "ir/operations/NonUnitaryOperation.hpp" +#include "ir/operations/OpType.hpp" +#include "ir/operations/Operation.hpp" +#include "ir/operations/StandardOperation.hpp" +#include "qasm3/Exception.hpp" +#include "qasm3/Gate.hpp" +#include "qasm3/Parser.hpp" +#include "qasm3/Statement.hpp" +#include "qasm3/StdGates.hpp" +#include "qasm3/Types.hpp" +#include "qasm3/passes/ConstEvalPass.hpp" +#include "qasm3/passes/TypeCheckPass.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qasm3 { + +using const_eval::ConstEvalPass; +using const_eval::ConstEvalValue; +using type_checking::InferredType; +using type_checking::TypeCheckPass; + +auto Importer::importf(const std::string& filename) -> qc::QuantumComputation { + std::ifstream file(filename); + if (!file.good()) { + throw std::runtime_error("Could not open file " + filename); + } + return import(file); +} + +auto Importer::import(std::istream& is) -> qc::QuantumComputation { + // parse the program into an AST + Parser parser(is); + const auto program = parser.parseProgram(); + // translate the AST into a quantum computation + qc::QuantumComputation qc; + Importer importer(qc); + importer.visitProgram(program); + // initialize the initial layout and output permutation + qc.initializeIOMapping(); + return qc; +} + +auto Importer::imports(const std::string& qasm) -> qc::QuantumComputation { + std::istringstream is(qasm); + return import(is); +} + +std::map> +Importer::initializeBuiltins() { + std::map> builtins{}; + + InferredType const floatTy{std::dynamic_pointer_cast( + std::make_shared>(Float, 64))}; + + builtins.emplace("pi", std::pair{ConstEvalValue(qc::PI), floatTy}); + builtins.emplace("π", std::pair{ConstEvalValue(qc::PI), floatTy}); + builtins.emplace("tau", std::pair{ConstEvalValue(qc::TAU), floatTy}); + builtins.emplace("τ", std::pair{ConstEvalValue(qc::TAU), floatTy}); + builtins.emplace("euler", std::pair{ConstEvalValue(qc::E), floatTy}); + builtins.emplace("ℇ", std::pair{ConstEvalValue(qc::E), floatTy}); + + return builtins; +} + +void Importer::translateGateOperand( + const std::shared_ptr& gateOperand, + std::vector& qubits, const qc::QuantumRegisterMap& qregs, + const std::shared_ptr& debugInfo) { + translateGateOperand(gateOperand->identifier, gateOperand->expression, qubits, + qregs, debugInfo); +} + +void Importer::translateGateOperand( + const std::string& gateIdentifier, + const std::shared_ptr& indexExpr, + std::vector& qubits, const qc::QuantumRegisterMap& qregs, + const std::shared_ptr& debugInfo) { + const auto qubitIter = qregs.find(gateIdentifier); + if (qubitIter == qregs.end()) { + throw CompilerError("Usage of unknown quantum register.", debugInfo); + } + auto qubit = qubitIter->second; + + if (indexExpr != nullptr) { + const auto result = evaluatePositiveConstant(indexExpr, debugInfo); + + if (result >= qubit.getSize()) { + throw CompilerError( + "Index expression must be smaller than the width of the " + "quantum register.", + debugInfo); + } + qubit.getStartIndex() += static_cast(result); + qubit.getSize() = 1; + } + + for (uint64_t i = 0; i < qubit.getSize(); ++i) { + qubits.emplace_back(static_cast(qubit.getStartIndex() + i)); + } +} + +void Importer::translateBitOperand( + const std::string& bitIdentifier, + const std::shared_ptr& indexExpr, std::vector& bits, + const std::shared_ptr& debugInfo) const { + const auto iter = qc->getClassicalRegisters().find(bitIdentifier); + if (iter == qc->getClassicalRegisters().end()) { + throw CompilerError("Usage of unknown classical register.", debugInfo); + } + auto creg = iter->second; + + if (indexExpr != nullptr) { + const auto index = evaluatePositiveConstant(indexExpr, debugInfo); + if (index >= creg.getSize()) { + throw CompilerError( + "Index expression must be smaller than the width of the " + "classical register.", + debugInfo); + } + + creg.getStartIndex() += index; + creg.getSize() = 1; + } + + for (uint64_t i = 0; i < creg.getSize(); ++i) { + bits.emplace_back(creg.getStartIndex() + i); + } +} + +uint64_t +Importer::evaluatePositiveConstant(const std::shared_ptr& expr, + const std::shared_ptr& debugInfo, + const uint64_t defaultValue) { + if (expr == nullptr) { + return defaultValue; + } + + const auto constInt = std::dynamic_pointer_cast(expr); + if (!constInt) { + throw CompilerError("Expected a constant integer expression.", debugInfo); + } + + return constInt->getUInt(); +} + +Importer::Importer(qc::QuantumComputation& quantumComputation) + : typeCheckPass(&constEvalPass), qc(&quantumComputation), + gates(STANDARD_GATES) { + for (const auto& [identifier, builtin] : initializeBuiltins()) { + constEvalPass.addConst(identifier, builtin.first); + typeCheckPass.addBuiltin(identifier, builtin.second); + } +} + +void Importer::visitProgram( + const std::vector>& program) { + // TODO: in the future, don't exit early, but collect all errors + // To do this, we need to insert make sure that erroneous declarations + // actually insert a dummy entry; also, we need to synchronize to the next + // semicolon, to make sure we don't do some weird stuff and report false + // errors. + for (const auto& statement : program) { + constEvalPass.processStatement(*statement); + typeCheckPass.processStatement(*statement); + statement->accept(this); + } + + // Finally, if we have a initial layout and output permutation specified, + // apply them. + if (!initialLayout.empty()) { + qc->initialLayout = initialLayout; + } + if (!outputPermutation.empty()) { + qc->outputPermutation = outputPermutation; + } +} + +void Importer::visitVersionDeclaration( + const std::shared_ptr versionDeclaration) { + if (versionDeclaration->version < 3) { + openQASM2CompatMode = true; + } +} + +void Importer::visitDeclarationStatement( + const std::shared_ptr declarationStatement) { + const auto identifier = declarationStatement->identifier; + if (declarations.find(identifier).has_value()) { + // TODO: show the location of the previous declaration + throw CompilerError("Identifier '" + identifier + "' already declared.", + declarationStatement->debugInfo); + } + + std::shared_ptr const ty = + std::get<1>(declarationStatement->type); + + if (const auto sizedTy = + std::dynamic_pointer_cast>(ty)) { + const auto designator = sizedTy->getDesignator(); + switch (sizedTy->type) { + case Qubit: + qc->addQubitRegister(designator, identifier); + break; + case Bit: + case Int: + case Uint: + qc->addClassicalRegister(designator, identifier); + break; + case Float: + // not adding to qc + break; + case Angle: + throw CompilerError("Angle type is currently not supported.", + declarationStatement->debugInfo); + } + } else { + throw CompilerError("Only sized types are supported.", + declarationStatement->debugInfo); + } + declarations.emplace(identifier, declarationStatement); + + if (declarationStatement->expression == nullptr) { + // value is uninitialized + return; + } + if (const auto measureExpression = + std::dynamic_pointer_cast( + declarationStatement->expression->expression)) { + assert(!declarationStatement->isConst && + "Type check pass should catch this"); + visitMeasureAssignment(identifier, nullptr, measureExpression, + declarationStatement->debugInfo); + return; + } + if (declarationStatement->isConst) { + // nothing to do + return; + } + + throw CompilerError( + "Only measure statements are supported for initialization.", + declarationStatement->debugInfo); +} + +void Importer::visitAssignmentStatement( + const std::shared_ptr assignmentStatement) { + const auto identifier = assignmentStatement->identifier->identifier; + const auto declaration = declarations.find(identifier); + assert(declaration.has_value() && "Checked by type check pass"); + assert(!declaration->get()->isConst && "Checked by type check pass"); + + if (const auto measureExpression = + std::dynamic_pointer_cast( + assignmentStatement->expression->expression)) { + visitMeasureAssignment(identifier, assignmentStatement->indexExpression, + measureExpression, assignmentStatement->debugInfo); + return; + } + + // In the future, handle classical computation. + throw CompilerError("Classical computation not supported.", + assignmentStatement->debugInfo); +} + +void Importer::visitInitialLayout(const std::shared_ptr layout) { + if (!initialLayout.empty()) { + throw CompilerError("Multiple initial layout specifications found.", + layout->debugInfo); + } + initialLayout = layout->permutation; +} + +void Importer::visitOutputPermutation( + const std::shared_ptr permutation) { + if (!outputPermutation.empty()) { + throw CompilerError("Multiple output permutation specifications found.", + permutation->debugInfo); + } + outputPermutation = permutation->permutation; +} + +void Importer::visitGateStatement( + const std::shared_ptr gateStatement) { + auto identifier = gateStatement->identifier; + if (gateStatement->isOpaque) { + if (gates.find(identifier) == gates.end()) { + // only builtin gates may be declared as opaque. + throw CompilerError("Unsupported opaque gate '" + identifier + "'.", + gateStatement->debugInfo); + } + + return; + } + + if (openQASM2CompatMode) { + // we need to check if this is a standard gate + identifier = parseGateIdentifierCompatMode(identifier).first; + } + + if (auto prevDeclaration = gates.find(identifier); + prevDeclaration != gates.end()) { + if (std::dynamic_pointer_cast(prevDeclaration->second)) { + // we ignore redeclarations of standard gates + return; + } + // TODO: print location of previous declaration + throw CompilerError("Gate '" + identifier + "' already declared.", + gateStatement->debugInfo); + } + + const auto parameters = gateStatement->parameters; + const auto qubits = gateStatement->qubits; + + // first we check that all parameters and qubits are unique + std::vector parameterIdentifiers{}; + for (const auto& parameter : parameters->identifiers) { + if (std::find(parameterIdentifiers.begin(), parameterIdentifiers.end(), + parameter->identifier) != parameterIdentifiers.end()) { + throw CompilerError("Parameter '" + parameter->identifier + + "' already declared.", + gateStatement->debugInfo); + } + parameterIdentifiers.emplace_back(parameter->identifier); + } + std::vector qubitIdentifiers{}; + for (const auto& qubit : qubits->identifiers) { + if (std::find(qubitIdentifiers.begin(), qubitIdentifiers.end(), + qubit->identifier) != qubitIdentifiers.end()) { + throw CompilerError("Qubit '" + qubit->identifier + "' already declared.", + gateStatement->debugInfo); + } + qubitIdentifiers.emplace_back(qubit->identifier); + } + + auto compoundGate = std::make_shared(CompoundGate( + parameterIdentifiers, qubitIdentifiers, gateStatement->statements)); + + gates.emplace(identifier, compoundGate); +} + +void Importer::visitGateCallStatement( + const std::shared_ptr gateCallStatement) { + const auto& qregs = qc->getQuantumRegisters(); + if (auto op = evaluateGateCall( + gateCallStatement, gateCallStatement->identifier, + gateCallStatement->arguments, gateCallStatement->operands, qregs); + op != nullptr) { + qc->emplace_back(std::move(op)); + } +} + +std::unique_ptr Importer::evaluateGateCall( + const std::shared_ptr& gateCallStatement, + const std::string& identifier, + const std::vector>& parameters, + std::vector> targets, + const qc::QuantumRegisterMap& qregs) { + auto iter = gates.find(identifier); + std::shared_ptr gate; + size_t implicitControls{0}; + + if (iter == gates.end()) { + if (identifier == "mcx" || identifier == "mcx_gray" || + identifier == "mcx_vchain" || identifier == "mcx_recursive" || + identifier == "mcphase") { + // we create a temp gate definition for these gates + gate = getMcGateDefinition(identifier, gateCallStatement->operands.size(), + gateCallStatement->debugInfo); + } else if (openQASM2CompatMode) { + auto [updatedIdentifier, nControls] = + parseGateIdentifierCompatMode(identifier); + + iter = gates.find(updatedIdentifier); + if (iter == gates.end()) { + throw CompilerError("Usage of unknown gate '" + identifier + "'.", + gateCallStatement->debugInfo); + } + gate = iter->second; + implicitControls = nControls; + } else { + throw CompilerError("Usage of unknown gate '" + identifier + "'.", + gateCallStatement->debugInfo); + } + } else { + gate = iter->second; + } + + if (gate->getNParameters() != parameters.size()) { + throw CompilerError( + "Gate '" + identifier + "' takes " + + std::to_string(gate->getNParameters()) + " parameters, but " + + std::to_string(parameters.size()) + " were supplied.", + gateCallStatement->debugInfo); + } + + // here we count the number of controls + std::vector, bool>> controls{}; + // since standard gates may define a number of control targets, we first + // need to handle those + size_t nControls{gate->getNControls() + implicitControls}; + if (targets.size() < nControls) { + throw CompilerError("Gate '" + identifier + "' takes " + + std::to_string(nControls) + " controls, but only " + + std::to_string(targets.size()) + + " qubits were supplied.", + gateCallStatement->debugInfo); + } + + controls.reserve(nControls); + for (size_t i = 0; i < nControls; ++i) { + controls.emplace_back(targets[i], true); + } + + bool invertOperation = false; + for (const auto& modifier : gateCallStatement->modifiers) { + if (auto ctrlModifier = + std::dynamic_pointer_cast(modifier); + ctrlModifier != nullptr) { + size_t const n = evaluatePositiveConstant(ctrlModifier->expression, + gateCallStatement->debugInfo, + /*defaultValue=*/ + 1); + if (targets.size() < n + nControls) { + throw CompilerError( + "Gate '" + identifier + "' takes " + std::to_string(n + nControls) + + " controls, but only " + std::to_string(targets.size()) + + " were supplied.", + gateCallStatement->debugInfo); + } + + for (size_t i = 0; i < n; ++i) { + controls.emplace_back(targets[nControls + i], ctrlModifier->ctrlType); + } + nControls += n; + } else if (auto invModifier = + std::dynamic_pointer_cast(modifier); + invModifier != nullptr) { + // if we have an even number of inv modifiers, they cancel each other + // out + invertOperation = !invertOperation; + } else { + throw CompilerError("Only ctrl/negctrl/inv modifiers are supported.", + gateCallStatement->debugInfo); + } + } + targets.erase(targets.begin(), + targets.begin() + static_cast(nControls)); + + if (gate->getNTargets() != targets.size()) { + throw CompilerError("Gate '" + identifier + "' takes " + + std::to_string(gate->getNTargets()) + + " targets, but " + std::to_string(targets.size()) + + " were supplied.", + gateCallStatement->debugInfo); + } + + // now evaluate all arguments; we only support const arguments. + std::vector evaluatedParameters{}; + for (const auto& param : parameters) { + auto result = constEvalPass.visit(param); + if (!result.has_value()) { + throw CompilerError( + "Only const expressions are supported as gate parameters, but " + "found '" + + param->getName() + "'.", + gateCallStatement->debugInfo); + } + + evaluatedParameters.emplace_back(result->toExpr()->asFP()); + } + + size_t broadcastingWidth{1}; + qc::Targets targetBits{}; + std::vector targetBroadcastingIndices{}; + size_t i{0}; + for (const auto& target : targets) { + qc::Targets t{}; + translateGateOperand(target, t, qregs, gateCallStatement->debugInfo); + + targetBits.emplace_back(t[0]); + + if (t.size() > 1) { + if (broadcastingWidth != 1 && t.size() != broadcastingWidth) { + throw CompilerError( + "When broadcasting, all registers must be of the same width.", + gateCallStatement->debugInfo); + } + broadcastingWidth = t.size(); + + targetBroadcastingIndices.emplace_back(i); + } + + i++; + } + + std::vector controlBits{}; + std::vector controlBroadcastingIndices{}; + i = 0; + for (const auto& [control, type] : controls) { + qc::Targets c{}; + translateGateOperand(control, c, qregs, gateCallStatement->debugInfo); + + controlBits.emplace_back(c[0], type ? qc::Control::Type::Pos + : qc::Control::Type::Neg); + + if (c.size() > 1) { + if (broadcastingWidth != 1 && c.size() != broadcastingWidth) { + throw CompilerError( + "When broadcasting, all registers must be of the same width.", + gateCallStatement->debugInfo); + } + broadcastingWidth = c.size(); + + controlBroadcastingIndices.emplace_back(i); + } + + i++; + } + + // check if any of the bits are duplicate + std::unordered_set allQubits; + for (const auto& control : controlBits) { + if (allQubits.find(control.qubit) != allQubits.end()) { + throw CompilerError("Duplicate qubit in control list.", + gateCallStatement->debugInfo); + } + allQubits.emplace(control.qubit); + } + for (const auto& qubit : targetBits) { + if (allQubits.find(qubit) != allQubits.end()) { + throw CompilerError("Duplicate qubit in target list.", + gateCallStatement->debugInfo); + } + allQubits.emplace(qubit); + } + + if (broadcastingWidth == 1) { + return applyQuantumOperation(gate, targetBits, controlBits, + evaluatedParameters, invertOperation, + gateCallStatement->debugInfo); + } + + // if we are broadcasting, we need to create a compound operation + auto op = std::make_unique(); + for (size_t j = 0; j < broadcastingWidth; ++j) { + // first we apply the operation + auto nestedOp = applyQuantumOperation(gate, targetBits, controlBits, + evaluatedParameters, invertOperation, + gateCallStatement->debugInfo); + if (nestedOp == nullptr) { + return nullptr; + } + op->getOps().emplace_back(std::move(nestedOp)); + + // after applying the operation, we update the broadcast bits + for (auto index : targetBroadcastingIndices) { + targetBits[index] = qc::Qubit{targetBits[index] + 1}; + } + for (auto index : controlBroadcastingIndices) { + controlBits[index].qubit = qc::Qubit{controlBits[index].qubit + 1}; + } + } + return op; +} + +std::shared_ptr +Importer::getMcGateDefinition(const std::string& identifier, size_t operandSize, + const std::shared_ptr& debugInfo) { + std::vector targetParams{}; + std::vector> operands; + size_t nTargets = operandSize; + if (identifier == "mcx_vchain") { + nTargets -= (nTargets + 1) / 2 - 2; + } else if (identifier == "mcx_recursive" && nTargets > 5) { + nTargets -= 1; + } + for (size_t i = 0; i < operandSize; ++i) { + targetParams.emplace_back("q" + std::to_string(i)); + if (i < nTargets) { + operands.emplace_back( + std::make_shared("q" + std::to_string(i), nullptr)); + } + } + const size_t nControls = nTargets - 1; + + std::string nestedGateIdentifier = "x"; + std::vector> nestedParameters{}; + std::vector nestedParameterNames{}; + if (identifier == "mcphase") { + nestedGateIdentifier = "p"; + nestedParameters.emplace_back(std::make_shared("x")); + nestedParameterNames.emplace_back("x"); + } + + // ctrl(nTargets - 1) @ x q0, ..., q(nTargets - 1) + const auto gateCall = GateCallStatement( + debugInfo, nestedGateIdentifier, + std::vector>{ + std::make_shared( + true, std::make_shared(nControls, false))}, + nestedParameters, operands); + const auto inner = std::make_shared(gateCall); + + const CompoundGate g{nestedParameterNames, targetParams, {inner}}; + return std::make_shared(g); +} + +std::unique_ptr Importer::applyQuantumOperation( + const std::shared_ptr& gate, const qc::Targets& targetBits, + const std::vector& controlBits, + const std::vector& evaluatedParameters, const bool invertOperation, + const std::shared_ptr& debugInfo) { + if (auto* standardGate = dynamic_cast(gate.get())) { + auto op = std::make_unique( + qc::Controls{}, targetBits, standardGate->info.type, + evaluatedParameters); + if (invertOperation) { + op->invert(); + } + op->setControls(qc::Controls{controlBits.begin(), controlBits.end()}); + return op; + } + if (auto* compoundGate = dynamic_cast(gate.get())) { + constEvalPass.pushEnv(); + + for (size_t i = 0; i < compoundGate->parameterNames.size(); ++i) { + constEvalPass.addConst(compoundGate->parameterNames[i], + evaluatedParameters[i]); + } + + auto nestedQubits = qc::QuantumRegisterMap{}; + size_t index = 0; + for (const auto& qubitIdentifier : compoundGate->targetNames) { + nestedQubits.try_emplace(qubitIdentifier, targetBits[index], 1, + qubitIdentifier); + index++; + } + + auto op = std::make_unique(true); + for (const auto& nestedGate : compoundGate->body) { + if (auto barrierStatement = + std::dynamic_pointer_cast(nestedGate); + barrierStatement != nullptr) { + std::vector qubits{}; + for (const auto& g : barrierStatement->gates) { + translateGateOperand(g, qubits, nestedQubits, + barrierStatement->debugInfo); + } + op->emplace_back(qubits, qc::Barrier); + } else if (auto resetStatement = + std::dynamic_pointer_cast(nestedGate); + resetStatement != nullptr) { + std::vector qubits{}; + translateGateOperand(resetStatement->gate, qubits, nestedQubits, + resetStatement->debugInfo); + op->emplace_back(qubits, qc::Reset); + } else if (auto gateCallStatement = + std::dynamic_pointer_cast(nestedGate); + gateCallStatement != nullptr) { + for (const auto& operand : gateCallStatement->operands) { + // OpenQASM 3.0 doesn't support indexing of gate arguments. + if (operand->expression != nullptr && + std::find(compoundGate->targetNames.begin(), + compoundGate->targetNames.end(), operand->identifier) != + compoundGate->targetNames.end()) { + throw CompilerError( + "Gate arguments cannot be indexed within gate body.", + debugInfo); + } + } + + auto nestedOp = + evaluateGateCall(gateCallStatement, gateCallStatement->identifier, + gateCallStatement->arguments, + gateCallStatement->operands, nestedQubits); + if (nestedOp == nullptr) { + return nullptr; + } + op->getOps().emplace_back(std::move(nestedOp)); + } else { + throw CompilerError("Unhandled quantum statement.", debugInfo); + } + } + op->setControls(qc::Controls{controlBits.begin(), controlBits.end()}); + if (invertOperation) { + op->invert(); + } + + constEvalPass.popEnv(); + + if (op->getOps().size() == 1) { + return std::move(op->getOps()[0]); + } + + return op; + } + + throw CompilerError("Unknown gate type.", debugInfo); +} + +void Importer::visitMeasureAssignment( + const std::string& identifier, + const std::shared_ptr& indexExpression, + const std::shared_ptr& measureExpression, + const std::shared_ptr& debugInfo) { + const auto decl = declarations.find(identifier); + if (!decl.has_value()) { + throw CompilerError("Usage of unknown identifier '" + identifier + "'.", + debugInfo); + } + + if (!std::get<1>(decl.value()->type)->isBit()) { + throw CompilerError( + "Measure expression can only be assigned to a bit register.", + debugInfo); + } + + std::vector qubits{}; + std::vector bits{}; + translateGateOperand(measureExpression->gate, qubits, + qc->getQuantumRegisters(), debugInfo); + translateBitOperand(identifier, indexExpression, bits, debugInfo); + + if (qubits.size() != bits.size()) { + throw CompilerError( + "Classical and quantum register must have the same width in " + "measure statement. Classical register '" + + identifier + "' has " + std::to_string(bits.size()) + + " bits, but quantum register '" + + measureExpression->gate->identifier + "' has " + + std::to_string(qubits.size()) + " qubits.", + debugInfo); + } + + qc->measure(qubits, bits); +} + +void Importer::visitBarrierStatement( + const std::shared_ptr barrierStatement) { + std::vector qubits{}; + for (const auto& gate : barrierStatement->gates) { + translateGateOperand(gate, qubits, qc->getQuantumRegisters(), + barrierStatement->debugInfo); + } + qc->barrier(qubits); +} + +void Importer::visitResetStatement( + const std::shared_ptr resetStatement) { + std::vector qubits{}; + translateGateOperand(resetStatement->gate, qubits, qc->getQuantumRegisters(), + resetStatement->debugInfo); + qc->reset(qubits); +} + +void Importer::visitIfStatement( + const std::shared_ptr ifStatement) { + // TODO: for now we only support statements comparing a classical bit reg + // to a constant. + const auto condition = + std::dynamic_pointer_cast(ifStatement->condition); + if (condition == nullptr) { + throw CompilerError("Condition not supported for if statement.", + ifStatement->debugInfo); + } + + const auto comparisonKind = getComparisonKind(condition->op); + if (!comparisonKind) { + throw CompilerError("Unsupported comparison operator.", + ifStatement->debugInfo); + } + + const auto lhs = + std::dynamic_pointer_cast(condition->lhs); + const auto rhs = std::dynamic_pointer_cast(condition->rhs); + + if (lhs == nullptr) { + throw CompilerError("Only classical registers are supported in conditions.", + ifStatement->debugInfo); + } + if (rhs == nullptr) { + throw CompilerError("Can only compare to constants.", + ifStatement->debugInfo); + } + + const auto creg = qc->getClassicalRegisters().find(lhs->identifier); + if (creg == qc->getClassicalRegisters().end()) { + throw CompilerError("Usage of unknown or invalid identifier '" + + lhs->identifier + "' in condition.", + ifStatement->debugInfo); + } + + // translate statements in then/else blocks + if (!ifStatement->thenStatements.empty()) { + auto thenOps = translateBlockOperations(ifStatement->thenStatements); + qc->emplace_back( + std::move(thenOps), creg->second, rhs->getUInt(), *comparisonKind); + } + + if (!ifStatement->elseStatements.empty()) { + const auto invertedComparisonKind = + qc::getInvertedComparisonKind(*comparisonKind); + auto elseOps = translateBlockOperations(ifStatement->elseStatements); + qc->emplace_back( + std::move(elseOps), creg->second, rhs->getUInt(), + invertedComparisonKind); + } +} + +std::unique_ptr Importer::translateBlockOperations( + const std::vector>& statements) { + auto blockOps = std::make_unique(); + for (const auto& statement : statements) { + auto gateCall = std::dynamic_pointer_cast(statement); + if (gateCall == nullptr) { + throw CompilerError("Only quantum statements are supported in blocks.", + statement->debugInfo); + } + const auto& qregs = qc->getQuantumRegisters(); + + auto op = evaluateGateCall(gateCall, gateCall->identifier, + gateCall->arguments, gateCall->operands, qregs); + + blockOps->emplace_back(std::move(op)); + } + + return blockOps; +} + +std::pair +Importer::parseGateIdentifierCompatMode(const std::string& identifier) { + // we need to copy as we modify the string and need to return the original + // string if we don't find a match. + std::string gateIdentifier = identifier; + size_t implicitControls = 0; + while (!gateIdentifier.empty() && gateIdentifier[0] == 'c') { + gateIdentifier = gateIdentifier.substr(1); + implicitControls++; + } + + if (gates.find(gateIdentifier) == gates.end()) { + return std::pair{identifier, 0}; + } + return std::pair{gateIdentifier, implicitControls}; +} +} // namespace qasm3 diff --git a/src/ir/parsers/qasm3_parser/Parser.cpp b/src/qasm3/Parser.cpp similarity index 92% rename from src/ir/parsers/qasm3_parser/Parser.cpp rename to src/qasm3/Parser.cpp index d26e5602b..3a74bd8bd 100644 --- a/src/ir/parsers/qasm3_parser/Parser.cpp +++ b/src/qasm3/Parser.cpp @@ -7,18 +7,20 @@ * Licensed under the MIT License */ -#include "ir/parsers/qasm3_parser/Parser.hpp" +#include "qasm3/Parser.hpp" #include "Definitions.hpp" #include "ir/Permutation.hpp" -#include "ir/parsers/qasm3_parser/Statement.hpp" -#include "ir/parsers/qasm3_parser/StdGates.hpp" -#include "ir/parsers/qasm3_parser/Token.hpp" -#include "ir/parsers/qasm3_parser/Types.hpp" +#include "qasm3/Exception.hpp" +#include "qasm3/Statement.hpp" +#include "qasm3/StdGates.hpp" +#include "qasm3/Token.hpp" +#include "qasm3/Types.hpp" #include #include #include +#include #include #include #include @@ -40,6 +42,72 @@ void Parser::scan() { } } +std::shared_ptr Parser::makeDebugInfo(Token const& begin, + Token const& /*end*/) { + // Parameter `end` is currently not used. + return std::make_shared(begin.line, begin.col, + scanner.top().filename.value_or(""), + includeDebugInfo); +} + +std::shared_ptr Parser::makeDebugInfo(Token const& token) { + return std::make_shared(token.line, token.col, + scanner.top().filename.value_or(""), + includeDebugInfo); +} + +void Parser::error(const Token& token, const std::string& msg) { + throw CompilerError(msg, makeDebugInfo(token)); +} + +Token Parser::last() const { + if (scanner.empty()) { + throw std::runtime_error("No scanner available"); + } + return scanner.top().last; +} + +Token Parser::current() const { + if (scanner.empty()) { + throw std::runtime_error("No scanner available"); + } + return scanner.top().t; +} + +Token Parser::peek() const { + if (scanner.empty()) { + throw std::runtime_error("No scanner available"); + } + return scanner.top().next; +} + +Token Parser::expect(const Token::Kind& expected, + const std::optional& context) { + if (current().kind != expected) { + std::string message = "Expected '" + Token::kindToString(expected) + + "', got '" + Token::kindToString(current().kind) + + "'."; + if (context.has_value()) { + message += " " + context.value(); + } + error(current(), message); + } + + auto token = current(); + scan(); + return token; +} + +Parser::Parser(std::istream& is, const bool implicitlyIncludeStdgates) { + scanner.emplace(&is); + scan(); + if (implicitlyIncludeStdgates) { + scanner.emplace(std::make_unique(STDGATES), + "stdgates.inc", true); + scan(); + } +} + std::shared_ptr Parser::parseVersionDeclaration() { auto const tBegin = expect(Token::Kind::OpenQasm); Token const versionToken = expect(Token::Kind::FloatLiteral); diff --git a/src/ir/parsers/qasm3_parser/Scanner.cpp b/src/qasm3/Scanner.cpp similarity index 94% rename from src/ir/parsers/qasm3_parser/Scanner.cpp rename to src/qasm3/Scanner.cpp index ef71c4774..92106dc98 100644 --- a/src/ir/parsers/qasm3_parser/Scanner.cpp +++ b/src/qasm3/Scanner.cpp @@ -7,13 +7,15 @@ * Licensed under the MIT License */ -#include "ir/parsers/qasm3_parser/Scanner.hpp" +#include "qasm3/Scanner.hpp" #include "Definitions.hpp" -#include "ir/parsers/qasm3_parser/Token.hpp" +#include "qasm3/Token.hpp" #include +#include #include +#include #include #include #include @@ -121,6 +123,19 @@ Token Scanner::consumeName() { return t; } +void Scanner::error(const std::string& msg) const { + std::cerr << "Error at line " << line << ", column " << col << ": " << msg + << '\n'; +} + +void Scanner::expect(const char expected) { + if (ch != expected) { + error("Expected '" + std::to_string(expected) + "', got '" + ch + "'"); + } else { + nextCh(); + } +} + bool Scanner::isValidDigit(const uint8_t base, const char c) { if (base == 2) { return c == '0' || c == '1'; @@ -635,6 +650,20 @@ Token Scanner::next() { return t; } +bool Scanner::isSpace(const char c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +bool Scanner::isFirstIdChar(const char c) { + return isalpha(c) != 0 || c == '_'; +} + +bool Scanner::isNum(const char c) { return c >= '0' && c <= '9'; } + +bool Scanner::isHex(const char c) { + return isNum(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + bool Scanner::hasTimingSuffix(const char first, const char second) { if (first == 's') { return true; diff --git a/src/qasm3/Statement.cpp b/src/qasm3/Statement.cpp new file mode 100644 index 000000000..e65f60f47 --- /dev/null +++ b/src/qasm3/Statement.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 Chair for Design Automation, TUM + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "qasm3/Statement.hpp" + +#include "ir/operations/ClassicControlledOperation.hpp" +#include "qasm3/InstVisitor.hpp" + +#include +#include +#include +#include +#include +#include + +namespace qasm3 { + +std::optional +getComparisonKind(const BinaryExpression::Op op) { + switch (op) { + case BinaryExpression::Op::LessThan: + return qc::ComparisonKind::Lt; + case BinaryExpression::Op::LessThanOrEqual: + return qc::ComparisonKind::Leq; + case BinaryExpression::Op::GreaterThan: + return qc::ComparisonKind::Gt; + case BinaryExpression::Op::GreaterThanOrEqual: + return qc::ComparisonKind::Geq; + case BinaryExpression::Op::Equal: + return qc::ComparisonKind::Eq; + case BinaryExpression::Op::NotEqual: + return qc::ComparisonKind::Neq; + default: + return std::nullopt; + } +} +void OutputPermutation::accept(InstVisitor* visitor) { + visitor->visitOutputPermutation(shared_from_this()); +} +void DeclarationStatement::accept(InstVisitor* visitor) { + visitor->visitDeclarationStatement(shared_from_this()); +} +void GateCallStatement::accept(InstVisitor* visitor) { + visitor->visitGateCallStatement(shared_from_this()); +} +void AssignmentStatement::accept(InstVisitor* visitor) { + visitor->visitAssignmentStatement(shared_from_this()); +} +void BarrierStatement::accept(InstVisitor* visitor) { + visitor->visitBarrierStatement(shared_from_this()); +} +void ResetStatement::accept(InstVisitor* visitor) { + visitor->visitResetStatement(shared_from_this()); +} +void IfStatement::accept(InstVisitor* visitor) { + visitor->visitIfStatement(shared_from_this()); +} + +GateDeclaration::GateDeclaration( + std::shared_ptr debug, std::string id, + std::shared_ptr params, + std::shared_ptr qbits, + std::vector> stmts, const bool opaque) + : Statement(std::move(debug)), identifier(std::move(id)), + parameters(std::move(params)), qubits(std::move(qbits)), + statements(std::move(stmts)), isOpaque(opaque) { + if (opaque) { + assert(statements.empty() && "Opaque gate should not have statements."); + } +} + +void GateDeclaration::accept(InstVisitor* visitor) { + visitor->visitGateStatement(shared_from_this()); +} +void VersionDeclaration::accept(InstVisitor* visitor) { + visitor->visitVersionDeclaration(shared_from_this()); +} +void InitialLayout::accept(InstVisitor* visitor) { + visitor->visitInitialLayout(shared_from_this()); +} +} // namespace qasm3 diff --git a/src/qasm3/Token.cpp b/src/qasm3/Token.cpp new file mode 100644 index 000000000..95a7e6946 --- /dev/null +++ b/src/qasm3/Token.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2025 Chair for Design Automation, TUM + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "qasm3/Token.hpp" + +#include +#include +#include +#include + +namespace qasm3 { + +std::string Token::kindToString(const Kind kind) { + // Print a token kind string representation. + // This is the representation used in the error messages. + switch (kind) { + case Kind::None: + return "None"; + case Kind::OpenQasm: + return "OPENQASM"; + case Kind::Include: + return "include"; + case Kind::DefCalGrammar: + return "DefCalGrammar"; + case Kind::Def: + return "Def"; + case Kind::Cal: + return "Cal"; + case Kind::DefCal: + return "DefCal"; + case Kind::Gate: + return "gate"; + case Kind::Opaque: + return "opaque"; + case Kind::Extern: + return "extern"; + case Kind::Box: + return "box"; + case Kind::Let: + return "let"; + case Kind::Break: + return "break"; + case Kind::Continue: + return "continue"; + case Kind::If: + return "if"; + case Kind::Else: + return "else"; + case Kind::End: + return "end"; + case Kind::Return: + return "return"; + case Kind::For: + return "for"; + case Kind::While: + return "while"; + case Kind::In: + return "in"; + case Kind::Pragma: + return "pragma"; + case Kind::Input: + return "input"; + case Kind::Output: + return "output"; + case Kind::Const: + return "const"; + case Kind::ReadOnly: + return "readOnly"; + case Kind::Mutable: + return "mutable"; + case Kind::Qreg: + return "qreg"; + case Kind::Qubit: + return "qubit"; + case Kind::CReg: + return "cReg"; + case Kind::Bool: + return "bool"; + case Kind::Bit: + return "bit"; + case Kind::Int: + return "int"; + case Kind::Uint: + return "uint"; + case Kind::Float: + return "float"; + case Kind::Angle: + return "angle"; + case Kind::Complex: + return "complex"; + case Kind::Array: + return "array"; + case Kind::Void: + return "void"; + case Kind::Duration: + return "duration"; + case Kind::Stretch: + return "stretch"; + case Kind::Gphase: + return "gphase"; + case Kind::Inv: + return "inv"; + case Kind::Pow: + return "pow"; + case Kind::Ctrl: + return "ctrl"; + case Kind::NegCtrl: + return "negCtrl"; + case Kind::Dim: + return "#dim"; + case Kind::DurationOf: + return "durationof"; + case Kind::Delay: + return "delay"; + case Kind::Reset: + return "reset"; + case Kind::Measure: + return "measure"; + case Kind::Barrier: + return "barrier"; + case Kind::True: + return "true"; + case Kind::False: + return "false"; + case Kind::LBracket: + return "["; + case Kind::RBracket: + return "]"; + case Kind::LBrace: + return "{"; + case Kind::RBrace: + return "}"; + case Kind::LParen: + return "("; + case Kind::RParen: + return ")"; + case Kind::Colon: + return ":"; + case Kind::Semicolon: + return ";"; + case Kind::Eof: + return "Eof"; + case Kind::Dot: + return "."; + case Kind::Comma: + return ","; + case Kind::Equals: + return "="; + case Kind::Arrow: + return "->"; + case Kind::Plus: + return "+"; + case Kind::DoublePlus: + return "++"; + case Kind::Minus: + return "-"; + case Kind::Asterisk: + return "*"; + case Kind::DoubleAsterisk: + return "**"; + case Kind::Slash: + return "/"; + case Kind::Percent: + return "%"; + case Kind::Pipe: + return "|"; + case Kind::DoublePipe: + return "||"; + case Kind::Ampersand: + return "&"; + case Kind::DoubleAmpersand: + return "&&"; + case Kind::Caret: + return "^"; + case Kind::At: + return "@"; + case Kind::Tilde: + return "~"; + case Kind::ExclamationPoint: + return "!"; + case Kind::DoubleEquals: + return "=="; + case Kind::NotEquals: + return "!="; + case Kind::PlusEquals: + return "+="; + case Kind::MinusEquals: + return "-="; + case Kind::AsteriskEquals: + return "*="; + case Kind::SlashEquals: + return "/="; + case Kind::AmpersandEquals: + return "&="; + case Kind::PipeEquals: + return "|="; + case Kind::TildeEquals: + return "~="; + case Kind::CaretEquals: + return "^="; + case Kind::LeftShitEquals: + return "<<="; + case Kind::RightShiftEquals: + return ">>="; + case Kind::PercentEquals: + return "%="; + case Kind::DoubleAsteriskEquals: + return "**="; + case Kind::LessThan: + return "<"; + case Kind::LessThanEquals: + return "<="; + case Kind::GreaterThan: + return ">"; + case Kind::GreaterThanEquals: + return ">="; + case Kind::LeftShift: + return "<<"; + case Kind::RightShift: + return ">>"; + case Kind::Imag: + return "imag"; + case Kind::Underscore: + return "underscore"; + case Kind::DoubleQuote: + return "\""; + case Kind::SingleQuote: + return "'"; + case Kind::BackSlash: + return "\\"; + case Kind::Identifier: + return "Identifier"; + case Kind::HardwareQubit: + return "HardwareQubit"; + case Kind::StringLiteral: + return "StringLiteral"; + case Kind::IntegerLiteral: + return "IntegerLiteral"; + case Kind::FloatLiteral: + return "FloatLiteral"; + case Kind::TimingLiteral: + return "TimingLiteral"; + case Kind::Sin: + return "sin"; + case Kind::Cos: + return "cos"; + case Kind::Tan: + return "tan"; + case Kind::Exp: + return "exp"; + case Kind::Ln: + return "ln"; + case Kind::Sqrt: + return "sqrt"; + case Kind::InitialLayout: + return "InitialLayout"; + case Kind::OutputPermutation: + return "OutputPermutation"; + + default: + // This cannot happen, as we have a case for every enum value. + // The default case is only here to silence compiler warnings. + throw std::runtime_error("Unknown token kind"); + } +} + +std::string Token::toString() const { + std::stringstream ss; + ss << kindToString(kind); + switch (kind) { + case Kind::Identifier: + ss << " (" << str << ")"; + break; + case Kind::StringLiteral: + ss << " (\"" << str << "\")"; + break; + case Kind::InitialLayout: + case Kind::OutputPermutation: + ss << " (" << str << ")"; + break; + case Kind::IntegerLiteral: + ss << " (" << val << ")"; + break; + case Kind::FloatLiteral: + ss << " (" << valReal << ")"; + break; + case Kind::TimingLiteral: + ss << " (" << valReal << " [s])"; + break; + default: + break; + } + return ss.str(); +} + +std::ostream& operator<<(std::ostream& os, const Token::Kind& k) { + os << Token::kindToString(k); + return os; +} + +std::ostream& operator<<(std::ostream& os, const Token& t) { + os << t.toString(); + return os; +} +} // namespace qasm3 diff --git a/src/ir/parsers/qasm3_parser/Types.cpp b/src/qasm3/Types.cpp similarity index 97% rename from src/ir/parsers/qasm3_parser/Types.cpp rename to src/qasm3/Types.cpp index 0c8987571..206ab3aba 100644 --- a/src/ir/parsers/qasm3_parser/Types.cpp +++ b/src/qasm3/Types.cpp @@ -7,7 +7,7 @@ * Licensed under the MIT License */ -#include "ir/parsers/qasm3_parser/Types.hpp" +#include "qasm3/Types.hpp" #include #include diff --git a/src/ir/parsers/qasm3_parser/passes/ConstEvalPass.cpp b/src/qasm3/passes/ConstEvalPass.cpp similarity index 90% rename from src/ir/parsers/qasm3_parser/passes/ConstEvalPass.cpp rename to src/qasm3/passes/ConstEvalPass.cpp index e6f451266..ff30f1d53 100644 --- a/src/ir/parsers/qasm3_parser/passes/ConstEvalPass.cpp +++ b/src/qasm3/passes/ConstEvalPass.cpp @@ -7,11 +7,12 @@ * Licensed under the MIT License */ -#include "ir/parsers/qasm3_parser/passes/ConstEvalPass.hpp" +#include "qasm3/passes/ConstEvalPass.hpp" -#include "ir/parsers/qasm3_parser/Exception.hpp" -#include "ir/parsers/qasm3_parser/Statement.hpp" -#include "ir/parsers/qasm3_parser/Types.hpp" +#include "Definitions.hpp" +#include "qasm3/Exception.hpp" +#include "qasm3/Statement.hpp" +#include "qasm3/Types.hpp" #include #include @@ -42,6 +43,14 @@ template T power(T base, T exponent) { } } // namespace +void ConstEvalPass::processStatement(Statement& statement) { + try { + statement.accept(this); + } catch (const ConstEvalError& e) { + throw CompilerError(e.what(), statement.debugInfo); + } +} + void ConstEvalPass::visitDeclarationStatement( const std::shared_ptr declarationStatement) { // The type designator expression is already resolved by the type check pass. @@ -95,6 +104,40 @@ void ConstEvalPass::visitGateCallStatement( } } +std::shared_ptr ConstEvalValue::toExpr() const { + switch (type) { + case ConstInt: + return std::make_shared(Constant(std::get<0>(value), true)); + case ConstUint: + return std::make_shared(Constant(std::get<0>(value), false)); + case ConstFloat: + return std::make_shared(Constant(std::get<1>(value))); + case ConstBool: + return std::make_shared(Constant(std::get<2>(value))); + default: + qc::unreachable(); + } +} + +bool ConstEvalValue::operator==(const ConstEvalValue& rhs) const { + if (type != rhs.type) { + return false; + } + + switch (type) { + case ConstInt: + case ConstUint: + return std::get<0>(value) == std::get<0>(rhs.value); + case ConstFloat: + return std::abs(std::get<1>(value) - std::get<1>(rhs.value)) < + std::numeric_limits::epsilon() * 1024; + case ConstBool: + return std::get<2>(value) == std::get<2>(rhs.value); + } + + return false; +} + std::string ConstEvalValue::toString() const { std::stringstream ss{}; switch (type) { @@ -115,9 +158,13 @@ std::string ConstEvalValue::toString() const { return ss.str(); } -ConstEvalValue ConstEvalPass::evalIntExpression(BinaryExpression::Op op, - int64_t lhs, int64_t rhs, - size_t width, bool isSigned) { +namespace { +template int64_t castToWidth(const int64_t value) { + return static_cast(static_cast(value)); +} + +ConstEvalValue evalIntExpression(BinaryExpression::Op op, int64_t lhs, + int64_t rhs, size_t width, bool isSigned) { auto lhsU = static_cast(lhs); auto rhsU = static_cast(rhs); ConstEvalValue result{0, isSigned, width}; @@ -260,8 +307,8 @@ ConstEvalValue ConstEvalPass::evalIntExpression(BinaryExpression::Op op, return result; } -ConstEvalValue ConstEvalPass::evalFloatExpression(BinaryExpression::Op op, - double lhs, double rhs) { +ConstEvalValue evalFloatExpression(const BinaryExpression::Op op, + const double lhs, const double rhs) { ConstEvalValue result{0.0}; switch (op) { @@ -314,9 +361,9 @@ ConstEvalValue ConstEvalPass::evalFloatExpression(BinaryExpression::Op op, return result; } -ConstEvalValue ConstEvalPass::evalBoolExpression(const BinaryExpression::Op op, - const bool lhs, - const bool rhs) { + +ConstEvalValue evalBoolExpression(const BinaryExpression::Op op, const bool lhs, + const bool rhs) { ConstEvalValue result{false}; switch (op) { @@ -347,6 +394,7 @@ ConstEvalValue ConstEvalPass::evalBoolExpression(const BinaryExpression::Op op, return result; } +} // namespace std::optional ConstEvalPass::visitBinaryExpression( const std::shared_ptr binaryExpression) { diff --git a/src/ir/parsers/qasm3_parser/passes/TypeCheckPass.cpp b/src/qasm3/passes/TypeCheckPass.cpp similarity index 93% rename from src/ir/parsers/qasm3_parser/passes/TypeCheckPass.cpp rename to src/qasm3/passes/TypeCheckPass.cpp index 7a8d4dc38..2ba762225 100644 --- a/src/ir/parsers/qasm3_parser/passes/TypeCheckPass.cpp +++ b/src/qasm3/passes/TypeCheckPass.cpp @@ -7,11 +7,12 @@ * Licensed under the MIT License */ -#include "ir/parsers/qasm3_parser/passes/TypeCheckPass.hpp" +#include "qasm3/passes/TypeCheckPass.hpp" -#include "ir/parsers/qasm3_parser/Exception.hpp" -#include "ir/parsers/qasm3_parser/Statement.hpp" -#include "ir/parsers/qasm3_parser/Types.hpp" +#include "qasm3/Exception.hpp" +#include "qasm3/Statement.hpp" +#include "qasm3/Types.hpp" +#include "qasm3/passes/ConstEvalPass.hpp" #include #include @@ -34,6 +35,29 @@ InferredType TypeCheckPass::error(const std::string& msg, return InferredType::error(); } +void TypeCheckPass::processStatement(Statement& statement) { + try { + statement.accept(this); + + if (hasError) { + throw TypeCheckError("Type check failed."); + } + } catch (const TypeCheckError& e) { + throw CompilerError(e.what(), statement.debugInfo); + } +} + +void TypeCheckPass::checkGateOperand(const GateOperand& operand) { + if (operand.expression == nullptr) { + return; + } + + if (const auto type = visit(operand.expression); + !type.isError && !type.type->isUint()) { + error("Index must be an unsigned integer"); + } +} + void TypeCheckPass::visitGateStatement( const std::shared_ptr gateStatement) { // we save the current environment to restore it afterward diff --git a/test/circuit_optimizer/CMakeLists.txt b/test/circuit_optimizer/CMakeLists.txt index 7ca0f01bb..bc684c0b4 100644 --- a/test/circuit_optimizer/CMakeLists.txt +++ b/test/circuit_optimizer/CMakeLists.txt @@ -9,5 +9,5 @@ if(TARGET MQT::CoreCircuitOptimizer) file(GLOB_RECURSE CIRCUIT_OPTIMIZER_TEST_SOURCES *.cpp) package_add_test(mqt-core-circuit-optimizer-test MQT::CoreCircuitOptimizer ${CIRCUIT_OPTIMIZER_TEST_SOURCES}) - target_link_libraries(mqt-core-circuit-optimizer-test PRIVATE MQT::CoreAlgorithms) + target_link_libraries(mqt-core-circuit-optimizer-test PRIVATE MQT::CoreAlgorithms MQT::CoreQASM) endif() diff --git a/test/circuit_optimizer/test_remove_final_measurements.cpp b/test/circuit_optimizer/test_remove_final_measurements.cpp index de3f36dbb..993993e8c 100644 --- a/test/circuit_optimizer/test_remove_final_measurements.cpp +++ b/test/circuit_optimizer/test_remove_final_measurements.cpp @@ -12,6 +12,7 @@ #include "ir/QuantumComputation.hpp" #include "ir/operations/CompoundOperation.hpp" #include "ir/operations/OpType.hpp" +#include "qasm3/Importer.hpp" #include #include @@ -147,10 +148,7 @@ TEST(RemoveFinalMeasurements, removeFinalMeasurementsWithOperationsInFront) { const std::string circ = "OPENQASM 2.0;include \"qelib1.inc\";qreg q[3];qreg r[3];h q;cx q, " "r;creg c[3];creg d[3];barrier q;measure q->c;measure r->d;\n"; - std::stringstream ss{}; - ss << circ; - QuantumComputation qc{}; - qc.import(ss, qc::Format::OpenQASM2); + auto qc = qasm3::Importer::imports(circ); std::cout << "-----------------------------\n"; qc.print(std::cout); CircuitOptimizer::removeFinalMeasurements(qc); diff --git a/test/circuits/test.qc b/test/circuits/test.qc deleted file mode 100755 index d69d7f6e6..000000000 --- a/test/circuits/test.qc +++ /dev/null @@ -1,29 +0,0 @@ -# declare variables names -.v a b c d e -# declare inputs (missing variables are assumed ancillaries) -.i a b c -# declare outputs (missing variables are assumed garbage) -.o a b c d -# declare constants -.c 1 0 -# begin circuit description -BEGIN -H a -X b -Y c -Z d -S a -S* b -P c -P* d -T a -T* b -Rx(2.0) c -Ry(pi/2^3) d -Rz(-3.2e-2) a -cnot b c -tof d a b -Zd c d a -swap b c -X d a b c -END diff --git a/test/circuits/test.real b/test/circuits/test.real deleted file mode 100755 index 9939918b5..000000000 --- a/test/circuits/test.real +++ /dev/null @@ -1,42 +0,0 @@ -# declare number of variables -.numvars 4 -# declare variable names -.variables a b c d -.constants --01 -.garbage --11 - -# begin circuit description -.begin -01 a -id1 a -h1 a -n1 a -c a b -x1 a -y1 a -s1 a -si1 a -sp1 a -s+1 a -v a b -vp a b -v+ a b -rx1:2 a -ry1:2 a -rz1:2 a -f a b -if a b -p a b c -pi a b c -p+ a b c -q1:1 a -q1:2 a -q1:-2 a -q1:4 a -q1:-4 a -q1:8 a -q1:-8 a -t2 a b -t3 a b c -t4 a b c d -.end diff --git a/test/circuits/test.tfc b/test/circuits/test.tfc deleted file mode 100644 index e8a870ca6..000000000 --- a/test/circuits/test.tfc +++ /dev/null @@ -1,50 +0,0 @@ -# Example .tfc format: -# -# Tags: -# (Note: There must be whitespace of some sort between the tag and -# parameters (with the exception of the comment tag, #) -# -# # - Indicates comment. Any text following a # on the beginning -# of a line will be ignored. -# .v - Variable list. All variables (in AND out) need to be listed -# here, separated by commas. Any whitespace following the -# beginning of this list will be considered as a part of the -# variable name. -# .i - List of variables used as input (remaining variables from .v -# list are constant). .v must have been previously defined. -# .o - List of variables used as output (remaining variables from .v -# list are garbage). .v must have been previously defined. -# .c - Values for constant input variables. .v and .i must have been -# previously defined. -# -# Once the four main tags have been defined, a line containing the text: -# "BEGIN" (without quotes, any case) signifies the beginning of the gate -# listing. -# -# Gates: -# -# Gates should be written on a single line. Gates will continue to -# be read until a line is reached that is not a proper gate, or end -# of file. -# -# Format = Gx : G = 'T' or 'F' (lowercase acceptable) meaning -# Toffoli or Fredkin gate -# x = Number of parameters -# -# Parameters follow on the same line and, similar to the parameters -# for the tags above, are separated from the gate type by whitespace. -# Each parameter should be found in the .v list defined above, and -# should be separated by commas. -# -# END statement terminates the input. - -.v a,b,c,d,e -.i a,b,c -.o d,c -.c 0,1 -BEGIN -t3 a,b,d -t2 a,b -t3 b,c,d -t2 b,c -END diff --git a/test/dd/CMakeLists.txt b/test/dd/CMakeLists.txt index 6e2ef01ca..c7b0dbfb1 100644 --- a/test/dd/CMakeLists.txt +++ b/test/dd/CMakeLists.txt @@ -9,4 +9,5 @@ if(TARGET MQT::CoreDD) file(GLOB_RECURSE DD_TEST_SOURCES *.cpp) package_add_test(mqt-core-dd-test MQT::CoreDD ${DD_TEST_SOURCES}) target_link_libraries(mqt-core-dd-test PRIVATE MQT::CoreCircuitOptimizer) + target_link_libraries(mqt-core-dd-test PRIVATE MQT::CoreQASM) endif() diff --git a/test/dd/test_dd_functionality.cpp b/test/dd/test_dd_functionality.cpp index 53b43ab8e..f21058efb 100644 --- a/test/dd/test_dd_functionality.cpp +++ b/test/dd/test_dd_functionality.cpp @@ -21,6 +21,7 @@ #include "ir/operations/Control.hpp" #include "ir/operations/OpType.hpp" #include "ir/operations/StandardOperation.hpp" +#include "qasm3/Importer.hpp" #include #include @@ -375,7 +376,7 @@ TEST_F(DDFunctionality, changePermutation) { "include \"qelib1.inc\";" "qreg q[2];" "x q[0];\n"; - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); const auto sim = simulate(qc, dd->makeZeroState(qc.getNqubits()), *dd); EXPECT_TRUE(sim.p->e[0].isZeroTerminal()); EXPECT_TRUE(sim.p->e[1].w.exactlyOne()); diff --git a/test/ir/CMakeLists.txt b/test/ir/CMakeLists.txt index 03f35b657..06c64b47c 100644 --- a/test/ir/CMakeLists.txt +++ b/test/ir/CMakeLists.txt @@ -9,4 +9,5 @@ if(TARGET MQT::CoreIR) file(GLOB_RECURSE IR_TEST_SOURCES *.cpp) package_add_test_with_working_dir(mqt-core-ir-test MQT::CoreIR ${CMAKE_CURRENT_BINARY_DIR} ${IR_TEST_SOURCES}) + target_link_libraries(mqt-core-ir-test PRIVATE MQT::CoreQASM) endif() diff --git a/test/ir/test_io.cpp b/test/ir/test_io.cpp index a6786ada4..57eec1179 100644 --- a/test/ir/test_io.cpp +++ b/test/ir/test_io.cpp @@ -13,7 +13,8 @@ #include "ir/operations/NonUnitaryOperation.hpp" #include "ir/operations/OpType.hpp" #include "ir/operations/StandardOperation.hpp" -#include "ir/parsers/qasm3_parser/Exception.hpp" +#include "qasm3/Exception.hpp" +#include "qasm3/Importer.hpp" #include #include @@ -33,7 +34,7 @@ class IO : public testing::Test { protected: void TearDown() override {} - void SetUp() override { qc = std::make_unique(); } + void SetUp() override {} std::size_t nqubits = 0; std::size_t seed = 0; @@ -43,22 +44,19 @@ class IO : public testing::Test { std::string output4 = "tmp.tmp.qasm"; std::string output5 = "./tmpdir/circuit.qasm"; std::string output5dir = "tmpdir"; - std::unique_ptr qc; + qc::QuantumComputation qc; }; namespace { -void compareFiles(const std::string& file1, const std::string& file2, - bool stripWhitespaces = false) { +void compareFiles(const std::string& file1, const std::string& file2) { std::ifstream fstream1(file1); std::string str1((std::istreambuf_iterator(fstream1)), std::istreambuf_iterator()); std::ifstream fstream2(file2); std::string str2((std::istreambuf_iterator(fstream2)), std::istreambuf_iterator()); - if (stripWhitespaces) { - str1.erase(std::remove_if(str1.begin(), str1.end(), isspace), str1.end()); - str2.erase(std::remove_if(str2.begin(), str2.end(), isspace), str2.end()); - } + str1.erase(std::remove_if(str1.begin(), str1.end(), isspace), str1.end()); + str2.erase(std::remove_if(str2.begin(), str2.end(), isspace), str2.end()); ASSERT_EQ(str1, str2); } } // namespace @@ -68,13 +66,13 @@ TEST_F(IO, importAndDumpQASM) { constexpr auto format = qc::Format::OpenQASM2; std::cout << "FILE: " << input << "\n"; - ASSERT_NO_THROW(qc->import(input, format)); - ASSERT_NO_THROW(qc->dump(output, format)); - ASSERT_NO_THROW(qc->reset()); - ASSERT_NO_THROW(qc->import(output, format)); - ASSERT_NO_THROW(qc->dump(output2, format)); + qc = qasm3::Importer::importf(input); + qc.dump(output, format); + qc.reset(); + qc = qasm3::Importer::importf(output); + qc.dump(output2, format); - compareFiles(output, output2, true); + compareFiles(output, output2); std::filesystem::remove(output); std::filesystem::remove(output2); } @@ -84,25 +82,25 @@ TEST_F(IO, importAndDumpQASMFromConstructor) { constexpr auto format = qc::Format::OpenQASM2; std::cout << "FILE: " << input << "\n"; - ASSERT_NO_THROW(qc = std::make_unique(input)); - ASSERT_NO_THROW(qc->dump(output, format)); - ASSERT_NO_THROW(qc->reset()); - ASSERT_NO_THROW(qc->import(output, format)); - ASSERT_NO_THROW(qc->dump(output2, format)); + qc = qasm3::Importer::importf(input); + qc.dump(output, format); + qc.reset(); + qc = qasm3::Importer::importf(output); + qc.dump(output2, format); - compareFiles(output, output2, true); + compareFiles(output, output2); std::filesystem::remove(output); std::filesystem::remove(output2); } TEST_F(IO, dumpValidFilenames) { - ASSERT_NO_THROW(qc->dump(output3, qc::Format::OpenQASM2)); - ASSERT_NO_THROW(qc->dump(output4, qc::Format::OpenQASM2)); - ASSERT_NO_THROW(qc->dump(output4)); + qc.dump(output3, qc::Format::OpenQASM2); + qc.dump(output4, qc::Format::OpenQASM2); + qc.dump(output4); std::filesystem::create_directory(output5dir); - ASSERT_NO_THROW(qc->dump(output5, qc::Format::OpenQASM2)); - ASSERT_NO_THROW(qc->dump(output5)); + qc.dump(output5, qc::Format::OpenQASM2); + qc.dump(output5); std::filesystem::remove(output3); std::filesystem::remove(output4); @@ -111,79 +109,48 @@ TEST_F(IO, dumpValidFilenames) { } TEST_F(IO, importFromStringQASM) { - ASSERT_NO_THROW(*qc = qc::QuantumComputation::fromQASM("qreg q[2];" - "U(pi/2,0,pi) q[0];" - "CX q[0],q[1];")); - std::cout << *qc << "\n"; -} - -TEST_F(IO, importFromStringReal) { - std::stringstream ss{ - ".numvars 2\n.variables q0 q1\n.begin\nh1 q0\nt2 q0 q1\n.end"}; - ASSERT_NO_THROW(qc->import(ss, qc::Format::Real)); - std::cout << *qc << "\n"; + qc = qasm3::Importer::imports("qreg q[2];" + "U(pi/2,0,pi) q[0];" + "CX q[0],q[1];"); + std::cout << qc << "\n"; } TEST_F(IO, controlledOpActingOnWholeRegister) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM("qreg q[2];" - "cx q,q[1];"), + EXPECT_THROW(qc = qasm3::Importer::imports("qreg q[2];" + "cx q,q[1];"), qc::QFRException); } -TEST_F(IO, invalidRealHeader) { - std::stringstream ss{ - ".numvars 2\nvariables q0 q1\n.begin\nh1 q0\nt2 q0 q1\n.end"}; - EXPECT_THROW(qc->import(ss, qc::Format::Real), qc::QFRException); -} - -TEST_F(IO, invalidRealCommand) { - std::stringstream ss{".numvars 2\n.var q0 q1\n.begin\nh1 q0\n# test " - "comment\nt2 q0 q1\n.end"}; - EXPECT_THROW(qc->import(ss, qc::Format::Real), qc::QFRException); -} - TEST_F(IO, insufficientRegistersQelib) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM("qreg q[2];" - "cx q[0];"), + EXPECT_THROW(qc = qasm3::Importer::imports("qreg q[2];" + "cx q[0];"), qasm3::CompilerError); } TEST_F(IO, insufficientRegistersEnhancedQelib) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM( - "qreg q[4];" - "ctrl(3) @ z q[0], q[1], q[2];"), + EXPECT_THROW(qc = qasm3::Importer::imports("qreg q[4];" + "ctrl(3) @ z q[0], q[1], q[2];"), qasm3::CompilerError); } TEST_F(IO, superfluousRegistersQelib) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM("qreg q[3];" - "cx q[0], q[1], q[2];"), + EXPECT_THROW(qc = qasm3::Importer::imports("qreg q[3];" + "cx q[0], q[1], q[2];"), qasm3::CompilerError); } TEST_F(IO, superfluousRegistersEnhancedQelib) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM( - "qreg q[5];" - "ctrl(3) z q[0], q[1], q[2], q[3], q[4];"), - qasm3::CompilerError); -} - -TEST_F(IO, dumpNegativeControl) { - std::stringstream ss{".numvars 2\n.variables a b\n.begin\nt2 -a b\n.end"}; - qc->import(ss, qc::Format::Real); - *qc = qc::QuantumComputation::fromQASM(qc->toQASM()); - ASSERT_EQ(qc->getNops(), 1); - const auto& gate = qc->front(); - EXPECT_EQ(gate->getType(), qc::X); - EXPECT_EQ(gate->getControls().size(), 1); - EXPECT_EQ(gate->getControls().begin()->type, qc::Control::Type::Neg); + EXPECT_THROW( + qc = qasm3::Importer::imports("qreg q[5];" + "ctrl(3) z q[0], q[1], q[2], q[3], q[4];"), + qasm3::CompilerError); } TEST_F(IO, qiskitMcxGray) { - *qc = qc::QuantumComputation::fromQASM("qreg q[4];" - "mcx_gray q[0], q[1], q[2], q[3];"); - std::cout << *qc << "\n"; - const auto& gate = qc->front(); + qc = qasm3::Importer::imports("qreg q[4];" + "mcx_gray q[0], q[1], q[2], q[3];"); + std::cout << qc << "\n"; + const auto& gate = qc.front(); EXPECT_EQ(gate->getType(), qc::X); EXPECT_EQ(gate->getNcontrols(), 3); EXPECT_EQ(gate->getTargets().at(0), 3); @@ -191,101 +158,100 @@ TEST_F(IO, qiskitMcxGray) { TEST_F(IO, qiskitMcxSkipGateDefinition) { - *qc = qc::QuantumComputation::fromQASM( + qc = qasm3::Importer::imports( "qreg q[4];" "gate mcx q0,q1,q2,q3 { ctrl(3) @ x q0,q1,q2,q3; }" "mcx q[0], q[1], q[2], q[3];"); - std::cout << *qc << "\n"; - const auto& gate = qc->front(); + std::cout << qc << "\n"; + const auto& gate = qc.front(); EXPECT_EQ(gate->getType(), qc::X); EXPECT_EQ(gate->getNcontrols(), 3); EXPECT_EQ(gate->getTargets().at(0), 3); } TEST_F(IO, qiskitMcphase) { - *qc = qc::QuantumComputation::fromQASM("qreg q[4];" - "mcphase(pi) q[0], q[1], q[2], q[3];"); - std::cout << *qc << "\n"; - const auto& gate = qc->front(); + qc = qasm3::Importer::imports("qreg q[4];" + "mcphase(pi) q[0], q[1], q[2], q[3];"); + std::cout << qc << "\n"; + const auto& gate = qc.front(); EXPECT_EQ(gate->getType(), qc::Z); EXPECT_EQ(gate->getNcontrols(), 3); EXPECT_EQ(gate->getTargets().at(0), 3); } TEST_F(IO, qiskitMcphaseInDeclaration) { - *qc = qc::QuantumComputation::fromQASM( + qc = qasm3::Importer::imports( "qreg q[4];" "gate foo q0, q1, q2, q3 { mcphase(pi) q0, q1, q2, q3; }" "foo q[0], q[1], q[2], q[3];"); - std::cout << *qc << "\n"; - const auto& op = qc->front(); + std::cout << qc << "\n"; + const auto& op = qc.front(); EXPECT_EQ(op->getType(), qc::Z); EXPECT_EQ(op->getNcontrols(), 3); EXPECT_EQ(op->getTargets().at(0), 3); } TEST_F(IO, qiskitMcxRecursive) { - *qc = qc::QuantumComputation::fromQASM( + qc = qasm3::Importer::imports( "qreg q[6];" "qreg anc[1];" "mcx_recursive q[0], q[1], q[2], q[3], q[4];" "mcx_recursive q[0], q[1], q[2], q[3], q[4], q[5], anc[0];"); - std::cout << *qc << "\n"; - const auto& gate = qc->at(0); + std::cout << qc << "\n"; + const auto& gate = qc.at(0); EXPECT_EQ(gate->getType(), qc::X); EXPECT_EQ(gate->getNcontrols(), 4); EXPECT_EQ(gate->getTargets().at(0), 4); - const auto& second = qc->at(1); + const auto& second = qc.at(1); EXPECT_EQ(second->getType(), qc::X); EXPECT_EQ(second->getNcontrols(), 5); EXPECT_EQ(second->getTargets().at(0), 5); } TEST_F(IO, qiskitMcxVchain) { - *qc = qc::QuantumComputation::fromQASM( - "qreg q[4];" - "qreg anc[1];" - "mcx_vchain q[0], q[1], q[2], q[3], anc[0];"); - std::cout << *qc << "\n"; - const auto& gate = qc->front(); + qc = qasm3::Importer::imports("qreg q[4];" + "qreg anc[1];" + "mcx_vchain q[0], q[1], q[2], q[3], anc[0];"); + std::cout << qc << "\n"; + const auto& gate = qc.front(); EXPECT_EQ(gate->getType(), qc::X); EXPECT_EQ(gate->getNcontrols(), 3); EXPECT_EQ(gate->getTargets().at(0), 3); } TEST_F(IO, qiskitMcxRecursiveInDeclaration) { - *qc = qc::QuantumComputation::fromQASM( + qc = qasm3::Importer::imports( "qreg q[7];" "gate foo q0, q1, q2, q3, q4 { mcx_recursive q0, q1, q2, q3, q4; }" "gate bar q0, q1, q2, q3, q4, q5, anc { mcx_recursive q0, q1, q2, q3, " "q4, q5, anc; }" "foo q[0], q[1], q[2], q[3], q[4];" "bar q[0], q[1], q[2], q[3], q[4], q[5], q[6];"); - std::cout << *qc << "\n"; - const auto& op = qc->at(0); + std::cout << qc << "\n"; + const auto& op = qc.at(0); EXPECT_EQ(op->getType(), qc::X); EXPECT_EQ(op->getNcontrols(), 4); EXPECT_EQ(op->getTargets().at(0), 4); - const auto& second = qc->at(1); + const auto& second = qc.at(1); EXPECT_EQ(second->getType(), qc::X); EXPECT_EQ(second->getNcontrols(), 5); EXPECT_EQ(second->getTargets().at(0), 5); } TEST_F(IO, qiskitMcxVchainInDeclaration) { - *qc = qc::QuantumComputation::fromQASM( + qc = qasm3::Importer::imports( "qreg q[5];" "gate foo q0, q1, q2, q3, anc { mcx_vchain q0, q1, q2, q3, anc; }" "foo q[0], q[1], q[2], q[3], q[4];"); - std::cout << *qc << "\n"; - const auto& op = qc->front(); + std::cout << qc << "\n"; + const auto& op = qc.front(); EXPECT_EQ(op->getType(), qc::X); EXPECT_EQ(op->getNcontrols(), 3); EXPECT_EQ(op->getTargets().at(0), 3); } TEST_F(IO, qiskitMcxDuplicateQubit) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM( + EXPECT_THROW(qc = qasm3::Importer::imports( "qreg q[4];" "qreg anc[1];" "mcx_vchain q[0], q[0], q[2], q[3], anc[0];"), @@ -293,39 +259,39 @@ TEST_F(IO, qiskitMcxDuplicateQubit) { } TEST_F(IO, qiskitMcxQubitRegister) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM( - "qreg q[4];" - "qreg anc[1];" - "mcx_vchain q, q[0], q[2], q[3], anc[0];"), - qasm3::CompilerError); + EXPECT_THROW( + qc = qasm3::Importer::imports("qreg q[4];" + "qreg anc[1];" + "mcx_vchain q, q[0], q[2], q[3], anc[0];"), + qasm3::CompilerError); } TEST_F(IO, barrierInDeclaration) { - *qc = - qc::QuantumComputation::fromQASM("qreg q[1];" - "gate foo q0 { h q0; barrier q0; h q0; }" - "foo q[0];"); - std::cout << *qc << "\n"; - EXPECT_EQ(qc->getNops(), 1); - const auto& op = qc->at(0); + qc = qasm3::Importer::imports("qreg q[1];" + "gate foo q0 { h q0; barrier q0; h q0; }" + "foo q[0];"); + std::cout << qc << "\n"; + EXPECT_EQ(qc.getNops(), 1); + const auto& op = qc.at(0); EXPECT_EQ(op->getType(), qc::Compound); const auto* comp = dynamic_cast(op.get()); ASSERT_NE(comp, nullptr); - EXPECT_EQ(comp->size(), 2); + EXPECT_EQ(comp->size(), 3); EXPECT_EQ(comp->at(0)->getType(), qc::H); - EXPECT_EQ(comp->at(1)->getType(), qc::H); + EXPECT_EQ(comp->at(1)->getType(), qc::Barrier); + EXPECT_EQ(comp->at(2)->getType(), qc::H); } TEST_F(IO, CommentInDeclaration) { - *qc = qc::QuantumComputation::fromQASM("qreg q[1];gate foo q0 {" - "h q0;" - "//x q0;\n" - "h q0;" - "}" - "foo q[0];"); - std::cout << *qc << "\n"; - EXPECT_EQ(qc->getNops(), 1); - const auto& op = qc->at(0); + qc = qasm3::Importer::imports("qreg q[1];gate foo q0 {" + "h q0;" + "//x q0;\n" + "h q0;" + "}" + "foo q[0];"); + std::cout << qc << "\n"; + EXPECT_EQ(qc.getNops(), 1); + const auto& op = qc.at(0); EXPECT_EQ(op->getType(), qc::Compound); const auto* comp = dynamic_cast(op.get()); ASSERT_NE(comp, nullptr); @@ -334,100 +300,84 @@ TEST_F(IO, CommentInDeclaration) { EXPECT_EQ(comp->at(1)->getType(), qc::H); } -TEST_F(IO, realInput) { - qc->import("../circuits/test.real"); - std::cout << *qc << "\n"; -} - -TEST_F(IO, tfcInput) { - qc->import("../circuits/test.tfc"); - std::cout << *qc << "\n"; -} - -TEST_F(IO, qcInput) { - qc->import("../circuits/test.qc"); - std::cout << *qc << "\n"; -} - TEST_F(IO, classicControlled) { - *qc = - qc::QuantumComputation::fromQASM("qreg q[1];" - "creg c[1];" - "h q[0];" - "measure q->c;" - "// test classic controlled operation\n" - "if (c==1) x q[0];"); - std::cout << *qc << "\n"; + qc = qasm3::Importer::imports("qreg q[1];" + "creg c[1];" + "h q[0];" + "measure q->c;" + "// test classic controlled operation\n" + "if (c==1) x q[0];"); + std::cout << qc << "\n"; } TEST_F(IO, iSWAPDumpIsValid) { - qc->addQubitRegister(2); - qc->iswap(0, 1); - std::cout << *qc << "\n"; - const auto qasm = qc->toQASM(); + qc.addQubitRegister(2); + qc.iswap(0, 1); + std::cout << qc << "\n"; + const auto qasm = qc.toQASM(); std::cout << qasm << "\n"; - EXPECT_NO_THROW(*qc = qc::QuantumComputation::fromQASM(qasm)); - std::cout << *qc << "\n"; + EXPECT_NO_THROW(qc = qasm3::Importer::imports(qasm)); + std::cout << qc << "\n"; } TEST_F(IO, iSWAPdagDumpIsValid) { - qc->addQubitRegister(2); - qc->iswapdg(0, 1); - std::cout << *qc << "\n"; - const auto qasm = qc->toQASM(); + qc.addQubitRegister(2); + qc.iswapdg(0, 1); + std::cout << qc << "\n"; + const auto qasm = qc.toQASM(); std::cout << qasm << "\n"; - EXPECT_NO_THROW(*qc = qc::QuantumComputation::fromQASM(qasm)); - std::cout << *qc << "\n"; + EXPECT_NO_THROW(qc = qasm3::Importer::imports(qasm)); + std::cout << qc << "\n"; } TEST_F(IO, PeresDumpIsValid) { - qc->addQubitRegister(2); - qc->peres(0, 1); - std::cout << *qc << "\n"; - const auto qasm = qc->toQASM(); + qc.addQubitRegister(2); + qc.peres(0, 1); + std::cout << qc << "\n"; + const auto qasm = qc.toQASM(); std::cout << qasm << "\n"; - EXPECT_NO_THROW(*qc = qc::QuantumComputation::fromQASM(qasm)); - std::cout << *qc << "\n"; + EXPECT_NO_THROW(qc = qasm3::Importer::imports(qasm)); + std::cout << qc << "\n"; } TEST_F(IO, PeresdagDumpIsValid) { - qc->addQubitRegister(2); - qc->peresdg(0, 1); - std::cout << *qc << "\n"; - const auto qasm = qc->toQASM(); + qc.addQubitRegister(2); + qc.peresdg(0, 1); + std::cout << qc << "\n"; + const auto qasm = qc.toQASM(); std::cout << qasm << "\n"; - EXPECT_NO_THROW(*qc = qc::QuantumComputation::fromQASM(qasm)); - std::cout << *qc << "\n"; + EXPECT_NO_THROW(qc = qasm3::Importer::imports(qasm)); + std::cout << qc << "\n"; } TEST_F(IO, printingNonUnitary) { - *qc = qc::QuantumComputation::fromQASM("qreg q[2];" - "creg c[2];" - "h q[0];" - "reset q[0];" - "h q[0];" - "barrier q;" - "measure q -> c;"); - std::cout << *qc << "\n"; - for (const auto& op : *qc) { - op->print(std::cout, qc->getNqubits()); + qc = qasm3::Importer::imports("qreg q[2];" + "creg c[2];" + "h q[0];" + "reset q[0];" + "h q[0];" + "barrier q;" + "measure q -> c;"); + std::cout << qc << "\n"; + for (const auto& op : qc) { + op->print(std::cout, qc.getNqubits()); std::cout << "\n"; } } TEST_F(IO, sxAndSxdag) { - *qc = qc::QuantumComputation::fromQASM("qreg q[1];" - "creg c[1];" - "gate test q0 { sx q0; sxdg q0;}" - "sx q[0];" - "sxdg q[0];" - "test q[0];"); - std::cout << *qc << "\n"; - const auto& op1 = qc->at(0); + qc = qasm3::Importer::imports("qreg q[1];" + "creg c[1];" + "gate test q0 { sx q0; sxdg q0;}" + "sx q[0];" + "sxdg q[0];" + "test q[0];"); + std::cout << qc << "\n"; + const auto& op1 = qc.at(0); EXPECT_EQ(op1->getType(), qc::OpType::SX); - const auto& op2 = qc->at(1); + const auto& op2 = qc.at(1); EXPECT_EQ(op2->getType(), qc::OpType::SXdg); - const auto& op3 = qc->at(2); + const auto& op3 = qc.at(2); ASSERT_TRUE(op3->isCompoundOperation()); auto* compOp = dynamic_cast(op3.get()); ASSERT_NE(compOp, nullptr); @@ -438,14 +388,14 @@ TEST_F(IO, sxAndSxdag) { } TEST_F(IO, unifyRegisters) { - *qc = qc::QuantumComputation::fromQASM("qreg q[1];" - "qreg r[1];" - "x q[0];" - "x r[0];"); - std::cout << *qc << "\n"; - qc->unifyQuantumRegisters(); - std::cout << *qc << "\n"; - const auto qasm = qc->toQASM(false); + qc = qasm3::Importer::imports("qreg q[1];" + "qreg r[1];" + "x q[0];" + "x r[0];"); + std::cout << qc << "\n"; + qc.unifyQuantumRegisters(); + std::cout << qc << "\n"; + const auto qasm = qc.toQASM(false); EXPECT_EQ(qasm, "// i 0 1\n" "// o 0 1\n" "OPENQASM 2.0;\n" @@ -456,12 +406,12 @@ TEST_F(IO, unifyRegisters) { } TEST_F(IO, appendMeasurementsAccordingToOutputPermutation) { - *qc = qc::QuantumComputation::fromQASM("// o 1\n" - "qreg q[2];" - "x q[1];"); - qc->appendMeasurementsAccordingToOutputPermutation(); - std::cout << *qc << "\n"; - const auto& op = qc->back(); + qc = qasm3::Importer::imports("// o 1\n" + "qreg q[2];" + "x q[1];"); + qc.appendMeasurementsAccordingToOutputPermutation(); + std::cout << qc << "\n"; + const auto& op = qc.back(); ASSERT_EQ(op->getType(), qc::OpType::Measure); const auto& meas = dynamic_cast(op.get()); ASSERT_NE(meas, nullptr); @@ -472,14 +422,14 @@ TEST_F(IO, appendMeasurementsAccordingToOutputPermutation) { } TEST_F(IO, appendMeasurementsAccordingToOutputPermutationAddRegister) { - *qc = qc::QuantumComputation::fromQASM("// o 0 1\n" - "qreg q[2];" - "creg d[1];" - "x q;"); - qc->appendMeasurementsAccordingToOutputPermutation(); - std::cout << *qc << "\n"; - EXPECT_EQ(qc->getNcbits(), 2U); - const auto& op = qc->back(); + qc = qasm3::Importer::imports("// o 0 1\n" + "qreg q[2];" + "creg d[1];" + "x q;"); + qc.appendMeasurementsAccordingToOutputPermutation(); + std::cout << qc << "\n"; + EXPECT_EQ(qc.getNcbits(), 2U); + const auto& op = qc.back(); ASSERT_EQ(op->getType(), qc::OpType::Measure); const auto& meas = dynamic_cast(op.get()); ASSERT_NE(meas, nullptr); @@ -487,7 +437,7 @@ TEST_F(IO, appendMeasurementsAccordingToOutputPermutationAddRegister) { EXPECT_EQ(meas->getTargets().front(), 1U); EXPECT_EQ(meas->getClassics().size(), 1U); EXPECT_EQ(meas->getClassics().front(), 1U); - const auto& op2 = *(++qc->rbegin()); + const auto& op2 = *(++qc.rbegin()); ASSERT_EQ(op2->getType(), qc::OpType::Measure); const auto& meas2 = dynamic_cast(op2.get()); ASSERT_NE(meas2, nullptr); @@ -495,7 +445,7 @@ TEST_F(IO, appendMeasurementsAccordingToOutputPermutationAddRegister) { EXPECT_EQ(meas2->getTargets().front(), 0U); EXPECT_EQ(meas2->getClassics().size(), 1U); EXPECT_EQ(meas2->getClassics().front(), 0U); - const auto qasm = qc->toQASM(false); + const auto qasm = qc.toQASM(false); std::cout << qasm << "\n"; EXPECT_EQ(qasm, "// i 0 1\n" "// o 0 1\n" @@ -530,41 +480,40 @@ TEST_F(IO, NativeTwoQubitGateImportAndExport) { std::stringstream ss{}; ss << header << gate << " q[0], q[1];\n"; const auto target = ss.str(); - qc->import(ss, qc::Format::OpenQASM3); - std::cout << *qc << "\n"; + qc = qasm3::Importer::imports(target); + std::cout << qc << "\n"; std::ostringstream oss{}; - qc->dump(oss, qc::Format::OpenQASM2); + qc.dumpOpenQASM(oss, false); std::cout << oss.str() << "\n"; EXPECT_STREQ(oss.str().c_str(), target.c_str()); - qc->reset(); + qc.reset(); std::cout << "---\n"; } } TEST_F(IO, UseQelib1Gate) { - *qc = qc::QuantumComputation::fromQASM("include \"qelib1.inc\";" - "qreg q[3];" - "rccx q[0], q[1], q[2];"); - std::cout << *qc << "\n"; - EXPECT_EQ(qc->getNqubits(), 3U); - EXPECT_EQ(qc->getNops(), 1U); - EXPECT_EQ(qc->front()->getType(), qc::Compound); - const auto& op = - dynamic_cast(qc->front().get()); + qc = qasm3::Importer::imports("include \"qelib1.inc\";" + "qreg q[3];" + "rccx q[0], q[1], q[2];"); + std::cout << qc << "\n"; + EXPECT_EQ(qc.getNqubits(), 3U); + EXPECT_EQ(qc.getNops(), 1U); + EXPECT_EQ(qc.front()->getType(), qc::Compound); + const auto& op = dynamic_cast(qc.front().get()); ASSERT_NE(op, nullptr); EXPECT_EQ(op->size(), 9U); } TEST_F(IO, ParameterizedGateDefinition) { - *qc = qc::QuantumComputation::fromQASM( + qc = qasm3::Importer::imports( "qreg q[1];" "gate foo(theta, beta) q { rz(theta) q; rx(beta) q; }" "foo(2*cos(pi/4), 0.5*sin(pi/2)) q[0];"); - std::cout << *qc << "\n"; - EXPECT_EQ(qc->getNqubits(), 1U); - EXPECT_EQ(qc->getNops(), 1U); - EXPECT_EQ(qc->at(0)->getType(), qc::Compound); - const auto& op = dynamic_cast(qc->at(0).get()); + std::cout << qc << "\n"; + EXPECT_EQ(qc.getNqubits(), 1U); + EXPECT_EQ(qc.getNops(), 1U); + EXPECT_EQ(qc.at(0)->getType(), qc::Compound); + const auto& op = dynamic_cast(qc.at(0).get()); ASSERT_NE(op, nullptr); EXPECT_EQ(op->size(), 2U); EXPECT_EQ(op->at(0)->getType(), qc::RZ); @@ -578,8 +527,7 @@ TEST_F(IO, ParameterizedGateDefinition) { } TEST_F(IO, NonExistingInclude) { - EXPECT_THROW(*qc = qc::QuantumComputation::fromQASM( - "include \"nonexisting.inc\";"), + EXPECT_THROW(qc = qasm3::Importer::imports("include \"nonexisting.inc\";"), qasm3::CompilerError); } @@ -587,13 +535,13 @@ TEST_F(IO, NonStandardInclude) { std::ofstream ofs{"defs.inc"}; ofs << "gate foo q { h q; }\n"; ofs.close(); - *qc = qc::QuantumComputation::fromQASM("include \"defs.inc\";" - "qreg q[1];" - "foo q[0];"); - std::cout << *qc << "\n"; - EXPECT_EQ(qc->getNqubits(), 1U); - EXPECT_EQ(qc->getNops(), 1U); - EXPECT_EQ(qc->front()->getType(), qc::H); + qc = qasm3::Importer::imports("include \"defs.inc\";" + "qreg q[1];" + "foo q[0];"); + std::cout << qc << "\n"; + EXPECT_EQ(qc.getNqubits(), 1U); + EXPECT_EQ(qc.getNops(), 1U); + EXPECT_EQ(qc.front()->getType(), qc::H); std::filesystem::remove("defs.inc"); } @@ -602,23 +550,23 @@ TEST_F(IO, SingleRegistersDoubleCreg) { "qreg q[1];\n" "creg c[2];\n" "measure p[0] -> c[0];"; - *qc = qc::QuantumComputation::fromQASM(qasmIn); - std::cout << *qc << "\n"; - const auto qasmOut = qc->toQASM(false); + qc = qasm3::Importer::imports(qasmIn); + std::cout << qc << "\n"; + const auto qasmOut = qc.toQASM(false); std::cout << qasmOut << "\n"; EXPECT_NE(qasmOut.find(qasmIn), std::string::npos); } TEST_F(IO, MarkAncillaryAndDump) { - *qc = qc::QuantumComputation::fromQASM("qreg q[2];" - "x q[0];" - "x q[1];"); - std::cout << *qc << "\n"; - qc->setLogicalQubitAncillary(0U); - EXPECT_EQ(qc->getNancillae(), 1U); - EXPECT_TRUE(qc->logicalQubitIsAncillary(0U)); - std::cout << *qc << "\n"; - const auto qasm = qc->toQASM(false); + qc = qasm3::Importer::imports("qreg q[2];" + "x q[0];" + "x q[1];"); + std::cout << qc << "\n"; + qc.setLogicalQubitAncillary(0U); + EXPECT_EQ(qc.getNancillae(), 1U); + EXPECT_TRUE(qc.logicalQubitIsAncillary(0U)); + std::cout << qc << "\n"; + const auto qasm = qc.toQASM(false); std::cout << qasm << "\n"; const auto* const expected = "// i 0 1\n" "// o 0 1\n" @@ -631,23 +579,23 @@ TEST_F(IO, MarkAncillaryAndDump) { } TEST_F(IO, dumpEmptyOpenQASM) { - *qc = qc::QuantumComputation::fromQASM(""); + qc = qasm3::Importer::imports(""); std::string const openQASM2 = "// i\n// o\nOPENQASM 2.0;\ninclude \"qelib1.inc\";\n"; std::string const openQASM3 = "// i\n// o\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\n"; - EXPECT_EQ(openQASM2, qc->toQASM(false)); - EXPECT_EQ(openQASM3, qc->toQASM(true)); + EXPECT_EQ(openQASM2, qc.toQASM(false)); + EXPECT_EQ(openQASM3, qc.toQASM(true)); } TEST_F(IO, fromCompoundOperation) { - qc->addQubitRegister(2); - qc->addClassicalRegister(2); - qc->x(1); - qc->measure(1, 1); - const auto compound = qc->asCompoundOperation(); + qc.addQubitRegister(2); + qc.addClassicalRegister(2); + qc.x(1); + qc.measure(1, 1); + const auto compound = qc.asCompoundOperation(); const auto qc2 = qc::QuantumComputation::fromCompoundOperation(*compound); EXPECT_EQ(qc2.getNqubits(), 2); @@ -666,10 +614,10 @@ TEST_F(IO, fromCompoundOperation) { } TEST_F(IO, classicalControlledOperationToOpenQASM3) { - qc->addQubitRegister(2); - const auto& creg = qc->addClassicalRegister(2); - qc->classicControlled(qc::X, 0, 0); - qc->classicControlled(qc::X, 1, creg); + qc.addQubitRegister(2); + const auto& creg = qc.addClassicalRegister(2); + qc.classicControlled(qc::X, 0, 0); + qc.classicControlled(qc::X, 1, creg); const std::string expected = "// i 0 1\n" "// o 0 1\n" "OPENQASM 3.0;\n" @@ -683,17 +631,17 @@ TEST_F(IO, classicalControlledOperationToOpenQASM3) { " x q[1];\n" "}\n"; - const auto actual = qc->toQASM(); + const auto actual = qc.toQASM(); EXPECT_EQ(expected, actual); } TEST_F(IO, dumpingIncompleteOutputPermutationNotStartingAtZero) { - qc->addQubitRegister(2); - qc->addClassicalRegister(1); - qc->measure(1, 0); - qc->initializeIOMapping(); - const auto qasm = qc->toQASM(); + qc.addQubitRegister(2); + qc.addClassicalRegister(1); + qc.measure(1, 0); + qc.initializeIOMapping(); + const auto qasm = qc.toQASM(); std::cout << qasm << "\n"; - const auto qc2 = qc::QuantumComputation::fromQASM(qasm); - EXPECT_EQ(*qc, qc2); + const auto qc2 = qasm3::Importer::imports(qasm); + EXPECT_EQ(qc, qc2); } diff --git a/test/ir/test_qasm3_parser.cpp b/test/ir/test_qasm3_parser.cpp index f20b876ec..1738d2711 100644 --- a/test/ir/test_qasm3_parser.cpp +++ b/test/ir/test_qasm3_parser.cpp @@ -11,12 +11,13 @@ #include "ir/QuantumComputation.hpp" #include "ir/operations/ClassicControlledOperation.hpp" #include "ir/operations/OpType.hpp" -#include "ir/parsers/qasm3_parser/Exception.hpp" -#include "ir/parsers/qasm3_parser/Parser.hpp" -#include "ir/parsers/qasm3_parser/Scanner.hpp" -#include "ir/parsers/qasm3_parser/Statement.hpp" -#include "ir/parsers/qasm3_parser/Token.hpp" -#include "ir/parsers/qasm3_parser/passes/ConstEvalPass.hpp" +#include "qasm3/Exception.hpp" +#include "qasm3/Importer.hpp" +#include "qasm3/Parser.hpp" +#include "qasm3/Scanner.hpp" +#include "qasm3/Statement.hpp" +#include "qasm3/Token.hpp" +#include "qasm3/passes/ConstEvalPass.hpp" #include #include @@ -43,7 +44,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3) { "\n" "// lines */\n" "bit[3] c;\n"; - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); EXPECT_EQ(qc.getNqubits(), 3); EXPECT_EQ(qc.getNcbits(), 3); } @@ -53,7 +54,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3OldSyntax) { "include \"stdgates.inc\";\n" "qreg q[3];\n" "creg r[3];\n"; - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); EXPECT_EQ(qc.getNqubits(), 3); EXPECT_EQ(qc.getNcbits(), 3); } @@ -67,7 +68,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3GateDecl) { " x q2;\n" "}\n" "my_x q[0], q[1];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -100,7 +101,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3CtrlModifier) { "ctrl @ swap q[0], q[1], q[2];\n" "ctrl @ rxx(pi) q[0], q[1], q[2];\n" "ctrl @ negctrl @ x q[0], q[1], q[2];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = @@ -132,7 +133,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3InvModifier) { "include \"stdgates.inc\";\n" "qubit[1] q;\n" "inv @ s q[0];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0\n" @@ -154,7 +155,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3CompoundGate) { " h q;\n" "}\n" "my_compound_gate q;"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0\n" @@ -175,7 +176,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3ControlledCompoundGate) { " x q;\n" "}\n" "ctrl @ my_compound_gate q[0], q[1];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -195,7 +196,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3ParamCompoundGate) { " rz(a) q;\n" "}\n" "my_compound_gate(1.0 * pi) q[0];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -219,7 +220,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3Measure) { "r2 = measure q;\n" "measure q[1] -> r1;\n" "measure q[1] -> r2[0];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -245,7 +246,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3InitialLayout) { "OPENQASM 3.0;\n" "include \"stdgates.inc\";\n" "qubit[2] q;\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 1 0\n" @@ -258,7 +259,6 @@ TEST_F(Qasm3ParserTest, ImportQasm3InitialLayout) { } TEST_F(Qasm3ParserTest, ImportQasm3ConstEval) { - std::stringstream ss{}; const std::string testfile = "OPENQASM 3.0;\n" "include \"stdgates.inc\";\n" @@ -266,10 +266,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3ConstEval) { "qubit[N * 2] q;\n" "ctrl @ x q[0], q[N * 2 - 1];\n" "x q;"; - - ss << testfile; - auto qc = QuantumComputation(); - qc.import(ss, Format::OpenQASM3); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1 2 3 4 5 6 7\n" @@ -299,7 +296,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3NonUnitary) { "barrier q1, q2;\n" "reset q1;\n" "bit c = measure q1[0];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1 2 3\n" @@ -325,7 +322,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3IfStatement) { "if (c == 1) {\n" " x q[1];\n" "}"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -361,7 +358,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3IfElseStatement) { " x q[0];\n" " x q[1];\n" "}"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = @@ -397,7 +394,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3EmptyIfElse) { "if (c == 1) {\n" "} else {\n" "}"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -415,7 +412,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3OutputPerm) { const std::string testfile = "// i 0 2 1 3\n" "// o 3 0\n" "qubit[4] q;\n"; - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); std::stringstream out{}; QuantumComputation::printPermutation(qc.outputPermutation, out); @@ -428,7 +425,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3OutputPerm) { TEST_F(Qasm3ParserTest, ImportQasm3OutputPermDefault) { const std::string testfile = "// i 0 2 1 3\n" "qubit[4] q;\n"; - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); std::stringstream out{}; QuantumComputation::printPermutation(qc.outputPermutation, out); @@ -449,7 +446,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3IfElseNoBlock) { "if (c == 1) {} else \n" " x q[1];\n" "x q[0];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -477,7 +474,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3InvalidStatementInBlock) { "}"; EXPECT_THROW( try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Only quantum statements are supported in blocks."); @@ -489,7 +486,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3InvalidStatementInBlock) { TEST_F(Qasm3ParserTest, ImportQasm3ImplicitInclude) { const std::string testfile = "qubit q;\n" "h q[0];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0\n" @@ -506,7 +503,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3Qelib1) { "include \"qelib1.inc\";\n" "qubit q;\n" "h q[0];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0\n" @@ -524,7 +521,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3Teleportation) { "opaque teleport src, anc, tgt;\n" "qubit[3] q;\n" "teleport q[0], q[1], q[2];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = @@ -546,7 +543,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3NestedGates) { "gate my_x2 q1 { x q1; }\n" "qubit[1] q;\n" "my_x2 q[0];\n"; - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); EXPECT_EQ(qc.getNops(), 1); EXPECT_EQ(qc.at(0)->getType(), OpType::X); } @@ -557,7 +554,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3AlternatingControl) { "qubit[7] q;\n" "ctrl @ negctrl(2) @ negctrl @ ctrl @ ctrl @ x " "q[0], q[1], q[2], q[3], q[4], q[5], q[6];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1 2 3 4 5 6\n" @@ -576,7 +573,7 @@ TEST_F(Qasm3ParserTest, ImportQasmConstEval) { "const uint N_1 = 0xa;\n" "const uint N_2 = 8;\n" "qubit[N_1 - N_2] q;\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -597,7 +594,7 @@ TEST_F(Qasm3ParserTest, ImportQasmBroadcasting) { "h q1;\n" "reset q2;\n" "cx q1, q2;\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1 2 3\n" @@ -627,7 +624,7 @@ TEST_F(Qasm3ParserTest, ImportQasmComparison) { "if (c1 >= 0) { x q[0]; }\n" "if (c1 == 0) { x q[0]; }\n" "if (c1 != 0) { x q[0]; }\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -668,7 +665,7 @@ TEST_F(Qasm3ParserTest, ImportQasmNativeRedeclaration) { "gate h q { U(pi/2, 0, pi) q; }\n" "h q;\n" "c1 = measure q;\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0\n" @@ -689,7 +686,7 @@ TEST_F(Qasm3ParserTest, ImportQasm2CPrefix) { "gate ccccx q1, q2, q3, q4, q5 {\n" "}\n" "ccccx q[0], q[1], q[2], q[3], q[4];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1 2 3 4\n" @@ -705,7 +702,7 @@ TEST_F(Qasm3ParserTest, ImportMCXGate) { const std::string testfile = "OPENQASM 3.0;\n" "qubit[4] q;\n" "mcx q[0], q[1], q[2], q[3];\n"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1 2 3\n" @@ -740,7 +737,7 @@ TEST_F(Qasm3ParserTest, ImportMQTBenchCircuit) { measure eval[0] -> meas[0]; measure q[0] -> meas[1]; )"; - auto qc = qc::QuantumComputation::fromQASM(qasm); + auto qc = qasm3::Importer::imports(qasm); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1\n" @@ -775,7 +772,7 @@ TEST_F(Qasm3ParserTest, ImportMSGate) { "ms(0.844396) q[0], q[1], q[2];" "c = measure q;"; - auto qc = QuantumComputation::fromQASM(testfile); + auto qc = qasm3::Importer::imports(testfile); const std::string out = qc.toQASM(); const std::string expected = "// i 0 1 2\n" @@ -798,7 +795,7 @@ TEST_F(Qasm3ParserTest, ImportQasm2CPrefixInvalidGate) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Usage of unknown gate 'cccck'."); throw; @@ -814,7 +811,7 @@ TEST_F(Qasm3ParserTest, ImportQasm3CPrefix) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Usage of unknown gate 'ccccx'."); throw; @@ -892,7 +889,7 @@ TEST_F(Qasm3ParserTest, ImportQasmParseOperators) { "x **= 1;\n"; ss << testfile; - qasm3::Parser parser(&ss, false); + qasm3::Parser parser(ss, false); const auto expectedTypes = std::vector{ qasm3::AssignmentStatement::Type::PlusAssignment, @@ -925,7 +922,7 @@ TEST_F(Qasm3ParserTest, ImportQasmParseUnaryExpressions) { "sqrt(x)\n"; ss << testfile; - qasm3::Parser parser(&ss, false); + qasm3::Parser parser(ss, false); const auto expectedTypes = std::vector{ qasm3::UnaryExpression::Op::Sin, qasm3::UnaryExpression::Op::Cos, @@ -953,7 +950,7 @@ TEST_F(Qasm3ParserTest, ImportQasmParseBinaryExpressions) { "x > 5\n"; ss << testfile; - qasm3::Parser parser(&ss, false); + qasm3::Parser parser(ss, false); const auto expectedTypes = std::vector{ qasm3::BinaryExpression::Op::Power, @@ -981,7 +978,7 @@ TEST_F(Qasm3ParserTest, ImportQasmUnknownQreg) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Usage of unknown quantum register."); throw; @@ -998,7 +995,7 @@ TEST_F(Qasm3ParserTest, ImportQasmIndexOutOfBounds) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Index expression must be smaller than the " "width of the quantum register."); @@ -1017,7 +1014,7 @@ TEST_F(Qasm3ParserTest, ImportQasmIndexOutOfBoundsClassical) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Index expression must be smaller than the " "width of the classical register."); @@ -1035,7 +1032,7 @@ TEST_F(Qasm3ParserTest, ImportQasmDuplicateDeclaration) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Identifier 'q' already declared."); throw; @@ -1052,7 +1049,7 @@ TEST_F(Qasm3ParserTest, ImportQasmInitConstRegWithMeasure) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Constant Evaluation: Constant declaration " "initialization expression must be const."); @@ -1070,7 +1067,7 @@ TEST_F(Qasm3ParserTest, ImportQasmAssignmentUnknownIdentifier) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1088,7 +1085,7 @@ TEST_F(Qasm3ParserTest, ImportQasmAssignmentConstVar) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1106,7 +1103,7 @@ TEST_F(Qasm3ParserTest, ImportQasmMultipleInputPermutations) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Multiple initial layout specifications found."); throw; @@ -1124,7 +1121,7 @@ TEST_F(Qasm3ParserTest, ImportQasmMultipleOutputPermutations) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Multiple output permutation specifications found."); @@ -1141,7 +1138,7 @@ TEST_F(Qasm3ParserTest, ImportQasmInvalidOpaqueGate) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Unsupported opaque gate 'asdf'."); throw; @@ -1158,7 +1155,7 @@ TEST_F(Qasm3ParserTest, ImportQasmDuplicateGateDecl) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Gate 'my_x' already declared."); throw; @@ -1174,7 +1171,7 @@ TEST_F(Qasm3ParserTest, ImportQasmDuplicateQubitArgGate) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Qubit 'q' already declared."); throw; @@ -1191,7 +1188,7 @@ TEST_F(Qasm3ParserTest, ImportQasmUndeclaredGate) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Usage of unknown gate 'my_x'."); throw; @@ -1209,7 +1206,7 @@ TEST_F(Qasm3ParserTest, ImportQasmInvalidGateTargets) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Gate 'my_x' takes 1 targets, but 2 were supplied."); @@ -1227,7 +1224,7 @@ TEST_F(Qasm3ParserTest, ImportQasmInvalidGateControls) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Gate 'cx' takes 1 targets, but 0 were supplied."); @@ -1245,7 +1242,7 @@ TEST_F(Qasm3ParserTest, ImportQasmInvalidGateModifiers) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Gate 'x' takes 2 controls, but only 1 were supplied."); @@ -1264,7 +1261,7 @@ TEST_F(Qasm3ParserTest, ImportQasmGateCallNonConst) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Only const expressions are supported as gate " "parameters, but found 'IdentifierExpr (c)'."); @@ -1283,7 +1280,7 @@ TEST_F(Qasm3ParserTest, ImportQasmGateCallBroadcastingInvalidWidth) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ( e.message, @@ -1303,7 +1300,7 @@ TEST_F(Qasm3ParserTest, ImportQasmGateCallIndexingGateBody) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Gate arguments cannot be indexed within gate body."); @@ -1321,7 +1318,7 @@ TEST_F(Qasm3ParserTest, ImportQasmGateMeasureInvalidSizes) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Classical and quantum register must have the same width " @@ -1340,7 +1337,7 @@ TEST_F(Qasm3ParserTest, ImportQasmGateOldStyleDesignator) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "In OpenQASM 3.0, the designator has been " "changed to `type[designator] identifier;`"); @@ -1357,7 +1354,7 @@ TEST_F(Qasm3ParserTest, ImportQasmGateExpectStatement) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Expected quantum statement, got '+'."); throw; @@ -1372,7 +1369,7 @@ TEST_F(Qasm3ParserTest, ImportQasmGateVersionDeclaration) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ( e.message, @@ -1390,7 +1387,7 @@ TEST_F(Qasm3ParserTest, ImportQasmInvalidExpected) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Expected ',', got 'Identifier'."); throw; @@ -1406,7 +1403,7 @@ TEST_F(Qasm3ParserTest, ImportQasmTypeMismatchAssignment) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1422,7 +1419,7 @@ TEST_F(Qasm3ParserTest, ImportQasmTypeMismatchBinaryExpr) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1437,7 +1434,7 @@ TEST_F(Qasm3ParserTest, ImportQasmConstNotInitialized) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Constant Evaluation: Constant declaration initialization " @@ -1454,7 +1451,7 @@ TEST_F(Qasm3ParserTest, ImportQasmUnaryTypeMismatchLogicalNot) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1470,7 +1467,7 @@ TEST_F(Qasm3ParserTest, ImportQasmUnaryTypeMismatchBitwiseNot) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1485,7 +1482,7 @@ TEST_F(Qasm3ParserTest, ImportQasmBinaryTypeMismatch) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1501,7 +1498,7 @@ TEST_F(Qasm3ParserTest, ImportQasmAssignmentIndexType) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1516,7 +1513,7 @@ TEST_F(Qasm3ParserTest, ImportQasmUnknownIdentifier) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1531,7 +1528,7 @@ TEST_F(Qasm3ParserTest, ImportQasmUnknownQubit) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1546,7 +1543,7 @@ TEST_F(Qasm3ParserTest, ImportQasmNegativeTypeDesignator) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Type Check Error: Type check failed."); throw; @@ -1565,7 +1562,7 @@ TEST_F(Qasm3ParserTest, ImportQasmRegisterDeclarationInDefinition) { EXPECT_THROW( { try { - const auto qc = QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); } catch (const qasm3::CompilerError& e) { EXPECT_EQ(e.message, "Expected quantum statement, got 'qubit'."); throw; diff --git a/test/ir/test_qfr_functionality.cpp b/test/ir/test_qfr_functionality.cpp index dd51395d6..cf1aec64c 100644 --- a/test/ir/test_qfr_functionality.cpp +++ b/test/ir/test_qfr_functionality.cpp @@ -19,6 +19,7 @@ #include "ir/operations/Operation.hpp" #include "ir/operations/StandardOperation.hpp" #include "ir/operations/SymbolicOperation.hpp" +#include "qasm3/Importer.hpp" #include #include @@ -179,7 +180,6 @@ TEST_F(QFRFunctionality, splitQreg) { } TEST_F(QFRFunctionality, StripIdleAndDump) { - std::stringstream ss{}; const std::string testfile = "OPENQASM 2.0;\n" "include \"qelib1.inc\";\n" "qreg q[5];\n" @@ -192,19 +192,17 @@ TEST_F(QFRFunctionality, StripIdleAndDump) { "reset q[2];\n" "cx q[0],q[4];\n"; - ss << testfile; - auto qc = QuantumComputation(); - qc.import(ss, Format::OpenQASM2); + auto qc = qasm3::Importer::imports(testfile); qc.print(std::cout); qc.stripIdleQubits(); qc.print(std::cout); std::stringstream goal{}; qc.print(goal); std::stringstream test{}; - qc.dump(test, Format::OpenQASM2); + qc.dumpOpenQASM(test, false); std::cout << test.str() << "\n"; qc.reset(); - qc.import(test, Format::OpenQASM2); + qc = qasm3::Importer::import(test); qc.print(std::cout); qc.stripIdleQubits(); qc.print(std::cout); @@ -589,7 +587,6 @@ TEST_F(QFRFunctionality, UpdateOutputPermutation) { } TEST_F(QFRFunctionality, RzAndPhaseDifference) { - QuantumComputation qc(2); const std::string qasm = "// i 0 1\n" "// o 0 1\n" "OPENQASM 2.0;\n" @@ -599,12 +596,10 @@ TEST_F(QFRFunctionality, RzAndPhaseDifference) { "p(1/8) q[1];\n" "crz(1/8) q[0],q[1];\n" "cp(1/8) q[0],q[1];\n"; - std::stringstream ss; - ss << qasm; - qc.import(ss, Format::OpenQASM2); + auto qc = qasm3::Importer::imports(qasm); std::cout << qc << "\n"; std::stringstream oss; - qc.dumpOpenQASM2(oss); + qc.dumpOpenQASM(oss, false); } TEST_F(QFRFunctionality, U3toU2Gate) { @@ -700,11 +695,10 @@ TEST_F(QFRFunctionality, dumpAndImportTeleportation) { QuantumComputation qc(3); qc.emplace_back(Targets{0, 1, 2}, OpType::Teleportation); std::stringstream ss; - qc.dumpOpenQASM2(ss); + qc.dumpOpenQASM(ss, false); EXPECT_TRUE(ss.str().find("teleport") != std::string::npos); - QuantumComputation qcImported(3); - qcImported.import(ss, Format::OpenQASM2); + auto qcImported = qasm3::Importer::import(ss); ASSERT_EQ(qcImported.size(), 1); EXPECT_EQ(qcImported.at(0)->getType(), OpType::Teleportation); } @@ -1513,7 +1507,7 @@ TEST_F(QFRFunctionality, stripIdleQubits) { "c[1] = measure q_h[0];\n"; EXPECT_EQ(qc.toQASM(), expected); - const auto qc2 = QuantumComputation::fromQASM(expected); + const auto qc2 = qasm3::Importer::imports(expected); EXPECT_EQ(qc2.toQASM(), expected); } } // namespace qc diff --git a/test/ir/test_real_parser.cpp b/test/ir/test_real_parser.cpp deleted file mode 100644 index 8b871fe8b..000000000 --- a/test/ir/test_real_parser.cpp +++ /dev/null @@ -1,1570 +0,0 @@ -/* - * Copyright (c) 2025 Chair for Design Automation, TUM - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "Definitions.hpp" -#include "ir/Permutation.hpp" -#include "ir/QuantumComputation.hpp" - -#include "gmock/gmock-matchers.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace qc; -using ::testing::NotNull; - -class RealParserTest : public testing::Test { -public: - RealParserTest& usingVersion(double versionNumber) { - realFileContent << realHeaderVersionCommandPrefix << " " << std::fixed - << std::setprecision(1) << versionNumber << "\n"; - return *this; - } - - RealParserTest& usingNVariables(std::size_t numVariables) { - realFileContent << realHeaderNumVarsCommandPrefix << " " - << std::to_string(numVariables) << "\n"; - return *this; - } - - RealParserTest& usingVariables( - const std::initializer_list& variableIdents) { - return usingVariables(variableIdents, std::nullopt); - } - - RealParserTest& - usingVariables(const std::initializer_list& variableIdents, - const std::optional& optionalPostfix) { - pipeStringifiedCollectionToStream(realFileContent, - realHeaderVariablesCommandPrefix, - variableIdents, " ", optionalPostfix); - return *this; - } - - RealParserTest& usingInitialLayout( - const std::initializer_list& variableIdents) { - return usingInitialLayout(variableIdents, std::nullopt); - } - - RealParserTest& usingInitialLayout( - const std::initializer_list& variableIdents, - const std::optional& optionalPostfix) { - pipeStringifiedCollectionToStream(realFileContent, - realHeaderInitialLayoutCommandPrefix, - variableIdents, " ", optionalPostfix); - return *this; - } - - RealParserTest& - usingInputs(const std::initializer_list& inputIdents) { - return usingInputs(inputIdents, std::nullopt); - } - - RealParserTest& - usingInputs(const std::initializer_list& inputIdents, - const std::optional& optionalPostfix) { - pipeStringifiedCollectionToStream(realFileContent, - realHeaderInputCommandPrefix, inputIdents, - " ", optionalPostfix); - return *this; - } - - RealParserTest& - usingOutputs(const std::initializer_list& outputIdents) { - return usingOutputs(outputIdents, std::nullopt); - } - - RealParserTest& - usingOutputs(const std::initializer_list& outputIdents, - const std::optional& optionalPostfix) { - pipeStringifiedCollectionToStream(realFileContent, - realHeaderOutputCommandPrefix, - outputIdents, " ", optionalPostfix); - return *this; - } - - RealParserTest& - withConstants(const std::initializer_list& constantValuePerVariable) { - return withConstants(constantValuePerVariable, std::nullopt); - } - - RealParserTest& - withConstants(const std::initializer_list& constantValuePerVariable, - const std::optional& optionalPostfix) { - const std::string concatenatedConstantValues(constantValuePerVariable); - pipeStringifiedCollectionToStream( - realFileContent, realHeaderConstantsCommandPrefix + " ", - {concatenatedConstantValues}, "", optionalPostfix); - return *this; - } - - RealParserTest& withGarbageValues( - const std::initializer_list& isGarbageValuePerVariable) { - return withGarbageValues(isGarbageValuePerVariable, std::nullopt); - } - - RealParserTest& withGarbageValues( - const std::initializer_list& isGarbageValuePerVariable, - const std::optional& optionalPostfix) { - - const std::string concatenatedIsGarbageValues(isGarbageValuePerVariable); - pipeStringifiedCollectionToStream( - realFileContent, realHeaderGarbageCommandPrefix + " ", - {concatenatedIsGarbageValues}, "", optionalPostfix); - return *this; - } - - RealParserTest& withEmptyGateList() { - realFileContent << realHeaderGateListPrefix << "\n" - << reakHeaderGateListPostfix; - return *this; - } - - RealParserTest& withGates( - const std::initializer_list& stringifiedGateList) { - if (stringifiedGateList.size() == 0) { - return withEmptyGateList(); - } - - realFileContent << realHeaderGateListPrefix; - for (const auto& stringifiedGate : stringifiedGateList) { - realFileContent << "\n" << stringifiedGate; - } - - realFileContent << "\n" << reakHeaderGateListPostfix; - return *this; - } - -protected: - const std::string realHeaderVersionCommandPrefix = ".version"; - const std::string realHeaderNumVarsCommandPrefix = ".numvars"; - const std::string realHeaderVariablesCommandPrefix = ".variables"; - const std::string realHeaderInitialLayoutCommandPrefix = ".initial_layout"; - const std::string realHeaderInputCommandPrefix = ".inputs"; - const std::string realHeaderOutputCommandPrefix = ".outputs"; - const std::string realHeaderConstantsCommandPrefix = ".constants"; - const std::string realHeaderGarbageCommandPrefix = ".garbage"; - const std::string realHeaderGateListPrefix = ".begin"; - const std::string reakHeaderGateListPostfix = ".end"; - - static constexpr double DEFAULT_REAL_VERSION = 2.0; - - const char constantValueZero = '0'; - const char constantValueOne = '1'; - const char constantValueNone = '-'; - - const char isGarbageState = '1'; - const char isNotGarbageState = '-'; - static constexpr char COMMENT_LINE_PREFIX = '#'; - - enum class GateType : std::uint8_t { Toffoli, V }; - - std::unique_ptr quantumComputationInstance; - std::stringstream realFileContent; - - static void pipeStringifiedCollectionToStream( - std::stringstream& pipedToStream, std::string_view elementsPrefix, - const std::initializer_list& elements, - std::string_view elementDelimiter, - const std::optional& optionalPostfix) { - pipedToStream << elementsPrefix; - for (const auto& element : elements) { - pipedToStream << elementDelimiter << element; - } - - if (optionalPostfix.has_value()) { - pipedToStream << optionalPostfix.value(); - } - - pipedToStream << "\n"; - } - - static std::string createComment(std::string_view commentData) { - return std::string(1, COMMENT_LINE_PREFIX) + std::string(commentData); - } - - void SetUp() override { - quantumComputationInstance = std::make_unique(); - ASSERT_THAT(quantumComputationInstance, NotNull()); - } - - static Permutation getIdentityPermutation(std::size_t nQubits) { - auto identityPermutation = Permutation(); - for (std::size_t i = 0; i < nQubits; ++i) { - const auto qubit = static_cast(i); - identityPermutation.insert({qubit, qubit}); - } - return identityPermutation; - } - - static std::string stringifyGateType(const GateType gateType) { - if (gateType == GateType::Toffoli) { - return "t"; - } - if (gateType == GateType::V) { - return "v"; - } - - throw std::invalid_argument("Failed to stringify gate type"); - } - - static std::string - stringifyGate(const GateType gateType, - const std::initializer_list& controlLines, - const std::initializer_list& targetLines) { - return stringifyGate(gateType, std::nullopt, controlLines, targetLines, - std::nullopt); - } - - static std::string - stringifyGate(const GateType gateType, - const std::optional& optionalNumberOfGateLines, - const std::initializer_list& controlLines, - const std::initializer_list& targetLines, - const std::optional& optionalPostfix) { - EXPECT_TRUE(targetLines.size() > static_cast(0)) - << "Gate must have at least one line defined"; - - std::stringstream stringifiedGateBuffer; - if (controlLines.size() == 0 && !optionalNumberOfGateLines.has_value()) { - stringifiedGateBuffer << stringifyGateType(gateType); - } else { - stringifiedGateBuffer - << stringifyGateType(gateType) - << std::to_string(optionalNumberOfGateLines.value_or( - controlLines.size() + targetLines.size())); - } - for (const auto& controlLine : controlLines) { - stringifiedGateBuffer << " " << controlLine; - } - - for (const auto& targetLine : targetLines) { - stringifiedGateBuffer << " " << targetLine; - } - - if (optionalPostfix.has_value()) { - stringifiedGateBuffer << optionalPostfix.value(); - } - - return stringifiedGateBuffer.str(); - } -}; - -// ERROR TESTS -TEST_F(RealParserTest, MoreVariablesThanNumVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2", "v3"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, MoreInputsThanVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "i2", "i3"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, MoreOutputsThanVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o1", "o2", "o3"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, MoreConstantsThanVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withConstants({constantValueZero, constantValueZero, constantValueZero}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, MoreGarbageEntriesThanVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGarbageValues({isGarbageState, isNotGarbageState, isGarbageState}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, MoreIdentsInInitialLayoutThanVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v1", "v2", "v3"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, LessVariablesThanNumVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, LessInputsThanNumVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, LessOutputsThanNumVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, LessConstantsThanNumVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withConstants({constantValueNone}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, LessGarbageEntriesThanNumVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGarbageValues({isNotGarbageState}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, LessIdentsInInitialLayoutThanVariablesDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidVariableIdentDefinition) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"variable-1", "v2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidInputIdentDefinition) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"test-input1", "i2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidInputIdentDefinitionInQuote) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"\"test-input1\"", "i2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidVariableIdentDefinitionInInitialLayout) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v-1", "v2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, EmptyInputIdentInQuotesNotAllowed) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "\"\""}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidOutputIdentDefinition) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"i1", "test-output1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidOutputIdentDefinitionInQuote) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"\"test-output1\"", "o2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, EmptyOutputIdentInQuotesNotAllowed) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"\"\"", "o2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InputIdentMatchingVariableIdentIsNotAllowed) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "v2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, OutputIdentMatchingVariableIdentIsNotAllowed) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"v1", "o2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateVariableIdentDefinition) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateInputIdentDefinition) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "i1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateOutputIdentDefinition) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o1", "o1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateVariableIdentDefinitionInInitialLayout) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v1", "v1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, - MissingClosingQuoteInIoIdentifierDoesNotLeadToInfinityLoop) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"\"o1", "o1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, MissingOpeningQuoteInIoIdentifierIsDetectedAsFaulty) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o1\"", "o1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidConstantStateValue) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withConstants({constantValueOne, 't'}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InvalidGarbageStateValue) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGarbageValues({'t', isNotGarbageState}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, GateWithMoreLinesThanDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(3) - .usingVariables({"v1", "v2", "v3"}) - .withGates({stringifyGate(GateType::Toffoli, std::optional(2), - {"v1", "v2"}, {"v3"}, std::nullopt)}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, GateWithLessLinesThanDeclared) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(3) - .usingVariables({"v1", "v2", "v3"}) - .withGates({stringifyGate(GateType::Toffoli, std::optional(3), {"v1"}, - {"v3"}, std::nullopt)}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, GateWithControlLineTargetingUnknownVariable) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate(GateType::Toffoli, {"v3"}, {"v2"})}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, GateWithTargetLineTargetingUnknownVariable) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v3"})}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, UnknownVariableIdentDefinitionInInitialLayout) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v4", "v1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateNumVarsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingNVariables(3) - .usingVariables({"v1", "v2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateVariablesDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "i2"}) - .usingVariables({"v1", "v2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateInputsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "i2"}) - .withConstants({constantValueOne, constantValueNone}) - .usingOutputs({"o1", "o2"}) - .usingInputs({"i1", "i2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateConstantsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "i2"}) - .withConstants({constantValueOne, constantValueNone}) - .usingOutputs({"o1", "o2"}) - .withConstants({constantValueOne, constantValueNone}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateOutputsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o1", "o2"}) - .withGarbageValues({isGarbageState, isNotGarbageState}) - .usingOutputs({"o1", "o2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateGarbageDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o1", "o2"}) - .withGarbageValues({isGarbageState, isNotGarbageState}) - .withGarbageValues({isGarbageState, isNotGarbageState}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateInitialLayoutDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v2", "v1"}) - .withGarbageValues({isGarbageState, isNotGarbageState}) - .usingInitialLayout({"v2", "v1"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, HeaderWithoutNumVarsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingVariables({"v1", "v2"}) - .withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, HeaderWithoutVariablesDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION).usingNVariables(2).withEmptyGateList(); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, ContentWithoutGateListNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, VariableDefinitionPriorToNumVarsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingVariables({"v1", "v2"}) - .usingNVariables(2); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InputsDefinitionPrioToVariableDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingInputs({"i1", "i2"}) - .usingVariables({"v1", "v2"}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, OutputDefinitionPriorToVariableDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingOutputs({"o1", "o2"}) - .usingVariables({"v1", "v2"}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, ConstantsDefinitionPriorToNumVarsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .withConstants({constantValueOne, constantValueZero}) - .usingNVariables(2) - .usingVariables({"v1", "v2"}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, GarbageDefinitionPriorToNumVarsDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .withGarbageValues({isGarbageState, isGarbageState}) - .usingNVariables(2) - .usingVariables({"v1", "v2"}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, InitialLayoutPriorToVariableDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingInitialLayout({"v1", "v2"}) - .usingVariables({"v1", "v2"}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, OutputsDefinitionPriorToInputDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"i2", "i1"}) - .usingInputs({"i1", "i2"}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateControlLineInGateDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(3) - .usingVariables({"v1", "v2", "v3"}) - .withGates( - {stringifyGate(GateType::Toffoli, {"v1", "v2", "v1"}, {"v3"})}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, DuplicateTargetLineInGateDefinitionNotPossible) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(3) - .usingVariables({"v1", "v2", "v3"}) - .withGates({stringifyGate(GateType::V, {"v1"}, {"v2", "v3", "v2"})}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, NotDefinedVariableNotUsableAsControlLine) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate(GateType::Toffoli, {"v1", "v3"}, {"v2"})}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, NotDefinedVariableNotUsableAsTargetLine) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v3"})}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -TEST_F(RealParserTest, GateLineNotUsableAsControlAndTargetLine) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v1"})}); - - EXPECT_THROW( - quantumComputationInstance->import(realFileContent, Format::Real), - QFRException); -} - -// OK TESTS -TEST_F(RealParserTest, ConstantValueZero) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withConstants({constantValueZero, constantValueNone}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"})}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNancillae()); - ASSERT_EQ(0, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(true, false)); - - ASSERT_EQ( - std::hash{}(getIdentityPermutation(2)), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, ConstantValueOne) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withConstants({constantValueNone, constantValueOne}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"})}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNancillae()); - ASSERT_EQ(0, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, true)); - - ASSERT_EQ( - std::hash{}(getIdentityPermutation(2)), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, GarbageValues) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGarbageValues({isNotGarbageState, isGarbageState}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"})}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(1, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, true)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, false)); - - Permutation expectedOutputPermutation; - expectedOutputPermutation.emplace(static_cast(0), - static_cast(0)); - - ASSERT_EQ( - std::hash{}(expectedOutputPermutation), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, InputIdentDefinitionInQuotes) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i1", "\"test_input_1\""}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"})}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(0, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, false)); - - ASSERT_EQ( - std::hash{}(getIdentityPermutation(2)), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, OutputIdentDefinitionInQuotes) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"\"other_output_2\"", "\"o2\""}) - .withGates({stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"})}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(0, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, false)); - - ASSERT_EQ( - std::hash{}(getIdentityPermutation(2)), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, - InputIdentInQuotesAndMatchingOutputNotInQuotesNotConsideredEqual) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(4) - .usingVariables({"v1", "v2", "v3", "v4"}) - .usingInputs({"i1", "\"o2\"", "i3", "\"o4\""}) - .withGarbageValues({isNotGarbageState, isGarbageState, isNotGarbageState, - isGarbageState}) - .usingOutputs({"i1", "o2", "i3", "o4"}) - .withGates({ - stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"}), - stringifyGate(GateType::Toffoli, {"v3"}, {"v4"}), - stringifyGate(GateType::Toffoli, {"v4"}, {"v3"}), - }); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(4, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(2, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, true, false, true)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, false, false, false)); - - auto expectedOutputPermutation = getIdentityPermutation(4); - expectedOutputPermutation.erase(1); - expectedOutputPermutation.erase(3); - - ASSERT_EQ( - std::hash{}(expectedOutputPermutation), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, - InputIdentNotInQuotesAndMatchingOutputInQuotesNotConsideredEqual) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(4) - .usingVariables({"v1", "v2", "v3", "v4"}) - .usingInputs({"i1", "i2", "i3", "i4"}) - .withGarbageValues({isNotGarbageState, isGarbageState, isNotGarbageState, - isGarbageState}) - .usingOutputs({"i1", "\"i1\"", "i2", "\"i4\""}) - .withGates({ - stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"}), - stringifyGate(GateType::Toffoli, {"v3"}, {"v4"}), - stringifyGate(GateType::Toffoli, {"v4"}, {"v3"}), - }); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(4, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(2, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, false, true, true)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, false, false, false)); - - auto expectedOutputPermutation = getIdentityPermutation(4); - expectedOutputPermutation.erase(1); - expectedOutputPermutation.erase(3); - expectedOutputPermutation[2] = static_cast(1); - - ASSERT_EQ( - std::hash{}(expectedOutputPermutation), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, MatchingInputAndOutputNotInQuotes) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(4) - .usingVariables({"v1", "v2", "v3", "v4"}) - .usingInputs({"i1", "i2", "i3", "i4"}) - .withConstants({constantValueOne, constantValueNone, constantValueNone, - constantValueZero}) - .withGarbageValues({isGarbageState, isNotGarbageState, isNotGarbageState, - isGarbageState}) - .usingOutputs({"o1", "i1", "i4", "o2"}) - .withGates({ - stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"}), - stringifyGate(GateType::Toffoli, {"v3"}, {"v4"}), - stringifyGate(GateType::Toffoli, {"v4"}, {"v3"}), - }); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(4, quantumComputationInstance->getNqubits()); - ASSERT_EQ(2, quantumComputationInstance->getNancillae()); - ASSERT_EQ(2, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, true, true, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(true, false, false, true)); - - Permutation expectedOutputPermutation; - expectedOutputPermutation.emplace(static_cast(1), - static_cast(0)); - - expectedOutputPermutation.emplace(static_cast(2), - static_cast(3)); - - ASSERT_EQ( - std::hash{}(expectedOutputPermutation), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, MatchingInputAndOutputInQuotes) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(4) - .usingVariables({"v1", "v2", "v3", "v4"}) - .usingInputs({"i1", "\"i2\"", "\"i3\"", "i4"}) - .withConstants({constantValueNone, constantValueOne, constantValueZero, - constantValueNone}) - .withGarbageValues({isNotGarbageState, isNotGarbageState, - isNotGarbageState, isGarbageState}) - .usingOutputs({"i4", "\"i3\"", "\"i2\"", "o1"}) - .withGates({ - stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"}), - stringifyGate(GateType::Toffoli, {"v3"}, {"v4"}), - stringifyGate(GateType::Toffoli, {"v4"}, {"v3"}), - }); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(4, quantumComputationInstance->getNqubits()); - ASSERT_EQ(2, quantumComputationInstance->getNancillae()); - ASSERT_EQ(1, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(true, false, false, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, true, true, false)); - - Permutation expectedOutputPermutation; - expectedOutputPermutation.emplace(static_cast(0), - static_cast(3)); - - expectedOutputPermutation.emplace(static_cast(1), - static_cast(2)); - - expectedOutputPermutation.emplace(static_cast(2), - static_cast(1)); - - ASSERT_EQ( - std::hash{}(expectedOutputPermutation), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, - OutputPermutationCorrectlySetBetweenMatchingInputAndOutputEntries) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(4) - .usingVariables({"v1", "v2", "v3", "v4"}) - .usingInputs({"i1", "i2", "i3", "i4"}) - .usingOutputs({"i4", "i3", "i2", "i1"}) - .withGates({ - stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"}), - stringifyGate(GateType::Toffoli, {"v3"}, {"v4"}), - stringifyGate(GateType::Toffoli, {"v4"}, {"v3"}), - }); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(4, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(0, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, false, false, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, false, false, false)); - - Permutation expectedOutputPermutation; - expectedOutputPermutation.emplace(static_cast(0), - static_cast(3)); - - expectedOutputPermutation.emplace(static_cast(1), - static_cast(2)); - - expectedOutputPermutation.emplace(static_cast(2), - static_cast(1)); - - expectedOutputPermutation.emplace(static_cast(3), - static_cast(0)); - - ASSERT_EQ( - std::hash{}(expectedOutputPermutation), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, OutputPermutationForGarbageQubitsNotCreated) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(4) - .usingVariables({"v1", "v2", "v3", "v4"}) - .usingInputs({"i1", "i2", "i3", "i4"}) - .withGarbageValues({isNotGarbageState, isGarbageState, isGarbageState, - isNotGarbageState}) - .usingOutputs({"i4", "o1", "o2", "i1"}) - .withGates({ - stringifyGate(GateType::Toffoli, {"v1"}, {"v2"}), - stringifyGate(GateType::Toffoli, {"v2"}, {"v1"}), - stringifyGate(GateType::Toffoli, {"v3"}, {"v4"}), - stringifyGate(GateType::Toffoli, {"v4"}, {"v3"}), - }); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(4, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(2, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, true, true, false)); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(false, false, false, false)); - - Permutation expectedOutputPermutation; - expectedOutputPermutation.emplace(static_cast(0), - static_cast(3)); - - expectedOutputPermutation.emplace(static_cast(3), - static_cast(0)); - - ASSERT_EQ( - std::hash{}(expectedOutputPermutation), - std::hash{}(quantumComputationInstance->outputPermutation)); -} - -TEST_F(RealParserTest, CheckIdentityInitialLayout) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v1", "v2"}) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - - const Permutation expectedInitialLayout = getIdentityPermutation(2); - ASSERT_EQ( - std::hash{}(expectedInitialLayout), - std::hash{}(quantumComputationInstance->initialLayout)); -} - -TEST_F(RealParserTest, CheckNoneIdentityInitialLayout) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(4) - .usingVariables({"v1", "v2", "v3", "v4"}) - .usingInitialLayout({"v4", "v2", "v1", "v3"}) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(4, quantumComputationInstance->getNqubits()); - Permutation expectedInitialLayout; - - expectedInitialLayout.emplace(static_cast(0), static_cast(2)); - - expectedInitialLayout.emplace(static_cast(1), static_cast(1)); - - expectedInitialLayout.emplace(static_cast(2), static_cast(3)); - - expectedInitialLayout.emplace(static_cast(3), static_cast(0)); - ASSERT_EQ( - std::hash{}(expectedInitialLayout), - std::hash{}(quantumComputationInstance->initialLayout)); -} - -TEST_F(RealParserTest, GateWithoutExplicitNumGateLinesDefinitionOk) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate(GateType::V, {}, {"v1", "v2"})}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNops()); -} - -TEST_F(RealParserTest, VariableDefinitionWithCommentLineAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}, - std::make_optional(createComment(" a test comment"))) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); -} - -TEST_F(RealParserTest, VariableDefinitionWithWhitespacePostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}, std::make_optional(" \t\t \t")) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); -} - -TEST_F(RealParserTest, InitialLayoutDefinitionWithCommentLineAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v2", "v1"}, - std::make_optional(createComment(" a test comment"))) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); -} - -TEST_F(RealParserTest, InitialLayoutDefinitionWithWhitespaceAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInitialLayout({"v2", "v1"}, std::make_optional(" \t\t \t")) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); -} - -TEST_F(RealParserTest, InputsDefinitionWithCommentLineAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i2", "i1"}, - std::make_optional(createComment(" a test comment"))) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); -} - -TEST_F(RealParserTest, InputsDefinitionWithWhitespaceAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingInputs({"i2", "i1"}, std::make_optional(" \t\t \t")) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); -} - -TEST_F(RealParserTest, ConstantsDefinitionWithCommentLineAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withConstants({constantValueOne, constantValueNone}, - std::make_optional(createComment(" a test comment"))) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNancillae()); - ASSERT_EQ(0, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(true, false)); -} - -TEST_F(RealParserTest, ConstantsDefinitionWithWhitespaceAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withConstants({constantValueOne, constantValueNone}, - std::make_optional(" \t\t \t")) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNancillae()); - ASSERT_EQ(0, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getAncillary(), - testing::ElementsAre(true, false)); -} - -TEST_F(RealParserTest, OutputsDefinitionWithCommentLineAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o2", "o1"}, - std::make_optional(createComment(" a test comment"))) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); -} - -TEST_F(RealParserTest, OutputsDefinitionWithWhitespaceAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .usingOutputs({"o2", "o1"}, std::make_optional(" \t\t \t")) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); -} - -TEST_F(RealParserTest, GarbageDefinitionWithCommentLineAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGarbageValues({isGarbageState, isNotGarbageState}, - std::make_optional(createComment(" a test comment"))) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(1, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(true, false)); -} - -TEST_F(RealParserTest, GarbageDefinitionWithWhitespaceAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGarbageValues({isNotGarbageState, isGarbageState}, - std::make_optional(" \t\t \t")) - .withEmptyGateList(); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(0, quantumComputationInstance->getNancillae()); - ASSERT_EQ(1, quantumComputationInstance->getNgarbageQubits()); - ASSERT_THAT(quantumComputationInstance->getGarbage(), - testing::ElementsAre(false, true)); -} - -TEST_F(RealParserTest, GateDefinitionWithCommentLineAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate( - GateType::Toffoli, std::nullopt, {"v1"}, {"v2"}, - std::make_optional(createComment(" a test comment")))}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNops()); -} - -TEST_F(RealParserTest, GateDefinitionWithWhitespaceAsPostfix) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables({"v1", "v2"}) - .withGates({stringifyGate(GateType::Toffoli, std::nullopt, {"v1"}, {"v2"}, - std::make_optional(" \t\t \t"))}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNops()); -} - -TEST_F(RealParserTest, CombinationOfCommentLineAndWhitespacePostfixAllowed) { - usingVersion(DEFAULT_REAL_VERSION) - .usingNVariables(2) - .usingVariables( - {"v1", "v2"}, - std::make_optional(" \t\t \t" + createComment(" a test comment"))) - .withGates({stringifyGate( - GateType::Toffoli, std::nullopt, {"v1"}, {"v2"}, - std::make_optional(" \t\t \t" + createComment(" a test comment")))}); - - EXPECT_NO_THROW( - quantumComputationInstance->import(realFileContent, Format::Real)); - - ASSERT_EQ(2, quantumComputationInstance->getNqubits()); - ASSERT_EQ(1, quantumComputationInstance->getNops()); -} diff --git a/test/na/CMakeLists.txt b/test/na/CMakeLists.txt index 3655bf0af..312f32ca7 100644 --- a/test/na/CMakeLists.txt +++ b/test/na/CMakeLists.txt @@ -8,4 +8,5 @@ if(TARGET MQT::CoreNA) file(GLOB_RECURSE NA_TEST_SOURCES *.cpp) package_add_test(mqt-core-na-test MQT::CoreNA ${NA_TEST_SOURCES}) + target_link_libraries(mqt-core-na-test PRIVATE MQT::CoreQASM) endif() diff --git a/test/na/test_nadefinitions.cpp b/test/na/test_nadefinitions.cpp index 3972d362f..aeafe1ddd 100644 --- a/test/na/test_nadefinitions.cpp +++ b/test/na/test_nadefinitions.cpp @@ -10,6 +10,7 @@ #include "ir/QuantumComputation.hpp" #include "ir/operations/OpType.hpp" #include "na/NADefinitions.hpp" +#include "qasm3/Importer.hpp" #include #include @@ -60,7 +61,7 @@ TEST(NADefinitions, IsGlobal) { "qubit[3] q;\n" "rz(pi/4) q[0];\n" "ry(pi/2) q;\n"; - const auto qc = qc::QuantumComputation::fromQASM(testfile); + const auto qc = qasm3::Importer::imports(testfile); EXPECT_EQ(qc.getHighestLogicalQubitIndex(), 2); EXPECT_FALSE(isGlobal(*qc.at(0), 3)); EXPECT_TRUE(isGlobal(*qc.at(1), 3)); diff --git a/test/zx/CMakeLists.txt b/test/zx/CMakeLists.txt index ad7de4d60..358cb9bbe 100644 --- a/test/zx/CMakeLists.txt +++ b/test/zx/CMakeLists.txt @@ -8,4 +8,5 @@ if(TARGET MQT::CoreZX) file(GLOB_RECURSE ZX_TEST_SOURCES *.cpp) package_add_test(mqt-core-zx-test MQT::CoreZX ${ZX_TEST_SOURCES}) + target_link_libraries(mqt-core-zx-test PRIVATE MQT::CoreQASM) endif() diff --git a/test/zx/test_zx_functionality.cpp b/test/zx/test_zx_functionality.cpp index c3e80ad96..547aa6eb1 100644 --- a/test/zx/test_zx_functionality.cpp +++ b/test/zx/test_zx_functionality.cpp @@ -14,6 +14,7 @@ #include "ir/operations/Expression.hpp" #include "ir/operations/OpType.hpp" #include "ir/operations/StandardOperation.hpp" +#include "qasm3/Importer.hpp" #include "zx/FunctionalityConstruction.hpp" #include "zx/Simplify.hpp" #include "zx/ZXDefinitions.hpp" @@ -38,7 +39,7 @@ TEST_F(ZXFunctionalityTest, parseQasm) { "qreg q[2];" "h q[0];" "cx q[0],q[1];\n"; - qc = qc::QuantumComputation::fromQASM(testfile); + qc = qasm3::Importer::imports(testfile); EXPECT_TRUE(zx::FunctionalityConstruction::transformableToZX(&qc)); const zx::ZXDiagram diag = zx::FunctionalityConstruction::buildFunctionality(&qc); @@ -152,7 +153,7 @@ TEST_F(ZXFunctionalityTest, complexCircuit) { << "z q[1];" << "cx q[0],q[1];" << "h q[0];\n"; - qc.import(ss, qc::Format::OpenQASM3); + qc = qasm3::Importer::import(ss); EXPECT_TRUE(zx::FunctionalityConstruction::transformableToZX(&qc)); zx::ZXDiagram diag = zx::FunctionalityConstruction::buildFunctionality(&qc); @@ -208,7 +209,7 @@ TEST_F(ZXFunctionalityTest, Compound) { "qreg q[3];" "toff q[0],q[1],q[2];" "ccx q[0],q[1],q[2];\n"; - qc = qc::QuantumComputation::fromQASM(testfile); + qc = qasm3::Importer::imports(testfile); EXPECT_TRUE(zx::FunctionalityConstruction::transformableToZX(&qc)); zx::ZXDiagram diag = zx::FunctionalityConstruction::buildFunctionality(&qc); zx::fullReduce(diag); From 5cb74d6ba2250be8c6d92b139aa4cda695356e1c Mon Sep 17 00:00:00 2001 From: burgholzer Date: Mon, 3 Feb 2025 20:59:52 +0100 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=8E=A8=20allow=20getting=20mutable=20?= =?UTF-8?q?references=20to=20the=20ancillary=20and=20garbage=20vector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- include/mqt-core/ir/QuantumComputation.hpp | 2 ++ src/python/ir/register_quantum_computation.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/mqt-core/ir/QuantumComputation.hpp b/include/mqt-core/ir/QuantumComputation.hpp index 30f67bb7d..9d04e163e 100644 --- a/include/mqt-core/ir/QuantumComputation.hpp +++ b/include/mqt-core/ir/QuantumComputation.hpp @@ -105,9 +105,11 @@ class QuantumComputation { [[nodiscard]] const std::vector& getAncillary() const noexcept { return ancillary; } + [[nodiscard]] std::vector& getAncillary() noexcept { return ancillary; } [[nodiscard]] const std::vector& getGarbage() const noexcept { return garbage; } + [[nodiscard]] std::vector& getGarbage() noexcept { return garbage; } [[nodiscard]] std::size_t getNcbits() const noexcept { return nclassics; } [[nodiscard]] std::string getName() const noexcept { return name; } [[nodiscard]] const auto& getQuantumRegisters() const noexcept { diff --git a/src/python/ir/register_quantum_computation.cpp b/src/python/ir/register_quantum_computation.cpp index 243840a46..9ea9ab675 100644 --- a/src/python/ir/register_quantum_computation.cpp +++ b/src/python/ir/register_quantum_computation.cpp @@ -208,7 +208,8 @@ void registerQuantumComputation(py::module& m) { /// \n Ancillary and Garbage Handling \n ///--------------------------------------------------------------------------- - qc.def_property_readonly("ancillary", &qc::QuantumComputation::getAncillary); + qc.def_property_readonly( + "ancillary", py::overload_cast<>(&qc::QuantumComputation::getAncillary)); qc.def("set_circuit_qubit_ancillary", &qc::QuantumComputation::setLogicalQubitAncillary, "q"_a); qc.def("se_circuit_qubits_ancillary", @@ -216,7 +217,8 @@ void registerQuantumComputation(py::module& m) { "q_max"_a); qc.def("is_circuit_qubit_ancillary", &qc::QuantumComputation::logicalQubitIsAncillary, "q"_a); - qc.def_property_readonly("garbage", &qc::QuantumComputation::getGarbage); + qc.def_property_readonly( + "garbage", py::overload_cast<>(&qc::QuantumComputation::getGarbage)); qc.def("set_circuit_qubit_garbage", &qc::QuantumComputation::setLogicalQubitGarbage, "q"_a); qc.def("set_circuit_qubits_garbage", From 104d2171ca7bd09a4ddb9194a1d415f6cf395799 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Tue, 4 Feb 2025 15:53:15 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=9A=A8=20address=20linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- include/mqt-core/ir/QuantumComputation.hpp | 1 - include/mqt-core/qasm3/Importer.hpp | 12 ++++-- include/mqt-core/qasm3/Statement.hpp | 2 +- include/mqt-core/qasm3/Token.hpp | 1 + .../mqt-core/qasm3/passes/ConstEvalPass.hpp | 1 + .../mqt-core/qasm3/passes/TypeCheckPass.hpp | 1 + src/qasm3/Importer.cpp | 37 ++++++++++--------- .../test_remove_final_measurements.cpp | 13 +++---- test/ir/test_qasm3_parser.cpp | 6 +-- 9 files changed, 39 insertions(+), 35 deletions(-) diff --git a/include/mqt-core/ir/QuantumComputation.hpp b/include/mqt-core/ir/QuantumComputation.hpp index 9d04e163e..051eed065 100644 --- a/include/mqt-core/ir/QuantumComputation.hpp +++ b/include/mqt-core/ir/QuantumComputation.hpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/include/mqt-core/qasm3/Importer.hpp b/include/mqt-core/qasm3/Importer.hpp index e13f39ee5..78631ad0f 100644 --- a/include/mqt-core/qasm3/Importer.hpp +++ b/include/mqt-core/qasm3/Importer.hpp @@ -10,17 +10,21 @@ #pragma once #include "Definitions.hpp" -#include "InstVisitor.hpp" -#include "NestedEnvironment.hpp" #include "ir/Permutation.hpp" -#include "passes/ConstEvalPass.hpp" -#include "passes/TypeCheckPass.hpp" +#include "qasm3/InstVisitor.hpp" +#include "qasm3/NestedEnvironment.hpp" +#include "qasm3/Statement_fwd.hpp" +#include "qasm3/passes/ConstEvalPass.hpp" +#include "qasm3/passes/TypeCheckPass.hpp" +#include #include +#include #include #include #include #include +#include #include namespace qc { diff --git a/include/mqt-core/qasm3/Statement.hpp b/include/mqt-core/qasm3/Statement.hpp index 418732c5d..fba2b78f2 100644 --- a/include/mqt-core/qasm3/Statement.hpp +++ b/include/mqt-core/qasm3/Statement.hpp @@ -250,7 +250,7 @@ class GateDeclaration final std::shared_ptr params, std::shared_ptr qbits, std::vector> stmts, - const bool opaque = false); + bool opaque = false); void accept(InstVisitor* visitor) override; }; diff --git a/include/mqt-core/qasm3/Token.hpp b/include/mqt-core/qasm3/Token.hpp index 03b526306..96ff8b42c 100644 --- a/include/mqt-core/qasm3/Token.hpp +++ b/include/mqt-core/qasm3/Token.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace qasm3 { diff --git a/include/mqt-core/qasm3/passes/ConstEvalPass.hpp b/include/mqt-core/qasm3/passes/ConstEvalPass.hpp index 5ec111d9d..2cf9156e9 100644 --- a/include/mqt-core/qasm3/passes/ConstEvalPass.hpp +++ b/include/mqt-core/qasm3/passes/ConstEvalPass.hpp @@ -11,6 +11,7 @@ #include "qasm3/InstVisitor.hpp" #include "qasm3/NestedEnvironment.hpp" +#include "qasm3/Statement_fwd.hpp" #include "qasm3/passes/CompilerPass.hpp" #include diff --git a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp index 296f2e2a0..4e3a8be86 100644 --- a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp +++ b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp @@ -11,6 +11,7 @@ #include "CompilerPass.hpp" #include "qasm3/InstVisitor.hpp" +#include "qasm3/Statement_fwd.hpp" #include "qasm3/Types.hpp" #include diff --git a/src/qasm3/Importer.cpp b/src/qasm3/Importer.cpp index c6dd082a5..903b5d576 100644 --- a/src/qasm3/Importer.cpp +++ b/src/qasm3/Importer.cpp @@ -44,11 +44,6 @@ namespace qasm3 { -using const_eval::ConstEvalPass; -using const_eval::ConstEvalValue; -using type_checking::InferredType; -using type_checking::TypeCheckPass; - auto Importer::importf(const std::string& filename) -> qc::QuantumComputation { std::ifstream file(filename); if (!file.good()) { @@ -75,19 +70,27 @@ auto Importer::imports(const std::string& qasm) -> qc::QuantumComputation { return import(is); } -std::map> +std::map> Importer::initializeBuiltins() { - std::map> builtins{}; - - InferredType const floatTy{std::dynamic_pointer_cast( - std::make_shared>(Float, 64))}; - - builtins.emplace("pi", std::pair{ConstEvalValue(qc::PI), floatTy}); - builtins.emplace("π", std::pair{ConstEvalValue(qc::PI), floatTy}); - builtins.emplace("tau", std::pair{ConstEvalValue(qc::TAU), floatTy}); - builtins.emplace("τ", std::pair{ConstEvalValue(qc::TAU), floatTy}); - builtins.emplace("euler", std::pair{ConstEvalValue(qc::E), floatTy}); - builtins.emplace("ℇ", std::pair{ConstEvalValue(qc::E), floatTy}); + std::map> + builtins{}; + + type_checking::InferredType const floatTy{ + std::dynamic_pointer_cast( + std::make_shared>(Float, 64))}; + + builtins.emplace("pi", + std::pair{const_eval::ConstEvalValue(qc::PI), floatTy}); + builtins.emplace("π", std::pair{const_eval::ConstEvalValue(qc::PI), floatTy}); + builtins.emplace("tau", + std::pair{const_eval::ConstEvalValue(qc::TAU), floatTy}); + builtins.emplace("τ", + std::pair{const_eval::ConstEvalValue(qc::TAU), floatTy}); + builtins.emplace("euler", + std::pair{const_eval::ConstEvalValue(qc::E), floatTy}); + builtins.emplace("ℇ", std::pair{const_eval::ConstEvalValue(qc::E), floatTy}); return builtins; } diff --git a/test/circuit_optimizer/test_remove_final_measurements.cpp b/test/circuit_optimizer/test_remove_final_measurements.cpp index 993993e8c..f8ffd5132 100644 --- a/test/circuit_optimizer/test_remove_final_measurements.cpp +++ b/test/circuit_optimizer/test_remove_final_measurements.cpp @@ -17,12 +17,11 @@ #include #include #include -#include #include namespace qc { TEST(RemoveFinalMeasurements, removeFinalMeasurements) { - const std::size_t nqubits = 2; + constexpr std::size_t nqubits = 2; QuantumComputation qc(nqubits, nqubits); qc.h(0); qc.h(1); @@ -48,7 +47,7 @@ TEST(RemoveFinalMeasurements, removeFinalMeasurements) { } TEST(RemoveFinalMeasurements, removeFinalMeasurementsTwoQubitMeasurement) { - const std::size_t nqubits = 2; + constexpr std::size_t nqubits = 2; QuantumComputation qc(nqubits, nqubits); qc.h(0); qc.h(1); @@ -73,7 +72,7 @@ TEST(RemoveFinalMeasurements, removeFinalMeasurementsTwoQubitMeasurement) { } TEST(RemoveFinalMeasurements, removeFinalMeasurementsCompound) { - const std::size_t nqubits = 2; + constexpr std::size_t nqubits = 2; QuantumComputation qc(nqubits, nqubits); QuantumComputation comp(nqubits, nqubits); comp.measure(0, 0); @@ -101,7 +100,7 @@ TEST(RemoveFinalMeasurements, removeFinalMeasurementsCompound) { } TEST(RemoveFinalMeasurements, removeFinalMeasurementsCompoundDegraded) { - const std::size_t nqubits = 2; + constexpr std::size_t nqubits = 2; QuantumComputation qc(nqubits, nqubits); QuantumComputation comp(nqubits, nqubits); comp.measure(0, 0); @@ -126,7 +125,7 @@ TEST(RemoveFinalMeasurements, removeFinalMeasurementsCompoundDegraded) { } TEST(RemoveFinalMeasurements, removeFinalMeasurementsCompoundEmpty) { - const std::size_t nqubits = 2; + constexpr std::size_t nqubits = 2; QuantumComputation qc(nqubits, nqubits); QuantumComputation comp(nqubits, nqubits); comp.measure(0, 0); @@ -159,7 +158,7 @@ TEST(RemoveFinalMeasurements, removeFinalMeasurementsWithOperationsInFront) { } TEST(RemoveFinalMeasurements, removeFinalMeasurementsWithBarrier) { - const std::size_t nqubits = 2; + constexpr std::size_t nqubits = 2; QuantumComputation qc(nqubits, nqubits); qc.barrier({0, 1}); qc.measure(0, 0); diff --git a/test/ir/test_qasm3_parser.cpp b/test/ir/test_qasm3_parser.cpp index 1738d2711..003eb5418 100644 --- a/test/ir/test_qasm3_parser.cpp +++ b/test/ir/test_qasm3_parser.cpp @@ -7,7 +7,6 @@ * Licensed under the MIT License */ -#include "Definitions.hpp" #include "ir/QuantumComputation.hpp" #include "ir/operations/ClassicControlledOperation.hpp" #include "ir/operations/OpType.hpp" @@ -30,10 +29,7 @@ using namespace qc; -class Qasm3ParserTest : public testing::TestWithParam { -protected: - void SetUp() override {} -}; +class Qasm3ParserTest : public testing::TestWithParam {}; TEST_F(Qasm3ParserTest, ImportQasm3) { const std::string testfile = "OPENQASM 3.0;\n" From a8d60ca41bdaecc7c919ed3ea8c90486309be9f6 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Tue, 4 Feb 2025 16:21:14 +0100 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=9A=A8=20avoid=20forward=20declaratio?= =?UTF-8?q?n=20of=20class=20member=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- include/mqt-core/qasm3/passes/TypeCheckPass.hpp | 7 ++----- src/qasm3/passes/TypeCheckPass.cpp | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp index 4e3a8be86..83f9fa2ea 100644 --- a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp +++ b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp @@ -9,10 +9,11 @@ #pragma once -#include "CompilerPass.hpp" #include "qasm3/InstVisitor.hpp" #include "qasm3/Statement_fwd.hpp" #include "qasm3/Types.hpp" +#include "qasm3/passes/CompilerPass.hpp" +#include "qasm3/passes/ConstEvalPass.hpp" #include #include @@ -24,10 +25,6 @@ class GateOperand; struct DebugInfo; } // namespace qasm3 -namespace qasm3::const_eval { -class ConstEvalPass; -} - namespace qasm3::type_checking { struct InferredType { bool isError; diff --git a/src/qasm3/passes/TypeCheckPass.cpp b/src/qasm3/passes/TypeCheckPass.cpp index 2ba762225..f1988d8a2 100644 --- a/src/qasm3/passes/TypeCheckPass.cpp +++ b/src/qasm3/passes/TypeCheckPass.cpp @@ -12,7 +12,6 @@ #include "qasm3/Exception.hpp" #include "qasm3/Statement.hpp" #include "qasm3/Types.hpp" -#include "qasm3/passes/ConstEvalPass.hpp" #include #include From 01c6a80001cd09f6e9bef71dd658fd852b32d972 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Tue, 4 Feb 2025 16:23:26 +0100 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=8E=A8=20use=20reference=20in=20pass?= =?UTF-8?q?=20constructor=20over=20pointer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- include/mqt-core/qasm3/passes/TypeCheckPass.hpp | 4 ++-- src/qasm3/Importer.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp index 83f9fa2ea..6a019f9dc 100644 --- a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp +++ b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp @@ -67,8 +67,8 @@ class TypeCheckPass final : public CompilerPass, const std::shared_ptr& debugInfo = nullptr); public: - explicit TypeCheckPass(const_eval::ConstEvalPass* pass) - : constEvalPass(pass) {} + explicit TypeCheckPass(const_eval::ConstEvalPass& pass) + : constEvalPass(&pass) {} ~TypeCheckPass() override = default; diff --git a/src/qasm3/Importer.cpp b/src/qasm3/Importer.cpp index 903b5d576..f98a0855f 100644 --- a/src/qasm3/Importer.cpp +++ b/src/qasm3/Importer.cpp @@ -177,7 +177,7 @@ Importer::evaluatePositiveConstant(const std::shared_ptr& expr, } Importer::Importer(qc::QuantumComputation& quantumComputation) - : typeCheckPass(&constEvalPass), qc(&quantumComputation), + : typeCheckPass(constEvalPass), qc(&quantumComputation), gates(STANDARD_GATES) { for (const auto& [identifier, builtin] : initializeBuiltins()) { constEvalPass.addConst(identifier, builtin.first); From e7a8e15e214be2c8e13b5cb13724709a60b07e19 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 4 Feb 2025 17:49:41 +0100 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=9A=A8=F0=9F=8F=81=20address=20compil?= =?UTF-8?q?er=20warnings=20on=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- include/mqt-core/qasm3/passes/TypeCheckPass.hpp | 2 +- src/algorithms/Grover.cpp | 2 +- src/ir/QuantumComputation.cpp | 5 +++-- src/qasm3/passes/TypeCheckPass.cpp | 2 +- test/zx/test_expression.cpp | 1 - 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp index 6a019f9dc..56675f109 100644 --- a/include/mqt-core/qasm3/passes/TypeCheckPass.hpp +++ b/include/mqt-core/qasm3/passes/TypeCheckPass.hpp @@ -110,7 +110,7 @@ class TypeCheckPass final : public CompilerPass, visitConstantExpression(std::shared_ptr constantInt) override; InferredType visitIdentifierExpression( std::shared_ptr identifierExpression) override; - [[noreturn]] InferredType + InferredType visitIdentifierList(std::shared_ptr identifierList) override; InferredType visitMeasureExpression( std::shared_ptr measureExpression) override; diff --git a/src/algorithms/Grover.cpp b/src/algorithms/Grover.cpp index 96f06a70f..043764b65 100644 --- a/src/algorithms/Grover.cpp +++ b/src/algorithms/Grover.cpp @@ -33,7 +33,7 @@ auto appendGroverOracle(QuantumComputation& qc, const GroverBitString& targetValue) -> void { const auto nDataQubits = static_cast(qc.getNqubits() - 1); Controls controls{}; - for (std::size_t i = 0; i < nDataQubits; ++i) { + for (Qubit i = 0; i < nDataQubits; ++i) { controls.emplace(i, targetValue.test(i) ? Control::Type::Pos : Control::Type::Neg); } diff --git a/src/ir/QuantumComputation.cpp b/src/ir/QuantumComputation.cpp index 4e534dd19..48d668ed7 100644 --- a/src/ir/QuantumComputation.cpp +++ b/src/ir/QuantumComputation.cpp @@ -362,7 +362,8 @@ void QuantumComputation::addQubitRegister(std::size_t nq, "qubits have been added"); } - quantumRegisters.try_emplace(regName, nqubits, nq, regName); + quantumRegisters.try_emplace(regName, static_cast(nqubits), nq, + regName); for (std::size_t i = 0; i < nq; ++i) { auto j = static_cast(nqubits + i); initialLayout.insert({j, j}); @@ -404,7 +405,7 @@ void QuantumComputation::addAncillaryRegister(std::size_t nq, "[addAncillaryRegister] New register size must be larger than 0"); } - const auto totalqubits = nqubits + nancillae; + const auto totalqubits = static_cast(nqubits + nancillae); ancillaRegisters.try_emplace(regName, totalqubits, nq, regName); ancillary.resize(totalqubits + nq); garbage.resize(totalqubits + nq); diff --git a/src/qasm3/passes/TypeCheckPass.cpp b/src/qasm3/passes/TypeCheckPass.cpp index f1988d8a2..6131d5473 100644 --- a/src/qasm3/passes/TypeCheckPass.cpp +++ b/src/qasm3/passes/TypeCheckPass.cpp @@ -324,7 +324,7 @@ InferredType TypeCheckPass::visitIdentifierExpression( return type->second; } -[[noreturn]] InferredType TypeCheckPass::visitIdentifierList( +InferredType TypeCheckPass::visitIdentifierList( std::shared_ptr /*identifierList*/) { throw TypeCheckError("TypeCheckPass::visitIdentifierList not implemented"); } diff --git a/test/zx/test_expression.cpp b/test/zx/test_expression.cpp index 67109a636..1321e103a 100644 --- a/test/zx/test_expression.cpp +++ b/test/zx/test_expression.cpp @@ -211,7 +211,6 @@ TEST_F(ExpressionTest, Arithmetic) { const auto beta = zx::PiRational(1, 2); EXPECT_EQ(beta * 2, zx::PiRational(1, 1)); - EXPECT_EQ(beta * 2., zx::PiRational(1, 1)); const auto betaExpr = zx::PiExpression{beta}; EXPECT_EQ(betaExpr * 2., zx::PiExpression{zx::PiRational(1, 1)});