From fa864d7d5be6e64f5b7ca90bebc209dbeeb4a29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Tue, 17 Dec 2024 12:22:55 +0100 Subject: [PATCH 01/13] Initial content proposal --- .../external-providers-primitives-v2.mdx | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 docs/migration-guides/external-providers-primitives-v2.mdx diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx new file mode 100644 index 00000000000..20a787d6a95 --- /dev/null +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -0,0 +1,233 @@ +--- +title: TODO +description: Migrate to the primitives interface in external providers + +--- + +# Motivation + +As quantum processing units (QPUs) scale and quantum algorithms evolve, +the Qiskit Primitives interface was introduced to provide a higher-level abstraction for +accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: +circuit sampling (Sampler) and expectation value estimation (Estimator). + +The Primitives interface has replaced the legacy `backend.run` model for user access of IBM Quantum Hardware, +resulting in its deprecation and removal from the Qiskit Runtime service. A series of migration guides have +been developed for users to upgrade their workflows to use the different primitive implementations available +in the qiskit ecosystem, including the runtime primitives for real hardware access, the statevector primitives +for local statevector simulation, or the aer primitives for access to local noisy simulation. + +This migration guide, however, has been designed to help external providers implement their own flavor of the +primitives V2 interface (Sampler or Estimator) for easier pluggability into the standard qiskit workflow. These +implementations can be used to wrap any custom provider hardware access function +(for example: `execute_workload(QPU)` or `resource.access()`) +as long as the final inputs and outputs conform to the established contract from the interface. + +## If the external provider already implemented `backend.run` + +The qiskit SDK offers ready-to-go wrappers for `backend.run` that can be easily adapted to a custom primitives workflow +through subclassing, these are the `BackendSamplerV2` (API ref link) and `BackendEstimatorV2` (API ref link). The inputs +to the primitives should follow the pub syntax specified in the V2 primitives interface (link to pub docs). + +An advantage of this strategy is that the wrapper can handle the input/output manipulation, so knowledge of the PUB data +model is not required. However, this might result in a sub-optimal runtime, which could be refined through a fully +custom primitives implementation. + +The snippets below show how to create a custom Estimator instance following the strategy described above. +The process would be analogous for a custom Sampler, modifying the base class to BackendSamplerV2. + +``` python +class CustomEstimator(BackendEstimatorV2): + """:class:`BaseEstimatorV2 ` primitive for custom provider.""" + + _backend: CustomProviderResource + + def __init__( + self, + backend: CustomProviderResource, + options: dict | None = None, + extra_flag_used_in_estimation: bool = True, + another_extra_flag: bool = False, + ) -> None: + """ + Args: + backend: Custom provider resource to evaluate circuits on. + options: options passed to through to the underlying BackendEstimatorV2. + extra_flag_used_in_estimation: if `False`, do this. + another_extra_flag: if `True`, do that, + """ + + # preprocess arguments if necessary according to custom flags + processed_backend = ... + processed_options = ... + + super().__init__( + processed_backend, + options=processed_options, + ) + + @property + def backend(self) -> CustomProviderResource: + """Computing resource used for circuit evaluation.""" + return self._backend +``` + +## If the external provider didn't implement `backend.run` or you look for a fully custom implementation + +If a new provider is developed that doesn't conform to the legacy `backend.run` interface, the pre-packaged +wrapper may not be the most optimal route for implementing the primtives. Instead, you should implement a particular +instance of the abstract base primitive interfaces (link to BaseSamplerV2 and BaseEstimatorV2). This route requires an +understanding of the PUB data model for input/output handling. + +The following snippet shows a minimal example of an implementation of a custom Sampler primitive following this strategy. +This example has been extracted and generalized from the StatevectorSampler implementation. The full original +implementation can be found in the StatevectorSampler source code. + +``` python +from qiskit.primitives.base import BaseSamplerV2 +from qiskit.primitives.containers import ( + BitArray, + DataBin, + PrimitiveResult, + SamplerPubResult, + SamplerPubLike, +) +from qiskit.primitives.containers.sampler_pub import SamplerPub +from qiskit.primitives.primitive_job import PrimitiveJob +... + +class CustomStatevectorSampler(BaseSamplerV2): + + def __init__(self, *, default_shots: int = 1024, seed: np.random.Generator | int | None = None): + """ + Args: + default_shots: The default shots for the sampler if not specified during run. + seed: The seed or Generator object for random number generation. + If None, a random seeded default RNG will be used. + """ + self._default_shots = default_shots + self._seed = seed + + ... + + def run( + self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None + ) -> PrimitiveJob[PrimitiveResult[SamplerPubResult]]: + if shots is None: + shots = self._default_shots + coerced_pubs = [SamplerPub.coerce(pub, shots) for pub in pubs] + if any(len(pub.circuit.cregs) == 0 for pub in coerced_pubs): + warnings.warn( + "One of your circuits has no output classical registers and so the result " + "will be empty. Did you mean to add measurement instructions?", + UserWarning, + ) + + job = PrimitiveJob(self._run, coerced_pubs) + job._submit() + return job + + def _run(self, pubs: Iterable[SamplerPub]) -> PrimitiveResult[SamplerPubResult]: + results = [self._run_pub(pub) for pub in pubs] + return PrimitiveResult(results, metadata={"version": 2}) + + def _run_pub(self, pub: SamplerPub) -> SamplerPubResult: + circuit, qargs, meas_info = _preprocess_circuit(pub.circuit) + bound_circuits = pub.parameter_values.bind_all(circuit) + arrays = { + item.creg_name: np.zeros( + bound_circuits.shape + (pub.shots, item.num_bytes), dtype=np.uint8 + ) + for item in meas_info + } + for index, bound_circuit in enumerate(bound_circuits): + + # ACCESS PROVIDER RESOURCE HERE + # in this case, we are showing an illustrative implementation + samples_array = ProviderResource.sample() + + for item in meas_info: + ary = _samples_to_packed_array(samples_array, item.num_bits, item.qreg_indices) + arrays[item.creg_name][index] = ary + + meas = { + item.creg_name: BitArray(arrays[item.creg_name], item.num_bits) for item in meas_info + } + return SamplerPubResult( + DataBin(**meas, shape=pub.shape), + metadata={"shots": pub.shots, "circuit_metadata": pub.circuit.metadata}, + ) +``` + +The mechanics to implement a custom Estimator are analogous to the Sampler, but may require an extra pre/post-processing +step in the run method to extract expectation values from samples. In the case of the custom SatevectorEstimator that will be +used as reference, we encapsulate thi computation in the `_statevector_from_circuit` utility function. + + +``` python +from .base import BaseEstimatorV2 +from .containers import DataBin, EstimatorPubLike, PrimitiveResult, PubResult +from .containers.estimator_pub import EstimatorPub +from .primitive_job import PrimitiveJob +from .utils import _statevector_from_circuit +... + +class CustomStatevectorEstimator(BaseEstimatorV2): + + def __init__( + self, *, default_precision: float = 0.0, seed: np.random.Generator | int | None = None + ): + """ + Args: + default_precision: The default precision for the estimator if not specified during run. + seed: The seed or Generator object for random number generation. + If None, a random seeded default RNG will be used. + """ + self._default_precision = default_precision + self._seed = seed + + ... + + def run( + self, pubs: Iterable[EstimatorPubLike], *, precision: float | None = None + ) -> PrimitiveJob[PrimitiveResult[PubResult]]: + if precision is None: + precision = self._default_precision + coerced_pubs = [EstimatorPub.coerce(pub, precision) for pub in pubs] + + job = PrimitiveJob(self._run, coerced_pubs) + job._submit() + return job + + def _run(self, pubs: list[EstimatorPub]) -> PrimitiveResult[PubResult]: + return PrimitiveResult([self._run_pub(pub) for pub in pubs], metadata={"version": 2}) + + def _run_pub(self, pub: EstimatorPub) -> PubResult: + rng = np.random.default_rng(self._seed) + circuit = pub.circuit + observables = pub.observables + parameter_values = pub.parameter_values + precision = pub.precision + bound_circuits = parameter_values.bind_all(circuit) + bc_circuits, bc_obs = np.broadcast_arrays(bound_circuits, observables) + evs = np.zeros_like(bc_circuits, dtype=np.float64) + stds = np.zeros_like(bc_circuits, dtype=np.float64) + for index in np.ndindex(*bc_circuits.shape): + bound_circuit = bc_circuits[index] + observable = bc_obs[index] + final_state = _statevector_from_circuit(bound_circuit, rng) + paulis, coeffs = zip(*observable.items()) + obs = SparsePauliOp(paulis, coeffs) + expectation_value = np.real_if_close(final_state.expectation_value(obs)) + if precision != 0: + if not np.isreal(expectation_value): + raise ValueError("Given operator is not Hermitian and noise cannot be added.") + expectation_value = rng.normal(expectation_value, precision) + evs[index] = expectation_value + + data = DataBin(evs=evs, stds=stds, shape=evs.shape) + return PubResult( + data, metadata={"target_precision": precision, "circuit_metadata": pub.circuit.metadata} + ) +``` + From aa485135badcb0123f1226a3eb01c5770ce8f710 Mon Sep 17 00:00:00 2001 From: Rebecca Dimock Date: Tue, 17 Dec 2024 09:29:57 -0600 Subject: [PATCH 02/13] Test push --- .../external-providers-primitives-v2.mdx | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 20a787d6a95..54a7eaca22b 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -6,34 +6,34 @@ description: Migrate to the primitives interface in external providers # Motivation -As quantum processing units (QPUs) scale and quantum algorithms evolve, -the Qiskit Primitives interface was introduced to provide a higher-level abstraction for -accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: +As quantum processing units (QPUs) scaled and quantum algorithms evolved, +the Qiskit Primitives interface was introduced to provide a higher-level abstraction for +accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: circuit sampling (Sampler) and expectation value estimation (Estimator). The Primitives interface has replaced the legacy `backend.run` model for user access of IBM Quantum Hardware, resulting in its deprecation and removal from the Qiskit Runtime service. A series of migration guides have -been developed for users to upgrade their workflows to use the different primitive implementations available +been developed for users to upgrade their workflows to use the different primitive implementations available in the qiskit ecosystem, including the runtime primitives for real hardware access, the statevector primitives for local statevector simulation, or the aer primitives for access to local noisy simulation. -This migration guide, however, has been designed to help external providers implement their own flavor of the -primitives V2 interface (Sampler or Estimator) for easier pluggability into the standard qiskit workflow. These -implementations can be used to wrap any custom provider hardware access function +This migration guide, however, has been designed to help external providers implement their own flavor of the +primitives V2 interface (Sampler or Estimator) for easier pluggability into the standard qiskit workflow. These +implementations can be used to wrap any custom provider hardware access function (for example: `execute_workload(QPU)` or `resource.access()`) as long as the final inputs and outputs conform to the established contract from the interface. ## If the external provider already implemented `backend.run` The qiskit SDK offers ready-to-go wrappers for `backend.run` that can be easily adapted to a custom primitives workflow -through subclassing, these are the `BackendSamplerV2` (API ref link) and `BackendEstimatorV2` (API ref link). The inputs -to the primitives should follow the pub syntax specified in the V2 primitives interface (link to pub docs). +through subclassing, these are the `BackendSamplerV2` (API ref link) and `BackendEstimatorV2` (API ref link). The inputs +to the primitives should follow the pub syntax specified in the V2 primitives interface (link to pub docs). An advantage of this strategy is that the wrapper can handle the input/output manipulation, so knowledge of the PUB data model is not required. However, this might result in a sub-optimal runtime, which could be refined through a fully custom primitives implementation. -The snippets below show how to create a custom Estimator instance following the strategy described above. +The snippets below show how to create a custom Estimator instance following the strategy described above. The process would be analogous for a custom Sampler, modifying the base class to BackendSamplerV2. ``` python @@ -56,7 +56,7 @@ class CustomEstimator(BackendEstimatorV2): extra_flag_used_in_estimation: if `False`, do this. another_extra_flag: if `True`, do that, """ - + # preprocess arguments if necessary according to custom flags processed_backend = ... processed_options = ... @@ -80,7 +80,7 @@ instance of the abstract base primitive interfaces (link to BaseSamplerV2 and Ba understanding of the PUB data model for input/output handling. The following snippet shows a minimal example of an implementation of a custom Sampler primitive following this strategy. -This example has been extracted and generalized from the StatevectorSampler implementation. The full original +This example has been extracted and generalized from the StatevectorSampler implementation. The full original implementation can be found in the StatevectorSampler source code. ``` python @@ -107,7 +107,7 @@ class CustomStatevectorSampler(BaseSamplerV2): """ self._default_shots = default_shots self._seed = seed - + ... def run( @@ -126,7 +126,7 @@ class CustomStatevectorSampler(BaseSamplerV2): job = PrimitiveJob(self._run, coerced_pubs) job._submit() return job - + def _run(self, pubs: Iterable[SamplerPub]) -> PrimitiveResult[SamplerPubResult]: results = [self._run_pub(pub) for pub in pubs] return PrimitiveResult(results, metadata={"version": 2}) @@ -141,11 +141,11 @@ class CustomStatevectorSampler(BaseSamplerV2): for item in meas_info } for index, bound_circuit in enumerate(bound_circuits): - + # ACCESS PROVIDER RESOURCE HERE # in this case, we are showing an illustrative implementation samples_array = ProviderResource.sample() - + for item in meas_info: ary = _samples_to_packed_array(samples_array, item.num_bits, item.qreg_indices) arrays[item.creg_name][index] = ary @@ -156,10 +156,10 @@ class CustomStatevectorSampler(BaseSamplerV2): return SamplerPubResult( DataBin(**meas, shape=pub.shape), metadata={"shots": pub.shots, "circuit_metadata": pub.circuit.metadata}, - ) + ) ``` -The mechanics to implement a custom Estimator are analogous to the Sampler, but may require an extra pre/post-processing +The mechanics to implement a custom Estimator are analogous to the Sampler, but may require an extra pre/post-processing step in the run method to extract expectation values from samples. In the case of the custom SatevectorEstimator that will be used as reference, we encapsulate thi computation in the `_statevector_from_circuit` utility function. From a77b070862f80f2c6679c667f70a4f62a930cad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Tue, 17 Dec 2024 16:59:19 +0100 Subject: [PATCH 03/13] Simplify code snippets, edit introduction text, add placeholders for links to other guides/API refs --- .../external-providers-primitives-v2.mdx | 118 ++++++++---------- 1 file changed, 49 insertions(+), 69 deletions(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 20a787d6a95..b772852be9d 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -1,31 +1,35 @@ --- -title: TODO +title: Migrate provider interfaces from backend.run to primitives description: Migrate to the primitives interface in external providers --- -# Motivation +# Migrate provider interfaces from backend.run to primitives -As quantum processing units (QPUs) scale and quantum algorithms evolve, -the Qiskit Primitives interface was introduced to provide a higher-level abstraction for +## Motivation and context + +The evolution of quantum algorithms with the scaling of quantum processing units (QPUs) motivated the +introduction of the Qiskit primitives interface (link to primitives guide) to provide a higher-level abstraction for accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: circuit sampling (Sampler) and expectation value estimation (Estimator). -The Primitives interface has replaced the legacy `backend.run` model for user access of IBM Quantum Hardware, -resulting in its deprecation and removal from the Qiskit Runtime service. A series of migration guides have -been developed for users to upgrade their workflows to use the different primitive implementations available -in the qiskit ecosystem, including the runtime primitives for real hardware access, the statevector primitives -for local statevector simulation, or the aer primitives for access to local noisy simulation. +After a time of co-existence of the `backend.run` model and primitives model, where the primitives +evolved to the updated V2 primitives (link to v2 primitives guide?), the `backend.run` model was finally deprecated in favor of +these newer interfaces to streamline quantum workload execution. + +The existing collection of migration guides (link relevant guides) allow users to transition to the runtime provider and +update their user code to the new primitives V2 interface in time for the upcoming removal of `backend.run`. +To complement that, this guide focuses on supporting external providers (both existing and upcoming) +in the implementation their own flavor of the primitives V2 interface (Sampler or Estimator) to integrate seamlessly into the +standard Qiskit workflow. -This migration guide, however, has been designed to help external providers implement their own flavor of the -primitives V2 interface (Sampler or Estimator) for easier pluggability into the standard qiskit workflow. These -implementations can be used to wrap any custom provider hardware access function -(for example: `execute_workload(QPU)` or `resource.access()`) -as long as the final inputs and outputs conform to the established contract from the interface. +These implementations can be used to wrap any custom provider hardware access function +(for example: `execute_workload(QPU)` or `resource.access()`), or local simulator, +as long as the final inputs and outputs conform to the established contract from the primitive interfaces. -## If the external provider already implemented `backend.run` +## If your provider already implemented `backend.run` -The qiskit SDK offers ready-to-go wrappers for `backend.run` that can be easily adapted to a custom primitives workflow +The Qiskit SDK offers ready-to-go wrappers for `backend.run` that can be easily adapted to a custom primitives workflow through subclassing, these are the `BackendSamplerV2` (API ref link) and `BackendEstimatorV2` (API ref link). The inputs to the primitives should follow the pub syntax specified in the V2 primitives interface (link to pub docs). @@ -34,11 +38,13 @@ model is not required. However, this might result in a sub-optimal runtime, whic custom primitives implementation. The snippets below show how to create a custom Estimator instance following the strategy described above. -The process would be analogous for a custom Sampler, modifying the base class to BackendSamplerV2. +The process would be analogous for a custom Sampler, modifying the base class to `BackendSamplerV2`. ``` python +from qiskit.primitives import BackendEstimatorV2 + class CustomEstimator(BackendEstimatorV2): - """:class:`BaseEstimatorV2 ` primitive for custom provider.""" + """Estimator primitive for custom provider.""" _backend: CustomProviderResource @@ -51,7 +57,7 @@ class CustomEstimator(BackendEstimatorV2): ) -> None: """ Args: - backend: Custom provider resource to evaluate circuits on. + backend: custom provider resource to evaluate circuits on. options: options passed to through to the underlying BackendEstimatorV2. extra_flag_used_in_estimation: if `False`, do this. another_extra_flag: if `True`, do that, @@ -72,16 +78,16 @@ class CustomEstimator(BackendEstimatorV2): return self._backend ``` -## If the external provider didn't implement `backend.run` or you look for a fully custom implementation +## If your provider didn't implement `backend.run` or you prefer a fully custom implementation If a new provider is developed that doesn't conform to the legacy `backend.run` interface, the pre-packaged -wrapper may not be the most optimal route for implementing the primtives. Instead, you should implement a particular +wrapper may not be the optimal route for implementing primitives. Instead, you should implement a particular instance of the abstract base primitive interfaces (link to BaseSamplerV2 and BaseEstimatorV2). This route requires an -understanding of the PUB data model for input/output handling. +understanding of the PUB data model for input/output handling (link to pub explanation). The following snippet shows a minimal example of an implementation of a custom Sampler primitive following this strategy. -This example has been extracted and generalized from the StatevectorSampler implementation. The full original -implementation can be found in the StatevectorSampler source code. +This example has been extracted and generalized from the `StatevectorSampler` (link) implementation. It has been simplified +for readability purposes. The full original implementation can be found in the StatevectorSampler (link) source code. ``` python from qiskit.primitives.base import BaseSamplerV2 @@ -98,31 +104,13 @@ from qiskit.primitives.primitive_job import PrimitiveJob class CustomStatevectorSampler(BaseSamplerV2): - def __init__(self, *, default_shots: int = 1024, seed: np.random.Generator | int | None = None): - """ - Args: - default_shots: The default shots for the sampler if not specified during run. - seed: The seed or Generator object for random number generation. - If None, a random seeded default RNG will be used. - """ - self._default_shots = default_shots - self._seed = seed - ... def run( self, pubs: Iterable[SamplerPubLike], *, shots: int | None = None ) -> PrimitiveJob[PrimitiveResult[SamplerPubResult]]: - if shots is None: - shots = self._default_shots + ... coerced_pubs = [SamplerPub.coerce(pub, shots) for pub in pubs] - if any(len(pub.circuit.cregs) == 0 for pub in coerced_pubs): - warnings.warn( - "One of your circuits has no output classical registers and so the result " - "will be empty. Did you mean to add measurement instructions?", - UserWarning, - ) - job = PrimitiveJob(self._run, coerced_pubs) job._submit() return job @@ -132,6 +120,8 @@ class CustomStatevectorSampler(BaseSamplerV2): return PrimitiveResult(results, metadata={"version": 2}) def _run_pub(self, pub: SamplerPub) -> SamplerPubResult: + + # pre-processing of the sampling inputs to fit the required format circuit, qargs, meas_info = _preprocess_circuit(pub.circuit) bound_circuits = pub.parameter_values.bind_all(circuit) arrays = { @@ -144,8 +134,9 @@ class CustomStatevectorSampler(BaseSamplerV2): # ACCESS PROVIDER RESOURCE HERE # in this case, we are showing an illustrative implementation - samples_array = ProviderResource.sample() + samples_array = ProviderResource.sample(bound_circuit) + # post-processing of the sampling output to fit the required format for item in meas_info: ary = _samples_to_packed_array(samples_array, item.num_bits, item.qreg_indices) arrays[item.creg_name][index] = ary @@ -159,40 +150,26 @@ class CustomStatevectorSampler(BaseSamplerV2): ) ``` -The mechanics to implement a custom Estimator are analogous to the Sampler, but may require an extra pre/post-processing -step in the run method to extract expectation values from samples. In the case of the custom SatevectorEstimator that will be -used as reference, we encapsulate thi computation in the `_statevector_from_circuit` utility function. - +The mechanics to implement a custom Estimator are analogous to the Sampler, but may require a different pre/post-processing +step in the `run` method to extract expectation values from samples. Similarly to the Sampler +example, this snippet has been modified and simplified for generality and readability purposes. +The full original implementation can be found in the StatevectorEstimator (link) source code. ``` python from .base import BaseEstimatorV2 from .containers import DataBin, EstimatorPubLike, PrimitiveResult, PubResult from .containers.estimator_pub import EstimatorPub from .primitive_job import PrimitiveJob -from .utils import _statevector_from_circuit ... class CustomStatevectorEstimator(BaseEstimatorV2): - def __init__( - self, *, default_precision: float = 0.0, seed: np.random.Generator | int | None = None - ): - """ - Args: - default_precision: The default precision for the estimator if not specified during run. - seed: The seed or Generator object for random number generation. - If None, a random seeded default RNG will be used. - """ - self._default_precision = default_precision - self._seed = seed - ... def run( self, pubs: Iterable[EstimatorPubLike], *, precision: float | None = None ) -> PrimitiveJob[PrimitiveResult[PubResult]]: - if precision is None: - precision = self._default_precision + ... coerced_pubs = [EstimatorPub.coerce(pub, precision) for pub in pubs] job = PrimitiveJob(self._run, coerced_pubs) @@ -212,17 +189,20 @@ class CustomStatevectorEstimator(BaseEstimatorV2): bc_circuits, bc_obs = np.broadcast_arrays(bound_circuits, observables) evs = np.zeros_like(bc_circuits, dtype=np.float64) stds = np.zeros_like(bc_circuits, dtype=np.float64) + for index in np.ndindex(*bc_circuits.shape): + # pre-processing of the sampling inputs to fit the required format bound_circuit = bc_circuits[index] observable = bc_obs[index] - final_state = _statevector_from_circuit(bound_circuit, rng) paulis, coeffs = zip(*observable.items()) obs = SparsePauliOp(paulis, coeffs) - expectation_value = np.real_if_close(final_state.expectation_value(obs)) - if precision != 0: - if not np.isreal(expectation_value): - raise ValueError("Given operator is not Hermitian and noise cannot be added.") - expectation_value = rng.normal(expectation_value, precision) + + # ACCESS PROVIDER RESOURCE HERE + # in this case, we are showing an illustrative implementation + samples_array = ProviderResource.sample(bound_circuit, rng, precision) + + # post-processing of the sampling output to extract expectation value + expectation_value = compute_expectation_value(samples_array, obs) evs[index] = expectation_value data = DataBin(evs=evs, stds=stds, shape=evs.shape) From f86e387068a891697ac51545081b1957bce5df9f Mon Sep 17 00:00:00 2001 From: Rebecca Dimock Date: Tue, 17 Dec 2024 10:19:18 -0600 Subject: [PATCH 04/13] Add links --- .../external-providers-primitives-v2.mdx | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 879fbcc92a6..872b0860f15 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -8,36 +8,36 @@ description: Migrate to the primitives interface in external providers ## Motivation and context -The evolution of quantum algorithms and scaling of quantum processing units (QPUs) motivated the -introduction of the Qiskit primitives interface (link to primitives guide) to provide a higher-level abstraction for -accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: +The evolution of quantum algorithms and scaling of quantum processing units (QPUs) motivated the +introduction of the Qiskit primitives interface (link to primitives guide) to provide a higher-level abstraction for +accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: circuit sampling (Sampler) and expectation value estimation (Estimator). -After a time of co-existence of the `backend.run` model and primitives model, where the primitives -evolved to the updated V2 primitives (link to v2 primitives guide?), the `backend.run` model was finally deprecated in favor of -these newer interfaces to streamline quantum workload execution. +After a time of co-existence of the `backend.run` model and primitives model, where the primitives +evolved to the updated V2 primitives (link to v2 primitives guide?), the `backend.run` model was finally deprecated in favor of +these newer interfaces to streamline quantum workload execution. The existing collection of migration guides (link relevant guides) allow users to transition to the runtime provider and -update their user code to the new primitives V2 interface in time for the upcoming removal of `backend.run`. +update their user code to the new primitives V2 interface in time for the upcoming removal of `backend.run`. To complement that, this guide focuses on supporting external providers (both existing and upcoming) -in the implementation their own flavor of the primitives V2 interface (Sampler or Estimator) to integrate seamlessly into the -standard Qiskit workflow. +in the implementation their own flavor of the primitives V2 interface (Sampler or Estimator) to integrate seamlessly into the +standard Qiskit workflow. -These implementations can be used to wrap any custom provider hardware access function -(for example: `execute_workload(QPU)` or `resource.access()`), or local simulator, +These implementations can be used to wrap any custom provider hardware access function +(for example: `execute_workload(QPU)` or `resource.access()`), or local simulator, as long as the final inputs and outputs conform to the established contract from the primitive interfaces. ## If your provider already implemented `backend.run` The Qiskit SDK offers ready-to-go wrappers for `backend.run` that can be easily adapted to a custom primitives workflow -through subclassing, these are the `BackendSamplerV2` (API ref link) and `BackendEstimatorV2` (API ref link). The inputs -to the primitives should follow the pub syntax specified in the V2 primitives interface (link to pub docs). +through subclassing, these are the [`BackendEstimatorV2`](/api/qiskit/qiskit.primitives.BackendEstimatorV2) and [`BackendSamplerV2`.](/api/qiskit/qiskit.primitives.BackendSamplerV2) The inputs +to the primitives should follow the Primitive Unified Bloc (PUB) syntax specified in the V2 primitives interface. See the [Overview of PUBs section](/guides/primitive-input-output#pubs) in the Primitive inputs and outputs guide for details. An advantage of this strategy is that the wrapper can handle the input/output manipulation, so knowledge of the PUB data model is not required. However, this might result in a sub-optimal runtime, which could be refined through a fully custom primitives implementation. -The snippets below show how to create a custom Estimator instance following the strategy described above. +The snippets below show how to create a custom Estimator instance following the strategy described above. The process would be analogous for a custom Sampler, modifying the base class to `BackendSamplerV2`. ``` python @@ -81,9 +81,9 @@ class CustomEstimator(BackendEstimatorV2): ## If your provider didn't implement `backend.run` or you prefer a fully custom implementation If a new provider is developed that doesn't conform to the legacy `backend.run` interface, the pre-packaged -wrapper may not be the optimal route for implementing primitives. Instead, you should implement a particular -instance of the abstract base primitive interfaces (link to BaseSamplerV2 and BaseEstimatorV2). This route requires an -understanding of the PUB data model for input/output handling (link to pub explanation). +wrapper may not be the most optimal route for implementing the primtives. Instead, you should implement a particular +instance of the abstract base primitive interfaces ([BaseEstimatorV2](/api/qiskit/qiskit.primitives.BaseEstimatorV2) or [BaseSamplerV2](/api/qiskit/qiskit.primitives.BaseSamplerV2)). This route requires an +understanding of the PUB data model for input/output handling. The following snippet shows a minimal example of an implementation of a custom Sampler primitive following this strategy. This example has been extracted and generalized from the `StatevectorSampler` (link) implementation. It has been simplified @@ -135,7 +135,7 @@ class CustomStatevectorSampler(BaseSamplerV2): # ACCESS PROVIDER RESOURCE HERE # in this case, we are showing an illustrative implementation samples_array = ProviderResource.sample(bound_circuit) - + # post-processing of the sampling output to fit the required format for item in meas_info: ary = _samples_to_packed_array(samples_array, item.num_bits, item.qreg_indices) @@ -150,9 +150,9 @@ class CustomStatevectorSampler(BaseSamplerV2): ) ``` -The mechanics to implement a custom Estimator are analogous to the Sampler, but may require a different pre/post-processing +The mechanics to implement a custom Estimator are analogous to the Sampler, but may require a different pre/post-processing step in the `run` method to extract expectation values from samples. Similarly to the Sampler -example, this snippet has been modified and simplified for generality and readability purposes. +example, this snippet has been modified and simplified for generality and readability purposes. The full original implementation can be found in the `StatevectorEstimator` (link) source code. ``` python @@ -200,7 +200,7 @@ class CustomStatevectorEstimator(BaseEstimatorV2): # ACCESS PROVIDER RESOURCE HERE # in this case, we are showing an illustrative implementation samples_array = ProviderResource.sample(bound_circuit, rng, precision) - + # post-processing of the sampling output to extract expectation value expectation_value = compute_expectation_value(samples_array, obs) evs[index] = expectation_value From 4941f0da4b61f3cb191b3eb7d97d5b89a8d80cc5 Mon Sep 17 00:00:00 2001 From: Rebecca Dimock Date: Tue, 17 Dec 2024 10:39:43 -0600 Subject: [PATCH 05/13] more links, qiskit bot, TOC --- docs/migration-guides/_toc.json | 4 ++++ .../external-providers-primitives-v2.mdx | 20 +++++++++---------- docs/migration-guides/index.mdx | 8 +++++--- qiskit_bot.yaml | 4 ++++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/migration-guides/_toc.json b/docs/migration-guides/_toc.json index 3d80ca99bbb..7e0d19547c0 100644 --- a/docs/migration-guides/_toc.json +++ b/docs/migration-guides/_toc.json @@ -68,6 +68,10 @@ } ] }, + { + "title": "Migrate provider interfaces from backend.run to primitives", + "url": "/migration-guides/external-providers-primitives-v2" + }, { "title": "Qiskit 0.44 changes", "children": [ diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 872b0860f15..9bbd6ea02c4 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -9,18 +9,18 @@ description: Migrate to the primitives interface in external providers ## Motivation and context The evolution of quantum algorithms and scaling of quantum processing units (QPUs) motivated the -introduction of the Qiskit primitives interface (link to primitives guide) to provide a higher-level abstraction for -accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: -circuit sampling (Sampler) and expectation value estimation (Estimator). +introduction of the Qiskit [primitives interface](https://docs.quantum.ibm.com/guides/primitives) to provide a higher-level abstraction for +accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: expectation value estimation (Estimator) and +circuit sampling (Sampler). After a time of co-existence of the `backend.run` model and primitives model, where the primitives -evolved to the updated V2 primitives (link to v2 primitives guide?), the `backend.run` model was finally deprecated in favor of +evolved to the updated V2 primitives, the `backend.run` model was finally deprecated in favor of these newer interfaces to streamline quantum workload execution. -The existing collection of migration guides (link relevant guides) allow users to transition to the runtime provider and -update their user code to the new primitives V2 interface in time for the upcoming removal of `backend.run`. +The existing collection of migration guides allow users to [transition to the runtime provider](https://docs.quantum.ibm.com/migration-guides/qiskit-runtime) and +[update their user code to the new primitives V2 interface](/migration-guides/v2-primitives) in time for the upcoming removal of `backend.run`. To complement that, this guide focuses on supporting external providers (both existing and upcoming) -in the implementation their own flavor of the primitives V2 interface (Sampler or Estimator) to integrate seamlessly into the +in the implementation their own flavor of the primitives V2 interface (Estimator or Sampler) to integrate seamlessly into the standard Qiskit workflow. These implementations can be used to wrap any custom provider hardware access function @@ -86,8 +86,8 @@ instance of the abstract base primitive interfaces ([BaseEstimatorV2](/api/qiski understanding of the PUB data model for input/output handling. The following snippet shows a minimal example of an implementation of a custom Sampler primitive following this strategy. -This example has been extracted and generalized from the `StatevectorSampler` (link) implementation. It has been simplified -for readability purposes. The full original implementation can be found in the `StatevectorSampler` (link) source code. +This example has been extracted and generalized from the [`StatevectorSampler`](/api/qiskit/qiskit.primitives.StatevectorSampler) implementation. It has been simplified +for readability purposes. The full original implementation can be found in the [`StatevectorSampler` source code.](https://github.com/Qiskit/qiskit/blob/stable/1.3/qiskit/primitives/statevector_sampler.py) ``` python from qiskit.primitives.base import BaseSamplerV2 @@ -153,7 +153,7 @@ class CustomStatevectorSampler(BaseSamplerV2): The mechanics to implement a custom Estimator are analogous to the Sampler, but may require a different pre/post-processing step in the `run` method to extract expectation values from samples. Similarly to the Sampler example, this snippet has been modified and simplified for generality and readability purposes. -The full original implementation can be found in the `StatevectorEstimator` (link) source code. +The full original implementation can be found in the [`StatevectorEstimator` source code.](https://github.com/Qiskit/qiskit/tree/stable/1.3/qiskit/primitives/statevector_estimator.py#L31-L174) ``` python from .base import BaseEstimatorV2 diff --git a/docs/migration-guides/index.mdx b/docs/migration-guides/index.mdx index 90c3b4acb40..56e2fc9d580 100644 --- a/docs/migration-guides/index.mdx +++ b/docs/migration-guides/index.mdx @@ -11,16 +11,18 @@ These migration guides help you more effectively use Qiskit and Qiskit Runtime: * Migrate to Qiskit 1.0 * [Qiskit 1.0 packaging changes](./qiskit-1.0-installation) * [Qiskit 1.0 feature changes](./qiskit-1.0-features) +* [Migrate from Qiskit Pulse to fractional gates](./pulse-migration) * [Migrate to the Qiskit Runtime V2 primitives](./v2-primitives) -* [Qiskit Runtime execution mode changes](./sessions) -* Migrate to Qiskit Runtime +* [Migrate to local simulators](./local-simulators) +* [Execution mode changes](./sessions) +* Migrate from `backend.run` to primitives * [Overview](./qiskit-runtime) * [Migrate from `qiskit-ibmq-provider`](./qiskit-runtime-from-ibmq-provider) * [Migrate from `qiskit_ibm_provider`](./qiskit-runtime-from-ibm-provider) * [Migrate options](./qiskit-runtime-options) * [Common use cases](./qiskit-runtime-use-case) * [Examples](./qiskit-runtime-examples) -* [Migrate from cloud simulators to local simulators](./local-simulators) +* [Migrate provider interfaces from backend.run to primitives](./external-providers-primitives-v2) * Qiskit 0.44 changes * [`qiskit.algorithms` new interface](./qiskit-algorithms-module) * [`qiskit.opflow` deprecation](./qiskit-opflow-module) diff --git a/qiskit_bot.yaml b/qiskit_bot.yaml index 4aa43ee27da..caae97d82c4 100644 --- a/qiskit_bot.yaml +++ b/qiskit_bot.yaml @@ -254,6 +254,10 @@ notifications: - "@javabster" "docs/migration-guides/qiskit-algorithms-module": - "@ElePT" + "docs/migration-guides/external-providers-primitives-v2": + - "@ElePT" + - "@jyu00" + - "@beckykd" "docs/migration-guides/qiskit-opflow-module": - "@ElePT" "docs/migration-guides/qiskit-quantum-instance": From 11a469aa6a214ead34058907457dc9c65aaa8f5e Mon Sep 17 00:00:00 2001 From: Rebecca Dimock Date: Tue, 17 Dec 2024 11:30:37 -0600 Subject: [PATCH 06/13] local links --- docs/migration-guides/external-providers-primitives-v2.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 9bbd6ea02c4..275b9b0157c 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -9,7 +9,7 @@ description: Migrate to the primitives interface in external providers ## Motivation and context The evolution of quantum algorithms and scaling of quantum processing units (QPUs) motivated the -introduction of the Qiskit [primitives interface](https://docs.quantum.ibm.com/guides/primitives) to provide a higher-level abstraction for +introduction of the Qiskit [primitives interface](/guides/primitives) to provide a higher-level abstraction for accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: expectation value estimation (Estimator) and circuit sampling (Sampler). @@ -17,7 +17,7 @@ After a time of co-existence of the `backend.run` model and primitives model, wh evolved to the updated V2 primitives, the `backend.run` model was finally deprecated in favor of these newer interfaces to streamline quantum workload execution. -The existing collection of migration guides allow users to [transition to the runtime provider](https://docs.quantum.ibm.com/migration-guides/qiskit-runtime) and +The existing collection of migration guides allow users to [transition to the runtime provider](/migration-guides/qiskit-runtime) and [update their user code to the new primitives V2 interface](/migration-guides/v2-primitives) in time for the upcoming removal of `backend.run`. To complement that, this guide focuses on supporting external providers (both existing and upcoming) in the implementation their own flavor of the primitives V2 interface (Estimator or Sampler) to integrate seamlessly into the From 45328ae990b96f449d25c4fcb1c36ccecc550ee9 Mon Sep 17 00:00:00 2001 From: Rebecca Dimock Date: Tue, 17 Dec 2024 12:32:31 -0600 Subject: [PATCH 07/13] edits --- .../external-providers-primitives-v2.mdx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 275b9b0157c..176fb7f428d 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -17,23 +17,23 @@ After a time of co-existence of the `backend.run` model and primitives model, wh evolved to the updated V2 primitives, the `backend.run` model was finally deprecated in favor of these newer interfaces to streamline quantum workload execution. -The existing collection of migration guides allow users to [transition to the runtime provider](/migration-guides/qiskit-runtime) and -[update their user code to the new primitives V2 interface](/migration-guides/v2-primitives) in time for the upcoming removal of `backend.run`. -To complement that, this guide focuses on supporting external providers (both existing and upcoming) -in the implementation their own flavor of the primitives V2 interface (Estimator or Sampler) to integrate seamlessly into the +The existing collection of migration guides helps users [transition to the runtime provider](/migration-guides/qiskit-runtime) and +[update their user code to the V2 primitives interface](/migration-guides/v2-primitives) in time for the upcoming removal of `backend.run`. +To complement that, this guide supports external providers (both existing and upcoming) +in implementing their own version of the primitives V2 interface (Estimator or Sampler) that integrates seamlessly into the standard Qiskit workflow. These implementations can be used to wrap any custom provider hardware access function (for example: `execute_workload(QPU)` or `resource.access()`), or local simulator, -as long as the final inputs and outputs conform to the established contract from the primitive interfaces. +as long as the final inputs and outputs conform to the established standards from the primitive interfaces. ## If your provider already implemented `backend.run` -The Qiskit SDK offers ready-to-go wrappers for `backend.run` that can be easily adapted to a custom primitives workflow -through subclassing, these are the [`BackendEstimatorV2`](/api/qiskit/qiskit.primitives.BackendEstimatorV2) and [`BackendSamplerV2`.](/api/qiskit/qiskit.primitives.BackendSamplerV2) The inputs +The Qiskit SDK offers wrappers for `backend.run` that can be easily adapted to a custom primitives workflow +through subclassing; these are the [`BackendEstimatorV2`](/api/qiskit/qiskit.primitives.BackendEstimatorV2) and [`BackendSamplerV2`.](/api/qiskit/qiskit.primitives.BackendSamplerV2) The inputs to the primitives should follow the Primitive Unified Bloc (PUB) syntax specified in the V2 primitives interface. See the [Overview of PUBs section](/guides/primitive-input-output#pubs) in the Primitive inputs and outputs guide for details. -An advantage of this strategy is that the wrapper can handle the input/output manipulation, so knowledge of the PUB data +An advantage of this strategy is that the wrapper can handle the input and output manipulation, so knowledge of the PUB data model is not required. However, this might result in a sub-optimal runtime, which could be refined through a fully custom primitives implementation. @@ -81,13 +81,13 @@ class CustomEstimator(BackendEstimatorV2): ## If your provider didn't implement `backend.run` or you prefer a fully custom implementation If a new provider is developed that doesn't conform to the legacy `backend.run` interface, the pre-packaged -wrapper may not be the most optimal route for implementing the primtives. Instead, you should implement a particular -instance of the abstract base primitive interfaces ([BaseEstimatorV2](/api/qiskit/qiskit.primitives.BaseEstimatorV2) or [BaseSamplerV2](/api/qiskit/qiskit.primitives.BaseSamplerV2)). This route requires an -understanding of the PUB data model for input/output handling. +wrapper might not be the optimal route for implementing the primtives. Instead, you should implement a particular +instance of the abstract base primitive interfaces ([BaseEstimatorV2](/api/qiskit/qiskit.primitives.BaseEstimatorV2) or [BaseSamplerV2](/api/qiskit/qiskit.primitives.BaseSamplerV2)). This process requires an +understanding of the PUB data model for input and output handling. The following snippet shows a minimal example of an implementation of a custom Sampler primitive following this strategy. This example has been extracted and generalized from the [`StatevectorSampler`](/api/qiskit/qiskit.primitives.StatevectorSampler) implementation. It has been simplified -for readability purposes. The full original implementation can be found in the [`StatevectorSampler` source code.](https://github.com/Qiskit/qiskit/blob/stable/1.3/qiskit/primitives/statevector_sampler.py) +for readability. The full original implementation can be found in the [`StatevectorSampler` source code.](https://github.com/Qiskit/qiskit/blob/stable/1.3/qiskit/primitives/statevector_sampler.py) ``` python from qiskit.primitives.base import BaseSamplerV2 @@ -150,9 +150,9 @@ class CustomStatevectorSampler(BaseSamplerV2): ) ``` -The mechanics to implement a custom Estimator are analogous to the Sampler, but may require a different pre/post-processing -step in the `run` method to extract expectation values from samples. Similarly to the Sampler -example, this snippet has been modified and simplified for generality and readability purposes. +The mechanics to implement a custom Estimator are analogous to the Sampler, but might require a different pre or post-processing +step in the `run` method to extract expectation values from samples. Similar to the Sampler +example, this snippet has been modified and simplified for generality and readability. The full original implementation can be found in the [`StatevectorEstimator` source code.](https://github.com/Qiskit/qiskit/tree/stable/1.3/qiskit/primitives/statevector_estimator.py#L31-L174) ``` python From 0edf39966c10cf8590800f3fc9dea0d0e3e226d6 Mon Sep 17 00:00:00 2001 From: Rebecca Dimock Date: Tue, 17 Dec 2024 12:37:22 -0600 Subject: [PATCH 08/13] add link --- docs/open-source/create-a-provider.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/open-source/create-a-provider.mdx b/docs/open-source/create-a-provider.mdx index 28a2b8b15c0..7b3a5220845 100644 --- a/docs/open-source/create-a-provider.mdx +++ b/docs/open-source/create-a-provider.mdx @@ -1,14 +1,14 @@ --- title: Create a provider -description: A short guide on integrating Qiskit into an external provider's quantum resources. +description: A short guide on integrating Qiskit into an external provider's quantum resources. --- # Integrate external quantum resources with Qiskit -The Qiskit SDK is built to support third parties in creating external providers of quantum resources. +The Qiskit SDK is built to support third parties in creating external providers of quantum resources. -This means that any organization which develops or deploys quantum compute resources can integrate their services into Qiskit and tap into its userbase. +This means that any organization which develops or deploys quantum compute resources can integrate their services into Qiskit and tap into its userbase. -Doing so requires creating a package which supports requests for quantum compute resources and returns them to the user. +Doing so requires creating a package which supports requests for quantum compute resources and returns them to the user. Additionally, the package must allow users to submit jobs and retrieve their results through an implementation of the `qiskit.primitives` objects. @@ -46,7 +46,7 @@ In addition to providing a service returning hardware configurations, a service To handle job status and results, the Qiskit SDK provides a [`DataBin`](../api/qiskit/qiskit.primitives.DataBin), [`PubResult`](../api/qiskit/qiskit.primitives.PubResult), [`PrimitiveResult`](../api/qiskit/qiskit.primitives.PrimitiveResult), and [`BasePrimitiveJob`](../api/qiskit/qiskit.primitives.BasePrimitiveJob) objects should be used. -See the `qiskit.primitives` [API documentation](../api/qiskit/primitives) as well as the reference implementations [`BackendEstimatorV2`](../api/qiskit/qiskit.primitives.BackendEstimatorV2) and [`BackendSampleV2`](../api/qiskit/qiskit.primitives.BackendSampler) for more information. +See the `qiskit.primitives` [API documentation](../api/qiskit/primitives) as well as the reference implementations [`BackendEstimatorV2`](../api/qiskit/qiskit.primitives.BackendEstimatorV2) and [`BackendSampleV2`](../api/qiskit/qiskit.primitives.BackendSampler) for more information. If you created a provider that uses `backend.run`, see [Migrate provider interfaces from backend.run to primitives.](../migration-guides/external-providers-primitives-v2) An example implementation of the Estimator primitive may look like: @@ -105,8 +105,8 @@ class SamplerImplentation(BaseSamplerV2): self._backend = backend self._options = options self._default_shots = 1024 - - @property + + @property def backend(self) -> BackendV2: """ Return the Sampler's backend """ return self._backend @@ -124,5 +124,5 @@ class SamplerImplentation(BaseSamplerV2): """ job = BasePrimitiveJob(pubs, shots) job_with_results = job.submit() - return job_with_results + return job_with_results ``` \ No newline at end of file From 38fe250ed5b58b996c03fdf405b9d40e1b1a8de7 Mon Sep 17 00:00:00 2001 From: Rebecca Dimock <66339736+beckykd@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:45:45 -0600 Subject: [PATCH 09/13] Apply suggestions from code review Co-authored-by: abbycross --- .../external-providers-primitives-v2.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 176fb7f428d..58886d186e7 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -13,19 +13,19 @@ introduction of the Qiskit [primitives interface](/guides/primitives) to provide accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: expectation value estimation (Estimator) and circuit sampling (Sampler). -After a time of co-existence of the `backend.run` model and primitives model, where the primitives +After a time of co-existence of the `backend.run` model and primitives model, when the primitives evolved to the updated V2 primitives, the `backend.run` model was finally deprecated in favor of these newer interfaces to streamline quantum workload execution. -The existing collection of migration guides helps users [transition to the runtime provider](/migration-guides/qiskit-runtime) and +The existing collection of migration guides helps users [transition to the Qiskit Runtime provider](/migration-guides/qiskit-runtime) and [update their user code to the V2 primitives interface](/migration-guides/v2-primitives) in time for the upcoming removal of `backend.run`. To complement that, this guide supports external providers (both existing and upcoming) in implementing their own version of the primitives V2 interface (Estimator or Sampler) that integrates seamlessly into the standard Qiskit workflow. These implementations can be used to wrap any custom provider hardware access function -(for example: `execute_workload(QPU)` or `resource.access()`), or local simulator, -as long as the final inputs and outputs conform to the established standards from the primitive interfaces. +(for example: `execute_workload(QPU)` or `resource.access()`) or local simulator, +as long as the final inputs and outputs conform to the established standards set by the primitive interfaces. ## If your provider already implemented `backend.run` @@ -34,11 +34,11 @@ through subclassing; these are the [`BackendEstimatorV2`](/api/qiskit/qiskit.pri to the primitives should follow the Primitive Unified Bloc (PUB) syntax specified in the V2 primitives interface. See the [Overview of PUBs section](/guides/primitive-input-output#pubs) in the Primitive inputs and outputs guide for details. An advantage of this strategy is that the wrapper can handle the input and output manipulation, so knowledge of the PUB data -model is not required. However, this might result in a sub-optimal runtime, which could be refined through a fully +model is not required. However, this might result in a suboptimal runtime, which could be refined through a fully custom primitives implementation. -The snippets below show how to create a custom Estimator instance following the strategy described above. -The process would be analogous for a custom Sampler, modifying the base class to `BackendSamplerV2`. +The following snippets show how to create a custom Estimator instance following the strategy described above. +The process is analogous for a custom Sampler, modifying the base class to `BackendSamplerV2`. ``` python from qiskit.primitives import BackendEstimatorV2 @@ -81,7 +81,7 @@ class CustomEstimator(BackendEstimatorV2): ## If your provider didn't implement `backend.run` or you prefer a fully custom implementation If a new provider is developed that doesn't conform to the legacy `backend.run` interface, the pre-packaged -wrapper might not be the optimal route for implementing the primtives. Instead, you should implement a particular +wrapper might not be the optimal route for implementing the primitives. Instead, you should implement a particular instance of the abstract base primitive interfaces ([BaseEstimatorV2](/api/qiskit/qiskit.primitives.BaseEstimatorV2) or [BaseSamplerV2](/api/qiskit/qiskit.primitives.BaseSamplerV2)). This process requires an understanding of the PUB data model for input and output handling. @@ -150,7 +150,7 @@ class CustomStatevectorSampler(BaseSamplerV2): ) ``` -The mechanics to implement a custom Estimator are analogous to the Sampler, but might require a different pre or post-processing +The mechanics to implement a custom Estimator are analogous to those for the Sampler, but might require a different pre- or post-processing step in the `run` method to extract expectation values from samples. Similar to the Sampler example, this snippet has been modified and simplified for generality and readability. The full original implementation can be found in the [`StatevectorEstimator` source code.](https://github.com/Qiskit/qiskit/tree/stable/1.3/qiskit/primitives/statevector_estimator.py#L31-L174) From 1b0ad7615818e515c08b4947886b32f01132ae4b Mon Sep 17 00:00:00 2001 From: Rebecca Dimock <66339736+beckykd@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:46:51 -0600 Subject: [PATCH 10/13] Apply suggestions from code review Co-authored-by: abbycross --- docs/open-source/create-a-provider.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/open-source/create-a-provider.mdx b/docs/open-source/create-a-provider.mdx index 7b3a5220845..ecf75b18349 100644 --- a/docs/open-source/create-a-provider.mdx +++ b/docs/open-source/create-a-provider.mdx @@ -6,9 +6,9 @@ description: A short guide on integrating Qiskit into an external provider's qua The Qiskit SDK is built to support third parties in creating external providers of quantum resources. -This means that any organization which develops or deploys quantum compute resources can integrate their services into Qiskit and tap into its userbase. +This means that any organization that develops or deploys quantum compute resources can integrate their services into Qiskit and tap into its userbase. -Doing so requires creating a package which supports requests for quantum compute resources and returns them to the user. +Doing so requires creating a package that supports requests for quantum compute resources and returns them to the user. Additionally, the package must allow users to submit jobs and retrieve their results through an implementation of the `qiskit.primitives` objects. From 7d8769645080e60870eb2779fcc6198940dd4828 Mon Sep 17 00:00:00 2001 From: Rebecca Dimock <66339736+beckykd@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:47:15 -0600 Subject: [PATCH 11/13] Update docs/migration-guides/external-providers-primitives-v2.mdx --- docs/migration-guides/external-providers-primitives-v2.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 58886d186e7..a7f947738db 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -4,7 +4,7 @@ description: Migrate to the primitives interface in external providers --- -# Migrate provider interfaces from backend.run to primitives +# Migrate provider interfaces from `backend.run` to primitives ## Motivation and context From ebb7b5bb524507116afbbd7fedfca73d4ffe57bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Wed, 18 Dec 2024 11:03:51 +0100 Subject: [PATCH 12/13] Add comment to explain type checking change in BackendEstimator example --- docs/migration-guides/external-providers-primitives-v2.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index a7f947738db..9549c19f53b 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -46,6 +46,9 @@ from qiskit.primitives import BackendEstimatorV2 class CustomEstimator(BackendEstimatorV2): """Estimator primitive for custom provider.""" + # This line is for type checking purposes. + # We are changing the type of self._backend from qiskit's + # BackendV1/BackendV2 classes to our custom provider resource. _backend: CustomProviderResource def __init__( From 55c4a2452323aa05d402a94ab048575d36ce54e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Thu, 19 Dec 2024 17:41:06 +0100 Subject: [PATCH 13/13] Apply Jessie's suggestions for the motivation section --- .../external-providers-primitives-v2.mdx | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/docs/migration-guides/external-providers-primitives-v2.mdx b/docs/migration-guides/external-providers-primitives-v2.mdx index 9549c19f53b..bdf8b110602 100644 --- a/docs/migration-guides/external-providers-primitives-v2.mdx +++ b/docs/migration-guides/external-providers-primitives-v2.mdx @@ -6,24 +6,34 @@ description: Migrate to the primitives interface in external providers # Migrate provider interfaces from `backend.run` to primitives -## Motivation and context - -The evolution of quantum algorithms and scaling of quantum processing units (QPUs) motivated the -introduction of the Qiskit [primitives interface](/guides/primitives) to provide a higher-level abstraction for -accessing QPUs. This interface is optimized for two core tasks in quantum algorithm development: expectation value estimation (Estimator) and -circuit sampling (Sampler). - -After a time of co-existence of the `backend.run` model and primitives model, when the primitives -evolved to the updated V2 primitives, the `backend.run` model was finally deprecated in favor of -these newer interfaces to streamline quantum workload execution. - -The existing collection of migration guides helps users [transition to the Qiskit Runtime provider](/migration-guides/qiskit-runtime) and -[update their user code to the V2 primitives interface](/migration-guides/v2-primitives) in time for the upcoming removal of `backend.run`. -To complement that, this guide supports external providers (both existing and upcoming) -in implementing their own version of the primitives V2 interface (Estimator or Sampler) that integrates seamlessly into the -standard Qiskit workflow. - -These implementations can be used to wrap any custom provider hardware access function +## Why implement primitives for external providers? + +Similar to the early days of classical computers, when developers had to manipulate CPU registers directly, +the early interface to QPUs simply returned the raw data coming out of the control electronics. +This was not a huge issue when QPUs lived in labs and only allowed direct access by researchers. +When IBM first brought its QPUs to the cloud, we recognized most developers would not and +should not be familiar with distilling such raw data into 0s and 1s. Therefore, +we introduced `backend.run`, our first abstraction for accessing QPUs. This allowed developers +to operate on a data format they were more familiar with and focus on the bigger picture. + +As access to QPUs became more wide spread, and with more quantum algorithms being developed, +we again recognized the need for a higher-level abstraction. This led to the introduction of +the Qiskit [primitives interface](/guides/primitives), which are optimized for two core tasks in quantum algorithm development: +expectation value estimation (Estimator) and circuit sampling (Sampler). The goal is once +again to help developers to focus more on innovation and less on data conversion. + +For backwards compatibility purposes, the `backend.run` interface continues to exist in Qiskit. However, it is deprecated +in Qiskit Runtime, as most of the IBM Quantum users have migrated to V2 primitives due to their improved +usability and efficiency. There is already a collection of migration guides for users to +[transition to the Qiskit Runtime provider](/migration-guides/qiskit-runtime) and +[update their user code to the V2 primitives interface](/migration-guides/v2-primitives) in +time for the upcoming removal of `backend.run` in Qiskit Runtime. + +This migration guide shifts the focus from users and aims to help service providers to migrate from the +`backend.run` interface to primitives, +so their users can also benefit from their improvements. + +Custom primitive implementations can be used to wrap any service provider hardware access function (for example: `execute_workload(QPU)` or `resource.access()`) or local simulator, as long as the final inputs and outputs conform to the established standards set by the primitive interfaces.