Skip to content

Sorts gate array to have deterministic gate selection#16139

Merged
alexanderivrii merged 9 commits into
Qiskit:mainfrom
amruthpremjith:random-cliff-array-sort
May 24, 2026
Merged

Sorts gate array to have deterministic gate selection#16139
alexanderivrii merged 9 commits into
Qiskit:mainfrom
amruthpremjith:random-cliff-array-sort

Conversation

@amruthpremjith
Copy link
Copy Markdown
Contributor

AI/LLM disclosure

  • I didn't use LLM tooling, or only used it privately.
  • I used the following tool to help write this PR description:
  • I used the following tool to generate or modify code:

Fix #16138

Sort the gate array before selecting from it to return the same gates for the same set of inputs.

Tested with and without changes.

from qiskit.circuit.random.utils import random_clifford_circuit

cliff_circuit = random_clifford_circuit(num_qubits=10, num_gates=100, gates="all", seed=0)
print(cliff_circuit.count_ops())

Without change

(venv) amruthpremjith@MacBookPro qiskit % python3 test-6.py 
OrderedDict({'x': 10, 'h': 10, 'iswap': 9, 'cz': 9, 'z': 9, 's': 8, 'cy': 8, 'sx': 6, 'ecr': 6, 'cx': 6, 'dcx': 4, 'swap': 4, 'sdg': 4, 'y': 3, 'id': 3, 'sxdg': 1})
(venv) amruthpremjith@MacBookPro qiskit % python3 test-6.py
OrderedDict({'h': 10, 'sxdg': 10, 'iswap': 9, 'cz': 9, 'sdg': 9, 'z': 8, 'cy': 8, 'x': 6, 'ecr': 6, 'cx': 6, 'dcx': 4, 'swap': 4, 'sx': 4, 'id': 3, 'y': 3, 's': 1})

With change

(venv) amruthpremjith@MacBookPro qiskit % python3 test-6.py
OrderedDict({'z': 10, 'x': 10, 'ecr': 9, 'cy': 9, 'h': 9, 'sx': 8, 'cz': 8, 'id': 6, 'iswap': 6, 'cx': 6, 'swap': 4, 'dcx': 4, 'sxdg': 4, 's': 3, 'y': 3, 'sdg': 1})
(venv) amruthpremjith@MacBookPro qiskit % python3 test-6.py
OrderedDict({'z': 10, 'x': 10, 'ecr': 9, 'cy': 9, 'h': 9, 'sx': 8, 'cz': 8, 'id': 6, 'iswap': 6, 'cx': 6, 'swap': 4, 'dcx': 4, 'sxdg': 4, 's': 3, 'y': 3, 'sdg': 1})

@amruthpremjith amruthpremjith requested a review from a team as a code owner May 5, 2026 11:42
@amruthpremjith amruthpremjith requested a review from jakelishman May 5, 2026 11:42
@qiskit-bot qiskit-bot added the Community PR PRs from contributors that are not 'members' of the Qiskit repo label May 5, 2026
@qiskit-bot
Copy link
Copy Markdown
Collaborator

Thank you for opening a new pull request.

Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.

While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

@alexanderivrii
Copy link
Copy Markdown
Member

Thanks! That was fast. Do you think there is any meaningful test we could add? Could you also add a (short) bugfix release note?

@alexanderivrii alexanderivrii added the Changelog: Fixed Add a "Fixed" entry in the GitHub Release changelog. label May 5, 2026
@amruthpremjith
Copy link
Copy Markdown
Contributor Author

amruthpremjith commented May 5, 2026

Sure, maybe we can add a test case that runs it twice and compares the gate count?

@jakelishman
Copy link
Copy Markdown
Member

The randomisation of set with string keys is fixed by PYTHONHASHSEED, which is only read/set once during initialisation of the Python interpreter, so just doing the same thing twice in the same process would pass without this change. We can test either by comparing a decent-sized circuit to a "blessed" value, or by spawning separate interpreters with different PYTHONHASHSEEDs and ensuring they come back the same.

Comment thread qiskit/circuit/random/utils.py Outdated
Comment on lines +711 to +712
gates_1q = sorted(set(_BASIS_1Q.keys()) - {"v", "w", "id", "iden", "sinv"})
gates_2q = sorted(_BASIS_2Q.keys())
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.

sorted is perhaps a bit unnecessarily heavy as a normalisation step; dict is already consistently ordered (including the dict_keys iterator), so the sort of gates_2q isn't needed, and the top line can be something along the lines of

ignore_1q = {"v", "w", "id", "iden", "sinv"}
gates_1q = [gate for gate in _BASIS_1Q if gate not in ignore_1q]

and avoid the extra $\lg n$. (Not that this would be performance critical, but still.)

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 updated it to the snippet you mentioned, I was thinking it was straightforward to just sort it it to be consistent across any changes in the source dictionary as well.

@alexanderivrii
Copy link
Copy Markdown
Member

alexanderivrii commented May 5, 2026

Lol, Jake beat me at replying :)

@coveralls
Copy link
Copy Markdown

coveralls commented May 5, 2026

Coverage Report for CI Build 26029779428

Coverage decreased (-0.002%) to 87.694%

