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.

Outer search and branch aggregation

NNBEGM separates two decisions: how adjuster candidates are generated, and how the keeper and adjuster branches combine.

FiniteOuterGrid

FiniteOuterGrid(grid=...)

Solves one exact inner problem per outer post-decision target in grid and selects a grid-snapped target. Simulation recovers the declared outer action by exactly inverting the post-decision map, so that action may lie between action-grid nodes. The result is exact relative to the finite target set. How many targets are solved per dispatch is an execution choice under the outer_candidate axis, not a field of the candidate set.

AdaptiveOuterMesh

AdaptiveOuterMesh(
    initial_grid=...,
    max_nodes=129,
    max_refinement_rounds=6,
    golden_iterations=32,
    fail_closed=True,
)

Starts from initial_grid, evaluates exact inner solves on a shared mesh, validates interpolation at proposed points, and refines bracket-local optima. It supports only literal borrowing floors; a callable post_decision_lower_bound floor raises ModelInitializationError at model construction, because honouring it at every refined node is prohibitively slow (see post-decision lower bounds).

Important fields:

Without a valid Lipschitz bound, midpoint/mesh validation cannot exclude an arbitrarily narrow peak between sampled points.

Golden section assumes that the objective is unimodal inside the bracket. A kink inside a retained bracket, for example where a borrowing floor starts to bind, breaks that assumption, so the refined point can land away from the optimum of the bracket. Treat refined values near a known kink as approximate. When the kink location matters, use a FiniteOuterGrid that has a node at the kink.

The replay policy published under an adaptive search is read against the exact mesh the solve generated, which the solving Model instance records privately beside the result. It is therefore retained under ResultRetention.VALUES_AND_REPLAY and omitted as NOT_PERSISTED under ALL_PERSISTABLE_ARTIFACTS; see Runtime and results.

OuterSearch is the abstract configuration marker.

Branch aggregation

DeterministicOuterMaximum

DeterministicOuterMaximum()

Takes max(V_keeper, V_adjuster) with the keeper winning exact ties.

UniformObservedFixedCost

UniformObservedFixedCost(
    shock_name=...,
    scale_function=...,
    lower=...,
    upper=...,
)

Analytically integrates a shock chi ~ U(lower, upper) entering only the adjuster’s fixed adjustment cost through a non-negative scale_function. The shock must be observed before branch choice, must not change conditional actions, and must not enter state transitions except through that branch choice. It is supported with AdaptiveOuterMesh and needs no solve-state grid.

A regime declaring it is solve-only. The solve integrates the observed cost analytically, and simulation cannot yet draw the shock and replay the contingent keeper/adjuster policy, so Model.simulate() raises UnsupportedOperationError.

BranchAggregateResult contains expected value, adjustment and no-adjustment probabilities, and the cutoff draw. OuterBranchAggregator is the abstract configuration marker.

Method background: Nested endogenous-grid methods.