Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Rust generator function for QuantumVolume class #13283

Merged
merged 4 commits into from
Oct 16, 2024

Conversation

mtreinish
Copy link
Member

Summary

In #13238 we added a new function quantum_volume for generating a quantum volume model circuit with the eventual goal of replacing the existing QuantumVolume class. This new function is ~10x faster to generate the circuit than the existing python class. This commit builds off of that to internally call the new function for generating the circuit from the class. While the plan is to deprecate and remove the class for Qiskit 2.0 until that time we can give a performance boost to users of it for the lifespan of the 1.x release series.

Details and comments

In Qiskit#13238 we added a new function quantum_volume for generating a
quantum volume model circuit with the eventual goal of replacing the
existing QuantumVolume class. This new function is ~10x faster to
generate the circuit than the existing python class. This commit builds
off of that to internally call the new function for generating the
circuit from the class. While the plan is to deprecate and remove the
class for Qiskit 2.0 until that time we can give a performance boost to
users of it for the lifespan of the 1.x release series.
@mtreinish mtreinish added performance Changelog: New Feature Include in the "Added" section of the changelog Changelog: API Change Include in the "Changed" section of the changelog mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library labels Oct 4, 2024
@mtreinish mtreinish added this to the 1.3.0 milestone Oct 4, 2024
@mtreinish mtreinish requested a review from a team as a code owner October 4, 2024 22:11
@qiskit-bot
Copy link
Collaborator

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

  • @Cryoris
  • @Qiskit/terra-core
  • @ajavadia

Copy link
Contributor

@kevinhartman kevinhartman left a comment

Choose a reason for hiding this comment

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

Looks mostly good, other than the test failure regarding seeding.

@@ -83,39 +83,48 @@ def __init__(
depth = depth or num_qubits # how many layers of SU(4)
width = num_qubits // 2 # how many SU(4)s fit in each layer
rng = seed if isinstance(seed, np.random.Generator) else np.random.default_rng(seed)
seed_name = seed
if seed is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO it would be clearer to check if seed_name is None instead, since the purpose of this branch is to make sure it has a value.

else:
self._append(CircuitInstruction(qv_circ.to_instruction(), tuple(self.qubits)))
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we instead just early-return here instead of having the else on line 107? That way, it's a bit clearer that the handling of classical permutations is treated specially and forwarded to the Rust quantum_volume.

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't return because __init__ normally doesn't have a return; that's an implicit None but there is a pylint rule that doesn't like an explicit return in one path and not another.

Copy link
Member

Choose a reason for hiding this comment

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

I think pylint is fine with bare return for early return, it's return None it has a problem with (unless it's changed).

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, that's probably it, I normally would do return None in this case. I can change it to a bare return if there is a strong preference here.

@mtreinish
Copy link
Member Author

I did a quick speed test of a 100q qv model circuit, with QuantumVolume(100, 100, seed=42) with this PR it takes 0.06909546643420132 sec and with 1.2.4 it took: 0.08987236272822026 sec (averaged over 50 runs). Most of the time is spent in to_instruction() for _append

@coveralls
Copy link

Pull Request Test Coverage Report for Build 11243570672

Details

  • 20 of 21 (95.24%) changed or added relevant lines in 1 file are covered.
  • 19 unchanged lines in 4 files lost coverage.
  • Overall coverage increased (+0.009%) to 88.884%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/circuit/library/quantum_volume.py 20 21 95.24%
Files with Coverage Reduction New Missed Lines %
qiskit/transpiler/passes/synthesis/unitary_synthesis.py 2 88.26%
crates/qasm2/src/lex.rs 4 92.98%
crates/qasm2/src/parse.rs 6 97.61%
qiskit/synthesis/two_qubit/xx_decompose/decomposer.py 7 90.91%
Totals Coverage Status
Change from base Build 11241264267: 0.009%
Covered Lines: 74763
Relevant Lines: 84113

💛 - Coveralls

CircuitInstruction(gate, (qubits[perm[qubit]], qubits[perm[qubit + 1]]))
)
if classical_permutation:
if seed is not None:
Copy link
Contributor

Choose a reason for hiding this comment

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

I might be slower than usual as I'm reviewing this before my first coffee, but should this be the following?

Suggested change
if seed is not None:
if seed is None:

Copy link
Member Author

Choose a reason for hiding this comment

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

This is actually correct. This code path is to normalize the seeds we pass to rust so it will fit in a uint64 (although I realized I used int64 by mistake, I'll fix that). The typing on the python class was a python int which was doesn't have an upper bound and it was possible to pass in an value greater than 18446744073709551615 which would cause an error when one didn't happen before. To solve that I used a numpy rng to select a random int from the provided seed value that we then pass to rust as a seed for it's internal rng.

When seed is None we can just pass that to rust because it will use system entropy to initialize the rust rng.

@Cryoris Cryoris added this pull request to the merge queue Oct 16, 2024
Merged via the queue into Qiskit:main with commit 50f2965 Oct 16, 2024
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: API Change Include in the "Changed" section of the changelog Changelog: New Feature Include in the "Added" section of the changelog mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants