Skip to content
This repository has been archived by the owner on Jul 13, 2022. It is now read-only.

Commit

Permalink
QV experiment (qiskit-community#32)
Browse files Browse the repository at this point in the history
Co-authored-by: Gadi Aleksandrowicz <gadia@il.ibm.com>
  • Loading branch information
dekelmeirom and gadial committed Jul 19, 2021
1 parent 057d79c commit 4f11eef
Show file tree
Hide file tree
Showing 14 changed files with 1,234 additions and 0 deletions.
378 changes: 378 additions & 0 deletions docs/tutorials/qv_example.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions qiskit_experiments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@
from . import analysis
from . import randomized_benchmarking
from . import tomography
from . import quantum_volume
15 changes: 15 additions & 0 deletions qiskit_experiments/quantum_volume/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Quantum Volume Experiment Classes."""
from .qv_experiment import QuantumVolume
from .qv_analysis import QuantumVolumeAnalysis
266 changes: 266 additions & 0 deletions qiskit_experiments/quantum_volume/qv_analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
Quantum Volume analysis class.
"""

import math
import warnings
from typing import Optional
import numpy as np

from qiskit_experiments.base_analysis import BaseAnalysis
from qiskit_experiments.base_analysis import AnalysisResult
from qiskit_experiments.analysis import plotting


class QuantumVolumeAnalysis(BaseAnalysis):
"""Quantum Volume Analysis class."""

# pylint: disable = arguments-differ
def _run_analysis(
self,
experiment_data,
plot: bool = True,
ax: Optional["plotting.pyplot.AxesSubplot"] = None,
):
"""Run analysis on circuit data.
Args:
experiment_data (ExperimentData): the experiment data to analyze.
plot: If True generate a plot of fitted data.
ax: Optional, matplotlib axis to add plot to.
Returns:
tuple: A pair ``(analysis_result, figures)`` where
``analysis_results`` may be a single or list of
AnalysisResult objects, and ``figures`` may be
None, a single figure, or a list of figures.
"""
depth = experiment_data.experiment.num_qubits
data = experiment_data.data()
num_trials = len(data)
heavy_output_prob_exp = []

for data_trial in data:
heavy_output = self._calc_ideal_heavy_output(
data_trial["metadata"]["ideal_probabilities"], data_trial["metadata"]["depth"]
)
heavy_output_prob_exp.append(
self._calc_exp_heavy_output_probability(data_trial, heavy_output)
)

analysis_result = AnalysisResult(
self._calc_quantum_volume(heavy_output_prob_exp, depth, num_trials)
)

if plot and plotting.HAS_MATPLOTLIB:
ax = self._format_plot(ax, analysis_result)
figures = [ax.get_figure()]
else:
figures = None
return [analysis_result], figures

@staticmethod
def _calc_ideal_heavy_output(probabilities_vector, depth):
"""
Calculate the bit strings of the heavy output for the ideal simulation
Args:
ideal_data (dict): the simulation result of the ideal circuit
Returns:
list: the bit strings of the heavy output
"""

format_spec = "{0:0%db}" % depth
# Keys are bit strings and values are probabilities of observing those strings
all_output_prob_ideal = {
format_spec.format(b): float(np.real(probabilities_vector[b]))
for b in range(2 ** depth)
}

median_probabilities = float(np.real(np.median(probabilities_vector)))
heavy_strings = list(
filter(
lambda x: all_output_prob_ideal[x] > median_probabilities,
list(all_output_prob_ideal.keys()),
)
)
return heavy_strings

@staticmethod
def _calc_exp_heavy_output_probability(data, heavy_outputs):
"""
Calculate the probability of measuring heavy output string in the data
Args:
data (dict): the result of the circuit exectution
heavy_outputs (list): the bit strings of the heavy output from the ideal simulation
Returns:
int: heavy output probability
"""
circ_shots = sum(data["counts"].values())

# Calculate the number of heavy output counts in the experiment
heavy_output_counts = sum([data["counts"].get(value, 0) for value in heavy_outputs])

# Calculate the experimental heavy output probability
return heavy_output_counts / circ_shots

@staticmethod
def _calc_z_value(mean, sigma):
"""Calculate z value using mean and sigma.
Args:
mean (float): mean
sigma (float): standard deviation
Returns:
float: z_value in standard normal distibution.
"""

if sigma == 0:
# Assign a small value for sigma if sigma = 0
sigma = 1e-10
warnings.warn("Standard deviation sigma should not be zero.")

z_value = (mean - 2 / 3) / sigma

return z_value

@staticmethod
def _calc_confidence_level(z_value):
"""Calculate confidence level using z value.
Accumulative probability for standard normal distribution
in [-z, +infinity] is 1/2 (1 + erf(z/sqrt(2))),
where z = (X - mu)/sigma = (hmean - 2/3)/sigma
Args:
z_value (float): z value in in standard normal distibution.
Returns:
float: confidence level in decimal (not percentage).
"""

confidence_level = 0.5 * (1 + math.erf(z_value / 2 ** 0.5))

return confidence_level

def _calc_quantum_volume(self, heavy_output_prob_exp, depth, trials):
"""
Calc the quantum volume of the analysed system.
quantum volume is determined by the largest successful depth.
A depth is successful if it has 'mean heavy-output probability' > 2/3 with confidence
level > 0.977 (corresponding to z_value = 2), and at least 100 trials have been ran.
we assume the error (standard deviation) of the heavy output probability is due to a
binomial distribution. standard deviation for binomial distribution is sqrt(np(1-p)),
where n is the number of trials and p is the success probability.
Returns:
dict: quantum volume calculations -
the quantum volume,
whether the results passed the threshold,
the confidence of the result,
the heavy output probability for each trial,
the mean heavy output probability,
the error of the heavy output probability,
the depth of the circuit,
the number of trials ran
"""
quantum_volume = 1
success = False

mean_hop = np.mean(heavy_output_prob_exp)
sigma_hop = (mean_hop * ((1.0 - mean_hop) / trials)) ** 0.5
z = 2
threshold = 2 / 3 + z * sigma_hop
z_value = self._calc_z_value(mean_hop, sigma_hop)
confidence_level = self._calc_confidence_level(z_value)
# Must have at least 100 trials
if trials < 100:
warnings.warn("Must use at least 100 trials to consider Quantum Volume as successful.")
if mean_hop > threshold and trials >= 100:
quantum_volume = 2 ** depth
success = True

result = {
"quantum volume": quantum_volume,
"qv success": success,
"confidence": confidence_level,
"heavy output probability": heavy_output_prob_exp,
"mean hop": mean_hop,
"sigma": sigma_hop,
"depth": depth,
"trials": trials,
}
return result

@staticmethod
def _format_plot(ax, analysis_result):
"""
Format the QV plot
Args:
ax: matplotlib axis to add plot to.
analysis_result: the results of the experimnt
Returns:
AxesSubPlot: the matplotlib axes containing the plot.
"""
trial_list = np.arange(1, analysis_result["trials"] + 1) # x data

hop_accumulative = np.cumsum(analysis_result["heavy output probability"]) / trial_list
two_sigma = 2 * (hop_accumulative * (1 - hop_accumulative) / trial_list) ** 0.5

# Plot inidivual HOP as scatter
ax = plotting.plot_scatter(
trial_list,
analysis_result["heavy output probability"],
ax=ax,
s=3,
zorder=3,
label="Individual HOP",
)
# Plot accumulative HOP
ax.plot(trial_list, hop_accumulative, color="r", label="Cumulative HOP")
# Plot two-sigma shaded area
ax = plotting.plot_errorbar(
trial_list,
hop_accumulative,
two_sigma,
ax=ax,
fmt="none",
ecolor="lightgray",
elinewidth=20,
capsize=0,
alpha=0.5,
label="2$\\sigma$",
)
# Plot 2/3 success threshold
ax.axhline(2 / 3, color="k", linestyle="dashed", linewidth=1, label="Threshold")

ax.set_ylim(
max(hop_accumulative[-1] - 4 * two_sigma[-1], 0),
min(hop_accumulative[-1] + 4 * two_sigma[-1], 1),
)

ax.set_xlabel("Number of Trials", fontsize=14)
ax.set_ylabel("Heavy Output Probability", fontsize=14)

ax.set_title(
"Quantum Volume experiment for depth "
+ str(analysis_result["depth"])
+ " - accumulative hop",
fontsize=14,
)

# Re-arrange legend order
handles, labels = ax.get_legend_handles_labels()
handles = [handles[1], handles[2], handles[0], handles[3]]
labels = [labels[1], labels[2], labels[0], labels[3]]
ax.legend(handles, labels)
return ax
Loading

0 comments on commit 4f11eef

Please sign in to comment.