diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp b/include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp index b5a9372fb..2eb08900d 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp +++ b/include/mqt-core/ir/parsers/qasm3_parser/Scanner.hpp @@ -32,6 +32,8 @@ class Scanner { return isNum(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } + [[nodiscard]] static bool hasTimingSuffix(char first, char second); + static char readUtf8Codepoint(std::istream* in); void nextCh(); diff --git a/include/mqt-core/ir/parsers/qasm3_parser/Token.hpp b/include/mqt-core/ir/parsers/qasm3_parser/Token.hpp index 1e027ec79..05b37b0c0 100644 --- a/include/mqt-core/ir/parsers/qasm3_parser/Token.hpp +++ b/include/mqt-core/ir/parsers/qasm3_parser/Token.hpp @@ -142,14 +142,6 @@ struct Token { Underscore, - TimeUnitDt, - TimeUnitNs, - TimeUnitUs, - TimeUnitMys, - TimeUnitMs, - // might be either TimeUnitS or the `s` gate - S, - DoubleQuote, SingleQuote, BackSlash, @@ -160,6 +152,7 @@ struct Token { StringLiteral, IntegerLiteral, FloatLiteral, + TimingLiteral, Sin, Cos, @@ -401,18 +394,6 @@ struct Token { return "imag"; case Kind::Underscore: return "underscore"; - case Kind::TimeUnitDt: - return "dt"; - case Kind::TimeUnitNs: - return "ns"; - case Kind::TimeUnitUs: - return "us"; - case Kind::TimeUnitMys: - return "mys"; - case Kind::TimeUnitMs: - return "ms"; - case Kind::S: - return "s"; case Kind::DoubleQuote: return "\""; case Kind::SingleQuote: @@ -429,6 +410,8 @@ struct Token { return "IntegerLiteral"; case Kind::FloatLiteral: return "FloatLiteral"; + case Kind::TimingLiteral: + return "TimingLiteral"; case Kind::Sin: return "sin"; case Kind::Cos: @@ -473,6 +456,9 @@ struct Token { case Kind::FloatLiteral: ss << " (" << valReal << ")"; break; + case Kind::TimingLiteral: + ss << " (" << valReal << " [s])"; + break; default: break; } diff --git a/src/ir/parsers/qasm3_parser/Parser.cpp b/src/ir/parsers/qasm3_parser/Parser.cpp index aa9a2e37c..c6ce0e57a 100644 --- a/src/ir/parsers/qasm3_parser/Parser.cpp +++ b/src/ir/parsers/qasm3_parser/Parser.cpp @@ -153,8 +153,7 @@ std::shared_ptr Parser::parseQuantumStatement() { current().kind == Token::Kind::Ctrl || current().kind == Token::Kind::NegCtrl || current().kind == Token::Kind::Identifier || - current().kind == Token::Kind::Gphase || - current().kind == Token::Kind::S) { + current().kind == Token::Kind::Gphase) { // TODO: since we do not support classical function calls yet, we can assume // that this is a gate statement return parseGateCallStatement(); @@ -385,9 +384,6 @@ std::shared_ptr Parser::parseGateCallStatement() { scan(); identifier = "gphase"; operandsOptional = true; - } else if (current().kind == Token::Kind::S) { - scan(); - identifier = "s"; } else { identifier = expect(Token::Kind::Identifier).str; } diff --git a/src/ir/parsers/qasm3_parser/Scanner.cpp b/src/ir/parsers/qasm3_parser/Scanner.cpp index 8128c5e8a..bfc3c6612 100644 --- a/src/ir/parsers/qasm3_parser/Scanner.cpp +++ b/src/ir/parsers/qasm3_parser/Scanner.cpp @@ -1,7 +1,9 @@ #include "ir/parsers/qasm3_parser/Scanner.hpp" +#include "Definitions.hpp" #include "ir/parsers/qasm3_parser/Token.hpp" +#include #include #include #include @@ -9,6 +11,8 @@ #include #include #include +#include +#include namespace qasm3 { char Scanner::readUtf8Codepoint(std::istream* in) { @@ -225,16 +229,40 @@ Token Scanner::consumeNumberLiteral() { if (negative) { t.valReal *= -1; } - - return t; + } else { + t.val = static_cast( + parseIntegerLiteral(valBeforeDecimalSeparator, base)); + t.kind = Token::Kind::IntegerLiteral; + if (negative) { + t.val *= -1; + t.isSigned = true; + } } - t.val = static_cast( - parseIntegerLiteral(valBeforeDecimalSeparator, base)); - t.kind = Token::Kind::IntegerLiteral; - if (negative) { - t.val *= -1; - t.isSigned = true; + const auto suffix1 = ch; + const auto suffix2 = peek(); + if (hasTimingSuffix(suffix1, suffix2)) { + double factor = 1.0; + nextCh(); + if (suffix1 != 's' && (suffix1 != 'd' || suffix2 != 't')) { + nextCh(); + const auto suffix = std::string{suffix1, suffix2}; + if (suffix == "ms") { + factor = 1e-3; + } else if (suffix == "us") { + factor = 1e-6; + } else if (suffix == "ns") { + factor = 1e-9; + } else if (suffix == "ps") { + factor = 1e-12; + } + } + if (t.kind == Token::Kind::FloatLiteral) { + t.valReal *= factor; + } else { + t.valReal = static_cast(t.val) * factor; + } + t.kind = Token::Kind::TimingLiteral; } t.endCol = col; @@ -345,12 +373,6 @@ Scanner::Scanner(std::istream* in) : is(in) { keywords["true"] = Token::Kind::True; keywords["false"] = Token::Kind::False; keywords["im"] = Token::Kind::Imag; - keywords["dt"] = Token::Kind::TimeUnitDt; - keywords["ns"] = Token::Kind::TimeUnitNs; - keywords["us"] = Token::Kind::TimeUnitUs; - keywords["mys"] = Token::Kind::TimeUnitMys; - keywords["ms"] = Token::Kind::TimeUnitMs; - keywords["s"] = Token::Kind::S; keywords["sin"] = Token::Kind::Sin; keywords["cos"] = Token::Kind::Cos; keywords["tan"] = Token::Kind::Tan; @@ -603,4 +625,16 @@ Token Scanner::next() { t.endLine = line; return t; } + +bool Scanner::hasTimingSuffix(const char first, const char second) { + if (first == 's') { + return true; + } + const auto suffixes = std::vector>{ + {'m', 's'}, {'u', 's'}, {'n', 's'}, {'p', 's'}, {'d', 't'}}; + return std::any_of(suffixes.begin(), suffixes.end(), + [first, second](const auto& suffix) { + return suffix.first == first && suffix.second == second; + }); +} } // namespace qasm3 diff --git a/src/mqt/core/__init__.py b/src/mqt/core/__init__.py index b85e55805..c6723ffc1 100644 --- a/src/mqt/core/__init__.py +++ b/src/mqt/core/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from os import PathLike +import os from pathlib import Path from typing import TYPE_CHECKING, Union @@ -14,7 +14,7 @@ from qiskit.circuit import QuantumCircuit """The type of input that can be used to load a quantum circuit.""" - CircuitInputType = Union[QuantumComputation, str, PathLike[str], QuantumCircuit] + CircuitInputType = Union[QuantumComputation, str, os.PathLike[str], QuantumCircuit] def load(input_circuit: CircuitInputType) -> QuantumComputation: @@ -33,15 +33,17 @@ def load(input_circuit: CircuitInputType) -> QuantumComputation: if isinstance(input_circuit, QuantumComputation): return input_circuit - if isinstance(input_circuit, (str, PathLike)): - if not Path(input_circuit).is_file(): - if isinstance(input_circuit, PathLike) or "OPENQASM" not in input_circuit: + if isinstance(input_circuit, (str, os.PathLike)): + input_str = str(input_circuit) + max_filename_length = 255 if os.name == "nt" else os.pathconf("/", "PC_NAME_MAX") + if len(input_str) > max_filename_length or not Path(input_circuit).is_file(): + if isinstance(input_circuit, os.PathLike) or "OPENQASM" not in input_circuit: msg = f"File {input_circuit} does not exist." raise FileNotFoundError(msg) # otherwise, we assume that this is a QASM string - return QuantumComputation.from_qasm(str(input_circuit)) + return QuantumComputation.from_qasm(input_str) - return QuantumComputation(str(input_circuit)) + return QuantumComputation(input_str) try: from .plugins.qiskit import qiskit_to_mqt diff --git a/test/ir/test_qasm3_parser.cpp b/test/ir/test_qasm3_parser.cpp index 2f97771c7..a64775fe5 100644 --- a/test/ir/test_qasm3_parser.cpp +++ b/test/ir/test_qasm3_parser.cpp @@ -754,6 +754,34 @@ TEST_F(Qasm3ParserTest, ImportMQTBenchCircuit) { EXPECT_EQ(out, expected); } +TEST_F(Qasm3ParserTest, ImportMSGate) { + const std::string testfile = "OPENQASM 3.0;" + "qubit[3] q;" + "bit[3] c;" + "gate ms(p0) q0, q1, q2 {" + " rxx(p0) q0, q1;" + " rxx(p0) q0, q2;" + " rxx(p0) q1, q2;" + "}" + "ms(0.844396) q[0], q[1], q[2];" + "c = measure q;"; + + auto qc = QuantumComputation::fromQASM(testfile); + + const std::string out = qc.toQASM(); + const std::string expected = "// i 0 1 2\n" + "// o 0 1 2\n" + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "qubit[3] q;\n" + "bit[3] c;\n" + "rxx(0.844396) q[0], q[1];\n" + "rxx(0.844396) q[0], q[2];\n" + "rxx(0.844396) q[1], q[2];\n" + "c = measure q;\n"; + EXPECT_EQ(out, expected); +} + TEST_F(Qasm3ParserTest, ImportQasm2CPrefixInvalidGate) { const std::string testfile = "OPENQASM 2.0;\n" "qubit[5] q;\n" @@ -1644,12 +1672,6 @@ TEST_F(Qasm3ParserTest, TestPrintTokens) { qasm3::Token(qasm3::Token::Kind::RightShift, 0, 0), qasm3::Token(qasm3::Token::Kind::Imag, 0, 0), qasm3::Token(qasm3::Token::Kind::Underscore, 0, 0), - qasm3::Token(qasm3::Token::Kind::TimeUnitDt, 0, 0), - qasm3::Token(qasm3::Token::Kind::TimeUnitNs, 0, 0), - qasm3::Token(qasm3::Token::Kind::TimeUnitUs, 0, 0), - qasm3::Token(qasm3::Token::Kind::TimeUnitMys, 0, 0), - qasm3::Token(qasm3::Token::Kind::TimeUnitMs, 0, 0), - qasm3::Token(qasm3::Token::Kind::S, 0, 0), qasm3::Token(qasm3::Token::Kind::DoubleQuote, 0, 0), qasm3::Token(qasm3::Token::Kind::SingleQuote, 0, 0), qasm3::Token(qasm3::Token::Kind::BackSlash, 0, 0), @@ -1658,6 +1680,7 @@ TEST_F(Qasm3ParserTest, TestPrintTokens) { qasm3::Token(qasm3::Token::Kind::StringLiteral, 0, 0, "hello, world"), qasm3::Token(qasm3::Token::Kind::IntegerLiteral, 0, 0), qasm3::Token(qasm3::Token::Kind::FloatLiteral, 0, 0), + qasm3::Token(qasm3::Token::Kind::TimingLiteral, 0, 0), qasm3::Token(qasm3::Token::Kind::Sin, 0, 0), qasm3::Token(qasm3::Token::Kind::Cos, 0, 0), qasm3::Token(qasm3::Token::Kind::Tan, 0, 0), @@ -1781,12 +1804,6 @@ TEST_F(Qasm3ParserTest, TestPrintTokens) { ">>\n" "imag\n" "underscore\n" - "dt\n" - "ns\n" - "us\n" - "mys\n" - "ms\n" - "s\n" "\"\n" "'\n" "\\\n" @@ -1796,6 +1813,7 @@ TEST_F(Qasm3ParserTest, TestPrintTokens) { "StringLiteral (\"hello, world\")\n" "IntegerLiteral (0)\n" "FloatLiteral (0)\n" + "TimingLiteral (0 [s])\n" "sin\n" "cos\n" "tan\n" @@ -2168,3 +2186,58 @@ TEST_F(Qasm3ParserTest, TestConstEval) { EXPECT_EQ(result, expected); } } + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralSeconds) { + qasm3::Scanner scanner(new std::istringstream("1.0s")); + const auto token = scanner.next(); + EXPECT_EQ(token.kind, qasm3::Token::Kind::TimingLiteral); + EXPECT_DOUBLE_EQ(token.valReal, 1.0); +} + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralMilliseconds) { + qasm3::Scanner scanner(new std::istringstream("1.0ms")); + const auto token = scanner.next(); + EXPECT_EQ(token.kind, qasm3::Token::Kind::TimingLiteral); + EXPECT_DOUBLE_EQ(token.valReal, 1.0e-3); +} + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralMicroseconds) { + qasm3::Scanner scanner(new std::istringstream("1.0us")); + const auto token = scanner.next(); + EXPECT_EQ(token.kind, qasm3::Token::Kind::TimingLiteral); + EXPECT_DOUBLE_EQ(token.valReal, 1.0e-6); +} + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralNanoseconds) { + qasm3::Scanner scanner(new std::istringstream("1.0ns")); + const auto token = scanner.next(); + EXPECT_EQ(token.kind, qasm3::Token::Kind::TimingLiteral); + EXPECT_DOUBLE_EQ(token.valReal, 1.0e-9); +} + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralPicoseconds) { + qasm3::Scanner scanner(new std::istringstream("1.0ps")); + const auto token = scanner.next(); + EXPECT_EQ(token.kind, qasm3::Token::Kind::TimingLiteral); + EXPECT_DOUBLE_EQ(token.valReal, 1.0e-12); +} + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralDoubleSuffix) { + qasm3::Scanner scanner(new std::istringstream("1.0dt")); + const auto token = scanner.next(); + EXPECT_EQ(token.kind, qasm3::Token::Kind::TimingLiteral); + EXPECT_DOUBLE_EQ(token.valReal, 1.0); +} + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralInvalidSuffix) { + qasm3::Scanner scanner(new std::istringstream("1.0xs")); + const auto token = scanner.next(); + EXPECT_NE(token.kind, qasm3::Token::Kind::TimingLiteral); +} + +TEST_F(Qasm3ParserTest, TokenKindTimingLiteralMicrosecondsInteger) { + qasm3::Scanner scanner(new std::istringstream("1us")); + const auto token = scanner.next(); + EXPECT_EQ(token.kind, qasm3::Token::Kind::TimingLiteral); + EXPECT_DOUBLE_EQ(token.valReal, 1.0e-6); +}