Skip to content

♻️ Propagate native failures through explicit results and diagnostics - #2545

Open
burgholzer wants to merge 12 commits into
mainfrom
codex/fix-mlir-exception-boundaries
Open

burgholzer wants to merge 12 commits into
mainfrom
codex/fix-mlir-exception-boundaries

Conversation

@burgholzer

@burgholzer burgholzer commented Sep 12, 2026 •

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Description

Represent recoverable native failures with MLIR/LLVM LogicalResult and FailureOr<T> instead of exceptions crossing compiler/runtime boundaries. Scoped thread-local diagnostics retain messages, severity, error categories, and original QDMI status codes. Common boundary adapters preserve Python exception categories and C status codes.

MQT::CoreSupport follows the normal static/shared build setting. Default static builds embed private diagnostic support in the driver and devices, without a support DLL dependency. QDMI C interfaces carry status codes and keep messages in local logging; no C extensions are added. Direct C++ driver calls can copy the first error into an optional caller-owned Diagnostic output. Explicitly shared Core builds retain their configured shared dependencies.

Stack: main → #2229 → #2373 → #2545. Native multi-program submission, concurrent reusable DDSIM workers, cancellation, compact DD results, and installed worker staging are provided by #2373. This PR adapts their fallible native operations and carries diagnostics over the worker transport. Worker failure affects its assigned program; completed siblings remain readable and execution is never replayed automatically.

LLVM/MLIR 23.1+ is required for source builds and C++ consumers. All builds include the compiler; bundled QDMI devices remain independently selectable and opt-in for embedded consumers. Remove compiler-disabled presets/CI and reuse LLVM's SHA-256 and unreachable facilities.

Infallible operations return ordinary values or void, including benchmark metadata, owned DD operations, measurement probabilities/deterministic collapse, and QIR state transfer/growth. Native reference decrements require balanced ownership; measurement requires a qubit present in the state. Python retains boundary checks. Benchmark evaluation shares probability calculations and typed gate paths avoid repeated arity checks.

Private JSON translation units parse valid input once and contain dependency exceptions locally. Duplicate-key, schema, range, UTF-8, and source-diagnostic validation remain. GCC and Clang compile native algorithms without exception handling; MSVC retains its normal STL configuration. QIR allocation failures use explicit error-output pointers and release partial allocations; unhandled runtime failures stop execution within the existing worker boundary.

For C++ callers, use factories for fallible construction and check failed(result) before dereferencing. ScopedDiagnosticHandler captures diagnostics within its library; use the optional Diagnostic* output on driver entry points to obtain failure details across a C++ library boundary. Direct C++ consumers require a compatible C++ ABI. Successful absence remains std::optional<T>; borrowed results use pointers. Python signatures and QDMI status codes remain stable. Current-main Shor/W-state APIs, DD scaling/precision, structured compilation, and the replaceable-driver ownership/discovery contracts are retained.

Validation

  • Local release native suite: 3,975 passed and one expected unsupported SC query skip.
  • Affected Python suites (benchmarks, DDs, QDMI, and MLIR): 934 passed. All 432 Python QDMI cases passed again after the final provider export changes.
  • All 61 focused diagnostic, registry, and configuration checks passed. The installed C++ consumer and repository lint passed. Earlier binding changes have regenerated stubs.
  • Local macOS symbol/dependency inspection confirms that the default driver and both devices neither depend on a support dylib nor export the embedded diagnostic handlers. Existing platform CI covers the other platforms.
  • A small two-library experiment showed that consuming llvm::Error across isolated static LLVM copies aborts because their error type identities differ. The implementation retains FailureOr/LogicalResult and copies diagnostic data at the C++ boundary instead.
  • Hosted native and Python tests, coverage, and Slurm checks passed on def3512ac across the configured platforms. Its C++ lint initializer finding and two missing Doxygen parameter descriptions are fixed in the follow-up. All four diagnostic tests, the complete Doxygen reference (warnings treated as errors), and repository lint pass locally. Hosted validation of the follow-up is pending; local C++ lint remains blocked by the unavailable clang-tidy 23 executable.

AI assistance: Codex performed the migration, restack, simplification review, and validation. Human review remains required.

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • Any agent that created, edited, or submitted GitHub content was explicitly authorized for that scope, as required by our AI Usage Guidelines.
  • Every agent-authored or agent-edited public text body begins with the visible disclosure 🤖 *AI text below* 🤖 (titles are exempt).
  • I have disclosed AI assistance in the PR description.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

