SRI-International / QC-App-Oriented-Benchmarks

QED-C: The Quantum Economic Development Consortium provides these computer programs and software for use in the fields of quantum science and engineering.
Apache License 2.0
143 stars 79 forks source link

Support for Quantinuum within QED-C benchmarking suite #282

Open vprusso opened 1 year ago

vprusso commented 1 year ago

As of a recent fix based on this issue on the qiskit-quantinuum-provider project, one can now invoke a backend instance of a Quantinuum device via the Qiskit provider interface (as is done for the other supported backends within QED-C).

As a proof of concept (assuming one has access to a Quantinuum device), doing this following:

    import os
    from qiskit_quantinuum import Quantinuum
    from qiskit import execute
    Quantinuum.save_account(os.environ.get("QUANTINUUM_USERNAME"))

    backends = Quantinuum.backends()
    backend = Quantinuum.get_backend("H1-2E")

    qc = QuantumCircuit(2, 2)
    qc.h(0)
    qc.cx(0, 1)
    qc.measure([0,1], [0,1])
    result = execute(qc, backend).result()
    print(result.get_counts())

yields the following output:

Your id token is expired. Refreshing...
{'11': 502, '0': 512, '1': 4, '10': 6}

indicating a successful run on the device.

Now, presumably, one can run one of the QED-C benchmarking algorithms via the newly obtained provider functionality. Taking the quantum-fourier-transform Qiskit benchmark as an example, adding the following snippet to the bottom of this file:

if __name__ == "__main__":
    import os
    from qiskit_quantinuum import Quantinuum
    Quantinuum.save_account(os.environ.get("QUANTINUUM_USERNAME"))

    backends = Quantinuum.backends()
    backend = Quantinuum.get_backend("H1-2E")
    run(provider_backend=backend)

supplies the QFT benchmark with the custom Quantinuum Qiskit provider. However, running:

python qft_benchmark.py

with this addition appears to fail:

Quantum Fourier Transform Benchmark Program - Qiskit
... using circuit method 1
... execution starting at Jun 07, 2023 17:22:56 UTC
************
Executing [3] circuits with num_qubits = 2
... number of gates, depth = 11, 8
Traceback (most recent call last):
  File "/Users/vincent.russo/Projects/research/unitary_fund/metriq-api/benchmark/benchmark/QC-App-Oriented-Benchmarks/quantum-fourier-transform/qiskit/qft_benchmark.py", line 410, in <module>
    print(run(provider_backend=backend, min_qubits=min_qubits, max_qubits=max_qubits, num_shots=num_shots))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/vincent.russo/Projects/research/unitary_fund/metriq-api/benchmark/benchmark/QC-App-Oriented-Benchmarks/quantum-fourier-transform/qiskit/qft_benchmark.py", line 330, in run
    ex.throttle_execution(metrics.finalize_group)
  File "/Users/vincent.russo/Projects/research/unitary_fund/metriq-api/benchmark/benchmark/QC-App-Oriented-Benchmarks/quantum-fourier-transform/qiskit/../../_common/qiskit/execute.py", line 922, in throttle_execution
    check_jobs(completion_handler)
  File "/Users/vincent.russo/Projects/research/unitary_fund/metriq-api/benchmark/benchmark/QC-App-Oriented-Benchmarks/quantum-fourier-transform/qiskit/../../_common/qiskit/execute.py", line 1039, in check_jobs
    job_complete(job)
  File "/Users/vincent.russo/Projects/research/unitary_fund/metriq-api/benchmark/benchmark/QC-App-Oriented-Benchmarks/quantum-fourier-transform/qiskit/../../_common/qiskit/execute.py", line 741, in job_complete
    if job.status() == JobStatus.DONE:
       ^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/qiskit_quantinuum/quantinuumjob.py", line 313, in status
    self._result = self._process_results()
                   ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/qiskit_quantinuum/quantinuumjob.py", line 337, in _process_results
    status = res_resp.get('status', 'failed')
             ^^^^^^^^^^^^
AttributeError: 'JobStatus' object has no attribute 'get'

Am I making use of the provider_backend argument correctly? Based on the initial code example we know that we can successfully connect to and run a simple circuit on the Quantinuum device, so presumably this is failing because either I am using this incorrectly or because the QFT circuit is more involved and requires some type of transpilation for the Quantinuum device, or something else.

Any obvious reason as to why this is presently failing?

@rtvuser1 @WrathfulSpatula @nathanshammah @ninjab3381

WrathfulSpatula commented 1 year ago

@vprusso Remember that we've seen already that the Quantinuum provider in its current form might not fully adhere to the ProviderV1 API defined in Qiskit. (I'm referencing the earlier the issue you ran into trying to pull results out by the job ID, which isn't supported.)

Actually, looking at the stack trace, this might be exactly the issue, as Quantinuum currently only supports 1 job at a time, and the provider developers opted to deviate from the usual API by cutting certain metadata and metadata query capabilities designed to support multiple jobs.

WrathfulSpatula commented 1 year ago

@vprusso Please do me a huge favor, since you have Quantinuum API access: please check what happens if you (locally) install the branch that commit 346469e is in (from above) and run this again.

rtvuser1 commented 1 year ago

Note: You can also set the global variable

 # maximum number of active jobs
 _common.execute.max_jobs_active = 5;

to just 1 and it will only launch one job at a time.

Or, even better, you can set the following variable to True

# job mode: False = wait, True = submit multiple jobs
job_mode = False

Either way will execute only one job at a time, but the job_mode will process the result directly returned from backend.execute() instead of from querying for the job_id

rtvuser1 commented 1 year ago

Note: you should also pass the backend_id to the run method, as we do in the benchmark notebooks. In this case it seem like it should be "H1-2E". This is only used for identifying the run in the charts that get created and is not likely the source of your status issue

WrathfulSpatula commented 1 year ago

Thank you, @rtvuser1! That's excellent to know, and we likely need to do that, too.

All the same, now that I've perused the Quantinuum provider code and compared to the stack trace from @vprusso, I think we (also) nonetheless need to open a PR on the upstream provider repo for my branch, above. That variable isn't anticipated to be a JobStatus at all, but it looks like that's what they're getting back from Quantinuum, here, hence the case needs handling in general.

vprusso commented 1 year ago

I flipped both of these global variables such that

max_jobs_active = 1
...
job_mode = True

within execute.py. Indeed, the job still fails, but now it fails serially:

Quantum Fourier Transform Benchmark Program - Qiskit
... using circuit method 1
... execution starting at Jun 10, 2023 13:34:54 UTC
************
Executing [3] circuits with num_qubits = 2
... number of gates, depth = 11, 8
... error occurred during job.status() for circuit 2 2 -- retry 1
....

I also attempted to pull down the patch from @WrathfulSpatula:

pip install https://github.com/WrathfulSpatula/qiskit-quantinuum-provider/archive/282_qed-c.zip

And ran again, but this still appears to fail with the same output as stated above. The code I'm running has been updated to reflect the "*Provider" pattern that is used in the benchmarks-qiskit.ipynb file as well, specifically:

    import os
    from qiskit_quantinuum import QuantinuumProvider
    provider = QuantinuumProvider()

    provider.save_account(os.environ.get("QUANTINUUM_USERNAME"))
    backends = provider.backends()
    backend = provider.get_backend("H1-2E")
    run(provider_backend=backend)
WrathfulSpatula commented 1 year ago

@vprusso I can take another look at this if I have the stack trace from the error in jobs.status().