Skip to content

✨ Add typed structured quantum benchmarks - #2135

Open
denialhaag wants to merge 90 commits into
mainfrom
benchmarks-dot-jeff
Open

✨ Add typed structured quantum benchmarks#2135
denialhaag wants to merge 90 commits into
mainfrom
benchmarks-dot-jeff

Conversation

@denialhaag

@denialhaag denialhaag commented Aug 17, 2026

Copy link
Copy Markdown
Member

Description

🤖 AI text below 🤖

Add a typed foundation for structured quantum benchmarks.

The new MLIR-free MQT::CoreBench library owns each benchmark's parameters, validation, logical output, analytic reference, and evaluation. This vertical slice contains:

  • Bernstein--Vazirani with an explicit hidden bitstring and static or dynamic method;
  • GHZ with qubit count, topology, and measurement basis;
  • Grover search with an explicit marked bitstring and automatic or explicit iteration count;
  • QFT with qubit count, period exponent, and standard or semiclassical method;
  • QPE with precision, an exact rational phase in turns, and standard or iterative method.

Strict, versioned JSON instances materialize all defaults. Self-checking manifests record the resolved parameters, output convention, reference model, and a deterministic case ID. The C++, Python, and mqt-core-bench APIs expose the same contract. The command-line tool lists and describes benchmarks, generates structured QC or jeff, and evaluates counts against a manifest.

The MLIR layer contains compact, structured QC emitters for the five benchmark families. It consumes typed instances and keeps its private dispatch table aligned with the semantic registry instead of duplicating defaults. The Python mqt.core.bench module delegates program generation to the existing MLIR extension, which keeps one MLIR runtime per process.

Distribution-level execution tests against the DD simulator remain a follow-up on the stack that starts at #2077.

Fixes #1115

AI notice

This PR and its contents were created with the assistance of Opus 5 via Claude Code and GPT-5.6 Sol via Codex.

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.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • 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.

@denialhaag denialhaag self-assigned this Aug 17, 2026
@denialhaag denialhaag added feature New feature or request MLIR Anything related to MLIR labels Aug 17, 2026
@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.74367% with 117 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/bench/JSON.cpp 87.5% 58 Missing ⚠️
mlir/bench/MQTCoreBench.cpp 75.0% 41 Missing ⚠️
mlir/bench/Generate.cpp 68.0% 15 Missing ⚠️
src/bench/QPE.cpp 97.9% 2 Missing ⚠️
src/bench/SHA256.cpp 98.5% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread mlir/benchmark/programs/QFT.cpp Outdated
@denialhaag
denialhaag force-pushed the benchmarks-dot-jeff branch from 33b6b18 to a8f62c2 Compare August 17, 2026 12:48
Assisted-by: Claude Opus 5 via Claude Code
Assisted-by: Claude Opus 5 via Claude Code
Assisted-by: Claude Opus 5 via Claude Code
Assisted-by: Claude Opus 5 via Claude Code
Assisted-by: Claude Opus 5 via Claude Code
Assisted-by: Claude Opus 5 via Claude Code
Assisted-by: Opus 5 via Claude Code
Assisted-by: Opus 5 via Claude Code
@denialhaag
denialhaag force-pushed the benchmarks-dot-jeff branch from c0882d7 to 2d99355 Compare August 18, 2026 10:06
@denialhaag

Copy link
Copy Markdown
Member Author

Modulo some more testing, we should be able to mark this PR as fixing #1115.

Assisted-by: Opus 5 via Claude Code
Assisted-by: Opus 5 via Claude Code
@denialhaag denialhaag changed the title ✨ Add generator for structured jeff benchmark programs ✨ Add structured benchmark programs and a jeff generator Aug 18, 2026
Assisted-by: Opus 5 via Claude Code
Assisted-by: Opus 5 via Claude Code
@burgholzer burgholzer changed the title ✨ Add structured benchmark programs and a jeff generator ✨ Add typed structured quantum benchmarks Aug 23, 2026
@burgholzer burgholzer self-assigned this Aug 23, 2026
Rename the unreleased public component to bench, add typed Bernstein--Vazirani and QFT families, centralize private dispatch, simplify generation, and make the notebook executable.

Preserve Daniel Haag’s original structured-program commits in the branch history while keeping new contracts and architecture in this commit.

Assisted-by: Codex
Process the no-swap Fourier circuit from the most significant physical qubit and apply semiclassical feed-forward in the same order.

