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

Conversation

merav-aharoni
Copy link
Contributor

@merav-aharoni merav-aharoni commented Nov 29, 2023

Summary

When the backend is a FakeBackend or AerSimulator, run the program locally, rather than through the IBM Cloud.

Details and comments

Fixes #

@merav-aharoni
Copy link
Contributor Author

merav-aharoni commented Nov 29, 2023

I wrote the basic flow, including a simple test. However, I encountered quite a few problems/questions:

  • What is the correct way to transfer options? Which options should be transferred?
  • Currently working only when skip_transpilation=True - fixed
  • Should we support FakeBackends V1 in addition to V2? - see @kt474's comments below
  • Still need to add support for circuit-runner. Is QASM3 supported on simulators?
  • Should ibmq_qasm_simulator also run locally? - see @kt474's comments below
  • Does Session have any meaning when running on simulator? - see @kt474's comments below
  • Must the user have an account to run on a simulator? Currently, we need to initialize a QiskitRuntimeService, and this fails when no account. - see @kt474's comments below

@kt474 , @jyu00 - any insights on the above are welcome.

@kt474
Copy link
Member

kt474 commented Dec 5, 2023

What is the correct way to transfer options? Which options should be transferred?
I'm not sure about how options would work in these fake backends, i'll have to take a look. @jyu00 thoughts here?

Should we support FakeBackends V1 in addition to V2?

I don't believe so, we can just support V2 for now

Still need to add support for circuit-runner. Is QASM3 supported on simulators?

No, QASM3 is not currently supported on simulators

Should ibmq_qasm_simulator also run locally?

No, I don't believe so - ibmq_qasm_simulator is one of the simulators that will be sunset.

Does Session have any meaning when running on simulator?

No, sessions don't have any meaning when using simulators - jobs on simulators run the same way, independent of whether
or not they are in a session

Must the user have an account to run on a simulator? Currently, we need to initialize a QiskitRuntimeService, and this fails when no account.

Yes, the user must have an account

@merav-aharoni
Copy link
Contributor Author

On second thought, I am not sure we need to do anything more for executing backend.run for fake backends. Have a look at the following code:

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit import QuantumCircuit

backend_name="fake_almaden"
service = QiskitRuntimeService(channel="ibm_quantum")
backend = service.backend(name=backend_name)

circuit = QuantumCircuit(2,2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure([0,1], [0,1])
result = backend.run(circuit, seed_simulator=4, shots=10, method="statevector").result()

print("name = ", result.backend_name)
print("shots = ", result.results[0].shots)
print("seed = ", result.results[0].seed_simulator)
print("method = ", result.results[0].metadata["method"])

Results are:

name =  aer_simulator
shots =  10
seed =  4
method =  statevector

What do you think?

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 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

@@ -12,6 +12,7 @@

"""Utility functions for options."""

from qiskit.providers.fake_provider import fake_backend
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here too

) -> None:
"""FakeRuntimeJob constructor."""
super().__init__(backend=backend, job_id=job_id)
# self._service = service # do we need this?
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need service, but it would be nice to have the other ones - creation_date, user_callback, result_decoder, tags

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 added creation_date and tags.
Removed user_callback as you wrote below and also because it refers to interim results.
Removed result_decoder as you wrote below, because results will not be returned encoded.

inputs=primitive_inputs,
# are the following relevant for simulators?
# callback=combined.get("environment", {}).get("callback", None),
# result_decoder=DEFAULT_DECODERS.get(self._program_id()),
Copy link
Member

Choose a reason for hiding this comment

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

I think we can leave these out for simulators

Copy link
Member

Choose a reason for hiding this comment

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

In terms of the other options, I think it'd be nice to have the run options like:

{'shots': 400, 'init_qubits': True, 'noise_model': None, 'seed_simulator': None}
and
{'max_execution_time': None, 'log_level': 'WARNING', 'job_tags': []}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  • shots and seed_simulator are already supported.
  • I couldn't fine init_qubits or anything similar in the AerSimulator, so I don't think there is any point of passing it on. Let me know if you know otherwise. I did find it as a parameter of assemble_circuits in Terra, but I am not sure how and if this is called.
  • Same as above for max_execution_time.
  • noise_model - I think this is only relevant when calling AerSimulator and not for fake backends, because the latter define their own noise models. For Aer, I pass along all the options that are AerSimulator._default_options().
  • Regarding log_level, in Terra and in Aer, I only found setting the log_level globally as an environment variable, so I am not sure what we can do here.
  • job_tags - I added to FakeRuntimeJob.

qiskit_ibm_runtime/fake_provider/fake_provider.py Outdated Show resolved Hide resolved
qiskit_ibm_runtime/fake_provider/fake_provider.py Outdated Show resolved Hide resolved
@@ -98,6 +101,13 @@ def __init__(
if isinstance(backend, IBMBackend):
self._service = backend.service
self._backend = backend
elif isinstance(backend, AerSimulator):
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should check for both Aer and FakeBackend

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good comment. I actually only supported FakeBackendV2 when given as a string. I modified here and added to the test TestRunSimulation.test_basic_flow.

@@ -20,10 +20,13 @@
from dataclasses import asdict
import warnings

from qiskit_aer import AerSimulator
Copy link
Collaborator

Choose a reason for hiding this comment

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

Aer is not a required package, so this import be wrapped with try/except

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added try and raise exception when attempting to run with a simulator and not aer.
Also added skip for relevant tests.
Please have a look if it is done properly.

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.

Comment on lines +49 to +57
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()
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?

Comment on lines 36 to 38
is_simulator = (
isinstance(backend, fake_backend.FakeBackendV2) or backend.configuration().simulator
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is no need to change optimization_level and resilience_level for fake backends. Fake backends should always have noise model (from the real backend it mimics), so one doesn't have to pass it in. And none supports resilience_level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed so we don't apply this to a FakeBackend.

Comment on lines +106 to +110
self._service = (
QiskitRuntimeService()
if QiskitRuntimeService.global_service is None
else QiskitRuntimeService.global_service
)
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.

@kt474
Copy link
Member

kt474 commented Mar 20, 2024

Closing, this was done in #1495

@kt474 kt474 closed this Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants