Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ jobs:
- name: Install package with test dependencies
run: pip install -e .[tests]
- name: Run pytest
run: pytest -vv
run: pytest -vv ${{ matrix.python-version == '3.10' && '-W ignore::Warning' || '' }}
2 changes: 1 addition & 1 deletion docs/clean.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
rm -rfv build
rm -rfv build jupyter_execute
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def run(self, **kwargs):
add_module_names = False
autodoc_default_options = {
"undoc-members": True,
"special-members": "__call__",
"members": None,
"ignore-module-all": True,
}
Expand Down
8 changes: 5 additions & 3 deletions src/stacie/cost.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class LowFreqCost:
model: SpectrumModel = attrs.field()
"""The model to be fitted to the spectrum."""

def __call__(self, pars: NDArray[float], *, deriv: int = 0) -> float:
def __call__(self, pars: NDArray[float], *, deriv: int = 0) -> list[NDArray[float]]:
"""Evaluate the cost function and its derivatives.

Parameters
Expand Down Expand Up @@ -143,7 +143,7 @@ def expected(self, pars: NDArray[float]) -> NDArray[float]:

def logpdf_gamma(
x: NDArray[float], alpha: NDArray[float], theta: NDArray[float], *, deriv: int = 0
):
) -> list[NDArray[float]]:
"""Compute the logarithm of the probability density function of the Gamma distribution.

