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
182 changes: 182 additions & 0 deletions .agent/plans/directional-gate-mapping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Compile directional target gates through native synthesis

This ExecPlan follows `.agent/PLANS.md` and records the supported contract,
implementation, and validation for ordered compiler-target applicability.

## Purpose / Big Picture

Compile gates for devices that support an entangler in only one operand order.
Routing makes operands adjacent; native synthesis repairs their direction and
final conformance checks exact physical sites. Alternating CX directions on two
adjacent sites must not introduce routing SWAPs.

## Progress

- [x] (2026-09-03) Preserve ordered operation support and calibration metadata.
- [x] (2026-09-04) Remove directional routing and its dedicated wrapper.
- [x] (2026-09-04) Replace ambiguous-site analysis with a checked staged walk.
- [x] (2026-09-04) Remove whole-module cloning for failed synthesis.
- [x] (2026-09-04) Add focused regressions and align public pass documentation.
- [x] (2026-09-04) Apply specialist, adversarial, and Ponytail Review feedback.
- [x] (2026-09-04) Pass 303 focused tests, documentation, and repository lint.
- [x] (2026-09-04) Attempt full C++ lint and analyze changed sources directly;
record the unrelated build blocker below.
- [x] (2026-09-04) Unify site tuples across the target, attributes, QDMI, and
Python.
- [x] (2026-09-04) Remove synthesis planning and repeated matrix extraction.
- [x] (2026-09-04) Validate the revised model and obtain adversarial review.
- [x] (2026-09-04) Accept plain Python placements and positional MLIR tuple
sites.
- [x] (2026-09-04) Consolidate target lookups and restore LLVM containers.
- [ ] (2026-09-04) Validate compact syntax and the final synthesis
simplification.

## Decision Log

On 2026-09-03 the maintainer approved adjacency-only routing. Direction repair
belongs to synthesis. Weighted routing edges remain possible future work; there
is no current need for an extra cost wrapper.

On 2026-09-03 the maintainer approved requiring one known physical site per
quantum value, equal branch-result sites, and site-preserving loop backedges.
These conditions are checked, including for all-to-all placement and standalone
passes. Ordinary structured control flow remains supported.

On 2026-09-03 the maintainer approved removing synthesis rollback. Compilation
runs in place; callers must not rely on program contents after failure. Generic
capability tuples and constant-time ordered-pair support queries remain part of
the target model.

On 2026-09-04 the maintainer approved one `site_tuples` list and no
applicability enum. An empty list means general applicability; a nonempty list
contains every supported ordered placement with optional calibration. Missing
values inherit operation defaults. The QDMI adapter omits operations reported
with no supported placements and retains uncalibrated supported tuples.

Plain Python tuples and lists denote uncalibrated placements. Explicit
`SiteTuple` values remain available for calibration. MLIR prints positional
sites as `<[4, 7]>`, with named optional calibration fields.

## Surprises & Discoveries

An executed two-site probe produced five native CXs with directional routing and
two with direct synthesis. The extra SWAP is avoidable. Another valid-IR probe
passed a site through `scf.execute_region`; name-only fallback incorrectly
accepted a reversed CX. Unknown site transfers must fail with a diagnostic.

Explicit mapping realigns structured region exits to physical slots. All-to-all
placement only replaces allocations, so site consistency must be checked rather
than assumed. Runtime symmetric gates such as RXX need direct operand reordering
because their matrix is unavailable at compile time.

LLVM 23 dense maps track occupancy separately and no longer reserve sentinel
keys. Standard LLVM dense containers therefore support the full nonnegative
site-ID range without custom traits. One per-operation tuple set borrows keys
from immutable target storage and replaces arity-specific lookup caches.

## Context and Orientation

`mlir/lib/Compiler/Target.cpp` owns immutable target capabilities and basis
selection. A usable synthesis basis supplies one-qubit gates on every site and
an entangler on every routing edge in at least one direction. Each supported
site tuple may carry calibration overrides.

`mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp` performs placement and
routing. `mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/TargetSynthesis.cpp`
then assigns exact sites, preserves or reorders native gates, and decomposes
other supported gates. Its conformance pass checks emitted capabilities.

## Plan of Work

First remove `mlir/include/mlir/Compiler/MappingTarget.h`, its implementation
and dedicated tests, and restore topology-only mapping and build wiring. Prove
that alternating CXs need no routing SWAPs and only two native entanglers.

