Quick Start

This is the smallest NFXP path: fit the reference structural estimator, then inspect parameters, standard errors, policy, and value objects. It is a usage example, not a substitute for the pre-estimation checks.

from econirl.datasets import load_rust_bus
from econirl import NFXP

df = load_rust_bus()

model = NFXP(n_states=90, discount=0.9999, utility="linear_cost")
model.fit(df, state="mileage_bin", action="replaced", id="bus_id")

print(model.params_)   # {"theta_c": ..., "RC": ...}
print(model.se_)
print(model.summary())

Fitted attributes follow the same convention as CCP and UFXP:

Attribute

Meaning

params_

Estimated structural reward parameters.

se_

Standard errors for the structural parameters.

coef_

Coefficients as a numpy array.

policy_

Estimated action probabilities by state.

value_

Estimated value function by state.

log_likelihood_

Maximized conditional choice log likelihood.

converged_

Whether the outer optimizer reported convergence.

Custom Reward Features

The utility="linear_cost" default builds the Rust bus features for you. To use your own features, pass a RewardSpec to fit. The feature array has shape (n_states, n_actions, n_features), and reward= takes priority over the default reward features.

import numpy as np
from econirl import NFXP, RewardSpec
from econirl.datasets import load_rust_bus

df = load_rust_bus()
n_states, n_actions = 90, 2
mileage = np.arange(n_states) / n_states

features = np.zeros((n_states, n_actions, 2))
features[:, 0, 0] = -mileage   # operating cost on "keep", rising with mileage
features[:, 1, 1] = -1.0       # flat replacement cost on "replace"

spec = RewardSpec(features, names=["operating_cost", "replacement_cost"])

model = NFXP(n_states=n_states, discount=0.9999)
model.fit(df, state="mileage_bin", action="replaced", id="bus_id", reward=spec)

print(model.params_)   # {"operating_cost": ..., "replacement_cost": ...}

A RewardSpec whose state or action dimension does not match n_states / n_actions raises a clear ValueError at fit time, not a broadcasting error later.

Counterfactual Example

cf = model.counterfactual(RC=4.0)

print(cf.params)
print(cf.policy[50, 1])

This solves the fitted model again with a higher replacement cost and returns the new value function and policy.

Full Estimator API

Use econirl.estimation.nfxp.NFXPEstimator when you need direct control over panel objects, utility objects, transition tensors, optimizer options, or diagnostic metadata:

from econirl.estimation import NFXPEstimator

result = NFXPEstimator(inner_solver="hybrid", inner_tol=1e-10).estimate(
    panel, utility, problem, transitions
)