@burgholzer burgholzer added fix Fix for something that isn't working c++ Anything related to C++ code MLIR Anything related to MLIR QDMI Anything related to QDMI labels Sep 12, 2026
@burgholzer burgholzer self-assigned this Sep 12, 2026
@burgholzer
burgholzer force-pushed the codex/fix-mlir-exception-boundaries branch from 18f186e to e95a1ee Compare September 12, 2026 11:05
@burgholzer burgholzer added this to the v4.1.0 - QDMI 1.4 / MQSF milestone Sep 12, 2026
@mergify mergify Bot added the conflict label Sep 12, 2026
@burgholzer
burgholzer force-pushed the codex/fix-mlir-exception-boundaries branch from c5bdf1b to 46025a6 Compare September 12, 2026 11:55
@mergify mergify Bot removed the conflict label Sep 12, 2026
@burgholzer burgholzer changed the title 🐛 Preserve MLIR exception diagnostics on macOS 🐛 Return QDMI and benchmark errors before MLIR Sep 12, 2026
@burgholzer
burgholzer force-pushed the codex/fix-mlir-exception-boundaries branch 2 times, most recently from f410460 to bb0ae82 Compare September 12, 2026 12:59
@mergify mergify Bot added the conflict label Sep 14, 2026
@burgholzer
burgholzer force-pushed the codex/fix-mlir-exception-boundaries branch from bb0ae82 to f7553f5 Compare September 14, 2026 18:08
@mergify mergify Bot removed the conflict label Sep 14, 2026
@burgholzer burgholzer mentioned this pull request Sep 14, 2026
10 tasks done
@burgholzer burgholzer changed the title 🐛 Return QDMI and benchmark errors before MLIR ♻️ Return explicit errors across native Core APIs Sep 19, 2026
@mergify mergify Bot added the conflict label Sep 19, 2026
@burgholzer
burgholzer force-pushed the codex/fix-mlir-exception-boundaries branch from 0e7e58b to 501b3b9 Compare September 19, 2026 19:35
@mergify mergify Bot removed the conflict label Sep 19, 2026
@burgholzer burgholzer changed the title ♻️ Return explicit errors across native Core APIs ♻️ Unify native results and isolate DDSIM execution Sep 22, 2026
@burgholzer
burgholzer force-pushed the codex/fix-mlir-exception-boundaries branch from 1a125ca to c5f0dd5 Compare September 22, 2026 20:24
@mergify mergify Bot added the conflict label Sep 22, 2026
@burgholzer
burgholzer force-pushed the codex/fix-mlir-exception-boundaries branch from 8c8deda to f33d8d8 Compare September 22, 2026 23:09
@mergify mergify Bot removed the conflict label Sep 22, 2026
@burgholzer

Copy link
Copy Markdown
Member Author

@burgholzer

I went through all files again and I think it looks quite good now. From my end, this could be merged after a final sanity check from your side.

Will try to get through this either today or tomorrow (more likely).

@simon1hofmann

Copy link
Copy Markdown
Contributor

🤖 AI text below 🤖
Confirmed review finding: the vector DD deserializer accepts skipped qubit levels, but single-qubit measurement now treats their absence as an assertion failure. This turns a previously recoverable error into a Python process abort.
At 461590d, this reproduces SIGABRT in Package::determineMeasurementProbabilities:

from mqt.core.dd import DDPackage, VectorDD

p = DDPackage(3)
v = VectorDD.from_bytes(p, b"1\n1\n0 2 (-1 1) ()\n", False)
p.inc_ref_vec(v)
p.measure_collapsing(v, 0)

Fixed in b028fd09d. The shared deserializer now rejects nonzero vector edges that skip qubit levels or reach a terminal above qubit zero. Zero-weight edges and valid skipped identity levels in matrix DDs remain supported. Python raises ValueError during deserialization.
The new tests fail before the fix and pass afterward. Native regression coverage exercises premature terminals and skipped intermediate levels in both text and binary input, and confirms that matrix reductions still round-trip.
Validation: 3,977 native tests passed with one expected skip; all 66 Python DD tests passed; repository lint passed on both branches. Local C++ lint could not run because clang-tidy 23 is unavailable, so hosted CI must validate that check.
The fix is also included in stacked PR #2476, preserving its planned toolchain configuration.

This feels a bit arbitrary. The DD package does not support skipped levels for Vector DDs. Never has, likely never will. Are we sure this is a clean solution here?

They were not supported before as well, but probably still makes sense to have that guard.