Next use MLIR's staged operation walk and one site map. Propagate sites through
unitaries, reset, and measurement, seed supported region arguments, and compare
branch results and loop backedges. Reject unknown or conflicting sites. Remove
the module clone and duplicate planning. Preserve matrix/output permutation for
directional synthesis and direct symmetric operand reordering.

Finally update `docs/mlir/target_compilation.md`, pass descriptions, and the
existing changelog entry. Keep regression tests in the established native
synthesis and compiler test suites. Obtain independent reviews after the first
implementation, then incorporate the separate Ponytail Review findings.

## Concrete Steps and Validation

Run from the repository root with the configured LLVM/MLIR 23 installation:

cmake --preset release
cmake --build --preset release --target mqt-core-mlir-unittests-compiler mqt-core-mlir-unittest-mapping mqt-core-mlir-unittest-target-synthesis mqt-core-mlir-unittest-mqt-ir -j 8
build/release/mlir/unittests/Compiler/mqt-core-mlir-unittests-compiler
build/release/mlir/unittests/Dialect/QCO/Transforms/Mapping/mqt-core-mlir-unittest-mapping
build/release/mlir/unittests/Dialect/QCO/Transforms/NativeSynthesis/mqt-core-mlir-unittest-target-synthesis
build/release/mlir/unittests/Dialect/MQT/IR/mqt-core-mlir-unittest-mqt-ir
uvx nox -s stubs
SKBUILD_CMAKE_ARGS=-DBUILD_MQT_CORE_QDMI_SC_DEVICE=ON uvx nox -s tests-3.13 -- test/python/test_mlir.py -q
uvx nox -s cpp-lint
uvx nox --non-interactive -s docs
git diff --check
uvx nox -s lint

Successful output must verify, retain exact ordered target applicability and
quantum semantics, and support consistent if/switch/for/while site transfers.
Unknown sites, conflicting branch exits, and changing loop-backedge sites must
be diagnosed. Failed compilation need not preserve input IR. C++, Python, and
serialized targets use only `site_tuples` for ordered availability and
calibration.

The tuple simplification removes duplicate lists, attributes, and validation
from C++, MLIR, QDMI, and Python. Native synthesis processes users before their
producers, keeping original site facts valid while rewriting each operation
immediately. Use the existing bounded site walk: generic control-flow interfaces
prune known loop edges and require extra exceptions for this contract.

Fusion also visits operations in reverse order. When a run head fuses its
successors, those operations have already been visited. This removes the
run-head snapshot and duplicate matrix extraction, and can expose earlier
cancellations when a later run disappears. Each rewrite still strictly reduces
the number of two-qubit operations.

## Idempotence and Recovery

Builds and checks are repeatable. Preserve unrelated changes and keep generated
build output untracked. No dependency additions or generated-file edits are
needed.

## Outcomes & Retrospective

The target model now has one tuple list with optional calibration. Its enum,
duplicate lists, attributes, validators, and serialization paths are removed.
Synthesis checks and rewrites each gate in reverse order, without a separate
plan or repeated matrix extraction. The tuple-model round removed 284 production
lines. The compact-syntax and lookup round removes another 91 production lines:
a shared LLVM tuple cache, fewer single-use wrappers, and direct reverse fusion.
Python accepts plain tuples or lists; explicit `SiteTuple` values add
calibration. MLIR prints positional tuple sites.

Specialist and adversarial review found no remaining blockers. Adversarial
review retained a compact shared matrix guard for unsupported multi-target
control shells; its regression verifies that the input is valid and linear
before checking the diagnostic. Ordinary dependent rewrites retain semantic
equivalence.

All 307 focused C++ tests pass: compiler 153, mapping 94, target synthesis 44,
and MQT IR 16. All 51 Python MLIR tests pass. Python stubs are regenerated, and
strict documentation and repository lint pass. Full C++ lint stops before
analysis because unchanged QIR runtime test executables have unresolved QTensor
symbols. Building the ten changed C++ translation units directly succeeds; the
same whole-file linter reports zero findings across all ten files. No lint
configuration or unrelated build wiring was changed.

