Skip to content

Add Quantum Counting algorithm implementation#176

Merged
sesmart merged 11 commits into
amazon-braket:mainfrom
axif0:counting
Feb 27, 2026
Merged

Add Quantum Counting algorithm implementation#176
sesmart merged 11 commits into
amazon-braket:mainfrom
axif0:counting

Conversation

@axif0
Copy link
Copy Markdown
Contributor

@axif0 axif0 commented Feb 15, 2026

Issue #, if available:
#1193

Description of changes:
Add the Quantum Counting algorithm (Brassard et al., 1998) — uses QPE on the Grover operator to estimate the number of marked items in an unstructured search space. Includes source module, unit tests, tutorial notebook, and README entry.
Testing done:

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have checked that my tests are not configured for a specific region or account (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@axif0 axif0 requested a review from a team as a code owner February 15, 2026 19:42
@axif0 axif0 changed the title Counting Add Quantum Counting algorithm implementation Feb 15, 2026
@sesmart
Copy link
Copy Markdown
Contributor

sesmart commented Feb 17, 2026

Hi @axif0, thanks for the contribution! Updating from main should fix the readthedocs issue, but you likely need to run tox -e linters as well. Thanks!

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (3c92423) to head (2b6d28f).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main      #176    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           30        32     +2     
  Lines         1220      1358   +138     
  Branches       156       174    +18     
==========================================
+ Hits          1220      1358   +138     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@sesmart sesmart left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution! Overall the notebook looks very nice, good edge cases, left some concerns below.

Comment thread src/braket/experimental/algorithms/quantum_counting/__init__.py
Comment thread src/braket/experimental/algorithms/quantum_counting/quantum_counting.py Outdated
Comment thread src/braket/experimental/algorithms/quantum_counting/quantum_counting.py Outdated
controlled_matrix = np.kron(p0, id_matrix) + np.kron(p1, grover_unitary)

targets = [control] + list(target_qubits)
circ.unitary(matrix=controlled_matrix, targets=targets)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

While this does work, unfortunately, this is not quite what we are looking for 🥲 - it would probably be okay for a black box oracle, but here we can actually build the circuits, though this can certainly be valuable for building an intuition and confirming our logic.

Can you maybe grab some of the circuits from the Grover's file?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The Grover operator is now built entirely from circuit primitives: build_oracle() for the phase oracle and amplify() for diffusion, both from grovers_search.py. The unitary is only extracted (to_unitary()) for the controlled-G operation in QPE, which is necessary since implementing arbitrary controlled multi-gate circuits natively is non-trivial.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I made an error in suggesting to remove the run function -> the more common pattern here is to have a run_algorithm function, so if you could restore that, it would be great. My apologies.

I think there are maybe two options here, and it should close it out. We would like for there to be an actual circuit, and I agree that while the controlled unitary is non trivial, here there might be a reasonable way.

(1) For this particular U, I think you might be able to leverage the nature of the oracle a bit more. You could drop the amplify function, and then just call the oracle for "1" + the relevant states. I.e. in the diffussion operator "00" -> "100", and in the oracle, "xx" -> "1xx". Then you just need to implement the controlled H, with a Z on the ancilla to correct the phase. Does this make sense? This of course leads to an exponentially deep circuit, but that's a known issue with QPE.

(2) If this doesn't work, you can call qiskit-braket-provider from the run function before execution. If possible, we would like to limit this to 1- and 2q unitaries, which have standard and near-optimal solutions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've updated the PR to implement Option 1 (building the controlled-Grover operator from gate-level primitives). Here’s a summary of the changes:

  1. Gate-Level Controlled Grover: I replaced the to_unitary() matrix approach with a circuit-based implementation.
  • Made the oracle controlled by prepending "1" to the marked states (adding the QPE qubit as an extra control on the MCZ).
  • Made the diffusion operator controlled using controlled-Hadamard gates (decomposed via S·H·T·CX·T†·H·S†) and a controlled zero-oracle.
  1. Restored run_quantum_counting: Added it back to match the library's run_algorithm pattern and exported it in init.py.

  2. Updated Tests: Updated to use the new gate-level circuits and added tests for
    run_quantum_counting. All tests and linting pass.

  3. Updated Notebook: Replaced direct device.run() calls with run_quantum_counting, regenerated the circuit diagrams to show the native gates, and updated the note to mention that the circuit is now actually more compatible with QPU execution."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Very nice! Looks good, am just running the tests.

Comment thread notebooks/textbook/Quantum_Counting_Algorithm.ipynb
Comment thread src/braket/experimental/algorithms/quantum_counting/quantum_counting.py Outdated
Comment thread notebooks/textbook/Quantum_Counting_Algorithm.ipynb
Returns:
Circuit: The complete quantum counting circuit with result types.
"""
counting_circ.add_circuit(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Avoid mutating circuits by pattern like

new_circuit = Circuit().add_circuit(counting_circ)
...

@yitchen-tim
Copy link
Copy Markdown
Contributor

@sesmart I resolved the most of your comments that are solved. There is one that needs your eye on.

@axif0 I added a comment about avoiding side effect.

@axif0
Copy link
Copy Markdown
Contributor Author

axif0 commented Feb 26, 2026

@sesmart, @yitchen-tim hello, Is there anything else for improvement?

@axif0 axif0 requested review from sesmart and yitchen-tim February 26, 2026 15:33
controlled_matrix = np.kron(p0, id_matrix) + np.kron(p1, grover_unitary)

targets = [control] + list(target_qubits)
circ.unitary(matrix=controlled_matrix, targets=targets)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I made an error in suggesting to remove the run function -> the more common pattern here is to have a run_algorithm function, so if you could restore that, it would be great. My apologies.

I think there are maybe two options here, and it should close it out. We would like for there to be an actual circuit, and I agree that while the controlled unitary is non trivial, here there might be a reasonable way.

(1) For this particular U, I think you might be able to leverage the nature of the oracle a bit more. You could drop the amplify function, and then just call the oracle for "1" + the relevant states. I.e. in the diffussion operator "00" -> "100", and in the oracle, "xx" -> "1xx". Then you just need to implement the controlled H, with a Z on the ancilla to correct the phase. Does this make sense? This of course leads to an exponentially deep circuit, but that's a known issue with QPE.

(2) If this doesn't work, you can call qiskit-braket-provider from the run function before execution. If possible, we would like to limit this to 1- and 2q unitaries, which have standard and near-optimal solutions.

…nting and add `run_quantum_counting` function.
@axif0 axif0 requested a review from sesmart February 26, 2026 22:01

# Second set of controlled-H gates on search qubits
for sq in search_qubits:
_add_controlled_h(circ, control, sq)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lastly, I think you need a Z gate here - grover is 2 ss* - I, but I believe the oracle here is I - 2ss*. Might have just missed it.

… inherent -1 global phase, removing the need for post-processing phase adjustments.
@axif0 axif0 requested a review from sesmart February 27, 2026 17:10
@axif0
Copy link
Copy Markdown
Contributor Author

axif0 commented Feb 27, 2026

@sesmart, could you please share any suggestions? I’ve pushed the latest updates.

@sesmart
Copy link
Copy Markdown
Contributor

sesmart commented Feb 27, 2026

Looks good to go, thanks @axif0 !

@sesmart
Copy link
Copy Markdown
Contributor

sesmart commented Feb 27, 2026

@axif0, can you please comment on one of the algorithms issues in the BDK? It needs to be assigned but you have to comment first.

@sesmart sesmart merged commit 894ba33 into amazon-braket:main Feb 27, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants