Skip to content

Commit

Permalink
Data processor with uncertainties package (#481)
Browse files Browse the repository at this point in the history
* fix SVD shape

* fix docs

* fix comment

* fix var name

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* comments

* update except for SVD

* update note tests

* fix processor logic

* documentation

* lint

* add reno

use np.testing

* fix test

* Update qiskit_experiments/data_processing/data_action.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_action.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_action.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_action.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* add description for correlation

* remove typehint for trainable node

* update return doc

* add type check

* detail for full array

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* add comment

* fix document

* black

* Update qiskit_experiments/data_processing/__init__.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* update docs

* update docs

* restrict count validation

* update return type of data processor

* lint

* add svd test

* update average node

* Update qiskit_experiments/data_processing/data_processor.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* Update qiskit_experiments/data_processing/nodes.py

Co-authored-by: Daniel J. Egger <[email protected]>

* add comment

* add handling for level2 memory

* black

* lint

Co-authored-by: Daniel J. Egger <[email protected]>
  • Loading branch information
nkanazawa1989 and eggerdj authored Dec 1, 2021
1 parent c7bc1e8 commit e9297ad
Show file tree
Hide file tree
Showing 9 changed files with 831 additions and 484 deletions.
24 changes: 12 additions & 12 deletions qiskit_experiments/curve_analysis/curve_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import warnings
from abc import ABC
from typing import Any, Dict, List, Tuple, Callable, Union, Optional
from uncertainties import unumpy as unp

import numpy as np
from qiskit.providers import Backend
Expand Down Expand Up @@ -577,30 +578,29 @@ def _is_target_series(datum, **filters):

x_key = self._get_option("x_key")
try:
x_values = [datum["metadata"][x_key] for datum in data]
x_values = np.asarray([datum["metadata"][x_key] for datum in data], dtype=float)
except KeyError as ex:
raise DataProcessorError(
f"X value key {x_key} is not defined in circuit metadata."
) from ex

if isinstance(data_processor, DataProcessor):
y_values, y_sigmas = data_processor(data)
if y_sigmas is None:
y_sigmas = np.full(y_values.shape, np.nan)
y_data = data_processor(data)

y_nominals = unp.nominal_values(y_data)
y_stderrs = unp.std_devs(y_data)
else:
y_values, y_sigmas = zip(*map(data_processor, data))
y_nominals, y_stderrs = zip(*map(data_processor, data))

y_nominals = np.asarray(y_nominals, dtype=float)
y_stderrs = np.asarray(y_stderrs, dtype=float)

# Store metadata
metadata = np.asarray([datum["metadata"] for datum in data], dtype=object)

# Store shots
shots = np.asarray([datum.get("shots", np.nan) for datum in data])

# Format data
x_values = np.asarray(x_values, dtype=float)
y_values = np.asarray(y_values, dtype=float)
y_sigmas = np.asarray(y_sigmas, dtype=float)

# Find series (invalid data is labeled as -1)
data_index = np.full(x_values.size, -1, dtype=int)
for idx, series_def in enumerate(self.__series__):
Expand All @@ -613,8 +613,8 @@ def _is_target_series(datum, **filters):
raw_data = CurveData(
label="raw_data",
x=x_values,
y=y_values,
y_err=y_sigmas,
y=y_nominals,
y_err=y_stderrs,
shots=shots,
data_index=data_index,
metadata=metadata,
Expand Down
30 changes: 26 additions & 4 deletions qiskit_experiments/data_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,32 @@
.. currentmodule:: qiskit_experiments.data_processing
Data processing is the act of taking taking the data returned by the backend and
converting it into a format that can be analyzed. For instance, counts can be
converted to a probability while two-dimensional IQ data may be converted to a
one-dimensional signal.
Data processing is the act of taking the data returned by the backend and
converting it into a format that can be analyzed.
It is implemented as a chain of data processing steps that transform various input data,
e.g. IQ data, into a desired format, e.g. population, which can be analyzed.
These data transformations may consist of multiple steps, such as kerneling and discrimination.
Each step is implemented by a :class:`~qiskit_experiments.data_processing.data_action.DataAction`
also called a `node`.
The data processor implements the :meth:`__call__` method. Once initialized, it
can thus be used as a standard python function:
.. code-block:: python
processor = DataProcessor(input_key="memory", [Node1(), Node2(), ...])
out_data = processor(in_data)
The data input to the processor is a sequence of dictionaries each representing the result
of a single circuit. The output of the processor is a numpy array whose shape and data type
depend on the combination of the nodes in the data processor.
Uncertainties that arise from quantum measurements or finite sampling can be taken into account
in the nodes: a standard error can be generated in a node and can be propagated
through the subsequent nodes in the data processor.
Correlation between computed values is also considered.
Classes
=======
Expand Down
61 changes: 31 additions & 30 deletions qiskit_experiments/data_processing/data_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,66 +13,65 @@
"""Defines the steps that can be used to analyse data."""

from abc import ABCMeta, abstractmethod
from typing import Any, List, Optional, Tuple

import numpy as np


class DataAction(metaclass=ABCMeta):
"""
Abstract action done on measured data to process it. Each subclass of DataAction must
define the way it formats, validates and processes data.
"""Abstract action done on measured data to process it.
Each subclass of DataAction must define the way it formats, validates and processes data.
"""

def __init__(self, validate: bool = True):
"""
"""Create new node.
Args:
validate: If set to False the DataAction will not validate its input.
"""
self._validate = validate

@abstractmethod
def _process(self, datum: Any, error: Optional[Any] = None) -> Tuple[Any, Any]:
"""
Applies the data processing step to the datum.
def _process(self, data: np.ndarray) -> np.ndarray:
"""Applies the data processing step to the data.
Args:
datum: A single item of data which will be processed.
error: An optional error estimation on the datum that can be further propagated.
data: A data array to process. This is a single numpy array containing
all circuit results input to the data processor.
If the elements are ufloat objects consisting of a nominal value and
a standard error, then the error propagation is automatically computed.
Returns:
processed data: The data that has been processed along with the propagated error.
The processed data.
"""

@abstractmethod
def _format_data(self, datum: Any, error: Optional[Any] = None) -> Tuple[Any, Any]:
def _format_data(self, data: np.ndarray) -> np.ndarray:
"""Format and validate the input.
Check that the given data and error has the correct structure. This method may
Check that the given data has the correct structure. This method may
additionally change the data type, e.g. converting a list to a numpy array.
Args:
datum: The data instance to check and format.
error: An optional error estimation on the datum to check and format.
data: A data array to format. This is a single numpy array containing
all circuit results input to the data processor.
Returns:
datum, error: The formatted datum and its optional error.
Raises:
DataProcessorError: If either the data or the error do not have the proper format.
The data that has been validated and formatted.
"""
return data

def __call__(self, data: Any, error: Optional[Any] = None) -> Tuple[Any, Any]:
"""Call the data action of this node on the data and propagate the error.
def __call__(self, data: np.ndarray) -> np.ndarray:
"""Call the data action of this node on the data.
Args:
data: The data to process. The action nodes in the data processor will
raise errors if the data does not have the appropriate format.
error: An optional error estimation on the datum that can be further processed.
data: A numpy array with arbitrary dtype. If the elements are ufloat objects
consisting of a nominal value and a standard error, then the error propagation
is done automatically.
Returns:
processed data: The data processed by self as a tuple of processed datum and
optionally the propagated error estimate.
The processed data.
"""
return self._process(*self._format_data(data, error))
return self._process(self._format_data(data))

def __repr__(self):
"""String representation of the node."""
Expand All @@ -94,11 +93,13 @@ def is_trained(self) -> bool:
"""

@abstractmethod
def train(self, data: List[Any]):
def train(self, data: np.ndarray):
"""Train a DataAction.
Certain data processing nodes, such as a SVD, require data to first train.
Args:
data: A list of datum. Each datum is a point used to train the node.
data: A data array for training. This is a single numpy array containing
all circuit results input to the data processor :meth:`~qiskit_experiments.\
data_processing.data_processor.DataProcessor#train` method.
"""
Loading

0 comments on commit e9297ad

Please sign in to comment.