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

Support local execution on simulators #1243

Closed
wants to merge 60 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
ef7956f
Recognized fake backends
merav-aharoni Nov 15, 2023
d6ab634
Continuing support for fake backends in Sampler
merav-aharoni Nov 23, 2023
fc6e323
Merge branch 'main' into fake_backends
merav-aharoni Nov 23, 2023
d105760
Created FakeRuntimeJob
merav-aharoni Nov 23, 2023
80ff68f
Added AerSimulator
merav-aharoni Nov 28, 2023
7b8578d
Merged with main
merav-aharoni Nov 29, 2023
e55831b
Small fixes
merav-aharoni Nov 29, 2023
9afe8b3
Cleaning up
merav-aharoni Nov 29, 2023
9e0d7ff
Added sample test
merav-aharoni Nov 29, 2023
a56063f
Fixed passing options to fake backend
merav-aharoni Nov 29, 2023
959445b
Added set_transpile_options
merav-aharoni Dec 4, 2023
86b402b
Merge branch 'main' into fake_backends
merav-aharoni Dec 4, 2023
0bb8f03
Fixed options parameters
merav-aharoni Dec 5, 2023
a83701e
lint and black
merav-aharoni Dec 5, 2023
12fe17c
Use fake_provider.py to get fake backends
merav-aharoni Dec 5, 2023
c4fbb9f
Merge branch 'fake_backends' of github.com:merav-aharoni/qiskit-ibm-r…
merav-aharoni Dec 5, 2023
b4072af
Added estimator to test
merav-aharoni Dec 5, 2023
fb28323
Added check that fake_backend is not IBMBackend, for test suite
merav-aharoni Dec 5, 2023
1918431
lint
merav-aharoni Dec 5, 2023
a59784b
Fixes for mypy
merav-aharoni Dec 6, 2023
ee0ed3e
black
merav-aharoni Dec 6, 2023
2eae3b5
Merge branch 'main' into fake_backends
merav-aharoni Dec 6, 2023
a6516a3
black
merav-aharoni Dec 6, 2023
0eb8c6a
Merge branch 'fake_backends' of github.com:merav-aharoni/qiskit-ibm-r…
merav-aharoni Dec 6, 2023
e1166f8
Fixed integration tests
merav-aharoni Dec 6, 2023
d851705
Merge branch 'main' into fake_backends
merav-aharoni Dec 6, 2023
3d150f1
Added channel to constructor of FakeRuntimeService
merav-aharoni Dec 6, 2023
86f23fd
Merge branch 'fake_backends' of github.com:merav-aharoni/qiskit-ibm-r…
merav-aharoni Dec 6, 2023
8f2f568
Added transfer of AerSimulator options along with test
merav-aharoni Dec 6, 2023
5a251be
Merge branch 'main' into fake_backends
merav-aharoni Dec 6, 2023
6da6e58
attempt to fix account problem
merav-aharoni Dec 6, 2023
bc13543
Merge branch 'fake_backends' of github.com:merav-aharoni/qiskit-ibm-r…
merav-aharoni Dec 6, 2023
1b07547
Added doc string
merav-aharoni Dec 6, 2023
d8b6b86
black
merav-aharoni Dec 6, 2023
625066b
mypy
merav-aharoni Dec 6, 2023
2157aad
Another attempt at fixing account. Removed print.
merav-aharoni Dec 7, 2023
1959df6
Removed unnecessary import
merav-aharoni Dec 7, 2023
35678a3
lint
merav-aharoni Dec 7, 2023
a6e9379
cleaning up
merav-aharoni Dec 7, 2023
09aadbe
Release note
merav-aharoni Dec 7, 2023
343a0e5
Merge branch 'main' into fake_backends
merav-aharoni Dec 7, 2023
0d28bf4
Merge branch 'main' into fake_backends
kt474 Dec 8, 2023
be47496
Merge branch 'main' into fake_backends
merav-aharoni Dec 11, 2023
e077401
Merge branch 'main' into fake_backends
merav-aharoni Dec 13, 2023
acfeb20
Added creation_date to FakeRuntimeJob
merav-aharoni Dec 13, 2023
c5ca5fc
Merge branch 'fake_backends' of github.com:merav-aharoni/qiskit-ibm-r…
merav-aharoni Dec 13, 2023
afa0c1a
Added creation_date and tags to FakeRuntimeJob
merav-aharoni Dec 13, 2023
af5fcef
black
merav-aharoni Dec 13, 2023
ea59600
Added docstring. Removed comments that contained questions
merav-aharoni Dec 13, 2023
22773c5
Added support for FakeBackendV2 also when given as an object, not onl…
merav-aharoni Dec 13, 2023
97a6868
mypy
merav-aharoni Dec 13, 2023
606c902
Added checks on importing Aer
merav-aharoni Dec 13, 2023
6b8d07f
When backend is FakeBackendV2, don't modify resilience and optimizati…
merav-aharoni Dec 14, 2023
8830e76
Added test for backend.run() on simulator
merav-aharoni Dec 14, 2023
9bbac3d
Merge branch 'main' into fake_backends
merav-aharoni Dec 14, 2023
d3e030b
Merge branch 'main' into fake_backends
merav-aharoni Dec 19, 2023
e4422a9
Merged with main after transfer of FakeBackendV2 to here
merav-aharoni Dec 25, 2023
5171d7e
black
merav-aharoni Dec 25, 2023
74ed194
lint
merav-aharoni Dec 25, 2023
3a90e02
Fixed path in base_primitive
merav-aharoni Dec 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions qiskit_ibm_runtime/base_primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@
from dataclasses import asdict
import warnings

