Pular para o conteúdo

Python Quickstart

Este conteúdo não está disponível em sua língua ainda.

Install Cobre and run a study in a few steps.

Terminal window
pip install cobre-python

Requires Python 3.12, 3.13, or 3.14.

import cobre
result = cobre.run.run("path/to/case")

The cobre.run.run() function loads the case, trains an SDDP policy, optionally runs simulation, and writes output files. It returns a dictionary with the following keys:

KeyTypeDescription
convergedboolWhether training converged
iterationsintNumber of training iterations completed
lower_boundfloatFinal lower bound
upper_boundfloat or NoneFinal upper bound (None if no simulation)
gap_percentfloat or NoneOptimality gap percentage (None if unavailable)
total_time_msintTotal wall-clock time in milliseconds
output_dirstrPath to the output directory
simulationdict or NoneSimulation summary (if enabled)
stochasticdict or NoneStochastic preprocessing summary
hydro_modelsdict or NoneHydro model summary
provenancedictBuild version and environment metadata
print(f"Converged: {result['converged']}")
print(f"Iterations: {result['iterations']}")
print(f"Lower bound: {result['lower_bound']:.2f}")
if result['gap_percent'] is not None:
print(f"Gap: {result['gap_percent']:.2f}%")
print(f"Output dir: {result['output_dir']}")
result = cobre.run.run(
"path/to/case",
output_dir="path/to/output", # default: case_dir/output
threads=4, # default: 1
skip_simulation=True, # default: False
)

Cobre writes results as Parquet files, which can be loaded directly with Polars or any Arrow-compatible library:

import polars as pl
# Convergence trajectory
convergence = pl.read_parquet("output/training/convergence.parquet")
print(convergence.head())
# Simulation costs (if simulation was enabled) — Hive-partitioned
costs = pl.read_parquet("output/simulation/costs/")
print(costs.describe())

For larger datasets, use the built-in Arrow loaders that avoid serialization overhead:

# Returns a pyarrow.Table (zero-copy)
convergence_table = cobre.results.load_convergence_arrow("output/")
simulation_tables = cobre.results.load_simulation_arrow("output/")
# Convert to Polars without copying
import polars as pl
df = pl.from_arrow(convergence_table)

A trained policy is a checkpoint of Benders cuts on disk (the on-disk AffinePiece layout is documented in the FlatBuffers Policy Schema). cobre.results.load_policy reads a checkpoint into plain Python dicts, and cobre.write_policy_checkpoint writes one from dicts of the same shape — so a loaded checkpoint round-trips: load, edit, write. See Policy Management for the cross-study boundary-cuts workflow this API supports.

cobre.results.load_policy(output_dir, policy_subdir="policy")

output_dir is the study’s output directory; load_policy reads the checkpoint from <output_dir>/<policy_subdir> (policy_subdir defaults to "policy"). It returns a dict with three top-level keys:

KeyTypeDescription
metadatadictformat_version, cobre_version, created_at, num_stages, graph_manifest, producer
stage_cutslist[dict]One entry per stage — see below
stage_baseslist[dict]One entry per saved LP basis: stage_id, iteration, column_status, row_status, num_cut_rows

Each stage_cuts entry carries:

KeyTypeDescription
stage_idintStage index
state_dimensionintLength every cut’s coefficients must have
capacityintCut-pool slot capacity
warm_start_countintCuts loaded from a previous artifact at run start
populated_countintNumber of populated cut slots
entity_manifestlist[dict]Per-slot entity markers — see below
cutslist[dict]Cut records — see below

Each entry in cuts carries cut_id, slot_index, iteration, forward_pass_index, intercept, coefficients, and is_active. The Python dict key for a cut’s id is cut_id (the FlatBuffers wire field is piece_id; cut_id is the record-level name used everywhere in this API).

entity_manifest is new this release: a list of per-slot markers, {entity_type, entity_id, subindex, was_active, delivery_date}, that let an externally-authored boundary-cut checkpoint round-trip through write_policy_checkpoint without losing slot identity. Its delivery marker is delivery_date — a YYYYMMDD calendar date — which replaced the former delivery_anchor (a month-integer).

Write a Checkpoint (write_policy_checkpoint)

Section titled “Write a Checkpoint (write_policy_checkpoint)”
write_policy_checkpoint(path, stage_cuts, metadata, stage_bases=None, stage_states=None, inflow_lag_depth=None)

stage_cuts and metadata mirror the load_policy shapes above (the dict shape is unchanged from earlier releases), so a checkpoint loaded from disk round-trips through Python without reshaping. inflow_lag_depth, when set to N > 0, reserves N canonical HydroInflowLag state slots per storage hydro and places each cut’s inflow_lag_coefficients at their (hydro, depth) positions (for authoring a boundary policy of a case with no autoregressive inflow model); absent or 0 it is byte-identical. write_policy_checkpoint writes manifest.bin, cuts/, basis/, and states/ directly under path — pass a policy/-style directory as path, then load with load_policy on its parent directory (which joins policy_subdir="policy" onto it).

The following example is copy-runnable and mirrors the shipped test fixture (crates/cobre-python/tests/test_write_policy_checkpoint.py): it authors a checkpoint from scratch, writes it, loads it back, appends a third cut, and writes the edited policy back to the same path.

import tempfile
import cobre
import cobre.results
parent = tempfile.mkdtemp()
stage_cuts = [
{
"stage_id": 0,
"state_dimension": 3,
"capacity": 10,
"cuts": [
{
"cut_id": 1,
"slot_index": 0,
"iteration": 1,
"forward_pass_index": 0,
"intercept": 42.0,
"coefficients": [1.0, 2.0, 3.0],
"is_active": True,
},
{
"cut_id": 2,
"slot_index": 1,
"iteration": 1,
"forward_pass_index": 1,
"intercept": 10.5,
"coefficients": [0.5, -1.5, 2.5],
"is_active": True,
},
],
}
]
metadata = {
"cobre_version": "0.13.0",
"created_at": "2026-07-30T00:00:00Z",
"num_stages": 1,
"producer": {
"completed_iterations": 5,
"final_lower_bound": 123.45,
"best_upper_bound": 130.0,
"max_iterations": 10,
"forward_passes": 4,
"warm_start_cuts": 0,
"warm_start_counts": [0],
"rng_seed": 42,
"total_visited_states": 0,
"training_block_mode": "parallel",
"training_block_mode_per_stage": [],
"cost_scale_factor": 2_500_000.0,
},
}
# Write to `<parent>/policy` — `load_policy` joins policy_subdir="policy" onto
# the directory it is given, so it is loaded from `parent` below.
cobre.write_policy_checkpoint(f"{parent}/policy", stage_cuts, metadata)
loaded = cobre.results.load_policy(parent)
assert loaded["metadata"]["format_version"] == 1
assert loaded["stage_cuts"][0]["populated_count"] == 2
# Edit: append a third cut. Its coefficients must match state_dimension (3).
cuts = loaded["stage_cuts"][0]["cuts"]
cuts.append(
{
"cut_id": 3,
"slot_index": 2,
"iteration": 2,
"forward_pass_index": 0,
"intercept": -5.0,
"coefficients": [4.0, -2.0, 0.0],
"is_active": True,
}
)
loaded["stage_cuts"][0]["populated_count"] = len(cuts)
# Write back: same round-trip shape, one more cut than before.
cobre.write_policy_checkpoint(
f"{parent}/policy", loaded["stage_cuts"], loaded["metadata"]
)

write_policy_checkpoint raises ValueError on two malformed-input shapes, naming the offending stage and cut (or stage) in the message.

A cut whose coefficients length disagrees with its stage’s state_dimension:

stage_cuts = [
{
"stage_id": 0,
"state_dimension": 3,
"capacity": 10,
"cuts": [
{
"cut_id": 1,
"slot_index": 0,
"iteration": 1,
"forward_pass_index": 0,
"intercept": 42.0,
"coefficients": [1.0, 2.0], # length 2, but state_dimension=3
"is_active": True,
},
],
}
]
cobre.write_policy_checkpoint("output/policy", stage_cuts, metadata)
# ValueError: stage 0 cut 1: coefficients has 2 entries, expected state_dimension=3

A stage_states entry whose flat data length disagrees with count * state_dimension:

stage_states = [
{
"stage_id": 0,
"state_dimension": 3,
"count": 2,
"data": [1.0, 2.0, 3.0], # length 3, but count * state_dimension = 6
}
]
cobre.write_policy_checkpoint(
"output/policy", stage_cuts, metadata, stage_states=stage_states
)
# ValueError: stage 0: states data has 3 entries, expected count*state_dimension=6
  • See the case directory format for input file specifications.
  • Explore the examples for ready-to-run cases.
  • Read the Jupyter quickstart notebook for a complete end-to-end workflow with visualization.