Skip to content

♻️ Replace nlohmann/json with LLVM JSON - #2824

Merged
burgholzer merged 15 commits into
mainfrom
codex/llvm-json-migration
Oct 25, 2026
Merged

burgholzer merged 15 commits into
mainfrom
codex/llvm-json-migration

Conversation

@burgholzer

@burgholzer burgholzer commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Description

Replace nlohmann/json with stock LLVM JSON parsing, values, and streams across benchmarks, DD statistics, QDMI configuration and bindings, and IQM export. No custom JSON support library or compatibility parser remains.

Native benchmark factories, raw-count evaluation, JSON parsing, and Shor execution return owned LLVM errors. Native probability and QAOA cutSize return scalars with a binary-outcome/width precondition; Python validates those inputs. Evaluation checks every count key, including zero-weight keys, and rejects empty, zero-total, or overflowing counts.

Evaluation results own their validated shots total in C++ and Python. JSON formatting is private and infallible after manifest verification and successful evaluation. The public evaluationToJSON(caseId, shots, result) API is removed; callers use evaluateJSON(manifest, counts). ModularMultiplier reuses the result total. Native Shor execution borrows a valid, nonempty, nonthrowing callback through llvm::function_ref; Python callback exceptions retain their identity.

Bindings use nanobind for error handling, hashing, and warnings. Upstream's direct typed benchmark-generation path is preserved. CoreBench follows LLVM's EH/RTTI policy. The QDMI driver copies and SC tests match the SDK's RTTI setting. Benchmark bindings retain nanobind's RTTI and consume errors through LLVM's out-of-line APIs, so unoptimized modules also import correctly. Static CoreBench linkage preserves LLVM error-type identity; the MSVC project-options guard prevents /EHsc from overriding LLVM's no-exceptions policy. Existing QDMI exception boundaries remain. The C++ lint wrapper checks cpp-linter's finding count because its local backend can exit zero with findings.

Benchmark/configuration JSON uses LC_NUMERIC=C; Core does not change process locale. DD statistics and IQM numeric output use locale-independent APFloat. Allocation failure is outside the recoverable-error contract. The broader compiler/DD/device exception migration remains separate.

Base: 1d384a5d23aa1f02b26fd5684dab586ef26167fe. Native validation revision: ef4d19c2e35ed14118db7bc61a58e9e1acd61f8d. Guidance-only head: bf004898a2f489d22bea66a48fbf3a14aa2ef5f6.

Claim Evidence
Input and numerical contracts remain checked 4,096 native tests pass in both GCC Debug and Clang ThinLTO Release, and all 2,042 Python tests pass. The JSON regression uses independently calculated TVD/fidelity.
Unoptimized LLVM consumers link and import The prior head fails locally with the same missing LLVM typeinfo as the three failed hosted jobs. The corrected Debug native build passes; the Debug benchmark module imports and passes 85 Python tests. Malformed JSON keeps its ValueError and source diagnostic; overflow and callback-identity checks pass. All 183 focused QDMI coverage tests pass.
Shot totals belong to evaluation Six targeted checks fail when propagation is replaced with zero, covering generic, QAOA, Shor, and JSON results; restored code passes. Generated stubs expose all three shots properties.
Native callbacks and evaluation remain exception-free All three CoreBench compile units use no-EH/no-RTTI flags. Emitted LLVM IR has no invoke/landingpad instructions. Callback-error identity tests pass.
Keep ordered counts Three alternating public-Python comparisons found unordered_map plus deterministic traversal about 15% slower for QFT4096 and 32% slower for QAOA4096; Shor improved 7%. Native phase probes isolate the added sort cost. The prototype is discarded.
Memory measures are distinct For QFT4096, map/hash allocation traffic is 447/490 KB, peak tracked live memory 447/422 KB, retained memory zero, and process peak RSS including profiling about 34 MB. No whole-program speedup is claimed for the retained changes.
Final local checks pass Full changed-file C++ lint, repository lint, stub generation, and specialist Ponytail review pass. The new commit is signed and verified. Stubs are unchanged. AGENTS.md asks for configurations relevant to the change and local reproduction before deleting build settings. It does not require both Debug and Release or CI monitoring. Repository lint passes after the guidance edit.

Local execution was on Linux. No new platforms, supported input shapes, adaptive execution, pulse functionality, validated-input framework, or custom container/caster is introduced.