try:
from qiskit_aer import AerSimulator

HAS_AER_SIMULATOR = True
except ImportError:
HAS_AER_SIMULATOR = False

from qiskit.providers.options import Options as TerraOptions
from qiskit.providers.exceptions import QiskitBackendNotFoundError

from qiskit_ibm_provider.session import get_cm_session as get_cm_provider_session

from qiskit_ibm_runtime.fake_provider.fake_backend import FakeBackendV2
from .fake_provider.fake_provider import FakeProviderForBackendV2
from .options import Options
from .options.utils import set_default_error_levels
from .runtime_job import RuntimeJob
Expand All @@ -43,7 +53,7 @@ class BasePrimitive(ABC):

def __init__(
self,
backend: Optional[Union[str, IBMBackend]] = None,
backend: Optional[Union[str, IBMBackend, FakeBackendV2]] = None,
session: Optional[Union[Session, str, IBMBackend]] = None,
options: Optional[Union[Dict, Options]] = None,
):
Expand Down Expand Up @@ -74,7 +84,10 @@ def __init__(
# a nested dictionary to categorize options.
self._session: Optional[Session] = None
self._service: QiskitRuntimeService = None
self._backend: Optional[IBMBackend] = None
self._backend: Optional[Union[IBMBackend, FakeBackendV2]] = None

if isinstance(backend, (AerSimulator, FakeBackendV2)) and not HAS_AER_SIMULATOR:
raise QiskitBackendNotFoundError("To use an Aer Simulator, you must install aer")

if options is None:
self._options = asdict(Options())
Expand All @@ -98,6 +111,13 @@ def __init__(
if isinstance(backend, IBMBackend):
self._service = backend.service
self._backend = backend
elif isinstance(backend, (AerSimulator, FakeBackendV2)):
self._backend = backend
self._service = (
QiskitRuntimeService()
if QiskitRuntimeService.global_service is None
else QiskitRuntimeService.global_service
)
Comment on lines +116 to +120
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of adding all the local logic into QiskitRuntimeService, it'd be much cleaner to just create a new LocalRuntimeService to "mock" any server requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure I understand your intention in this comment. Do you mean that when running on a simulator, we don't need a real service, and don't need to have a real account?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, so instead of modifying QiskitRuntimeService, base_primitive can set self._service to a mock (local) service. And its run() method would handle the local operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will try this. Are you also saying that the local run will not need an account? because in a comment above, @kt474 thought it should have an account.

Copy link
Member

Choose a reason for hiding this comment

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

We can do it this way, without an account

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't get a chance to complete this item. I think it might be best to do in a separate PR. In any case, it is up to you @kt474 or @jyu00 to continue from here.

elif isinstance(backend, str):
self._service = (
QiskitRuntimeService()
Expand Down Expand Up @@ -137,6 +157,11 @@ def _run_primitive(self, primitive_inputs: Dict, user_kwargs: Dict) -> RuntimeJo
Returns:
Submitted job.
"""
run_simulator = isinstance(self._backend, AerSimulator) or (
FakeProviderForBackendV2().backend(self._backend.name) is not None
and not isinstance(self._backend, IBMBackend)
)

combined = Options._merge_options(self._options, user_kwargs)

if self._backend:
Expand All @@ -154,16 +179,27 @@ def _run_primitive(self, primitive_inputs: Dict, user_kwargs: Dict) -> RuntimeJo

combined = Options._set_default_resilience_options(combined)
combined = Options._remove_none_values(combined)

primitive_inputs.update(Options._get_program_inputs(combined))

if self._backend and combined["transpilation"]["skip_transpilation"]:
if self._backend and combined["transpilation"]["skip_transpilation"] and not run_simulator:
Copy link
Collaborator

Choose a reason for hiding this comment

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

check_faulty should apply to fake backends as well, if we ever get one that mimics a real backend with faulty edge/qubits

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is currently no check_faulty method in FakeBackendV2 nor in AerSimulator. To support this, we would need to add such a method to FakeBackendV2 after it is transferred to Runtime. So I suggest we defer this to a separate issue.

for circ in primitive_inputs["circuits"]:
self._backend.check_faulty(circ)

logger.info("Submitting job using options %s", combined)

runtime_options = Options._get_runtime_options(combined)

if run_simulator:
# do we need to keep the other 'flat' options as well,
# for passing to terra directly?
primitive_inputs["optimization_level"] = combined["optimization_level"]
runtime_options["backend"] = self._backend

return self._service.run(
program_id=self._program_id(),
options=runtime_options,
inputs=primitive_inputs,
)
if self._session:
return self._session.run(
program_id=self._program_id(),
Expand Down
21 changes: 9 additions & 12 deletions qiskit_ibm_runtime/fake_provider/fake_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
Fake provider class that provides access to fake backends.
"""

from typing import List

from qiskit.providers.provider import ProviderV1
from qiskit.providers.exceptions import QiskitBackendNotFoundError
from qiskit.providers.fake_provider.fake_backend import FakeBackendV2
Copy link
Collaborator

@ElePT ElePT Dec 8, 2023

Choose a reason for hiding this comment

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

I have recently opened a PR that migrates the fake provider base classes from qiskit (#1270), should it be merged before this PR, this import path would change to from qiskit_ibm_runtime.fake_provider.fake_backend import FakeBackendV2. I'm happy to monitor it on my side, but thought it would make sense to bring it to your attention too.


from .backends import *

Expand Down Expand Up @@ -69,21 +72,15 @@ class FakeProviderForBackendV2(ProviderV1):
available in the :mod:`qiskit_ibm_runtime.fake_provider`.
"""

def backend(self, name=None, **kwargs): # type: ignore
"""
Filter backends in provider by name.
"""
backend = self._backends[0]
def backend(self, name: str = None, **kwargs) -> FakeBackendV2: # type: ignore
"""Return the backend according to its name. If kwargs are defined, then
additional filters may be applied. If no such backend, return None."""
if name:
filtered_backends = [backend for backend in self._backends if backend.name() == name]
if not filtered_backends:
raise QiskitBackendNotFoundError()

backend = filtered_backends[0]

filtered_backends = [backend for backend in self.backends() if backend.name == name]
backend = filtered_backends[0] if len(filtered_backends) > 0 else None
return backend

def backends(self, name=None, **kwargs): # type: ignore
def backends(self, name=None, **kwargs) -> List[FakeBackendV2]: # type: ignore
return self._backends

def __init__(self) -> None:
Expand Down
98 changes: 98 additions & 0 deletions qiskit_ibm_runtime/fake_runtime_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Qiskit fake runtime job."""

from typing import Optional, Dict, Any, List
from datetime import datetime

from qiskit.primitives.primitive_job import PrimitiveJob
from qiskit.providers import JobStatus, JobV1
from qiskit.providers.fake_provider import FakeBackendV2 as FakeBackend
Copy link
Collaborator

Choose a reason for hiding this comment

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

same comment as above regarding the import path


# pylint: disable=cyclic-import
# from .qiskit_runtime_service import QiskitRuntimeService


class FakeRuntimeJob(JobV1):
"""Representation of a runtime program execution on a simulator."""

def __init__(
self,
primitive_job: PrimitiveJob,
backend: FakeBackend,
job_id: str,
program_id: str,
params: Optional[Dict] = None,
creation_date: Optional[str] = None,
tags: Optional[List] = None,
) -> None:
"""FakeRuntimeJob constructor."""
super().__init__(backend=backend, job_id=job_id)
self._primitive_job = primitive_job
self._job_id = job_id
self._params = params or {}
self._program_id = program_id
self._creation_date = creation_date
self._tags = tags

def result(self) -> Any:
"""Return the results of the job."""
return self._primitive_job.result()

def cancel(self) -> None:
self._primitive_job.cancel()

def status(self) -> JobStatus:
return self._primitive_job.status()
Comment on lines +48 to +56
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not just inherit PrimitiveJob, then you only need to add methods not already supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried, but couldn't manage to do that, because I am receiving primitive_job as an object and couldn't find a way to construct the FakeRuntimeObject with a pre-constructed super() object. I would need something like a copy constructor.
I could simply use PrimitiveJob directly, but then I wouldn't be able to add any fields/methods.
Can you advise how to do this with inheritance?


@property
def inputs(self) -> Dict:
"""Job input parameters.

Returns:
Input parameters used in this job.
"""
return self._params

@property
def creation_date(self) -> Optional[datetime]:
"""Job creation date in local time.

Returns:
The job creation date as a datetime object, in local time, or
``None`` if creation date is not available.
"""
return self._creation_date

@property
def tags(self) -> List:
"""Job tags.

Returns:
Tags assigned to the job that can be used for filtering.
"""
return self._tags

def submit(self) -> None:
"""Unsupported method.
Note:
This method is not supported, please use
:meth:`~qiskit_ibm_runtime.QiskitRuntimeService.run`
to submit a job.
Raises:
NotImplementedError: Upon invocation.
"""
raise NotImplementedError(
"job.submit() is not supported. Please use "
"QiskitRuntimeService.run() to submit a job."
)
14 changes: 6 additions & 8 deletions qiskit_ibm_runtime/options/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

"""Utility functions for options."""

from qiskit_ibm_runtime.fake_provider.fake_backend import FakeBackendV2
from ..ibm_backend import IBMBackend


Expand All @@ -32,20 +33,17 @@ def set_default_error_levels(
Returns:
options with correct error level defaults.
"""
# The check that this is not a FakeBackendV2 is needed because FakeBackendV2
# does not have a configuration
is_simulator = not isinstance(backend, FakeBackendV2) and backend.configuration().simulator
if options.get("optimization_level") is None:
if (
backend.configuration().simulator
and options.get("simulator", {}).get("noise_model") is None
):
if is_simulator and options.get("simulator", {}).get("noise_model") is None:
options["optimization_level"] = 1
else:
options["optimization_level"] = default_optimization_level

if options.get("resilience_level") is None:
if (
backend.configuration().simulator
and options.get("simulator", {}).get("noise_model") is None
):
if is_simulator and options.get("simulator", {}).get("noise_model") is None:
options["resilience_level"] = 0
else:
options["resilience_level"] = default_resilience_level
Expand Down
Loading
Loading