## kernels.MackFit


A fitted distribution-free chain ladder on one cohort.


Usage

``` python
kernels.MackFit(
    cum,
    obs_mask,
    latest_dev,
    f,
    sigma2,
    s,
    n_obs,
    n_pos,
    origin_periods,
    dev_grain_months,
    sigma_rule,
    units=None,
    loss_field=None
)
```


Arrays are 0-based on both axes: dev index `j` spans `0 .. n_d - 1` and the development step `j -> j + 1` carries `f[j]`, `sigma2[j]`, `s[j]` (its volume denominator), `n_obs[j]` (the origins behind the FACTOR) and `n_pos[j]` (the origins behind the SIGMA), each of length `n_d - 1`. The last two differ only where a pair origin has a zero cumulative; see the module docstring.

`cum` keeps the observed triangle (NaN outside it); [full](kernels.MackFit.md#ibnr.kernels.MackFit.full) is the same matrix with the lower triangle filled by the chain-ladder projection, so `full[:, -1]` is the ultimate and `full[i, j]` for `j > latest_dev[i]` is the `C-hat_{i,j}` that Mack's and Merz-Wuthrich's variance formulas both evaluate at.


## Parameter Attributes


`cum: np.ndarray`  

`obs_mask: np.ndarray`  

`latest_dev: np.ndarray`  

`f: np.ndarray`  

`sigma2: np.ndarray`  

`s: np.ndarray`  

`n_obs: np.ndarray`  

`n_pos: np.ndarray`  

`origin_periods: list[dt.date]`  

`dev_grain_months: int`  

`sigma_rule: str`  

`units: str | None = None`  

`loss_field: str | None = None`  


## Attributes

| Name | Description |
|----|----|
| [full](#full) | (n_w, n_d) observed triangle completed by the chain-ladder projection. |
| [latest](#latest) | (n_w,) each origin's cumulative loss on the latest diagonal. |
| [reserve](#reserve) | (n_w,) IBNR = ultimate - latest. Zero for a fully developed origin. |
| [ultimate](#ultimate) | (n_w,) projected ultimate = the completed triangle's last column. |

------------------------------------------------------------------------


#### full


(n_w, n_d) observed triangle completed by the chain-ladder projection.


`full: np.ndarray`


------------------------------------------------------------------------


#### latest


(n_w,) each origin's cumulative loss on the latest diagonal.


`latest: np.ndarray`


------------------------------------------------------------------------


#### reserve


(n_w,) IBNR = ultimate - latest. Zero for a fully developed origin.


`reserve: np.ndarray`


------------------------------------------------------------------------


#### ultimate


(n_w,) projected ultimate = the completed triangle's last column.


`ultimate: np.ndarray`


## Methods

| Name | Description |
|----|----|
| [from_arrow()](#from_arrow) | Decode a fit written by [to_arrow()](kernels.to_arrow.md#ibnr.kernels.to_arrow), refusing any other kind - |
| [msep_runoff()](#msep_runoff) | Mack's conditional MSEP of the FULL run-off reserve. |
| [require_positive_open_diagonals()](#require_positive_open_diagonals) | Every OPEN origin's latest-diagonal cell must be strictly positive. |
| [summary()](#summary) | One row per origin: latest, ultimate, IBNR and the run-off standard |
| [to_arrow()](#to_arrow) | Arrow IPC bytes. Carries `n_obs` and `n_pos` separately, because |

------------------------------------------------------------------------


#### from_arrow()


Decode a fit written by [to_arrow()](kernels.to_arrow.md#ibnr.kernels.to_arrow), refusing any other kind -


Usage

``` python
from_arrow(data)
```


including the `MackFitPanel` it may well have come out of.


------------------------------------------------------------------------


#### msep_runoff()


Mack's conditional MSEP of the FULL run-off reserve.


Usage

``` python
msep_runoff()
```


Mack (1993) formula (3), per accident year `i`:

    msep_i = C-hat_{i,J}^2 * sum_{j=k_i}^{J-1} (sigma_j^2 / f_j^2)
                             * (1 / C-hat_{i,j} + 1 / S_j)

where `k_i = latest_dev[i]` is the dev index of `i`'s diagonal cell and `J = n_d - 1`. The `1/C-hat` term is process risk, the `1/S_j` term estimation risk; both are returned separately because `cl.MackChainladder` exposes them separately and the tie-out checks each. For the aggregate, Mack's second formula adds the estimation-risk covariance between accident years, which share the same estimated factors:

    msep_total = sum_i msep_i
               + 2 * sum_{i<k} C-hat_{i,J} C-hat_{k,J}
                     * sum_{j=k_i}^{J-1} (sigma_j^2 / f_j^2) / S_j

(the inner sum runs over the OLDER year's dev range, which is the intersection of the two ranges). Process risk carries no cross term: accident years are independent under Mack's assumptions.

Returns `msep` / `process` / `parameter` per origin (variances, not standard errors) plus the scalars `msep_total`, `process_total`, `parameter_total`.


------------------------------------------------------------------------


#### require_positive_open_diagonals()


Every OPEN origin's latest-diagonal cell must be strictly positive.


Usage

``` python
require_positive_open_diagonals()
```


The factor estimator cannot enforce this and never could. Its `c0` cells are exactly the cells with an observed successor, and on a run-off staircase an open origin's diagonal cell has none - so the diagonal is the one cell class no factor-side guard ever sees. Every variance formula downstream then divides by it: [msep_runoff](kernels.MackFit.md#ibnr.kernels.MackFit.msep_runoff)'s process term (`ratio_j / C-hat_{i,j}` starting at `j = latest_dev[i]`) and Merz-Wuthrich's `Phi_i = ratio_k / C_{i,k} + ...`.

numpy divides silently, so without this the failure is invisible: a zero diagonal returns NaN msep for that origin AND a NaN total, a negative one returns a finite NEGATIVE msep whose square root is then NaN, and [simulate_ultimates](kernels.simulate_ultimates.md#ibnr.kernels.simulate_ultimates) returns an exactly degenerate zero column because `var = sigma2 * state` is non-positive and every draw comes back at its mean. Nothing raises; the numbers are just wrong.

Deliberately NOT called on the point path. The chain-ladder ultimate is a product of factors off that cell and needs no positivity at all, and the gallery's skill benchmark (`scripts/compare_gallery.py`) wants the ultimate even for a cohort whose variance is undefined. [fit_mack](kernels.fit_mack.md#ibnr.kernels.fit_mack) therefore still succeeds; only [msep_runoff](kernels.MackFit.md#ibnr.kernels.MackFit.msep_runoff) / [simulate_ultimates](kernels.simulate_ultimates.md#ibnr.kernels.simulate_ultimates) / the CDR refuse.

A CLOSED origin (already at the last dev column) is exempt: it has no remaining step, so nothing divides by its diagonal.


------------------------------------------------------------------------


#### summary()


One row per origin: latest, ultimate, IBNR and the run-off standard


Usage

``` python
summary()
```


error, plus a `total` row. Mirrors `cl.MackChainladder.summary_`.


------------------------------------------------------------------------


#### to_arrow()


Arrow IPC bytes. Carries `n_obs` and `n_pos` separately, because


Usage

``` python
to_arrow(*, compression=None)
```


they are different counts and the positivity contract is the difference.
