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

Refactor BackendEstimator #9074

Closed
wants to merge 54 commits into from

Conversation

pedrorrivero
Copy link
Member

@pedrorrivero pedrorrivero commented Nov 3, 2022

Summary

Holistic refactor of the BackendEstimator class:

  • Partially informed by my work on extending the Estimator functionality for ZNE, but it is not limited to it.
  • Increases performance, and the API is actually enhanced without introducing any incompatibilities.
  • A common pattern through the entire refactor (and the reason why it had to be done holistically) was to avoid heavy use of side-effects, statefulness, and other global-like behavior which made the code difficult to predict and extend. For the most part, this can be considered a simplification and modularization of the previous code.

To-do

  • Spin-up multiple backend jobs if max_experiments is surpassed
  • Remove irrelevant TODOs
  • Transfer backend_estimator_dev to backend_estimator (and same thing for tests)

@qiskit-bot
Copy link
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 the following people are requested to review this:

@pedrorrivero
Copy link
Member Author

@jyu00

@coveralls
Copy link

coveralls commented Nov 3, 2022

Pull Request Test Coverage Report for Build 4057223086

  • 261 of 272 (95.96%) changed or added relevant lines in 1 file are covered.
  • 820 unchanged lines in 71 files lost coverage.
  • Overall coverage increased (+0.4%) to 85.222%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/primitives/backend_estimator_dev.py 261 272 95.96%
Files with Coverage Reduction New Missed Lines %
qiskit/algorithms/gradients/lin_comb_qgt.py 1 97.92%
qiskit/algorithms/gradients/lin_comb_sampler_gradient.py 1 96.83%
qiskit/algorithms/gradients/param_shift_estimator_gradient.py 1 95.24%
qiskit/algorithms/gradients/param_shift_sampler_gradient.py 1 95.74%
qiskit/algorithms/gradients/spsa_estimator_gradient.py 1 96.23%
qiskit/algorithms/gradients/spsa_sampler_gradient.py 1 96.97%
qiskit/primitives/sampler.py 1 95.12%
qiskit/transpiler/passes/optimization/commutative_cancellation.py 1 98.97%
qiskit/transpiler/passes/optimization/optimize_1q_commutation.py 1 99.11%
qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py 1 98.85%
Totals Coverage Status
Change from base Build 3940320475: 0.4%
Covered Lines: 66863
Relevant Lines: 78457

💛 - Coveralls

@pedrorrivero pedrorrivero mentioned this pull request Nov 4, 2022
29 tasks
@pedrorrivero pedrorrivero marked this pull request as ready for review November 14, 2022 22:40
@pedrorrivero
Copy link
Member Author

Once this is okay to go, I will substitute backend_estimator.py for backend_estimator_dev.py (and same for tests). I leave it as is for now to make it easier to review.

All tests from test_backend_estimator.py have been added to the new test suite as integration tests and they pass.

Copy link
Contributor

@ikkoham ikkoham left a comment

Choose a reason for hiding this comment

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

Thank you. I haven't seen the details yet, but I did see a little bit. I'm not too particular about code, so I personally don't have strong opinions about refactoring, but I do care about API compatibility and performance regressions.

qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
"""Backend to use for circuit measurements."""
return self._backend

@backend.setter
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need setter for backend?

Copy link
Member Author

Choose a reason for hiding this comment

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

Technically no, you are right! But now that all the logic is decoupled we can provide this functionality. If you think it is better to disallow this I am happy to remove it, let me know.

On top of that, I use the setter to (pseudo-)normalize attributes as explained in the previous thread 🙂

Copy link
Contributor

Choose a reason for hiding this comment

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

It is best not to make anything that does not have a use case. It makes maintenance difficult and may cause bugs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you remove this method?

@t-imamichi
Copy link
Member

I agree with Hamamura-san's comment #9074 (comment) about performance. Since this PR rewrote BackendEstimator, it is necessary to confirm the performance is as good as the current one.

Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

This isn't a proper review of the code (which I'm not equipped to do properly), more of a flick through and some comments.