The final specialist, adversarial, and Ponytail reviews found no further useful
deletion within the supported contract. The new fusion regression cancels
`CX01, CX02, CX02, CX01` and checks decision-diagram equivalence. Compact syntax
checks cover mixed calibrated placements and calibration roundtrips. Cache
checks cover maximum site IDs and retained target copies. The initial Python run
reused a package without the test device; rebuilding with
`BUILD_MQT_CORE_QDMI_SC_DEVICE=ON` resolves both device fixture errors.

Revision note: aligned the scope with the approved routing, site, and failure
contracts while retaining exact device metadata.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ 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, and target
compilation through C++, Python, and `mqt-cc` ([#1687], [#1993], [#1999],
[#2049]) ([**@MatthiasReumann**], [**@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**])

#### Import and export

Expand Down Expand Up @@ -889,6 +890,7 @@ for previous changelogs._
[#2315]: https://github.com/munich-quantum-toolkit/core/pull/2315
[#2299]: https://github.com/munich-quantum-toolkit/core/pull/2299
[#2298]: https://github.com/munich-quantum-toolkit/core/pull/2298
[#2285]: https://github.com/munich-quantum-toolkit/core/pull/2285
[#2284]: https://github.com/munich-quantum-toolkit/core/pull/2284
[#2283]: https://github.com/munich-quantum-toolkit/core/pull/2283
[#2288]: https://github.com/munich-quantum-toolkit/core/pull/2288
Expand Down
21 changes: 16 additions & 5 deletions bindings/mlir/register_mlir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ either unrestricted or explicitly enumerated native-operation support.)pb");

auto siteTuple = nb::class_<mlir::CompilerTarget::SiteTuple>(
compilerTarget, "SiteTuple",
"Calibration data for an ordered tuple of target sites.");
"A supported ordered placement with optional calibration.");
siteTuple
.def(
"__init__",
Expand All @@ -555,6 +555,9 @@ either unrestricted or explicitly enumerated native-operation support.)pb");
.def_prop_ro("fidelity", &mlir::CompilerTarget::SiteTuple::fidelity,
"The operation fidelity, if available.");

nb::implicitly_convertible<std::vector<mlir::CompilerTarget::SiteId>,
mlir::CompilerTarget::SiteTuple>();

nb::enum_<mlir::CompilerTarget::Operation::Arity::Kind>(
compilerTarget, "OperationArityKind",
"How an operation capability accepts qubit widths.")
Expand All @@ -580,7 +583,8 @@ either unrestricted or explicitly enumerated native-operation support.)pb");

auto targetOperation = nb::class_<mlir::CompilerTarget::Operation>(
compilerTarget, "Operation",
"A homogeneous target-wide operation capability and its calibration.");
"A target operation capability, calibration, and ordered "
"applicability.");
targetOperation
.def(
"__init__",
Expand Down Expand Up @@ -644,7 +648,8 @@ either unrestricted or explicitly enumerated native-operation support.)pb");
return std::vector<mlir::CompilerTarget::SiteTuple>(
operation.siteTuples().begin(), operation.siteTuples().end());
},
"Ordered site-specific calibration data.")
"Supported ordered placements with optional calibration; empty means "
"general applicability.")
.def_prop_ro("duration", &mlir::CompilerTarget::Operation::duration,
"The raw default duration, if available.")
.def_prop_ro("fidelity", &mlir::CompilerTarget::Operation::fidelity,
Expand Down Expand Up @@ -905,11 +910,17 @@ either unrestricted or explicitly enumerated native-operation support.)pb");
.def(
"supports_operation",
[](const mlir::CompilerTarget& target, const std::string_view name,
const size_t arity, const std::optional<size_t> numParameters) {
const size_t arity, const std::optional<size_t> numParameters,
const std::optional<std::vector<mlir::CompilerTarget::SiteId>>&
sites) {
if (sites) {
return target.supportsOperation(name, arity, numParameters,
*sites);
}
return target.supportsOperation(name, arity, numParameters);
},
"name"_a, "arity"_a, "num_parameters"_a = nb::none(),
"Whether the target supports an operation.");
"sites"_a = nb::none(), "Whether the target supports an operation.");

auto program = nb::class_<mlir::Program>(
m, "Program", R"pb(Base class for a typed MLIR compiler program.
Expand Down
2 changes: 1 addition & 1 deletion bindings/patterns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ mqt\.core\.mlir\.CompilerTarget\.Operation\.__init__$:
name: str,
arity: int | CompilerTarget.OperationArity,
num_parameters: int,
site_tuples: Sequence[CompilerTarget.SiteTuple] | None = None,
site_tuples: Sequence[CompilerTarget.SiteTuple | Sequence[int]] | None = None,
duration: int | None = None,
fidelity: float | None = None,
) -> None:
Expand Down
39 changes: 30 additions & 9 deletions docs/mlir/target_compilation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

An MLIR {code}`mlir::CompilerTarget` is an immutable snapshot of a circuit-model
device. It contains the device sites, topology, native operations, and available
calibration data. Compilation decomposes supported multi-qubit operations,
optimizes and maps the program, synthesizes native gates, and verifies that the
result conforms to the target.
calibration and ordered-applicability data. Compilation decomposes supported
multi-qubit operations, optimizes and maps the program, synthesizes native
gates, and verifies that the result conforms to the target.

The snapshot is independent of its originating QDMI session. It can therefore be
stored, copied cheaply, and reused for multiple compilations.
Expand Down Expand Up @@ -41,7 +41,12 @@ target = CompilerTarget(
num_parameters=1,
),
CompilerTarget.Operation("u", arity=1, num_parameters=3),
CompilerTarget.Operation("cx", arity=2, num_parameters=0),
CompilerTarget.Operation(
"cx",
arity=2,
num_parameters=0,
site_tuples=[(1, 0), (1, 2)],
),
CompilerTarget.Operation("measure", arity=1, num_parameters=0),
CompilerTarget.Operation("reset", arity=1, num_parameters=0),
]),
Expand All @@ -58,10 +63,26 @@ not provide a complete connectivity model and a representable native-operation
set. An explicit operation arity is either fixed or variadic with a positive,
inclusive minimum. Fixed zero represents a global-phase operation. A variadic
capability accepts every total width from its minimum through the target's site
count; site-specific calibration tuples are therefore available only for fixed,
positive arities. Structural and program-format constructs are not
count; site tuples are therefore available only for fixed, positive arities. An
empty `site_tuples` list makes an operation available on every valid placement.
A nonempty list contains all supported ordered placements. Each tuple may carry
calibration values; omitted values inherit the operation-wide defaults. Retain
placements without calibration in this list, and omit operations that are not
available anywhere. Structural and program-format constructs are not
compiler-target operations.

Use plain tuples for placements without calibration. Use
`CompilerTarget.SiteTuple([1, 0], duration=40, fidelity=0.99)` to attach
calibration to a placement; both forms can appear in the same list.

Routing uses undirected adjacency; native synthesis repairs unsupported operand
directions. Target compilation requires a known static physical site for each
qubit. Structured branch exits must agree on sites, and loop backedges must
preserve the entry sites. Unsupported or inconsistent site transfers are
diagnosed, including after all-to-all placement. A synthesis basis must provide
the same one-qubit gate family on every site and an entangler on every routing
edge in at least one direction.

Target synthesis preserves a native `gphase`. If the target does not support
`gphase`, target synthesis preserves relative phase effects and removes only the
unobservable global phase of the entry point.
Expand Down Expand Up @@ -136,9 +157,9 @@ if (!qco || !qco->compileForTarget(*target)) {
}
```

The adapter accepts circuit-model devices whose operations are available
throughout the topology in both operand orientations. Operand-symmetric gates,
such as CZ, may report each edge once. Operations with arity above two must
The adapter accepts circuit-model devices whose two-qubit operations cover every
topology edge in at least one operand orientation and preserves the exact
ordered tuples reported by the device. Operations with arity above two must
report every ordered tuple of distinct sites. Neutral-atom zone models require a
different compilation model and are rejected with a diagnostic.

Expand Down
6 changes: 4 additions & 2 deletions mlir/include/mlir/Compiler/QDMIAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ namespace mlir {
*
* @details The returned target owns all queried metadata and remains valid
* after the originating device and session have been destroyed. Neutral-atom
* zone models and site-dependent operation support are not supported by the
* circuit-model compiler pipeline.
* zone models are not supported. Explicit QDMI site lists must cover every
* site for one-qubit operations, every undirected topology edge for two-qubit
* operations, and every ordered tuple of distinct sites for higher arities.
* Each supported ordered placement carries optional calibration data.
*/
[[nodiscard]] llvm::Expected<CompilerTarget>
compilerTargetFromDevice(const qdmi::Device& device);
Expand Down
Loading
Loading