# Plan: Add Survivor Option to Pension

This document outlines the formulation, API, UI, and documentation changes needed to add joint-and-survivor (J&S) pension options to Owl.

---

## 1. Formulation (Variable Names and Math)

### 1.1 Current State

- **π_in** (pi_in): Annual pension benefit for individual *i* in year *n* (paper notation)
- **π̄_in** (piBar_in): Inflation-adjusted pension; enters cash-flow as ordinary income
- Pension currently **ceases at death** of the individual who holds it (`end_idx = horizons[i]`)
- Paper explicitly states: *"Pension survivorship rules (e.g. joint-and-survivor annuity options) are not modeled"*

### 1.2 Survivor Option Semantics

When a pension holder elects a **joint-and-survivor** option:
- **During life**: They receive a *reduced* monthly amount (actuarially reduced because the plan expects to pay longer)
- **After death**: The surviving spouse receives a fraction (typically 50%, 75%, or 100%) of that reduced amount for life

**Design choice**: The user enters the **actual** monthly amount they receive (already reduced if they elected J&S). Owl does not perform actuarial reduction—the amount is plan-specific. We only add the **survivor continuation** logic.

### 1.3 New Parameter: Survivor Fraction

| Symbol | Name | Type | Description |
|--------|------|------|-------------|
| **ρ_i** | `pension_survivor_fraction` | float ∈ [0, 1] | Fraction of individual *i*'s pension that continues to the surviving spouse after *i*'s death |

- **ρ_i = 0**: Single-life annuity (current behavior)—pension ceases at death
- **ρ_i = 0.5**: 50% J&S—survivor gets 50% of primary's amount
- **ρ_i = 0.75**: 75% J&S
- **ρ_i = 1.0**: 100% J&S—survivor gets full amount

**Applicability**: Only meaningful for **married couples** (N_i = 2) and when one spouse dies before the other (n_d < N_n). For singles, ρ_i is ignored.

### 1.4 Mathematical Formulation

**Indices** (unchanged):
- *i* ∈ {0, …, N_i−1}: individuals
- *n* ∈ {0, …, N_n−1}: plan years
- *i_d*: first-to-die individual; *i_s*: survivor; *n_d*: year index of first death

**Primary pension** (unchanged):
- π_in = annual pension for *i* in year *n*, from commencement through `horizons[i]`−1
- π̄_in = γ_n · π_in if indexed, else π_in

