-
Notifications
You must be signed in to change notification settings - Fork 38
BackendV3
#77
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
base: master
Are you sure you want to change the base?
BackendV3
#77
Conversation
Move explanation to detailed design section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this @ElePT this is a great start. I did a quick read through it and left some initial thoughts inline.
Co-authored-by: Matthew Treinish <[email protected]>
What is the benefit of having a |
Co-authored-by: Matthew Treinish <[email protected]>
From a developer and API perspective these middle man objects have a clear purpose and intrinsic value. They maintain the separation of concerns and deal with different server-side APIs. Primitives are not lightweight, and having a single backend-sampler-estimator would be chaotic and difficult to manage. The decoupling allows for better maintainability, stability, and less breaking changes overall. They also already exist and are well established and documented, and previous migrations have shown that drastic changes such as "wiping out" objects are increasingly difficult to carry out. From a user perspective, the primitive examples also instantiate the primitives and define and set execution mode and options (shots, error mitigation and the works). If we were to merge backend and both primitives into a single backend-sampler-estimator, let's imagine that we are using
instead of the proposed
? Where the second one is compatible with current workflows and the first one would involve significant development and documentation (learning, etc) overhead. |
So the primary benefit is that I do not have to care/learn about another object. Especially one that is primarily an IBM Quantum construct. i.e. no one else has such things. Moreover, if you look at your example, you are treating the estimator instance as an extension of the backend rather than just using the backend itself. For example, the two options you set have nothing to do with computing expectation values, but are rather parameters used for execution on-chip, and have meaning outside of estimating. So something like: service = QiskitRuntimeService()
backend = service.backend("ibm_fez")
backend.update_execution_mode("batch")
backend.update_execution_options({"shots":1024, "rep_delay": 1e-4})
job = backend.estimate([pub]) makes a ton more sense. Notice how I also changed the method names because nothing you have done is actually an estimator setting, but rather execution option. There are really 3 sets of options that exist today: execution, error suppression, and error mitigation. The sampler and estimator really just cherry pick from these sets. So tying all to the backend instance allows me to do: job2 = backend.sample([qc]) and still have the above settings set. i.e. the job would be in batch mode with the selected shots and rep-delay. I cannot respond to the separation of concerns part because I do not see where that arises here? Namely, the underlying mode calls and calls to |
My argument is not to introduce something new, but rather not disrupt what already exists. We are not building a new model from scratch, primitives already exist in our ecosystem. We are not picking between two equal options and my stance here is that removing them at this point would cause more harm than benefit. This is why, instead of removing the notion of primitives, this is a proposal to streamline their creation and smoothen the curve for users. With |
At the end of the day do what you will I guess. By abstracting away the execution mode, you are going in the right direction, but somehow are not willing to go the final step to backend nirvana. Just seeing the notion of |
We are working with examples that are too overfitted to the |
Yeah no your correct. That is a false statement on my end. The rest I stand behind though. E.g. there is no sane world where |
|
||
service = QiskitRuntimeService() | ||
backend = service.backend("ibm_fez") | ||
estimator = Estimator(backend=backend, mode="batch", options={"shots":1024}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
qiskit-ibm-runtime estimator currently has this construction signature:
def __init__(
self,
mode: Optional[Union[BackendV2, Session, Batch]] = None,
options: Optional[Union[Dict, EstimatorOptions]] = None,
):
|
||
2. **The role of .run, why is it valuable** | ||
|
||
The role of `run()`, as defined in `BackendV2` often gets immediately translated to "return counts", which is more restrictive than what the interface dictates. `run()` is designed to be free form, the only requirements are that the input is a `QuantumCircuit` and the output is a `Result`. `Result` is purposely unrestricted, there are no requirements on the data it contains and the implementation details are left up to the implementer/vendor. There is a bias/convention towards returning `Counts` per circuit input in the job for historical reasons, and there are some formatting expectations if using those count mechanisms. But the output of `.run()` doesn't have to be "counts". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have trouble finding the value of abstracting an execution mode whose implementation is optional, and when implemented, whose output is free-form. It seems like the real motivation for keeping .run in the abstraction is for historical continuity.
|
||
**Optional Functionality** -> `QubitProperties` | ||
|
||
For historical reasons, `QubitProperties` remains an optional property on both the `Target` and `BackendV2` objects. This adds redundancy and ambiguity, as any functionality that uses `QubitProperties` must query both and establish rules on what takes precedence over what. This can be easily fixed in `BackendV3` by no longer storing `QubitProperties` in the backend, and keeping them exclusively as an optional `Target` property. Querying these properties would still be possible in `BackendV3` for backwards compatibility by aliasing the target query. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aliasing them would solve the problem of them being out of sync, but it doesn't seem to address the ambiguity problem, which I view as the real problem, as I suspect that the average user will not understand that it's an alias, and still wonder why the heck the same information seems to be in two places.
I would favor making a breaking change here.
From Qiskit Runtime's perspective, our users just went through great pain to migrate from |
For what it's worth, I have shared the proposal with internal Runtime users and the response was quite positive. Choosing the right primitive (especially when switching between local simulation and HW execution) is one of the biggest pain points for users and the biggest user-facing motivation for this proposal. |
If we don't plan to deprecate
Qiskit Runtime has a local mode that's meant to allow users to switch between local simulation and IBM HW without changing the code besides the |
This RFC summarizes a proposal for
BackendV3
that has been discussed for a few months already. The proposal is simple, but the discussions around the topic are not. The document focuses on adding historical context and capturing arguments for and against to clarify the motivation for the proposal.