HPCA 2023 Supermarq Tutorial using qiskit-superstaq
You can run this notebook on Colab or Binder. The Colab requires sign in while Binder does not.
[ ]:
try:
import qiskit
import qiskit_superstaq as qss
except ImportError:
print("Installing qiskit-superstaq...")
%pip install --quiet 'qiskit-superstaq[examples]'
print("Installed qiskit-superstaq.")
print("You may need to restart the kernel to import newly installed packages.")
import qiskit
import qiskit_superstaq as qss
[2]:
try:
import supermarq
except ImportError:
print("Installing supermarq...")
%pip install --quiet supermarq
print("Installed supermarq.")
print("You may need to restart the kernel to import newly installed packages.")
import supermarq
Basics:
1. Provider creation
[3]:
# Provide your api key to `qss.SuperstaqProvider()` using the `api_key=` argument if
# the SUPERSTAQ_API_KEY environment variable is not set.
# Submit qiskit circuits via `qiskit-superstaq`
provider = qss.SuperstaqProvider()
print(provider.get_balance())
997.25 credits
[4]:
# See which targets are available
provider.get_targets(available=True)
[4]:
[Target(target='aqt_keysight_qpu', supports_submit=False, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='aqt_zurich_qpu', supports_submit=False, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='aws_dm1_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='aws_sv1_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='aws_tn1_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='cq_sqale_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='eeroq_wonderlake_qpu', supports_submit=False, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ibmq_fez_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ibmq_kingston_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ibmq_marrakesh_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ibmq_pittsburgh_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ibmq_torino_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ionq_forte-1_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ionq_ion_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='qscout_peregrine_qpu', supports_submit=False, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='qtm_h1-1_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='qtm_h1-1e_simulator', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='qtm_h2-1_qpu', supports_submit=True, supports_submit_qubo=False, supports_compile=True, available=True, retired=False, accessible=True),
Target(target='ss_unconstrained_simulator', supports_submit=True, supports_submit_qubo=True, supports_compile=True, available=True, retired=False, accessible=True)]
2. Benchmark instantiation
NOTE: after executing a benchmark circuit, the score should always be evaluated using the same Benchmark object that was used to generate the circuit.
All of the benchmarks can be found in supermarq/benchmarks/.
Think about other circuits/benchmarks you would like to implement and run!
[5]:
# Create your benchmark using qiskit circuits
ghz = supermarq.ghz.GHZ(5)
ghz_circuit = ghz.qiskit_circuit()
ghz_circuit.draw()
[5]:
┌───┐ ┌─┐
q_0: ┤ H ├──■───────┤M├──────────────────────
└───┘┌─┴─┐ └╥┘ ┌─┐
q_1: ─────┤ X ├──■───╫──────┤M├──────────────
└───┘┌─┴─┐ ║ └╥┘ ┌─┐
q_2: ──────────┤ X ├─╫───■───╫──────┤M├──────
└───┘ ║ ┌─┴─┐ ║ └╥┘┌─┐
q_3: ────────────────╫─┤ X ├─╫───■───╫─┤M├───
║ └───┘ ║ ┌─┴─┐ ║ └╥┘┌─┐
q_4: ────────────────╫───────╫─┤ X ├─╫──╫─┤M├
║ ║ └───┘ ║ ║ └╥┘
c: 5/════════════════╩═══════╩═══════╩══╩══╩═
0 1 2 3 4 3. Circuit evaluation
The generated circuits can be evaluated on a backend using any compatible service: AWS Braket, IBM Qiskit, cirq-superstaq, qiskit-superstaq, etc. Here we use qiskit-superstaq
[6]:
backend = provider.get_backend("ss_unconstrained_simulator")
job_qss = backend.run(ghz_circuit, shots=1000, method="dry-run")
4. Compute the score
NOTE: after executing a benchmark circuit, the score should always be evaluated using the same Benchmark object that was used to generate the circuit.
[7]:
job_qss.status()
[7]:
<JobStatus.DONE: 'job has successfully run'>
[8]:
counts = job_qss.result().get_counts()
print(counts)
score_qss = ghz.score(counts)
print(score_qss)
{'00000': 476, '11111': 524}
0.9994236678412428
5. Visualize the results
The function in supermarq/plotting.plot_results produces a simple bar plot. Feel free to copy and paste that code here to generate more detailed figures.
[9]:
supermarq.plotting.plot_results([score_qss], ["ghz_qss"])
Quantum Program Profiling
The current hardware-agnostic application features can be found in supermarq/features.py. Quantum program profiling is an exciting area of research that is just getting started – what kinds of features would you find meaningful? Try implementing them!
Compute features
[10]:
ghz_circuit = supermarq.ghz.GHZ(10).qiskit_circuit()
ghz_features = [
supermarq.features.compute_communication_with_qiskit(ghz_circuit),
supermarq.features.compute_depth_with_qiskit(ghz_circuit),
supermarq.features.compute_entanglement_with_qiskit(ghz_circuit),
supermarq.features.compute_liveness_with_qiskit(ghz_circuit),
supermarq.features.compute_measurement_with_qiskit(ghz_circuit),
supermarq.features.compute_parallelism_with_qiskit(ghz_circuit),
]
print(ghz_features)
[0.2, 1.0, 0.9, 0.2636363636363636, 0.0, 0.0]
Visualize the feature vector
[11]:
supermarq.plotting.plot_benchmark(
title="A single GHZ benchmark",
labels=["ghz10"],
features=[ghz_features],
spoke_labels=["PC", "CD", "Ent", "Liv", "Mea", "Par"],
)
Correlating performance <> features
To correlate the performance of a particular device with a certain application feature, it is helpful to evaluate several different benchmarks on that same device. This code example creates 4 different benchmarks, computes their feature vectors, evaluates them on a backend, and then measures the correlation between the performance seen and the application features.
Characterize the benchmarks
[12]:
benchmark_features = {}
benchmarks = [
(supermarq.ghz.GHZ(5), "ghz5"),
(supermarq.hamiltonian_simulation.HamiltonianSimulation(4), "hsim4"),
]
for benchmark, label in benchmarks:
benchmark_features[label] = [
supermarq.features.compute_communication_with_qiskit(benchmark.qiskit_circuit()),
supermarq.features.compute_depth_with_qiskit(benchmark.qiskit_circuit()),
supermarq.features.compute_entanglement_with_qiskit(benchmark.qiskit_circuit()),
supermarq.features.compute_liveness_with_qiskit(benchmark.qiskit_circuit()),
supermarq.features.compute_measurement_with_qiskit(benchmark.qiskit_circuit()),
supermarq.features.compute_parallelism_with_qiskit(benchmark.qiskit_circuit()),
]
print(benchmark_features)
{'ghz5': [0.4, 1.0, 0.8, 0.4666666666666667, 0.0, 0], 'hsim4': [0.5, 1.0, 0.2857142857142857, 0.5961538461538461, 0.0, 0.20512820512820515]}
Evaluate
[13]:
jobs = []
backend = provider.get_backend("ss_unconstrained_simulator")
for benchmark, label in benchmarks:
job = backend.run(benchmark.qiskit_circuit(), shots=1000)
jobs.append((label, job, benchmark))
Wait until the benchmarks have successfully executed…
[14]:
device_scores = {}
for label, job, benchmark in jobs:
if job.status() == qiskit.providers.jobstatus.JobStatus.DONE:
counts = job.result(0).get_counts()
print(counts)
score = benchmark.score(counts)
print(score)
device_scores[label] = score
else:
print(label, "not done!")
{'11111': 498, '00000': 502}
0.9999959999839999
{'1111': 545, '0010': 8, '1110': 77, '0111': 81, '1011': 93, '0011': 15, '1010': 14, '1101': 87, '1001': 23, '0101': 18, '1100': 18, '0110': 14, '0001': 5, '1000': 1, '0100': 1}
0.9996966094067259
Measure the correlation between device performance and application features
[15]:
supermarq.plotting.plot_correlations(
benchmark_features,
device_scores,
["PC", "CD", "Ent", "Liv", "Mea", "Par"],
device_name="ibmq_sim",
)