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

Add Statevector-based SamplerV2 #11566

Merged
merged 6 commits into from Jan 18, 2024
Merged

Conversation

t-imamichi
Copy link
Member

@t-imamichi t-imamichi commented Jan 15, 2024

Summary

Add Statevector-based Sampler V2 based on BaseSamplerV2 #11529.

This uses ongoing PRs #11543 #11552 and #11529 and we need to wait for them to be merged.
This PR will be rebased once they are merged.

previous version #11264

Details and comments

@t-imamichi t-imamichi added on hold Can not fix yet mod: primitives Related to the Primitives module labels Jan 15, 2024
@t-imamichi t-imamichi mentioned this pull request Jan 15, 2024
4 tasks
@coveralls
Copy link

coveralls commented Jan 15, 2024

Pull Request Test Coverage Report for Build 7571175775

Warning: This coverage report may be inaccurate.

We've detected an issue with your CI configuration that might affect the accuracy of this pull request's coverage report.
To ensure accuracy in future PRs, please see these guidelines.
A quick fix for this PR: rebase it; your next report should be accurate.

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.02%) to 89.491%

Totals Coverage Status
Change from base Build 7561569421: 0.02%
Covered Lines: 59463
Relevant Lines: 66446

💛 - Coveralls

@t-imamichi
Copy link
Member Author

The file names and class names are tentative.

@t-imamichi
Copy link
Member Author

I'm wondering shots=0 is allowed or not. Can BitArray handle shots=0?
If shots=0 is not allowed, we need to change the validation of SamplerPub.

@t-imamichi t-imamichi changed the title [WIP] Add Statevector-based SamplerV2 Add Statevector-based SamplerV2 Jan 17, 2024
@t-imamichi t-imamichi marked this pull request as ready for review January 17, 2024 10:18
@t-imamichi t-imamichi requested review from a team as code owners January 17, 2024 10:18
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

  • @Qiskit/terra-core
  • @ajavadia
  • @ikkoham
  • @levbishop
  • @t-imamichi

Co-authored-by: Ian Hincks <ian.hincks@gmail.com>
Copy link
Member

@chriseclectic chriseclectic left a comment

Choose a reason for hiding this comment

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

Looks good, couple questions and suggestions. The only major change I would say is to change the seed behavior to give deterministic output to every run call with the same input pubs.

qreg_indices: list[int]


class Sampler(BaseSamplerV2):
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to call it Sampler or StatevectorSampler ? In 11227 the reference estimator is called StatevectorEstimator

Copy link
Contributor

Choose a reason for hiding this comment

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

I slightly prefer StatevectorSampler, but more importantly, they should have the same convention.

Copy link
Member Author

Choose a reason for hiding this comment

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

The reference impl of SamplerV1 is called just Sampler link. I'm wondering calling the new one SamplerV2 or StatevectorSampler. I will follow the convention of #11227.

qiskit/primitives/statevector_sampler.py Outdated Show resolved Hide resolved
Simple implementation of :class:`BaseSamplerV2` with Statevector.
"""

_DEFAULT_SHOTS: int = 512
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
_DEFAULT_SHOTS: int = 512

I would replace this with init kwarg

Copy link
Contributor

Choose a reason for hiding this comment

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

I suggested not putting it as a kwarg to discourage users from assuming that constructors are the "right" place to set shots. If it is placed in the constructor, consider calling it "default_shots". IMO, its fine right here as a class atribute, but if you both prefer it in the constructor, please go ahead.

Copy link
Member Author

Choose a reason for hiding this comment

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

Here is the original Ian's suggestion #11566 (comment), JFYI.
I made default_shots kwarg.

Comment on lines 64 to 67
if isinstance(self._seed, np.random.Generator):
self._rng = self._seed
else:
self._rng = np.random.default_rng(self._seed)
Copy link
Member

Choose a reason for hiding this comment

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

You can seed a default_rng with another generator so this can always be np.random.default_rng(seed) even when seed is a generator. Eg

rng1 = np.random.default_rng(10)
rng2 = np.random.default_rng(rng1)
rng2.integers(10, size=10)

and this is the same as using a fixed seed for rng2. Hence you can just have

Suggested change
if isinstance(self._seed, np.random.Generator):
self._rng = self._seed
else:
self._rng = np.random.default_rng(self._seed)
self._rng = np.random.default_rng(self._seed)

Copy link
Member Author

Choose a reason for hiding this comment

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

To be deterministic, we don't need _rng and I put _seed directly to Statevector.

qiskit/primitives/statevector_sampler.py Outdated Show resolved Hide resolved
qiskit/primitives/statevector_sampler.py Outdated Show resolved Hide resolved
qiskit/primitives/statevector_sampler.py Outdated Show resolved Hide resolved
qiskit/primitives/statevector_sampler.py Outdated Show resolved Hide resolved
final_state = Statevector(bound_circuit_to_instruction(bound_circuit))
final_state.seed(self._rng)
if qargs:
samples = final_state.sample_memory(shots=pub.shots, qargs=qargs)
Copy link
Member

Choose a reason for hiding this comment

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

TODO For the future: we should add a sample method to Statevector (or make a helper function here) that directly samples in the BitArray format rather than constructing bit strings and converting them which is going to be more inefficient.

self.assertEqual(len(result), 1)
self._assert_allclose(result[0].data.meas, np.array({1: self._shots}))

def test_circuit_with_multiple_cregs(self):
Copy link
Member

Choose a reason for hiding this comment

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

It might be good to also add a test where there are aliased registers, which can happen if you compose a circuit with conditionals into another circuit with a flat register. Eg for a bell teleportation like one

q = QuantumRegister(3, 'q')
c1 = ClassicalRegister(1, 'c1')
c2 = ClassicalRegister(1, 'c2')

qc = QuantumCircuit(q, c1, c2)
qc.h(2)
qc.cx(2, 1)
qc.cx(0, 1)
qc.h(0)
qc.measure(0, c1)
qc.measure(1, c2)
qc.z(2).c_if(c1, 1)
qc.x(2).c_if(c2, 1)
qc.draw()


qc2 = QuantumCircuit(5, 5)
qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True)
qc2.cregs

qc2 will have cregs

[ClassicalRegister(5, 'c'),
 ClassicalRegister(1, 'c4'),
 ClassicalRegister(1, 'c5')]

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 found that register alias renamed register names ('c1' -> 'c4', 'c2' -> 'c5' in your example).
Is it possible to keep the register names?

Here is a obvious case.

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister

q = QuantumRegister(3, 'q')
c1 = ClassicalRegister(1, 'c123456')
c2 = ClassicalRegister(1, 'ccccccc')

qc = QuantumCircuit(q, c1, c2)
qc.h(2)
qc.cx(2, 1)
qc.cx(0, 1)
qc.h(0)
qc.measure(0, c1)
qc.measure(1, c2)
qc.z(2).c_if(c1, 1)
qc.x(2).c_if(c2, 1)
print(qc.cregs)
# [ClassicalRegister(1, 'c123456'), ClassicalRegister(1, 'ccccccc')]
qc2 = QuantumCircuit(5, 5)
qc2.compose(qc, [0, 2, 3], [2, 4], inplace=True)
print(qc2.cregs)
# [ClassicalRegister(5, 'c'), ClassicalRegister(1, 'c0'), ClassicalRegister(1, 'c1')]

Copy link
Member Author

Choose a reason for hiding this comment

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

Anyways, I added aliased registers to databin though they are renamed.

qiskit/primitives/base/base_sampler.py Outdated Show resolved Hide resolved
qreg_indices: list[int]


class Sampler(BaseSamplerV2):
Copy link
Contributor

Choose a reason for hiding this comment

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

I slightly prefer StatevectorSampler, but more importantly, they should have the same convention.

Simple implementation of :class:`BaseSamplerV2` with Statevector.
"""

