Module fitk.fisher_utils

Various helper utilities for Fisher objects.

Expand source code
"""
Various helper utilities for Fisher objects.
"""


from __future__ import annotations

from typing import \
    Collection, \
    Optional

import numpy as np



def is_iterable(value):
    """
    Checks whether an object is iterable.
    """
    try:
        _ = iter(value)
        return True
    except TypeError:
        return False



# TODO make sure that the representation doesn't exceed some fixed number of digits
def float_to_latex(
    value : float,
    sigdigits : int = 3,
    ):
    """
    Format a float into a useful LaTeX representation.

    Parameters
    ----------
    sigdigits : int, default = 3
        number of significant digits
    """

    # annoying case of small exponents
    if 1e-4 <= np.abs(value) < 1e-2:
        specifier = 'e'
    else:
        specifier = 'g'

    fmt_string = f'{{:.{sigdigits}{specifier}}}'
    float_str = fmt_string.format(value)

    if 'e' in float_str:
        base, exponent = float_str.split('e')
        if np.isclose(float(base), 1):
            return f'10^{{{int(exponent)}}}'
        # remove any trailing zeros and decimal points
        base = base.rstrip('0.')
        return f'{base} \\times 10^{{{int(exponent)}}}'

    return float_str



class ParameterNotFoundError(ValueError):
    """
    Error raised when a parameter is not found in the Fisher object.
    """
    def __init__(
        self,
        name : str,
        names : Collection[str],
    ):
        self.message = f'Parameter \'{name}\' not found in array {names}'
        super().__init__(self.message)



class MismatchingSizeError(ValueError):
    """
    Error for handling a list of arrays that have mismatching sizes.
    """
    def __init__(
        self,
        *args,
    ):
        sizes = [str(len(arg)) for arg in args]
        self.message = f'Mismatching sizes: {", ".join(sizes)}'
        super().__init__(self.message)



class HTMLWrapper:
    """
    A wrapper class for pretty printing objects in a Jupyter notebook.
    """
    def __init__(self, string):
        self._string = string


    def _repr_html_(self):
        return self._string



def make_html_table(
    values : Collection,
    names : Optional[Collection[str]] = None,
    fmt_values : str = '{}',
    fmt_names : str = '{}',
    title : Optional[str] = None,
):
    """
    Makes a HTML formatted table with names (optional) and values.

    Parameters
    ----------
    values : Collection
        the values in the table

    names : Collection[str], default = None
        the names of the values in the table. Must have the same length as `values`

    fmt_values : str, default = '{}'
        the format string for the values

    fmt_names : str, default = '{}'
        the format string for the names

    title : Optional[str], default = None
        the title of the table
    """
    if title is not None:
        title = f'<tr><th align="left">{title}</th></tr>'
    else:
        title = ''

    temp = (
        '<tr>' + (f'<th>{fmt_names}</th>' * len(names)).format(*names) + '</tr>'
    ) if names is not None else ''

    header = f'<thead>{title}{temp}</thead>'

    body = '<tbody>' + (
        f'<td>{fmt_values}</td>' * len(values)
    ).format(*values) + '</tbody>'

    return f'<table>{header}{body}</table>'



def make_default_names(
    size : int,
    character : str = 'p',
):
    """
    Returns the array with default names `p1, ..., pn`
    """
    if size < 0:
        raise ValueError
    return np.array([f'{character}{_ + 1}' for _ in range(size)], dtype=object)



def is_square(values):
    """
    Checks whether a numpy array-able object is square.
    """
    try:
        values = np.array(values, dtype=float)
    except ValueError:
        return False

    shape = np.shape(values)
    return all(shape[0] == _ for _ in shape)



def is_symmetric(values):
    """
    Checks whether a numpy array-able object is symmetric.
    """
    return np.allclose(
        np.transpose(values),
        values
    )



def is_positive_semidefinite(values):
    """
    Checks whether a numpy array-able object is positive semi-definite.
    """
    return np.all(np.linalg.eigvalsh(values) >= 0)



def get_index_of_other_array(A, B):
    """
    Returns the index (an array) which is such that `B[index] == A`.
    """
    A, B = np.array(A), np.array(B)
    xsorted = np.argsort(B)

    return xsorted[np.searchsorted(B[xsorted], A)]



def jsonify(data):
    """
    Converts arbitrary data into a JSON-compatible format.
    """
    try:
        return data.tolist()
    except AttributeError:
        return data



def reindex_array(values, index):
    """
    Returns the array sorted according to (1D) index `index`.
    """
    for dim in range(np.ndim(values)):
        values = np.swapaxes(
            np.swapaxes(values, 0, dim)[index],
            dim, 0
        )

    return values



def get_default_rcparams():
    """
    Returns a dictionary with default parameters used in FITK.

    Returns
    -------
    `dict`
    """
    return {
        'mathtext.fontset' : 'cm',
        'font.family' : 'serif',
    }

Functions

def float_to_latex(value: float, sigdigits: int = 3)

Format a float into a useful LaTeX representation.

Parameters

sigdigits : int, default = 3
number of significant digits
Expand source code
def float_to_latex(
    value : float,
    sigdigits : int = 3,
    ):
    """
    Format a float into a useful LaTeX representation.

    Parameters
    ----------
    sigdigits : int, default = 3
        number of significant digits
    """

    # annoying case of small exponents
    if 1e-4 <= np.abs(value) < 1e-2:
        specifier = 'e'
    else:
        specifier = 'g'

    fmt_string = f'{{:.{sigdigits}{specifier}}}'
    float_str = fmt_string.format(value)

    if 'e' in float_str:
        base, exponent = float_str.split('e')
        if np.isclose(float(base), 1):
            return f'10^{{{int(exponent)}}}'
        # remove any trailing zeros and decimal points
        base = base.rstrip('0.')
        return f'{base} \\times 10^{{{int(exponent)}}}'

    return float_str
def get_default_rcparams()

Returns a dictionary with default parameters used in FITK.

Returns

dict

Expand source code
def get_default_rcparams():
    """
    Returns a dictionary with default parameters used in FITK.

    Returns
    -------
    `dict`
    """
    return {
        'mathtext.fontset' : 'cm',
        'font.family' : 'serif',
    }
def get_index_of_other_array(A, B)

Returns the index (an array) which is such that B[index] == A.

Expand source code
def get_index_of_other_array(A, B):
    """
    Returns the index (an array) which is such that `B[index] == A`.
    """
    A, B = np.array(A), np.array(B)
    xsorted = np.argsort(B)

    return xsorted[np.searchsorted(B[xsorted], A)]
def is_iterable(value)

Checks whether an object is iterable.

Expand source code
def is_iterable(value):
    """
    Checks whether an object is iterable.
    """
    try:
        _ = iter(value)
        return True
    except TypeError:
        return False
def is_positive_semidefinite(values)

Checks whether a numpy array-able object is positive semi-definite.

Expand source code
def is_positive_semidefinite(values):
    """
    Checks whether a numpy array-able object is positive semi-definite.
    """
    return np.all(np.linalg.eigvalsh(values) >= 0)
def is_square(values)

Checks whether a numpy array-able object is square.

Expand source code
def is_square(values):
    """
    Checks whether a numpy array-able object is square.
    """
    try:
        values = np.array(values, dtype=float)
    except ValueError:
        return False

    shape = np.shape(values)
    return all(shape[0] == _ for _ in shape)
def is_symmetric(values)

Checks whether a numpy array-able object is symmetric.

Expand source code
def is_symmetric(values):
    """
    Checks whether a numpy array-able object is symmetric.
    """
    return np.allclose(
        np.transpose(values),
        values
    )
def jsonify(data)

Converts arbitrary data into a JSON-compatible format.

Expand source code
def jsonify(data):
    """
    Converts arbitrary data into a JSON-compatible format.
    """
    try:
        return data.tolist()
    except AttributeError:
        return data
def make_default_names(size: int, character: str = 'p')

Returns the array with default names p1, …, pn

Expand source code
def make_default_names(
    size : int,
    character : str = 'p',
):
    """
    Returns the array with default names `p1, ..., pn`
    """
    if size < 0:
        raise ValueError
    return np.array([f'{character}{_ + 1}' for _ in range(size)], dtype=object)
def make_html_table(values: Collection, names: Optional[Collection[str]] = None, fmt_values: str = '{}', fmt_names: str = '{}', title: Optional[str] = None)

Makes a HTML formatted table with names (optional) and values.

Parameters

values : Collection
the values in the table
names : Collection[str], default = None
the names of the values in the table. Must have the same length as values
fmt_values : str, default = '{}'
the format string for the values
fmt_names : str, default = '{}'
the format string for the names
title : Optional[str], default = None
the title of the table
Expand source code
def make_html_table(
    values : Collection,
    names : Optional[Collection[str]] = None,
    fmt_values : str = '{}',
    fmt_names : str = '{}',
    title : Optional[str] = None,
):
    """
    Makes a HTML formatted table with names (optional) and values.

    Parameters
    ----------
    values : Collection
        the values in the table

    names : Collection[str], default = None
        the names of the values in the table. Must have the same length as `values`

    fmt_values : str, default = '{}'
        the format string for the values

    fmt_names : str, default = '{}'
        the format string for the names

    title : Optional[str], default = None
        the title of the table
    """
    if title is not None:
        title = f'<tr><th align="left">{title}</th></tr>'
    else:
        title = ''

    temp = (
        '<tr>' + (f'<th>{fmt_names}</th>' * len(names)).format(*names) + '</tr>'
    ) if names is not None else ''

    header = f'<thead>{title}{temp}</thead>'

    body = '<tbody>' + (
        f'<td>{fmt_values}</td>' * len(values)
    ).format(*values) + '</tbody>'

    return f'<table>{header}{body}</table>'
def reindex_array(values, index)

Returns the array sorted according to (1D) index index.

Expand source code
def reindex_array(values, index):
    """
    Returns the array sorted according to (1D) index `index`.
    """
    for dim in range(np.ndim(values)):
        values = np.swapaxes(
            np.swapaxes(values, 0, dim)[index],
            dim, 0
        )

    return values

Classes

class HTMLWrapper (string)

A wrapper class for pretty printing objects in a Jupyter notebook.

Expand source code
class HTMLWrapper:
    """
    A wrapper class for pretty printing objects in a Jupyter notebook.
    """
    def __init__(self, string):
        self._string = string


    def _repr_html_(self):
        return self._string
class MismatchingSizeError (*args)

Error for handling a list of arrays that have mismatching sizes.

Expand source code
class MismatchingSizeError(ValueError):
    """
    Error for handling a list of arrays that have mismatching sizes.
    """
    def __init__(
        self,
        *args,
    ):
        sizes = [str(len(arg)) for arg in args]
        self.message = f'Mismatching sizes: {", ".join(sizes)}'
        super().__init__(self.message)

Ancestors

  • builtins.ValueError
  • builtins.Exception
  • builtins.BaseException
class ParameterNotFoundError (name: str, names: Collection[str])

Error raised when a parameter is not found in the Fisher object.

Expand source code
class ParameterNotFoundError(ValueError):
    """
    Error raised when a parameter is not found in the Fisher object.
    """
    def __init__(
        self,
        name : str,
        names : Collection[str],
    ):
        self.message = f'Parameter \'{name}\' not found in array {names}'
        super().__init__(self.message)

Ancestors

  • builtins.ValueError
  • builtins.Exception
  • builtins.BaseException