Skip to content

Conversation

@marcelwa
Copy link
Contributor

@marcelwa marcelwa commented Feb 9, 2026

Description

This PR adds Sampler and Estimator primitives to the QDMI-Qiskit Interface.

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.

@marcelwa marcelwa self-assigned this Feb 9, 2026
@marcelwa marcelwa added feature New feature or request python Anything related to Python code QDMI Anything related to QDMI labels Feb 9, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds QDMIEstimator and QDMISampler Qiskit primitives and exports; introduces a "gphase" gate alias; updates classical-bit parsing in tests; adds unit tests and documentation for the new primitives.

Changes

Cohort / File(s) Summary
Module Exports
python/mqt/core/plugins/qiskit/__init__.py
Import and export QDMIEstimator and QDMISampler in public symbols when Qiskit is available.
Backend Configuration
python/mqt/core/plugins/qiskit/backend.py
Added gate alias mapping: "global_phase": {"gphase"} for bidirectional name mapping.
Estimator Implementation
python/mqt/core/plugins/qiskit/estimator.py
New QDMIEstimator (EstimatorV2): input coercion, precision→shots logic, Pauli-term decomposition to measurement circuits, parameter binding/broadcasting, batched backend execution, expectation/variance computation, and PubResult packaging.
Sampler Implementation
python/mqt/core/plugins/qiskit/sampler.py
New QDMISampler (SamplerV2): input coercion, default/override shots, parameter binding, batched backend execution, counts extraction, conversion to per-creg BitArray, shaping to pub.shape, and SamplerPubResult packaging.
Test Helpers
test/python/plugins/qiskit/conftest.py
Rewrote classical-bit counting to sum multiple QASM2/QASM3 declarations via findall; removed raising an error on parse failure.
Tests
test/python/plugins/qiskit/test_estimator.py, test/python/plugins/qiskit/test_sampler.py
Added comprehensive unit tests for Estimator and Sampler covering instantiation, parameterized runs, precision/shots behavior, broadcasting, multiple cregs, edge cases, and metadata.
Docs
docs/qdmi/qdmi_backend.md
Added "Qiskit Primitives" documentation and examples for QDMIEstimator and QDMISampler, including usage and implementation notes.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Estimator as QDMIEstimator
    participant ObsCircuits as ObservableCircuits
    participant Backend as QDMIBackend
    participant Results as ResultProcessor

    User->>Estimator: run(pubs, precision)
    Estimator->>Estimator: coerce to EstimatorPub, select precision
    Estimator->>ObsCircuits: _get_observable_circuits(observables)
    ObsCircuits-->>Estimator: (coeffs, term_circuits, indices)
    Estimator->>Estimator: bind params, compose full circuits
    Estimator->>Backend: execute(all_circuits, shots)
    Backend-->>Estimator: counts per circuit
    Estimator->>Results: compute expectations & variances
    Results-->>User: PubResult (evs, stds, metadata)
Loading
sequenceDiagram
    actor User
    participant Sampler as QDMISampler
    participant Binder as ParamBinder
    participant Backend as QDMIBackend
    participant Converter as BitArrayConverter

    User->>Sampler: run(pubs, shots)
    Sampler->>Sampler: coerce to SamplerPub, determine shots
    Sampler->>Binder: bind parameters -> bound circuits
    Binder-->>Sampler: bound circuits
    alt circuits exist
        Sampler->>Backend: execute(bound_circuits, shots)
        Backend-->>Sampler: counts per circuit
        Sampler->>Converter: _get_bit_arrays(cregs, counts, shape)
        Converter-->>User: shaped BitArray results + metadata
    else no circuits
        Sampler-->>User: empty SamplerPubResult
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • ystade
  • burgholzer

Poem

🐇 I bind and hop through parameter land,

Circuits flip and counts fall in my hand,
Estimator hums, Sampler drums the bits,
Gates and gphases dance in tiny skits,
I nibble results and bound them with a strand.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive PR description lacks detail on implementation scope, dependencies, and motivation; includes a checklist with mixed completion but no explanation of what was/wasn't completed. Expand the description with implementation scope, key design decisions, dependencies, and explain why certain checklist items (upgrade guide, self-review) were not completed.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding Sampler and Estimator primitives to the QDMI-Qiskit interface, which is directly supported by all the file changes in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sampler-estimator-primitives

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@python/mqt/core/plugins/qiskit/sampler.py`:
- Around line 174-177: The code skips reshaping when shape is an empty tuple
(scalar) because "if shape:" is falsy; update the logic around
BitArray.from_counts and array.reshape so scalars are handled: replace the
truthy check with an explicit None check and call reshape with unpacking so
scalar tuple becomes a no-arg reshape (i.e., use if shape is not None and call
array.reshape(*shape)); reference BitArray.from_counts and array.reshape to
locate the change.

In `@test/python/plugins/qiskit/test_estimator.py`:
- Around line 165-173: Remove the dead assignment to vals that is immediately
overwritten: delete the line vals = [[0.0], [np.pi / 2], [np.pi]] so only the
intended test value assignment (vals = [[0.0], [np.pi]]) remains in
test_estimator.py; refer to the vals variable in the broadcasting test to locate
the correct assignment to keep.

In `@test/python/plugins/qiskit/test_sampler.py`:
- Around line 39-63: In test_sampler_run_simple_circuit add an assertion that
the measurement bit-array shape is the scalar empty-tuple case by asserting
bit_array.shape == (), next to the existing checks for num_shots/num_bits;
update the test function test_sampler_run_simple_circuit so pub_result (from
sampler.run) and data.meas (bit_array) are validated for scalar shape to guard
the falsy-empty-tuple bug in _get_bit_arrays.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/python/plugins/qiskit/test_estimator.py`:
- Around line 153-179: Update the outdated comments in
test_estimator_broadcasting to reflect the actual test data: remove references
to "3 Parameter sets", "2 observables × 3 parameters", and reshaping/outer
product and instead state that vals contains 2 parameter sets (shape (2,) / two
values) and the test asserts broadcasting for 2 observables × 2 parameter sets;
adjust the comment around the EstimatorPub.coerce((qc, ops, vals)) and the
subsequent assertions to describe the current shape expectations (data.evs.shape
== (2,), data.stds.shape == (2,)).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/python/plugins/qiskit/test_estimator.py`:
- Line 69: Remove the unnecessary cast when calling estimator.run: replace
estimator.run(cast("Any", [pub])) with estimator.run([pub]) because pub is
already an EstimatorPub (an EstimatorPubLike) and [pub] satisfies
Iterable[EstimatorPubLike]; leave other intentional casts (e.g., cast("Any",
pub_result.data)) untouched.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@docs/qdmi/qdmi_backend.md`:
- Line 401: Fix the typo in the heading text "# Get results for the first pub
(Primitive Unified Bloc)" by changing "Bloc" to "Block" so the heading reads "#
Get results for the first pub (Primitive Unified Block)"; update any other
occurrences of "Primitive Unified Bloc" in the same document to "Primitive
Unified Block" to keep the acronym PUB consistent.
- Around line 520-526: Replace the ambiguous phrase "verification circuits" in
the "Primitives Implementation" section with clearer wording such as "provided
circuits" or "input circuits" wherever it appears (e.g., the sentence in the
Sampler/Estimator description that reads "appends necessary basis rotations and
measurements to the verification circuits"); update the Estimator description to
say it appends rotations/measurements to the provided/input circuits and
reconstructs expectation values from their measurement counts so the docs
clearly indicate these are user-supplied circuits.
- Line 398: The example uses a non-standard 1-tuple wrapper when calling
sampler.run; update the call to use bare circuit or an explicit 2-tuple per
BaseSamplerV2 conventions: replace sampler.run([(qc,)], shots=1024) with either
sampler.run([qc], shots=1024) for a non-parametric circuit or sampler.run([(qc,
None)], shots=1024) for an explicit parametric form, keeping the same
sampler.run and qc symbols so intent and API compatibility are clear.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@docs/qdmi/qdmi_backend.md`:
- Line 382: Update the sentence that currently says "It is used to sample
quantum circuits and obtain bitstrings (quasi-distributions)" to remove the
misleading "(quasi-distributions)" and clarify that BaseSamplerV2 returns
SamplerPubResult containing BitArray bitstrings/counts; reference BaseSamplerV2,
SamplerPubResult, and BitArray (and note that QuasiDistribution is specific to
the older Sampler V1) so readers understand V2 returns bitstrings/counts rather
than V1-style quasi-distributions.

In `@test/python/plugins/qiskit/test_estimator.py`:
- Around line 33-200: Add negative/error-path tests alongside the existing
happy-path ones by creating two new pytest functions: one that calls
QDMIEstimator.run with a mismatched Observable vs circuit qubit count (e.g., use
QuantumCircuit(1) with SparsePauliOp("II") or SparsePauliOp("XX")) and asserts
that the call raises the expected exception (ValueError or QiskitError) from
QDMIEstimator/EstimatorPub; and another that calls QDMIEstimator.run with an
invalid precision (e.g., precision=-0.1 and precision=0) and asserts it raises
the input-validation error; add these tests near test_estimator_no_circuits or
test_estimator_precision_handling and reference QDMIEstimator.run and
EstimatorPub.coerce in the assertions so failures are tied to the right code
paths.

@mergify mergify bot added the conflict label Feb 9, 2026
@mergify mergify bot removed the conflict label Feb 9, 2026
@codecov
Copy link

codecov bot commented Feb 10, 2026

Codecov Report

❌ Patch coverage is 96.51163% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
python/mqt/core/plugins/qiskit/estimator.py 96.6% 4 Missing ⚠️
python/mqt/core/plugins/qiskit/sampler.py 96.1% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@marcelwa marcelwa requested a review from burgholzer February 10, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request python Anything related to Python code QDMI Anything related to QDMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant