Skip to content

Models

Pre-built Models

Lee-Carter (LC) mortality model.

Linear predictor::

η_xt = α_x + β_x κ_t

Identifiability constraint: Σ_x β_x = 1, mean(κ_t) absorbed into α_x.

Parameters:

Name Type Description Default
link Literal['log', 'logit']

"log" (Poisson, default) or "logit" (Binomial).

'log'

Returns:

Type Description
StMoMo
References

Lee, R.D. & Carter, L.R. (1992). Modeling and Forecasting U.S. Mortality. JASA, 87(419), 659–671.

Examples:

>>> from pystmomo import lc, load_ew_male
>>> model = lc()
>>> data = load_ew_male()
>>> fit = model.fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
Source code in src/pystmomo/models/lc.py
def lc(link: Literal["log", "logit"] = "log") -> StMoMo:
    """Lee-Carter (LC) mortality model.

    Linear predictor::

        η_xt = α_x + β_x κ_t

    Identifiability constraint: Σ_x β_x = 1, mean(κ_t) absorbed into α_x.

    Parameters
    ----------
    link:
        ``"log"`` (Poisson, default) or ``"logit"`` (Binomial).

    Returns
    -------
    StMoMo

    References
    ----------
    Lee, R.D. & Carter, L.R. (1992). Modeling and Forecasting U.S. Mortality.
    *JASA*, 87(419), 659–671.

    Examples
    --------
    >>> from pystmomo import lc, load_ew_male
    >>> model = lc()
    >>> data = load_ew_male()
    >>> fit = model.fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
    """
    return StMoMo(
        link=link,
        static_age_fun=True,
        period_age_fun=(NonParametricAgeFun(),),
        cohort_age_fun=None,
        const_fun=_lc_sum_constraint,
        text_formula="η_xt = α_x + β_x κ_t",
    )

Cairns-Blake-Dowd (CBD) mortality model.

Linear predictor::

η_xt = κ_t^(1) + (x - x̄) κ_t^(2)

No identifiability constraints are needed as the two period indexes are separately identified by the intercept and slope over age.

Parameters:

Name Type Description Default
link Literal['logit', 'log']

"logit" (Binomial, default) or "log" (Poisson).

'logit'

Returns:

Type Description
StMoMo
References

Cairns, A.J.G., Blake, D. & Dowd, K. (2006). A Two-Factor Model for Stochastic Mortality with Parameter Uncertainty. Journal of Risk and Insurance, 73(4), 687–718.

Examples:

>>> from pystmomo import cbd, load_ew_male
>>> data = load_ew_male()
>>> fit = cbd().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
Source code in src/pystmomo/models/cbd.py
def cbd(link: Literal["logit", "log"] = "logit") -> StMoMo:
    """Cairns-Blake-Dowd (CBD) mortality model.

    Linear predictor::

        η_xt = κ_t^(1) + (x - x̄) κ_t^(2)

    No identifiability constraints are needed as the two period indexes are
    separately identified by the intercept and slope over age.

    Parameters
    ----------
    link:
        ``"logit"`` (Binomial, default) or ``"log"`` (Poisson).

    Returns
    -------
    StMoMo

    References
    ----------
    Cairns, A.J.G., Blake, D. & Dowd, K. (2006). A Two-Factor Model for
    Stochastic Mortality with Parameter Uncertainty. *Journal of Risk and
    Insurance*, 73(4), 687–718.

    Examples
    --------
    >>> from pystmomo import cbd, load_ew_male
    >>> data = load_ew_male()
    >>> fit = cbd().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
    """
    return StMoMo(
        link=link,
        static_age_fun=False,
        period_age_fun=(ConstantAgeFun(), LinearAgeFun()),
        cohort_age_fun=None,
        const_fun=None,
        text_formula="η_xt = κ_t^(1) + (x - x̄) κ_t^(2)",
    )

Age-Period-Cohort (APC) mortality model.

Linear predictor::

η_xt = α_x + κ_t + γ_{t-x}

Identifiability constraints (following Villegas et al. 2018):

  • Zero-mean period index: mean(κ_t) = 0, absorbed into α_x.
  • Zero-mean cohort index: mean(γ_c) = 0.
  • Zero linear trend in cohort index: no linear trend in γ_c.