Details

  • Coverage decreased (-0.002%) from the base build.
  • Patch coverage: 1 of 1 lines across 1 file are fully covered (100%).
  • 16 coverage regressions across 5 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

16 previously-covered lines in 5 files lost coverage.

File Lines Losing Coverage Coverage
crates/qasm2/src/lex.rs 6 91.52%
crates/qasm2/src/parse.rs 6 97.63%
crates/circuit/src/parameter/symbol_expr.rs 2 73.93%
crates/circuit/src/parameter/parameter_expression.rs 1 91.04%
crates/qasm2/src/expr.rs 1 93.82%

Coverage Stats

Coverage Status
Relevant Lines: 123047
Covered Lines: 107905
Line Coverage: 87.69%
Coverage Strength: 952443.89 hits per line

💛 - Coveralls

@amruthpremjith
Copy link
Copy Markdown
Contributor Author

I added a test case that passes in different seeds to a subprocess, without the change the test case failed and it was passing with it.

@alexanderivrii
Copy link
Copy Markdown
Member

Thanks @amruthpremjith. I think the fix and the test look ok now (taking the comparing to a "blessed" value approach from Jake's comment, but I would like to have @jakelishman take another look as well.

@alexanderivrii
Copy link
Copy Markdown
Member

Some of the tests fail because we seem to get different blessed values on different platforms. @amruthpremjith, consider changing the test to check that all the results come back the same.

@amruthpremjith
Copy link
Copy Markdown
Contributor Author

@alexanderivrii, I've updated the test to first generate the circuit and then use that with varying hashes.

Copy link
Copy Markdown
Member

@alexanderivrii alexanderivrii left a comment

Choose a reason for hiding this comment

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

Thanks! I have retriggered CI, let's see what happens. The fix itself looks good, but I believe formatting might complain, please run black if you have not done so already.

check=True
)

# self.assertEqual(result.stdout, "OrderedDict({'sxdg': 10, 'sdg': 10, 'iswap': 9, 'cz': 9, 'id': 9, 'h': 8, 'cy': 8, 'x': 6, 'ecr': 6, 'cx': 6, 'dcx': 4, 'swap': 4, 's': 4, 'y': 3, 'sx': 3, 'z': 1})\n")
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.

You should remove this comment now.

@amruthpremjith
Copy link
Copy Markdown
Contributor Author

@alexanderivrii, I ran lint and added a bugfix note, could you please check now?

Copy link
Copy Markdown
Member

@alexanderivrii alexanderivrii 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 remembering to add the release note. I have left a few minor suggestions.

Comment on lines +2 to +19
# Delete all sections that you do not need. You can have more than one section in a file, and
# more than one bullet point per section. This is a regular YAML file, and all text should use
# Sphinx's flavor of rST (restructured text). Each bullet point is a separate "release note".
#
# Each public bug fix, feature, deprecation, and behavior change needs a release note.
# Documentation- or internal-only changes do not need release notes.
#
# Each bullet point appears without any context, so make sure each can be read in isolation. The
# reader will not see your code change, the issue, or any other bullet points in this file. You
# might need to repeat yourself between bullets.
#
# Use Sphinx cross-references like :meth:`.YourClass.your_method` whenever mentioning a
# function/class/method by name.
#
# Use `- |` for multiline YAML strings (like in the "fixes" example in this template) to preserve
# newlines, which you need for Sphinx syntax.
#
# There are some rare additional sections listed in `releasenotes/config.yaml` not included here.
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.

You can delete this comment (but pay attention not to delete the initial "---" line).


fixes:
- |
Always return consistent clifford circuits while calling ``random_clifford_circuit()`` with same inputs
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.

I think "deterministic" might be a bit more precise than "consistent", maybe something like:

Suggested change
Always return consistent clifford circuits while calling ``random_clifford_circuit()`` with same inputs
Fixed :func:`.random_clifford_circuit` to produce deterministic results across multiple runs given the same ``seed``.

- |
Always return consistent clifford circuits while calling ``random_clifford_circuit()`` with same inputs

Fixes `#16138 <https://github.com/Qiskit/qiskit/issues/16138>`. No newline at end of file
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.

You need the two trailing underscores for this to render nicely:

Suggested change
Fixes `#16138 <https://github.com/Qiskit/qiskit/issues/16138>`.
Fixes `#16138 <https://github.com/Qiskit/qiskit/issues/16138>`__.

Also you want an empty trailing line (see the circled minus sign when you look at the changed files).

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.

updated the comments, pls check

Copy link
Copy Markdown
Member

@alexanderivrii alexanderivrii left a comment

Choose a reason for hiding this comment

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

Thanks!

@alexanderivrii alexanderivrii added this pull request to the merge queue May 24, 2026
Merged via the queue into Qiskit:main with commit 61169fc May 24, 2026
45 of 47 checks passed
@ShellyGarion ShellyGarion added this to the 2.5.0 milestone May 24, 2026
@github-project-automation github-project-automation Bot moved this from Ready to Done in Qiskit 2.5 May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: Fixed Add a "Fixed" entry in the GitHub Release changelog. Community PR PRs from contributors that are not 'members' of the Qiskit repo

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

The function random_clifford_circuit should yield deterministic results on each program run.

6 participants