econirl.UFXP

class econirl.UFXP(n_states=90, n_actions=2, discount=0.9999, utility='linear_cost', weights='optimal', num_projections=32, verbose=False)[source]

Bases: NFXP

Sklearn-style UFXP estimator for dynamic discrete choice models.

UFXP (Unnested Fixed Point) estimates utility parameters from the first-order conditions of Bellman’s equation. The value-function dependence of those conditions is eliminated by dual fixed points computed once, before the parameter search, so no dynamic program is ever solved inside an optimizer. With the default optimal weighting (the OUFXP form of Oguz and Bray 2026) and a linear utility, the estimator is a single closed-form weighted moment solve that is as asymptotically efficient as maximum likelihood, with standard errors from the efficient moment variance.

Parameters:
  • n_states (int, default=90) – Number of discrete states (e.g., mileage bins).

  • n_actions (int, default=2) – Number of discrete actions (e.g., keep/replace).

  • discount (float, default=0.9999) – Time discount factor (beta).

  • utility (str or RewardSpec, default="linear_cost") – Utility specification. Pass "linear_cost" for the classic bus engine model, or a RewardSpec for custom features.

  • weights (str, default="optimal") – "optimal" is the efficient OUFXP weighting with standard errors; "random" is the plain random-projection UFXP (no standard errors).

  • num_projections (int, default=32) – Number of random projections when weights="random".

  • verbose (bool, default=False) – Whether to print progress messages during estimation.

Variables:
  • params (dict) – Estimated parameters after fitting.

  • se (dict) – Standard errors for each parameter (weights="optimal" only).

  • coef (numpy.ndarray) – Coefficients as a numpy array (sklearn convention).

  • log_likelihood (float) – Log-likelihood evaluated at the estimate.

  • policy (numpy.ndarray) – Estimated choice probabilities P(a|s) of shape (n_states, n_actions).

  • value (numpy.ndarray) – Estimated value function V(s) of shape (n_states,).

  • transitions (numpy.ndarray) – Transition probability matrix (n_states x n_states).

  • converged (bool) – Whether the closed-form solve had full rank.

References

Bray, R. “Unnesting the Fixed Point in the Estimation of Dynamic

Programs.”

Oguz, E. and Bray, R. (2026). “Training Neural Networks Embedded in

Dynamic Discrete Choice Models.”

__init__(n_states=90, n_actions=2, discount=0.9999, utility='linear_cost', weights='optimal', num_projections=32, verbose=False)[source]

Initialize the NFXP estimator.

Parameters:
  • n_states (int, default=90) – Number of discrete states.

  • n_actions (int, default=2) – Number of discrete actions.

  • discount (float, default=0.9999) – Time discount factor (beta).

  • utility (str or RewardSpec, default="linear_cost") – Utility specification to use. Pass "linear_cost" for the classic Rust bus model, or a RewardSpec for custom features.

  • se_method (str, default="robust") – Method for computing standard errors.

  • verbose (bool, default=False) – Whether to print progress messages.

  • weights (Literal['optimal', 'random'])

  • num_projections (int)

fit(data, state=None, action=None, id=None, transitions=None, reward=None)[source]

Fit the UFXP estimator to data.

Parameters:
  • data (pandas.DataFrame or Panel or TrajectoryPanel) – Panel data with observations. When a DataFrame is passed, state, action, and id column names are required.

  • state (str, optional) – Column names (required for DataFrame input).

  • action (str, optional) – Column names (required for DataFrame input).

  • id (str, optional) – Column names (required for DataFrame input).

  • transitions (numpy.ndarray, optional) – Pre-estimated transition matrix of shape (n_states, n_states). If None, transitions are estimated from the data.

  • reward (RewardSpec, optional) – Reward/utility specification overriding the constructor’s.

Returns:

self

Return type:

UFXP

summary()[source]

Generate a formatted summary of estimation results.

Return type:

str

conf_int(alpha=0.05)

Compute confidence intervals for parameters.

Parameters:

alpha (float, default=0.05) – Significance level. Returns (1 - alpha) confidence intervals.

Returns:

{param_name: (lower, upper)} confidence intervals.

Return type:

dict

Raises:

RuntimeError – If the model has not been fitted yet.

counterfactual(**param_changes)

Compute outcomes under different parameter values.

Performs counterfactual analysis by solving the dynamic programming problem under alternative parameter values. This enables “what if” questions like “what would the policy be if RC was 15 instead of 10?”

Parameters:

**param_changes (float) – Keyword arguments specifying parameter changes. Keys must be valid parameter names (e.g., “theta_c”, “RC”). Values are the counterfactual parameter values.

Returns:

Object containing: - params: Dictionary of all parameter values used - value_function: V(s) under new parameters - policy: P(a|s) under new parameters

Return type:

CounterfactualResult

Raises:
  • RuntimeError – If the model has not been fitted yet.

  • ValueError – If an unknown parameter name is provided.

Examples

>>> model = NFXP(n_states=90)
>>> model.fit(data, state="mileage", action="replaced", id="bus_id")
>>>
>>> # What if replacement cost was higher?
>>> cf = model.counterfactual(RC=15.0)
>>> print(f"Original RC: {model.params_['RC']:.2f}")
>>> print(f"Counterfactual RC: {cf.params['RC']:.2f}")
>>> print(f"P(replace|state=50) changes from "
...       f"{model.predict_proba(np.array([50]))[0,1]:.3f} to "
...       f"{cf.policy[50,1]:.3f}")
>>>
>>> # Multiple parameter changes
>>> cf2 = model.counterfactual(RC=15.0, theta_c=0.05)
predict_proba(states)

Predict choice probabilities for given states.

Parameters:

states (numpy.ndarray) – Array of state indices.

Returns:

Choice probabilities of shape (len(states), n_actions). Each row sums to 1.

Return type:

numpy.ndarray

property reward_matrix_: ndarray | None

Structural reward matrix R(s,a) of shape (n_states, n_actions).

Computes the utility matrix from the fitted parameters and the feature specification. Returns None if the model has not been fitted.

simulate(n_agents, n_periods, seed=None)

Simulate choices under the estimated policy.

Generates synthetic data by simulating agents making decisions according to the fitted model. Each agent starts at state 0 and evolves according to the estimated transition probabilities and choice probabilities.

Parameters:
  • n_agents (int) – Number of agents to simulate.

  • n_periods (int) – Number of time periods per agent.

  • seed (int, optional) – Random seed for reproducibility.

Returns:

DataFrame with columns: - agent_id: Identifier for each agent (0 to n_agents-1) - period: Time period (0 to n_periods-1) - state: State at the beginning of the period - action: Action taken (sampled from estimated policy)

Return type:

pandas.DataFrame

Raises:

RuntimeError – If the model has not been fitted yet.

Examples

>>> model = NFXP(n_states=90)
>>> model.fit(data, state="mileage", action="replaced", id="bus_id")
>>> sim_data = model.simulate(n_agents=100, n_periods=50, seed=42)
>>> print(sim_data.head())