**Survivor pension** (new):
- When *i_d* dies at end of year n_d−1, from year n_d onward the survivor *i_s* receives:
  - **ρ_{i_d} × π̄_{i_d, n_d−1}** (or equivalently ρ_{i_d} × π̄_{i_d, n} for n ≥ n_d, since the primary's amount is constant in real terms when indexed, or nominal when not)

**Effective pension series**:
- For *i = i_d*: π̄_{i_d,n} for n < n_d; 0 for n ≥ n_d (unchanged)
- For *i = i_s*: π̄_{i_s,n} for n < horizons[i_s], **plus** ρ_{i_d} × π̄_{i_d,n_d−1} for n ∈ [n_d, horizons[i_s]) if *i_d* had a pension with ρ_{i_d} > 0

**Implementation note**: The survivor receives the same *real* amount as the primary's last year (when indexed), or the same nominal amount (when not indexed). So:
- `survivor_annual = ρ[i_d] * piBar_in[i_d, n_d - 1]` for n ≥ n_d
- This is added to `piBar_in[i_s, n]` for n in [n_d, horizons[i_s])

### 1.5 Variable Naming Summary

| Layer | Symbol/Name | Description |
|-------|-------------|-------------|
| Paper | ρ_i | Survivor fraction for pension of individual *i* |
| Plan (Python) | `pensionSurvivorFraction` | np.array of shape (N_i), values in [0, 1] |
| Config (TOML) | `pension_survivor_fraction` | List of floats, one per individual |
| Schema | `pension_survivor_fraction` | `Optional[List[float]]`, default `[0.0] * N_i` |
| UI keys | `pSurv{i}` or `pensionSurvivorPct{i}` | Percent (0, 50, 75, 100) for display; stored as fraction |

**Recommendation**: Use **fraction** (0–1) internally and in config for consistency with `beneficiary_fractions` and `phi_j`. Display as percent in UI.

---

## 2. API Changes

### 2.1 Plan Class (`src/owlplanner/plan.py`)

**New attribute**:
```python
self.pensionSurvivorFraction = np.zeros(self.N_i)  # default: no survivor
```

**`setPension` signature change**:
```python
def setPension(self, amounts, ages, indexed=None, survivor_fraction=None):
    """
    Set pension for each individual.

    Parameters
    ----------
    amounts : array-like
        Monthly pension amounts per individual ($)
    ages : array-like
        Commencement ages per individual
    indexed : list of bool, optional
        Whether each pension is inflation-indexed
    survivor_fraction : list of float, optional
        Fraction of pension continuing to surviving spouse (0–1).
        Default: [0] * N_i (single-life, no survivor).
    """
```

**Logic**:
1. Compute `pi_in` via `pension.compute_pension_benefits(...)` (unchanged)
2. After building `piBar_in` (inflation adjustment), apply survivor logic for couples:
   - If N_i == 2 and n_d < N_n and `pensionSurvivorFraction[i_d] > 0` and `piBar_in[i_d, n_d-1] > 0`:
     - For n in [n_d, horizons[i_s]): add `pensionSurvivorFraction[i_d] * piBar_in[i_d, n_d-1]` to `piBar_in[i_s, n]`

**Placement**: The survivor adjustment happens in the same place where `piBar_in` is built (around line 1340 in plan.py), after inflation scaling and before the LP uses it.

### 2.2 Pension Module (`src/owlplanner/pension.py`)

**No change** to `compute_pension_benefits`. Survivor logic lives in `plan.py` because it depends on `n_d`, `i_d`, `i_s`, and `piBar_in` (inflation-adjusted). The pension module stays focused on timing (commencement, first-year proration).

### 2.3 Config Bridge (`src/owlplanner/config/plan_bridge.py`)

- **config_to_plan**: Read `pension_survivor_fraction` from `fixed_income`, pass to `setPension(..., survivor_fraction=...)`
- **plan_to_config**: Write `pension_survivor_fraction` from `plan.pensionSurvivorFraction.tolist()`

### 2.4 Schema (`src/owlplanner/config/schema.py`)

```python
# In FixedIncome
pension_survivor_fraction: Optional[List[float]] = Field(
    default=None,
    description="Fraction of pension continuing to surviving spouse (0–1). 0 = single-life."
)
```

---

## 3. UI Changes

### 3.1 Fixed Income Page (`ui/Fixed_Income.py`)

**New control** (married only, per individual with pension):
- Dropdown or selectbox: "Survivor option: None / 50% / 75% / 100%"
- Stored as fraction: 0, 0.5, 0.75, 1.0
- Keys: `pSurv0`, `pSurv1` (or `pensionSurvivorPct0`, etc.)

**Layout**: Add below "Inflation adjusted" for each spouse's pension section. Only show when `status == "married"`.

**Help text**: "If you elected a joint-and-survivor option, the surviving spouse receives this percentage of your pension after your death. Select 'None' for single-life annuity."

### 3.2 UI Bridge (`src/owlplanner/config/ui_bridge.py`)

- **config_to_ui**: Map `pension_survivor_fraction[i]` → `pSurv{i}` (as percent 0/50/75/100 for display)
- **ui_to_config**: Map `pSurv{i}` → `pension_survivor_fraction` (convert percent to fraction)

### 3.3 Owlbridge (`ui/owlbridge.py`)

- When pushing plan → UI: `dic[f"pSurv{i}"] = int(plan.pensionSurvivorFraction[i] * 100)` (or enum)
- When pulling UI → plan: use `pension_survivor_fraction` from config

---

## 4. Documentation Changes

### 4.1 Paper (`papers/owl.tex`)

- **Section 2.6 (Pension Income)**:
  - Add subsection on survivor option
  - Define ρ_i
  - Update "Limitations and assumptions" to remove "Pension survivorship rules... are not modeled"
  - Add: "When ρ_i > 0 for a married couple, the surviving spouse receives ρ_i × π̄_{i_d, n_d−1} from year n_d until the survivor's horizon."

- **Parameters section** (around line 1199):
  - Add `ρ_i` (or `pension_survivor_fraction`) to the parameter list

- **Cash-flow equations**: No change—π̄_in already appears; the survivor portion is added into π̄_{i_s,n} before the LP.

### 4.2 PARAMETERS.md

Add to `fixed_income` table:

| Parameter | Type | Description |
|-----------|------|-------------|
| `pension_survivor_fraction` | list of `N_i` floats | Fraction of pension (0–1) continuing to surviving spouse. 0 = single-life. Typical: 0, 0.5, 0.75, 1.0 |

### 4.3 User Guide / Tutorials

- Mention survivor option in pension description
- Example: `plan.setPension([0, 10.5], [65, 65], survivor_fraction=[0, 0.5])` — Jill's pension has 50% J&S

### 4.4 In-App Documentation (`ui/Documentation.py`)

- Add tooltip/help for survivor option in Fixed Income section
- Update any pension-related descriptions

---

## 5. Implementation Order

1. **Formulation** ✓ (this document)
2. **Pension logic** in `plan.py`: add `pensionSurvivorFraction`, survivor adjustment to `piBar_in`
3. **Schema + plan_bridge**: config round-trip
4. **setPension** API: add `survivor_fraction` parameter
5. **UI**: Fixed_Income page, ui_bridge, owlbridge
6. **Documentation**: paper, PARAMETERS.md, user guide, Documentation.py
7. **Tests**: unit tests for survivor logic, config round-trip, regression

---

## 6. Edge Cases

- **Single individual**: ρ_i ignored; no behavioral change
- **Both die same year** (horizons equal): n_d = N_n; no survivor period
- **Primary has no pension** (amount = 0): survivor gets 0 regardless of ρ
- **Survivor also has own pension**: Additive; both pensions appear in π̄_{i_s,n}
- **Backward compatibility**: Default `survivor_fraction=[0]*N_i` preserves current behavior

---

## 7. Open Questions

1. **Percent vs fraction in UI**: Recommend fraction internally, percent in labels (0, 50, 75, 100).
2. **Other survivor percentages**: Some plans offer 25%, 66⅔%, etc. Could allow free-form float in config; UI could offer common options + "Other".
3. **Different beneficiary**: Some plans allow non-spouse beneficiary. Out of scope for v1; assume spouse.