_DEFAULT_SHOTS: int = 512
Copy link
Contributor

Choose a reason for hiding this comment

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

I suggested not putting it as a kwarg to discourage users from assuming that constructors are the "right" place to set shots. If it is placed in the constructor, consider calling it "default_shots". IMO, its fine right here as a class atribute, but if you both prefer it in the constructor, please go ahead.

qiskit/primitives/statevector_sampler.py Outdated Show resolved Hide resolved
@ihincks ihincks removed the on hold Can not fix yet label Jan 17, 2024
Co-authored-by: Ian Hincks <ian.hincks@gmail.com>
Co-authored-by: Christopher J. Wood <cjwood@us.ibm.com>
@t-imamichi
Copy link
Member Author

I think BitArray cannot support shots=0. How about updating SumplerPub.{validate, coerce} to check shots=0?
The change will be like t-imamichi/qiskit-terra@sampler-v2-rev2...t-imamichi:qiskit-terra:sampler-v2-shots

#11566 (comment)

Comment on lines 197 to 191
creg = loc.registers[0]
qbit = circuit.find_bit(item.qubits[0]).index
if cbit in active_cbits and qbit in active_qubits:
mapping[creg] = qbit
for creg in loc.registers:
mapping[creg] = qbit
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: This change supports aliased classical registers.

meas1 = result1[0].data.meas
result2 = sampler.run([bell], shots=self._shots).result()
meas2 = result2[0].data.meas
self._assert_allclose(meas1, meas2, rtol=0)
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: Let rtol=0 to check two results are exactly same.

ihincks
ihincks previously approved these changes Jan 18, 2024
Copy link
Contributor

@ihincks ihincks left a comment

Choose a reason for hiding this comment

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

LGTM

Edit: I saw your comment about shots=0 after reading through the files and approving. I agree we should eliminate the possibility that shots=0 in the SamplerPub.

@t-imamichi
Copy link
Member Author

Thanks. I updated this PR to raise ValueError with shots=0.

Copy link
Contributor

@ihincks ihincks left a comment

Choose a reason for hiding this comment

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

Thanks!

Copy link
Member

@chriseclectic chriseclectic left a comment

Choose a reason for hiding this comment

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

LGTM

@chriseclectic chriseclectic added this pull request to the merge queue Jan 18, 2024
Merged via the queue into Qiskit:main with commit a0b2996 Jan 18, 2024
12 checks passed
@t-imamichi t-imamichi deleted the sampler-v2-rev2 branch January 19, 2024 00:31
@1ucian0
Copy link
Member

1ucian0 commented Jan 19, 2024

This PR tracks RFC 0016 https://github.com/Qiskit/RFCs/blob/master/0016-sampler-interface.md . With it's merge, can RFC 0016 be consider implemented?

@ihincks
Copy link
Contributor

ihincks commented Jan 19, 2024

Yes, in fact I think #11529 could have been technically sufficient, but it's nice to have a reference implementation here, too.

ihincks added a commit to ihincks/qiskit-terra that referenced this pull request Jan 19, 2024
* Add Statevector-based BaseSamplerV2 implementation

Co-authored-by: Ian Hincks <ian.hincks@gmail.com>

* Apply suggestions from code reivew

Co-authored-by: Ian Hincks <ian.hincks@gmail.com>
Co-authored-by: Christopher J. Wood <cjwood@us.ibm.com>

* Apply suggestions from code review

* shots should be positive

* add tests of zero shots

---------

Co-authored-by: Ian Hincks <ian.hincks@gmail.com>
Co-authored-by: Christopher J. Wood <cjwood@us.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mod: primitives Related to the Primitives module priority: high
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants