Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .agent/plans/selected-payload-target-environment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Independent compiler capability prototype

Status: implemented.

## Scope and release boundary

Core #2219 owns the compiler-only representation of selected program execution
capabilities and the corresponding target environment. It has no dependency on
the QDMI 1.4 adoption branch. Core PR #2162 adds control-flow legalization. Core
PR #2227 is the separate QDMI integration layer. Settled compiler-target and
typed attribute support already landed through #2218, #2323, and #2215.

The compiler-only payload model targets Core 4.0. Core #2365 and QDMI #523 track
the separate QDMI 1.4 adaptation for Core 4.1 and do not gate this prototype.
Rebase mechanics do not settle format identity, operation sets, execution
guarantees, classical capabilities, or opaque-program semantics.

## Preserved behavior

Target inference rejects unknown topology or gate sets. Retain fixed and
variadic operation arities, arbitrary controlled DDSIM gates, and zero-arity
global phase. Retain current QCO linearity checks, reusable-function boundaries,
and SDK input support. Do not reintroduce superseded target-inference commits.

The canonical pipeline keeps target-aware decomposition and deterministic
placement for all-to-all connectivity, with routing only for explicit graphs.
Mapping, native synthesis, and conformance consume the validated module
environment through MLIR's analysis manager. Placement and decomposition retain
their current target-taking factories. The pipeline builder receives the
selected environment, attaches it at entry, and seeds the analysis with its
prepared target. Standalone passes decode the module attribute on demand. The
environment is immutable during compilation. The typed pair has no unused DLTI
extension or query layer.

## Implementation

The prototype types and cached analysis live in
`mlir/Compiler/TargetEnvironment.h` and `TargetEnvironment.cpp`; typed metadata
belongs to the MQT dialect. `mlir/lib/Compiler/Pipeline.cpp` owns pipeline
execution. Keep `Programs.cpp` focused on the program representation.

Bindings and `mqt-cc` accept a selected environment, while untargeted output
selection stays separate. No unreleased QDMI APIs or provider SDK dependencies
are introduced. Capability records remain prototype vocabulary until the design
tracker settles their semantics.

## Validation

Run the independent release build, compiler/mapping/synthesis/MQT IR tests,
command-line checks, Python compiler and QDMI regressions, generated stubs,
lint, and C++ lint. Explicitly cover missing environments, invalidation after
metadata changes, variadic gates and global phase, unsupported output without
consuming input, and preservation of current linearity checks.

The prior capability-snapshot validation passed the release build and 3,879
native tests, with one existing optional-device skip. All 558 targeted Python
tests passed with the superconducting reference device enabled. Stub generation
and C++ lint passed.

## Audit decisions and validation

The selected environment is the pipeline's only input. Its initialization pass
attaches the typed pair and seeds the analysis with the prepared immutable
target; standalone passes decode IR on demand. Regression tests cover shared
target storage, cache retention and invalidation, and replacement of stale
metadata. The unused DLTI extension and query layer is removed.