Parameters:

Name Type Description Default
link Literal['log', 'logit']

"log" (Poisson, default) or "logit" (Binomial).

'log'

Returns:

Type Description
StMoMo
References

Currie, I.D. (2006). Smoothing and Forecasting Mortality Rates with P-splines. Talk at the Institute of Actuaries.

Examples:

>>> from pystmomo import apc, load_ew_male
>>> data = load_ew_male()
>>> fit = apc().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
Source code in src/pystmomo/models/apc.py
def apc(link: Literal["log", "logit"] = "log") -> StMoMo:
    """Age-Period-Cohort (APC) mortality model.

    Linear predictor::

        η_xt = α_x + κ_t + γ_{t-x}

    Identifiability constraints (following Villegas et al. 2018):

    * Zero-mean period index: mean(κ_t) = 0, absorbed into α_x.
    * Zero-mean cohort index: mean(γ_c) = 0.
    * Zero linear trend in cohort index: no linear trend in γ_c.

    Parameters
    ----------
    link:
        ``"log"`` (Poisson, default) or ``"logit"`` (Binomial).

    Returns
    -------
    StMoMo

    References
    ----------
    Currie, I.D. (2006). Smoothing and Forecasting Mortality Rates with P-splines.
    Talk at the Institute of Actuaries.

    Examples
    --------
    >>> from pystmomo import apc, load_ew_male
    >>> data = load_ew_male()
    >>> fit = apc().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
    """
    return StMoMo(
        link=link,
        static_age_fun=True,
        period_age_fun=(ConstantAgeFun(),),
        cohort_age_fun=ConstantAgeFun(),
        const_fun=_apc_constraint,
        text_formula="η_xt = α_x + κ_t + γ_{t-x}",
    )

Renshaw-Haberman (RH) mortality model.

An extension of Lee-Carter with a cohort effect::

η_xt = α_x + β_x^(1) κ_t + β_x^(0) γ_{t-x}

where β_x^(0) is a freely-fitted cohort age modulation.

Identifiability constraints: LC sum constraint + zero-mean cohort effect.

Parameters:

Name Type Description Default
link Literal['log', 'logit']

"log" (Poisson, default) or "logit" (Binomial).

'log'

Returns:

Type Description
StMoMo
References

Renshaw, A.E. & Haberman, S. (2006). A Cohort-Based Extension to the Lee-Carter Model for Mortality Reduction Factors. IME, 38(3), 556–570.

Examples:

>>> from pystmomo import rh, load_ew_male
>>> data = load_ew_male()
>>> fit = rh().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
Source code in src/pystmomo/models/rh.py
def rh(link: Literal["log", "logit"] = "log") -> StMoMo:
    """Renshaw-Haberman (RH) mortality model.

    An extension of Lee-Carter with a cohort effect::

        η_xt = α_x + β_x^(1) κ_t + β_x^(0) γ_{t-x}

    where β_x^(0) is a freely-fitted cohort age modulation.

    Identifiability constraints: LC sum constraint + zero-mean cohort effect.

    Parameters
    ----------
    link:
        ``"log"`` (Poisson, default) or ``"logit"`` (Binomial).

    Returns
    -------
    StMoMo

    References
    ----------
    Renshaw, A.E. & Haberman, S. (2006). A Cohort-Based Extension to the
    Lee-Carter Model for Mortality Reduction Factors. *IME*, 38(3), 556–570.

    Examples
    --------
    >>> from pystmomo import rh, load_ew_male
    >>> data = load_ew_male()
    >>> fit = rh().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
    """
    return StMoMo(
        link=link,
        static_age_fun=True,
        period_age_fun=(NonParametricAgeFun(),),
        cohort_age_fun=NonParametricAgeFun(),
        const_fun=_rh_constraint,
        text_formula="η_xt = α_x + β_x κ_t + β_x^(0) γ_{t-x}",
    )

CBD model extended with a cohort effect (M6).

Linear predictor::

η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + γ_{t-x}

Identifiability constraint: zero-mean cohort effect.

Parameters:

Name Type Description Default
link Literal['logit', 'log']

"logit" (Binomial, default) or "log" (Poisson).

'logit'

