Skip to content

Discriminator Experiment Class #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1752e59
basic features
coruscating Apr 28, 2021
49c0f4c
test scaffold and minor fixes
coruscating Apr 28, 2021
496fe2e
added legend
coruscating Apr 28, 2021
b4aba54
black formatting
coruscating Apr 28, 2021
66beb81
notebook update
coruscating Apr 28, 2021
6b0b952
Merge branch 'main' into discriminator-exp-class
coruscating May 12, 2021
87754b8
added discriminator nodes
coruscating May 19, 2021
13383e4
discriminator type checking
coruscating May 19, 2021
a09ffcd
added default options
coruscating May 26, 2021
4a29c15
Merge branch 'main' into discriminator-exp-class
coruscating Jun 1, 2021
452b9b1
changed to seeded rng
coruscating Jun 1, 2021
eadb62d
fixes
coruscating Jun 4, 2021
edd4d53
Merge branch 'main' into discriminator-exp-class
coruscating Jun 4, 2021
13993d5
addressing comments
coruscating Jun 9, 2021
5b3004b
minor fixes
coruscating Jun 9, 2021
aa2190d
split into two files
coruscating Jun 23, 2021
caf9cfa
docstring
coruscating Jun 23, 2021
018d49d
Merge branch 'main' into discriminator-exp-class
coruscating Jun 23, 2021
b36136b
moved backend
coruscating Jun 23, 2021
ac8c4a6
moved backend
coruscating Jun 23, 2021
d16c924
Merge branch 'main' into move_backend
coruscating Jun 23, 2021
de038e1
fixed mock_job
coruscating Jun 23, 2021
03491ed
Merge branch 'move_backend' of github.com:coruscating/qiskit-experime…
coruscating Jun 23, 2021
3c31632
black
coruscating Jun 23, 2021
62c616b
pylint
coruscating Jun 23, 2021
6966c69
lint fix
coruscating Jun 23, 2021
fc11f03
Merge remote-tracking branch 'upstream/main' into move_backend
coruscating Jun 23, 2021
4617507
Merge remote-tracking branch 'upstream/main' into move_backend
coruscating Jun 24, 2021
a6786e4
Merge branch 'move_backend' into discriminator-exp-class
coruscating Jun 24, 2021
1a1633c
tutorial
coruscating Jun 24, 2021
98286d3
Merge remote-tracking branch 'upstream/main' into discriminator-exp-c…
coruscating Jun 29, 2021
b16c382
options
coruscating Jun 29, 2021
a42a427
updates
coruscating Jul 7, 2021
10bb035
added warning and test
coruscating Jul 7, 2021
17c533b
Merge remote-tracking branch 'upstream/main'
coruscating Jul 7, 2021
edb5e51
Merge branch 'main' into discriminator-exp-class
coruscating Jul 10, 2021
c69fc6a
Merge remote-tracking branch 'upstream/main' into discriminator-exp-c…
coruscating Jul 12, 2021
ddbe866
removed change from other branch
coruscating Jul 12, 2021
476e4a8
added training
coruscating Jul 13, 2021
7e190fa
added tests
coruscating Jul 14, 2021
33ab02c
fixed parallel experiment
coruscating Jul 19, 2021
86ad69e
Merge branch 'main' into discriminator-exp-class
coruscating Jul 19, 2021
7f7e263
added sklearn
coruscating Jul 19, 2021
97960c1
lint and fixed tests
coruscating Jul 19, 2021
3c4c024
notebook fixes
coruscating Jul 20, 2021
2f3451d
Merge remote-tracking branch 'upstream/main' into discriminator-exp-c…
coruscating Jul 20, 2021
3dba385
changed module name
coruscating Jul 21, 2021
caef2ae
Merge remote-tracking branch 'upstream/main'
coruscating Jul 21, 2021
2c93d2a
Merge remote-tracking branch 'upstream/main'
coruscating Jul 22, 2021
d2a0efc
Merge remote-tracking branch 'upstream/main'
coruscating Jul 22, 2021
428f5ea
Merge remote-tracking branch 'upstream/main' into discriminator-exp-c…
coruscating Jul 23, 2021
345ed50
Merge remote-tracking branch 'upstream/main'
coruscating Jul 23, 2021
1cdc705
moved file path
coruscating Jul 26, 2021
940e3d3
Merge remote-tracking branch 'upstream/main'
coruscating Jul 26, 2021
42f4c57
Merge remote-tracking branch 'upstream/main' into discriminator-exp-c…
coruscating Jul 28, 2021
c40103d
Merge remote-tracking branch 'upstream/main'
coruscating Jul 28, 2021
636019d
docstring
coruscating Aug 1, 2021
eac8dcb
Merge branch 'main' of github.com:coruscating/qiskit-experiments
coruscating Aug 2, 2021
04db69f
Merge remote-tracking branch 'upstream/main' into discriminator-exp-c…
coruscating Aug 3, 2021
2955373
Merge remote-tracking branch 'upstream/main'
coruscating Aug 11, 2021
f8d9b12
Merge branch 'main' into discriminator-exp-class
coruscating Aug 19, 2021
368f287
partial refactoring
coruscating Aug 20, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
372 changes: 372 additions & 0 deletions docs/tutorials/discriminator.ipynb

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions qiskit_experiments/curve_analysis/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,45 @@ def plot_errorbar(
ax.tick_params(labelsize=labelsize)
ax.grid(grid)
return ax


@requires_matplotlib
def plot_contourf(
xdata: np.ndarray,
ydata: np.ndarray,
zdata: np.ndarray,
ax=None,
labelsize: int = 14,
grid: bool = True,
**kwargs,
):
"""Generate a contour plot of xyz data.

Wraps :func:`matplotlib.pyplot.contourf`.

Args:
xdata: xdata used for plotting
ydata: ydata used for plotting
zdata: zdata used for plotting
ax (matplotlib.axes.Axes): Optional, a matplotlib axes to add the plot to.
labelsize: label size for plot
grid: Show grid on plot.
**kwargs: Additional options for :func:`matplotlib.pyplot.contourf`

Returns:
matplotlib.axes.Axes: the matplotlib axes containing the plot.
"""
if ax is None:
figure = pyplot.figure()
ax = figure.subplots()

# Default plot options
plot_opts = kwargs.copy()

# Plot data
ax.contourf(xdata, ydata, zdata, **plot_opts)

# Formatting
ax.tick_params(labelsize=labelsize)
ax.grid(grid)
return ax
81 changes: 81 additions & 0 deletions qiskit_experiments/data_processing/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"""Different data analysis steps."""

from abc import abstractmethod

from qiskit_experiments.library.measurement.twoleveldiscriminator_analysis import (
TwoLevelDiscriminatorAnalysis,
)
from typing import Any, Dict, List, Optional, Tuple, Union
import numpy as np

Expand Down Expand Up @@ -390,6 +394,83 @@ def _process(self, datum: np.array, error: Optional[np.array] = None) -> np.arra
return datum[..., 1] * self.scale, None


class TwoLevelDiscriminate(TrainableDataAction):
"""Base class for discriminator processor. Takes IQ data and calibrated discriminator as input,
outputs counts."""
Comment on lines +398 to +399
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this take an untrained discriminator as input as the base class would suggest?


def __init__(self, validate: bool = True):
"""Initialize a kerneled data to counts data conversion node.

Args:
validate: If set to False the DataAction will not validate its input.
"""
# self._handle = handle
self._analysis = TwoLevelDiscriminatorAnalysis()
self.train(self._handle)
super().__init__(validate)

def _format_data(self, datum: Any, error: Optional[Any] = None) -> Tuple[Any, Any]:
datum = np.asarray(datum, dtype=float)
return datum, None

def train(self, data: List[Any]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow where the training is happening here. A docstring might help.

if not data:
return
if isinstance(data, CompositeExperimentData):
self._analysis = lambda q: data.component_experiment_data(q).analysis_results(0)
elif isinstance(data, ExperimentData):
self._analysis = data.analysis_results
else:
raise DataProcessorError("Invalid training data input type")

@property
def is_trained(self) -> bool:
"""Return True if the discriminator has been trained.

Returns:
True if the discriminator has been trained.
"""
return self._analysis is not None

def _to_dict(self, list_data: List[int]) -> Dict[str, Any]:
"""Converts discriminated data in lists to dictionary of counts.
Args:
list_data: Data in list form at the output of a discriminator.

Returns:
processed data: A dict with the counts.
"""
datum = {}
for shot in zip(*list_data):
bitstring = "".join(map(str, shot))
if bitstring in datum:
datum[bitstring] += 1
else:
datum[bitstring] = 1
return datum

def _process(self, datum: np.array, error: Optional[np.array] = None) -> np.array:
"""Applies discriminator to IQ data to return counts.
Args:
datum: Input IQ data to be discriminated.

Returns:
processed data: Counts dictionary.
"""
if not self.is_trained:
raise DataProcessorError(
"The discriminator must be trained on data before it can be used."
)
list_data = []

for i in range(np.shape(datum)[0]):
if "discriminator" not in self._analysis(i):
raise DataProcessorError("Input not a discriminator.")
discriminator = self._analysis(i)["discriminator"]
list_data.append(discriminator.predict(np.array(datum)[i, :, 0, :]))
return self._to_dict(list_data), None


class Probability(DataAction):
"""Count data post processing. This returns the probabilities of the outcome string
used to initialize an instance of Probability."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,6 @@ def _add_single_data(self, data):
sub_data["counts"] = marginal_counts(data["counts"], composite_clbits[i])
else:
sub_data["counts"] = data["counts"]
if "memory" in data:
sub_data["memory"] = data["memory"]
self._components[index].add_data(sub_data)
2 changes: 1 addition & 1 deletion qiskit_experiments/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class instance to manage parameters and pulse schedules.
~calibration.DragCal
~calibration.Rabi
~calibration.EFRabi
~calibration.FineAmplitude
~calibration.FineAmplitude>
~calibration.FineXAmplitude
~calibration.FineSXAmplitude

Expand Down
39 changes: 39 additions & 0 deletions qiskit_experiments/library/measurement/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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.
"""
=======================================================================================
Discriminator Experiments (:mod:`qiskit_experiments.measurememt.discriminator`)
=======================================================================================

.. currentmodule:: qiskit_experiments.discriminator

The discriminator classifies kerneled data to state data.

Experiments
===========
.. autosummary::
:toctree: ../stubs/
:template: autosummary/experiment.rst

TwoLevelDiscriminator


Analysis
========

.. autosummary::
:toctree: ../stubs/

TwoLevelDiscriminatorAnalysis
"""
from .twoleveldiscriminator_experiment import TwoLevelDiscriminator
from .twoleveldiscriminator_analysis import TwoLevelDiscriminatorAnalysis
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
## 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.
"""
Standard discriminator analysis class.
"""
from typing import List, Optional, Tuple
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis

from qiskit.qobj.utils import MeasLevel

from qiskit_experiments.curve_analysis.visualization import plot_contourf, plot_scatter
from qiskit_experiments.framework import BaseAnalysis, Options, AnalysisResultData


class TwoLevelDiscriminatorAnalysis(BaseAnalysis):
"""A class to analyze discriminator experiments.

LDA and QDA methods are supported.
"""

@classmethod
def _default_options(cls):
return Options(
meas_level=MeasLevel.KERNELED,
meas_return="single",
discriminator_type="LDA",
)

def _run_analysis(
self,
experiment_data,
discriminator_type="LDA",
data_processor: Optional[callable] = None,
plot: bool = True,
**options,
) -> Tuple[AnalysisResultData, List["matplotlib.figure.Figure"]]:
"""Run analysis on discriminator data.
Args:
experiment_data (ExperimentData): The experiment data to analyze.
discriminator_type (str): Type of discriminator to use in analysis. Default is
Linear Discriminant Analysis, which fits a Gaussian density to each class
with the assumption that all classes have the same covariance, generating
a linear decision boundary.
options: kwarg options for analysis function.
Returns:
tuple: A pair ``(analysis_results, 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.
"""
data = experiment_data.data()

qubit = data[0]["metadata"]["qubit"]
_xdata, _ydata = self._process_data(data, qubit)

if discriminator_type == "LDA":
discriminator = LinearDiscriminantAnalysis()
elif discriminator_type == "QDA":
discriminator = QuadraticDiscriminantAnalysis()
else:
raise AttributeError("Unsupported discriminator type")

discriminator.fit(_ydata, _xdata)
score = discriminator.score(_ydata, _xdata)

if plot:
xx, yy = np.meshgrid(
np.arange(
min(_ydata[:, 0]),
max(_ydata[:, 0]),
(max(_ydata[:, 0]) - min(_ydata[:, 0])) / 500,
),
np.arange(
min(_ydata[:, 1]),
max(_ydata[:, 1]),
(max(_ydata[:, 1]) - min(_ydata[:, 1])) / 500,
),
)
ax = plot_scatter(_ydata[:, 0], _ydata[:, 1], c=_xdata)
zz = discriminator.predict(np.c_[xx.ravel(), yy.ravel()])
zz = np.array(zz).astype(float).reshape(xx.shape)
ax = plot_contourf(xx, yy, zz, ax, alpha=0.2)
ax.set_xlabel("I data")
ax.set_ylabel("Q data")
figures = [ax.get_figure()]
else:
figures = None

if discriminator_type == "LDA":
analysis_results = [
AnalysisResultData(
"discriminator",
discriminator,
),
AnalysisResultData(
"coef",
discriminator.coef_,
),
AnalysisResultData(
"intercept",
discriminator.intercept_,
),
AnalysisResultData(
"score",
score,
),
]
elif discriminator_type == "QDA":
analysis_results = [
AnalysisResultData(
"discriminator",
discriminator,
),
AnalysisResultData(
"rotations",
discriminator.rotations_,
),
AnalysisResultData(
"score",
score,
),
]

return analysis_results, figures

def _process_data(self, data, qubit):
"""Returns x and y data for discriminator on specific qubit."""
xdata = np.array([int(data[0]["metadata"]["ylabel"])] * len(data[0]["memory"]))
ydata = np.array(data[0]["memory"])[:, 0, :]
xdata = np.concatenate(
(
xdata,
[int(data[1]["metadata"]["ylabel"])] * len(data[1]["memory"]),
)
)
ydata = np.concatenate((ydata, np.array(data[1]["memory"])[:, 0, :]))
return xdata, ydata
Loading