Assisted-by: Codex
(cherry picked from commit abd9ac38fcf3b7068588895e1bf16eb6725a6943)
Apply the repository formatter to the new BV and Fourier sources and their semantic tests.

Assisted-by: Codex
Record combined-stack, installation, wheel, documentation, test, and lint results, including the unrelated QDMI Python blocker.

Assisted-by: Codex

@DRovara DRovara left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is really cool! I didn't expect to find an entire benchmark suite with actual evaluations here when I opened the PR but this is pretty nice.

In fact, looking ahead, I can really imagine adding further custom benchmarks to this to e.g. create a reproduction environment for paper results, but that's beyond the scope for now.

I guess the one disadvantage I see here is that it seems like adding another benchmark program requires quite some effort, but I guess there could be worse things.

The programs themselves look correct, I didn't notice any issues there.

Comment thread docs/benchmarks.md Outdated
Comment on lines +11 to +15
MQT Core defines a benchmark as validated, benchmark-specific parameters plus an
analytic reference. One instance produces a structured QC program, a resolved
manifest, and a stable case ID. Each benchmark returns one classical register
named `result`. Outcome strings are big-endian: the highest-index result bit is
the leftmost character.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like this is a bit difficult to understand, especially considering the loaded term "benchmark".

If I understand it correctly, how about a rewording like:

Benchmarks in MQT Core are defines as a tuple of:
- benchmark-specific parameters
- an analytic reference
They allow the construction of:
- A structured QC program
- a resolved manifest
- a stable case ID

Resulting programs return one classical register named [...]

For instance, from the current description, I still wouldn't understand that we are talking about specific programs here. And since your first sentence defines a benchmark as parameters + analytic reference, the sentence "each benchmark returns [...]" feels a bit weird - how can parameters + analytic reference even return anything?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah. I like the direction of your suggestion! I'll take that into the next iteration!

Comment thread docs/benchmarks.md
Comment on lines +22 to +45
```{code-cell} ipython3
import json
import subprocess


def run_bench(*arguments: str) -> dict[str, object]:
"""Run mqt-core-bench and parse its JSON output."""
completed = subprocess.run(
["mqt-core-bench", *arguments],
check=True,
capture_output=True,
text=True,
)
return json.loads(completed.stdout)


catalog = run_bench("list")
[entry["id"] for entry in catalog["benchmarks"]]
```

```{code-cell} ipython3
qft_schema = run_bench("describe", "qft")
qft_schema["properties"]["parameters"]
```

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Isn't it a bit weird to illustrate example usages of a CLI tool with python code and subprocess? This makes it seem like subprocess is the exected way to run it...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah. It certainly is; especially since there is Python code for doing this.
I suppose this was mostly about showing how the CLI can be used, but there are better ways to do that in Jupyter Notebooks. I'll revisit this.

Comment thread include/mqt-core/bench/QFT.hpp Outdated
namespace mqt::bench {

/// Circuit method used for the quantum Fourier transform.
enum class QFTMethod : uint8_t { Standard, Semiclassical };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I just realised that (I think) there is no documentation anywhere on what these options mean. Here, I didn't quite get what "Semiclassical" means. Is it the same iterative approach as the iQPE?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No, these should probably be documented inline with Doxygen comments.

Same idea as iQPE. semiclassical is just the established name from the literature.

return {result};
}

auto query = builder.allocQubitRegisterStorage(width, "query");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is just a quick random thought but would it make sense/how much effort would it be to also provide an option that can be used for all benchmark programs that replaces any and all qubit registers with multiple individually allocated single qubits?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm. I see the idea. Feels like this would be a bit complicated though. Especially for the algorithms using loops because they currently index into registers in the loops, which is not directly possible if the program just has loose qubits.
At the top of my head, I don't see a very easy solution. But maybe I am also overlooking things.

Clarify the instance/program boundary and document the dynamic Fourier methods in the executable notebook. Align the feature with current repository lint and guidance after the main merge.

Assisted-by: Codex
@mergify mergify Bot removed the conflict label Aug 27, 2026
}
}

b.scfFor(0, search, 1, [&](Value iv) { b.h(b.loadQubit(q.value, iv)); });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Seeing this, I think it could make sense to have an abstraction that "applies" single qubit gates to registers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One could even think about allowing registers as arguments of the gates and defining broadcast rules similar to OpenQASM. But I am not quite sure we want this, because it adds a whole bunch of complexity.