Returns:

Type Description
StMoMo
References

Cairns, A.J.G., Blake, D., Dowd, K., Coughlan, G.D., Epstein, D., Ong, A. & Balevich, I. (2009). A Quantitative Comparison of Stochastic Mortality Models Using Data From England and Wales and the United States. NAAJ, 13(1), 1–35.

Examples:

>>> from pystmomo import m6, load_ew_male
>>> data = load_ew_male()
>>> fit = m6().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
Source code in src/pystmomo/models/m6.py
def m6(link: Literal["logit", "log"] = "logit") -> StMoMo:
    """CBD model extended with a cohort effect (M6).

    Linear predictor::

        η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + γ_{t-x}

    Identifiability constraint: zero-mean cohort effect.

    Parameters
    ----------
    link:
        ``"logit"`` (Binomial, default) or ``"log"`` (Poisson).

    Returns
    -------
    StMoMo

    References
    ----------
    Cairns, A.J.G., Blake, D., Dowd, K., Coughlan, G.D., Epstein, D., Ong, A.
    & Balevich, I. (2009). A Quantitative Comparison of Stochastic Mortality
    Models Using Data From England and Wales and the United States.
    *NAAJ*, 13(1), 1–35.

    Examples
    --------
    >>> from pystmomo import m6, load_ew_male
    >>> data = load_ew_male()
    >>> fit = m6().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
    """
    return StMoMo(
        link=link,
        static_age_fun=False,
        period_age_fun=(ConstantAgeFun(), LinearAgeFun()),
        cohort_age_fun=ConstantAgeFun(),
        const_fun=_m6_constraint,
        text_formula="η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + γ_{t-x}",
    )

CBD model with quadratic age term and cohort effect (M7).

Linear predictor::

η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + ((x - x̄)² - σ²_x) κ_t^(3) + γ_{t-x}

Three identifiability constraints are applied to the cohort effect: zero mean, zero linear trend, and zero quadratic trend.

Parameters:

Name Type Description Default
link Literal['logit', 'log']

"logit" (Binomial, default) or "log" (Poisson).

'logit'

Returns:

Type Description
StMoMo
References

Cairns et al. (2009) — see :func:m6.

Examples:

>>> from pystmomo import m7, load_ew_male
>>> data = load_ew_male()
>>> fit = m7().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
Source code in src/pystmomo/models/m7.py
def m7(link: Literal["logit", "log"] = "logit") -> StMoMo:
    """CBD model with quadratic age term and cohort effect (M7).

    Linear predictor::

        η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + ((x - x̄)² - σ²_x) κ_t^(3) + γ_{t-x}

    Three identifiability constraints are applied to the cohort effect:
    zero mean, zero linear trend, and zero quadratic trend.

    Parameters
    ----------
    link:
        ``"logit"`` (Binomial, default) or ``"log"`` (Poisson).

    Returns
    -------
    StMoMo

    References
    ----------
    Cairns et al. (2009) — see :func:`m6`.

    Examples
    --------
    >>> from pystmomo import m7, load_ew_male
    >>> data = load_ew_male()
    >>> fit = m7().fit(data.deaths, data.exposures, ages=data.ages, years=data.years)
    """
    return StMoMo(
        link=link,
        static_age_fun=False,
        period_age_fun=(ConstantAgeFun(), LinearAgeFun(), QuadraticAgeFun()),
        cohort_age_fun=ConstantAgeFun(),
        const_fun=_m7_constraint,
        text_formula=(
            "η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + "
            "((x - x̄)² - σ²_x) κ_t^(3) + γ_{t-x}"
        ),
    )

CBD model with age-modulated cohort effect (M8).

Linear predictor::

η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + (x_c - x) γ_{t-x}

where x_c is a fixed reference age (typically ages[-1] + 0.5).

Identifiability constraint: zero-mean cohort effect.

Parameters:

Name Type Description Default
link Literal['logit', 'log']

"logit" (Binomial, default) or "log" (Poisson).

'logit'
xc float | None

Reference age. Defaults to 89.5 (for ages 55–89 data).

None

Returns:

Type Description
StMoMo
References

Cairns et al. (2009) — see :func:m6.

Examples:

