Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Extension Points

Purpose

This spec defines how Cobre selects, composes, and validates algorithm variants at configuration time. The training loop is parameterized by four abstraction points — risk measure, cut formulation, horizon mode, and sampling scheme — each with a defined set of variants. This spec maps each variant to its configuration source, lists validation rules, and documents cross-variant compatibility constraints.

For the behavioral contracts that the training loop imposes on each abstraction point, see Training Loop SS3. For the mathematical definitions of each variant, see the referenced math specs.

1. Variant Architecture Overview

The training loop has a fixed structure (forward pass, backward pass, MPI synchronization, convergence monitoring) parameterized by four configurable abstraction points:

Abstraction PointDeterminesConfigured InCurrent Variants
Risk MeasureHow backward outcomes are aggregated into cut coefficientsstages.jsonExpectation, CVaR (SS2)
Cut FormulationStructure of cuts added to the FCFFixedSingle-cut (SS3)
Horizon ModeStage transitions, terminal conditions, discount factorsstages.jsonFinite (SS4); Cyclic — deferred
Sampling SchemeHow the forward pass selects scenario realizationsconfig.jsonInSample, External, Historical, OutOfSample (SS5)

Fixed components (not configurable — same behavior regardless of variant selection):

2. Risk Measure Variants

The risk measure determines how backward pass outcomes (one per opening) are aggregated into a single cut. It can vary per stage (configured in each stage’s risk_measure field in stages.json).

2.1 Variant Table

VariantConfig ValueBehaviorMath Reference
Expectation"expectation"Probability-weighted average of per-outcome intercepts and gradients. Weights equal the uniform opening probabilities .Risk Measures SS1
CVaR{"cvar": {"alpha": ..., "lambda": ...}}Convex combination: . Sorting-based greedy weight allocation places maximum weight on worst-cost scenarios.Risk Measures SS3, SS7

2.2 Configuration Mapping

The risk_measure field appears per stage in stages.json (see Input Scenarios SS1.7):

{
  "stages": [
    { "id": 0, "risk_measure": { "cvar": { "alpha": 0.95, "lambda": 0.5 } } },
    { "id": 1, "risk_measure": "expectation" }
  ]
}

2.3 Validation Rules

RuleConditionError
R1alpha must be in alpha=0 produces undefined CVaR; alpha=1 is equivalent to expectation
R2lambda must be in Weight outside valid range
R3lambda=0 is equivalent to "expectation"Accepted (no error), but normalized to "expectation" internally

2.4 Behavioral Contract

Each risk measure variant must provide two operations consumed by the training loop:

  1. Cut aggregation — Given backward outcomes with their probabilities, produce a single set of cut coefficients . For Expectation, the weights are uniform. For CVaR, the weights are computed via the sorting-based greedy allocation from Risk Measures SS7.

  2. Risk evaluation — Given a set of cost values and probabilities, produce a scalar risk-adjusted cost. Used for convergence bound computation.

Bound validity warning: When CVaR is active, the first-stage LP objective is a convergence indicator only — it is NOT a valid lower bound. See Risk Measures SS10.

2.5 Deferred Variants

No additional risk measure variants are currently planned. The Expectation + CVaR convex combination covers the standard risk-averse SDDP formulation. Entropic risk measures would require a different dual representation and are not in scope.

3. Cut Formulation Variants

The cut formulation determines the structure of cuts added to the FCF at each backward pass stage.

3.1 Variant Table

VariantStatusBehaviorMath Reference
Single-cutCurrentOne aggregated cut per iteration per stage. .SDDP Algorithm SS6.1
Multi-cutDeferredOne cut per opening per iteration. for each .Deferred Features SSC.3

3.2 Configuration

No configuration required — single-cut is the only available formulation. When multi-cut is implemented, it will be selectable via config.jsontraining.cut_formulation.

4. Horizon Mode Variants

The horizon mode determines stage traversal, terminal conditions, and discount factors. It is derived from the policy_graph field in stages.json.

4.1 Variant Table

VariantStatusConfig TriggerBehaviorMath Reference
FiniteCurrentpolicy_graph.type = "finite_horizon" (or absent)Linear chain . Terminal value . No cycle, no mandatory discount.SDDP Algorithm SS4.1
CyclicDeferredpolicy_graph.type = "cyclic"At least one transition creates a cycle (source_id > target_id or equal). Cut pools organized by season. Discount required for convergence.Infinite Horizon

4.2 Configuration Mapping

The horizon mode is implicitly selected by the policy_graph structure in stages.json (see Input Scenarios SS1.2):

Finite horizon:

{
  "policy_graph": {
    "type": "finite_horizon",
    "annual_discount_rate": 0.06,
    "transitions": [
      { "source_id": 0, "target_id": 1, "probability": 1.0 },
      { "source_id": 1, "target_id": 2, "probability": 1.0 }
    ]
  }
}

Cyclic (infinite periodic) horizon:

{
  "policy_graph": {
    "type": "cyclic",
    "annual_discount_rate": 0.06,
    "transitions": [
      { "source_id": 0, "target_id": 1, "probability": 1.0 },
      { "source_id": 59, "target_id": 48, "probability": 1.0 }
    ]
  }
}

4.3 Validation Rules

RuleConditionError
H1At least one stage must existEmpty stage set
H2For cyclic: cumulative cycle discount Cycle discount prevents convergence
H3For cyclic: cycle start stage must exist in the stage setCycle start out of bounds
H4All transition target stages must existDangling transition

4.4 Behavioral Contract

Each horizon mode variant must provide:

  1. Successor computation — Given a stage, return successor stages with transition probabilities
  2. Terminal detection — Whether a stage has no successors (finite only; cyclic stages are never terminal)
  3. Discount factor — The discount factor for each transition. For finite horizon, this comes from the per-transition or global annual_discount_rate (may be 1.0 if undiscounted). For cyclic, the back-edge transition always applies the discount factor.
  4. Validation — Verify stage configuration is valid for the selected mode

Forward pass termination in cyclic mode: The forward pass simulates multiple cycles until the cumulative discount drops below a tolerance threshold or a max_horizon_length safety bound is reached. See Infinite Horizon SS6.

5. Sampling Scheme Variants

The sampling scheme determines how the forward pass selects scenario realizations at each stage. This is the fourth abstraction point defined in Training Loop SS3.4.

5.1 Variant Table

VariantConfig ValueForward Noise SourceBackward Noise SourceMath Reference
InSample"in_sample"Random index from fixed opening treeSame opening treeScenario Generation SS3
External"external"User-provided external_scenarios.parquetOpening tree from PAR fitted to external dataScenario Generation SS4
Historical"historical"inflow_history.parquet mapped to stagesOpening tree from PAR fitted to historyScenario Generation SS3
OutOfSample"out_of_sample"Fresh PAR-generated scenarios (independent of training tree)Opening tree from PAR modelScenario Generation SS3

5.2 Configuration Mapping

The scenario_source field in config.json (see Configuration Reference):

{ "scenario_source": { "sampling_scheme": "in_sample", "seed": 42 } }
{
  "scenario_source": {
    "sampling_scheme": "external",
    "selection_mode": "random"
  }
}
{ "scenario_source": { "sampling_scheme": "historical" } }

5.3 Validation Rules

RuleConditionError
S1in_sample requires seedReproducibility requires explicit seed
S2external requires external_scenarios.parquet in input directoryMissing external scenario file
S3historical requires inflow_history.parquet in input directoryMissing historical inflow file
S4external with selection_mode must be "random" or "sequential"Invalid selection mode

5.4 Key Invariant

The backward pass always uses the fixed opening tree, regardless of the forward sampling scheme. The separation of forward and backward noise sources is fundamental to SDDP correctness — see Scenario Generation SS3.1.

6. Variant Selection Pipeline

At startup, the variant selection pipeline resolves configuration into concrete algorithm behavior. This happens during the initialization phase described in CLI and Lifecycle:

StepActionInputOutput
1Parse config.json and stages.jsonRaw JSON filesTyped configuration structures
2Select horizon modepolicy_graph.typeFinite or Cyclic mode
3Select per-stage risk measuresstages[].risk_measureRisk measure per stage
4Select sampling schemescenario_source.sampling_schemeSampling scheme
5Validate individual variantsPer-variant rules (SS2.3, SS4.3, SS5.3)Reject invalid configurations
6Validate cross-variant compositionComposition rules (SS8)Reject incompatible combinations
7Construct training loopAll resolved variantsParameterized training loop

The risk measure is per-stage (step 3 produces a mapping from stage ID to risk measure variant). All other abstraction points are global (one selection for the entire run).

7. Dispatch Mechanism

Open design point: The dispatch mechanism — how variant-specific behavior is invoked at runtime — is an implementation decision deferred to coding time.

OptionDescriptionTrade-off
Compile-time monomorphizationGeneric training loop TrainingLoop<R, C, H, S> instantiated with concrete types. Variant selected via cfg features or top-level match.Zero overhead. No dynamic dispatch cost. But: per-stage risk measure variation requires a different approach (can’t monomorphize per stage).
Enum dispatchEach abstraction point uses a flat enum (e.g., enum RiskMeasure { Expectation, CVaR { alpha, lambda } }). Match at each call site.No heap allocation, inlineable, supports per-stage variation naturally. Small match overhead at each backward pass iteration.
Trait objectsBox<dyn RiskMeasure> with dynamic dispatch.Maximum flexibility, supports arbitrary nesting (e.g., convex combination of arbitrary inner risk measures). Virtual call overhead on hot path.

The per-stage risk measure requirement (SS2.2) rules out pure compile-time monomorphization for the risk measure dimension. Enum dispatch is the natural fit for a small, fixed set of variants.

Note: The solver abstraction uses compile-time cfg-feature selection for the LP solver backend (CLP vs HiGHS) — see Solver Abstraction SS10. The algorithm variant dispatch mechanism is a separate decision and need not use the same approach.

8. Variant Composition Validation

Some variant combinations have specific interactions that require validation or special handling.

8.1 Cross-Variant Rules

RuleCombinationConstraintRationale
X1CVaR + Cyclic horizonConvergence indicator only (not a valid lower bound)Risk Measures SS10: risk-averse LB is not a valid bound
X2CVaR + Simulation stopping ruleSimulation-based stopping must use risk-adjusted forward costsThe upper bound estimate requires risk-adjusted evaluation
X3External/Historical + CyclicForward pass cycle wrapping must handle external scenario index mappingExternal scenarios may not align with cycle boundaries
X4Any risk measure + Multi-cut (deferred)Per-opening risk adjustment not applicable with multi-cutMulti-cut produces one cut per opening, bypassing risk aggregation

8.2 Default Configuration

When fields are omitted, the following defaults apply:

Abstraction PointDefaultRationale
Risk measure"expectation"Risk-neutral is the standard SDDP formulation
Cut formulationSingle-cutOnly available formulation
Horizon mode"finite_horizon"Standard finite horizon,
Sampling scheme"in_sample"PAR-generated opening tree

Cross-References