polynomial_coef.py 3.75 KiB
from typing import Dict, List

from extreme_fit.function.param_function.abstract_coef import AbstractCoef
from extreme_fit.function.param_function.linear_coef import LinearCoef
from spatio_temporal_dataset.coordinates.abstract_coordinates import AbstractCoordinates


class PolynomialCoef(AbstractCoef):
    """
    Object that maps each degree to its corresponding coefficient.
        degree = 1 correspond to the coefficient of the first order polynomial
        degree = 2 correspond to the the coefficient of the first order polynomial
        degree = 3 correspond to the the coefficient of the first order polynomial
    """

    def __init__(self, param_name: str, default_value: float = 1.0, degree_to_coef=None):
        super().__init__(param_name, default_value, idx_to_coef=degree_to_coef)
        self.max_degree = max(self.idx_to_coef.keys()) if self.idx_to_coef is not None else None

    def compute_default_value(self, idx):
        return self.default_value / idx

    @property
    def nb_params(self):
        return self.max_degree + 1


class PolynomialAllCoef(LinearCoef):

    def __init__(self, param_name, dim_to_polynomial_coef: Dict[int, PolynomialCoef], intercept=None):
        super().__init__(param_name, 1.0, None)
        self.dim_to_polynomial_coef = dim_to_polynomial_coef
        self._intercept = intercept

    @property
    def nb_params(self):
        if self.dim_to_polynomial_coef is None:
            return 1
        else:
            nb_redundant_intercept = len(self.dim_to_polynomial_coef) - 1
            return sum([c.nb_params for c in self.dim_to_polynomial_coef.values()]) - nb_redundant_intercept

    @property
    def intercept(self) -> float:
        if self._intercept is not None:
            return self._intercept
        else:
            return super().intercept

    @classmethod
    def from_coef_dict(cls, coef_dict: Dict[str, float], param_name: str, dims: List[int],
                       coordinates: AbstractCoordinates):
        degree0 = coef_dict[cls.coef_template_str(param_name, coefficient_name=cls.INTERCEPT_NAME).format(1)]
        list_dim_and_max_degree = dims
        j = 2
        if len(list_dim_and_max_degree) == 0:
            dim_to_polynomial_coef = None
            intercept = degree0
        else:
            intercept = None
            dim_to_polynomial_coef = {}
            for dim, max_degree in list_dim_and_max_degree:
                coefficient_name = coordinates.coordinates_names[dim]
                if coefficient_name == AbstractCoordinates.COORDINATE_T:
                    j = 1
                degree_to_coef = {0: degree0}
                for degree in range(1, max_degree + 1):
                    coef_value = coef_dict[cls.coef_template_str(param_name, coefficient_name).format(j)]
                    degree_to_coef[degree] = coef_value
                    j += 1
                dim_to_polynomial_coef[dim] = PolynomialCoef(param_name=param_name, degree_to_coef=degree_to_coef)
        return cls(param_name=param_name, dim_to_polynomial_coef=dim_to_polynomial_coef, intercept=intercept)

    def form_dict(self, coordinates_names: List[str], dims) -> Dict[str, str]:
        if len(coordinates_names) >= 2:
            raise NotImplementedError(
                'Check how do we sum two polynomails without having two times an intercept parameter')
        formula_list = []
        if len(coordinates_names) == 0:
            formula_str = '1'
        else:
            for dim, name in zip(dims, coordinates_names):
                polynomial_coef = self.dim_to_polynomial_coef[dim]
                formula_list.append('poly({}, {}, raw = TRUE)'.format(name, polynomial_coef.max_degree))
            formula_str = ' '.join(formula_list)
        return {self.param_name + '.form': self.param_name + ' ~ ' + formula_str}