GPT-6 via Codex assisted implementation, validation, review, and this description. Human review remains required before acceptance.

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 dependencies Pull requests that update a dependency file refactor Anything related to code refactoring c++ Anything related to C++ code labels Oct 23, 2026
@burgholzer burgholzer self-assigned this Oct 23, 2026
@burgholzer burgholzer added this to the v4.2.0 - SC26 milestone Oct 23, 2026
@mergify mergify Bot added the conflict label Oct 24, 2026
@burgholzer
burgholzer force-pushed the codex/llvm-json-migration branch from 89232f0 to 1d3fa0b Compare October 24, 2026 00:49
@burgholzer
burgholzer force-pushed the codex/llvm-json-migration branch 2 times, most recently from 42a546d to 2f535a8 Compare October 24, 2026 10:53
@codecov

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.83827% with 75 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mlir/bench/MQTCoreBench.cpp 66.9% 35 Missing ⚠️
src/bench/JSON.cpp 96.3% 22 Missing ⚠️
src/qdmi/devices/sc/Configuration.cpp 91.7% 14 Missing ⚠️
src/bench/QFTAdder.cpp 88.8% 3 Missing ⚠️
src/qdmi/driver/DeviceRegistry.cpp 98.0% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@mergify mergify Bot added the conflict label Oct 24, 2026
🤖 *AI text below* 🤖

Use LLVM JSON values and streams with strict locale-independent parsing.
Return explicit validation errors, version benchmark manifests and case
IDs, and preserve Python/C boundaries and required exception cleanup.

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

Copy validated unescaped strings directly into owning LLVM values.
Preserve control-byte checks and escaped-string decoding; regression
mutations confirm both ownership and validation failures are detected.

Link CoreBench statically so LLVM errors and their handlers share type
IDs without depending on shared-library symbol interposition.

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

Validate manifest structure with LLVM equality and retain an exact
unsigned output-width check. Reference versions compare by numeric value;
canonical output and case IDs continue to use resolved benchmark values.

Resolve serialized metadata only for callers that request it, removing
comparison-only serialization from manifest reading and evaluation.
Keep unsigned reference versions clear of LLVM's signed conversion edge.

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

Accept equivalent parsed numeric output widths while retaining exact
integer precision and manifest consistency at large dimensions.

Serialize case-ID JSON directly into its domain-prefixed buffer. This
removes a temporary string without changing canonical bytes or hashes.
Cover spelling lenience and rounding boundaries with mutation-checked
regressions and document decimal parsing limits.

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

Use LLVM parsing, value equality, and formatting under an explicit C numeric
locale requirement. Keep IQM numeric output locale independent with APFloat,
and retain exact typed benchmark parameters and calibration validation.

Delete CoreJSON and its generic policy tests, compact JSON construction, and
replace benchmark CLI exceptions with LLVM errors so generation and the CLI
follow LLVM's default exception and RTTI settings. Keep native constructor
validation and atomic no-overwrite publication boundaries.

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

Stream the existing DD statistics schema with LLVM JSON and APFloat so
floating values do not depend on the process numeric locale. Preserve
unused states, integer precision, and aggregate active-entry fields.
Keep LLVM's parser behavior and typed configuration validation without
adding a separate UTF-8 or nonfinite input policy.

Restore upstream CMake formatting and remove stale formatter guards
left by the rebase. Cover the DD locale contract with a regression that
fails against the previous implementation.

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

Read benchmark size parameters through LLVM's exact unsigned accessor and
check the native range instead of relying on platform-specific integer
aliases. Remove mapper objects made redundant by existing key validation.

Match both QDMI driver targets to the linked LLVM RTTI policy while
retaining exception guards. Make singleton nested-array fixtures explicit
and require the runtime rejection to reach connectivity validation.

Validate null and invalid size inputs, and demonstrate that the new
regressions reject null acceptance and flattened fixtures.

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

Match the SC configuration test target to LLVM's RTTI setting. Translate
native benchmark validation and overflow exceptions within the Darwin
module, where nanobind's split runtime can miss their type identities.
Leave Python callback exceptions and unrelated error categories alone.

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

The cpp-linter local backend logs check counts instead of writing GitHub
output variables, and returns zero even when it finds issues. Require its
explicit count and fail closed when it is missing. Accept both LF and CRLF
output so the local check also works on Windows.

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

Use checked factories and LLVM Expected results for benchmark construction,
probability, evaluation, and Shor execution. Adapt native callers and retain
Python constructors, exception categories, and callback exception identity.
Remove the JSON exception bridges and macOS constructor translator.

Compile CoreBench without exception handling under the LLVM policy outside
MSVC, which retains its normal STL ABI. Preserve current validation limits
and numerical formulas; keep broader compiler and device migration separate.

Validated with 4,089 native tests, 2,036 Python tests, 167 GCC debug benchmark
tests, C++ and repository lint, generated stubs, and executable documentation.
Mutation checks detect wrong overflow categories and lost callback exceptions.
Paired GHZ/QFT probes preserve JSON and QIR; no speedup is claimed.

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