A major thing: why is this being done? It's not clear to me why this PR should be accepted at all; the previous code was working, and there didn't appear (to me, who admittedly wasn't following closely) to be any structural issues causing problems for others, or bugs that couldn't be fixed due to poor structure of the code. If anything, this PR seems to add a lot of complexity to a class that's simply meant as a transitional object to get people running on the new APIs as fast as possible, while they're waiting for their providers to catch up properly.

Refactors aren't a priori a good thing - they offer far more chance for bugs to creep in, and they typically erase the institutional knowledge that was gained over the creation of the previous implementation. This doesn't appear to be fulfilling a new requirement, doesn't appear to be fixing bugs, and doesn't appear to simplify, so it's not clear to me why it should be merged.

At the very least, please can you update the top comment of the PR to explain exactly why this is being done for future knowledge? I'll then back off this PR - it's not my domain, and I'll leave any decision on approval / whether it's suitable to merge to @ikkoham and @t-imamichi as code owner.

qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
################################################################################
## IMPLEMENTATION
################################################################################
# TODO: caching
Copy link
Member

Choose a reason for hiding this comment

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

These large feature-request todos are likely to stay that way for ever unless it becomes separately clear that there's a need for them. It's probably best not to put them in at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, thanks! I plan to go over all the todos before merging, I'll make sure to remove as many as possible 🙂

# Pre-processing
circuit_bundles = self._preprocess(circuits, observables, parameter_values)
metadata_bundles = tuple(tuple(c.metadata for c in bun) for bun in circuit_bundles)
job_circuits = list(c for bun in circuit_bundles for c in bun) # TODO: accept tuple
Copy link
Member

Choose a reason for hiding this comment

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

this todo is simple; better to either do it (though I'm not sure why) or leave the comment.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, but it is not that simple because it is Backend.run() what does not accept Sequences other than list. Fixing this here is out of scope, hence the TODO (again, I'll open issues before merging and remove this from here).

qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
test/python/primitives/test_backend_estimator_dev.py Outdated Show resolved Hide resolved
test/python/primitives/test_backend_estimator_dev.py Outdated Show resolved Hide resolved
"""Test observable decomposer property."""
estimator = BackendEstimator(Mock(Backend))
self.assertTrue(estimator.abelian_grouping)
self.assertIsInstance(estimator._observable_decomposer, AbelianDecomposer)
Copy link
Member

Choose a reason for hiding this comment

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

Testing private components like this is generally not a good idea. They aren't part of the public interface, and writing hyper-explicit tests like this means that the structure of the underlying code cannot change without the tests also changing, which very easily masks bugs. There's a good amount of confidence given by a PR that modifies code, and all the tests easily pass, and it's very concerning when a PR needs to modify the tests to get them to pass, unless the test is clearly buggy.

Copy link
Member Author

Choose a reason for hiding this comment

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

Interesting 🤔 this is my way of making sure that the behavior for abelian_grouping stays consistent. True, it is a private property, but merely a reflection of a user configurable attribute.

I understand what you are saying and think that it may apply to other places, but I am not so sure here. If we change a what abelian_grouping means, shouldn't the tests fail? How would yo handle this otherwise?

Copy link
Member

@jakelishman jakelishman Nov 30, 2022

Choose a reason for hiding this comment

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

Presumably the user setting abelian_grouping=True should cause some changes to the output they see from public methods? I'd test that, rather than deep specifics of the internal implementation.

Let's say you accept mine and Takashi's arguments above about the ObservableDecomposer class and remove it. You could keep all the public behaviour exactly the same while inlining NaiveDecomposer and AbelianDecomposer, but all your tests will suddenly fail, and (I could well be wrong on this) I'm not certain there are any end-to-end tests that would fail if the abelian_grouping option were suddenly to disappear. In that case, are these tests helping us catch bugs, or are they just making busy work for anybody changing the code? Users don't care about how the internals are implemented, and user-facing behaviour is what we care about not breaking, primarily.

Copy link
Member Author

Choose a reason for hiding this comment

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

Presumably the user setting abelian_grouping=True should cause some changes to the output they see from public methods?

Negative... it only changes the way things are done internally. This is inherited from the original code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since transpiled_circuits are exposed, it can be tested from the length of transpiled QuantumCircuit. (or preprocessed circuits)

Comment on lines 463 to 409
@unpack
def test_measurement_coefficient(self, pauli, bitstring, expected):
"""Test measurement coefficients."""
pauli = Pauli(pauli)
coeff = BackendEstimator._measurement_coefficient(bitstring, pauli)
self.assertEqual(coeff, expected)
Copy link
Member

Choose a reason for hiding this comment

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

Here's another case where a private method is being unit-tested (there's lots more in this file). This sort of thing really tightly constrains the structure of the underlying code, whereas what we really need is for all publicly visible behaviour to remain the same. It should not be a problem if a later implementation wanted to inline this function, or move it off the class, or move it to Rust, or have it return some different object, as long as the public behaviour of the entry points remained the same.