Parameters
Expand Down Expand Up @@ -179,7 +179,9 @@ def logpdf_gamma(
return results


def entropy_gamma(alpha: NDArray[float], theta: NDArray[float], *, deriv: int = 0):
def entropy_gamma(
alpha: NDArray[float], theta: NDArray[float], *, deriv: int = 0
) -> list[NDArray[float]]:
"""Compute the entropy of the Gamma distribution.

Parameters
Expand Down
7 changes: 4 additions & 3 deletions src/stacie/cutoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __call__(
self,
spectrum: Spectrum,
model: SpectrumModel,
props: dict[str, NDArray],
props: dict[str, NDArray[float]],
) -> dict[str, float]:
"""Compute the criterion for the given spectrum and model.

Expand All @@ -72,7 +72,8 @@ def __call__(
model
The model to be fitted to the spectrum.
props
The property dictionary returned by the :py:meth:`stacie.cost.LowFreqCost.props` method.
The property dictionary being constructed in the
:py:func:`stacie.estimate.fit_model_spectrum` function.

Returns
-------
Expand Down Expand Up @@ -135,7 +136,7 @@ def __call__(
self,
spectrum: Spectrum,
model: SpectrumModel,
props: dict[str, NDArray],
props: dict[str, NDArray[float]],
) -> dict[str, float]:
"""The disparity between fits to two different parts of the spectrum."""
# Compute weights for the two halves and the model
Expand Down
95 changes: 66 additions & 29 deletions src/stacie/estimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,43 @@ class Result:
props: dict[str] = attrs.field()
"""The properties marginalized over the ensemble of cutoff frequencies.

Properties of this class derive their results from information in this dictionary.
See docstring of :func:`fit_model_spectrum` for details.
The following properties documented in :func:`fit_model_spectrum` are estimated as
weighted averages over the cutoff frequencies:

- ``amplitudes_model``: model amplitudes at the included frequencies
- ``acint``: autocorrelation integral
- ``acint_std``: uncertainty of the autocorrelation integral
- ``acint_var``: variance of the autocorrelation integral
- ``cost_zscore``: z-score of the cost function
- ``criterion_zscore``: z-score of the cutoff criterion
- ``fcut``: cutoff frequency
- ``pars``: model parameters
- ``pars_covar``: covariance matrix of the parameters

Some properties are not averaged over cutoff frequencies:

- ``ncut``: number of points included in the fit, i.e. with weight larger than WEIGHT_EPS
- ``switch_exponent``: exponent used to construct the cutoff
- ``weights``: the weights used to combine the spectrum points in the fit

When using :class:`stacie.model.LorentzModel`, the following properties are added
(derived from the marginalized parameters and their covariance):

- ``pars_lorentz``: Lorentz parameters (converted from the Padé parameters)
- ``pars_lorentz_covar``: covariance matrix of the Lorentz parameters
- ``corrtime_exp``: exponential correlation time
- ``corrtime_exp_var``: variance of the exponential correlation time
- ``corrtime_exp_std``: standard error of the exponential correlation time
- ``exp_simulation_time``: recommended simulation time based on the exponential correlation time
- ``exp_block_time``: recommended block time based on the exponential correlation time

When using :class:`stacie.model.ExpPolyModel`, the following additional properties are added
(derived from the marginalized parameters and their covariance):

- ``log_acint``: the logarithm of the autocorrelation integral
- ``log_acint_var``: variance of the logarithm of the autocorrelation integral
- ``log_acint_std``: standard error of the logarithm of the autocorrelation integral

"""

history: list[dict[str]] = attrs.field()
Expand Down Expand Up @@ -370,56 +405,58 @@ def fit_model_spectrum(
Returns
-------
props
A dictionary containing various intermediate results of the cost function calculation,
computed for the optimized parameters.
A dictionary containing various intermediate results of the cost function calculation.
See Notes for details.

Notes
-----
The returned dictionary contains the following items if the fit succeeds:
The returned dictionary contains at least the following items, irrespective of
whether the fit succeeds or fails:

- ``fcut``: cutoff frequency used
- ``ncut``: number of points included in the fit, i.e. with weight larger than WEIGHT_EPS
- ``switch_exponent``: exponent used to construct the cutoff
- ``neff``: effective number of points used in the fit (sum of weights)
- ``pars_init``: initial guess of the parameters
- ``criterion``: value of the cutoff criterion, or infinity if the fit fails.
- ``msg``: error message, if the fit fails

- ``acint``: estimate of the autocorrelation integral
- ``acint_var``: variance of the estimate of the autocorrelation integral
- ``acint_std``: standard error of the estimate of the autocorrelation integral
If the fit succeeds, the following additional statistical estimates are also set:

- ``acint``: autocorrelation integral
- ``acint_var``: variance of the autocorrelation integral
- ``acint_std``: standard error of the autocorrelation integral
- ``cost_value``: cost function value
- ``cost_grad``: cost Gradient vector (if ``deriv>=1``)
- ``cost_hess``: cost Hessian matrix (if ``deriv==2``)
- ``cost_grad``: cost gradient vector (if ``deriv >= 1``)
- ``cost_hess``: cost Hessian matrix (if ``deriv == 2``)
- ``cost_hess_scales``: Hessian rescaling vector, see ``robust_posinv``.
- ``cost_hess_rescaled_evals``: Rescaled Hessian eigenvalues
- ``cost_hess_rescaled_evecs``: Rescaled hessian eigenvectors
- ``cost_hess_rescaled_evecs``: Rescaled Hessian eigenvectors
- ``cost_expected``: expected value of the cost function
- ``cost_var``: expected variance of the cost function
- ``cost_zscore``: z-score of the cost function
- ``switch_exponent``: exponent used to construct the cutoff
- ``criterion``: value of the criterion whose minimizer determines the frequency cutoff
- ``criterion_expected``: expected value of the criterion
- ``criterion_var``: expected variance of the criterion
- ``criterion_zscore``: z-score of the criterion
- ``criterion_expected``: expected value of the cutoff criterion
- ``criterion_var``: expected variance of the cutoff criterion
- ``criterion_zscore``: z-score of the cutoff criterion
- ``ll``: log likelihood
- ``pars_init``: initial guess of the parameters
- ``pars``: optimized parameters
- ``pars_covar``: covariance matrix of the parameters
- ``pars``: model parameters
- ``pars_covar``: covariance matrix of the model parameters

The ``LorentzModel`` has the following additional properties:
When using :class:`stacie.model.LorentzModel`, the following estimates are added:

- ``pars_lorentz``: optimized Lorentz parameters (converted from the Padé parameters)
- ``pars_lorentz``: Lorentz parameters (converted from the Padé parameters)
- ``pars_lorentz_covar``: covariance matrix of the Lorentz parameters
- ``corrtime_exp``: exponential correlation time, the slowest time scale in the sequences
- ``corrtime_exp_var``: variance of the estimated exponential correlation time
- ``corrtime_exp_std``: standard error of the estimated exponential correlation time
- ``corrtime_exp_var``: variance of the exponential correlation time
- ``corrtime_exp_std``: standard error of the exponential correlation time
- ``exp_simulation_time``: recommended simulation time based on the exponential correlation time
- ``exp_block_time``: recommended block time based on the exponential correlation time

The ``ExpPolyModel`` has the following additional properties:
When using :class:`stacie.model.ExpPolyModel`, the following estimates are added:

- ``log_acint``: the logarithm of the autocorrelation integral
- ``log_acint_var``: variance of the logarithm of the autocorrelation integral
- ``log_acint_std``: standard error of the logarithm of the autocorrelation integral

If the fit fails, the following properties are set:

- ``criterion``: infinity
- ``msg``: error message
"""
# Create a switching function for a smooth cutoff
weights = switch_func(spectrum.freqs, fcut, switch_exponent)
Expand Down
2 changes: 1 addition & 1 deletion src/stacie/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
class SpectrumModel:
"""Abstract base class for spectrum models.

Subclasses must override all methods that raise ``NotImplementedError``.
Subclasses must override all methods that raise :class:`NotImplementedError`.

The first parameter must have a property that is used when constructing an initial guess:
When the first parameter increases, the model should increase everywhere,
Expand Down
5 changes: 3 additions & 2 deletions src/stacie/spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def compute_spectrum(
timestep: float = 1,
include_zero_freq: bool = True,
) -> Spectrum:
r"""Compute a spectrum and store all inputs for ``estimate_acint`` in a ``Spectrum`` instance.
r"""Compute a spectrum and return it as a :class:`Spectrum` object.

The spectrum amplitudes are computed as follows:

Expand Down Expand Up @@ -165,8 +165,9 @@ def compute_spectrum(
Returns
-------
spectrum
A ``Spectrum`` object holding all the inputs needed to estimate
A :class:`Spectrum` object holding all the inputs needed to estimate
the integral of the autocorrelation function.
This can be used as input to :func:`stacie.estimate.estimate_acint`.
"""
# Handle tuple (prefactor, sequences) case
if isinstance(sequences, tuple):
Expand Down
12 changes: 6 additions & 6 deletions src/stacie/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class UnitConfig:
- The ``*_unit`` attributes are assumed to have the value of a "display unit" in
the same internal units.

For example, if your internal time unit is 1 ps and you want to display times
in ns, set ``time_unit=1000.0``, because your display unit (1 ns) is 1000 internal units (1 ps).
For example, if your internal time unit is 1 ps and you want times to be reported in ns,
set ``time_unit = 1000.0``, because your display unit (1 ns) is 1000 internal units (1 ps).

To make these conventions easy to follow (and to avoid unit hell in general),
it is recommended to pick consistent internal units for your system.
Expand All @@ -61,7 +61,7 @@ class UnitConfig:
which is also how this class is used in STACIE.

For example, when all variables are in SI base units and you want to display time in ns,
frequency in THz, and autocorrelation integrals in cm^2/s, then create a ``UnitConfig``
frequency in THz, and autocorrelation integrals in cm^2/s, then create a :class:`UnitConfig`
as follows:

.. code-block:: python
Expand Down Expand Up @@ -205,9 +205,9 @@ class PositiveDefiniteError(ValueError):
def robust_posinv(matrix: NDArray[float]) -> tuple[NDArray, NDArray, NDArray, NDArray]:
"""Compute the eigenvalues, eigenvectors and inverse of a positive definite symmetric matrix.

This function is a robust version of ``numpy.linalg.eigh`` and ``numpy.linalg.inv``
This function is a robust replacement for :func:`numpy.linalg.eigh` and :func:`numpy.linalg.inv`
that can handle large variations in order of magnitude of the diagonal elements.
If the matrix is not positive definite, a ``ValueError`` is raised.
If the matrix is not positive definite, a :class:`ValueError` is raised.

Parameters
----------
Expand Down Expand Up @@ -249,7 +249,7 @@ def robust_posinv(matrix: NDArray[float]) -> tuple[NDArray, NDArray, NDArray, ND
def robust_dot(scales, evals, evecs, other):
"""Compute the dot product of a robustly diagonalized matrix with another matrix.

- The first three arguments are the output of ``robust_posinv``.
- The first three arguments are the output of :func:`robust_posinv`.
- To multiply with the inverse, just use element-wise inversion of ``scales`` and ``evals``.

Parameters
Expand Down