Skip to content

0.8.0 - 2026-06-01

Choose a tag to compare

@github-actions github-actions released this 01 Jun 17:45
96b41cb

Release Notes

Deprecated

  • training.cut_selection.basis_activity_window is now ignored at
    config load and emits a tracing::warn! naming the field, its
    deprecation status, and the ignored semantics. The previous
    validation of the 1..=31 range is gone; any value (including
    formerly out-of-range values) loads successfully. The field will
    be removed from the schema in the next release; remove the entry
    from config.json to silence the warning. The rationale is that
    basis reconstruction now matches stored cut rows by slot identity
    alone, which makes the activity-window mask unobservable. See
    docs/design/basis-reconstruction-simplification.md.

Changed

  • Stage-LP state pinning uses column bounds (set_col_bounds) on the
    incoming-state columns instead of equality rows. The storage_fixing,
    lag_fixing, and anticipated_state_fixing row ranges in
    StageIndexer are permanent empty sentinels (0..0); callers must
    use StageIndexer::state_to_lp_incoming_column to resolve the column
    index for both pinning and dual extraction.
  • Cut subgradient extraction reads view.reduced_costs[col] (unscaled
    by multiplying by col_scale[col]) instead of view.dual[row]. The
    per-LP backward solve avoids N + N*L + A*K redundant equality rows
    per stage.
  • Cut deactivation toggles a cut row's RHS bounds to the
    f64::INFINITY sentinel (trivially satisfied) instead of removing
    the row from the LP. The cut pool is append-only: every cut ever
    generated remains stored at a stable slot index for the lifetime of
    the run. Stage-LP cut rows are stable across iterations, including
    after cut-selection deactivation.
  • Iteration template rebake now includes only active cuts. The
    per-iteration baked stage template carries one row per active cut in
    active_cuts() iteration order — inactive cuts are not encoded at
    sentinel [-INF, +INF] bounds. Recovers ~29% wall-time regression
    observed on production-scale convertido cases under the earlier
    sentinel-bake design.
  • training/metadata.json row_pool carries cuts_active (active
    cuts at end-of-run) and peak_active. The cuts_in_lp field
    introduced earlier in this development cycle is removed; the value
    was tied to the sentinel-bake model and is no longer meaningful.
    Existing manifests that carry cuts_in_lp are silently accepted —
    the field is dropped on read.
  • training/convergence.parquet row-selection schema carries
    10 columns. The cuts_in_lp column from the sentinel-bake model is
    removed; cuts_active stays.
  • ModelProvenanceReport is restructured into nested inflow and
    hydro_production sub-sections, aggregating FPHA-plane and
    evaporation-reference source counts from HydroModelProvenance via
    order-invariant tallies. The report and HydroModelSummary are now
    Serialize + Deserialize so they can be persisted and read back by
    cobre summary.
  • Cobre's offline geometric-mean column/row prescaler is restored and
    HiGHS's internal simplex scaler is disabled (simplex_scale_strategy: 0) across all phase profiles. The prescaler conditions the LP matrix
    before the first solve; HiGHS therefore operates on an already-scaled
    system and no double-scaling occurs. The retry escalation ladder
    inherits the Off strategy throughout.
  • HiGHS primal and dual feasibility tolerances are tightened to 1e-9
    for all three phase profiles (forward, backward, simulation). Prior
    releases used the HiGHS default of 1e-7.
  • The cobre-comm shared-memory subsystem (SharedMemoryProvider,
    SharedRegion, LocalCommunicator, HeapRegion) is now gated
    behind an off-by-default shared-memory Cargo feature. Downstream
    crates that previously compiled against these types unconditionally
    must add features = ["shared-memory"] to their dependency
    declaration, or remove the usage.
  • SharedMemoryProvider::split_local now returns LocalCommKind (a
    concrete enum: Local(LocalBackend) or, under mpi + shared-memory,
    Ferrompi(FerrompiLocalComm)) instead of Box<dyn LocalCommunicator>.
    This is a breaking change for crates that stored or forwarded the
    return value as a trait object.
  • training/metadata.json and simulation/metadata.json now persist
    the full execution topology (per-host rank layout via DistributionInfo.hosts),
    training and simulation solve-stat summaries, and the expected-cost
    statistics (mean, std, CVaR), so cobre summary can reproduce the
    complete live run end-block from a finished output directory.
  • Warm-start and resume training reuse the prior policy's stored LP bases:
    the first training iteration warm-starts its solves from the checkpoint
    basis instead of cold-starting. The same checkpoint basis reconstruction
    (which recovers per-cut slot identity from the stored basis) also speeds
    up simulation-only / cobre simulate runs.

Added

  • Python bindings (cobre-python crate, import cobre). A PyO3
    extension module exposing the full solver lifecycle to Python 3.12+,
    built with maturin's mixed layout. The public surface includes:
    • cobre.Study — load a case directory once; call .train() and
      .simulate(policy) without re-reading disk between calls; accepts
      config_overrides (dotted-key flat dict), output_dir, and
      threads.
    • cobre.Policy — in-memory trained-policy handle returned by
      Study.train(); exposes .iterations, .final_lower_bound,
      .final_upper_bound, .evaluate(stage, state), and
      .cut_matrix(stage) for stochastic introspection. Load from disk
      via Study.load_policy().
    • cobre.run.run — single-call equivalent of cobre run (load →
      train → simulate), GIL released for all Rust computation, same
      output artifacts as the CLI.
    • cobre.io.load_case, cobre.io.validate — case loading and
      validation with the same structured report as the CLI.
    • cobre.results.*load_results, load_convergence,
      load_convergence_arrow, load_simulation, load_simulation_arrow,
      load_policy, load_stochastic, report; Stochastic read-only
      introspection class.
    • cobre.model.*System, Bus, Line, Thermal, Hydro,
      EnergyContract, PumpingStation, NonControllableSource
      read-only model view classes.
    • cobre.errors.* — structured exception hierarchy rooted at
      CobreError(Exception): ValidationError(ValueError),
      PolicyIncompatibleError(ValueError), CaseIoError(OSError),
      OutputError(OSError), SolverError(RuntimeError),
      SimulationError(RuntimeError). Every leaf also subclasses the
      matching Python builtin so existing except OSError handlers
      continue to work.
    • cobre.schema.export — JSON schema export.
    • Study.train(on_iteration=...) accepts an optional Python callable
      invoked at each training-iteration boundary (dict with kind,
      iteration, lower_bound, upper_bound, gap, wall_time_ms);
      a truthy return requests a cooperative stop; a raising callback or
      KeyboardInterrupt is propagated after training artifacts are
      written. The GIL is reacquired only at iteration boundaries — never
      in the LP hot loop.
    • A per-call scoped rayon thread pool is created for each run,
      train, and simulate invocation so sequential calls with
      different threads values each honour their own count.
    • Output parity with the CLI: every file written by cobre run is
      also written by the Python bindings on the same code path, and
      vice-versa.
  • Per-phase LP solve profile mechanism. The LP solver is wrapped by
    ProfiledSolver<S>, which carries a solver-specific profile type
    (S::Profile) and applies it to the inner solver at phase boundaries.
    The profile type is exposed via the SolverInterface::Profile
    associated type, so different solvers (e.g. HiGHS, CLP) can each
    declare their full native tuning surface without a lossy abstraction.
    For the HiGHS backend the concrete type is HighsProfile, with
    fields for feasibility tolerances, iteration caps, dual edge-weight
    strategy, scale strategy, and price strategy. The profile is
    re-applied automatically before every solve to survive solver-internal
    option resets.
  • Backward-phase LP solver tuning. BACKWARD_PROFILE overrides one
    HiGHS option relative to the forward/simulation default:
    simplex_price_strategy switches from Row (1) to Row Hyper-Sparse
    (2), exploiting the sparsity of cut-subgradient rows that dominate
    the backward LP row count. The dual edge-weight strategy remains Devex
    on all three phases; empirical sweeps showed Dantzig and Steepest-Edge
    alternatives net worse on wall time and tail latency respectively.
  • Cut-selection kernel rebuilt around an m-block GEMM
    (matrixmultiply::dgemm). The kernel evaluates all populated cuts
    (active and inactive) at every visited forward-pass trial point, and
    applies the configured survival rule (Level1, LML1, Dominated). All
    three variants share one implementation that treats reactivation and
    deactivation symmetrically. Intra-stage work is distributed across
    the rayon thread pool via 8-trial-point blocks; the OR-merge across
    tasks is commutative so determinism is preserved regardless of thread
    count. The hot path allocates nothing beyond the bounded fold-leaf
    scratch.
  • tie_tolerance field on CutSelectionStrategy variants controls
    the per-state max-survival tolerance used by the value-evaluation
    kernel.
  • Trial-point sliding window on VisitedStatesArchive: the archive
    retains the most recent window_size forward-pass trial points and
    evicts older entries, bounding memory growth on long runs while
    keeping recent states visible to cut selection.
  • Cut reactivation wired into the training loop: cuts deactivated in
    a prior iteration that the kernel selects for survival are reactivated
    before the next backward pass.
  • cobre report now surfaces final bounds (final_lower_bound,
    final_upper_bound) and expected cost (mean_cost, std_cost,
    cvar) as top-level convenience keys in the JSON output, alongside
    the full nested metadata. Legacy output directories degrade gracefully
    to default values.
  • cobre summary reproduces the complete live run end-block (execution
    topology, hydro models, model provenance, training, simulation) from
    a finished output directory without re-running the solver. Reads the
    persisted metadata sidecars and degrades gracefully when a sidecar is
    absent.
  • Legacy lower bound recovery: cobre summary and cobre report can
    reconstruct the final lower bound from training/convergence.parquet
    when training/metadata.json predates the bounds field, preserving
    read-back for output directories from prior releases.
  • cobre-io exposes generic parquet readers and JSON sidecar
    read/write helpers (read_provenance_report, write_hydro_model_summary,
    etc.) so the CLI and Python bindings can share one load path for
    structured output artifacts.