Comment on lines 122 to 124
def circuit_composition_examples() -> Iterator[
tuple[QuantumCircuit, QuantumCircuit, QuantumCircuit]
]:
Copy link
Member

@jakelishman jakelishman Nov 16, 2022

Choose a reason for hiding this comment

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

This is a one-use function for parametrisation, and the objects it creates just get passed to a single builder function each - it would be better for the iterator just to be inlined in the data parametrisation for the specific test case. That would keep the data local to the test case, making it easier to read what's being tested.

Personally, I also find the building here very hard to read and track. I have to chase the logic through 5 different functions, which result in three different QuantumCircuits being created for the test case, and I have to remember the details of all of them to know whether I believe the test is correct or not. In general, test code should be obviously correct just from reading the test function, and I definitely wouldn't know that here.

(There's also the thing that all this complexity seems to just be in service of testing a private method anyway, which I already argued against.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Somewhat answered here: #9074 (comment)

@t-imamichi
Copy link
Member

Thank you for your comments, Jake. Actually, we have not discussed this refactoring beforehand. I think Pedro has some enhancement requests of BackendEstimator based on his ZNE project.

@pedrorrivero
Copy link
Member Author

Hi all, thanks for your reviews and comments. Just a few things to get us all in the same page:

  • This PR was migrated from an internal repo after internal discussion. This may have happened naturally during the different calls leading up to the Summit (e.g. Refactor BackendEstimator #9074 (comment)), so apologies if somebody did not hear about it.
  • As @t-imamichi says, this refactor is partially informed by my work on extending the Estimator functionality for ZNE, but it is not limited to it. I am happy to discuss offline if anyone is interested in the details which I cannot share publicly, although we have already commented some key points in our weekly meetings.
  • Concerning API compatibility and performance regressions, I think I have successfully shown in the relevant thread that this refactor increases performance, and the API is actually enhanced without introducing any incompatibilities. This was one of the main goals of this effort.
  • A common pattern through the entire refactor (and the reason why it had to be done holistically) was to avoid heavy use of side-effects, statefulness, and other global-like behavior which made the code difficult to predict and extend. For the most part, this can be considered a simplification and modularization of the previous code; although it introduces some performance and usability improvements as well.

Let's keep it up!

Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

I'm concerned that my understanding of your reasons aren't compatible with Terra's position as a fully open-source project, and with its position in the software stack.

You say you can't speak publicly about why you need some of these changes. If that's the case, how are we going to maintain those behaviours as invariant moving forwards, since we can't document the reasons? If things needed for IBM's "secret sauce", why are they being implemented here in Terra, and not in IBM-specific implementations of Estimator - one of the main reasons for the Estimator interface to exist in the first place? If there's things you can say publicly, it would be good to copy them into the PR context here - in 6 months' time, nobody will remember what was said in a meeting.

I actually think that "expanding the API" of this class is a bad thing, and I would be weakly opposed to it (but as I mentioned before, I won't attempt to exert control over something @ikkoham and @t-imamichi own). We don't really want people to use this class. It's here to ease users' transition into relying on the primitives, so they can take existing backends whose providers might not yet offer custom primitives, and raise them to the new API. We don't want people to be depending on this long term, and we certainly don't want them building advanced functionality off it specifically (as opposed to the general Estimator interface). Internal IBM code shouldn't be - we've got direct implementations of the primitives, and while we might use a class very similar to this internally, that's an internal choice, and not one that ought to be maintained in Terra's public interface.

I also don't agree that this has improved readability or maintainability of the class - you can see the evidence for that in the surprise that I, and to a lesser extent Takashi and Ikko, have had to some of the new structures and abstractions here, and all of us have experience as software developers. I get the impression that you might be a lot more comfortable with pure OO programming than the mixed-paradigm style that Python offers, but pure OO is not always (or even imo often) the right tool when you're not forced into it by the language. Here, I worry that overuse of OO-specific patterns is making things overcomplicated.


I've largely said what I had to say that's relevant, and there's clearly a big risk that we end up just talking about programming style, which is always going to vary between people. I don't agree with some of the design choices and I would be mildly opposed to merging any significant refactor of this class for the reasons I've given in previous comments (refactors should be "why?" not "why not?", and I don't buy the given "why") but my opinion is not very important in this area of Terra. Please don't consider this review a blocker despite my opposition and regardless of how you answer the questions I asked in various places - that decision is entirely Ikko and Takashi's as the code owners.

qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
################################################################################
## OBSERVABLE DECOMPOSER
################################################################################
class ObservableDecomposer(ABC):
Copy link
Member

@jakelishman jakelishman Nov 30, 2022

Choose a reason for hiding this comment

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

"Design Patterns" is specifically about pure OOP, and Python is not a pure OO language - we have first-class functions and far easier composition.

If we're talking programming principles, two absolute classics are "you aren't going to need it", and "keep it simple". For this 2-choice case, imo this inherited class structure is harder to read than basic conditional methods on the same class, since now the setup/teardown code for the abstract methods is separate from the rest of the logic; I have to chase the grouping logic through 3 possible places now to understand what might be happening. If we were implementing loads of options, it would be sensible to define an interface and invert the control (which isn't done here), but while there's only two, it's maybe overkill.

"""Test observable decomposer property."""
estimator = BackendEstimator(Mock(Backend))
self.assertTrue(estimator.abelian_grouping)
self.assertIsInstance(estimator._observable_decomposer, AbelianDecomposer)
Copy link
Member

@jakelishman jakelishman Nov 30, 2022

Choose a reason for hiding this comment

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

Presumably the user setting abelian_grouping=True should cause some changes to the output they see from public methods? I'd test that, rather than deep specifics of the internal implementation.

Let's say you accept mine and Takashi's arguments above about the ObservableDecomposer class and remove it. You could keep all the public behaviour exactly the same while inlining NaiveDecomposer and AbelianDecomposer, but all your tests will suddenly fail, and (I could well be wrong on this) I'm not certain there are any end-to-end tests that would fail if the abelian_grouping option were suddenly to disappear. In that case, are these tests helping us catch bugs, or are they just making busy work for anybody changing the code? Users don't care about how the internals are implemented, and user-facing behaviour is what we care about not breaking, primarily.

Comment on lines 403 to 408
@property
def _observable_decomposer(self) -> ObservableDecomposer:
"""Observable decomposer based on object's config."""
if self.abelian_grouping:
return AbelianDecomposer()
return NaiveDecomposer()
Copy link
Member

Choose a reason for hiding this comment

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

Creating a new class every time is fairly surprising behaviour for a property (it makes it unusually costly to query). I'm guessing it's done here because abelian_grouping is exposed as explicitly mutable state, but we'd still avoid these construction costs if decompose and build_pauli were just inlined into methods on this same class that dispatched on abelian_grouping in the same way.

The extra abstractions in this class makes it difficult to spot that this actually causes n_circuits * (n_observables + 1) individual decomposer classes to be instantiated on every call to _compute. This is the kind of thing I'm talking about when I say that too many properties kill us by a thousand papercuts; you'd always think that the expression self._observable_decomposer should be fine to call in a loop (as is done here), but we're actually allocating a whole new class every time it happens. Couple that with the _observable_decomposer property triggering a second internal property (abelian_grouping) on every lookup as well, and you can see how this stuff adds up. (I apply similar logic in my opposition of too many validation checks of type-hinted attributes that are supposed to be mutated / constructed quickly in Python too.)

Of course, removing these particular instances is unlikely to have any massive impact on performance in this case, but it's the general principle that this kind of abstraction-heavy code weighs things down, and it's easily avoidable.

Copy link
Member Author

@pedrorrivero pedrorrivero Nov 30, 2022

Choose a reason for hiding this comment

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

Do you have any metrics on how costly it is to instantiate a new class, and what is the performance gain by not doing it?

I ask because this saves memory by not having to maintain one of these objects in the instance dictionary, it reduces issues with mutability as you mention and some other minor things. So I would be interested to know what is the tradeoff.

Also, do you have data on this slow-down that properties cause as apposed to plain attributes? I'd like to address this on that footing rather than in the abstract.

Copy link
Contributor

@ikkoham ikkoham left a comment

Choose a reason for hiding this comment

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

I have started the review but will submit it in the middle of the review due to the volume.

Basically, all refactors are not recommended because of the risk. It would have been more acceptable if it were partial. It takes an enormous amount of time for both coding and review, but if you want to do it, I will do my best to review it.

"""Backend to use for circuit measurements."""
return self._backend

@backend.setter
Copy link
Contributor

Choose a reason for hiding this comment

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

It is best not to make anything that does not have a use case. It makes maintenance difficult and may cause bugs.

def __init__(
self,
backend: Backend,
*, # TODO: allow backend as positional after removing deprecations
Copy link
Contributor

Choose a reason for hiding this comment

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

This is API breaking.

# pylint: disable=missing-raises-doc
def __init__(
self,
backend: Backend,
Copy link
Contributor

Choose a reason for hiding this comment

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

Backend is broader than BackendV1 | BackendV2. It means this class supports BackendV3 in the future.

Copy link
Member Author

@pedrorrivero pedrorrivero Dec 1, 2022

Choose a reason for hiding this comment

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

As it should 🙂

It is equivalent now, nonetheless

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not the same. Clearly, the code only supports BackendV2 https://github.com/Qiskit/qiskit-terra/pull/9074/files#diff-15ffd51433f241eb453c3dc3eb62f89d82b1c3fbeeb43eea8c305a72f5d81054R111.
It's going to be an error in the future.

Copy link
Member Author

@pedrorrivero pedrorrivero Dec 5, 2022

Choose a reason for hiding this comment

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

Okay, I have a few questions regarding this:

  1. Is there any other class implementing Backend other than BackendV1 and BackendV2 as of today?
  2. Are there any ongoing plans for BackendV3, and when is it expected to release?

The self.backend property is currently normalized to BackendV2, which does not mean that it does not support BackendV1 or could not support other types in the future. As a matter of fact, it does handle BackendV1 through BackendV2Converter (i.e. through backend.setter), and when a new version comes out,BackendV2Converter should be updated in order to account for that (i.e. to convert to V2 from this new class) and this code will not error.

Copy link
Contributor

@ikkoham ikkoham Jan 17, 2023

Choose a reason for hiding this comment

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

Can you remember to update when a new version is released? If you are unaware and a new version is released, this will be a bug. We can't guarantee the future, so we do the best we can at this time.

qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
################################################################################
## OBSERVABLE DECOMPOSER
################################################################################
class ObservableDecomposer(ABC):
Copy link
Contributor

Choose a reason for hiding this comment

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

It is a premature abstraction. When we want to implement a Grouper (Decomposer) other than them, this design cannot handle it? Only one qubit measurement basis can be represented.

qiskit/primitives/backend_estimator_dev.py Show resolved Hide resolved
qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
max_circuits: int = getattr(self.backend, "max_circuits", None) or total_circuits

# Raw results
jobs: tuple[Job] = tuple(
Copy link
Contributor

Choose a reason for hiding this comment

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

Unnecessary type conversion to tuple makes difficult to read.

Copy link
Member Author

Choose a reason for hiding this comment

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

No type conversion is happening in this line

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. I know. But it is simply difficult to read. It's not Pythonic.

Copy link
Member Author

Choose a reason for hiding this comment

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

Original:

jobs = [
    backend.run(circuits[pos : pos + max_circuits], **run_options)
    for pos in range(0, len(circuits), max_circuits)
]

Refactor:

jobs: tuple[Job] = tuple(
    self.backend.run(circuits[split : split + max_circuits], **run_options)
    for split in range(0, total_circuits, max_circuits)
)

I don't see much of an stylistic difference other than the type annotation, and use of an immutable data structure instead of a mutable one (since the resulting instance should not be altered). Not sure how this makes it less pythonic, but what would you propose instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

The default in the language specification is list, and there is no reason to include unnecessary conversions. I also find the latter more difficult because I have to convert it once in my head.

@ikkoham ikkoham self-assigned this Jan 13, 2023
@ikkoham ikkoham added the mod: primitives Related to the Primitives module label Jan 13, 2023
qiskit/primitives/backend_estimator_dev.py Show resolved Hide resolved
"""Backend to use for circuit measurements."""
return self._backend

@backend.setter
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you remove this method?

qiskit/primitives/backend_estimator_dev.py Show resolved Hide resolved
# pylint: disable=missing-raises-doc
def __init__(
self,
backend: Backend,
Copy link
Contributor

@ikkoham ikkoham Jan 17, 2023

Choose a reason for hiding this comment

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

Can you remember to update when a new version is released? If you are unaware and a new version is released, this will be a bug. We can't guarantee the future, so we do the best we can at this time.

Copy link
Contributor

@ikkoham ikkoham left a comment

Choose a reason for hiding this comment

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

Overall LGTM if comments are achieved.

Especially, as Jake reviewed, we need to rethink how we test. Private testing is undesirable.

qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
counts_iter = (counts for job_counts in job_counts_iter for counts in job_counts)
counts_list: list[Counts] = []
for counts, circuit in zip(counts_iter, circuits):
counts.metadata = circuit.metadata # TODO: add `Counts.metadata` attr
Copy link
Contributor

Choose a reason for hiding this comment

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

Dynamic attributes

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this is in fact the worst thing from this design.

I hate it.

But at the same time it makes the code significantly cleaner and easier to follow by simply making the metadata in the executed circuits be ported to the associated counts metadata. I would like to propose this as new PR.

self,
observable: SparsePauliOp,
) -> tuple[SparsePauliOp]:
return tuple(observable)
Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer list to tuple for variable length. (not strong opinion but I can't find the reason of immutable)

Copy link
Member Author

Choose a reason for hiding this comment

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

Main reason for this to be immutable is that the decomposition is fixed, and it does not make sense to mutate it. If you you change it will no longer be the decomposition so better to have it as immutable to avoid accidental updates.

qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
qiskit/primitives/backend_estimator_dev.py Outdated Show resolved Hide resolved
@ikkoham
Copy link
Contributor

ikkoham commented Jan 17, 2023

I opened the PR to reflect comments: pedrorrivero#1

pedrorrivero and others added 5 commits January 31, 2023 12:28
Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
@pedrorrivero
Copy link
Member Author

pedrorrivero commented Jan 31, 2023

After careful consideration and offline discussions I have decided to drop this PR and open a new repo for StagedPrimitives. This will allow rapid iteration avoiding deprecation cycles and will serve as a proxy to test new features and evolve the specification to something more versatile.

The code has now been successfully migrated, so I proceed to close this PR. Thank you very much to everyone for your contributions!

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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants