Source code for macro_eeg_model.simulation.delay_calculator

# external imports
import numpy as np

# local imports
from .distributions import LagDistributions, DistributionFactory


[docs] class DelayCalculator: """ A class to calculate delay distributions based on distance and various statistical parameters. The delay is modeled using inverse generalized extreme value (GEV) distributions. Attributes ---------- _shape_param : float The shape parameter (xi) for the GEV distribution. _scale_param : float The scale parameter (sigma) for the GEV distribution. _location_param : float The location parameter (mu) for the GEV distribution. _truncation_percentile : float The percentile at which to truncate the resulting delay distribution. _velocity_factor : float A constant velocity factor (= 6) used to calculate the scale coefficient from the distance. Expressed in meters per second per micron diameter. """
[docs] def __init__(self, shape_param, scale_param, location_param, truncation_percentile): """ Initializes the DelayCalculator with specified parameters for the GEV distribution and the truncation percentile. Parameters ---------- shape_param : float The shape parameter (xi) for the GEV distribution. scale_param : float The scale parameter (sigma) for the GEV distribution. location_param : float The location parameter (mu) for the GEV distribution. truncation_percentile : float The percentile at which to truncate the resulting delay distribution. Must be in the range [0, 1). """ self._shape_param = shape_param self._scale_param = scale_param self._location_param = location_param self._truncation_percentile = truncation_percentile self._velocity_factor = 6 # m/s per micron diameter
[docs] def get_delays_distribution(self, tempx, distance): """ Generates a probability density function (PDF) for delays using inverse GEV distributions. The distributions are also scaled with parameter computed by :py:meth:`_calculate_scale_coefficient`. Depending on whether the distance is a single value or a tuple (in case of a relay station), it either sums inverse GEV distributions or uses a single inverse GEV distribution (see :py:class:`src.simulation.distributions.LagDistributions` and :py:class:`src.simulation.distributions.DistributionFactory`). It then truncates the resulting PDF using :py:meth:`_truncate_result`. Parameters ---------- tempx : numpy.ndarray The array of time points (x-axis) over which to calculate the delay distribution. distance : float or tuple The distance(s) over which to calculate the delay. If a tuple, the method will use the sum of two inverse GEV distributions. Returns ------- numpy.ndarray The truncated probability density function (PDF) representing the delay distribution. Raises ------ AssertionError If the distribution cannot be created. """ distribution = None if isinstance(distance, tuple): # we use sum of inverse GEVs scale_coef1 = self._calculate_scale_coefficient(distance[0]) scale_coef2 = self._calculate_scale_coefficient(distance[1]) distribution = DistributionFactory.get_distribution( LagDistributions.INVERSE_GEV_SUM, lmbd1=scale_coef1, lmbd2=scale_coef2, mu=self._location_param, sigma=self._scale_param, xi=self._shape_param ) elif isinstance(distance, float): # we use inverse GEV scale_coef = self._calculate_scale_coefficient(distance) distribution = DistributionFactory.get_distribution( LagDistributions.INVERSE_GEV, lmbd=scale_coef, mu=self._location_param, sigma=self._scale_param, xi=self._shape_param ) assert distribution is not None, "Couldn't create distribution" delays_pdf = distribution.pdf(tempx) return self._truncate_result(tempx, delays_pdf)
[docs] def _calculate_scale_coefficient(self, distance): """ Calculates the scale coefficient for the GEV distribution based on the given distance. Parameters ---------- distance : float The distance for which to calculate the scale coefficient. Returns ------- float The scale coefficient used in the GEV distribution. """ return distance / self._velocity_factor
# return self.velocity_factor / (1 * distance)
[docs] def _truncate_result(self, tempx, result): """ Truncates the PDF by setting values beyond a certain index to zero, based on the cumulative distribution function (CDF) and the truncation percentile. Parameters ---------- tempx : numpy.ndarray The array of time points (x-axis) corresponding to the PDF. result : numpy.ndarray The PDF to be truncated. Returns ------- numpy.ndarray The truncated PDF. Raises ------ AssertionError If the truncation percentile is outside the valid range [0, 1). """ assert 0 <= self._truncation_percentile < 1, "Truncation percentile must be in the range [0, 1)" if self._truncation_percentile == 0: return result # Calculate the cumulative sum of the PDF to simulate the CDF pdf_sum = np.sum(result) # Substitute with a safe value if pdf_sum is zero if pdf_sum == 0: cdf = np.zeros_like(result) else: cdf = np.cumsum(result) / pdf_sum # Find the maximum index where the cumulative sum is less than or equal to 0.04 (4%) valid_indices = np.where(cdf <= (1 - self._truncation_percentile))[0] if valid_indices.size > 0: cutoff_index = np.max(valid_indices) if np.any(cdf <= 0.04) else 0 else: cutoff_index = 0 # Truncate values in the PDF beyond this index truncated_result = np.where(tempx > tempx[cutoff_index], 0, result) return truncated_result