Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Beta-Delta (Quasi-Hyperbolic) Discounting

This notebook shows how to implement beta-delta discounting in PyLCM. The naive agent needs a phase-variant Koopmans aggregator, a custom WW declared via Phased(solve=..., simulate=...); the sophisticated and partially naive agents choose with one aggregator and store the value of another, declared via Decoupled(objective=..., value=...). It covers:

  1. The beta-delta discount function

  2. Why naive agents need different WW for solve vs. simulate

  3. A 3-period consumption-savings model with analytical solutions

  4. Verifying numerical results against closed-form solutions

  5. Sophisticated and partially naive agents with Decoupled

Background: Beta-Delta Discounting

Standard exponential discounting uses a single discount factor δ\delta to weight future utility:

Ut=ut+δ ut+1+δ2 ut+2+⋯U_t = u_t + \delta\, u_{t+1} + \delta^2\, u_{t+2} + \cdots

Quasi-hyperbolic (beta-delta) discounting (Laibson, 1997) introduces a present-bias parameter β∈(0,1]\beta \in (0, 1] that discounts all future periods relative to the present:

Ut=ut+βδ ut+1+βδ2 ut+2+⋯U_t = u_t + \beta\delta\, u_{t+1} + \beta\delta^2\, u_{t+2} + \cdots

The discount weights are {1,  βδ,  βδ2,  …}\{1,\; \beta\delta,\; \beta\delta^2,\; \ldots\}. When β=1\beta = 1 this reduces to exponential discounting. When β<1\beta < 1 the agent is present-biased — they overweight current utility relative to any future period.

Note the key structure: β\beta appears once (not compounded). The weight nn periods ahead is βδn\beta\delta^n, not (βδ)n(\beta\delta)^n.

PyLCM’s Koopmans Aggregator and the Naive Agent

PyLCM defines the value function recursively via an aggregation function WW:

Vt(s)=max⁡a  W(u(s,a),  Et[Vt+1(s′)])V_t(s) = \max_a \; W\bigl(u(s, a),\; \mathbb{E}_t[V_{t+1}(s')]\bigr)

The default WW is W(u,E[V′])=u+δ⋅E[V′]W(u, \mathbb{E}[V']) = u + \delta \cdot \mathbb{E}[V'].

A naive beta-delta agent believes their future selves will be exponential discounters, but at each decision point applies present bias βδ\beta\delta to the continuation value. This requires two different WW implementations:

  • Solve phase (backward induction): use exponential discounting with δ\delta to compute the perceived continuation values VEV^E

  • Simulate phase (action selection): use βδ\beta\delta to choose actions, applying present bias to the perceived VEV^E

Phase is a broadcast dimension of the regime spec: a bare function applies to both phases, while Phased(solve=..., simulate=...) gives each phase its own implementation. Declaring koopmans_aggregator as Phased(solve=LinearAggregator(), simulate=linear_beta_delta) is all the naive agent needs — the params template becomes the union of both variants’ parameters, so one params dict serves both phases.

Why not just use W(u,V′)=u+βδV′W(u, V') = u + \beta\delta V' for both phases? Because the recursion would compound β\beta at every step, giving effective weights (βδ)n(\beta\delta)^n instead of the correct βδn\beta\delta^n. Splitting the phases ensures β\beta is applied exactly once — at the action selection step.

Setup: A 3-Period Model

We use a minimal consumption-savings model:

  • 3 periods: t=0,1t = 0, 1 (decisions), t=2t = 2 (terminal)

  • 1 state: wealth ww

  • 1 action: consumption cc

  • Utility: u(c)=log⁡(c)u(c) = \log(c)

  • Budget: w′=w−cw' = w - c (interest rate R=1R = 1)

  • Constraint: c≤wc \le w

  • Terminal: V2(w)=log⁡(w)V_2(w) = \log(w) (consume everything)

We build two models:

  • One with the shipped LinearAggregator for the standard exponential agent

  • One with a phase-variant WW for the naive beta-delta agent — solving with exponential WW and simulating with beta-delta WW

Analytical Solution

With log⁡\log utility, the optimal consumption rule is ct=wt/Dtc_t = w_t / D_t, where DtD_t is a “marginal propensity to save” denominator.

At t=1t = 1 (one period before terminal), the agent maximizes:

max⁡c  [log⁡(c)+βδ⋅log⁡(w−c)]\max_c \;\bigl[\log(c) + \beta\delta \cdot \log(w - c)\bigr]

First-order condition: 1/c=βδ/(w−c)1/c = \beta\delta / (w - c), giving c1=w/(1+βδ)c_1 = w / (1 + \beta\delta).

At t=0t = 0, the naive agent uses the perceived exponential continuation value V1EV^E_1, which has coefficient (1+δ)(1 + \delta) on log⁡w\log w. So the agent maximizes:

max⁡c  [log⁡(c)+βδ(1+δ)log⁡(w−c)+const]\max_c \;\bigl[\log(c) + \beta\delta (1 + \delta) \log(w - c) + \text{const}\bigr]

giving c0=w/(1+βδ(1+δ))c_0 = w / (1 + \beta\delta(1 + \delta)).

D1D_1D0D_0
Exponential (β=1\beta = 1)1+δ1 + \delta1+δ(1+δ)1 + \delta(1 + \delta)
Naive (β<1\beta < 1)1+βδ1 + \beta\delta1+βδ(1+δ)1 + \beta\delta(1 + \delta)
Type                 c_0      c_1
---------------------------------
Exponential       7.0114   6.6608
Naive             8.7080   6.7820

The naive agent consumes more at t=0t = 0 than the exponential agent — present bias pulls consumption forward. At t=1t = 1, the naive agent also consumes more because βδ<δ\beta\delta < \delta.

Numerical Results

Exponential Agent (β=1\beta = 1)

solve call 8f3bcb9e10a7 phase public_simulate begin
INFO:lcm:solve call 8f3bcb9e10a7 phase public_simulate begin
solve call 8f3bcb9e10a7 phase params_validation begin
INFO:lcm:solve call 8f3bcb9e10a7 phase params_validation begin
solve call 8f3bcb9e10a7 phase params_validation end status=ok seconds=0.088931
INFO:lcm:solve call 8f3bcb9e10a7 phase params_validation end status=ok seconds=0.088931
solve call 8f3bcb9e10a7 phase simulation_inputs begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_inputs begin
solve call 8f3bcb9e10a7 phase simulation_inputs end status=ok seconds=0.144127
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_inputs end status=ok seconds=0.144127
solve call 8f3bcb9e10a7 phase authority_fingerprint begin
INFO:lcm:solve call 8f3bcb9e10a7 phase authority_fingerprint begin
solve call 8f3bcb9e10a7 phase authority_fingerprint end status=ok seconds=0.004937
INFO:lcm:solve call 8f3bcb9e10a7 phase authority_fingerprint end status=ok seconds=0.004937
solve call 8f3bcb9e10a7 phase solver_param_checks begin
INFO:lcm:solve call 8f3bcb9e10a7 phase solver_param_checks begin
solve call 8f3bcb9e10a7 phase solver_param_checks end status=ok seconds=0.000058
INFO:lcm:solve call 8f3bcb9e10a7 phase solver_param_checks end status=ok seconds=0.000058
solve call 8f3bcb9e10a7 phase state_action_spaces begin
INFO:lcm:solve call 8f3bcb9e10a7 phase state_action_spaces begin
solve call 8f3bcb9e10a7 phase state_action_spaces end status=ok seconds=0.000250
INFO:lcm:solve call 8f3bcb9e10a7 phase state_action_spaces end status=ok seconds=0.000250
solve call 8f3bcb9e10a7 phase continuation_templates begin
INFO:lcm:solve call 8f3bcb9e10a7 phase continuation_templates begin
solve call 8f3bcb9e10a7 phase continuation_templates end status=ok seconds=0.001334
INFO:lcm:solve call 8f3bcb9e10a7 phase continuation_templates end status=ok seconds=0.001334
solve call 8f3bcb9e10a7 phase program_graphs begin
INFO:lcm:solve call 8f3bcb9e10a7 phase program_graphs begin
solve call 8f3bcb9e10a7 phase program_graphs end status=ok seconds=0.000711
INFO:lcm:solve call 8f3bcb9e10a7 phase program_graphs end status=ok seconds=0.000711
solve call 8f3bcb9e10a7 phase structural_resolution begin
INFO:lcm:solve call 8f3bcb9e10a7 phase structural_resolution begin
structural blueprint: miss (hits=0, misses=1, entries=1)
DEBUG:lcm:structural blueprint: miss (hits=0, misses=1, entries=1)
solve call 8f3bcb9e10a7 phase structural_resolution end status=ok seconds=0.043616
INFO:lcm:solve call 8f3bcb9e10a7 phase structural_resolution end status=ok seconds=0.043616
solve call 8f3bcb9e10a7 phase residency_inventory begin
INFO:lcm:solve call 8f3bcb9e10a7 phase residency_inventory begin
solve call 8f3bcb9e10a7 phase residency_inventory end status=ok seconds=0.000007
INFO:lcm:solve call 8f3bcb9e10a7 phase residency_inventory end status=ok seconds=0.000007
solve call 8f3bcb9e10a7 phase compilation_waves begin
INFO:lcm:solve call 8f3bcb9e10a7 phase compilation_waves begin
candidate evaluation ('working', 0, 'main') {'action_product': 64, 'cell': 128}
DEBUG:lcm:candidate evaluation ('working', 0, 'main') {'action_product': 64, 'cell': 128}
candidate evaluation ('working', 1, 'main') {'action_product': 64, 'cell': 128}
DEBUG:lcm:candidate evaluation ('working', 1, 'main') {'action_product': 64, 'cell': 128}
candidate evaluation ('dead', 2, 'main') {'cell': 128}
DEBUG:lcm:candidate evaluation ('dead', 2, 'main') {'cell': 128}
AOT compilation wave 0: 3 unique lowerings for 3 regime-period-core triples (2 workers)
INFO:lcm:AOT compilation wave 0: 3 unique lowerings for 3 regime-period-core triples (2 workers)
1/3  working main (age 0, widths={'action_product': 64, 'cell': 128})
INFO:lcm:1/3  working main (age 0, widths={'action_product': 64, 'cell': 128})
  lowering working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  lowering working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
  lowered in 539.1ms
INFO:lcm:  lowered in 539.1ms
  compiling working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  compiling working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
2/3  working main (age 1, widths={'action_product': 64, 'cell': 128})
INFO:lcm:2/3  working main (age 1, widths={'action_product': 64, 'cell': 128})
  lowering working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  lowering working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
  lowered in 356.6ms
INFO:lcm:  lowered in 356.6ms
  compiling working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  compiling working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
3/3  dead main (age 2, widths={'cell': 128})
INFO:lcm:3/3  dead main (age 2, widths={'cell': 128})
  lowering dead main (age 2, widths={'cell': 128}) ...
INFO:lcm:  lowering dead main (age 2, widths={'cell': 128}) ...
  lowered in 116.0ms
INFO:lcm:  lowered in 116.0ms
  compiled  working main (age 0, widths={'action_product': 64, 'cell': 128})  783.6ms
INFO:lcm:  compiled  working main (age 0, widths={'action_product': 64, 'cell': 128})  783.6ms
  compiling dead main (age 2, widths={'cell': 128}) ...
INFO:lcm:  compiling dead main (age 2, widths={'cell': 128}) ...
  compiled  dead main (age 2, widths={'cell': 128})  199.3ms
INFO:lcm:  compiled  dead main (age 2, widths={'cell': 128})  199.3ms
  compiled  working main (age 1, widths={'action_product': 64, 'cell': 128})  849.9ms
INFO:lcm:  compiled  working main (age 1, widths={'action_product': 64, 'cell': 128})  849.9ms
AOT compilation wave 0: 3 unique variants compiled, 0 fallback evaluations skipped, 0 fallback requests avoided
INFO:lcm:AOT compilation wave 0: 3 unique variants compiled, 0 fallback evaluations skipped, 0 fallback requests avoided
solve call 8f3bcb9e10a7 phase compilation_waves end status=ok seconds=1.785786
INFO:lcm:solve call 8f3bcb9e10a7 phase compilation_waves end status=ok seconds=1.785786
solve call 8f3bcb9e10a7 phase workspace_selection begin
INFO:lcm:solve call 8f3bcb9e10a7 phase workspace_selection begin
core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":0,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
DEBUG:lcm:core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":0,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":1,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
DEBUG:lcm:core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":1,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
core plan record {"active_replica_bytes":0,"axis_extents":{"cell":200},"compiled_collectives":{},"compiler_peak_bytes":1636,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":2,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"dead","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"cell":128}}
DEBUG:lcm:core plan record {"active_replica_bytes":0,"axis_extents":{"cell":200},"compiled_collectives":{},"compiler_peak_bytes":1636,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":2,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"dead","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"cell":128}}
solve call 8f3bcb9e10a7 phase workspace_selection end status=ok seconds=0.020626
INFO:lcm:solve call 8f3bcb9e10a7 phase workspace_selection end status=ok seconds=0.020626
Starting solution
INFO:lcm:Starting solution
solve call 8f3bcb9e10a7 phase backward_induction begin
INFO:lcm:solve call 8f3bcb9e10a7 phase backward_induction begin
Age 2 (1 regimes):
INFO:lcm:Age 2 (1 regimes):
  finished in 248.6ms
INFO:lcm:  finished in 248.6ms
Age 1 (1 regimes):
INFO:lcm:Age 1 (1 regimes):
  finished in 33.2ms
INFO:lcm:  finished in 33.2ms
Age 0 (1 regimes):
INFO:lcm:Age 0 (1 regimes):
  finished in 6.2ms
INFO:lcm:  finished in 6.2ms
  dead  age 2.0   V min=-0.693  max=3.91  mean=2.95
DEBUG:lcm:  dead  age 2.0   V min=-0.693  max=3.91  mean=2.95
  working  age 1.0   V min=-2.19  max=6.28  mean=4.41
DEBUG:lcm:  working  age 1.0   V min=-2.19  max=6.28  mean=4.41
  working  age 0.0   V min=-3.73  max=8.03  mean=5.3
DEBUG:lcm:  working  age 0.0   V min=-3.73  max=8.03  mean=5.3
solve call 8f3bcb9e10a7 phase backward_induction end status=ok seconds=0.315359
INFO:lcm:solve call 8f3bcb9e10a7 phase backward_induction end status=ok seconds=0.315359
Solution complete  (317.7ms)
INFO:lcm:Solution complete  (317.7ms)
solve call 8f3bcb9e10a7 phase result_assembly begin
INFO:lcm:solve call 8f3bcb9e10a7 phase result_assembly begin
solve call 8f3bcb9e10a7 phase result_assembly end status=ok seconds=0.005314
INFO:lcm:solve call 8f3bcb9e10a7 phase result_assembly end status=ok seconds=0.005314
solve call 8f3bcb9e10a7 phase solution_resolution begin
INFO:lcm:solve call 8f3bcb9e10a7 phase solution_resolution begin
solve call 8f3bcb9e10a7 phase solution_resolution end status=ok seconds=0.001925
INFO:lcm:solve call 8f3bcb9e10a7 phase solution_resolution end status=ok seconds=0.001925
solve call 8f3bcb9e10a7 phase solution_handover begin
INFO:lcm:solve call 8f3bcb9e10a7 phase solution_handover begin
solve call 8f3bcb9e10a7 phase solution_handover end status=ok seconds=0.000004
INFO:lcm:solve call 8f3bcb9e10a7 phase solution_handover end status=ok seconds=0.000004
solve call 8f3bcb9e10a7 phase chunk_planning begin
INFO:lcm:solve call 8f3bcb9e10a7 phase chunk_planning begin
solve call 8f3bcb9e10a7 phase chunk_planning end status=ok seconds=0.000400
INFO:lcm:solve call 8f3bcb9e10a7 phase chunk_planning end status=ok seconds=0.000400
solve call 8f3bcb9e10a7 phase simulation_setup begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_setup begin
Starting simulation
INFO:lcm:Starting simulation
Simulation plan: route=legacy; 1 cpu device(s) (0,); outer chunks=1 (widths=(1,)); unbudgeted.
INFO:lcm:Simulation plan: route=legacy; 1 cpu device(s) (0,); outer chunks=1 (widths=(1,)); unbudgeted.
Simulation plan detail: route=legacy; subject_devices=(cpu) (0,); axis_widths_by_regime=[working: {}; dead: {}]; outer_chunk_count=1; admitted_chunk_widths=(1,); budget_mode=unbudgeted; effective_device_memory_bytes=None; subject_grouping=None.
DEBUG:lcm:Simulation plan detail: route=legacy; subject_devices=(cpu) (0,); axis_widths_by_regime=[working: {}; dead: {}]; outer_chunk_count=1; admitted_chunk_widths=(1,); budget_mode=unbudgeted; effective_device_memory_bytes=None; subject_grouping=None.
solve call 8f3bcb9e10a7 phase simulation_setup end status=ok seconds=0.121204
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_setup end status=ok seconds=0.121204
solve call 8f3bcb9e10a7 phase simulation_chunk begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_chunk begin
Age 0 (1 regimes):
INFO:lcm:Age 0 (1 regimes):
solve call 8f3bcb9e10a7 phase simulation_compilation begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation begin
solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.421275
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.421275
solve call 8f3bcb9e10a7 phase simulation_compilation begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation begin
solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.030364
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.030364
solve call 8f3bcb9e10a7 phase simulation_compilation begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation begin
solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.043599
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.043599
Off-grid next states: regime='working', target='working', period=0, state='wealth', phase='realized', count=0
INFO:lcm:Off-grid next states: regime='working', target='working', period=0, state='wealth', phase='realized', count=0
  transitions:
  - working → working = 1
DEBUG:lcm:  transitions:
  - working → working = 1
  finished in 1.0s
INFO:lcm:  finished in 1.0s
Age 1 (1 regimes):
INFO:lcm:Age 1 (1 regimes):
solve call 8f3bcb9e10a7 phase simulation_compilation begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation begin
solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.317485
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.317485
solve call 8f3bcb9e10a7 phase simulation_compilation begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation begin
solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.030122
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.030122
Off-grid next states: regime='working', target='dead', period=1, state='wealth', phase='realized', count=0
INFO:lcm:Off-grid next states: regime='working', target='dead', period=1, state='wealth', phase='realized', count=0
  transitions:
  - working → dead = 1
DEBUG:lcm:  transitions:
  - working → dead = 1
  finished in 463.0ms
INFO:lcm:  finished in 463.0ms
Age 2 (1 regimes):
INFO:lcm:Age 2 (1 regimes):
solve call 8f3bcb9e10a7 phase simulation_compilation begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation begin
solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.039240
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_compilation end status=ok seconds=0.039240
  transitions:
  - dead → dead = 1
DEBUG:lcm:  transitions:
  - dead → dead = 1
  finished in 53.8ms
INFO:lcm:  finished in 53.8ms
solve call 8f3bcb9e10a7 phase simulation_chunk end status=ok seconds=1.532919
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_chunk end status=ok seconds=1.532919
solve call 8f3bcb9e10a7 phase simulation_completion begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_completion begin
Simulation complete  (1.7s)
INFO:lcm:Simulation complete  (1.7s)
solve call 8f3bcb9e10a7 phase simulation_completion end status=ok seconds=0.001501
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_completion end status=ok seconds=0.001501
solve call 8f3bcb9e10a7 phase simulation_result begin
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_result begin
solve call 8f3bcb9e10a7 phase simulation_result end status=ok seconds=0.006923
INFO:lcm:solve call 8f3bcb9e10a7 phase simulation_result end status=ok seconds=0.006923
solve call 8f3bcb9e10a7 phase result_finalization begin
INFO:lcm:solve call 8f3bcb9e10a7 phase result_finalization begin
solve call 8f3bcb9e10a7 phase result_finalization end status=ok seconds=0.000008
INFO:lcm:solve call 8f3bcb9e10a7 phase result_finalization end status=ok seconds=0.000008
solve call 8f3bcb9e10a7 phase public_simulate end status=ok seconds=4.138876
INFO:lcm:solve call 8f3bcb9e10a7 phase public_simulate end status=ok seconds=4.138876
Exponential agent:
  c_0 = 7.0149  (analytical: 7.0114)
  c_1 = 6.7143  (analytical: 6.6608)

Naive Agent

The naive agent uses model_naive, whose WW is phase-variant. During backward induction, the solve variant (LinearAggregator) computes the perceived value function VEV^E; during simulation, the simulate variant (linear_beta_delta) applies present bias for action selection.

The params dict is the union of both variants’ parameters — discount_factor for LinearAggregator, plus beta and delta for linear_beta_delta. Each variant receives only the kwargs it expects.

solve call 44f3fb4c0322 phase public_simulate begin
INFO:lcm:solve call 44f3fb4c0322 phase public_simulate begin
solve call 44f3fb4c0322 phase params_validation begin
INFO:lcm:solve call 44f3fb4c0322 phase params_validation begin
solve call 44f3fb4c0322 phase params_validation end status=ok seconds=0.022872
INFO:lcm:solve call 44f3fb4c0322 phase params_validation end status=ok seconds=0.022872
solve call 44f3fb4c0322 phase simulation_inputs begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_inputs begin
solve call 44f3fb4c0322 phase simulation_inputs end status=ok seconds=0.007161
INFO:lcm:solve call 44f3fb4c0322 phase simulation_inputs end status=ok seconds=0.007161
solve call 44f3fb4c0322 phase authority_fingerprint begin
INFO:lcm:solve call 44f3fb4c0322 phase authority_fingerprint begin
solve call 44f3fb4c0322 phase authority_fingerprint end status=ok seconds=0.002763
INFO:lcm:solve call 44f3fb4c0322 phase authority_fingerprint end status=ok seconds=0.002763
solve call 44f3fb4c0322 phase solver_param_checks begin
INFO:lcm:solve call 44f3fb4c0322 phase solver_param_checks begin
solve call 44f3fb4c0322 phase solver_param_checks end status=ok seconds=0.000037
INFO:lcm:solve call 44f3fb4c0322 phase solver_param_checks end status=ok seconds=0.000037
solve call 44f3fb4c0322 phase state_action_spaces begin
INFO:lcm:solve call 44f3fb4c0322 phase state_action_spaces begin
solve call 44f3fb4c0322 phase state_action_spaces end status=ok seconds=0.000251
INFO:lcm:solve call 44f3fb4c0322 phase state_action_spaces end status=ok seconds=0.000251
solve call 44f3fb4c0322 phase continuation_templates begin
INFO:lcm:solve call 44f3fb4c0322 phase continuation_templates begin
solve call 44f3fb4c0322 phase continuation_templates end status=ok seconds=0.000470
INFO:lcm:solve call 44f3fb4c0322 phase continuation_templates end status=ok seconds=0.000470
solve call 44f3fb4c0322 phase program_graphs begin
INFO:lcm:solve call 44f3fb4c0322 phase program_graphs begin
solve call 44f3fb4c0322 phase program_graphs end status=ok seconds=0.000316
INFO:lcm:solve call 44f3fb4c0322 phase program_graphs end status=ok seconds=0.000316
solve call 44f3fb4c0322 phase structural_resolution begin
INFO:lcm:solve call 44f3fb4c0322 phase structural_resolution begin
structural blueprint: miss (hits=0, misses=1, entries=1)
DEBUG:lcm:structural blueprint: miss (hits=0, misses=1, entries=1)
solve call 44f3fb4c0322 phase structural_resolution end status=ok seconds=0.008874
INFO:lcm:solve call 44f3fb4c0322 phase structural_resolution end status=ok seconds=0.008874
solve call 44f3fb4c0322 phase residency_inventory begin
INFO:lcm:solve call 44f3fb4c0322 phase residency_inventory begin
solve call 44f3fb4c0322 phase residency_inventory end status=ok seconds=0.000009
INFO:lcm:solve call 44f3fb4c0322 phase residency_inventory end status=ok seconds=0.000009
solve call 44f3fb4c0322 phase compilation_waves begin
INFO:lcm:solve call 44f3fb4c0322 phase compilation_waves begin
candidate evaluation ('working', 0, 'main') {'action_product': 64, 'cell': 128}
DEBUG:lcm:candidate evaluation ('working', 0, 'main') {'action_product': 64, 'cell': 128}
candidate evaluation ('working', 1, 'main') {'action_product': 64, 'cell': 128}
DEBUG:lcm:candidate evaluation ('working', 1, 'main') {'action_product': 64, 'cell': 128}
candidate evaluation ('dead', 2, 'main') {'cell': 128}
DEBUG:lcm:candidate evaluation ('dead', 2, 'main') {'cell': 128}
AOT compilation wave 0: 3 unique lowerings for 3 regime-period-core triples (2 workers)
INFO:lcm:AOT compilation wave 0: 3 unique lowerings for 3 regime-period-core triples (2 workers)
1/3  working main (age 0, widths={'action_product': 64, 'cell': 128})
INFO:lcm:1/3  working main (age 0, widths={'action_product': 64, 'cell': 128})
  lowering working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  lowering working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
  lowered in 231.4ms
INFO:lcm:  lowered in 231.4ms
  compiling working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  compiling working main (age 0, widths={'action_product': 64, 'cell': 128}) ...
2/3  working main (age 1, widths={'action_product': 64, 'cell': 128})
INFO:lcm:2/3  working main (age 1, widths={'action_product': 64, 'cell': 128})
  lowering working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  lowering working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
  compiled  working main (age 0, widths={'action_product': 64, 'cell': 128})  66.1ms
INFO:lcm:  compiled  working main (age 0, widths={'action_product': 64, 'cell': 128})  66.1ms
  lowered in 236.9ms
INFO:lcm:  lowered in 236.9ms
  compiling working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
INFO:lcm:  compiling working main (age 1, widths={'action_product': 64, 'cell': 128}) ...
3/3  dead main (age 2, widths={'cell': 128})
INFO:lcm:3/3  dead main (age 2, widths={'cell': 128})
  lowering dead main (age 2, widths={'cell': 128}) ...
INFO:lcm:  lowering dead main (age 2, widths={'cell': 128}) ...
  lowered in 36.9ms
INFO:lcm:  lowered in 36.9ms
  compiling dead main (age 2, widths={'cell': 128}) ...
INFO:lcm:  compiling dead main (age 2, widths={'cell': 128}) ...
  compiled  working main (age 1, widths={'action_product': 64, 'cell': 128})  49.7ms
INFO:lcm:  compiled  working main (age 1, widths={'action_product': 64, 'cell': 128})  49.7ms
  compiled  dead main (age 2, widths={'cell': 128})  6.1ms
INFO:lcm:  compiled  dead main (age 2, widths={'cell': 128})  6.1ms
AOT compilation wave 0: 3 unique variants compiled, 0 fallback evaluations skipped, 0 fallback requests avoided
INFO:lcm:AOT compilation wave 0: 3 unique variants compiled, 0 fallback evaluations skipped, 0 fallback requests avoided
solve call 44f3fb4c0322 phase compilation_waves end status=ok seconds=0.553017
INFO:lcm:solve call 44f3fb4c0322 phase compilation_waves end status=ok seconds=0.553017
solve call 44f3fb4c0322 phase workspace_selection begin
INFO:lcm:solve call 44f3fb4c0322 phase workspace_selection begin
core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":0,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
DEBUG:lcm:core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":0,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":1,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
DEBUG:lcm:core plan record {"active_replica_bytes":0,"axis_extents":{"action_product":500,"cell":200},"compiled_collectives":{},"compiler_peak_bytes":4491,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"action_product":"streamed","cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":1,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"working","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"action_product":64,"cell":128}}
core plan record {"active_replica_bytes":0,"axis_extents":{"cell":200},"compiled_collectives":{},"compiler_peak_bytes":1636,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":2,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"dead","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"cell":128}}
DEBUG:lcm:core plan record {"active_replica_bytes":0,"axis_extents":{"cell":200},"compiled_collectives":{},"compiler_peak_bytes":1636,"compiler_reservation_bytes":null,"core":"main","device_ids":[0],"dispatch":{"cell":"streamed"},"logical_gathered_bytes":0,"logical_value_shape":[200],"period":2,"period_transfer_scratch_bytes":{},"physical_value_shape":[200],"regime":"dead","resident_bytes":null,"selected_block":null,"state_extents":{"wealth":200},"stored_owner_bytes":{},"transfer_workspace_bytes":0,"widths":{"cell":128}}
solve call 44f3fb4c0322 phase workspace_selection end status=ok seconds=0.016602
INFO:lcm:solve call 44f3fb4c0322 phase workspace_selection end status=ok seconds=0.016602
Starting solution
INFO:lcm:Starting solution
solve call 44f3fb4c0322 phase backward_induction begin
INFO:lcm:solve call 44f3fb4c0322 phase backward_induction begin
Age 2 (1 regimes):
INFO:lcm:Age 2 (1 regimes):
  finished in 5.5ms
INFO:lcm:  finished in 5.5ms
Age 1 (1 regimes):
INFO:lcm:Age 1 (1 regimes):
  finished in 6.0ms
INFO:lcm:  finished in 6.0ms
Age 0 (1 regimes):
INFO:lcm:Age 0 (1 regimes):
  finished in 6.9ms
INFO:lcm:  finished in 6.9ms
  dead  age 2.0   V min=-0.693  max=3.91  mean=2.95
DEBUG:lcm:  dead  age 2.0   V min=-0.693  max=3.91  mean=2.95
  working  age 1.0   V min=-2.19  max=6.28  mean=4.41
DEBUG:lcm:  working  age 1.0   V min=-2.19  max=6.28  mean=4.41
  working  age 0.0   V min=-3.73  max=8.03  mean=5.3
DEBUG:lcm:  working  age 0.0   V min=-3.73  max=8.03  mean=5.3
solve call 44f3fb4c0322 phase backward_induction end status=ok seconds=0.023941
INFO:lcm:solve call 44f3fb4c0322 phase backward_induction end status=ok seconds=0.023941
Solution complete  (26.1ms)
INFO:lcm:Solution complete  (26.1ms)
solve call 44f3fb4c0322 phase result_assembly begin
INFO:lcm:solve call 44f3fb4c0322 phase result_assembly begin
solve call 44f3fb4c0322 phase result_assembly end status=ok seconds=0.002836
INFO:lcm:solve call 44f3fb4c0322 phase result_assembly end status=ok seconds=0.002836
solve call 44f3fb4c0322 phase solution_resolution begin
INFO:lcm:solve call 44f3fb4c0322 phase solution_resolution begin
solve call 44f3fb4c0322 phase solution_resolution end status=ok seconds=0.001205
INFO:lcm:solve call 44f3fb4c0322 phase solution_resolution end status=ok seconds=0.001205
solve call 44f3fb4c0322 phase solution_handover begin
INFO:lcm:solve call 44f3fb4c0322 phase solution_handover begin
solve call 44f3fb4c0322 phase solution_handover end status=ok seconds=0.000003
INFO:lcm:solve call 44f3fb4c0322 phase solution_handover end status=ok seconds=0.000003
solve call 44f3fb4c0322 phase chunk_planning begin
INFO:lcm:solve call 44f3fb4c0322 phase chunk_planning begin
solve call 44f3fb4c0322 phase chunk_planning end status=ok seconds=0.000423
INFO:lcm:solve call 44f3fb4c0322 phase chunk_planning end status=ok seconds=0.000423
solve call 44f3fb4c0322 phase simulation_setup begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_setup begin
Starting simulation
INFO:lcm:Starting simulation
Simulation plan: route=legacy; 1 cpu device(s) (0,); outer chunks=1 (widths=(1,)); unbudgeted.
INFO:lcm:Simulation plan: route=legacy; 1 cpu device(s) (0,); outer chunks=1 (widths=(1,)); unbudgeted.
Simulation plan detail: route=legacy; subject_devices=(cpu) (0,); axis_widths_by_regime=[working: {}; dead: {}]; outer_chunk_count=1; admitted_chunk_widths=(1,); budget_mode=unbudgeted; effective_device_memory_bytes=None; subject_grouping=None.
DEBUG:lcm:Simulation plan detail: route=legacy; subject_devices=(cpu) (0,); axis_widths_by_regime=[working: {}; dead: {}]; outer_chunk_count=1; admitted_chunk_widths=(1,); budget_mode=unbudgeted; effective_device_memory_bytes=None; subject_grouping=None.
solve call 44f3fb4c0322 phase simulation_setup end status=ok seconds=0.005727
INFO:lcm:solve call 44f3fb4c0322 phase simulation_setup end status=ok seconds=0.005727
solve call 44f3fb4c0322 phase simulation_chunk begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_chunk begin
Age 0 (1 regimes):
INFO:lcm:Age 0 (1 regimes):
solve call 44f3fb4c0322 phase simulation_compilation begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation begin
solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.333132
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.333132
solve call 44f3fb4c0322 phase simulation_compilation begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation begin
solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.011053
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.011053
solve call 44f3fb4c0322 phase simulation_compilation begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation begin
solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.015557
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.015557
Off-grid next states: regime='working', target='working', period=0, state='wealth', phase='realized', count=0
INFO:lcm:Off-grid next states: regime='working', target='working', period=0, state='wealth', phase='realized', count=0
  transitions:
  - working → working = 1
DEBUG:lcm:  transitions:
  - working → working = 1
  finished in 392.3ms
INFO:lcm:  finished in 392.3ms
Age 1 (1 regimes):
INFO:lcm:Age 1 (1 regimes):
solve call 44f3fb4c0322 phase simulation_compilation begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation begin
solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.322184
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.322184
solve call 44f3fb4c0322 phase simulation_compilation begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation begin
solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.014225
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.014225
Off-grid next states: regime='working', target='dead', period=1, state='wealth', phase='realized', count=0
INFO:lcm:Off-grid next states: regime='working', target='dead', period=1, state='wealth', phase='realized', count=0
  transitions:
  - working → dead = 1
DEBUG:lcm:  transitions:
  - working → dead = 1
  finished in 366.9ms
INFO:lcm:  finished in 366.9ms
Age 2 (1 regimes):
INFO:lcm:Age 2 (1 regimes):
solve call 44f3fb4c0322 phase simulation_compilation begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation begin
solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.015252
INFO:lcm:solve call 44f3fb4c0322 phase simulation_compilation end status=ok seconds=0.015252
  transitions:
  - dead → dead = 1
DEBUG:lcm:  transitions:
  - dead → dead = 1
  finished in 28.1ms
INFO:lcm:  finished in 28.1ms
solve call 44f3fb4c0322 phase simulation_chunk end status=ok seconds=0.793068
INFO:lcm:solve call 44f3fb4c0322 phase simulation_chunk end status=ok seconds=0.793068
solve call 44f3fb4c0322 phase simulation_completion begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_completion begin
Simulation complete  (802.3ms)
INFO:lcm:Simulation complete  (802.3ms)
solve call 44f3fb4c0322 phase simulation_completion end status=ok seconds=0.001219
INFO:lcm:solve call 44f3fb4c0322 phase simulation_completion end status=ok seconds=0.001219
solve call 44f3fb4c0322 phase simulation_result begin
INFO:lcm:solve call 44f3fb4c0322 phase simulation_result begin
solve call 44f3fb4c0322 phase simulation_result end status=ok seconds=0.000773
INFO:lcm:solve call 44f3fb4c0322 phase simulation_result end status=ok seconds=0.000773
solve call 44f3fb4c0322 phase result_finalization begin
INFO:lcm:solve call 44f3fb4c0322 phase result_finalization begin
solve call 44f3fb4c0322 phase result_finalization end status=ok seconds=0.000010
INFO:lcm:solve call 44f3fb4c0322 phase result_finalization end status=ok seconds=0.000010
solve call 44f3fb4c0322 phase public_simulate end status=ok seconds=1.493690
INFO:lcm:solve call 44f3fb4c0322 phase public_simulate end status=ok seconds=1.493690
Naive agent:
  c_0 = 8.7183  (analytical: 8.7080)
  c_1 = 6.8145  (analytical: 6.7820)

Comparison

The small differences between numerical and analytical solutions are due to grid discretization.

Loading...

Sophisticated and Partially Naive Agents

The naive agent stores the value it chooses with: its solve phase maximizes the exponential WW, and the stored VEV^E is that maximum. A sophisticated agent knows its future selves are present-biased. The solve then has two jobs that need different aggregators:

  • choose the action each future self takes, with u+βδ⋅CEu + \beta\delta \cdot \mathrm{CE};

  • store the long-run value of that action, with u+δ⋅CEu + \delta \cdot \mathrm{CE}, which is what the current self uses to price the future.

Decoupled(objective=..., value=...) declares exactly this. The objective ranks the actions, the value is stored at the chosen action, and only the objective may be Phased. One parameter β~\tilde\beta, the agent’s belief about its future selves’ present bias, spans all three agent types:

  • β~=1\tilde\beta = 1: naive, it expects exponential future selves;

  • β~=β\tilde\beta = \beta: sophisticated, it expects its true present bias;

  • β<β~<1\beta < \tilde\beta < 1: partially naive.

The acting self always chooses with its true β\beta, so the simulate variant of the objective is the linear_beta_delta aggregator from above.

{'beta': 'float', 'beta_tilde': 'float', 'delta': 'float'}

Analytical Solution

With log utility all three agents consume the same at t=0t = 0 (Pollak, 1968). At t=1t = 1 the agent expects to consume w1/(1+β~δ)w_1 / (1 + \tilde\beta\delta), so the long-run value it stores is

V1(w)=(1+δ)log⁡w+k(β~),k(b)=−log⁡(1+bδ)+δlog⁡bδ1+bδ.V_1(w) = (1 + \delta) \log w + k(\tilde\beta), \qquad k(b) = -\log(1 + b\delta) + \delta \log \frac{b\delta}{1 + b\delta}.

The slope in log⁡w\log w does not depend on β~\tilde\beta, so neither does the choice c0=w0/(1+βδ(1+δ))c_0 = w_0 / (1 + \beta\delta(1 + \delta)). The beliefs show up in the acting self’s assessment at t=0t = 0, V0=log⁡c0+δV1(w0−c0)V_0 = \log c_0 + \delta V_1(w_0 - c_0), the value simulation publishes. The solve’s own V0V_0 chooses c0c_0 with β~\tilde\beta instead.

Loading...

Analytically all three agents consume the same, and the numbers agree up to grid error. The assessed value falls as the agent expects more present bias from its future selves. Each agent acts on the same rule at t=1t = 1, so the long-run value every one of them actually gets is the sophisticated agent’s.

Keep in mind what the stored value is:

  • It is a self-assessment. For naive and partially naive agents it is the long-run value of the policy the agent expects to follow. The policy it does follow has a lower long-run value here. Welfare comparisons need that realized value, which the next section computes.

  • It needs finer grids. The value is read off at the objective’s choice, away from its own maximum, so grid error in the chosen action enters it at first order.

  • Only GridSearch solves it, and collective regimes and regimes whose transitions carry value-dependent gates refuse it.

  • Taste shocks work. With ExtremeValueTasteShocks the objective sets the choice probabilities and the stored value is the expected value at the noisy choice.

Realized Welfare

The stored value is what the agent expects. The long-run value of the policy it actually follows is ∑tδtut\sum_t \delta^t u_t along its simulated path. PyLCM does not compute it by a second backward pass; simulating and discounting the realized utilities estimates it. With deterministic wealth one subject suffices here; with risk, average over many subjects.

Loading...

The realized values agree across the three agents up to grid error, and they equal the sophisticated agent’s assessed value: only the sophisticated agent forecasts its own behavior correctly. The naive and partially naive agents overstate what they get.

Summary

Beta-delta discounting in PyLCM requires no core modifications:

  1. Pair the shipped LinearAggregator with a present-biased variant — only the second one has to be written:

    from lcm.koopmans_aggregation import LinearAggregator  # utility + discount_factor * CE
    
    def linear_beta_delta(utility, CE, beta, delta):
        return utility + beta * delta * CE
  2. Declare koopmans_aggregator as phase-variant so backward induction uses the exponential WW while action selection uses the beta-delta WW:

    from lcm import Phased
    
    koopmans_aggregator=Phased(
        solve=LinearAggregator(),
        simulate=linear_beta_delta,
    )
  3. Provide the union of both variants’ parameters:

    params = {
        "working": {
            "koopmans_aggregator": {
                "discount_factor": delta,
                "beta": beta,
                "delta": delta,
            }
        }
    }
  4. For sophisticated and partially naive agents, decouple the objective from the value, with the agent’s belief β~\tilde\beta as a parameter:

    from lcm import Decoupled
    
    koopmans_aggregator=Decoupled(
        objective=Phased(solve=perceived_beta_delta, simulate=linear_beta_delta),
        value=long_run,
    )

The phase split is essential: using W(u,V′)=u+βδV′W(u, V') = u + \beta\delta V' for both phases would compound β\beta at every recursive step, giving effective weights (βδ)n(\beta\delta)^n instead of the correct βδn\beta\delta^n.