Use llvm_update_compile_flags for CoreBench on every platform now that its
recoverable errors use owned LLVM results. Remove its MSVC branch and
explicit RTTI override. Let the existing MSVC project options respect
LLVM's _HAS_EXCEPTIONS=0 instead of adding a conflicting /EHsc.

Keep exception handling and the linked LLVM RTTI policy at the existing
QDMI boundaries. Record the benchmark migration as part of the JSON PR.

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

Make native probability and cut-size queries infallible under an explicit
valid-outcome precondition. Validate Python strings at the binding boundary
and validate raw count maps once before evaluation. Keep factories and
raw-count evaluation fallible, and remove Shor propagation that preceding
checks already rule out.

Use nanobind for binding errors, hashing, and warnings. Keep one callback
identity test and check malformed scalar inputs at the Python boundary.
Trim benchmark documentation and remove four unnecessary QDMI RTTI
overrides verified by compilation and native tests. Preserve upstream's
typed benchmark generation and GroverWeakMeasurement rename after rebase.

Assisted-by: GPT-6 via Codex
@burgholzer
burgholzer force-pushed the codex/llvm-json-migration branch from 045d6e5 to a8a298b Compare October 25, 2026 08:37
@mergify mergify Bot removed the conflict label Oct 25, 2026
Let evaluation results own the validated shot total and keep JSON formatting
private to the checked manifest/evaluation path. Remove the public arbitrary
result serializer and its redundant validation. Reuse the total for modular
multiplier success rates and expose it consistently in Python.

Borrow Shor callbacks through llvm::function_ref while retaining the Python
exception boundary. Keep ordered counts: the hash-map prototype adds canonical
sorting and slows most measured public workloads.

Assisted-by: GPT-6 via Codex
@burgholzer
burgholzer force-pushed the codex/llvm-json-migration branch from ed3ce61 to 0522619 Compare October 25, 2026 09:16
🤖 *AI text below* 🤖

Match both QDMI driver targets and the SC device tests to the LLVM SDK RTTI
policy while preserving their exception boundaries. LLVM inline error
handlers emit missing ErrorInfoBase type information in unoptimized builds;
Release optimization had hidden the invalid link dependency.

Consume benchmark binding errors through LLVM's out-of-line APIs while
retaining nanobind's required RTTI, error categories, and callback exception
identity. The original Debug module failed to import; the fixed module
passes its benchmark suite.

Require Debug and Release consumer checks before native changes are pushed,
check the original configuration before removing build settings, and verify
affected hosted jobs when fixing CI.

Assisted-by: GPT-6 via Codex
Choose checks for the affected configuration and reproduce build-setting requirements locally. Remove the blanket Debug/Release requirement and CI monitoring after publication.

Assisted-by: GPT-6 via Codex

@burgholzer burgholzer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@denialhaag @simon1hofmann FYI: this PR does two things

  • it migrates MQT Core away from nlohmann_json towards the builtin LLVM JSON capabilities. One dependency less 🎉
  • it takes a first stab at the refactoring of the benchmark library to make it exception free. I hope this makes the migration in #2545 slightly smaller.

I tried to push Codex quite a bit to redesign/refactor the APIs to be more efficient and clean. 🧽

@burgholzer
burgholzer enabled auto-merge (squash) October 25, 2026 11:10
@simon1hofmann

Copy link
Copy Markdown
Contributor

@denialhaag @simon1hofmann FYI: this PR does two things

  • it migrates MQT Core away from nlohmann_json towards the builtin LLVM JSON capabilities. One dependency less 🎉
  • it takes a first stab at the refactoring of the benchmark library to make it exception free. I hope this makes the migration in ♻️ Consolidate native Python bindings #2545 slightly smaller.

I tried to push Codex quite a bit to redesign/refactor the APIs to be more efficient and clean. 🧽

Pretty nice 😎

@burgholzer
burgholzer merged commit 7926644 into main Oct 25, 2026
30 checks passed
@burgholzer
burgholzer deleted the codex/llvm-json-migration branch October 25, 2026 11:44
burgholzer added a commit that referenced this pull request Oct 25, 2026
Rework the exception migration on the LLVM JSON and benchmark APIs
merged in #2824. Use Expected and Error for recoverable native failures, capture
MLIR diagnostics at native boundaries, and preserve Python and QDMI C
error categories without a shared diagnostic runtime.

Use LLVM streams for DD formatting and borrowed bytes for parsing.
File wrappers own I/O failures; native DD, compiler and QIR code follow
LLVM's exception and RTTI policy. Keep allocation recovery and worker
host exceptions at their existing boundaries.

Linux native, Python, installed-consumer, stub and lint checks pass.
Windows DLL error identity still requires validation and a linkage
decision before merge.

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
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 dependencies Pull requests that update a dependency file refactor Anything related to code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants