cobre_sddp/lib.rs
1//! SDDP solver for hydrothermal dispatch.
2//!
3//! Implements the SDDP algorithm: forward/backward passes, Benders cuts, risk measures,
4//! convergence monitoring, and policy simulation. Parallelized via rayon (intra-rank)
5//! and ferrompi (inter-rank).
6
7// Relax strict production lints for test builds.
8#![cfg_attr(
9 test,
10 allow(
11 clippy::unwrap_used,
12 clippy::expect_used,
13 clippy::panic,
14 clippy::float_cmp,
15 clippy::cast_possible_truncation,
16 clippy::cast_possible_wrap,
17 )
18)]
19
20// Module visibility policy:
21//
22// - `pub mod` modules are accessed by name from outside the crate.
23// `setup` and `policy_export` are reached by qualified path from
24// `cobre-cli` / `cobre-python` / examples; the others are reached by
25// qualified path from this crate's own integration tests in `tests/`
26// (which compile as separate crates and so need pub visibility).
27// Downstream crates SHOULD prefer the curated re-exports below; the
28// `pub mod` namespaces are not a semver-stable API.
29// - `pub(crate)` modules are pure internals — never named from outside
30// this crate (verified by grep against `tests/`, `examples/`, and the
31// other workspace crates).
32pub mod config;
33pub mod convergence;
34pub mod cut;
35pub mod error;
36pub(crate) mod gemm;
37pub mod horizon_mode;
38pub(crate) mod hull;
39pub mod lp;
40pub mod policy;
41pub mod production;
42pub mod setup;
43pub mod simulation;
44pub mod solve;
45pub mod solver_stats;
46pub mod stochastic;
47pub mod training;
48pub mod validate_phases;
49pub mod workspace;
50
51// Crate-root submodule shim: re-exposes the inner `context` file module (now
52// `workspace::context` after the `workspace/` cluster move) at the pre-move
53// `cobre_sddp::context` path, so the in-crate `crate::context::{StageContext,
54// TrainingContext}` call sites resolve verbatim without per-site edits.
55pub use workspace::context;
56
57// Crate-root submodule shim: re-exposes the inner `solver_phase` / `stage_solve`
58// file modules (now `solve::solver_phase` / `solve::stage_solve` after the
59// `solve/` cluster move) at their pre-move crate-root paths, so the in-crate
60// `crate::solver_phase::Phase` use in `simulation/state.rs` and the
61// `crate::stage_solve::{fill_unscaled, fill_unscaled_dual, StageInputs,
62// run_stage_solve}` uses in `training/forward/`, `training/backward/`, and
63// `simulation/pipeline.rs` resolve verbatim without per-site edits.
64// `stage_solve` keeps `pub(crate)` visibility — it has no external raw-path
65// consumer.
66pub use solve::solver_phase;
67pub(crate) use solve::stage_solve;
68
69// Crate-root submodule shim: preserves the pre-move
70// `cobre_sddp::risk_measure::` / `cobre_sddp::stopping_rule::` raw paths
71// verbatim for the integration tests in `tests/conformance.rs`, which import
72// these submodules by qualified path.
73pub use convergence::{risk_measure, stopping_rule};
74
75// Crate-root submodule shims for the `cut/` cluster move (`cut_selection`,
76// `cut_sync`, `dcs`, `basis_reconstruct` were flat at the crate root; they now
77// live under `cut/`). Each shim re-exposes a moved submodule at its pre-move
78// crate-root path so consumers resolve verbatim without per-site edits:
79//
80// - `cut_selection` — `cobre_sddp::cut_selection::` in `benches/cut_selection_kernel.rs`
81// and the integration tests `tests/{cut_selection_kernel_perf,cut_selection_determinism_realistic}.rs`,
82// the internal `crate::cut_selection::` call sites across `training/backward/`,
83// `training/backward_pass_state.rs`, `config.rs`, `cut/pool.rs`, `cut/dcs.rs`,
84// `training/forward/`, `simulation/pipeline.rs`, `training/training.rs`, and
85// `training/session/mod.rs`, plus the `crate::cut_selection::CutMetadata`
86// intra-doc links in `cut/mod.rs`.
87// - `cut_sync` — the internal `crate::cut_sync::CutSyncBuffers` references
88// (reached via grouped `use crate::{ … }` imports) in `training/backward/`,
89// `training/backward_pass_state.rs`, and `training/session/mod.rs`.
90// - `dcs` — `cobre_sddp::dcs::` in `benches/dcs_batched_scoring.rs` plus the
91// internal `crate::dcs::` call sites across `training/backward/`,
92// `training/forward/`, `setup/{accessors,mod}.rs`, `simulation/pipeline.rs`,
93// and `workspace/workspace.rs`.
94// - `basis_reconstruct` — `cobre_sddp::basis_reconstruct::` in
95// `tests/hybrid_reconstruction.rs` (plus the doc-comment intra-doc links in
96// `tests/basis_reconstruct_churn.rs` and `cobre-python/src/run.rs`), and the
97// internal `crate::basis_reconstruct::` call sites in `cut/dcs.rs`,
98// `training/forward/`, and `workspace/workspace.rs`.
99pub use cut::{basis_reconstruct, cut_selection, cut_sync, dcs};
100
101// Crate-root submodule shims for the `lp/` cluster move (`indexer` /
102// `generic_constraints` / `lp_builder` were flat at the crate root; they now
103// live under `lp/`). Each shim re-exposes a moved submodule at its pre-move
104// crate-root path so consumers resolve verbatim without per-site edits:
105//
106// - `indexer` — covers the external raw-path consumers (`cobre_sddp::indexer::`
107// in 4 integration tests + 2 benches) AND the internal `crate::indexer::`
108// call sites across 13 files (including `lp/generic_constraints.rs` itself).
109// - `lp_builder` (alias of `lp::builder`) — keeps the 27 internal
110// `crate::lp_builder::Symbol` references across 11 files resolving to the
111// moved subtree without editing those files.
112// - `generic_constraints` — keeps `pub(crate)` visibility; covers the internal
113// `crate::generic_constraints::` references in `lp/builder/matrix.rs` and
114// `lp/builder/layout.rs`, which moved into the cluster and still reach the
115// constraint-lowering module by its pre-move crate-root path.
116pub use lp::builder as lp_builder;
117pub(crate) use lp::generic_constraints;
118pub use lp::indexer;
119
120// Crate-root submodule shim: preserves the pre-`policy/`-relocation raw
121// `cobre_sddp::<module>::` / `crate::<module>::` paths verbatim so consumers
122// resolve without edits. Each re-exported module has raw-path callers that the
123// curated re-exports above do not cover:
124// - `orchestration` — production callers `write_checkpoint`,
125// `CheckpointParams`, `export_stochastic_artifacts` in
126// `cobre-cli/src/commands/run/{outputs,setup}.rs` and
127// `cobre-python/src/run.rs`.
128// - `policy_export` — `tests/{boundary_cuts,decomp_integration,warm_start}.rs`
129// plus the intra-crate `crate::policy_export::` use in `orchestration`.
130// - `resolved_parameters` — `crate::resolved_parameters::` paths in
131// `lp_builder/{layout,patch,matrix,template}.rs` and `setup`.
132// - `scaling_report` — `crate::scaling_report::` paths in
133// `setup/{template_postprocess,mod}.rs`.
134pub use policy::{orchestration, policy_export, resolved_parameters, scaling_report};
135
136// Crate-root submodule shim: preserves the pre-`production/`-relocation raw
137// `cobre_sddp::<module>::` / `crate::<module>::` paths verbatim so consumers
138// resolve without edits. Each re-exported module has raw-path callers that the
139// curated re-exports above do not cover:
140// - `energy_conversion` — `cobre_sddp::energy_conversion::` in the integration
141// tests `tests/{scalar_parameters_declaration_order,simulation_pipeline_integration}.rs`,
142// plus intra-crate `crate::energy_conversion::` uses.
143// - `hydro_models` — `cobre_sddp::hydro_models::prepare_hydro_models_from_artifacts`
144// in `cobre-cli/src/commands/{run/setup,validate}.rs` and
145// `cobre-python/src/{io,run}.rs` (this symbol is intentionally NOT in the
146// curated re-export above; the shim is its sole resolution path), plus
147// intra-crate `crate::hydro_models::` uses.
148// - `fpha_fitting` — `crate::fpha_fitting::FphaFittingError` in the non-moved
149// `error.rs` plus intra-cluster uses in `hydro_models` and
150// `energy_conversion`; `pub(crate)` keeps its crate-private visibility.
151pub(crate) use production::fpha_fitting;
152pub use production::{energy_conversion, hydro_models};
153
154// Crate-root submodule shim: preserves the pre-`stochastic/`-relocation raw
155// `cobre_sddp::<module>::` / `crate::<module>::` paths verbatim so consumers
156// resolve without edits. Every one of the six modules has raw-path callers in
157// non-moved files that the curated re-exports below do not cover:
158// - `estimation` — `crate::estimation::` in `error.rs`,
159// `policy/{orchestration,provenance}.rs`, and
160// `setup/stochastic_pipeline.rs`.
161// - `inflow_method` — `crate::inflow_method::` in `indexer.rs`, `training/forward/`,
162// and `lp_builder/{template,matrix}.rs`, `simulation/pipeline.rs`.
163// - `lag_transition` — `cobre_sddp::lag_transition::precompute_stage_lag_transitions`
164// in `cobre-cli/src/commands/run/setup.rs` (this symbol is intentionally NOT
165// in the curated re-export; the shim is its sole resolution path), plus
166// `crate::lag_transition::` uses in `workspace/context.rs` and
167// `setup/{stochastic_pipeline,stage_data,mod}.rs`.
168// - `noise_key_diag` — `crate::noise_key_diag::` in `setup/mod.rs`.
169// - `noise` — `crate::noise::` in `workspace/context.rs`, `training/forward/`, and
170// `simulation/pipeline.rs`; `pub(crate)` keeps its crate-private visibility.
171// - `stochastic_summary` — `crate::stochastic_summary::` in
172// `policy/orchestration.rs`; `pub(crate)` keeps its crate-private visibility.
173pub use stochastic::{estimation, inflow_method, lag_transition, noise_key_diag};
174pub(crate) use stochastic::{noise, stochastic_summary};
175
176// Crate-root submodule shim: preserves the pre-`training/`-relocation raw
177// `crate::<module>::` paths verbatim for the internal references that the
178// curated re-exports below do not cover, so the consumer files resolve without
179// per-site edits. The pass files moved into `training/`; their pre-move
180// crate-root paths are re-exposed here:
181// - `forward` — `crate::forward::{ForwardResult, SyncResult}` in
182// `training/session/mod.rs`, the `crate::forward::sync_forward` use there and
183// its intra-doc links in `convergence/convergence.rs`, and the
184// `crate::forward::build_delta_cut_row_batch_into` intra-doc link in
185// `cut/row.rs`. Keeps `pub` to match its pre-move visibility.
186// - `backward` — `crate::backward::StagedCut` in `workspace/workspace.rs`,
187// `crate::backward::BackwardResult` in `training/session/mod.rs`, and the
188// `crate::backward::duals_extraction::extract_duals_from_view` intra-doc link
189// in `cut/row.rs`.
190// - `backward_pass_state` — `crate::backward_pass_state::BackwardPass{State,Inputs}`
191// in `training/backward/` and the doc reference in `cut/cut_selection.rs`.
192// - `forward_pass_state` — `crate::forward_pass_state::{ForwardPassInputs,
193// ForwardPassState}` in `training/forward/`.
194// - `lower_bound` — `crate::lower_bound::evaluate_lower_bound` intra-doc links
195// in `convergence/convergence.rs`. Keeps `pub` to match its pre-move visibility.
196// - `state_exchange` — `crate::state_exchange::ExchangeBuffers` in
197// `training/backward_pass_state.rs`, `training/backward/trial_point.rs`, and
198// `training/session/mod.rs` (load-bearing; do not remove).
199// - `trajectory` — `crate::trajectory::TrajectoryRecord` in `training/forward/`
200// and `training/state_exchange.rs`.
201// - `visited_states` — `crate::visited_states::VisitedStatesArchive` in
202// `training/training.rs`, `training/session/mod.rs`, and `policy/policy_export.rs`.
203pub(crate) use training::{
204 backward, backward_pass_state, forward_pass_state, state_exchange, trajectory, visited_states,
205};
206pub use training::{forward, lower_bound};
207
208// Crate-root submodule shim: aliases the nested `training/session/` subtree at
209// the pre-move `crate::training_session::` path so the
210// `crate::training_session::{iteration_scratch, rank_distribution, runtime}`
211// references in `training/forward_pass_state.rs` resolve verbatim.
212pub(crate) use training::session as training_session;
213
214// ── config ────────────────────────────────────────────────────────────────────
215pub use config::TrainingConfig;
216// ── convergence ───────────────────────────────────────────────────────────────
217pub use convergence::convergence::ConvergenceMonitor;
218// ── cut ───────────────────────────────────────────────────────────────────────
219pub use cut::wire::{CutWireHeader, cut_wire_size, deserialize_cut, serialize_cut};
220pub use cut::{CutPool, FutureCostFunction};
221// ── cut_selection ─────────────────────────────────────────────────────────────
222pub use cut::cut_selection::CutSelectionStrategy;
223// ── cut_sync ──────────────────────────────────────────────────────────────────
224pub use cut::cut_sync::CutSyncBuffers;
225// ── cut::row ──────────────────────────────────────────────────────────────────
226pub use cut::row::build_cut_row_batch_into;
227// ── energy_conversion ─────────────────────────────────────────────────────────
228pub use production::energy_conversion::{EnergyConversionSet, HydroEnergyProductivityOverride};
229// ── error ─────────────────────────────────────────────────────────────────────
230pub use error::SddpError;
231// ── estimation ────────────────────────────────────────────────────────────────
232pub use stochastic::estimation::{EstimationPath, EstimationReport, estimate_from_history};
233// ── forward ───────────────────────────────────────────────────────────────────
234pub use training::forward::SyncResult;
235// ── hydro_models ──────────────────────────────────────────────────────────────
236pub use production::hydro_models::{
237 FphaFitDeviationEntry, FphaHydroDetail, HydroFitTimings, HydroModelSummary,
238 PrepareHydroModelsResult, ProductionModelSource, build_deviation_summary,
239 build_evaporation_model_rows, build_fpha_deviation_point_rows, build_hydro_model_summary,
240 prepare_hydro_models,
241};
242// ── indexer ───────────────────────────────────────────────────────────────────
243pub use lp::indexer::{EquipmentCounts, FphaColumnLayout, StageIndexer};
244// ── inflow_method ─────────────────────────────────────────────────────────────
245pub use stochastic::inflow_method::InflowNonNegativityMethod;
246// ── lp_builder ────────────────────────────────────────────────────────────────
247pub use lp::builder::{StageTemplates, build_stage_templates};
248// ── policy_load ───────────────────────────────────────────────────────────────
249pub use policy::policy_load::{
250 build_basis_cache_from_checkpoint, inject_boundary_cuts, load_boundary_cuts,
251 validate_policy_compatibility,
252};
253// ── provenance ────────────────────────────────────────────────────────────────
254pub use policy::provenance::{
255 HydroProductionProvenance, InflowProvenance, ModelProvenanceReport, ProvenanceSource,
256 build_provenance_report,
257};
258// ── risk_measure ──────────────────────────────────────────────────────────────
259pub use convergence::risk_measure::{BackwardOutcome, RiskMeasure};
260// ── setup ─────────────────────────────────────────────────────────────────────
261pub use setup::{
262 DEFAULT_MAX_ITERATIONS, DEFAULT_SEED, PrepareStochasticResult, StudyParams, StudySetup,
263 prepare_stochastic,
264};
265// ── simulation ────────────────────────────────────────────────────────────────
266pub use simulation::{
267 ScenarioCategoryCosts, SimulationError, SimulationHydroResult, SimulationScenarioResult,
268 SimulationStageResult, SimulationSummary, aggregate_simulation, simulate,
269};
270// ── solver_phase ─────────────────────────────────────────────────────────────
271pub use solve::solver_phase::Phase;
272#[cfg(feature = "highs")]
273pub use solve::solver_phase::{BACKWARD_PROFILE, FORWARD_PROFILE, SIMULATION_PROFILE};
274// ── solver_stats ──────────────────────────────────────────────────────────────
275pub use solver_stats::{
276 SOLVER_STATS_DELTA_SCALAR_FIELDS, SolverStatsDelta, SolverStatsLogEntry, delta_to_stats_row,
277 pack_delta_scalars, pack_scenario_stats, solver_stats_log_to_rows, unpack_delta_scalars,
278 unpack_scenario_stats,
279};
280// ── stochastic_summary ────────────────────────────────────────────────────────
281pub use stochastic::stochastic_summary::{
282 ArOrderSummary, StochasticSource, StochasticSummary, build_stochastic_summary,
283 estimation_report_to_fitting_report, inflow_models_to_annual_component_rows,
284 inflow_models_to_ar_rows, inflow_models_to_stats_rows,
285};
286// ── stopping_rule ─────────────────────────────────────────────────────────────
287pub use convergence::stopping_rule::{MonitorState, StoppingMode, StoppingRule, StoppingRuleSet};
288// ── training ──────────────────────────────────────────────────────────────────
289pub use training::training::{TrainingOutcome, TrainingResult, train};
290// ── training_output ───────────────────────────────────────────────────────────
291pub use training::training_output::build_training_output;
292// ── resolved_parameters ───────────────────────────────────────────────────────
293pub use policy::resolved_parameters::{
294 ResolvedParameters, ResolvedParametersError, build_resolved_parameters,
295 deserialize_resolved_parameters, serialize_resolved_parameters,
296};
297// ── state_exchange ────────────────────────────────────────────────────────────
298pub use training::state_exchange::ExchangeBuffers;
299// ── trajectory ────────────────────────────────────────────────────────────────
300pub use training::trajectory::TrajectoryRecord;
301// ── workspace ─────────────────────────────────────────────────────────────────
302pub use workspace::workspace::{BASIS_BROADCAST_WIRE_VERSION, CapturedBasis};