Source code for pyecsca.sca.trace.combine

"""Provides functions for combining traces sample-wise."""
from typing import Callable, Tuple

import numpy as np
from public import public

from .trace import Trace, CombinedTrace


[docs] @public def average(*traces: Trace) -> CombinedTrace: """ Average :paramref:`~.average.traces`, sample-wise. :param traces: :return: """ if not traces: raise ValueError("Nothing to combine") if len(traces) == 1: return CombinedTrace(traces[0].samples.copy()) min_samples = min(map(len, traces)) s = np.zeros(min_samples, dtype=np.float64) for t in traces: s = np.add(s, t.samples[:min_samples]) avg = (1 / len(traces)) * s del s return CombinedTrace(avg)
[docs] @public def conditional_average( *traces: Trace, condition: Callable[[Trace], bool] ) -> CombinedTrace: """ Average :paramref:`~.conditional_average.traces` for which the :paramref:`~.conditional_average.condition` is ``True``, sample-wise. :param traces: :param condition: :return: """ return average(*filter(condition, traces))
[docs] @public def standard_deviation(*traces: Trace) -> CombinedTrace: """ Compute the sample standard-deviation of the :paramref:`~.standard_deviation.traces`, sample-wise. :param traces: :return: """ if not traces: raise ValueError("Nothing to combine") if len(traces) == 1: return CombinedTrace(np.zeros(len(traces[0]), dtype=np.float64)) min_samples = min(map(len, traces)) s = np.zeros(min_samples, dtype=np.float64) for t in traces: s = np.add(s, t.samples[:min_samples]) s = (1 / len(traces)) * s ts = np.zeros(min_samples, dtype=np.float64) for t in traces: d = np.subtract(t.samples[:min_samples], s) ts = np.add(ts, np.multiply(d, d, dtype=np.float64)) std = np.sqrt((1 / (len(traces) - 1)) * ts) del s del ts return CombinedTrace(std)
[docs] @public def variance(*traces: Trace) -> CombinedTrace: """ Compute the sample variance of the :paramref:`~.variance.traces`, sample-wise. :param traces: :return: """ if not traces: raise ValueError("Nothing to combine") if len(traces) == 1: return CombinedTrace(np.zeros(len(traces[0]), dtype=np.float64)) min_samples = min(map(len, traces)) s = np.zeros(min_samples, dtype=np.float64) for t in traces: s = np.add(s, t.samples[:min_samples]) s = (1 / len(traces)) * s ts = np.zeros(min_samples, dtype=np.float64) for t in traces: d = np.subtract(t.samples[:min_samples], s) ts = np.add(ts, np.multiply(d, d, dtype=np.float64)) var = (1 / (len(traces) - 1)) * ts del s del ts return CombinedTrace(var)
[docs] @public def average_and_variance(*traces) -> Tuple[CombinedTrace, CombinedTrace]: """ Compute the average and sample variance of the :paramref:`~.average_and_variance.traces`, sample-wise. :param traces: :return: """ if not traces: raise ValueError("Nothing to combine") if len(traces) == 1: return ( CombinedTrace(traces[0].samples.copy()), CombinedTrace(np.zeros(len(traces[0]), dtype=np.float64)), ) min_samples = min(map(len, traces)) s = np.zeros(min_samples, dtype=np.float64) for t in traces: s = np.add(s, t.samples[:min_samples]) s = (1 / len(traces)) * s ts = np.zeros(min_samples, dtype=np.float64) for t in traces: d = np.subtract(t.samples[:min_samples], s) ts = np.add(ts, np.multiply(d, d, dtype=np.float64)) var = (1 / (len(traces) - 1)) * ts del ts return (CombinedTrace(s), CombinedTrace(var))
[docs] @public def add(*traces: Trace) -> CombinedTrace: """ Add :paramref:`~.add.traces`, sample-wise. :param traces: :return: """ if not traces: raise ValueError("Nothing to combine") if len(traces) == 1: return CombinedTrace(traces[0].samples.copy()) min_samples = min(map(len, traces)) s = np.zeros(min_samples, dtype=np.float64) for t in traces: s = np.add(s, t.samples[:min_samples]) return CombinedTrace(s)
[docs] @public def subtract(one: Trace, other: Trace) -> CombinedTrace: """ Subtract :paramref:`~.subtract.other` from :paramref:`~.subtract.one`, sample-wise. :param one: :param other: :return: """ return CombinedTrace(np.subtract(one.samples, other.samples))