>>> from pystmomo import m8, load_ew_male
>>> data = load_ew_male()
>>> fit = m8(xc=float(data.ages[-1]) + 0.5).fit(
...     data.deaths, data.exposures, ages=data.ages, years=data.years
... )
Source code in src/pystmomo/models/m8.py
def m8(
    link: Literal["logit", "log"] = "logit",
    xc: float | None = None,
) -> StMoMo:
    """CBD model with age-modulated cohort effect (M8).

    Linear predictor::

        η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + (x_c - x) γ_{t-x}

    where x_c is a fixed reference age (typically ``ages[-1] + 0.5``).

    Identifiability constraint: zero-mean cohort effect.

    Parameters
    ----------
    link:
        ``"logit"`` (Binomial, default) or ``"log"`` (Poisson).
    xc:
        Reference age.  Defaults to ``89.5`` (for ages 55–89 data).

    Returns
    -------
    StMoMo

    References
    ----------
    Cairns et al. (2009) — see :func:`m6`.

    Examples
    --------
    >>> from pystmomo import m8, load_ew_male
    >>> data = load_ew_male()
    >>> fit = m8(xc=float(data.ages[-1]) + 0.5).fit(
    ...     data.deaths, data.exposures, ages=data.ages, years=data.years
    ... )
    """
    xc_val = 89.5 if xc is None else xc
    return StMoMo(
        link=link,
        static_age_fun=False,
        period_age_fun=(ConstantAgeFun(), LinearAgeFun()),
        cohort_age_fun=CenteredCohortAgeFun(xc=xc_val),
        const_fun=_m8_constraint,
        text_formula=f"η_xt = κ_t^(1) + (x - x̄) κ_t^(2) + ({xc_val} - x) γ_{{t-x}}",
    )

Model Specification

Generalised Age-Period-Cohort (GAPC) stochastic mortality model.

The linear predictor is::

η_xt = α_x + Σ_i β_x^(i) κ_t^(i) + β_x^(0) γ_{t-x}

with death counts modelled as:

  • Poisson (link = "log"): D_xt ~ Poisson(E_xt · μ_xt)
  • Binomial (link = "logit"): D_xt ~ Binomial(E_xt, q_xt)

Parameters:

Name Type Description Default
link Literal['log', 'logit']

Link function: "log" or "logit".

required
static_age_fun bool

Whether to include a static age term α_x.

required
period_age_fun tuple[AgeFunction, ...]

Tuple of age-modulating functions for period terms β_x^(i). Use :class:~pystmomo.core.age_functions.NonParametricAgeFun to request a freely-fitted (non-parametric) β_x.

()
cohort_age_fun AgeFunction | None

Age-modulating function for the cohort term β_x^(0), or None for no cohort effect.

None
const_fun Callable | None

Post-fit identifiability constraint function. Signature: (ax, bx, kt, b0x, gc, ages, years, cohorts) -> (ax, bx, kt, b0x, gc).

None
text_formula str

Human-readable formula string for display.

''

Examples:

Define the Lee-Carter model:

>>> from pystmomo import lc
>>> model = lc()
>>> print(model)
StMoMo(link='log', formula='η_xt = α_x + β_x κ_t')
Source code in src/pystmomo/core/stmomo.py
class StMoMo:
    """Generalised Age-Period-Cohort (GAPC) stochastic mortality model.

    The linear predictor is::

        η_xt = α_x + Σ_i β_x^(i) κ_t^(i) + β_x^(0) γ_{t-x}

    with death counts modelled as:

    * Poisson (link = ``"log"``): D_xt ~ Poisson(E_xt · μ_xt)
    * Binomial (link = ``"logit"``): D_xt ~ Binomial(E_xt, q_xt)

    Parameters
    ----------
    link:
        Link function: ``"log"`` or ``"logit"``.
    static_age_fun:
        Whether to include a static age term α_x.
    period_age_fun:
        Tuple of age-modulating functions for period terms β_x^(i).
        Use :class:`~pystmomo.core.age_functions.NonParametricAgeFun` to
        request a freely-fitted (non-parametric) β_x.
    cohort_age_fun:
        Age-modulating function for the cohort term β_x^(0), or ``None``
        for no cohort effect.
    const_fun:
        Post-fit identifiability constraint function.
        Signature: ``(ax, bx, kt, b0x, gc, ages, years, cohorts) -> (ax, bx, kt, b0x, gc)``.
    text_formula:
        Human-readable formula string for display.

    Examples
    --------
    Define the Lee-Carter model:

    >>> from pystmomo import lc
    >>> model = lc()
    >>> print(model)
    StMoMo(link='log', formula='η_xt = α_x + β_x κ_t')
    """

    def __init__(
        self,
        link: Literal["log", "logit"],
        static_age_fun: bool,
        period_age_fun: tuple[AgeFunction, ...] = (),
        cohort_age_fun: AgeFunction | None = None,
        const_fun: Callable | None = None,
        text_formula: str = "",
    ) -> None:
        self.link = link
        self.static_age_fun = static_age_fun
        self.period_age_fun = tuple(period_age_fun)
        self.cohort_age_fun = cohort_age_fun
        self.const_fun = const_fun
        self.text_formula = text_formula

    @property
    def N(self) -> int:  # noqa: N802
        """Number of period terms (bilinear components)."""
        return len(self.period_age_fun)

    @property
    def has_cohort(self) -> bool:
        """Whether the model includes a cohort effect."""
        return self.cohort_age_fun is not None

    @property
    def is_fully_parametric(self) -> bool:
        """True if all age functions are parametric (enabling GLM path)."""
        return all(
            not isinstance(af, NonParametricAgeFun) for af in self.period_age_fun
        ) and (
            self.cohort_age_fun is None
            or not isinstance(self.cohort_age_fun, NonParametricAgeFun)
        )

    def fit(
        self,
        Dxt: np.ndarray,
        Ext: np.ndarray,
        ages: np.ndarray,
        years: np.ndarray,
        *,
        wxt: np.ndarray | None = None,
        oxt: np.ndarray | None = None,
        max_iter: int = 500,
        tol: float = 1e-6,
        verbose: bool = False,
    ) -> FitStMoMo:
        """Fit the model to mortality data.

        Parameters
        ----------
        Dxt:
            Deaths matrix, shape (n_ages, n_years).
        Ext:
            Exposures matrix (central or initial), shape (n_ages, n_years).
        ages:
            Age labels, length n_ages.
        years:
            Calendar year labels, length n_years.
        wxt:
            Optional binary weight matrix.  Cells with weight 0 are excluded.
            Defaults to the automatic weight matrix that masks zero-exposure
            cells and sparse cohorts.
        oxt:
            Log-exposure offset (log E_xt for Poisson; log E_xt for Binomial
            initial exposures).  If ``None``, computed automatically from Ext.
        max_iter:
            Maximum IRLS iterations (bilinear path only).
        tol:
            Convergence tolerance on relative deviance change (bilinear path).
        verbose:
            Print iteration diagnostics (bilinear path only).

        Returns
        -------
        FitStMoMo
            Fitted model result.
        """
        ages = np.asarray(ages, dtype=int)
        years = np.asarray(years, dtype=int)
        Dxt = np.asarray(Dxt, dtype=float)
        Ext = np.asarray(Ext, dtype=float)

        check_mortality_data(Dxt, Ext, ages, years)
        cohorts = compute_cohorts(ages, years)

        if wxt is None:
            wxt = make_weight_matrix_fast(Dxt, Ext, ages, years)
        else:
            wxt = np.asarray(wxt, dtype=float)

        if oxt is None:
            oxt = np.log(np.where(Ext > 0, Ext, 1.0))

        if self.is_fully_parametric:
            from ..fit.glm_fit import fit_parametric
            return fit_parametric(
                self, Dxt, Ext, ages, years, cohorts, wxt, oxt
            )
        else:
            from ..fit.bilinear_fit import fit_bilinear
            return fit_bilinear(
                self, Dxt, Ext, ages, years, cohorts, wxt, oxt,
                max_iter=max_iter, tol=tol, verbose=verbose,
            )

    def __repr__(self) -> str:
        return (
            f"StMoMo(link={self.link!r}, "
            f"N={self.N}, "
            f"cohort={self.has_cohort}, "
            f"formula={self.text_formula!r})"
        )

    def __str__(self) -> str:
        return f"StMoMo(link={self.link!r}, formula={self.text_formula!r})"

N property

Number of period terms (bilinear components).

has_cohort property

Whether the model includes a cohort effect.

is_fully_parametric property

True if all age functions are parametric (enabling GLM path).

fit(Dxt, Ext, ages, years, *, wxt=None, oxt=None, max_iter=500, tol=1e-06, verbose=False)

Fit the model to mortality data.

Parameters:

Name Type Description Default
Dxt ndarray

Deaths matrix, shape (n_ages, n_years).

required
Ext ndarray

Exposures matrix (central or initial), shape (n_ages, n_years).

required
ages ndarray

Age labels, length n_ages.

required
years ndarray

Calendar year labels, length n_years.

required
wxt ndarray | None

Optional binary weight matrix. Cells with weight 0 are excluded. Defaults to the automatic weight matrix that masks zero-exposure cells and sparse cohorts.

None
oxt ndarray | None

Log-exposure offset (log E_xt for Poisson; log E_xt for Binomial initial exposures). If None, computed automatically from Ext.

None
max_iter int

Maximum IRLS iterations (bilinear path only).

500
tol float

Convergence tolerance on relative deviance change (bilinear path).

1e-06
verbose bool

Print iteration diagnostics (bilinear path only).

False

Returns:

Type Description
FitStMoMo

Fitted model result.

Source code in src/pystmomo/core/stmomo.py
def fit(
    self,
    Dxt: np.ndarray,
    Ext: np.ndarray,
    ages: np.ndarray,
    years: np.ndarray,
    *,
    wxt: np.ndarray | None = None,
    oxt: np.ndarray | None = None,
    max_iter: int = 500,
    tol: float = 1e-6,
    verbose: bool = False,
) -> FitStMoMo:
    """Fit the model to mortality data.

    Parameters
    ----------
    Dxt:
        Deaths matrix, shape (n_ages, n_years).
    Ext:
        Exposures matrix (central or initial), shape (n_ages, n_years).
    ages:
        Age labels, length n_ages.
    years:
        Calendar year labels, length n_years.
    wxt:
        Optional binary weight matrix.  Cells with weight 0 are excluded.
        Defaults to the automatic weight matrix that masks zero-exposure
        cells and sparse cohorts.
    oxt:
        Log-exposure offset (log E_xt for Poisson; log E_xt for Binomial
        initial exposures).  If ``None``, computed automatically from Ext.
    max_iter:
        Maximum IRLS iterations (bilinear path only).
    tol:
        Convergence tolerance on relative deviance change (bilinear path).
    verbose:
        Print iteration diagnostics (bilinear path only).

    Returns
    -------
    FitStMoMo
        Fitted model result.
    """
    ages = np.asarray(ages, dtype=int)
    years = np.asarray(years, dtype=int)
    Dxt = np.asarray(Dxt, dtype=float)
    Ext = np.asarray(Ext, dtype=float)

    check_mortality_data(Dxt, Ext, ages, years)
    cohorts = compute_cohorts(ages, years)

    if wxt is None:
        wxt = make_weight_matrix_fast(Dxt, Ext, ages, years)
    else:
        wxt = np.asarray(wxt, dtype=float)

    if oxt is None:
        oxt = np.log(np.where(Ext > 0, Ext, 1.0))

    if self.is_fully_parametric:
        from ..fit.glm_fit import fit_parametric
        return fit_parametric(
            self, Dxt, Ext, ages, years, cohorts, wxt, oxt
        )
    else:
        from ..fit.bilinear_fit import fit_bilinear
        return fit_bilinear(
            self, Dxt, Ext, ages, years, cohorts, wxt, oxt,
            max_iter=max_iter, tol=tol, verbose=verbose,
        )

Age Functions

Age-modulating functions for GAPC mortality models.

Each function maps the age vector to a weight vector used in the linear predictor: η_xt = α_x + Σ_i f_i(x) κ_t^(i) + f_0(x) γ_{t-x}.

NonParametricAgeFun

Sentinel that signals β_x must be fitted freely (Lee-Carter, RH).

When this appears in period_age_fun, the fitter switches to the bilinear block-coordinate IRLS path.

