Skip to content
This repository has been archived by the owner on Jun 12, 2023. It is now read-only.

WIP Discriminator #238

Merged
merged 45 commits into from
Sep 19, 2019
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
683bc70
* First draft of API for the abstract discriminator class.
eggerdj Jul 22, 2019
a10ba30
* Added diclaimer to discriminator.py
eggerdj Jul 22, 2019
cb42110
* Added calibration circuits and expected results as input to Abstra…
eggerdj Jul 22, 2019
5ee9b58
* Added a list of data circuits (i.e. circuits for which we do not know
eggerdj Jul 22, 2019
353880e
* created folder measurement and move the discriminator related class…
eggerdj Jul 23, 2019
31bd583
* deleted discriminator.py.
eggerdj Jul 26, 2019
8e9832c
* Added a skeleton for testing
eggerdj Jul 26, 2019
be6a722
* Made linear._calc_data() consistent with data structure.
eggerdj Aug 2, 2019
89c5468
* Cleaned up code style in discriminator.py and unit tests.
eggerdj Aug 5, 2019
8e0426d
* added .ipynb_checkpoints to .gitignore.
eggerdj Aug 5, 2019
a7c37cd
* Added quadratic discriminant analysis.
eggerdj Aug 5, 2019
f714ad7
* renamed linear.py to iq_discrimination.py.
eggerdj Aug 5, 2019
5befe5a
* Added a filter for discrimination.
eggerdj Aug 8, 2019
3bc9774
* Made small style changes to please pylint.
eggerdj Aug 9, 2019
f4da602
* Renamed IQDiscriminationFitter to ScikitIQDiscriminationFitter and:
eggerdj Aug 13, 2019
8216279
* schedules is now an optional argument. Default is to use all results.
eggerdj Aug 15, 2019
80addba
* Adapted the unit tests to the new arguments ordering.
eggerdj Aug 15, 2019
73345c0
* Added the option to rescale the qubit IQ data.
eggerdj Aug 16, 2019
75dbfb0
* Refactored the code so that the scaling parameters are calculated b…
eggerdj Aug 16, 2019
75fe39f
* Added discriminate function to Discriminator.
eggerdj Aug 16, 2019
c090c12
* Switched sclaing in discriminator to the sklearn preprocessing scaler.
eggerdj Aug 16, 2019
cfbf1ca
* Added the filter to the jupyter notebook.
eggerdj Sep 2, 2019
630aa1b
* Fixed style in ignis.utils.py
eggerdj Sep 2, 2019
c36e611
* Fixed style in Utils.py
eggerdj Sep 2, 2019
928fca2
* Small change in docstring.
eggerdj Sep 4, 2019
159fe43
* Added more details in the markdown of the notebook.
eggerdj Sep 5, 2019
5d9aa6e
* Moved utils.py to test folder.
eggerdj Sep 5, 2019
bf6b374
* Added y_data into memory of the discriminated level 2 data. See fil…
eggerdj Sep 5, 2019
dab3908
* Changed method extract_xdata of ScikitIQDiscriminatorFitter to hand…
eggerdj Sep 5, 2019
88202b4
* Small refactor of extract_xdata to improve readability.
eggerdj Sep 5, 2019
af47c16
* Refactored _calc_data in discriminator to handle 'single' and 'avg'…
eggerdj Sep 5, 2019
34e08eb
* Implemented a BaseDiscriminator that is independent of BaseFitter.
eggerdj Sep 9, 2019
4c5dae5
* Refactored the unit tests to the new Discriminator methodology.
eggerdj Sep 9, 2019
507df2b
* Renamed iq_discrimination.py to discriminators.py
eggerdj Sep 9, 2019
0395d50
* Made BaseDiscriminationFitter inherite from ABC.
eggerdj Sep 10, 2019
a2d47e4
* IDDiscrimnationFitter inherits from ABC.
eggerdj Sep 10, 2019
85e6095
* Refactored docstrings, e.g. better description and type hints every…
eggerdj Sep 11, 2019
f0cd0b0
Merge branch 'master' into discriminator
eggerdj Sep 11, 2019
4917952
* Move the IQ discriminators to their own python file.
eggerdj Sep 12, 2019
e3d847a
* Removed ABC from IQDiscriminationFitter.
eggerdj Sep 13, 2019
1fca249
* Improved docstrings.
eggerdj Sep 13, 2019
2842813
Add inits to test
dcmckayibm Sep 19, 2019
5c93932
* Added module docstrings.
eggerdj Sep 19, 2019
81d8f81
* Added class and method docstrings to test methods.
eggerdj Sep 19, 2019
c5a4543
move the discriminator tutorial
dcmckayibm Sep 19, 2019
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ test/python/*.prof
# dotenv
.env

# Jupyter notebooks checkpoints
qiskit/ignis/examples/.ipynb_checkpoints/

Choose a reason for hiding this comment

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

Should the examples be pure python examples rather than a notebook? I think notebooks should be reserved for qiskit-tutorials.

Copy link
Collaborator

Choose a reason for hiding this comment

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

For ignis I put these in the PR for testing then we pull them out into tutorials after (a way to do both concurrently without having to deal with branches in multiple repos)


# virtualenv
.venv
venv/
Expand Down
2 changes: 0 additions & 2 deletions qiskit/ignis/characterization/fitters.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,10 @@ def plot(self, qind, series='0', ax=None, show_plot=True):


def build_counts_dict_from_list(count_list):

"""
Add dictionary counts together

"""

if len(count_list) == 1:
return count_list[0]

Expand Down
542 changes: 542 additions & 0 deletions qiskit/ignis/examples/DiscriminatorTutorial.ipynb

Large diffs are not rendered by default.

233 changes: 233 additions & 0 deletions qiskit/ignis/measurement/discriminator/discriminators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2019.
#
# 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.
from abc import ABC, abstractmethod
from typing import Union, List

from qiskit.exceptions import QiskitError
from qiskit.result import Result
from qiskit.pulse.schedule import Schedule


class BaseDiscriminationFitter(ABC):
"""
IQDiscriminatorFitter takes IQ level 1 data produced by calibration
measurements with a known expected state. It fits a discriminator
that can be used to produce level 2 data, i.e. counts of quantum states.
"""

def __init__(self, cal_results: Union[Result, List[Result]],
qubit_mask: List[int], expected_states: List[str],
standardize: bool = False,
schedules: Union[List[str], List[Schedule]] = None):
"""
Args:
cal_results (Union[Result, List[Result]]): calibration results,
Result or list of Result used to fit the discriminator.
qubit_mask (List[int]): determines which qubit's level 1 data to
use in the discrimination process.
expected_states (List[str]): a list that should have the same
length as schedules. All results in cal_results are used if
schedules is None. expected_states must have the corresponding
length.
standardize (bool): if true the discriminator will standardize the
xdata using the internal method _scale_data.
schedules (Union[List[str], List[Schedule]]): The schedules or a
subset of schedules in cal_results used to train the
discriminator. The user may also pass the name of the schedules
instead of the schedules. If schedules is None, then all the
schedules in cal_results are used.
"""

# Use all results in cal_results if schedules is None
if schedules is None:
schedules = self._get_schedules(cal_results)

self._expected_state = {}
self._add_expected_states(expected_states, schedules)

# Used to rescale the xdata qubit by qubit.
self._description = None
self._standardize = standardize
self._scaler = None
self._qubit_mask = qubit_mask
self._schedules = schedules
self._backend_result_list = []
self._fitted = False

if cal_results is not None:
if isinstance(cal_results, list):
for result in cal_results:
self._backend_result_list.append(result)
else:
self._backend_result_list.append(cal_results)

self._xdata = self.get_xdata(self._backend_result_list, schedules)
self._ydata = self.get_ydata(self._backend_result_list, schedules)

def _add_ydata(self, schedule: Union[Schedule, str]):
"""
Adds the expected state of schedule to self._ydata.
Args:
schedule (Union[Schedule, str]): schedule or schedule name.
Used to get the expected state.
"""
if isinstance(schedule, Schedule):
self._ydata.append(self._expected_state[schedule.name])
else:
self._ydata.append(self._expected_state[schedule])

def add_data(self, result: Result, expected_states: List[str],
refit: bool = True,
schedules: Union[List[Schedule], List[str]] = None):
"""
Args:
result (Result): a Result containing new data to be used to
train the discriminator.
expected_states (List[str]): the expected states of the results in
result.
refit (bool): refit the discriminator if True.
schedules (Union[List[Schedule], List[str]]):
"""
eggerdj marked this conversation as resolved.
Show resolved Hide resolved
if schedules is None:
schedules = self._get_schedules(result)

self._backend_result_list.append(result)
self._add_expected_states(expected_states, schedules)
self._schedules.extend(schedules)
self._xdata = self.get_xdata(self._backend_result_list, schedules)
self._ydata = self.get_ydata(self._backend_result_list, schedules)

if refit:
self.fit()

def _add_expected_states(self, expected_states: List[str],
schedules: Union[List[Schedule], List[str]]):
"""
Adds the given expected states to self._expected_states.
Args:
expected_states (List[str]): list of expected states. Must have the
same length as the number of schedules.
schedules (Union[List[Schedule], List[str]]): schedules or their
names corresponding to the expected states.
"""
if len(expected_states) != len(schedules):
raise QiskitError('Number of input schedules and assigned '
'states must be equal.')

for idx, schedule in enumerate(schedules):
if isinstance(schedule, Schedule):
name = schedule.name
else:
name = schedule
expected_state = expected_states[idx]
self._expected_state[name] = expected_state

@staticmethod
def _get_schedules(results: Union[Result, List[Result]]) -> List[str]:
"""
Extracts the names of all Schedules in a Result or a list of Result.
Args:
results (Union[Result, List[Result]]): the results for which to
extract the names,
Returns (List[str]):
The name of the schedules in results.
"""
schedules = []
if isinstance(results, Result):
for res in results.results:
schedules.append(res.header.name)
else:
for result in results:
schedules.extend([_.header.name for _ in result.results])

return schedules

@property
def expected_states(self):
"""Returns the expected states used to train the discriminator."""
return self._expected_state

@property
def schedules(self):
"""Returns the schedules with which the discriminator was fitted."""
return self._schedules

@property
def fitted(self):
"""True if the discriminator has been fitted to calibration data."""
return self._fitted

@abstractmethod
def _scale_data(self, xdata: List[List[float]],
refit: bool = False) -> List[List[float]]:
"""
Scales xdata, for instance, by transforming it to zero mean and unit
variance data.
Args:
xdata (List[List[float]]): data as a list of features. Each
feature is itself a list.
refit (bool): if true than self._scaler is refit using the given
xdata.
Returns (List[List[float]]): the scaled xdata as a list of features.
"""
pass

@abstractmethod
def get_xdata(self, results: Union[Result, List[Result]],
schedules: Union[List[str], List[Schedule]] = None) \
-> List[List[float]]:
"""
Retrieves feature data (xdata) for the discriminator.
Args:
results (Union[Result, List[Result]]): the get_memory() method is
used to retrieve the level 1 data. If result is a list of
Result then the first Result to return the data of schedule in
schedules is used.
schedules (Union[List[str], List[Schedule]]): Either the names of
the schedules or the schedules themselves.
Returns (List[List[float]]): data as a list of features. Each feature
is a list.
"""
pass

@abstractmethod
def get_ydata(self, results: Union[Result, List[Result]],
schedules: Union[List[str], List[Schedule]] = None) \
-> List[str]:
"""
Args:
results (Union[Result, List[Result]]): results for which to
retrieve the y data (i.e. expected states).
schedules (Union[List[str], List[Schedule]]): the schedules for
which to get the y data.
Returns (List[str]): the y data, i.e. expected states. get_ydata is
designed to produce y data with the same length as the x data.
"""
pass

@abstractmethod
def fit(self):
""" Fits the discriminator using self._xdata and self._ydata. """
pass

@abstractmethod
def discriminate(self, x_data: List[List[float]]) -> List[str]:
"""
Applies the discriminator to x_data
Args:
x_data (List[List[float]]): list of features. Each feature is
itself a list.
Returns (List[str]): the discriminated x_data as a list of labels.
"""
pass
Loading