Removed

  • basis_reconstructions column from training/solver/iterations.parquet.
    The column was always zero for every row and phase (the field was
    never packed into the MPI solver-stats wire format) and carried no
    diagnostic value. Consumers reading this column by name must remove
    the reference; the column is absent from the schema going forward.

Fixed

  • Forward-pass solver statistics are now aggregated across all MPI
    ranks before being written to training/solver/iterations.parquet.
    Previously only rank 0's contribution reached the parquet writer,
    so lp_solves, simplex_iterations, and timing fields for forward
    rows were understated by a factor of world_size on multi-rank runs.
  • cobre report and cobre summary now report the true generated cut
    count (cuts actually added by add_cut) rather than the slot
    high-water mark, which over-counted by forward_passes × stages due
    to the 1-based iteration indexing leaving the warm-start reservation
    block permanently empty.
  • Algorithmic strategy options (simplex_dual_edge_weight_strategy,
    simplex_scale_strategy, simplex_price_strategy) are now fully
    re-applied after a retry-level restore alongside the feasibility
    tolerances. Previously only the tolerance fields were re-applied,
    silently reverting the backward-phase price strategy to the HiGHS
    default on the post-retry attempt.
  • Simulation-phase output-write errors (simulation results, solver
    stats) are now routed to CaseIoError(OSError) and
    SimulationError(RuntimeError) respectively in the Python bindings,
    matching the exception types raised by the equivalent training-phase
    failures. Previously these errors fell through to SolverError.
  • cobre init / 1dtoy quickstart template corrected: the bus deficit
    cost is raised so load shedding dominates operational constraint
    penalties, and initial_conditions.json gains the $schema URL
    present on all other template files. The scaffolded case now passes
    cobre validate with zero errors and zero warnings.
  • The redundant Hydro-production sub-section is removed from the
    cobre summary and live run end-block output. It duplicated the
    Hydro-models section and always printed a misleading
    FPHA planes: 0 computed, 0 precomputed line for studies with no
    FPHA hydros. The source breakdown remains in model_provenance.json
    and is accessible via cobre report and the Python bindings.
  • Warm-start and resume training now apply the loaded policy's cuts on the
    first iteration. Previously the first post-resume iteration solved a
    cut-less, myopic policy — producing a spuriously high upper-bound estimate
    (the lower bound was already correct) and a wasted iteration — before
    self-correcting on the next iteration.
  • Loading a policy for simulation-only mode (training disabled) no longer
    trips a basis-reconstruction assertion in debug builds. Release builds were
    unaffected (the assertion compiles out), but debug builds — e.g. the Python
    bindings under maturin develop — previously panicked when reconstructing
    the LP basis from a checkpoint.

Note

The cut-sync wire format was briefly bumped to version 2 during
development to carry an ActivityUpdateRecord alongside cut records;
this never shipped and has been removed. The wire format remains at
version 1 (cut records only); cross-version MPI runs are not a
supported deployment mode.

cobre-cli 0.8.0

Install cobre-cli 0.8.0

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/cobre-rs/cobre/releases/download/v0.8.0/cobre-cli-installer.sh | sh

Install prebuilt binaries via powershell script

powershell -ExecutionPolicy Bypass -c "irm https://github.com/cobre-rs/cobre/releases/download/v0.8.0/cobre-cli-installer.ps1 | iex"

Download cobre-cli 0.8.0

File Platform Checksum
cobre-cli-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
cobre-cli-x86_64-apple-darwin.tar.xz Intel macOS checksum
cobre-cli-x86_64-pc-windows-msvc.zip x64 Windows checksum
cobre-cli-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
cobre-cli-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum

cobre-mcp 0.8.0

Install cobre-mcp 0.8.0

Install prebuilt binaries via shell script

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/cobre-rs/cobre/releases/download/v0.8.0/cobre-mcp-installer.sh | sh

Install prebuilt binaries via powershell script

powershell -ExecutionPolicy Bypass -c "irm https://github.com/cobre-rs/cobre/releases/download/v0.8.0/cobre-mcp-installer.ps1 | iex"

Download cobre-mcp 0.8.0

File Platform Checksum
cobre-mcp-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
cobre-mcp-x86_64-apple-darwin.tar.xz Intel macOS checksum
cobre-mcp-x86_64-pc-windows-msvc.zip x64 Windows checksum
cobre-mcp-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
cobre-mcp-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum