diff --git a/.agent/plans/typed-compilation-target.md b/.agent/plans/typed-compilation-target.md new file mode 100644 index 0000000000..6d4d03a911 --- /dev/null +++ b/.agent/plans/typed-compilation-target.md @@ -0,0 +1,186 @@ +# Record compiler targets as typed MQT IR + +This ExecPlan is a living document. The sections `Progress`, +`Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must +be kept up to date as work proceeds. + +This ExecPlan must be maintained in accordance with `.agent/PLANS.md` from the +repository root. + +## Purpose / Big Picture + +MQT Core represents a compilation target as the context-free C++ +`mlir::CompilerTarget` value. After this change, clients can also store and +exchange the same immutable target facts as a typed `#mqt.compilation_target` +MLIR attribute. Converting the C++ value to the attribute and back preserves +names, ordered sites, timing data, connectivity, native operations, and fixed or +variadic operation arities. + +The attribute deliberately describes only compiler-target facts. It does not +define a payload format, execution capability, combined target environment, or +module attachment. Those contracts require separate design work. + +## Progress + +- [x] (2026-09-01 05:48Z) Added typed compiler-target and leaf attributes. +- [x] (2026-09-01 05:48Z) Added lossless C++ materialization and reconstruction. +- [x] (2026-09-01 05:48Z) Added parsing, printing, verification, and conversion + tests. +- [x] (2026-09-01 05:48Z) Added generated dialect-documentation examples. +- [x] (2026-09-01 06:16Z) Rebased on the final #2218 head and reran focused MLIR + tests, documentation generation, and repository lint. +- [x] (2026-09-01 06:16Z) Recorded final validation evidence and outcomes. +- [x] (2026-09-01 18:59Z) Restacked the focused commit on the final #2218 target + model. +- [x] (2026-09-01 19:02Z) Updated the attribute model and conversions for + complete target facts, zero-arity operations, and variadic operations. +- [x] (2026-09-01 19:07Z) Reran focused MLIR tests, documentation generation, + C++ lint, and repository lint. + +## Surprises & Discoveries + +- Observation: The original implementation combined compiler-target facts with + payload and DLTI contracts. Evidence: the complete original implementation + required `PayloadEnvAttr`, `TargetEnvAttr`, and `MLIRDLTIDialect`, while the + compiler-target conversion itself uses none of them. +- Observation: The refreshed base requires MLIR 23.1.0. Evidence: the cached + MLIR 22.1.8 configuration failed in QCO control-flow interfaces before any + changed source was compiled; configuring against the installed 23.1.0 + toolchain built every focused target successfully. +- Observation: The final #2218 model treats missing connectivity and operation + support as target-inference errors and gives operations a fixed or variadic + arity. Evidence: `CompilerTarget::Connectivity` and `NativeOperations` have no + unknown state, and `CompilerTarget::Operation::Arity` distinguishes fixed + widths from inclusive variadic minima. + +## Decision Log + +- Decision: Keep only `CompilationTargetAttr` and its leaf attributes. + Rationale: MQT Core 4.0 needs stable compiler-target serialization, while the + payload capability model is still under design. Date/Author: 2026-09-01, Lukas + Burgholzer. +- Decision: Do not define a canonical module attribute in this change. + Rationale: A module attachment would imply ownership and composition rules + that belong to the deferred target-environment design. Date/Author: + 2026-09-01, Lukas Burgholzer. +- Decision: Keep the existing C++ target enums and map them explicitly to the + MQT dialect enums. Rationale: The C++ target API remains independent of the + generated enum types, while the two-state mappings are exhaustive and small. + Date/Author: 2026-09-01, Lukas Burgholzer. +- Decision: Represent operation arity as `OperationArityAttr` with a fixed or + variadic kind and one value. Rationale: Fixed zero represents `gphase`, while + a positive variadic minimum represents a base gate that accepts additional + controls. Variadic and fixed-zero operations cannot have site tuples because + one tuple cannot describe all accepted widths. Date/Author: 2026-09-01, Lukas + Burgholzer. + +## Outcomes & Retrospective + +The focused implementation records compiler-target facts without adding a +payload-capability or DLTI model. The typed target round trip preserves fixed +zero-arity and positive-minimum variadic operations. The MQT dialect and +compiler tests, generated dialect documentation, C++ lint, and repository lint +all pass. + +## Context and Orientation + +`mlir/include/mlir/Compiler/Target.h` declares the immutable C++ target model. +`mlir/lib/Compiler/Target.cpp` validates target data, prepares routing and +synthesis caches, and implements conversions. The MQT dialect is declared in +`mlir/include/mlir/Dialect/MQT/IR/MQTDialect.td` and implemented in +`mlir/lib/Dialect/MQT/IR/MQTDialect.cpp`. TableGen generates the public C++ +attribute and enum declarations from that dialect file. + +A site is a hardware location identified by a target-defined nonnegative +integer. Its position in the site's array defines the compiler's dense vertex. +Connectivity is all-to-all or an explicit set of undirected couplings. +Native-operation support is unrestricted or an explicit list. An explicit +operation has a fixed width, including zero, or a positive variadic minimum. +These facts must survive every conversion without inference. + +## Plan of Work + +Define typed leaf attributes for duration units, sites, couplings, ordered site +tuples, and native operations. Compose them in `CompilationTargetAttr`. Give +each attribute a concise generated-documentation example and verify structural +invariants in `MQTDialect.cpp`. + +Add `CompilerTarget::materialize(MLIRContext&)` and +`CompilerTarget::create(mqt::CompilationTargetAttr)`. The conversion must copy +only source facts, including each operation's arity kind and value. +Reconstruction must use the existing C++ factories so that normal validation and +derived-cache construction remain centralized. + +Test textual parsing and printing in the MQT dialect unit tests. Test C++ to +MLIR to C++ conversion in the compiler-target unit tests, including fixed-zero +and variadic operations, both connectivity kinds, both operation-support kinds, +and invalid reconstructed topology. + +## Concrete Steps + +Run these commands from the repository root: + + cmake --preset release + cmake --build --preset release --target mqt-core-mlir-unittest-mqt-ir + cmake --build --preset release --target mqt-core-mlir-unittests-compiler + ./build/release/mlir/unittests/Dialect/MQT/IR/mqt-core-mlir-unittest-mqt-ir + ./build/release/mlir/unittests/Compiler/mqt-core-mlir-unittests-compiler + cmake --build --preset release --target mlir-doc + uvx nox -s cpp-lint -- origin/codex/generalize-compiler-target + uvx nox -s lint + +The two test binaries must report no failed tests. The documentation and lint +targets must complete successfully. + +## Validation and Acceptance + +Acceptance requires parsing and printing a detailed `#mqt.compilation_target` +attribute without structural change. Materializing a detailed `CompilerTarget`, +reconstructing it from the attribute, and materializing it again must produce +the same attribute. The same round trip must preserve all-to-all or explicit +connectivity, unrestricted or explicit operation support, and fixed or variadic +operation arities. Invalid or disconnected reconstructed targets must retain the +existing C++ diagnostics. + +Generated dialect documentation must show concrete syntax for the composite +attribute and its leaf records. Repository lint must report no formatting, +spelling, metadata, or generated-file problems. + +## Idempotence and Recovery + +All builds and tests are repeatable. CMake writes only below `build/`. If a +generated TableGen declaration is stale, rerun the normal CMake build rather +than editing generated files. No migration or destructive data operation is +required. + +## Artifacts and Notes + +The final diff must not contain payload descriptors, program capabilities, +payload environments, target environments, or an MLIR DLTI dependency. + +Validation evidence from 2026-09-01: + + 14 tests from MQTIRTest (14 passed) + 145 tests from CompilerTest (145 passed) + Built target mlir-doc + uvx nox -s cpp-lint -- origin/codex/generalize-compiler-target: success + uvx nox -s lint: success + +## Interfaces and Dependencies + +The public additions are: + + static llvm::Expected + CompilerTarget::create(mqt::CompilationTargetAttr attribute); + + mqt::CompilationTargetAttr + CompilerTarget::materialize(MLIRContext& context) const; + +The MQT dialect exports `DurationUnitAttr`, `SiteAttr`, `CouplingAttr`, +`SiteTupleAttr`, `OperationArityAttr`, `NativeOperationAttr`, +`CompilationTargetAttr`, `ConnectivityKind`, `NativeOperationsKind`, and +`OperationArityKind`. The implementation uses existing LLVM and MLIR libraries +and adds no third-party dependency. + +Revision note: This plan replaces the broader target-environment design with the +compiler-target-only scope selected for MQT Core 4.0. diff --git a/CHANGELOG.md b/CHANGELOG.md index e921548ba0..f36a9db10a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,8 +25,8 @@ releases may include breaking changes. [#1807], [#1808], [#1815], [#1824], [#1869], [#1872], [#1914], [#1925], [#1927], [#1935], [#1936], [#1938], [#1975], [#1976], [#2006], [#2014], [#2015], [#2017], [#2026], [#2028], [#2054], [#2058], [#2125], [#2136], - [#2149], [#2150], [#2158], [#2194], [#2210], [#2211], [#2220], [#2218], - [#2323]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], + [#2149], [#2150], [#2158], [#2194], [#2210], [#2211], [#2215], [#2218], + [#2220], [#2323]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**], [**@J4MMlE**]) - ✨ Add a library for typed structured quantum benchmarks with versioned @@ -885,6 +885,7 @@ for previous changelogs._ [#2218]: https://github.com/munich-quantum-toolkit/core/pull/2218 [#2217]: https://github.com/munich-quantum-toolkit/core/pull/2217 [#2216]: https://github.com/munich-quantum-toolkit/core/pull/2216 +[#2215]: https://github.com/munich-quantum-toolkit/core/pull/2215 [#2214]: https://github.com/munich-quantum-toolkit/core/pull/2214 [#2211]: https://github.com/munich-quantum-toolkit/core/pull/2211 [#2210]: https://github.com/munich-quantum-toolkit/core/pull/2210 diff --git a/mlir/include/mlir/Compiler/Target.h b/mlir/include/mlir/Compiler/Target.h index 3e5ee9288d..41ac93832d 100644 --- a/mlir/include/mlir/Compiler/Target.h +++ b/mlir/include/mlir/Compiler/Target.h @@ -10,6 +10,8 @@ #pragma once +#include "mlir/Dialect/MQT/IR/MQTAttributes.h" + #include #include #include @@ -26,6 +28,7 @@ namespace mlir { +class MLIRContext; class Operation; /// Immutable description of an MLIR compiler target. @@ -326,6 +329,10 @@ class CompilerTarget { NativeOperations nativeOperations, std::optional durationUnit = std::nullopt); + /// Reconstruct a validated compiler target from its MLIR attribute. + [[nodiscard]] static llvm::Expected + create(mqt::CompilationTargetAttr attribute); + /// Copying shares immutable storage; rvalues copy and keep the source valid. CompilerTarget(const CompilerTarget&) noexcept = default; CompilerTarget& operator=(const CompilerTarget&) noexcept = default; @@ -395,6 +402,10 @@ class CompilerTarget { /// Return one complete globally usable synthesis basis, if available. [[nodiscard]] std::optional synthesisBasis() const noexcept; + /// Materialize the source target facts as a typed MLIR attribute. + [[nodiscard]] mqt::CompilationTargetAttr + materialize(MLIRContext& context) const; + private: struct Storage; diff --git a/mlir/include/mlir/Dialect/MQT/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/MQT/IR/CMakeLists.txt index d0e04e4d20..00c188a889 100644 --- a/mlir/include/mlir/Dialect/MQT/IR/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/MQT/IR/CMakeLists.txt @@ -9,6 +9,10 @@ set(LLVM_TARGET_DEFINITIONS MQTDialect.td) mlir_tablegen(MQTDialect.h.inc -gen-dialect-decls -dialect=mqt) mlir_tablegen(MQTDialect.cpp.inc -gen-dialect-defs -dialect=mqt) +mlir_tablegen(MQTAttributes.h.inc -gen-attrdef-decls -attrdefs-dialect=mqt) +mlir_tablegen(MQTAttributes.cpp.inc -gen-attrdef-defs -attrdefs-dialect=mqt) +mlir_tablegen(MQTEnums.h.inc -gen-enum-decls) +mlir_tablegen(MQTEnums.cpp.inc -gen-enum-defs) add_mlir_dialect_tablegen_target(MLIRMQTDialectIncGen) add_mlir_doc(MQTDialect MQTDialect Dialects/ -gen-dialect-doc -dialect=mqt) diff --git a/mlir/include/mlir/Dialect/MQT/IR/MQTAttributes.h b/mlir/include/mlir/Dialect/MQT/IR/MQTAttributes.h new file mode 100644 index 0000000000..1cdaaefcae --- /dev/null +++ b/mlir/include/mlir/Dialect/MQT/IR/MQTAttributes.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/IR/MQTEnums.h.inc" // IWYU pragma: export + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mlir::mqt::detail { +template +[[nodiscard]] FailureOr> +parseArray(AsmParser& parser) { + llvm::SmallVector elements; + const auto parseElement = [&]() -> ParseResult { + auto element = FieldParser::parse(parser); + if (failed(element)) { + return failure(); + } + elements.emplace_back(std::move(*element)); + return success(); + }; + if (failed(parser.parseCommaSeparatedList(AsmParser::Delimiter::Square, + parseElement))) { + return failure(); + } + return elements; +} + +template +void printArray(AsmPrinter& printer, const llvm::ArrayRef elements) { + printer << '['; + llvm::interleaveComma(elements, printer, [&](const Element element) { + printer.printStrippedAttrOrType(element); + }); + printer << ']'; +} +} // namespace mlir::mqt::detail + +#define GET_ATTRDEF_CLASSES +#include "mlir/Dialect/MQT/IR/MQTAttributes.h.inc" // IWYU pragma: export diff --git a/mlir/include/mlir/Dialect/MQT/IR/MQTDialect.td b/mlir/include/mlir/Dialect/MQT/IR/MQTDialect.td index 2d34f55a38..f5c762b6bc 100644 --- a/mlir/include/mlir/Dialect/MQT/IR/MQTDialect.td +++ b/mlir/include/mlir/Dialect/MQT/IR/MQTDialect.td @@ -9,11 +9,14 @@ #ifndef MLIR_DIALECT_MQT_IR_MQTDIALECT_TD #define MLIR_DIALECT_MQT_IR_MQTDIALECT_TD +include "mlir/IR/AttrTypeBase.td" +include "mlir/IR/EnumAttr.td" include "mlir/IR/OpBase.td" def MQTDialect : Dialect { let name = "mqt"; let cppNamespace = "::mlir::mqt"; + let useDefaultAttributePrinterParser = 1; let summary = "Shared metadata for MQT quantum programs."; let description = [{ @@ -29,6 +32,7 @@ def MQTDialect : Dialect { namespace. `mqt.entry_point` marks the single defined program entry function in a module. + `#mqt.compilation_target` records compiler-target facts as typed IR. }]; let discardableAttrs = (ins "::mlir::StringAttr":$input_name, @@ -40,4 +44,162 @@ def MQTDialect : Dialect { let hasRegionResultAttrVerify = 1; } +class MQTAttr traits = []> + : AttrDef { + let mnemonic = attrMnemonic; +} + +class MQTArrayRefParameter + : ArrayRefParameter { + let parser = "::mlir::mqt::detail::parseArray<"#element#">($_parser)"; + let printer = "::mlir::mqt::detail::printArray($_printer, $_self)"; +} + +class MQTOptionalUInt64Parameter + : OptionalParameter<"std::optional"> { + let parser = "::mlir::FieldParser::parse($_parser)"; +} + +def ConnectivityKind + : I32EnumAttr<"ConnectivityKind", "Target connectivity", + [I32EnumAttrCase<"AllToAll", 0, "all_to_all">, + I32EnumAttrCase<"Explicit", 1, "explicit">]> { + let cppNamespace = "::mlir::mqt"; + let genSpecializedAttr = 0; +} + +def NativeOperationsKind + : I32EnumAttr<"NativeOperationsKind", "Native-operation support", + [I32EnumAttrCase<"Unrestricted", 0, "unrestricted">, + I32EnumAttrCase<"Explicit", 1, "explicit">]> { + let cppNamespace = "::mlir::mqt"; + let genSpecializedAttr = 0; +} + +def OperationArityKind + : I32EnumAttr<"OperationArityKind", "Operation arity kind", + [I32EnumAttrCase<"Fixed", 0, "fixed">, + I32EnumAttrCase<"Variadic", 1, "variadic">]> { + let cppNamespace = "::mlir::mqt"; + let genSpecializedAttr = 0; +} + +def DurationUnitAttr : MQTAttr<"DurationUnit", "duration_unit"> { + let summary = "Unit for raw compiler-target durations"; + let description = [{ + The scale factor converts each raw duration to the named unit. For example, + `#mqt.duration_unit` + records durations in nanoseconds. + }]; + let parameters = (ins "StringAttr":$unit, "FloatAttr":$scale_factor); + let assemblyFormat = "`<` struct(params) `>`"; + let genVerifyDecl = 1; +} + +def SiteAttr : MQTAttr<"Site", "site"> { + let summary = "One ordered compiler-target site"; + let description = [{ + Site identifiers come from the target. The array order defines dense + compiler vertices. For example, `#mqt.site` records + one named site. + }]; + let parameters = (ins "int64_t":$id, OptionalParameter<"StringAttr">:$name, + MQTOptionalUInt64Parameter<>:$t1, MQTOptionalUInt64Parameter<>:$t2); + let assemblyFormat = "`<` struct(params) `>`"; + let genVerifyDecl = 1; +} + +def CouplingAttr : MQTAttr<"Coupling", "coupling"> { + let summary = "One undirected compiler-target coupling"; + let description = [{ + A coupling joins two target site identifiers. For example, + `#mqt.coupling` permits interactions between sites + 4 and 7. + }]; + let parameters = (ins "int64_t":$source, "int64_t":$target); + let assemblyFormat = "`<` struct(params) `>`"; + let genVerifyDecl = 1; +} + +def SiteTupleAttr : MQTAttr<"SiteTuple", "site_tuple"> { + let summary = "Calibration data for an ordered site tuple"; + let description = [{ + The tuple defines one placement of a native operation. For example, + `#mqt.site_tuple` records an ordered + two-site placement with a raw duration of 40. + }]; + let parameters = (ins MQTArrayRefParameter<"int64_t">:$sites, + MQTOptionalUInt64Parameter<>:$duration, + OptionalParameter<"FloatAttr">:$fidelity); + let assemblyFormat = "`<` struct(params) `>`"; + let genVerifyDecl = 1; +} + +def OperationArityAttr : MQTAttr<"OperationArity", "operation_arity"> { + let summary = "Accepted width of a compiler-target operation"; + let description = [{ + A fixed arity accepts exactly its value. A variadic arity accepts its value + or any larger width. For example, + `#mqt.operation_arity` accepts a gate with one + target and any number of controls. + }]; + let parameters = (ins EnumParameter:$kind, + "uint64_t":$value); + let assemblyFormat = "`<` struct(params) `>`"; + let genVerifyDecl = 1; +} + +def NativeOperationAttr : MQTAttr<"NativeOperation", "native_operation"> { + let summary = "One native compiler-target operation"; + let description = [{ + The operation records its spelling, arity, parameter count, and optional + global or site-specific calibration data. The following example records a + placed controlled-X operation: + + ```mlir + #mqt.native_operation, + num_parameters = 0, site_tuples = []> + ``` + }]; + let parameters = (ins "StringAttr":$name, "OperationArityAttr":$arity, + "uint64_t":$num_parameters, + MQTArrayRefParameter<"SiteTupleAttr">:$site_tuples, + MQTOptionalUInt64Parameter<>:$duration, + OptionalParameter<"FloatAttr">:$fidelity); + let assemblyFormat = "`<` struct(params) `>`"; + let genVerifyDecl = 1; +} + +def CompilationTargetAttr : MQTAttr<"CompilationTarget", "compilation_target"> { + let summary = "Typed compiler-target facts"; + let description = [{ + Records source target facts without derived routing or synthesis caches. + Ordered sites define dense compiler vertices. Connectivity and native + operation support is unrestricted or explicit. + + The following target has two explicitly connected sites and one native + operation: + + ```mlir + #mqt.compilation_target< + name = "device", sites = [, ], + connectivity = explicit, couplings = [], + native_operations = explicit, + operations = [, + num_parameters = 0, site_tuples = []>]> + ``` + }]; + let parameters = (ins OptionalParameter<"StringAttr">:$name, + MQTArrayRefParameter<"SiteAttr">:$sites, + OptionalParameter<"DurationUnitAttr">:$duration_unit, + EnumParameter:$connectivity, + MQTArrayRefParameter<"CouplingAttr">:$couplings, + EnumParameter:$native_operations, + MQTArrayRefParameter<"NativeOperationAttr">:$operations); + let assemblyFormat = "`<` struct(params) `>`"; + let genVerifyDecl = 1; +} + #endif // MLIR_DIALECT_MQT_IR_MQTDIALECT_TD diff --git a/mlir/lib/Compiler/CMakeLists.txt b/mlir/lib/Compiler/CMakeLists.txt index 08fa49cf9e..dd6b46da0e 100644 --- a/mlir/lib/Compiler/CMakeLists.txt +++ b/mlir/lib/Compiler/CMakeLists.txt @@ -16,6 +16,7 @@ add_mlir_library( LINK_LIBS PUBLIC MLIRIR + MLIRMQTDialect MLIRQCODialect) mqt_mlir_target_use_project_options(MQTCompilerTarget) diff --git a/mlir/lib/Compiler/Target.cpp b/mlir/lib/Compiler/Target.cpp index 2870c0c100..d72309d422 100644 --- a/mlir/lib/Compiler/Target.cpp +++ b/mlir/lib/Compiler/Target.cpp @@ -10,6 +10,7 @@ #include "mlir/Compiler/Target.h" +#include "mlir/Dialect/MQT/IR/MQTAttributes.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" @@ -19,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -745,6 +748,120 @@ CompilerTarget::create(std::string name, std::vector sites, std::move(nativeOperations), std::move(durationUnit)); } +llvm::Expected +CompilerTarget::create(const mqt::CompilationTargetAttr attribute) { + if (!attribute) { + return invalidTarget("Compiler target attribute must not be null"); + } + if (attribute.getConnectivity() != mqt::ConnectivityKind::Explicit && + !attribute.getCouplings().empty()) { + return invalidTarget( + "Compiler target couplings require explicit connectivity"); + } + if (attribute.getNativeOperations() != mqt::NativeOperationsKind::Explicit && + !attribute.getOperations().empty()) { + return invalidTarget( + "Compiler target operations require explicit native operations"); + } + + std::optional name; + if (const auto nameAttr = attribute.getName()) { + name = nameAttr.getValue().str(); + } + + std::vector sites; + sites.reserve(attribute.getSites().size()); + for (const auto siteAttr : attribute.getSites()) { + std::optional siteName; + if (const auto nameAttr = siteAttr.getName()) { + siteName = nameAttr.getValue().str(); + } + auto site = Site::create(siteAttr.getId(), std::move(siteName), + siteAttr.getT1(), siteAttr.getT2()); + if (!site) { + return site.takeError(); + } + sites.emplace_back(std::move(*site)); + } + + std::optional durationUnit; + if (const auto unitAttr = attribute.getDurationUnit()) { + auto unit = + DurationUnit::create(unitAttr.getUnit().getValue().str(), + unitAttr.getScaleFactor().getValueAsDouble()); + if (!unit) { + return unit.takeError(); + } + durationUnit = std::move(*unit); + } + + std::vector couplings; + if (attribute.getConnectivity() == mqt::ConnectivityKind::Explicit) { + couplings.reserve(attribute.getCouplings().size()); + for (const auto coupling : attribute.getCouplings()) { + couplings.emplace_back(coupling.getSource(), coupling.getTarget()); + } + } + auto connectivity = + attribute.getConnectivity() == mqt::ConnectivityKind::AllToAll + ? Connectivity::allToAll() + : Connectivity::fromCouplings(couplings); + + auto nativeOperations = NativeOperations::unrestricted(); + if (attribute.getNativeOperations() == mqt::NativeOperationsKind::Explicit) { + std::vector operations; + operations.reserve(attribute.getOperations().size()); + for (const auto operationAttr : attribute.getOperations()) { + if (operationAttr.getArity().getValue() > + std::numeric_limits::max() || + operationAttr.getNumParameters() > + std::numeric_limits::max()) { + return invalidTarget( + "Compiler target operation size exceeds the host size domain"); + } + std::vector siteTuples; + siteTuples.reserve(operationAttr.getSiteTuples().size()); + for (const auto tupleAttr : operationAttr.getSiteTuples()) { + std::optional fidelity; + if (const auto fidelityAttr = tupleAttr.getFidelity()) { + fidelity = fidelityAttr.getValueAsDouble(); + } + auto siteTuple = + SiteTuple::create(std::vector(tupleAttr.getSites().begin(), + tupleAttr.getSites().end()), + tupleAttr.getDuration(), fidelity); + if (!siteTuple) { + return siteTuple.takeError(); + } + siteTuples.emplace_back(std::move(*siteTuple)); + } + + std::optional fidelity; + if (const auto fidelityAttr = operationAttr.getFidelity()) { + fidelity = fidelityAttr.getValueAsDouble(); + } + const auto arity = + operationAttr.getArity().getKind() == mqt::OperationArityKind::Fixed + ? Operation::Arity::fixed( + static_cast(operationAttr.getArity().getValue())) + : Operation::Arity::variadic( + static_cast(operationAttr.getArity().getValue())); + auto operation = Operation::create( + operationAttr.getName().getValue().str(), arity, + static_cast(operationAttr.getNumParameters()), + std::move(siteTuples), operationAttr.getDuration(), fidelity); + if (!operation) { + return operation.takeError(); + } + operations.emplace_back(std::move(*operation)); + } + nativeOperations = NativeOperations::fromOperations(operations); + } + + return createImpl(std::move(name), std::move(sites), std::move(connectivity), + std::move(nativeOperations), std::move(durationUnit)); +} + llvm::Expected CompilerTarget::createImpl(std::optional name, std::vector sites, Connectivity connectivity, @@ -929,4 +1046,80 @@ CompilerTarget::synthesisBasis() const noexcept { return storage_->basis; } +mqt::CompilationTargetAttr +CompilerTarget::materialize(MLIRContext& context) const { + Builder builder(&context); + + StringAttr nameAttr; + if (const auto targetName = name()) { + nameAttr = builder.getStringAttr(*targetName); + } + + SmallVector siteAttrs; + siteAttrs.reserve(sites().size()); + for (const auto& site : sites()) { + StringAttr siteNameAttr; + if (const auto siteName = site.name()) { + siteNameAttr = builder.getStringAttr(*siteName); + } + siteAttrs.emplace_back(mqt::SiteAttr::get(&context, site.id(), siteNameAttr, + site.t1(), site.t2())); + } + + mqt::DurationUnitAttr durationUnitAttr; + if (const auto& unit = durationUnit()) { + durationUnitAttr = mqt::DurationUnitAttr::get( + &context, builder.getStringAttr(unit->unit()), + builder.getF64FloatAttr(unit->scaleFactor())); + } + + SmallVector couplingAttrs; + couplingAttrs.reserve(couplings().size()); + for (const auto& [source, target] : couplings()) { + couplingAttrs.emplace_back( + mqt::CouplingAttr::get(&context, source, target)); + } + + SmallVector operationAttrs; + operationAttrs.reserve(operations().size()); + for (const auto& operation : operations()) { + SmallVector siteTupleAttrs; + siteTupleAttrs.reserve(operation.siteTuples().size()); + for (const auto& siteTuple : operation.siteTuples()) { + FloatAttr fidelityAttr; + if (const auto fidelity = siteTuple.fidelity()) { + fidelityAttr = builder.getF64FloatAttr(*fidelity); + } + siteTupleAttrs.emplace_back(mqt::SiteTupleAttr::get( + &context, siteTuple.sites(), siteTuple.duration(), fidelityAttr)); + } + + FloatAttr fidelityAttr; + if (const auto fidelity = operation.fidelity()) { + fidelityAttr = builder.getF64FloatAttr(*fidelity); + } + const auto arityKind = + operation.arity().kind() == Operation::Arity::Kind::Fixed + ? mqt::OperationArityKind::Fixed + : mqt::OperationArityKind::Variadic; + const auto arityAttr = mqt::OperationArityAttr::get( + &context, arityKind, operation.arity().value()); + operationAttrs.emplace_back(mqt::NativeOperationAttr::get( + &context, builder.getStringAttr(operation.name()), arityAttr, + operation.numParameters(), siteTupleAttrs, operation.duration(), + fidelityAttr)); + } + + const auto connectivity = connectivityKind() == Connectivity::Kind::AllToAll + ? mqt::ConnectivityKind::AllToAll + : mqt::ConnectivityKind::Explicit; + const auto nativeOperations = + nativeOperationsKind() == NativeOperations::Kind::Unrestricted + ? mqt::NativeOperationsKind::Unrestricted + : mqt::NativeOperationsKind::Explicit; + return mqt::CompilationTargetAttr::get( + &context, nameAttr, siteAttrs, durationUnitAttr, connectivity, + couplingAttrs, nativeOperations, operationAttrs); +} + } // namespace mlir diff --git a/mlir/lib/Dialect/MQT/IR/CMakeLists.txt b/mlir/lib/Dialect/MQT/IR/CMakeLists.txt index aafd4f293b..d30513cca9 100644 --- a/mlir/lib/Dialect/MQT/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/MQT/IR/CMakeLists.txt @@ -15,6 +15,7 @@ add_mlir_dialect_library( MLIRMQTDialectIncGen LINK_LIBS PRIVATE + LLVMSupport MLIRCBitDialect MLIRFuncDialect MLIRIR diff --git a/mlir/lib/Dialect/MQT/IR/MQTDialect.cpp b/mlir/lib/Dialect/MQT/IR/MQTDialect.cpp index 756e204499..037ee60f49 100644 --- a/mlir/lib/Dialect/MQT/IR/MQTDialect.cpp +++ b/mlir/lib/Dialect/MQT/IR/MQTDialect.cpp @@ -11,10 +11,16 @@ #include "mlir/Dialect/MQT/IR/MQTDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" +#include "mlir/Dialect/MQT/IR/MQTAttributes.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" +#include +#include +#include +#include +#include // IWYU pragma: keep #include #include #include @@ -23,19 +29,253 @@ #include #include #include +#include // IWYU pragma: keep #include #include #include #include +#include +#include +#include #include +#include using namespace mlir; using namespace mlir::mqt; #include "mlir/Dialect/MQT/IR/MQTDialect.cpp.inc" +#include "mlir/Dialect/MQT/IR/MQTEnums.cpp.inc" -void MQTDialect::initialize() {} +void MQTDialect::initialize() { + addAttributes< +#define GET_ATTRDEF_LIST +#include "mlir/Dialect/MQT/IR/MQTAttributes.cpp.inc" + >(); +} + +#define GET_ATTRDEF_CLASSES +#include "mlir/Dialect/MQT/IR/MQTAttributes.cpp.inc" + +LogicalResult +DurationUnitAttr::verify(const function_ref emitError, + const StringAttr unit, const FloatAttr scaleFactor) { + if (unit.getValue().trim().empty()) { + return emitError() << "duration unit must not be empty"; + } + if (!scaleFactor.getType().isF64()) { + return emitError() << "duration scale factor must be an f64 value"; + } + const auto value = scaleFactor.getValueAsDouble(); + if (!std::isfinite(value) || value <= 0.) { + return emitError() << "duration scale factor must be positive and finite"; + } + return success(); +} + +LogicalResult +SiteAttr::verify(const function_ref emitError, + const int64_t id, const StringAttr name, + const std::optional t1, + const std::optional t2) { + if (id < 0) { + return emitError() << "compiler target site ID must be nonnegative"; + } + if (name && name.getValue().empty()) { + return emitError() + << "compiler target site name must not be empty when present"; + } + if (t1 == 0 || t2 == 0) { + return emitError() + << "compiler target site coherence times must be positive"; + } + return success(); +} + +LogicalResult +CouplingAttr::verify(const function_ref emitError, + const int64_t source, const int64_t target) { + if (source < 0 || target < 0) { + return emitError() << "compiler target coupling sites must be nonnegative"; + } + if (source == target) { + return emitError() << "compiler target coupling must join distinct sites"; + } + return success(); +} + +[[nodiscard]] static LogicalResult +verifyFidelity(const function_ref& emitError, + const FloatAttr fidelity, const StringRef description) { + if (!fidelity) { + return success(); + } + if (!fidelity.getType().isF64()) { + return emitError() << description << " must be an f64 value"; + } + const auto value = fidelity.getValueAsDouble(); + if (!std::isfinite(value) || value < 0. || value > 1.) { + return emitError() << description << " must be finite and in [0, 1]"; + } + return success(); +} + +LogicalResult +SiteTupleAttr::verify(const function_ref emitError, + const ArrayRef sites, + const std::optional /*duration*/, + const FloatAttr fidelity) { + llvm::SmallDenseSet seen; + seen.reserve(sites.size()); + for (const int64_t site : sites) { + if (site < 0) { + return emitError() + << "compiler target site tuple contains a negative site ID"; + } + if (!seen.insert(site).second) { + return emitError() + << "compiler target site tuple contains a duplicate site"; + } + } + return verifyFidelity(emitError, fidelity, + "compiler target site-tuple fidelity"); +} + +LogicalResult +OperationArityAttr::verify(const function_ref emitError, + const OperationArityKind kind, + const uint64_t value) { + if (kind == OperationArityKind::Variadic && value == 0) { + return emitError() + << "compiler target operation variadic minimum must be positive"; + } + return success(); +} + +LogicalResult NativeOperationAttr::verify( + const function_ref emitError, const StringAttr name, + const OperationArityAttr arity, const uint64_t /*numParameters*/, + const ArrayRef siteTuples, + const std::optional /*duration*/, const FloatAttr fidelity) { + if (name.getValue().trim().empty()) { + return emitError() << "compiler target operation name must not be empty"; + } + if (failed(verifyFidelity(emitError, fidelity, + "compiler target operation fidelity"))) { + return failure(); + } + + if (!siteTuples.empty() && arity.getKind() == OperationArityKind::Variadic) { + return emitError() + << "compiler target variadic operation cannot contain site tuples"; + } + if (!siteTuples.empty() && arity.getValue() == 0) { + return emitError() + << "compiler target zero-arity operation cannot contain site tuples"; + } + + SmallVector> seen; + seen.reserve(siteTuples.size()); + for (const SiteTupleAttr siteTuple : siteTuples) { + if (siteTuple.getSites().size() != arity.getValue()) { + return emitError() + << "compiler target operation site tuple does not match its arity"; + } + if (llvm::is_contained(seen, siteTuple.getSites())) { + return emitError() + << "compiler target operation contains a duplicate site tuple"; + } + seen.emplace_back(siteTuple.getSites()); + } + return success(); +} + +LogicalResult CompilationTargetAttr::verify( + const function_ref emitError, const StringAttr name, + const ArrayRef sites, const DurationUnitAttr durationUnit, + const ConnectivityKind connectivity, const ArrayRef couplings, + const NativeOperationsKind nativeOperations, + const ArrayRef operations) { + if (name && name.getValue().empty()) { + return emitError() << "compiler target name must not be empty when present"; + } + if (sites.empty()) { + return emitError() << "compiler target must contain at least one site"; + } + + llvm::SmallDenseSet siteIds; + siteIds.reserve(sites.size()); + for (const SiteAttr site : sites) { + if (!siteIds.insert(site.getId()).second) { + return emitError() << "compiler target contains duplicate site IDs"; + } + } + + if (connectivity != ConnectivityKind::Explicit && !couplings.empty()) { + return emitError() + << "compiler target couplings require explicit connectivity"; + } + if (connectivity == ConnectivityKind::Explicit) { + llvm::SmallDenseSet> seen; + for (const CouplingAttr coupling : couplings) { + auto source = coupling.getSource(); + auto target = coupling.getTarget(); + if (!siteIds.contains(source) || !siteIds.contains(target)) { + return emitError() + << "compiler target coupling references an unknown site"; + } + if (target < source) { + std::swap(source, target); + } + if (!seen.insert({source, target}).second) { + return emitError() << "compiler target contains a duplicate coupling"; + } + } + } + + if (nativeOperations != NativeOperationsKind::Explicit && + !operations.empty()) { + return emitError() + << "compiler target operations require explicit native operations"; + } + for (const NativeOperationAttr operation : operations) { + if (operation.getArity().getValue() > sites.size()) { + if (operation.getArity().getKind() == OperationArityKind::Variadic) { + return emitError() << "compiler target operation variadic minimum " + "exceeds its site count"; + } + return emitError() << "compiler target operation arity exceeds its site " + "count"; + } + for (const SiteTupleAttr siteTuple : operation.getSiteTuples()) { + if (llvm::any_of(siteTuple.getSites(), [&](const int64_t site) { + return !siteIds.contains(site); + })) { + return emitError() << "compiler target operation site tuple references " + "an unknown site"; + } + } + } + + const bool hasTiming = + llvm::any_of(sites, + [](const SiteAttr site) { + return site.getT1().has_value() || + site.getT2().has_value(); + }) || + llvm::any_of(operations, [](const NativeOperationAttr operation) { + return operation.getDuration().has_value() || + llvm::any_of(operation.getSiteTuples(), + [](const SiteTupleAttr siteTuple) { + return siteTuple.getDuration().has_value(); + }); + }); + if (hasTiming && !durationUnit) { + return emitError() + << "compiler target timing metadata requires a duration unit"; + } + return success(); +} [[nodiscard]] static LogicalResult verifyEntryPoint(Operation* operation, const NamedAttribute attribute) { diff --git a/mlir/unittests/Compiler/test_compiler_target.cpp b/mlir/unittests/Compiler/test_compiler_target.cpp index 03b8d78349..2955d69e0e 100644 --- a/mlir/unittests/Compiler/test_compiler_target.cpp +++ b/mlir/unittests/Compiler/test_compiler_target.cpp @@ -9,6 +9,8 @@ */ #include "mlir/Compiler/Target.h" +#include "mlir/Dialect/MQT/IR/MQTAttributes.h" +#include "mlir/Dialect/MQT/IR/MQTDialect.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" @@ -388,6 +390,71 @@ TEST(CompilerTargetTest, PreservesCalibrationAndResolvesHomogeneousBasis) { EXPECT_EQ(target.synthesisBasis()->entangler, GateKind::CZ); } +TEST(CompilerTargetTest, RoundTripsTypedCompilationTargetAttribute) { + mlir::MLIRContext context; + context.loadDialect(); + + std::vector sites{valid(Site::create(7, "left", 100, 80)), + valid(Site::create(2, std::nullopt, 120, std::nullopt)), + valid(Site::create(11, "right"))}; + std::vector operations{ + valid( + Operation::create(" PRX ", 1, 2, + std::vector{valid(SiteTuple::create({7}, 0, 0.99)), + valid(SiteTuple::create({2}, 5, 0.98))}, + 0, 0.97)), + valid(Operation::create("gphase", Arity::fixed(0), 1)), + valid(Operation::create("h", Arity::variadic(1), 0))}; + const auto target = + valid(Target::create("device", std::move(sites), + Connectivity::fromCouplings({{7, 2}, {2, 11}}), + NativeOperations::fromOperations(operations), + valid(DurationUnit::create("ns", 0.5)))); + + const auto attribute = target.materialize(context); + const auto reconstructed = valid(Target::create(attribute)); + + EXPECT_EQ(reconstructed.materialize(context), attribute); + EXPECT_EQ(reconstructed.couplings(), target.couplings()); + EXPECT_EQ(reconstructed.supportsOperation("r", 1, 2), true); + EXPECT_EQ(reconstructed.supportsOperation("gphase", 0, 1), true); + EXPECT_EQ(reconstructed.supportsOperation("h", 3, 0), true); + EXPECT_EQ(reconstructed.operations()[1].arity(), Arity::fixed(0)); + EXPECT_EQ(reconstructed.operations()[2].arity(), Arity::variadic(1)); + EXPECT_EQ(reconstructed.synthesisBasis(), target.synthesisBasis()); +} + +TEST(CompilerTargetTest, RoundTripsSupportedTargetStates) { + mlir::MLIRContext context; + context.loadDialect(); + + const std::array targets{ + valid(Target::create(2, Connectivity::allToAll(), + NativeOperations::unrestricted())), + valid(Target::create(2, Connectivity::fromCouplings({{0, 1}}), + NativeOperations::fromOperations({}))), + }; + + for (const auto& target : targets) { + const auto attribute = target.materialize(context); + const auto reconstructed = valid(Target::create(attribute)); + EXPECT_EQ(reconstructed.materialize(context), attribute); + } + + expectInvalid(Target::create(mlir::mqt::CompilationTargetAttr{}), + "Compiler target attribute must not be null"); + + const auto site = + mlir::mqt::SiteAttr::get(&context, 0, {}, std::nullopt, std::nullopt); + const auto secondSite = + mlir::mqt::SiteAttr::get(&context, 1, {}, std::nullopt, std::nullopt); + expectInvalid(Target::create(mlir::mqt::CompilationTargetAttr::get( + &context, {}, {site, secondSite}, {}, + mlir::mqt::ConnectivityKind::Explicit, {}, + mlir::mqt::NativeOperationsKind::Unrestricted, {})), + "Compiler target topology must be connected"); +} + TEST(CompilerTargetTest, ClassifiesEveryEntangler) { using Entangler = std::tuple; const std::array entanglers{Entangler{GateKind::CZ, "cz", 0}, diff --git a/mlir/unittests/Dialect/MQT/IR/test_mqt_ir.cpp b/mlir/unittests/Dialect/MQT/IR/test_mqt_ir.cpp index 363efcbd48..63edc58e5c 100644 --- a/mlir/unittests/Dialect/MQT/IR/test_mqt_ir.cpp +++ b/mlir/unittests/Dialect/MQT/IR/test_mqt_ir.cpp @@ -14,16 +14,21 @@ */ #include "mlir/Dialect/CBit/IR/CBitDialect.h" +#include "mlir/Dialect/MQT/IR/MQTAttributes.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include +#include +#include #include #include #include +#include #include +#include #include #include #include @@ -31,6 +36,7 @@ #include #include +#include using namespace mlir; @@ -51,6 +57,17 @@ class MQTIRTest : public ::testing::Test { [[nodiscard]] OwningOpRef parse(const StringRef source) const { return parseSourceString(source, context.get()); } + + [[nodiscard]] Attribute parseAttr(const StringRef source) const { + return parseAttribute(source, context.get()); + } + + [[nodiscard]] Attribute roundTrip(const Attribute attribute) const { + std::string printed; + llvm::raw_string_ostream stream(printed); + attribute.print(stream); + return parseAttr(printed); + } }; TEST_F(MQTIRTest, AcceptsProgramInputAndRegisterNames) { @@ -85,6 +102,155 @@ TEST_F(MQTIRTest, AcceptsProgramInputAndRegisterNames) { )mlir")); } +TEST_F(MQTIRTest, RoundTripsTypedCompilationTarget) { + const auto compilationTarget = + dyn_cast_if_present( + parseAttr(R"mlir(#mqt.compilation_target< + name = "device", + sites = [, ], + connectivity = explicit, + couplings = [], + native_operations = explicit, + operations = [ + , + num_parameters = 0, site_tuples = []>, + , + num_parameters = 1, site_tuples = []>, + , + num_parameters = 0, site_tuples = []>]>)mlir")); + ASSERT_TRUE(compilationTarget); + EXPECT_EQ(compilationTarget.getName().getValue(), "device"); + ASSERT_EQ(compilationTarget.getSites().size(), 2U); + EXPECT_EQ(compilationTarget.getSites()[0].getId(), 4); + EXPECT_EQ(compilationTarget.getSites()[1].getId(), 7); + EXPECT_EQ(compilationTarget.getConnectivity(), + mqt::ConnectivityKind::Explicit); + EXPECT_EQ(compilationTarget.getNativeOperations(), + mqt::NativeOperationsKind::Explicit); + ASSERT_EQ(compilationTarget.getOperations().size(), 3U); + EXPECT_EQ(compilationTarget.getOperations()[0].getArity().getKind(), + mqt::OperationArityKind::Fixed); + EXPECT_EQ(compilationTarget.getOperations()[1].getArity().getValue(), 0U); + EXPECT_EQ(compilationTarget.getOperations()[2].getArity().getKind(), + mqt::OperationArityKind::Variadic); + EXPECT_TRUE( + compilationTarget.getOperations().front().getSiteTuples().empty()); + + EXPECT_EQ(roundTrip(compilationTarget), compilationTarget); +} + +TEST_F(MQTIRTest, RepresentsUnrestrictedTargetFacts) { + const auto unrestricted = dyn_cast_if_present( + parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, + couplings = [], native_operations = unrestricted, operations = []>)mlir")); + ASSERT_TRUE(unrestricted); + EXPECT_EQ(unrestricted.getConnectivity(), mqt::ConnectivityKind::AllToAll); + EXPECT_EQ(unrestricted.getNativeOperations(), + mqt::NativeOperationsKind::Unrestricted); +} + +TEST_F(MQTIRTest, RejectsInvalidTargetLeaves) { + EXPECT_FALSE(parseAttr(R"mlir(#mqt.duration_unit)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.duration_unit)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.duration_unit)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.site)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.site)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.site)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.site)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.site)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.coupling)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.site_tuple)mlir")); + EXPECT_FALSE( + parseAttr(R"mlir(#mqt.site_tuple)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.site_tuple)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.operation_arity< + kind = variadic, value = 0>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.native_operation, + num_parameters = 0, site_tuples = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.native_operation, + num_parameters = 1, site_tuples = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.native_operation, + num_parameters = 0, site_tuples = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.native_operation, + num_parameters = 0, site_tuples = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.native_operation, + num_parameters = 0, + site_tuples = [, ]>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.native_operation, + num_parameters = 0, site_tuples = [], duration =>)mlir")); +} + +TEST_F(MQTIRTest, RejectsInvalidCompilationTargets) { + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + name = "", sites = [], connectivity = all_to_all, + couplings = [], native_operations = unrestricted, operations = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, couplings = [], + native_operations = unrestricted, operations = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [, ], connectivity = all_to_all, couplings = [], + native_operations = unrestricted, operations = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [, ], connectivity = all_to_all, + couplings = [], + native_operations = unrestricted, operations = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, couplings = [], + native_operations = unrestricted, + operations = [, + num_parameters = 0, site_tuples = []>]>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [, ], connectivity = explicit, + couplings = [], + native_operations = unrestricted, operations = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [, ], connectivity = explicit, + couplings = [, ], + native_operations = unrestricted, operations = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, + couplings = [], native_operations = unrestricted, operations = []>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, couplings = [], + native_operations = explicit, + operations = [, + num_parameters = 0, site_tuples = []>]>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, couplings = [], + native_operations = explicit, + operations = [, + num_parameters = 0, site_tuples = []>]>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, couplings = [], + native_operations = explicit, + operations = [, + num_parameters = 0, site_tuples = []>]>)mlir")); + EXPECT_FALSE(parseAttr(R"mlir(#mqt.compilation_target< + sites = [], connectivity = all_to_all, couplings = [], + native_operations = explicit, + operations = [, + num_parameters = 0, site_tuples = [], duration = 1>]>)mlir")); +} + TEST_F(MQTIRTest, ManagesAndFindsEntryPoint) { auto moduleOp = parse(R"mlir( module {