QuantEconPOMDPsExt
API documentation for the POMDPs.jl integration, a package extension activated by loading both trigger packages alongside QuantEcon:
using QuantEcon
using POMDPs, POMDPToolsNote that solve must then be qualified (QuantEcon.solve or POMDPs.solve), as both packages export it.
Example: Aiyagari model
Consider the household problem in the Aiyagari model. The state is (a, z), current assets and productivity, and the action is next-period assets. Define the model through the POMDPs.jl interface:
using QuantEcon, POMDPs, POMDPTools
struct Household{TZ<:MarkovChain,TA<:AbstractVector,TU} <:
POMDPs.MDP{Tuple{Float64,Float64},Float64}
r::Float64
w::Float64
sigma::Float64
beta::Float64
z_chain::TZ
a_vals::TA
u::TU
end
function Household(; r = 0.01,
w = 1.0,
sigma = 1.0,
beta = 0.96,
z_chain = MarkovChain([0.9 0.1; 0.1 0.9], [0.1; 1.0]),
a_min = 1e-10,
a_max = 18.0,
a_size = 200,
a_vals = range(a_min, a_max, length = a_size),
u = sigma == 1 ? x -> log(x) :
x -> (x^(1 - sigma) - 1) / (1 - sigma))
return Household(r, w, sigma, beta, z_chain, a_vals, u)
end
POMDPs.states(am::Household) =
Iterators.product(am.a_vals, am.z_chain.state_values)
POMDPs.actions(am::Household) = am.a_vals
POMDPs.actions(am::Household, (a, z)::Tuple) =
(a_new for a_new in am.a_vals if am.w * z + (1 + am.r) * a - a_new > 0)
POMDPs.reward(am::Household, (a, z)::Tuple, a_new) =
am.u(am.w * z + (1 + am.r) * a - a_new)
POMDPs.transition(am::Household, (a, z)::Tuple, a_new) =
SparseCat([(a_new, z_new) for z_new in am.z_chain.state_values],
am.z_chain.p[findfirst(==(z), am.z_chain.state_values), :])
POMDPs.discount(am::Household) = am.betaCreate an instance of the model:
am = Household(; a_max = 20.0, r = 0.03, w = 0.956)
a_vals = am.a_vals
z_vals = am.z_chain.state_valuesA model defined this way can be used in two ways.
Modeling interface
The POMDPs.jl interface can be used as a modeling interface for DiscreteDP, in place of building the reward and transition arrays by hand: pass the model to the DiscreteDP constructor to obtain a native DiscreteDP, with the model's states and actions attached as state_values and action_values:
am_ddp = DiscreteDP(am) # sparse state-action pair form
results = QuantEcon.solve(am_ddp, PFI)
# Policy function decoded to state and action values
pf = DDPPolicyFunction(results) # (a, z) -> next-period assets
a_star = [pf((a, z)) for a in a_vals, z in z_vals]
# Obtain the controlled Markov chain and stationary mean assets
mc = markov_chain(results)
K = sum(stationary_distributions(mc)[1] .* first.(mc.state_values))DiscreteDP(am; sparse=Val(false)) builds the dense product form instead.
POMDPs.jl solver
DiscreteDPSolver is QuantEcon's solver for the POMDPs.jl interface: pass it to POMDPs.solve to obtain a POMDPs.Policy:
solver = DiscreteDPSolver(PFI) # solver isa POMDPs.Solver
policy = POMDPs.solve(solver, am) # policy isa POMDPs.Policy
# Query the policy and value function
s = (a_vals[117], z_vals[2]) # (a, z) ≈ (11.658, 1.0)
action(policy, s) # next-period assets
value(policy, s)
a_star = [action(policy, (a, z)) for a in a_vals, z in z_vals] # same table
mc = markov_chain(policy) # same chain as aboveSee Interacting with Policies and Implemented Simulators in the POMDPs.jl documentation for tools that work with policy. Like solve, simulate must be qualified as POMDPs.simulate. The native DPSolveResult remains available as policy.res.
Index
QuantEcon.DiscreteDPQuantEconPOMDPsExt.DiscreteDPPolicyQuantEconPOMDPsExt.DiscreteDPSolverQuantEconPOMDPsExt.DiscreteDPSolver
Interface
QuantEcon.DiscreteDP — Method
DiscreteDP(m::POMDPs.MDP; sparse=Val(true))Tabulate an explicit-finite POMDPs.jl model into a DiscreteDP, with state_values = vec(collect(states(m))) and action_values = vec(collect(actions(m))) attached. With sparse=Val(true) (the default) the result is in state-action pair form with a sparse transition matrix; with sparse=Val(false) it is in dense product form, constructed directly (equivalent to to_product_form of the sparse import, without the intermediate). The keyword is value-typed so that the formulation is determined in the type domain.
The importer is index-free: it enumerates states(m) and actions(m) and builds its own value-to-index maps, so stateindex/actionindex and initialstate are not consumed. States and actions must enumerate unique, value-hashable objects; actions(m, s) defines the feasible set at s; rewards are the expected reward(m, s, a, sp) under transition(m, s, a) (3-argument models are covered by the POMDPs.jl fallback); states with isterminal(m, s) are encoded as zero-reward self-loops under every action in actions(m), so their value is exactly zero (actions(m, s) is not consulted at terminal states, so it may be empty there).
Rewards and transition probabilities are normalized to Float64, whatever the model's numerical element types. Transition weights are taken from transition(m, s, a) as-is, with only exactly-zero branches dropped: their validity (nonnegativity, summing to one) is the model's responsibility, as it is the caller's in the native constructors.
Arguments
m::POMDPs.MDP: Explicit-finite POMDPs.jl model to tabulate.;sparse::Val(Val(true)): Value-typed tabulation-formulation flag:Val(true)for the state-action pair form with a sparse transition matrix,Val(false)for the dense product form.
Returns
ddp::DiscreteDP: The tabulated model, withstate_valuesandaction_valuesattached.
QuantEconPOMDPsExt.DiscreteDPPolicy — Type
DiscreteDPPolicyPolicy returned by POMDPs.solve(::DiscreteDPSolver, m). Supports action(policy, s) and value(policy, s), queried by state value; the native result is reachable as policy.res, and markov_chain(policy) returns the controlled Markov chain.
Fields
m::POMDPs.MDP: The source model.pf::DDPPolicyFunction: Policy function,s -> optimal action value.vf::DDPValueFunction: Value function,s -> optimal value.res::DPSolveResult: The native solution.
QuantEconPOMDPsExt.DiscreteDPSolver — Type
DiscreteDPSolver{Algo,S}POMDPs.jl solver based on the DiscreteDP solution methods: POMDPs.solve(solver, m) tabulates m via DiscreteDP(m), solves it, and returns a DiscreteDPPolicy. The type parameters carry the solution algorithm (Algo, one of VFI, PFI, or MPFI) and the tabulation-formulation flag (S, the Bool of the constructor's value-typed sparse keyword); the fields store the solve options. Construct with DiscreteDPSolver(algo; sparse, max_iter, epsilon, k).
Fields
max_iter::Int: Maximum number of iterations, passed tosolve.epsilon::Float64: Value for epsilon-optimality, passed tosolve.k::Int: Number of iterations for partial policy evaluation, passed tosolve.
QuantEconPOMDPsExt.DiscreteDPSolver — Method
DiscreteDPSolver(algo=VFI; sparse=Val(true), max_iter=250,
epsilon=1e-3, k=20)Construct a DiscreteDPSolver with solution algorithm algo.
Arguments
algo::Type{<:DDPAlgorithm}(VFI): Solution algorithm:VFI,PFI, orMPFI.;sparse::Val(Val(true)): Value-typed tabulation-formulation flag, carried as a type parameter of the solver:Val(true)for the state-action pair form with a sparse transition matrix,Val(false)for the dense product form.;max_iter::Integer(250): Maximum number of iterations, passed tosolve.;epsilon::Real(1e-3): Value for epsilon-optimality, passed tosolve.;k::Integer(20): Number of iterations for partial policy evaluation, passed tosolve.
Returns
solver::DiscreteDPSolver: The solver instance.