Source code in src/pystmomo/core/age_functions.py
class NonParametricAgeFun:
    """Sentinel that signals β_x must be fitted freely (Lee-Carter, RH).

    When this appears in ``period_age_fun``, the fitter switches to the
    bilinear block-coordinate IRLS path.
    """

    is_parametric: bool = False

    def __call__(self, ages: np.ndarray) -> np.ndarray:  # noqa: D102
        raise RuntimeError(
            "NonParametricAgeFun cannot be evaluated — "
            "it is a sentinel for the bilinear fitter."
        )

    def __repr__(self) -> str:
        return "NonParametricAgeFun()"

ConstantAgeFun

Age function f(x) = 1 (constant).

Used in CBD: κ_t^(1) · 1.

Source code in src/pystmomo/core/age_functions.py
class ConstantAgeFun:
    """Age function f(x) = 1 (constant).

    Used in CBD: κ_t^(1) · 1.
    """

    is_parametric: bool = True

    def __call__(self, ages: np.ndarray) -> np.ndarray:
        return np.ones(len(ages))

    def __repr__(self) -> str:
        return "ConstantAgeFun()"

LinearAgeFun

Age function f(x) = x - mean(ages).

Used in CBD: κ_t^(2) · (x - x̄).

Source code in src/pystmomo/core/age_functions.py
class LinearAgeFun:
    """Age function f(x) = x - mean(ages).

    Used in CBD: κ_t^(2) · (x - x̄).
    """

    is_parametric: bool = True

    def __call__(self, ages: np.ndarray) -> np.ndarray:
        return ages.astype(float) - ages.mean()

    def __repr__(self) -> str:
        return "LinearAgeFun()"

QuadraticAgeFun

Age function f(x) = (x - x̄)² - σ²_x.

Centred quadratic used in M7.

Source code in src/pystmomo/core/age_functions.py
class QuadraticAgeFun:
    """Age function f(x) = (x - x̄)² - σ²_x.

    Centred quadratic used in M7.
    """

    is_parametric: bool = True

    def __call__(self, ages: np.ndarray) -> np.ndarray:
        dev = ages.astype(float) - ages.mean()
        return dev ** 2 - np.mean(dev ** 2)

    def __repr__(self) -> str:
        return "QuadraticAgeFun()"

CenteredCohortAgeFun

Age function f(x) = x_c - x for a fixed reference age x_c.

Used in M8: γ_{t-x} · (x_c - x).

Parameters:

Name Type Description Default
xc float

Reference age, typically the highest age in the data plus 0.5.

89.5
Source code in src/pystmomo/core/age_functions.py
class CenteredCohortAgeFun:
    """Age function f(x) = x_c - x for a fixed reference age x_c.

    Used in M8: γ_{t-x} · (x_c - x).

    Parameters
    ----------
    xc:
        Reference age, typically the highest age in the data plus 0.5.
    """

    is_parametric: bool = True

    def __init__(self, xc: float = 89.5) -> None:
        self.xc = xc

    def __call__(self, ages: np.ndarray) -> np.ndarray:
        return self.xc - ages.astype(float)

    def __repr__(self) -> str:
        return f"CenteredCohortAgeFun(xc={self.xc})"

CallableAgeFun

Wrap any callable f(ages) -> weights as a parametric age function.

Parameters:

Name Type Description Default
fn Callable[[ndarray], ndarray]

Callable that accepts a 1-D numpy array of ages and returns a 1-D array of weights of the same length.

required
name str

Optional name for display.

'custom'
Source code in src/pystmomo/core/age_functions.py
class CallableAgeFun:
    """Wrap any callable f(ages) -> weights as a parametric age function.

    Parameters
    ----------
    fn:
        Callable that accepts a 1-D numpy array of ages and returns a 1-D
        array of weights of the same length.
    name:
        Optional name for display.
    """

    is_parametric: bool = True

    def __init__(self, fn: Callable[[np.ndarray], np.ndarray], name: str = "custom") -> None:
        self._fn = fn
        self._name = name

    def __call__(self, ages: np.ndarray) -> np.ndarray:
        return np.asarray(self._fn(ages), dtype=float)

    def __repr__(self) -> str:
        return f"CallableAgeFun(name={self._name!r})"