What should be easy is adding convenience methods in the builders for easing the construction of the construct here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think a modifier along the lines

%out:9 = qco.broadcast %in:9 (%target) { qco.h(%target) } : (!qco.qubit, ...., !qco.qubit)
%tensor_out = qco.broadcast %tensor_in (%target) { qco.x(%target) } : (!tensor.tensor<!qco.qubit>)

could be kind of nice as this doesn't add overhead to the individual gates. But definitely not a must have right now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There may be a precedent in MLIR for how to handle this. This is kind of like vectorization; and there is a vector dialect in MLIR. Might be worth to brainstorm these ideas with an LLM and collect the thoughts in an issue.
This si nothing to be addressed in this PR.

@denialhaag denialhaag 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.

Thanks a lot for all the improvements, @burgholzer! I really like this a lot! 🙂

I went through the PR pretty carefully now and left a comment wherever I stumbled. I'll feed these comments into Codex in a bit. I'm not sure about some of my comments, which is why I sometimes explicitly tagged you.

Comment thread docs/benchmarks.md Outdated
Comment thread docs/benchmarks.md Outdated
Comment thread docs/benchmarks.md Outdated
Comment thread docs/benchmarks.md Outdated
Comment thread docs/benchmarks.md Outdated
Comment thread src/bench/EvaluationUtils.hpp Outdated
Comment thread src/bench/EvaluationUtils.hpp Outdated
Comment thread src/bench/GHZ.cpp
Comment thread src/bench/Grover.cpp Outdated
Comment thread src/bench/SHA256.hpp
@burgholzer

Copy link
Copy Markdown
Member

Thanks a lot for all the improvements, @burgholzer! I really like this a lot! 🙂

I went through the PR pretty carefully now and left a comment wherever I stumbled. I'll feed these comments in a bit. I'm not sure about some of my comments, which is why I explicitly tagged you sometimes.

Nice! I think I commented on every tag. Let me know if I missed something!

Assisted-by: GPT-5.6 Sol via Codex
Call the resolved configuration an instance and define its generated
manifest as the semantic sidecar used for evaluation.

Assisted-by: GPT-5.6 Sol via Codex
Keep each emitter self-contained, use lowercase MLIR paths, and add
recurring constant operations to the program builder. Apply the remaining
C++ review cleanups in the same structural batch.

Assisted-by: GPT-5.6 Sol via Codex
Make notebook output easier to read, hide setup details, and link the
generated program API. Cover the cross-platform benchmark launcher
directly so Python patch coverage includes it.

Assisted-by: GPT-5.6 Sol via Codex
Assisted-by: GPT-5.6 Sol via Codex

@denialhaag denialhaag 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.

Another set of comments:

Comment thread test/python/test_cli.py Outdated
Comment thread include/mqt-core/bench/QFT.hpp Outdated
Comment thread include/mqt-core/bench/QPE.hpp Outdated
Comment thread mlir/benchmark/programs/Fourier.cpp Outdated
Comment thread docs/benchmarks.md Outdated
Comment thread docs/benchmarks.md Outdated
Name the registry iterator type explicitly instead of assuming that
std::array iterators are pointers. MSVC uses a wrapper iterator type.

Assisted-by: GPT-5.6 Sol via Codex
Group benchmark CLI tests after the core CLI cases. Use QFT and QPE
consistently, update constant helper comments to current Doxygen style,
and keep the benchmark guide independent of its current families.

Assisted-by: GPT-5.6 Sol via Codex
@denialhaag

denialhaag commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

I think I commented on every tag. Let me know if I missed something!

Thanks a lot for the quick input, @burgholzer! 🙂

I might have gotten a bit lost in the details here, but I think I'm happy with this now. I'm kind of indifferent about adding more benchmarks now or in one or more follow-ups. The latter would certainly make reviewing easier, but only if we leave the infrastructure (largely) alone. Either way, we should add most (if not all) structured benchmarks mentioned in the jeff repository sooner rather than later. 🤔

Assisted-by: GPT-5.6 Sol via Codex
@burgholzer

Copy link
Copy Markdown
Member

I'll try to get a review in for this tomorrow, so that we can get this merged and not hold it up for too long. Follow-ups should come quite naturally.

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

Labels

feature New feature or request MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🧪 MLIR - Add QPE and Iterative Phase Estimation examples

4 participants