-
Notifications
You must be signed in to change notification settings - Fork 128
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
Changes from all commits
1752e59
49c0f4c
496fe2e
b4aba54
66beb81
6b0b952
87754b8
13383e4
a09ffcd
4a29c15
452b9b1
eadb62d
edd4d53
13993d5
5b3004b
aa2190d
caf9cfa
018d49d
b36136b
ac8c4a6
d16c924
de038e1
03491ed
3c31632
62c616b
6966c69
fc11f03
4617507
a6786e4
1a1633c
98286d3
b16c382
a42a427
10bb035
17c533b
edb5e51
c69fc6a
ddbe866
476e4a8
7e190fa
33ab02c
86ad69e
7f7e263
97960c1
3c4c024
2f3451d
3dba385
caef2ae
2c93d2a
d2a0efc
428f5ea
345ed50
1cdc705
940e3d3
42f4c57
c40103d
636019d
eac8dcb
04db69f
2955373
f8d9b12
368f287
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
||
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.""" | ||
|
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 |
Uh oh!
There was an error while loading. Please reload this page.