@burgholzer
burgholzer force-pushed the codex/qdmi-multi-program-adoption branch from d741493 to 45409bd Compare October 7, 2026 15:44
@mergify mergify Bot added the conflict label Oct 7, 2026
@burgholzer
burgholzer force-pushed the codex/qdmi-multi-program-adoption branch 4 times, most recently from 6bf57c8 to 753428e Compare October 7, 2026 17:50
@burgholzer
burgholzer removed this pull request from stack #2677 October 8, 2026 10:52
@burgholzer
burgholzer force-pushed the codex/qdmi-multi-program-adoption branch 3 times, most recently from 2ed02a6 to ab2a1e4 Compare October 8, 2026 13:26
@burgholzer
burgholzer force-pushed the codex/qdmi-multi-program-adoption branch 3 times, most recently from d9c02f6 to 97c8326 Compare October 9, 2026 10:35
@simon1hofmann simon1hofmann removed this from the v4.1.0 - QDMI 1.4 / MQSF milestone Oct 9, 2026
burgholzer and others added 12 commits October 9, 2026 23:44
Restack the native error-handling migration above the replaceable-driver and
multi-program job APIs. Keep DDSIM worker isolation in the parent change and
adapt its compiler, result reconstruction, and diagnostics to FailureOr.

Preserve stable-ID configuration, standard driver loading, indexed outcomes,
worker cancellation, and Python exception translation. Migrate the newly merged
benchmark APIs and their callers without letting callback exceptions cross
exception-free native frames.

Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Keep diagnostic handlers in one shared runtime across DLL boundaries.
Stage runtime dependencies and retain build-tree lookup for native tools,
tests, and the DDSIM worker.

Reject untracked DD roots before Python measurements can reach native
reference-count assertions. Cover released and foreign roots in both
collapsing measurement paths.

Fix the 23 C++ lint findings reported after the stack merge.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Document how binding helpers translate diagnostics and adapt result APIs.
Use failed(...) consistently in mqt-cc as requested during review.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

ELF RUNPATH entries do not cover transitive dependencies. Give each Core
library its build RPATH so QDMI devices find the shared diagnostic runtime
before installation. CMake retains the configured install RPATH.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Keep the parsed measurement strength const and place the test-only template
helper in an anonymous namespace. These address all findings from the last
hosted C++ lint report, including repeated template instantiations.

Validation: repository lint and all 15 BenchmarkJSON tests pass. Local C++
lint remains unavailable because clang-tidy 23 is not installed.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Restore static diagnostic support and keep provider internals private. Direct
C++ driver calls can copy the first failure into an optional Diagnostic output,
so DLL boundaries no longer rely on a shared handler stack. QDMI C interfaces
retain status codes and local logging without additional extensions.

Keep FailureOr and LogicalResult: consuming LLVM Error across isolated static
LLVM copies aborts in a two-library reproducer. Caller-owned diagnostic output
preserves the existing types without another result framework.

Validation: 3,975 native tests passed with one expected skip; 934 affected Python
tests passed, followed by all 432 QDMI cases after the provider export changes.
All 61 focused diagnostic, registry, and configuration checks pass. Repository
lint and the installed C++ consumer pass. Local C++ lint requires unavailable
clang-tidy 23; hosted validation remains pending.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Use designated fields when constructing a Diagnostic to satisfy the
C++ lint check. Document the new error outputs on registration methods
so Doxygen's parameter checks can build the native API reference.

Validation: all four diagnostic tests, the complete Doxygen reference
with warnings treated as errors, and repository lint pass. Local C++
lint remains blocked by the unavailable clang-tidy 23 executable.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Satisfy clang-tidy's readability-trailing-comma check for the designated
Diagnostic initializer. The previous CI run passed all platform tests,
coverage, documentation, and Python checks; this was its only lint finding.

Repository lint passes. Local C++ lint remains blocked by unavailable
clang-tidy 23; hosted verification will run on the updated commit.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Reject nonzero vector edges that skip qubit levels before constructing DD
nodes. Malformed serialized input must fail at deserialization instead of
reaching a measurement assertion and aborting Python. Matrix DDs retain
their valid skipped identity levels.

Cover premature terminals and skipped intermediate levels in both text and
binary input, and verify Python raises ValueError for the malformed vectors.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Name the edge fields in the malformed-vector regression test to satisfy
modernize-use-designated-initializers without changing test behavior.

Assisted-by: GPT-6 via Codex
🤖 *AI text below* 🤖

Adapt the new sampling paths, Jeff bindings, and driver test target to
explicit results. Preserve main's macOS wheel RPATH policy and concrete
DD exports, and remove the obsolete public driver documentation.

Record the independent audit findings without applying its proposed
follow-up changes. Validate native and Python tests, generated stubs,
installed consumers, whole-file C++ lint, repository lint, and docs.

Assisted-by: GPT-6 via Codex

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Anything related to C++ code fix Fix for something that isn't working MLIR Anything related to MLIR QDMI Anything related to QDMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants