HPCA 2023 Supermarq Tutorial using qiskit-superstaq

Open in Colab Launch Binder

You can run this notebook on Colab or Binder. The Colab requires sign in while Binder does not.

[1]:
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

import qiskit
import qiskit_superstaq as qss

Basics:

1. Provider creation

[2]:
# 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())
865.39 credits
[3]:
# See which targets are available
provider.get_targets(available=True)
[3]:
[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='iqm_garnet_qpu', 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='rigetti_ankaa-3_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!

[4]:
# Create your benchmark using qiskit circuits
ghz = supermarq.ghz.GHZ(5)
ghz_circuit = ghz.qiskit_circuit()
ghz_circuit.draw()
[4]:
     ┌───┐          ┌─┐
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

[5]:
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.

[6]:
job_qss.status()
[6]:
<JobStatus.DONE: 'job has successfully run'>
[7]:
counts = job_qss.result().get_counts()
print(counts)
score_qss = ghz.score(counts)
print(score_qss)
{'11111': 498, '00000': 502}
0.9999959999839999

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.

[8]:
supermarq.plotting.plot_results([score_qss], ["ghz_qss"])
../../../_images/apps_supermarq_examples_Supermarq_HPCA_Tutorial_qss_16_0.png

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

[9]:
ghz_circuit = supermarq.ghz.GHZ(10).qiskit_circuit()
ghz_features = [
    supermarq.converters.compute_communication_with_qiskit(ghz_circuit),
    supermarq.converters.compute_depth_with_qiskit(ghz_circuit),
    supermarq.converters.compute_entanglement_with_qiskit(ghz_circuit),
    supermarq.converters.compute_liveness_with_qiskit(ghz_circuit),
    supermarq.converters.compute_measurement_with_qiskit(ghz_circuit),
    supermarq.converters.compute_parallelism_with_qiskit(ghz_circuit),
]
print(ghz_features)
[0.2, 1.0, 0.9, np.float64(0.2636363636363636), 0.0, 0.0]

Visualize the feature vector

[10]:
supermarq.plotting.plot_benchmark(
    title="A single GHZ benchmark",
    labels=["ghz10"],
    features=[ghz_features],
    spoke_labels=["PC", "CD", "Ent", "Liv", "Mea", "Par"],
)
../../../_images/apps_supermarq_examples_Supermarq_HPCA_Tutorial_qss_21_0.png

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

[11]:
benchmark_features = {}
benchmarks = [
    (supermarq.ghz.GHZ(5), "ghz5"),
    (supermarq.hamiltonian_simulation.HamiltonianSimulation(4), "hsim4"),
]
for benchmark, label in benchmarks:
    benchmark_features[label] = [
        supermarq.converters.compute_communication_with_qiskit(benchmark.qiskit_circuit()),
        supermarq.converters.compute_depth_with_qiskit(benchmark.qiskit_circuit()),
        supermarq.converters.compute_entanglement_with_qiskit(benchmark.qiskit_circuit()),
        supermarq.converters.compute_liveness_with_qiskit(benchmark.qiskit_circuit()),
        supermarq.converters.compute_measurement_with_qiskit(benchmark.qiskit_circuit()),
        supermarq.converters.compute_parallelism_with_qiskit(benchmark.qiskit_circuit()),
    ]
print(benchmark_features)
{'ghz5': [0.4, 1.0, 0.8, np.float64(0.4666666666666667), 0.0, 0], 'hsim4': [0.5, 1.0, 0.2857142857142857, np.float64(0.5961538461538461), 0.0, 0.20512820512820515]}

Evaluate

[12]:
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…

[13]:
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': 516, '00000': 484}
0.9997439344304242
{'1111': 531, '1110': 86, '1001': 22, '1011': 97, '0111': 94, '0101': 12, '1101': 85, '0011': 14, '1010': 21, '1100': 10, '0110': 16, '1000': 4, '0100': 3, '0001': 3, '0000': 2}
0.9989466094067263

Measure the correlation between device performance and application features

[14]:
supermarq.plotting.plot_correlations(
    benchmark_features,
    device_scores,
    ["PC", "CD", "Ent", "Liv", "Mea", "Par"],
    device_name="ibmq_sim",
)
../../../_images/apps_supermarq_examples_Supermarq_HPCA_Tutorial_qss_30_0.png