RustEmet1987.ox
replicates Table IX, Column 2, Rows 1 and 2 in Rust (Econometrica 1987), the celebrated bus engine replacement model.
Skip down to documentation of items defined in DP.ox
The program produces engine replacement probabilities that match those shown in Figure 3 on page 1025 of the paper. The code segments are not exactly the same as in the Ox file, which includes additional code beyond the basics shown here.
The paper models the decision to replace a bus engine as a function of mileage and unobserved IID shocks. Replacement is a one-time cost that resets the odometer to x=0. The cost of monthly maintenance increases linearly with x.
See RustEmet1987mle to see estimation using data as in the original paper.
Rust
class DDP:Ergodic
(infinite horizon with no terminal or absorbing states)d.N = 2
, the binary decision to replace the engine or not.
x' = (1-d)x w/ prob. θ31 (1-d)x +1 w/ prob. θ32 (1-d)x +2 w/ prob. 1-θ31-θ32
U = -[ dRC + (1-d)θ1mx ] + n + zd (5.1) p. 1015
Parameter | Row 1 | Row 2 |
---|---|---|
δ | 0.9999; | 0 |
RC | 10.07 | 7.6538 |
θ1 | 2.293 | 71.5133 |
θ3 | < 0.3919,0.5953> | < 0.3919,.5953> |
RC
is the bus engine replacement cost. θ1 is the operating cost given
mileage x. The parameter m=0.001
is a scaling factor, which appears to rescale odometer
readings. However, results were replicated when x is the bin number (0 to 89) not the odometer category.
V()
(see Figure 2, p. 1025). The normalization is not
explained, but my calculations seem to explain why it is necessary. The closed form extreme-value value function
iteration involves taking exponents of negative values (operating costs). When δ=0.9999 and n=0
above the result is numerical overflow in exp()
With n>0
set properly the arguments are
kept closer to 0. In the replication the normalization n
is set to the maintenance cost for
x=89/2, the median mileage category.
d
). It defines constants and declares a static
member x>
. Zurcher
then declares the required method Utility()
.
Utility
must have that name and cannot be static
.x
is accessed as CV(x)
. The value of x
is set to be the correct value at θ before Utility()
is called.Utility
must return a column vector of values corresponding to the feasible action vector A[Aind]
. In this case the action variable is simply d, and engine replacement is always feasible.d
is accessed using CV(d)
. This makes the code closer to the notation and more general since it will work even if other actions are added to the model.
Zurcher::Run
(). Like Run()
, a user's program must do these tasks in order:Initialize(new Zurcher())
for the parent DDP class. Since Zurcher
is of type Rust
, the program is calling Rust::Initialize()
, which will set the clock and add the binary action variable to the model, d
.new
operator) and add them to the model.CreateSpaces();
.Renewal
, which captures the process in Rust (1987) but is more general than in the empirical specification. d
. To do this d
is passed to new Renewal()
as an argument, along with how many categories x
takes on and the vector of increment
probabilities. The length of the vector determines how different states are feasible next period. In the empirical work, 3 of 90 odometer readings are feasible next period.
CreateSpaces()
has been called new actions and new state variables cannot be added to the model because the state spaces have be set up already.
Initialize()
before solving the model with that δ.)Method
, such as value function iteration.Solve()
for the solution method to compute V(θ) and P⋆(α|θ).
for()
loop solves the model twice, for Row 1 and Row 2 parameters. Emax->Solve()
. It calls the Output()
routine to capture results to be printed and compared with Figure 3 in the paper.
Output produced: niqlow/examples/output/RustEmet1987.txt
The values of EV do not match those shown in Figure 2 of the paper. This is due to Rust using a different normalization than the value used here. However, the choice probabilities appear to match very well those shown if Figure 3, as the image below suggests.
The paper labels the x axis as thousands of miles since engine replacement. This means that x should take on values 0, 5, ..., 445. It also suggests that the factor on costs scales 300,000 to 300. However, using this scaling I was not able to replicate the choice probabilities. Only when x takes on values 0, ..., 89 and the scaling factor still equals 0.001 was I able to reproduce the choice probabilities.
Panel
object and simulating a sample of data from the process. Panel::Simulate
creates a data set of 10 buses observed over 400 months each. The initial mileage for each bus will be drawn from the ergodic distribution. Simulated data Simulated data Fxed path t n T Aj| s11 s21 x t t' r f |ai| a 0 0 92 50 0 0 0 0 50 0 0 0 0 1 1 0 0 164 44 0 0 0 0 44 0 0 0 0 1 1 0 0 248 53 0 0 0 0 53 0 0 0 0 1 1 0 0 344 69 0 0 0 0 69 0 0 0 0 1 1
[5000x,5000(x+1)]
.
Const
is a placeholder for exogenous state variables.
Public fields | ||
![]() |
static const | Number of bins by table. |
![]() |
static | column of Table |
![]() |
static const | Discount factor by row. |
![]() |
static | Discretization method: Month or Mileage |
![]() |
static const | scaling on cost |
![]() |
static | added to U() to avoid underflow |
![]() |
static | # of bins, 90 or 175 |
![]() |
static const | 2x3x2x4 array of reported parameter estimates. |
![]() |
static | parameter vector ψ. |
![]() |
static | value of RC |
![]() |
static | row of table/column. |
![]() |
static | value of θ1 |
![]() |
static | mileage state, x |
Public methods | ||
![]() |
static | |
![]() |
static | Setup and solve the model for each discount factor and generate the figure. |
![]() |
static | Set the target of the replication. |
![]() |
Computes the one period linear cost as 2x1 vector U=dRC+(1−d)θ1x+n. | |
Enumerations | ||
![]() |
tags for table/column/row choices. | |
![]() |
tags for estimated parameters. |
This allows the basic code to be re-used for different specifications.
targ | is array of integer values for the Table, COlumn and Row from the original paper. |