The optimized native build passed all 3,204 configured tests, with one existing
optional-device skip. Repository lint passed. C++ lint covers whole changed
files in the PR diff.
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ releases may include breaking changes.
direct lowering and dense-array helpers for supported compiler inputs
([#1915], [#1973], [#2077], [#2078], [#2079], [#2334]) ([**@simon1hofmann**],
[**@burgholzer**])
- ✨ Add immutable MLIR compiler targets, QDMI device integration, ordered
operation applicability, directional native synthesis, and target compilation
through C++, Python, and `mqt-cc` ([#1687], [#1993], [#1999], [#2049],
[#2285]) ([**@MatthiasReumann**], [**@simon1hofmann**], [**@burgholzer**])
- ✨ Add immutable MLIR compiler targets, selected payload specifications, QDMI
device integration, ordered operation applicability, directional native
synthesis, and target compilation through C++, Python, and `mqt-cc` ([#2285],
[#2219], [#2049], [#1999], [#1993], [#1687]) ([**@MatthiasReumann**],
[**@simon1hofmann**], [**@burgholzer**])

#### Import and export

Expand Down Expand Up @@ -964,6 +965,7 @@ for previous changelogs._
[#2228]: https://github.com/munich-quantum-toolkit/core/pull/2228
[#2224]: https://github.com/munich-quantum-toolkit/core/pull/2224
[#2220]: https://github.com/munich-quantum-toolkit/core/pull/2220
[#2219]: https://github.com/munich-quantum-toolkit/core/pull/2219
[#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
Expand Down
130 changes: 119 additions & 11 deletions bindings/mlir/register_mlir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "mlir/Compiler/Programs.h"
#include "mlir/Compiler/QDMIAdapter.h"
#include "mlir/Compiler/Target.h"
#include "mlir/Compiler/TargetEnvironment.h"
#include "mlir/Dialect/MQT/IR/MQTDialect.h"
#include "mlir/Dialect/QCO/Utils/DDFunctionality.h"
#include "mlir/bench/Generate.h"
Expand Down Expand Up @@ -310,12 +311,26 @@ programFromInput(const nb::object& program, const bool inplace) {
/// Run the coordinated default pipeline and return a typed program.
[[nodiscard]] static mlir::CompilerProgram
compileProgram(const nb::object& program, const mlir::ProgramFormat output,
const bool inplace, const mlir::CompilerTarget* const target,
const std::string& qcoPipeline, const bool enableTiming,
const bool enableStatistics) {
const bool inplace, const std::string& qcoPipeline,
const bool enableTiming, const bool enableStatistics) {
return takeResult(mlir::runDefaultPipeline(programFromInput(program, inplace),
output, target, qcoPipeline,
enableTiming, enableStatistics));
output, qcoPipeline, enableTiming,
enableStatistics));
}

/// Compile for one target environment and return its selected payload.
[[nodiscard]] static mlir::CompilerProgram
compileProgramForTarget(const nb::object& program, const bool inplace,
const mlir::TargetEnvironment& environment,
const bool enableTiming, const bool enableStatistics) {
auto output = environment.payloadSpecification().compilerOutput();
if (!output) {
const auto message = llvm::toString(output.takeError());
throw nb::value_error(message.c_str());
}
return takeResult(mlir::runDefaultPipeline(programFromInput(program, inplace),
environment, enableTiming,
enableStatistics));
}

template <class Function>
Expand Down Expand Up @@ -475,6 +490,70 @@ NB_MODULE(MQT_CORE_MODULE_NAME, m) {
.value("QIR_ADAPTIVE", mlir::ProgramFormat::QIRAdaptive,
"QIR for the Adaptive Profile.");

nb::enum_<mlir::PayloadEncoding>(m, "PayloadEncoding",
"Payload representation encoding.")
.value("TEXT", mlir::PayloadEncoding::Text)
.value("BINARY", mlir::PayloadEncoding::Binary);

nb::class_<mlir::PayloadFormat>(m, "PayloadFormat", "Exact payload identity.")
.def(nb::init<std::string, std::string, std::string,
mlir::PayloadEncoding>(),
"format_id"_a, "version"_a, "profile"_a = "",
"encoding"_a = mlir::PayloadEncoding::Text)
.def_rw("format_id", &mlir::PayloadFormat::id)
.def_rw("version", &mlir::PayloadFormat::version)
.def_rw("profile", &mlir::PayloadFormat::profile)
.def_rw("encoding", &mlir::PayloadFormat::encoding);

nb::class_<mlir::ProgramConstraint>(m, "ProgramConstraint",
"One payload capability constraint.")
.def(nb::init<std::string, uint64_t>(), "constraint_id"_a, "value"_a)
.def_rw("constraint_id", &mlir::ProgramConstraint::id)
.def_rw("value", &mlir::ProgramConstraint::value);

nb::class_<mlir::ProgramCapability>(m, "ProgramCapability",
"One payload execution capability.")
.def(nb::init<std::string, uint64_t,
std::vector<mlir::ProgramConstraint>>(),
"capability_id"_a, "value"_a = 0,
"constraints"_a = std::vector<mlir::ProgramConstraint>{})
.def_rw("capability_id", &mlir::ProgramCapability::id)
.def_rw("value", &mlir::ProgramCapability::value)
.def_rw("constraints", &mlir::ProgramCapability::constraints);

nb::class_<mlir::PayloadSpecification>(m, "PayloadSpecification",
"Selected payload execution contract.")
.def(
"__init__",
[](mlir::PayloadSpecification& self, mlir::PayloadFormat format,
std::vector<mlir::ProgramCapability> capabilities,
const bool optionalCapabilitiesKnown) {
constructFromExpected(self, mlir::PayloadSpecification::create(
std::move(format),
std::move(capabilities),
optionalCapabilitiesKnown));
},
"payload_format"_a,
"capabilities"_a = std::vector<mlir::ProgramCapability>{},
"optional_capabilities_known"_a = false)
.def_prop_ro(
"format",
[](const mlir::PayloadSpecification& environment) {
return environment.format();
},
"The exact selected payload format.")
.def_prop_ro(
"capabilities",
[](const mlir::PayloadSpecification& environment) {
return std::vector<mlir::ProgramCapability>(
environment.capabilities().begin(),
environment.capabilities().end());
},
"The effective payload capabilities.")
.def_prop_ro("optional_capabilities_known",
&mlir::PayloadSpecification::optionalCapabilitiesKnown,
"Whether optional capability metadata is complete.");

auto compilerTarget = nb::class_<mlir::CompilerTarget>(
m, "CompilerTarget", R"pb(Immutable MLIR compiler target.

Expand Down Expand Up @@ -927,6 +1006,17 @@ either unrestricted or explicitly enumerated native-operation support.)pb");
"name"_a, "arity"_a, "num_parameters"_a = nb::none(),
"sites"_a = nb::none(), "Whether the target supports an operation.");

nb::class_<mlir::TargetEnvironment>(
m, "TargetEnvironment",
"A compiler target and its selected payload specification.")
.def(nb::init<mlir::CompilerTarget, mlir::PayloadSpecification>(),
"target"_a, "payload_specification"_a)
.def_prop_ro("target", &mlir::TargetEnvironment::target,
"The compiler target.")
.def_prop_ro("payload_specification",
&mlir::TargetEnvironment::payloadSpecification,
"The selected payload specification.");

auto program = nb::class_<mlir::Program>(
m, "Program", R"pb(Base class for a typed MLIR compiler program.

Expand Down Expand Up @@ -1139,7 +1229,7 @@ operations.)pb");
"must be at least 3; default 3 means wider than two-qubit).")
.def("compile_for_target",
&BooleanMemberAdapter<&mlir::QCOProgram::compileForTarget>::call,
"target"_a, nb::kw_only(), "enable_timing"_a = false,
"target_environment"_a, nb::kw_only(), "enable_timing"_a = false,
"enable_statistics"_a = false,
"Compile this QCO program for the target in place. Do not rely on "
"its contents if compilation fails.")
Expand Down Expand Up @@ -1355,8 +1445,8 @@ contracts.)pb");

m.def("compile_program", &compileProgram, "program"_a, nb::kw_only(),
"output"_a = mlir::ProgramFormat::QC, "inplace"_a = false,
"target"_a = nb::none(), "qco_pipeline"_a = "mqt-qco-default",
"enable_timing"_a = false, "enable_statistics"_a = false,
"qco_pipeline"_a = "mqt-qco-default", "enable_timing"_a = false,
"enable_statistics"_a = false,
Comment thread
burgholzer marked this conversation as resolved.
R"pb(
Run the coordinated default MQT compiler pipeline.

Expand All @@ -1370,16 +1460,34 @@ directly to construct a custom pipeline stage by stage.
program: Source text, a file path, a Qiskit circuit, or a typed compiler program.
output: The requested output stage of the compiler pipeline.
inplace: Whether a typed input program may be consumed.
target: An optional compiler target for decomposition, mapping, and native
synthesis. A target requires optimized QCO, QC, or QIR output.
qco_pipeline: The QCO optimization pipeline to run. A custom pipeline
cannot be combined with a target.
cannot be combined with target compilation.
enable_timing: Whether to collect pass timing information.
enable_statistics: Whether to collect pass statistics.

Returns:
A typed compiler program for the requested output format.
)pb");

m.def("compile_program", &compileProgramForTarget, "program"_a, nb::kw_only(),
"inplace"_a = false, "target_environment"_a, "enable_timing"_a = false,
"enable_statistics"_a = false,
R"pb(
Compile a program for a target and return the selected executable payload.

The payload specification determines the output format. Typed program inputs
are copied by default; set ``inplace=True`` to consume them.

Args:
program: Source text, a file path, a Qiskit circuit, or a typed compiler program.
inplace: Whether a typed input program may be consumed.
target_environment: The compiler target and selected payload specification.
enable_timing: Whether to collect pass timing information.
enable_statistics: Whether to collect pass statistics.

Returns:
A typed compiler program for the selected payload format.
)pb");
}

} // namespace mqt
21 changes: 16 additions & 5 deletions bindings/patterns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ mqt.core.mlir.compile_program:
*,
output: Literal[OutputFormat.QC, OutputFormat.QC_IMPORT] = ...,
inplace: bool = False,
target: CompilerTarget | None = None,
qco_pipeline: str = "mqt-qco-default",
enable_timing: bool = False,
enable_statistics: bool = False,
Expand All @@ -185,7 +184,6 @@ mqt.core.mlir.compile_program:
*,
output: Literal[OutputFormat.QCO, OutputFormat.QCO_OPTIMIZED],
inplace: bool = False,
target: CompilerTarget | None = None,
qco_pipeline: str = "mqt-qco-default",
enable_timing: bool = False,
enable_statistics: bool = False,
Expand Down Expand Up @@ -218,7 +216,6 @@ mqt.core.mlir.compile_program:
*,
output: Literal[OutputFormat.JEFF],
inplace: bool = False,
target: CompilerTarget | None = None,
qco_pipeline: str = "mqt-qco-default",
enable_timing: bool = False,
enable_statistics: bool = False,
Expand All @@ -235,7 +232,6 @@ mqt.core.mlir.compile_program:
*,
output: Literal[OutputFormat.QIR_BASE, OutputFormat.QIR_ADAPTIVE],
inplace: bool = False,
target: CompilerTarget | None = None,
qco_pipeline: str = "mqt-qco-default",
enable_timing: bool = False,
enable_statistics: bool = False,
Expand All @@ -252,9 +248,24 @@ mqt.core.mlir.compile_program:
*,
output: OutputFormat,
inplace: bool = False,
target: CompilerTarget | None = None,
qco_pipeline: str = "mqt-qco-default",
enable_timing: bool = False,
enable_statistics: bool = False,
) -> QCProgram | QCOProgram | OpenQASMProgram | JeffProgram | QIRProgram:
\doc
@overload
def compile_program(
program: str
| os.PathLike[str]
| qiskit.circuit.QuantumCircuit
| QCProgram
| QCOProgram
| JeffProgram
| OpenQASMProgram,
*,
inplace: bool = False,
target_environment: TargetEnvironment,
enable_timing: bool = False,
enable_statistics: bool = False,
) -> OpenQASMProgram | QIRProgram:
\doc
14 changes: 14 additions & 0 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,25 @@ compiler target
a compiler pipeline may use for one destination. It is a snapshot used for
compilation, not a live device connection.

target environment
A compiler target paired with the selected payload specification for one
compilation. It combines hardware facts with the selected output contract.

selected payload specification
The exact format, encoding, and effective execution capabilities selected for
a compiled program. It does not describe every format accepted by a device.

payload
The program IR on which a transform, schedule, or target-specific action
operates. Use a more specific term when the exact object, such as a function
or circuit, matters.

execution payload
The serialized program submitted for execution. This is distinct from the
MLIR transform dialect's payload IR. A payload format identifies its
representation; execution capabilities state what that representation may
contain for the selected target.

static
Known while compiling the program. Static does not necessarily mean a C++
object with static storage duration.
Expand Down
Loading
Loading