docs/dgp/04-transitions.md

# Transition model

Multinomial logit per origin state $k$. Self-loops have logit 0 by
construction; allowed destinations $j \ne k$ contribute the linear logit
$\eta_{kj}(i,t)$:

$$
P(S_{i,t}=j \mid S_{i,t-1}=k, u_{i,t}) =
\frac{\exp(\eta_{kj}(i,t))}{\sum_{l \in \mathcal{D}(k)} \exp(\eta_{kl}(i,t))}
$$

where $\mathcal{D}(k)$ is the allowed-destination set for origin $k$ (see the
[state-space note](03-state-space.md)).

## Logit equations

Coefficients live on `TransitionRow` instances inside `TransitionParams`.
Greek-letter names match the spec; the dataclass spells them out (for
example `α[UA→AW]` is `params.ua_to_aw.alpha`).

```
η[UA→AW] = α[UA→AW]
         + β_vol  · vol_t
         + β_mand · mand_t
         + β_rho  · ρ_{i,t}
         + β_r    · r_i
         + β_tau  · (1 - τ_t / T)

η[AW→UA] = α[AW→UA]                           # constant ≈ -3

η[AW→PR] = α[AW→PR]
         + β_mand · mand_t
         + β_rho  · ρ_{i,t}
         + β_pi   · π_t
         + β_r    · r_i
         + β_v    · v_i
         + β_tau  · (1 - τ_t / T)

η[PR→ER] = α[PR→ER]
         + β_mand · mand_t
         + β_tau  · (1 - τ_t / T)
         + β_negc · (-c_t)
         + β_r    · r_i
         + β_v    · v_i

η[PR→SH] = α[PR→SH]
         + β_negr · (-r_i)
         + β_negv · (-v_i)

η[ER→SH] = α[ER→SH]
         + β_tir  · tir_{i,t}
         + β_negc · (-c_t)
```

with $\tau_t = T - t$ so that $1 - \tau_t/T = t/T$ — closer to landfall ⇒
closer to 1.

## Bookkeeping side effects

Two transitions update the auxiliary attributes:

* `PR → ER``evac_path[i] = AWAY`.
* `PR → SH``evac_path[i] = HOME`.

(`ER → SH` does not need an update — `evac_path[i]` is already `AWAY`.)

## Vectorization

`sample_transitions` walks the four non-absorbing origin states in a fixed
order, gathers the indices of households currently in that origin, and runs
one batched softmax + categorical sample per origin. SH households are
untouched because `prev_state == State.SH` masks them out.

The inner per-state batch is the hot path. With the default parameters and
`N=10_000, T=120` the full simulation runs in about 0.3s on a contemporary
laptop — well under the 5-second target — because every household-level
operation is a NumPy reduction or column-stack.