Skip to content

1. Pipeline and utility tools for parallel benchmarking

1.1 Benchmark circuits

Included are a suite of benchmark circuits, useful for examples, getting started, and benchmarking; and include, * Bell state circuit/state * 3 and 4 qubit GHZ circuit/state * 3 and 4 qubit linear cluster states

""" Example benchmark circuits (we've previously seen them in examples)"""
from graphiq.benchmarks.circuits import ghz3_state_circuit

circuit, ideal_state = ghz3_state_circuit()
circuit.draw_circuit()
ideal_state.rep_data.draw();
No description has been provided for this image
No description has been provided for this image

1.2 Benchmark graph states

We also include a suite of benchmark graph states.

""" Linear cluster state """
from graphiq.benchmarks.graph_states import linear_cluster_state

linear_cluster_state(2).draw()
linear_cluster_state(10).draw();
No description has been provided for this image
No description has been provided for this image
""" Lattice cluster state """
from graphiq.benchmarks.graph_states import lattice_cluster_state

lattice_cluster_state((2, 4)).draw()

lattice_cluster_state((2,)).draw()

lattice_cluster_state((2, 2, 3)).draw();
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
""" Star graph state """
from graphiq.benchmarks.graph_states import star_graph_state

star_graph_state(5).draw()
No description has been provided for this image
""" Random connected graph state """

# NOTE that this function optionally takes a random number generator (rng) object
# An rng with a specific should ALWAYS be specified when benchmarking such that
# results are replicable
import numpy as np
from graphiq.benchmarks.graph_states import random_graph_state

n_qubits = 10
# Sparse circuit
prob_edge = 0.1  # this is the probability of an edge (ignoring edges necessary for connectedness)
rng = np.random.default_rng(4)
random_graph_state(n_qubits, prob_edge, rng).draw()

# Dense circuit
prob_edge = 0.9  # this is the probability of an edge (ignoring edges necessary for connectedness)
rng = np.random.default_rng(4)
random_graph_state(n_qubits, prob_edge, rng).draw()
No description has been provided for this image
No description has been provided for this image

1.2.1 Local Clifford equivalence

Local Clifford (LC) equivalence is significant in graph states, because local complementation tends to be easy to implement in quantum computing. Thus, if we can find a state which is LC equivalent to another, we can also easily find the other state. Thus, we provide a functionality to generate LC equivalent states, from a starting circuit using random_lc_equivalent (see docstring). This could be useful, for example, for determining whether all LC equivalent states are equally easy to find or not (given a specific solver).

# lc_equivalence_demo calls on random_lc_equivalent on a lattice state
from graphiq.benchmarks.graph_states import lc_equivalence_demo

lc_equivalence_demo()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
g1, g2 LC equivalent: True
g1, g3 LC equivalent: True
g2, g3 LC equivalent: True

1.3 Benchmark: Utils and Pipeline

Our benchmarking pipeline help manage different solver runs, and mediates the information collection, data saving, and multi-core computing distribution.

benchmark_utils.py was used for earlier data collection, but pipeline.py should be used for most future benchmarking tasks. It allows our code to run on multiple cores through a python package called ray.

A good example of how to use the pipeline is in the __main__ function of pipeline.py, so we won't duplicate the example here.

1.3.1 Functions offered by pipeline.py

  • metadata(): collects a data dictionary about the machine running the tests
  • run_combinations: allow us to generate all possible combinations different combinations of solvers/targets/compilers/target metrics, and returns a list of dictionaries, each capable of being used for a benchmark run
  • benchmark_run: benchmarks a single solver run
  • benchmark: benchmarks a sequence of runs

1.3.2 Data generated in benchmark runs

  • Machine metadata
  • Runtime of a benchmark run
  • Generated populations / halls of fame (circuits expressed as openQASM)
  • Solver logs at each iteration, for both the population and hall of fame (include interation number, statistics for the cost function cost, statistics for the circuit depths)
  • Class / information about the solver, compiler, etc.

1.3.3 Benchmarking consideration: measurement results and probabilistic effects

One word of warning about solver data.

We have probabilistic events occurring in our circuit simulation. Currently, we do not evaluate circuits multiple times in the solver. This means that the cost functions noted in the per-iteration logs are dependent only on one run, even if they might have failed in another run.

For example, a circuit may generate the correct output state if a Z measurement on emitter 0 returns 1, and return an incorrect output state otherwise. This is not reflected in our solver data.

Thus, before deciding a given circuit design is optimal, we should either test all possible measurement combinations (there are 2^n possible combinations for n measurements which lead to probabilistic gate application), or probabilistically simulate the circuit repeatedly and see whether its output is consistent.

It may useful to add some benchmarking functionality to that effect, in the future.

NOTE: the function circuit_measurement_independent in benchmark_utils.py was used for this purpose in earlier tests. However, it is only guaranteed to work in cases where only 1 probabilistic gate application.