From f8446d9249b9eee43b7360c3550d4e6b985d7593 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 11 May 2026 15:09:55 +0200 Subject: [PATCH 01/25] docs: silence HiGHS console output in tutorial notebooks HiGHS prints a banner + progress lines to the Python REPL on every m.solve() call by default. In a tutorial that calls solve many times, this drowns the actual lesson in solver chatter. Pass output_flag=False (a HiGHS solver option forwarded via **solver_options) to suppress it. Touches the four notebooks where solver_name="highs" is the only solver invoked: - create-a-model.ipynb - create-a-model-with-coordinates.ipynb - manipulating-models.ipynb (9 solves) - transport-tutorial.ipynb Left alone: - infeasible-model.ipynb (uses Gurobi, kwarg is OutputFlag there; also showing solver feedback may be pedagogically relevant for infeasibility detection). - solve-on-remote.ipynb / solve-on-oetc.ipynb (remote handler manages its own logging). - piecewise-*.ipynb (already addressed in #677). Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/create-a-model-with-coordinates.ipynb | 2 +- examples/create-a-model.ipynb | 2 +- examples/manipulating-models.ipynb | 18 +++++++++--------- examples/transport-tutorial.ipynb | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/create-a-model-with-coordinates.ipynb b/examples/create-a-model-with-coordinates.ipynb index f2b12eed..e84c21b9 100644 --- a/examples/create-a-model-with-coordinates.ipynb +++ b/examples/create-a-model-with-coordinates.ipynb @@ -150,7 +150,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.solve(solver_name=\"highs\")" + "m.solve(solver_name=\"highs\", output_flag=False)" ] }, { diff --git a/examples/create-a-model.ipynb b/examples/create-a-model.ipynb index a158e0cf..b6fc9705 100644 --- a/examples/create-a-model.ipynb +++ b/examples/create-a-model.ipynb @@ -215,7 +215,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.solve(solver_name=\"highs\")" + "m.solve(solver_name=\"highs\", output_flag=False)" ] }, { diff --git a/examples/manipulating-models.ipynb b/examples/manipulating-models.ipynb index 81106ab3..6903386b 100644 --- a/examples/manipulating-models.ipynb +++ b/examples/manipulating-models.ipynb @@ -48,9 +48,9 @@ "con2 = m.add_constraints(5 * x + 2 * y >= 3 * factor, name=\"con2\")\n", "\n", "m.add_objective(x + 2 * y)\n", - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "\n", - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "sol = m.solution.to_dataframe()\n", "sol.plot(grid=True, ylabel=\"Optimal Value\")" ] @@ -95,7 +95,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "sol = m.solution.to_dataframe()\n", "sol.plot(grid=True, ylabel=\"Optimal Value\")" ] @@ -137,7 +137,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "sol = m.solution.to_dataframe()\n", "sol.plot(grid=True, ylabel=\"Optimal Value\")" ] @@ -190,7 +190,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "sol = m.solution.to_dataframe()\n", "sol.plot(grid=True, ylabel=\"Optimal Value\")" ] @@ -242,7 +242,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "sol = m.solution.to_dataframe()\n", "sol.plot(grid=True, ylabel=\"Optimal Value\")" ] @@ -276,7 +276,7 @@ "metadata": {}, "outputs": [], "source": [ - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "sol = m.solution.to_dataframe()\n", "sol.plot(grid=True, ylabel=\"Optimal Value\")" ] @@ -326,7 +326,7 @@ "# Penalize activation of z in the objective\n", "m.objective = x + 3 * y + 10 * z\n", "\n", - "m.solve(solver_name=\"highs\")" + "m.solve(solver_name=\"highs\", output_flag=False)" ] }, { @@ -346,7 +346,7 @@ "source": [ "m.variables.binaries.fix()\n", "m.variables.binaries.relax()\n", - "m.solve(solver_name=\"highs\")\n", + "m.solve(solver_name=\"highs\", output_flag=False)\n", "\n", "# Dual values are now available on the constraints\n", "m.constraints[\"con1\"].dual" diff --git a/examples/transport-tutorial.ipynb b/examples/transport-tutorial.ipynb index b42e67e8..cd5cdccd 100644 --- a/examples/transport-tutorial.ipynb +++ b/examples/transport-tutorial.ipynb @@ -417,7 +417,7 @@ "outputs": [], "source": [ "# Solve the model\n", - "m.solve(solver_name=\"highs\")" + "m.solve(solver_name=\"highs\", output_flag=False)" ] }, { From 6d61112ec588a2b56e8c664a0bcebea9a8c53273 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 11 May 2026 15:24:47 +0200 Subject: [PATCH 02/25] docs: silence HiGHS console output in piecewise tutorials too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the log-silencing scope to the two piecewise tutorials, which together call m.solve() nine times. Same transformation as the other notebooks — output_flag=False as a HiGHS-specific kwarg forwarded via **solver_options. Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/piecewise-inequality-bounds.ipynb | 40 ++++++++++----------- examples/piecewise-linear-constraints.ipynb | 16 ++++----- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/examples/piecewise-inequality-bounds.ipynb b/examples/piecewise-inequality-bounds.ipynb index a79c612d..101f28d5 100644 --- a/examples/piecewise-inequality-bounds.ipynb +++ b/examples/piecewise-inequality-bounds.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Piecewise inequalities \u2014 per-tuple sign\n", + "# Piecewise inequalities — per-tuple sign\n", "\n", "`add_piecewise_formulation` accepts an optional third tuple element, `\"<=\"` or `\">=\"`, that marks one expression as **bounded** by the piecewise curve instead of pinned to it:\n", "\n", @@ -15,17 +15,17 @@ ")\n", "```\n", "\n", - "This notebook walks through the geometry, the curvature \u00d7 sign matching that lets `method=\"auto\"` skip MIP machinery entirely, and the feasible regions produced by each method (LP, SOS2, incremental). For the formulation math see the [reference page](piecewise-linear-constraints).\n", + "This notebook walks through the geometry, the curvature × sign matching that lets `method=\"auto\"` skip MIP machinery entirely, and the feasible regions produced by each method (LP, SOS2, incremental). For the formulation math see the [reference page](piecewise-linear-constraints).\n", "\n", "## Key points\n", "\n", "| Tuple form | Behaviour |\n", "|---|---|\n", "| `(expr, breaks)` | Pinned: `expr` lies exactly on the curve. |\n", - "| `(expr, breaks, \"<=\")` | Bounded above: `expr \u2264 f(other tuples)`. |\n", - "| `(expr, breaks, \">=\")` | Bounded below: `expr \u2265 f(other tuples)`. |\n", + "| `(expr, breaks, \"<=\")` | Bounded above: `expr ≤ f(other tuples)`. |\n", + "| `(expr, breaks, \">=\")` | Bounded below: `expr ≥ f(other tuples)`. |\n", "\n", - "Currently at most one tuple may carry a non-equality sign, and 3+ tuples must all be equality. Multi-bounded and N\u22653 inequality cases aren't supported yet \u2014 if you have a concrete use case, please open an issue at https://github.com/PyPSA/linopy/issues so we can scope it properly." + "Currently at most one tuple may carry a non-equality sign, and 3+ tuples must all be equality. Multi-bounded and N≥3 inequality cases aren't supported yet — if you have a concrete use case, please open an issue at https://github.com/PyPSA/linopy/issues so we can scope it properly." ] }, { @@ -48,7 +48,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## Setup \u2014 a concave curve\n\nWe use a concave, monotonically increasing curve. With a tuple bounded `<=`, the LP method is applicable (concave + `<=` is a tight relaxation)." + "source": "## Setup — a concave curve\n\nWe use a concave, monotonically increasing curve. With a tuple bounded `<=`, the LP method is applicable (concave + `<=` is a tight relaxation)." }, { "cell_type": "code", @@ -79,11 +79,11 @@ "\n", "With one tuple bounded `<=` and our concave curve, the three methods give the **same** feasible region within `[x_0, x_n]`:\n", "\n", - "- **`method=\"lp\"`** \u2014 tangent lines + domain bounds. No auxiliary variables.\n", - "- **`method=\"sos2\"`** \u2014 lambdas + SOS2 + split link (pinned equality, bounded signed). Solver picks the piece.\n", - "- **`method=\"incremental\"`** \u2014 delta fractions + binaries + split link. Same mathematics, MIP encoding instead of SOS2.\n", + "- **`method=\"lp\"`** — tangent lines + domain bounds. No auxiliary variables.\n", + "- **`method=\"sos2\"`** — lambdas + SOS2 + split link (pinned equality, bounded signed). Solver picks the piece.\n", + "- **`method=\"incremental\"`** — delta fractions + binaries + split link. Same mathematics, MIP encoding instead of SOS2.\n", "\n", - "`method=\"auto\"` dispatches to `\"lp\"` whenever applicable \u2014 it's always preferable because it's pure LP.\n", + "`method=\"auto\"` dispatches to `\"lp\"` whenever applicable — it's always preferable because it's pure LP.\n", "\n", "Let's verify they produce the same solution at `power=15`." ] @@ -98,17 +98,17 @@ } }, "outputs": [], - "source": "def solve(method, power_val):\n m = linopy.Model()\n power = m.add_variables(lower=0, upper=30, name=\"power\")\n fuel = m.add_variables(lower=0, upper=40, name=\"fuel\")\n m.add_piecewise_formulation(\n (fuel, y_pts, \"<=\"), # bounded\n (power, x_pts), # pinned\n method=method,\n )\n m.add_constraints(power == power_val)\n m.add_objective(-fuel) # maximise fuel to push against the bound\n m.solve()\n return float(m.solution[\"fuel\"]), list(m.variables), list(m.constraints)\n\n\nfor method in [\"lp\", \"sos2\", \"incremental\"]:\n fuel_val, vars_, cons_ = solve(method, 15)\n print(f\"{method:12}: fuel={fuel_val:.2f} vars={vars_} cons={cons_}\")" + "source": "def solve(method, power_val):\n m = linopy.Model()\n power = m.add_variables(lower=0, upper=30, name=\"power\")\n fuel = m.add_variables(lower=0, upper=40, name=\"fuel\")\n m.add_piecewise_formulation(\n (fuel, y_pts, \"<=\"), # bounded\n (power, x_pts), # pinned\n method=method,\n )\n m.add_constraints(power == power_val)\n m.add_objective(-fuel) # maximise fuel to push against the bound\n m.solve(output_flag=False)\n return float(m.solution[\"fuel\"]), list(m.variables), list(m.constraints)\n\n\nfor method in [\"lp\", \"sos2\", \"incremental\"]:\n fuel_val, vars_, cons_ = solve(method, 15)\n print(f\"{method:12}: fuel={fuel_val:.2f} vars={vars_} cons={cons_}\")" }, { "cell_type": "markdown", "metadata": {}, - "source": "All three give `fuel=25` at `power=15` (which is `f(15)` exactly) \u2014 the math is equivalent. The LP method is strictly cheaper: no auxiliary variables, just three chord constraints and two domain bounds.\n\nThe SOS2 and incremental methods create lambdas (or deltas + binaries) and split the link into a pinned-equality constraint plus a signed bounded link \u2014 but the feasible region is the same." + "source": "All three give `fuel=25` at `power=15` (which is `f(15)` exactly) — the math is equivalent. The LP method is strictly cheaper: no auxiliary variables, just three chord constraints and two domain bounds.\n\nThe SOS2 and incremental methods create lambdas (or deltas + binaries) and split the link into a pinned-equality constraint plus a signed bounded link — but the feasible region is the same." }, { "cell_type": "markdown", "metadata": {}, - "source": "## Visualising the feasible region\n\nThe feasible region for `(power, fuel)` with `fuel` bounded `<=` is the **hypograph** of `f` restricted to the curve's x-domain:\n\n$$\\{ (x, y) : x_0 \\le x \\le x_n,\\ y \\le f(x) \\}$$\n\nWe colour green feasible points, red infeasible ones. Three test points:\n\n- `(15, 15)` \u2014 inside the curve, `15 \u2264 f(15)=25` \u2713\n- `(15, 25)` \u2014 on the curve \u2713\n- `(15, 29)` \u2014 above `f(15)`, should be infeasible \u2717\n- `(35, 20)` \u2014 power beyond domain, infeasible \u2717" + "source": "## Visualising the feasible region\n\nThe feasible region for `(power, fuel)` with `fuel` bounded `<=` is the **hypograph** of `f` restricted to the curve's x-domain:\n\n$$\\{ (x, y) : x_0 \\le x \\le x_n,\\ y \\le f(x) \\}$$\n\nWe colour green feasible points, red infeasible ones. Three test points:\n\n- `(15, 15)` — inside the curve, `15 ≤ f(15)=25` ✓\n- `(15, 25)` — on the curve ✓\n- `(15, 29)` — above `f(15)`, should be infeasible ✗\n- `(35, 20)` — power beyond domain, infeasible ✗" }, { "cell_type": "code", @@ -144,7 +144,7 @@ "ax.set(\n", " xlabel=\"power\",\n", " ylabel=\"fuel\",\n", - " title=\"sign='<=' feasible region \u2014 hypograph of f(x) on [x_0, x_n]\",\n", + " title=\"sign='<=' feasible region — hypograph of f(x) on [x_0, x_n]\",\n", ")\n", "ax.grid(alpha=0.3)\n", "ax.legend()\n", @@ -157,16 +157,16 @@ "source": [ "## When is LP the right choice?\n", "\n", - "`tangent_lines` imposes the **intersection** of chord inequalities. Whether that intersection matches the true hypograph/epigraph of `f` depends on the curvature \u00d7 sign combination:\n", + "`tangent_lines` imposes the **intersection** of chord inequalities. Whether that intersection matches the true hypograph/epigraph of `f` depends on the curvature × sign combination:\n", "\n", "| curvature | bounded `<=` | bounded `>=` |\n", "|-----------|--------------|--------------|\n", - "| **concave** | **hypograph (exact \u2713)** | **wrong region** \u2014 requires `y \u2265 max_k chord_k(x) > f(x)` |\n", - "| **convex** | **wrong region** \u2014 requires `y \u2264 min_k chord_k(x) < f(x)` | **epigraph (exact \u2713)** |\n", + "| **concave** | **hypograph (exact ✓)** | **wrong region** — requires `y ≥ max_k chord_k(x) > f(x)` |\n", + "| **convex** | **wrong region** — requires `y ≤ min_k chord_k(x) < f(x)` | **epigraph (exact ✓)** |\n", "| linear | exact | exact |\n", "| mixed (non-convex) | convex hull of `f` (wrong for exact hypograph) | concave hull of `f` (wrong for exact epigraph) |\n", "\n", - "In the \u2717 cases, tangent lines do **not** give a loose relaxation \u2014 they give a **strictly wrong feasible region** that rejects points satisfying the true constraint. Example: for a concave `f` with `y \u2265 f(x)`, the chord of any piece extrapolated over another piece's x-range lies *above* `f`, so `y \u2265 max_k chord_k(x)` forbids `y = f(x)` itself.\n", + "In the ✗ cases, tangent lines do **not** give a loose relaxation — they give a **strictly wrong feasible region** that rejects points satisfying the true constraint. Example: for a concave `f` with `y ≥ f(x)`, the chord of any piece extrapolated over another piece's x-range lies *above* `f`, so `y ≥ max_k chord_k(x)` forbids `y = f(x)` itself.\n", "\n", "`method=\"auto\"` dispatches to LP only in the two **exact** cases (concave + `<=` or convex + `>=`). For the other combinations it falls back to SOS2 or incremental, which encode the hypograph/epigraph exactly via discrete piece selection.\n", "\n", @@ -185,12 +185,12 @@ } }, "outputs": [], - "source": "# 1. Non-convex curve: auto falls back (LP relaxation would be loose)\nx_nc = [0, 10, 20, 30]\ny_nc = [0, 20, 10, 30] # slopes change sign \u2192 mixed convexity\n\nm1 = linopy.Model()\nx1 = m1.add_variables(lower=0, upper=30, name=\"x\")\ny1 = m1.add_variables(lower=0, upper=40, name=\"y\")\nf1 = m1.add_piecewise_formulation((y1, y_nc, \"<=\"), (x1, x_nc))\nprint(f\"non-convex + '<=' \u2192 {f1.method}\")\n\n# 2. Concave curve + sign='>=': LP would be loose \u2192 auto falls back to MIP\nx_cc = [0, 10, 20, 30]\ny_cc = [0, 20, 30, 35] # concave\n\nm2 = linopy.Model()\nx2 = m2.add_variables(lower=0, upper=30, name=\"x\")\ny2 = m2.add_variables(lower=0, upper=40, name=\"y\")\nf2 = m2.add_piecewise_formulation((y2, y_cc, \">=\"), (x2, x_cc))\nprint(f\"concave + '>=' \u2192 {f2.method}\")\n\n# 3. Explicit method=\"lp\" with mismatched curvature raises\ntry:\n m3 = linopy.Model()\n x3 = m3.add_variables(lower=0, upper=30, name=\"x\")\n y3 = m3.add_variables(lower=0, upper=40, name=\"y\")\n m3.add_piecewise_formulation((y3, y_cc, \">=\"), (x3, x_cc), method=\"lp\")\nexcept ValueError as e:\n print(f\"lp(concave, '>=') \u2192 raises: {e}\")" + "source": "# 1. Non-convex curve: auto falls back (LP relaxation would be loose)\nx_nc = [0, 10, 20, 30]\ny_nc = [0, 20, 10, 30] # slopes change sign → mixed convexity\n\nm1 = linopy.Model()\nx1 = m1.add_variables(lower=0, upper=30, name=\"x\")\ny1 = m1.add_variables(lower=0, upper=40, name=\"y\")\nf1 = m1.add_piecewise_formulation((y1, y_nc, \"<=\"), (x1, x_nc))\nprint(f\"non-convex + '<=' → {f1.method}\")\n\n# 2. Concave curve + sign='>=': LP would be loose → auto falls back to MIP\nx_cc = [0, 10, 20, 30]\ny_cc = [0, 20, 30, 35] # concave\n\nm2 = linopy.Model()\nx2 = m2.add_variables(lower=0, upper=30, name=\"x\")\ny2 = m2.add_variables(lower=0, upper=40, name=\"y\")\nf2 = m2.add_piecewise_formulation((y2, y_cc, \">=\"), (x2, x_cc))\nprint(f\"concave + '>=' → {f2.method}\")\n\n# 3. Explicit method=\"lp\" with mismatched curvature raises\ntry:\n m3 = linopy.Model()\n x3 = m3.add_variables(lower=0, upper=30, name=\"x\")\n y3 = m3.add_variables(lower=0, upper=40, name=\"y\")\n m3.add_piecewise_formulation((y3, y_cc, \">=\"), (x3, x_cc), method=\"lp\")\nexcept ValueError as e:\n print(f\"lp(concave, '>=') → raises: {e}\")" }, { "cell_type": "markdown", "metadata": {}, - "source": "## Summary\n\n- Default is all-equality: every tuple lies on the curve.\n- Append `\"<=\"` or `\">=\"` as a third tuple element to mark one expression as bounded by the curve.\n- `method=\"auto\"` picks the most efficient formulation: LP for matching-curvature 2-variable inequalities, otherwise SOS2 or incremental.\n- At most one tuple may be bounded; with 3+ tuples all must be equality. Multi-bounded and N\u22653 inequality use cases \u2014 please open an issue at https://github.com/PyPSA/linopy/issues so we can scope them." + "source": "## Summary\n\n- Default is all-equality: every tuple lies on the curve.\n- Append `\"<=\"` or `\">=\"` as a third tuple element to mark one expression as bounded by the curve.\n- `method=\"auto\"` picks the most efficient formulation: LP for matching-curvature 2-variable inequalities, otherwise SOS2 or incremental.\n- At most one tuple may be bounded; with 3+ tuples all must be equality. Multi-bounded and N≥3 inequality use cases — please open an issue at https://github.com/PyPSA/linopy/issues so we can scope them." } ], "metadata": { diff --git a/examples/piecewise-linear-constraints.ipynb b/examples/piecewise-linear-constraints.ipynb index 392ca8f1..fe1da9b8 100644 --- a/examples/piecewise-linear-constraints.ipynb +++ b/examples/piecewise-linear-constraints.ipynb @@ -81,7 +81,7 @@ "pwf = m.add_piecewise_formulation((power, x_pts), (fuel, y_pts))\n", "m.add_constraints(power == demand, name=\"demand\")\n", "m.add_objective(fuel.sum())\n", - "m.solve(reformulate_sos=\"auto\")\n", + "m.solve(reformulate_sos=\"auto\", output_flag=False)\n", "\n", "print(pwf) # inspect the auto-resolved method\n", "m.solution[[\"power\", \"fuel\"]].to_pandas()" @@ -136,7 +136,7 @@ " m.add_piecewise_formulation((power, x_pts), (fuel, y_pts), method=method)\n", " m.add_constraints(power == demand, name=\"demand\")\n", " m.add_objective(fuel.sum())\n", - " m.solve(reformulate_sos=\"auto\")\n", + " m.solve(reformulate_sos=\"auto\", output_flag=False)\n", " return m.solution[\"fuel\"].to_pandas()\n", "\n", "\n", @@ -175,7 +175,7 @@ ")\n", "m.add_constraints(power + backup == xr.DataArray([15, 60, 75], coords=[time]))\n", "m.add_objective(cost.sum() + 10 * backup.sum())\n", - "m.solve(reformulate_sos=\"auto\")\n", + "m.solve(reformulate_sos=\"auto\", output_flag=False)\n", "m.solution[[\"power\", \"cost\", \"backup\"]].to_pandas()" ] }, @@ -225,7 +225,7 @@ ")\n", "m.add_constraints(power == xr.DataArray([30, 80, 100], coords=[time]))\n", "m.add_objective(-fuel.sum()) # push fuel against the bound\n", - "m.solve(reformulate_sos=\"auto\")\n", + "m.solve(reformulate_sos=\"auto\", output_flag=False)\n", "\n", "print(f\"resolved method={pwf.method}, curvature={pwf.convexity}\")\n", "m.solution[[\"power\", \"fuel\"]].to_pandas()" @@ -282,7 +282,7 @@ "# demand below p_min at t=1 — commit must be 0 and backup covers it\n", "m.add_constraints(power + backup == xr.DataArray([15, 80, 40], coords=[time]))\n", "m.add_objective(fuel.sum() + 50 * commit.sum() + 200 * backup.sum())\n", - "m.solve(reformulate_sos=\"auto\")\n", + "m.solve(reformulate_sos=\"auto\", output_flag=False)\n", "m.solution[[\"commit\", \"power\", \"fuel\", \"backup\"]].to_pandas()" ] }, @@ -330,7 +330,7 @@ ")\n", "m.add_constraints(fuel == xr.DataArray([20, 100, 160], coords=[time]))\n", "m.add_objective(power.sum())\n", - "m.solve(reformulate_sos=\"auto\")\n", + "m.solve(reformulate_sos=\"auto\", output_flag=False)\n", "m.solution[[\"power\", \"fuel\", \"heat\"]].to_pandas().round(2)" ] }, @@ -388,7 +388,7 @@ "m.add_piecewise_formulation((power, x_gen), (fuel, y_gen))\n", "m.add_constraints(power.sum(\"gen\") == xr.DataArray([80, 120, 50], coords=[time]))\n", "m.add_objective(fuel.sum())\n", - "m.solve(reformulate_sos=\"auto\")\n", + "m.solve(reformulate_sos=\"auto\", output_flag=False)\n", "m.solution[[\"power\", \"fuel\"]].to_dataframe()" ] }, @@ -417,7 +417,7 @@ ")\n", "m.add_constraints(power == demand, name=\"demand\")\n", "m.add_objective(fuel.sum())\n", - "m.solve(reformulate_sos=\"auto\")\n", + "m.solve(reformulate_sos=\"auto\", output_flag=False)\n", "\n", "m.solution[[\"power\", \"fuel\"]].to_pandas()" ] From ae018a38eb8d6e7aeb819c27c50a49f7ec571c89 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 09:44:17 +0200 Subject: [PATCH 03/25] docs: fix broken toctree, refresh API reference, and clean up references - Add doc/coordinate-alignment.nblink so the index.rst toctree entry resolves to examples/coordinate-alignment.ipynb. - Update api.rst to match the current public API: add the missing solver classes (COPT, Knitro, MindOpt, PIPS, cuPDLPx), expose top-level helpers (align, merge, options, EvolvingAPIWarning, PerformanceWarning), add the missing Model methods (add_sos_constraints, reformulate_sos_constraints, compute_infeasibilities, format_infeasibilities), add Variable methods (to_linexpr, fix/unfix, relax/unrelax), add sections for QuadraticExpression, Objective, and RemoteHandler, remove the duplicate Variables.integers, and fix the "hook" -> "hood" typo. - contributing.rst: replace stale Black reference with ruff, correct the nblink example (proper JSON, right path, fixed RST indentation that was breaking pygments), and use pre-commit run --all-files. - benchmark.rst: fix the rendered objective, which read as a product of two variables; corrected to the actual linear benchmark (2x + y with x - y >= i-1, matching benchmark_linopy.py). - prerequisites.rst: add SCIP, give MOSEK a description, drop the dangling "-" after MindOpt, remove the outdated HiGHS-platforms claim, and clarify what the [solvers] extra actually pulls in. - conf.py + index.rst: bump copyright to 2026 and fix the "contnuous" typo on the landing page. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 58 +++++++++++++++++++++++++++++++-- doc/benchmark.rst | 4 +-- doc/conf.py | 2 +- doc/contributing.rst | 29 +++++++++-------- doc/coordinate-alignment.nblink | 3 ++ doc/index.rst | 4 +-- doc/prerequisites.rst | 13 +++++--- 7 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 doc/coordinate-alignment.nblink diff --git a/doc/api.rst b/doc/api.rst index f817d866..d4c8ca95 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -18,6 +18,7 @@ Creating a model model.Model.add_variables model.Model.add_constraints model.Model.add_objective + model.Model.add_sos_constraints model.Model.add_piecewise_formulation piecewise.PiecewiseFormulation piecewise.Slopes @@ -26,10 +27,26 @@ Creating a model piecewise.tangent_lines model.Model.linexpr model.Model.remove_constraints + model.Model.reformulate_sos_constraints + model.Model.compute_infeasibilities + model.Model.format_infeasibilities model.Model.copy -Classes under the hook +Top-level helpers +================= + +.. autosummary:: + :toctree: generated/ + + align + merge + options + EvolvingAPIWarning + PerformanceWarning + + +Classes under the hood ====================== Variable @@ -46,7 +63,11 @@ Variable variables.Variable.sum variables.Variable.where variables.Variable.sanitize - variables.Variables + variables.Variable.to_linexpr + variables.Variable.fix + variables.Variable.unfix + variables.Variable.relax + variables.Variable.unrelax variables.ScalarVariable Variables @@ -61,7 +82,6 @@ Variables variables.Variables.add variables.Variables.remove variables.Variables.continuous - variables.Variables.integers variables.Variables.binaries variables.Variables.integers variables.Variables.flat @@ -82,6 +102,24 @@ LinearExpressions expressions.merge expressions.ScalarLinearExpression + +QuadraticExpressions +-------------------- + +.. autosummary:: + :toctree: generated/ + + expressions.QuadraticExpression + + +Objective +--------- + +.. autosummary:: + :toctree: generated/ + + objective.Objective + Constraint ---------- @@ -167,13 +205,27 @@ Solvers :toctree: generated/ solvers.CBC + solvers.COPT solvers.Cplex solvers.GLPK solvers.Gurobi solvers.Highs + solvers.Knitro + solvers.MindOpt solvers.Mosek + solvers.PIPS solvers.SCIP solvers.Xpress + solvers.cuPDLPx + + +Remote solving +============== + +.. autosummary:: + :toctree: generated/ + + remote.RemoteHandler Solving diff --git a/doc/benchmark.rst b/doc/benchmark.rst index da2a98e9..db9ec407 100644 --- a/doc/benchmark.rst +++ b/doc/benchmark.rst @@ -13,9 +13,9 @@ for large problems. The following figure shows the memory usage and speed for so .. math:: - & \min \;\; \sum_{i,j} 2 x_{i,j} \; y_{i,j} \\ + & \min \;\; \sum_{i,j} 2 x_{i,j} + y_{i,j} \\ s.t. & \\ - & x_{i,j} - y_{i,j} \; \ge \; i \qquad \forall \; i,j \in \{1,...,N\} \\ + & x_{i,j} - y_{i,j} \; \ge \; i-1 \qquad \forall \; i,j \in \{1,...,N\} \\ & x_{i,j} + y_{i,j} \; \ge \; 0 \qquad \forall \; i,j \in \{1,...,N\} diff --git a/doc/conf.py b/doc/conf.py index 5525d366..54a3ffab 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -18,7 +18,7 @@ # -- Project information ----------------------------------------------------- project = "linopy" -copyright = "2021, Fabian Hofmann" +copyright = "2021-2026, Fabian Hofmann" author = "Fabian Hofmann" # The full version, including alpha/beta/rc tags diff --git a/doc/contributing.rst b/doc/contributing.rst index 120683cb..4bb9b60a 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -13,14 +13,13 @@ You are invited to submit pull requests / issues to our Development Setup ================= -For linting, formatting and checking your code contributions -against our guidelines (e.g. we use `Black `_ as code style -and use `pre-commit `_: +For linting and formatting, we use `ruff `_ +and run it via `pre-commit `_: 1. Installation ``conda install -c conda-forge pre-commit`` or ``pip install pre-commit`` 2. Usage: * To automatically activate ``pre-commit`` on every ``git commit``: Run ``pre-commit install`` - * To manually run it: ``pre-commit run --all`` + * To manually run it: ``pre-commit run --all-files`` Running Tests ============= @@ -122,23 +121,25 @@ Then for every notebook: e.g. `Edit -> Clear all output` in JupyterLab. 3. Provide a link to the documentation: - Include a file ``foo.nblink`` located in ``doc/examples/foo.nblink`` + Include a file ``foo.nblink`` located in ``doc/foo.nblink`` with this content - .. code-block: - { - 'path' : '../../examples/foo.ipynb' - } + .. code-block:: json + + { + "path": "../examples/foo.ipynb" + } + + Adjust the path for your file's name. + This ``nblink`` allows us to link your notebook into the documentation. - Adjust the path for your file's name. - This ``nblink`` allows us to link your notebook into the documentation. 4. Link your file in the documentation: Either - * Include your ``examples/foo.nblink`` directly into one of - the documentations toctrees; or - * Tell us where in the documentation you want your example to show up + * Include your ``foo.nblink`` directly into one of + the documentation's toctrees; or + * Tell us where in the documentation you want your example to show up 5. Commit your changes. If the precommit hook you installed above kicks in, confirm diff --git a/doc/coordinate-alignment.nblink b/doc/coordinate-alignment.nblink new file mode 100644 index 00000000..ef588b91 --- /dev/null +++ b/doc/coordinate-alignment.nblink @@ -0,0 +1,3 @@ +{ + "path": "../examples/coordinate-alignment.ipynb" +} diff --git a/doc/index.rst b/doc/index.rst index a4d34ce7..c8906b74 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -23,7 +23,7 @@ Main features `xarray `__ which allows for many flexible data-handling features: -- Define (arrays of) contnuous or binary variables with +- Define (arrays of) continuous or binary variables with **coordinates**, e.g. time, consumers, etc. - Apply **arithmetic operations** on the variables like adding, subtracting, multiplying with all the **broadcasting** potentials of @@ -81,7 +81,7 @@ A BibTeX entry for LaTeX users is License ------- -Copyright 2021-2023 Fabian Hofmann +Copyright 2021-2026 Fabian Hofmann This package is published under MIT license. diff --git a/doc/prerequisites.rst b/doc/prerequisites.rst index 97d51296..afb0e8a7 100644 --- a/doc/prerequisites.rst +++ b/doc/prerequisites.rst @@ -36,21 +36,26 @@ CPU-based solvers - `Cbc `__ - open source, free, fast - `GLPK `__ - open source, free, not very fast - `HiGHS `__ - open source, free, fast +- `SCIP `__ - open source (Apache-2.0), fast MIP solver - `Gurobi `__ - closed source, commercial, very fast - `Xpress `__ - closed source, commercial, very fast (GPU acceleration available in v9.8+) - `Cplex `__ - closed source, commercial, very fast -- `MOSEK `__ -- `MindOpt `__ - +- `MOSEK `__ - closed source, commercial, strong on conic/QP +- `MindOpt `__ - closed source, commercial - `COPT `__ - closed source, commercial, very fast -For a subset of the solvers, Linopy provides a wrapper. +The ``linopy[solvers]`` extra installs the Python clients for the +supported solvers (HiGHS, SCIP, Gurobi, CPLEX, MOSEK, MindOpt, COPT, +Xpress, Knitro). For the commercial ones a separate license is still +required: .. code:: bash pip install linopy[solvers] -We recommend to install the HiGHS solver if possible, which is free and open source but not yet available on all platforms. +We recommend installing the HiGHS solver, which is free, open source, and +fast across a wide range of problem sizes: .. code:: bash From fe05bb1d22a0dfb6a2364fd49dcfbc33280d54ef Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 09:49:26 +0200 Subject: [PATCH 04/25] =?UTF-8?q?docs:=20reorganize=20toctree=20into=20bas?= =?UTF-8?q?ic=E2=86=92advanced=20sections=20and=20rewrite=20user-guide=20l?= =?UTF-8?q?anding=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the previously flat 16-item User Guide bag into focused sections so users move from install to advanced features in a clear order: Getting Started → User Guide (core building blocks) → Advanced Features → Tutorials → Solving → Troubleshooting → Benchmarking → Reference Rewrite user-guide.rst from a one-paragraph stub into a roadmap landing page: it groups the core notebooks (variables, expressions, constraints, coordinate alignment, manipulating models) into a recommended reading order and points outward to advanced topics, tutorials, remote/GPU solving, and troubleshooting. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/index.rst | 33 +++++++++++++++++++++---- doc/user-guide.rst | 61 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index c8906b74..3f56c60c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -112,18 +112,41 @@ This package is published under MIT license. creating-expressions creating-constraints coordinate-alignment + manipulating-models + +.. toctree:: + :hidden: + :maxdepth: 2 + :caption: Advanced Features + sos-constraints piecewise-linear-constraints - manipulating-models testing-framework + +.. toctree:: + :hidden: + :maxdepth: 2 + :caption: Tutorials + transport-tutorial - infeasible-model + migrating-from-pyomo + +.. toctree:: + :hidden: + :maxdepth: 2 + :caption: Solving + solve-on-remote solve-on-oetc gpu-acceleration - migrating-from-pyomo - gurobi-double-logging +.. toctree:: + :hidden: + :maxdepth: 2 + :caption: Troubleshooting + + infeasible-model + gurobi-double-logging .. toctree:: :hidden: @@ -136,7 +159,7 @@ This package is published under MIT license. .. toctree:: :hidden: :maxdepth: 2 - :caption: References + :caption: Reference api release_notes diff --git a/doc/user-guide.rst b/doc/user-guide.rst index 494ac240..f52275b7 100644 --- a/doc/user-guide.rst +++ b/doc/user-guide.rst @@ -1,12 +1,61 @@ .. _user-guide: -Overview -======== +User Guide +========== -Welcome to the User Guide for Linopy. This guide is designed to help you understand and effectively use Linopy's features to solve your optimization problems, complementing the ``Getting Started`` section. +This guide takes you from a working install to building, modifying, and +solving real models. Each page is a runnable Jupyter notebook — read it +top to bottom, or use it as a reference once you know what you're +looking for. -In the following sections, we will take a closer look at how to create and manipulate models, variables, and constraints, and how to solve these models to find optimal solutions. Each section includes detailed explanations and code examples to help you understand the concepts and apply them to your own projects. +If you haven't yet, run through the :doc:`Getting Started ` +section first: it installs linopy and a solver, then walks through a +first scalar model and the move to N-D variables with coordinates. -If you are completely new to Linopy, consider to first have a look at the `Getting Started` section. -Let's get started! +Core building blocks +-------------------- + +The four notebooks below cover the model object users interact with +most. Read them in order the first time; come back to them whenever +you're unsure what a particular operator or argument does. + +- :doc:`creating-variables` — declaring decision variables, with bounds + and coordinates. Continuous, integer, binary, and semi-continuous. +- :doc:`creating-expressions` — combining variables into linear (and + quadratic) expressions; arithmetic, broadcasting, ``sum``, + ``groupby``, ``rolling``, ``where``. +- :doc:`creating-constraints` — turning expressions into ``≤`` / ``≥`` + / ``==`` constraints, and the ``CSRConstraint`` memory-efficient + alternative. +- :doc:`coordinate-alignment` — how linopy lines up operands that live + on different coordinates, and how to control it with ``join``. + +After these four you can build any LP/MIP/QP linopy supports. + + +Working with an existing model +------------------------------ + +Once you've built a model, you'll often want to inspect it, change a +bound, swap a constraint, or copy it for what-if analysis. + +- :doc:`manipulating-models` — modifying or removing variables and + constraints in place; ``Model.copy()``; ``fix`` / ``relax`` for + variables. + + +Where to go next +---------------- + +- **Advanced features** — :doc:`sos-constraints`, + :doc:`piecewise-linear-constraints`, and the + :doc:`testing-framework` for asserting structural properties of a + model. +- **Tutorials** — end-to-end problem walkthroughs: + :doc:`transport-tutorial`, :doc:`migrating-from-pyomo`. +- **Solving** — :doc:`solve-on-remote` (SSH), + :doc:`solve-on-oetc` (OET Cloud), :doc:`gpu-acceleration` (cuPDLPx). +- **Troubleshooting** — :doc:`infeasible-model` (diagnosing infeasible + problems), :doc:`gurobi-double-logging` (and other solver quirks). +- **Reference** — the full :doc:`api` listing. From ce7c7b16ad3d149bc4fb1790b51bd3777cf5a0d6 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 09:59:55 +0200 Subject: [PATCH 05/25] docs: fix broken admonitions in notebooks and configure intersphinx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three RST admonitions in notebook markdown cells had a blank line between the directive and a tab-indented body. CommonMark then ate the indented block as a code block, so nbsphinx saw an empty directive ("Content block expected for the …" build errors). Fix by removing the blank line and using a 3-space indent — the convention already used by the working admonitions in the same notebooks (e.g. creating-variables cell `..note::\n Since we did not …`). - creating-expressions.ipynb cell 13: restored `.. important::` on coordinate-determination semantics. - creating-expressions.ipynb cell 17: restored `.. tip::` pointing at `.add/.sub/.mul/.div` with the `join` parameter and the coordinate-alignment guide. - creating-variables.ipynb cell 42: re-added a corrected `.. note::` on `coords=` being ignored when supplied alongside pandas objects. Dropped the stale "New in version 0.3.6" framing and the broken "is ignored is passed" wording from the original. conf.py: configure intersphinx_mapping for python, numpy, pandas, xarray, scipy, and dask. The intersphinx extension was already loaded but had no mapping, so cross-references like :class:`xarray.DataArray` or :func:`numpy.ndarray` were silently unresolved. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/conf.py | 10 ++++++++++ examples/creating-expressions.ipynb | 10 ++++++++-- examples/creating-variables.ipynb | 13 +++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 54a3ffab..e28bde83 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -72,6 +72,16 @@ autosummary_generate = True autodoc_typehints = "none" +# Intersphinx — resolve :class:`xarray.DataArray`, :func:`numpy.ndarray`, etc. +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "numpy": ("https://numpy.org/doc/stable", None), + "pandas": ("https://pandas.pydata.org/docs", None), + "xarray": ("https://docs.xarray.dev/en/stable", None), + "scipy": ("https://docs.scipy.org/doc/scipy", None), + "dask": ("https://docs.dask.org/en/stable", None), +} + # Napoleon configurations napoleon_google_docstring = False diff --git a/examples/creating-expressions.ipynb b/examples/creating-expressions.ipynb index 1d808b07..d0bf0db4 100644 --- a/examples/creating-expressions.ipynb +++ b/examples/creating-expressions.ipynb @@ -160,7 +160,10 @@ "cell_type": "markdown", "id": "f7578221", "metadata": {}, - "source": ".. important::\n\n\tWhen combining variables or expression with dimensions of the same name and size, the first object will determine the coordinates of the resulting expression. For example:" + "source": [ + ".. important::\n", + " When combining variables or expressions with dimensions of the same name and size, the first object determines the coordinates of the resulting expression. For example:" + ] }, { "cell_type": "code", @@ -196,7 +199,10 @@ { "cell_type": "markdown", "id": "a8xsfdqrcrn", - "source": ".. tip::\n\n\tFor explicit control over how coordinates are aligned during arithmetic, use the `.add()`, `.sub()`, `.mul()`, and `.div()` methods with a ``join`` parameter (``\"inner\"``, ``\"outer\"``, ``\"left\"``, ``\"right\"``). See the :doc:`coordinate-alignment` guide for details.", + "source": [ + ".. tip::\n", + " For explicit control over how coordinates are aligned during arithmetic, use the ``.add()``, ``.sub()``, ``.mul()``, and ``.div()`` methods with a ``join`` parameter (``\"inner\"``, ``\"outer\"``, ``\"left\"``, ``\"right\"``). See the :doc:`coordinate-alignment` guide for details." + ], "metadata": {} }, { diff --git a/examples/creating-variables.ipynb b/examples/creating-variables.ipynb index 8e879348..9179a31a 100644 --- a/examples/creating-variables.ipynb +++ b/examples/creating-variables.ipynb @@ -31,6 +31,12 @@ "m = Model()" ] }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "", + "id": "46c4f2824a2ed8aa" + }, { "cell_type": "markdown", "id": "6c6420a7", @@ -465,9 +471,12 @@ }, { "cell_type": "markdown", - "id": "77e264e2", + "id": "e8249281", "metadata": {}, - "source": ".. important::\n\n **New in version 0.3.6**\n\n As pandas objects always have indexes, the `coords` argument is not required and is ignored is passed. Before, it was used to overwrite the indexes of the pandas objects. A warning is raised if `coords` is passed and if these are not aligned with the pandas object." + "source": [ + ".. note::\n", + " As pandas objects already carry indexes, the ``coords`` argument is ignored when supplied alongside them. A warning is raised if a ``coords`` value is passed that does not align with the pandas object." + ] }, { "cell_type": "code", From e44cbd37f2168e8ff1eb4d03948cb3aabf2f1e07 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 10:00:54 +0200 Subject: [PATCH 06/25] docs: downgrade coordinate-determination admonition from important to note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The coordinate-determination behaviour is regular alignment semantics, not a sharp pitfall — note is the right level. Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/creating-expressions.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/creating-expressions.ipynb b/examples/creating-expressions.ipynb index d0bf0db4..6f98e6bb 100644 --- a/examples/creating-expressions.ipynb +++ b/examples/creating-expressions.ipynb @@ -161,7 +161,7 @@ "id": "f7578221", "metadata": {}, "source": [ - ".. important::\n", + ".. note::\n", " When combining variables or expressions with dimensions of the same name and size, the first object determines the coordinates of the resulting expression. For example:" ] }, From c5f9d4d4fca97fdd2b636735e695ae7ae06e0420 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 10:03:08 +0200 Subject: [PATCH 07/25] Revert "docs: downgrade coordinate-determination admonition from important to note" This reverts commit e44cbd3. Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/creating-expressions.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/creating-expressions.ipynb b/examples/creating-expressions.ipynb index 6f98e6bb..d0bf0db4 100644 --- a/examples/creating-expressions.ipynb +++ b/examples/creating-expressions.ipynb @@ -161,7 +161,7 @@ "id": "f7578221", "metadata": {}, "source": [ - ".. note::\n", + ".. important::\n", " When combining variables or expressions with dimensions of the same name and size, the first object determines the coordinates of the resulting expression. For example:" ] }, From 95b7e55297f8180f6f8a6ae0c4cb911f88d780de Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 10:06:57 +0200 Subject: [PATCH 08/25] ci: empty commit to retrigger CI Co-Authored-By: Claude Opus 4.7 (1M context) From 94336ac30f4897d5c809bbc999a59c7b420158e2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 10:19:31 +0200 Subject: [PATCH 09/25] docs: rename toctree captions and reorder Examples below Solving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Benchmarking → Comparisons. The two children (performance and syntax) both compare linopy with JuMP and Pyomo, not "benchmark" in the regression-tracking sense. - benchmark.rst H1: Benchmarks → Performance comparison, so the page title matches the section framing (syntax.rst was already "Syntax comparison"). - Tutorials → Examples. The contents are end-to-end worked problems and a migration guide; "Tutorials" overloads with the rest of the docs (every notebook is tutorial-style). - Move Examples below Solving so the section flow is Getting Started → User Guide → Advanced Features → Solving → Examples → Troubleshooting → Comparisons → Reference. The user now knows how to both build and run a model before being handed a full worked problem. Update the user-guide.rst cross-reference accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/benchmark.rst | 4 ++-- doc/index.rst | 16 ++++++++-------- doc/user-guide.rst | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/benchmark.rst b/doc/benchmark.rst index db9ec407..f56d5dff 100644 --- a/doc/benchmark.rst +++ b/doc/benchmark.rst @@ -1,7 +1,7 @@ .. _benchmark: -Benchmarks -========== +Performance comparison +====================== Linopy's performance scales well with the problem size. Its overall speed is comparable with the famous `JuMP `_ package written in `Julia `_. It even outperforms `JuMP` in total memory efficiency when it comes to large models. Compared to `Pyomo `_, the common optimization package in python, one can expect diff --git a/doc/index.rst b/doc/index.rst index 3f56c60c..4942c82f 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -126,19 +126,19 @@ This package is published under MIT license. .. toctree:: :hidden: :maxdepth: 2 - :caption: Tutorials + :caption: Solving - transport-tutorial - migrating-from-pyomo + solve-on-remote + solve-on-oetc + gpu-acceleration .. toctree:: :hidden: :maxdepth: 2 - :caption: Solving + :caption: Examples - solve-on-remote - solve-on-oetc - gpu-acceleration + transport-tutorial + migrating-from-pyomo .. toctree:: :hidden: @@ -151,7 +151,7 @@ This package is published under MIT license. .. toctree:: :hidden: :maxdepth: 2 - :caption: Benchmarking + :caption: Comparisons benchmark syntax diff --git a/doc/user-guide.rst b/doc/user-guide.rst index f52275b7..a2da0509 100644 --- a/doc/user-guide.rst +++ b/doc/user-guide.rst @@ -52,7 +52,7 @@ Where to go next :doc:`piecewise-linear-constraints`, and the :doc:`testing-framework` for asserting structural properties of a model. -- **Tutorials** — end-to-end problem walkthroughs: +- **Examples** — end-to-end problem walkthroughs: :doc:`transport-tutorial`, :doc:`migrating-from-pyomo`. - **Solving** — :doc:`solve-on-remote` (SSH), :doc:`solve-on-oetc` (OET Cloud), :doc:`gpu-acceleration` (cuPDLPx). From 15aa6a454160e3fcaadb791435b8d507541a7c5d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 10:20:17 +0200 Subject: [PATCH 10/25] docs: move Examples directly under User Guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sit Examples right after the core building blocks so users move from "I learned the mechanics" to "show me a complete worked problem" before tackling Advanced Features and Solving. Final sidebar order: Getting Started → User Guide → Examples → Advanced Features → Solving → Troubleshooting → Comparisons → Reference Reorder the corresponding bullets in user-guide.rst so the prose matches the sidebar. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/index.rst | 16 ++++++++-------- doc/user-guide.rst | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 4942c82f..ea61747c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -114,6 +114,14 @@ This package is published under MIT license. coordinate-alignment manipulating-models +.. toctree:: + :hidden: + :maxdepth: 2 + :caption: Examples + + transport-tutorial + migrating-from-pyomo + .. toctree:: :hidden: :maxdepth: 2 @@ -132,14 +140,6 @@ This package is published under MIT license. solve-on-oetc gpu-acceleration -.. toctree:: - :hidden: - :maxdepth: 2 - :caption: Examples - - transport-tutorial - migrating-from-pyomo - .. toctree:: :hidden: :maxdepth: 2 diff --git a/doc/user-guide.rst b/doc/user-guide.rst index a2da0509..056d0ccf 100644 --- a/doc/user-guide.rst +++ b/doc/user-guide.rst @@ -48,12 +48,12 @@ bound, swap a constraint, or copy it for what-if analysis. Where to go next ---------------- +- **Examples** — end-to-end problem walkthroughs: + :doc:`transport-tutorial`, :doc:`migrating-from-pyomo`. - **Advanced features** — :doc:`sos-constraints`, :doc:`piecewise-linear-constraints`, and the :doc:`testing-framework` for asserting structural properties of a model. -- **Examples** — end-to-end problem walkthroughs: - :doc:`transport-tutorial`, :doc:`migrating-from-pyomo`. - **Solving** — :doc:`solve-on-remote` (SSH), :doc:`solve-on-oetc` (OET Cloud), :doc:`gpu-acceleration` (cuPDLPx). - **Troubleshooting** — :doc:`infeasible-model` (diagnosing infeasible From 47db4be4f258b105220083f0585722477806c71b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 10:32:19 +0200 Subject: [PATCH 11/25] =?UTF-8?q?docs:=20bridge=20Getting=20Started=20?= =?UTF-8?q?=E2=86=92=20User=20Guide=20and=20rename=20landing=20page=20H1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small fixes that tighten the boundary between the two top sections without changing what each one covers: - Append a "Where to next" cell to create-a-model-with-coordinates, pointing into the five User Guide notebooks. The coordinates notebook is deliberately a shallow tour, so the handoff is now explicit rather than implied by toctree order. - Rewrite the user-guide.rst opening to acknowledge what the reader just did in Getting Started, framing the User Guide as the depth pass on the same surface. - Rename the user-guide.rst H1 from "User Guide" to "Overview" so the sidebar entry under the "User Guide" caption no longer duplicates the caption name. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/user-guide.rst | 19 +++++++++---------- .../create-a-model-with-coordinates.ipynb | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/doc/user-guide.rst b/doc/user-guide.rst index 056d0ccf..8b7ee5bd 100644 --- a/doc/user-guide.rst +++ b/doc/user-guide.rst @@ -1,22 +1,21 @@ .. _user-guide: -User Guide -========== +Overview +======== -This guide takes you from a working install to building, modifying, and -solving real models. Each page is a runnable Jupyter notebook — read it -top to bottom, or use it as a reference once you know what you're -looking for. +In :doc:`Getting Started ` you installed linopy, built +a first scalar model, and saw N-D variables on coordinates. The User +Guide reopens each of those pieces in depth and adds the rest of the +modelling surface. -If you haven't yet, run through the :doc:`Getting Started ` -section first: it installs linopy and a solver, then walks through a -first scalar model and the move to N-D variables with coordinates. +Each page is a runnable Jupyter notebook — read it top to bottom, or +use it as a reference once you know what you're looking for. Core building blocks -------------------- -The four notebooks below cover the model object users interact with +The four notebooks below cover the model object you'll interact with most. Read them in order the first time; come back to them whenever you're unsure what a particular operator or argument does. diff --git a/examples/create-a-model-with-coordinates.ipynb b/examples/create-a-model-with-coordinates.ipynb index e84c21b9..01840863 100644 --- a/examples/create-a-model-with-coordinates.ipynb +++ b/examples/create-a-model-with-coordinates.ipynb @@ -178,6 +178,22 @@ "source": [ "Alright! Now you learned how to set up linopy variables and expressions with coordinates. In the User Guide, which follows, we are going to see, how the representation of variables with coordinates allows us to formulate more advanced operations." ] + }, + { + "cell_type": "markdown", + "id": "4db583af", + "metadata": {}, + "source": [ + "## Where to next\n", + "\n", + "You've now seen the full path from declaring variables on coordinates to solving the model. The :doc:`User Guide ` reopens each piece in depth:\n", + "\n", + "- :doc:`creating-variables` for the full surface of ``add_variables`` (types, dimensions, initialization from numpy / pandas, masking).\n", + "- :doc:`creating-expressions` for arithmetic, broadcasting, ``.sum``, ``.groupby``, ``.rolling``, ``.where``.\n", + "- :doc:`creating-constraints` for equality / inequality constraints across dimensions.\n", + "- :doc:`coordinate-alignment` for the alignment rules that quietly drove the broadcasts you just saw.\n", + "- :doc:`manipulating-models` once you want to tweak an existing model in place." + ] } ], "metadata": { From 110ad81b991bd810e597a5afe2a5e3e8bba4a82e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 10:51:27 +0200 Subject: [PATCH 12/25] docs: fix malformed first cell of solve-on-remote notebook Cell 0 had two lines that contained literal "\n" sequences as text (plus a stray trailing double-quote), so the markdown rendered as one long line and Sphinx emitted four warnings about "SSH:nbsphinx-math" file-not-found and inline interpreted text. Rewrite the cell with proper newline-separated lines and turn the inline reference to solve-on-oetc.ipynb into a :doc: cross-reference. Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/solve-on-remote.ipynb | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/examples/solve-on-remote.ipynb b/examples/solve-on-remote.ipynb index 4e2a1b13..62267e4c 100644 --- a/examples/solve-on-remote.ipynb +++ b/examples/solve-on-remote.ipynb @@ -5,13 +5,38 @@ "id": "4db583af", "metadata": {}, "source": [ - "# Remote Solving with SSH", + "# Remote Solving with SSH\n", "\n", - "This example demonstrates how linopy can solve optimization models on remote machines using SSH connections. This is one of two remote solving options available in linopy", + "This example demonstrates how linopy can solve optimization models on remote machines using SSH connections. This is one of two remote solving options available in linopy:\n", "\n", - "1. **SSH Remote Solving** (this example) - Connect to your own servers via SSH\\n2. **OETC Cloud Solving** - Use cloud-based optimization services (see `solve-on-oetc.ipynb`)", - "\n\n", - "## SSH Remote Solving\\n\\nSSH remote solving is ideal when you have:\\n* Access to dedicated servers with optimization solvers installed\\n* Full control over the computing environment\\n* Existing infrastructure for optimization workloads\\n\\n## What you need for SSH remote solving:\\n* A running installation of paramiko on your local machine (`pip install paramiko`)\\n* A remote server with a working installation of linopy (e.g., in a conda environment)\\n* SSH access to that machine\\n\\n## How SSH Remote Solving Works\\n\\nThe workflow consists of the following steps, most of which linopy handles automatically:\\n\\n1. Define a model on the local machine\\n2. Save the model on the remote machine via SSH\\n3. Load, solve and write out the model on the remote machine\\n4. Copy the solved model back to the local machine\\n5. Load the solved model on the local machine\\n\\nThe model initialization happens locally, while the actual solving happens remotely.\"" + "1. **SSH Remote Solving** (this example) - Connect to your own servers via SSH\n", + "2. **OETC Cloud Solving** - Use cloud-based optimization services (see :doc:`solve-on-oetc`)\n", + "\n", + "## SSH Remote Solving\n", + "\n", + "SSH remote solving is ideal when you have:\n", + "\n", + "* Access to dedicated servers with optimization solvers installed\n", + "* Full control over the computing environment\n", + "* Existing infrastructure for optimization workloads\n", + "\n", + "## What you need for SSH remote solving\n", + "\n", + "* A running installation of paramiko on your local machine (`pip install paramiko`)\n", + "* A remote server with a working installation of linopy (e.g., in a conda environment)\n", + "* SSH access to that machine\n", + "\n", + "## How SSH Remote Solving Works\n", + "\n", + "The workflow consists of the following steps, most of which linopy handles automatically:\n", + "\n", + "1. Define a model on the local machine\n", + "2. Save the model on the remote machine via SSH\n", + "3. Load, solve and write out the model on the remote machine\n", + "4. Copy the solved model back to the local machine\n", + "5. Load the solved model on the local machine\n", + "\n", + "The model initialization happens locally, while the actual solving happens remotely.\n" ] }, { From 55867ac2fa82d14bc3ded79a81e69450acd954a4 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 11:08:32 +0200 Subject: [PATCH 13/25] docs: use markdown links for cross-refs in notebook markdown cells :doc: only resolves inside RST contexts (rst files and the body of RST directives like .. tip::). In plain markdown cells nbsphinx needs markdown links to the .ipynb files, which it then rewrites to the rendered .html targets. - create-a-model-with-coordinates: the new "Where to next" cell now uses [text](other.ipynb) for the five User Guide notebooks. - solve-on-remote: the inline :doc:`solve-on-oetc` introduced in the previous fix is now a markdown link too. Verified the build rewrites all five links to .html targets. Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/create-a-model-with-coordinates.ipynb | 12 ++++++------ examples/solve-on-remote.ipynb | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/create-a-model-with-coordinates.ipynb b/examples/create-a-model-with-coordinates.ipynb index 01840863..d5c8659b 100644 --- a/examples/create-a-model-with-coordinates.ipynb +++ b/examples/create-a-model-with-coordinates.ipynb @@ -186,13 +186,13 @@ "source": [ "## Where to next\n", "\n", - "You've now seen the full path from declaring variables on coordinates to solving the model. The :doc:`User Guide ` reopens each piece in depth:\n", + "You've now seen the full path from declaring variables on coordinates to solving the model. The User Guide reopens each piece in depth:\n", "\n", - "- :doc:`creating-variables` for the full surface of ``add_variables`` (types, dimensions, initialization from numpy / pandas, masking).\n", - "- :doc:`creating-expressions` for arithmetic, broadcasting, ``.sum``, ``.groupby``, ``.rolling``, ``.where``.\n", - "- :doc:`creating-constraints` for equality / inequality constraints across dimensions.\n", - "- :doc:`coordinate-alignment` for the alignment rules that quietly drove the broadcasts you just saw.\n", - "- :doc:`manipulating-models` once you want to tweak an existing model in place." + "- [Creating variables](creating-variables.ipynb) for the full surface of `add_variables` (types, dimensions, initialization from numpy / pandas, masking).\n", + "- [Creating expressions](creating-expressions.ipynb) for arithmetic, broadcasting, `.sum`, `.groupby`, `.rolling`, `.where`.\n", + "- [Creating constraints](creating-constraints.ipynb) for equality / inequality constraints across dimensions.\n", + "- [Coordinate alignment](coordinate-alignment.ipynb) for the alignment rules that quietly drove the broadcasts you just saw.\n", + "- [Manipulating models](manipulating-models.ipynb) once you want to tweak an existing model in place." ] } ], diff --git a/examples/solve-on-remote.ipynb b/examples/solve-on-remote.ipynb index 62267e4c..659cf9d6 100644 --- a/examples/solve-on-remote.ipynb +++ b/examples/solve-on-remote.ipynb @@ -10,7 +10,7 @@ "This example demonstrates how linopy can solve optimization models on remote machines using SSH connections. This is one of two remote solving options available in linopy:\n", "\n", "1. **SSH Remote Solving** (this example) - Connect to your own servers via SSH\n", - "2. **OETC Cloud Solving** - Use cloud-based optimization services (see :doc:`solve-on-oetc`)\n", + "2. **OETC Cloud Solving** - Use cloud-based optimization services (see [OETC notebook](solve-on-oetc.ipynb))\n", "\n", "## SSH Remote Solving\n", "\n", From 2caac0138dc98135c6fc21c4d404c89f1d21f168 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 11:14:29 +0200 Subject: [PATCH 14/25] docs: collapse "Where to next" to a single pointer at the User Guide overview Listing the five User Guide notebooks at the end of the coordinates notebook duplicated the bullet list the overview page already maintains. Single forward pointer keeps the User Guide overview as the source of truth and pushes the reader through the deliberate intro on that page. Co-Authored-By: Claude Opus 4.7 (1M context) --- examples/create-a-model-with-coordinates.ipynb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/examples/create-a-model-with-coordinates.ipynb b/examples/create-a-model-with-coordinates.ipynb index d5c8659b..e8021a35 100644 --- a/examples/create-a-model-with-coordinates.ipynb +++ b/examples/create-a-model-with-coordinates.ipynb @@ -186,13 +186,7 @@ "source": [ "## Where to next\n", "\n", - "You've now seen the full path from declaring variables on coordinates to solving the model. The User Guide reopens each piece in depth:\n", - "\n", - "- [Creating variables](creating-variables.ipynb) for the full surface of `add_variables` (types, dimensions, initialization from numpy / pandas, masking).\n", - "- [Creating expressions](creating-expressions.ipynb) for arithmetic, broadcasting, `.sum`, `.groupby`, `.rolling`, `.where`.\n", - "- [Creating constraints](creating-constraints.ipynb) for equality / inequality constraints across dimensions.\n", - "- [Coordinate alignment](coordinate-alignment.ipynb) for the alignment rules that quietly drove the broadcasts you just saw.\n", - "- [Manipulating models](manipulating-models.ipynb) once you want to tweak an existing model in place." + "You've now seen the full path from declaring variables on coordinates to solving the model. The [User Guide overview](user-guide.rst) reopens each piece in depth and points you at every topic from here." ] } ], From 6cfa229c166f535c9c0a54622bbd8a5a01ee7c98 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 11:56:11 +0200 Subject: [PATCH 15/25] =?UTF-8?q?docs:=20restructure=20api.rst=20=E2=80=94?= =?UTF-8?q?=20task-oriented=20top,=20classes=20under=20the=20hood,=20advan?= =?UTF-8?q?ced=20bottom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the flat alphabetic per-class dump with a layered structure that puts the surface 90% of users reach for at the top, the supporting classes in the middle, and the genuinely rare-use surface at the bottom. Task-oriented top sections (Model methods grouped by what the user is doing): Creating a model Inspecting a model Modifying a model Solving Post-solve access (Model post-solve accessors + status enums) Diagnostics IO Top-level helpers (align, options) Classes under the hood: Variable / Variables / LinearExpression / Constraint / Constraints / Objective / Piecewise Each gets a small thematic split inside (Attributes / Operations / Conversion / Post-solve / etc.) rather than an alphabetic dump. Advanced section at the bottom for surface that most users will not reach for: QuadraticExpression CSRConstraint Bulk variable operations (Variables.fix/unfix/relax/unrelax) Auto-reformulation (Model.reformulate_sos_constraints) Remote solving (RemoteHandler) Warnings (EvolvingAPIWarning, PerformanceWarning) Curated to drop internal escape hatches and helpers: - Model.to_gurobipy/to_highspy/to_mosek/to_cupdlpx (power-user escape hatches) - Model.linexpr (arithmetic on Variables is the natural path) - Constraints.format_labels, .coefficientrange (internal diagnostics) - LinearExpression.from_rule, .from_constant (niche constructors) - Constraint.freeze / .mutable, CSRConstraint.freeze / .mutable (CSR backend plumbing) - ScalarVariable, ScalarLinearExpression (internal types) - solvers.PIPS (stub that raises NotImplementedError) Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 579 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 436 insertions(+), 143 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index d4c8ca95..05f1a6cd 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -4,46 +4,116 @@ API reference ############# -This page provides an auto-generated summary of linopy's API. +Auto-generated summary of linopy's public API. Each entry links to a +dedicated page with the full signature and docstring. +.. contents:: + :local: + :depth: 2 Creating a model ================ .. autosummary:: - :toctree: generated/ + :toctree: generated/ - model.Model - model.Model.add_variables - model.Model.add_constraints - model.Model.add_objective - model.Model.add_sos_constraints - model.Model.add_piecewise_formulation - piecewise.PiecewiseFormulation - piecewise.Slopes - piecewise.breakpoints - piecewise.segments - piecewise.tangent_lines - model.Model.linexpr - model.Model.remove_constraints - model.Model.reformulate_sos_constraints - model.Model.compute_infeasibilities - model.Model.format_infeasibilities - model.Model.copy + model.Model + model.Model.add_variables + model.Model.add_constraints + model.Model.add_objective + model.Model.add_sos_constraints + model.Model.add_piecewise_formulation + piecewise.breakpoints + piecewise.segments + piecewise.Slopes + + +Inspecting a model +================== + +.. autosummary:: + :toctree: generated/ + + model.Model.variables + model.Model.constraints + model.Model.objective + model.Model.sense + model.Model.type + model.Model.is_linear + model.Model.is_quadratic + + +Modifying a model +================= + +.. autosummary:: + :toctree: generated/ + + model.Model.remove_variables + model.Model.remove_constraints + model.Model.remove_objective + model.Model.remove_sos_constraints + model.Model.copy + + +Solving +======= + +.. autosummary:: + :toctree: generated/ + + model.Model.solve + + +Post-solve access +================= + +.. autosummary:: + :toctree: generated/ + + model.Model.solution + model.Model.dual + model.Model.status + model.Model.termination_condition + constants.SolverStatus + constants.TerminationCondition + constants.Status + constants.Solution + constants.Result + + +Diagnostics +=========== + +.. autosummary:: + :toctree: generated/ + + model.Model.compute_infeasibilities + model.Model.format_infeasibilities + + +IO +== + +.. autosummary:: + :toctree: generated/ + + model.Model.to_file + model.Model.to_netcdf + model.Model.get_problem_file + model.Model.get_solution_file + io.read_netcdf Top-level helpers ================= .. autosummary:: - :toctree: generated/ + :toctree: generated/ - align - merge - options - EvolvingAPIWarning - PerformanceWarning + align + options Classes under the hood @@ -52,191 +122,414 @@ Classes under the hood Variable -------- -``Variable`` is a subclass of ``xarray.DataArray`` and contains all labels referring to a multi-dimensional variable. +``Variable`` is a subclass of ``xarray.DataArray`` and carries labels +for a multi-dimensional decision variable. + +.. autosummary:: + :toctree: generated/ + + variables.Variable + +Attributes +~~~~~~~~~~ .. autosummary:: - :toctree: generated/ + :toctree: generated/ + + variables.Variable.lower + variables.Variable.upper + variables.Variable.type + variables.Variable.solution + +Modifying state +~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + variables.Variable.fix + variables.Variable.unfix + variables.Variable.relax + variables.Variable.unrelax + +Operations +~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + variables.Variable.sum + variables.Variable.where + variables.Variable.sanitize + +Conversion +~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + variables.Variable.to_linexpr + variables.Variable.to_polars - variables.Variable - variables.Variable.lower - variables.Variable.upper - variables.Variable.sum - variables.Variable.where - variables.Variable.sanitize - variables.Variable.to_linexpr - variables.Variable.fix - variables.Variable.unfix - variables.Variable.relax - variables.Variable.unrelax - variables.ScalarVariable Variables --------- -``Variables`` is a container for multiple N-D labeled variables. It is automatically added to a ``Model`` instance when initialized. +``Variables`` is a container for the collection of variables on a +model. Accessed via ``model.variables``. .. autosummary:: - :toctree: generated/ + :toctree: generated/ - variables.Variables - variables.Variables.add - variables.Variables.remove - variables.Variables.continuous - variables.Variables.binaries - variables.Variables.integers - variables.Variables.flat + variables.Variables +Inventory by type +~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ -LinearExpressions ------------------ + variables.Variables.continuous + variables.Variables.binaries + variables.Variables.integers + variables.Variables.semi_continuous + variables.Variables.sos + +Aggregate access +~~~~~~~~~~~~~~~~ .. autosummary:: - :toctree: generated/ + :toctree: generated/ + + variables.Variables.lower + variables.Variables.upper + variables.Variables.solution - expressions.LinearExpression - expressions.LinearExpression.sum - expressions.LinearExpression.where - expressions.LinearExpression.groupby - expressions.LinearExpression.rolling - expressions.LinearExpression.from_tuples - expressions.merge - expressions.ScalarLinearExpression +Modification +~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + variables.Variables.add + variables.Variables.remove -QuadraticExpressions --------------------- + +LinearExpression +---------------- + +Linear combination of variables. Arithmetic on ``Variable`` / +``LinearExpression`` returns a ``LinearExpression``. .. autosummary:: - :toctree: generated/ + :toctree: generated/ - expressions.QuadraticExpression + expressions.LinearExpression +Building blocks +~~~~~~~~~~~~~~~ -Objective ---------- +.. autosummary:: + :toctree: generated/ + + expressions.LinearExpression.vars + expressions.LinearExpression.coeffs + expressions.LinearExpression.const + expressions.LinearExpression.nterm + +Construction +~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + expressions.LinearExpression.from_tuples + expressions.merge + +Manipulation +~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + expressions.LinearExpression.sum + expressions.LinearExpression.where + expressions.LinearExpression.groupby + expressions.LinearExpression.rolling + +Conversion +~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + expressions.LinearExpression.to_constraint + expressions.LinearExpression.to_quadexpr + expressions.LinearExpression.to_polars + +Post-solve access +~~~~~~~~~~~~~~~~~ .. autosummary:: - :toctree: generated/ + :toctree: generated/ + + expressions.LinearExpression.solution - objective.Objective Constraint ---------- -``Constraint`` is a subclass of ``xarray.DataArray`` and contains all labels referring to a multi-dimensional constraint. +``Constraint`` is a subclass of ``xarray.DataArray`` and carries labels +for a multi-dimensional constraint. .. autosummary:: - :toctree: generated/ + :toctree: generated/ - constraints.Constraint - constraints.Constraint.coeffs - constraints.Constraint.vars - constraints.Constraint.lhs - constraints.Constraint.sign - constraints.Constraint.rhs - constraints.Constraint.flat - constraints.Constraint.freeze - constraints.Constraint.mutable + constraints.Constraint +Structure +~~~~~~~~~ -CSRConstraint -------------- +.. autosummary:: + :toctree: generated/ + + constraints.Constraint.lhs + constraints.Constraint.sign + constraints.Constraint.rhs + constraints.Constraint.coeffs + constraints.Constraint.vars + +Post-solve access +~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + constraints.Constraint.dual -``CSRConstraint`` is a memory-efficient, immutable constraint representation backed by a scipy CSR sparse matrix. See the :doc:`creating-constraints` guide for usage. +Conversion +~~~~~~~~~~ .. autosummary:: - :toctree: generated/ + :toctree: generated/ - constraints.CSRConstraint - constraints.CSRConstraint.coeffs - constraints.CSRConstraint.vars - constraints.CSRConstraint.sign - constraints.CSRConstraint.rhs - constraints.CSRConstraint.ncons - constraints.CSRConstraint.nterm - constraints.CSRConstraint.freeze - constraints.CSRConstraint.mutable + constraints.Constraint.to_polars Constraints ----------- +Container for the collection of constraints on a model. Accessed via +``model.constraints``. + .. autosummary:: - :toctree: generated/ + :toctree: generated/ + + constraints.Constraints - constraints.Constraints - constraints.Constraints.add - constraints.Constraints.remove - constraints.Constraints.coefficientrange - constraints.Constraints.inequalities - constraints.Constraints.equalities - constraints.Constraints.sanitize_missings - constraints.Constraints.flat - constraints.Constraints.to_matrix +Inventory +~~~~~~~~~ +.. autosummary:: + :toctree: generated/ -IO functions -============ + constraints.Constraints.inequalities + constraints.Constraints.equalities + +Aggregate access +~~~~~~~~~~~~~~~~ .. autosummary:: - :toctree: generated/ + :toctree: generated/ - model.Model.get_problem_file - model.Model.get_solution_file - model.Model.to_file - model.Model.to_netcdf - io.read_netcdf + constraints.Constraints.coeffs + constraints.Constraints.vars + constraints.Constraints.sign + constraints.Constraints.rhs + constraints.Constraints.dual -Solver utilities -================= +Modification +~~~~~~~~~~~~ .. autosummary:: - :toctree: generated/ + :toctree: generated/ - solvers.available_solvers - solvers.quadratic_solvers - solvers.Solver + constraints.Constraints.add + constraints.Constraints.remove +Cleanup +~~~~~~~ -Solvers -======= +.. autosummary:: + :toctree: generated/ + + constraints.Constraints.sanitize_missings + +Conversion +~~~~~~~~~~ .. autosummary:: - :toctree: generated/ + :toctree: generated/ - solvers.CBC - solvers.COPT - solvers.Cplex - solvers.GLPK - solvers.Gurobi - solvers.Highs - solvers.Knitro - solvers.MindOpt - solvers.Mosek - solvers.PIPS - solvers.SCIP - solvers.Xpress - solvers.cuPDLPx + constraints.Constraints.to_matrix -Remote solving -============== +Objective +--------- + +Wraps the objective expression on a model. Accessed via +``model.objective``. .. autosummary:: - :toctree: generated/ + :toctree: generated/ - remote.RemoteHandler + objective.Objective + objective.Objective.expression + objective.Objective.sense + objective.Objective.value + objective.Objective.is_linear + objective.Objective.is_quadratic -Solving +Piecewise +--------- + +``PiecewiseFormulation`` is returned by +:func:`Model.add_piecewise_formulation` and exposes the resolved +formulation method together with the auxiliary variables/constraints +that were generated. :func:`tangent_lines` is a standalone helper for +composing chord-based bounds by hand, without going through +:func:`Model.add_piecewise_formulation`. + +.. autosummary:: + :toctree: generated/ + + piecewise.PiecewiseFormulation + piecewise.PiecewiseFormulation.method + piecewise.PiecewiseFormulation.convexity + piecewise.PiecewiseFormulation.variables + piecewise.PiecewiseFormulation.constraints + piecewise.tangent_lines + + +Solver interface +================ + +.. autosummary:: + :toctree: generated/ + + solvers.available_solvers + solvers.quadratic_solvers + solvers.Solver + + +Solver implementations +====================== + +.. autosummary:: + :toctree: generated/ + + solvers.CBC + solvers.COPT + solvers.Cplex + solvers.GLPK + solvers.Gurobi + solvers.Highs + solvers.Knitro + solvers.MindOpt + solvers.Mosek + solvers.SCIP + solvers.Xpress + solvers.cuPDLPx + + +Advanced ======== +Surface that most users don't reach for. Listed for completeness. + +QuadraticExpression +------------------- + +Quadratic combination of variables, returned when squared +``Variable``/``LinearExpression`` arithmetic is performed. + +.. autosummary:: + :toctree: generated/ + + expressions.QuadraticExpression + expressions.QuadraticExpression.vars + expressions.QuadraticExpression.coeffs + expressions.QuadraticExpression.const + expressions.QuadraticExpression.nterm + expressions.QuadraticExpression.to_constraint + expressions.QuadraticExpression.to_matrix + expressions.QuadraticExpression.to_polars + expressions.QuadraticExpression.solution + + +CSRConstraint +------------- + +Memory-efficient, immutable constraint representation backed by a scipy +CSR sparse matrix. Opt in via ``Model(freeze_constraints=True)`` or +``Model.add_constraints(..., freeze=True)``. See the +:doc:`creating-constraints` guide for usage. + +.. autosummary:: + :toctree: generated/ + + constraints.CSRConstraint + constraints.CSRConstraint.coeffs + constraints.CSRConstraint.vars + constraints.CSRConstraint.sign + constraints.CSRConstraint.rhs + constraints.CSRConstraint.dual + constraints.CSRConstraint.ncons + constraints.CSRConstraint.nterm + constraints.CSRConstraint.to_polars + + +Bulk variable operations +------------------------ + +Container-wide analogues of :func:`Variable.fix`, etc. + +.. autosummary:: + :toctree: generated/ + + variables.Variables.fix + variables.Variables.unfix + variables.Variables.relax + variables.Variables.unrelax + + +Auto-reformulation +------------------ + +.. autosummary:: + :toctree: generated/ + + model.Model.reformulate_sos_constraints + + +Remote solving +-------------- + +.. autosummary:: + :toctree: generated/ + + remote.RemoteHandler + + +Warnings +-------- + +These warning classes can be silenced or filtered via +:func:`warnings.filterwarnings`. + .. autosummary:: - :toctree: generated/ + :toctree: generated/ - model.Model.solve - constants.SolverStatus - constants.TerminationCondition - constants.Status - constants.Solution - constants.Result + EvolvingAPIWarning + PerformanceWarning From 8e3885b4659428e296caccf29bc384abb43bd0bc Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 12:07:20 +0200 Subject: [PATCH 16/25] docs: add docstrings for properties surfaced in api.rst autosummary tables The new api.rst structure exposed 14 properties whose autosummary table cell was blank because they had no docstring. Add a single-line description to each: - Model.is_linear / is_quadratic / type - BaseExpression.vars / coeffs / const (fixes both LinearExpression and QuadraticExpression entries) - Objective.is_linear / is_quadratic - PiecewiseFormulation.method / convexity (added as inline attribute docstrings on the dataclass fields) - OptionSettings class docstring (so the top-level `options` instance picks up a description) PiecewiseFormulation: also dropped the duplicated literal value lists from the class-level Attributes block and from the new attribute docstrings, referencing the PWL_METHOD / PWL_CONVEXITY type aliases instead so there is a single source of truth for the allowed values. Co-Authored-By: Claude Opus 4.7 (1M context) --- linopy/config.py | 2 ++ linopy/expressions.py | 3 +++ linopy/model.py | 3 +++ linopy/objective.py | 2 ++ linopy/piecewise.py | 11 ++++++----- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/linopy/config.py b/linopy/config.py index 240eaed6..5d269c4e 100644 --- a/linopy/config.py +++ b/linopy/config.py @@ -11,6 +11,8 @@ class OptionSettings: + """Runtime configuration knobs (e.g. display widths). Use as a context manager or set values directly via ``options(key=value)``.""" + def __init__(self, **kwargs: Any) -> None: self._defaults = kwargs self._current_values = kwargs.copy() diff --git a/linopy/expressions.py b/linopy/expressions.py index 2218eef3..2ab0b8d3 100644 --- a/linopy/expressions.py +++ b/linopy/expressions.py @@ -923,6 +923,7 @@ def coord_names(self) -> list[str]: @property def vars(self) -> DataArray: + """Variable labels referenced by each term of the expression.""" return self.data.vars @vars.setter @@ -931,6 +932,7 @@ def vars(self, value: DataArray) -> None: @property def coeffs(self) -> DataArray: + """Coefficient applied to each term of the expression.""" return self.data.coeffs @coeffs.setter @@ -939,6 +941,7 @@ def coeffs(self, value: DataArray) -> None: @property def const(self) -> DataArray: + """Constant offset added to the expression.""" return self.data.const @const.setter diff --git a/linopy/model.py b/linopy/model.py index 21e4e29c..f9273e83 100644 --- a/linopy/model.py +++ b/linopy/model.py @@ -1233,14 +1233,17 @@ def semi_continuous(self) -> Variables: @property def is_linear(self) -> bool: + """Whether the objective is linear.""" return self.objective.is_linear @property def is_quadratic(self) -> bool: + """Whether the objective is quadratic.""" return self.objective.is_quadratic @property def type(self) -> str: + """Short string identifying the problem type.""" if ( len(self.binaries) or len(self.integers) or len(self.semi_continuous) ) and len(self.continuous): diff --git a/linopy/objective.py b/linopy/objective.py index b1449270..a51b2207 100644 --- a/linopy/objective.py +++ b/linopy/objective.py @@ -232,10 +232,12 @@ def set_value(self, value: float) -> None: @property def is_linear(self) -> bool: + """Whether the objective expression is linear.""" return type(self.expression) is expressions.LinearExpression @property def is_quadratic(self) -> bool: + """Whether the objective expression is quadratic.""" return type(self.expression) is expressions.QuadraticExpression def to_matrix(self, *args: Any, **kwargs: Any) -> csc_matrix: diff --git a/linopy/piecewise.py b/linopy/piecewise.py index 7497c4bf..84ed4a01 100644 --- a/linopy/piecewise.py +++ b/linopy/piecewise.py @@ -346,11 +346,10 @@ class PiecewiseFormulation: name : str Formulation name (used as prefix for auxiliary variables and constraints). - method : str - Resolved method — one of ``{"sos2", "incremental", "lp"}``. Never - ``"auto"``; if the caller passed ``method="auto"``, this holds the - method actually chosen. - convexity : {"convex", "concave", "linear", "mixed"} or None + method : PWL_METHOD + Resolved method actually used. Never ``"auto"``; if the caller + passed ``method="auto"``, this holds the method that was chosen. + convexity : PWL_CONVEXITY or None Shape of the piecewise curve along the breakpoint axis when it is well-defined (exactly two expressions, non-disjunctive, strictly monotonic ``x`` breakpoints). ``None`` otherwise. @@ -358,10 +357,12 @@ class PiecewiseFormulation: name: str method: PWL_METHOD + """Resolved formulation method (see :data:`PWL_METHOD`).""" variable_names: list[str] constraint_names: list[str] model: Model convexity: PWL_CONVEXITY | None = None + """Shape of the piecewise curve when well-defined (see :data:`PWL_CONVEXITY`), else ``None``.""" @property def variables(self) -> Variables: From a7efef2c90116499ae861be2720f83ce04fb1460 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 12:56:27 +0200 Subject: [PATCH 17/25] docs: surface PWL_METHOD / PWL_CONVEXITY type aliases in api.rst The :data:`PWL_METHOD` and :data:`PWL_CONVEXITY` references added in the previous commit rendered as plain rather than as hyperlinks: their docs target didn't exist yet, and the role was being looked up in the wrong module. - Add docstrings to PWL_METHOD, PWL_METHODS, PWL_CONVEXITY, and PWL_CONVEXITIES in linopy/constants.py. - List all four in api.rst under the Piecewise subsection so autosummary generates dedicated pages. - Switch the cross-references in PiecewiseFormulation.method / .convexity and in constants.py to the fully-qualified form (:data:`~linopy.constants.PWL_METHOD`) so they resolve from whichever module they're rendered in. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 4 ++++ linopy/constants.py | 7 +++++++ linopy/piecewise.py | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 05f1a6cd..2435c361 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -410,6 +410,10 @@ composing chord-based bounds by hand, without going through piecewise.PiecewiseFormulation.variables piecewise.PiecewiseFormulation.constraints piecewise.tangent_lines + constants.PWL_METHOD + constants.PWL_METHODS + constants.PWL_CONVEXITY + constants.PWL_CONVEXITIES Solver interface diff --git a/linopy/constants.py b/linopy/constants.py index 5cc98ce2..09244323 100644 --- a/linopy/constants.py +++ b/linopy/constants.py @@ -60,9 +60,16 @@ class PerformanceWarning(UserWarning): PWL_DOMAIN_HI_SUFFIX = "_domain_hi" PWL_METHOD: TypeAlias = Literal["sos2", "lp", "incremental", "auto"] +"""Allowed values for the ``method`` argument of :func:`Model.add_piecewise_formulation`.""" + PWL_METHODS: frozenset[str] = frozenset(get_args(PWL_METHOD)) +"""Set of valid :data:`~linopy.constants.PWL_METHOD` values.""" + PWL_CONVEXITY: TypeAlias = Literal["convex", "concave", "linear", "mixed"] +"""Possible values for :attr:`~linopy.piecewise.PiecewiseFormulation.convexity`.""" + PWL_CONVEXITIES: frozenset[str] = frozenset(get_args(PWL_CONVEXITY)) +"""Set of valid :data:`~linopy.constants.PWL_CONVEXITY` values.""" BREAKPOINT_DIM = "_breakpoint" SEGMENT_DIM = "_segment" LP_PIECE_DIM = f"{BREAKPOINT_DIM}_piece" diff --git a/linopy/piecewise.py b/linopy/piecewise.py index 84ed4a01..ccc265a7 100644 --- a/linopy/piecewise.py +++ b/linopy/piecewise.py @@ -357,12 +357,12 @@ class PiecewiseFormulation: name: str method: PWL_METHOD - """Resolved formulation method (see :data:`PWL_METHOD`).""" + """Resolved formulation method (see :data:`~linopy.constants.PWL_METHOD`).""" variable_names: list[str] constraint_names: list[str] model: Model convexity: PWL_CONVEXITY | None = None - """Shape of the piecewise curve when well-defined (see :data:`PWL_CONVEXITY`), else ``None``.""" + """Shape of the piecewise curve when well-defined (see :data:`~linopy.constants.PWL_CONVEXITY`), else ``None``.""" @property def variables(self) -> Variables: From d4b20d317e9993984551afca252ec07124f87f88 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 13:13:17 +0200 Subject: [PATCH 18/25] docs: dissolve the api.rst Advanced umbrella; each item gets a natural home MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Advanced section was a subjective bucket. Each entry now sits where it belongs: - QuadraticExpression moves into Classes under the hood alongside LinearExpression — it's a class with its own surface, not a power-user appendix. - CSRConstraint moves into Classes under the hood next to Constraint — alternative storage backend, but a regular documented class. - Variables.fix / unfix / relax / unrelax return as a Bulk modify subgroup under the Variables container. - Model.reformulate_sos_constraints joins Modifying a model — it transforms the model in place. - Remote solving and Warnings become small top-level sections of their own at the end of the page rather than nested under Advanced. Preamble updated to drop the now-stale "Advanced section at the bottom" reference and to signal the actual top-level structure (task-oriented top, supporting classes below). Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 136 ++++++++++++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 74 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 2435c361..f23d94af 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -4,8 +4,10 @@ API reference ############# -Auto-generated summary of linopy's public API. Each entry links to a -dedicated page with the full signature and docstring. +Reference for linopy's public API. Top sections are task-oriented +(creating, inspecting, modifying, solving, IO); supporting classes +are grouped below. Each entry links to a dedicated page with the full +signature and docstring. .. contents:: :local: @@ -55,6 +57,7 @@ Modifying a model model.Model.remove_objective model.Model.remove_sos_constraints model.Model.copy + model.Model.reformulate_sos_constraints Solving @@ -214,6 +217,19 @@ Modification variables.Variables.add variables.Variables.remove +Bulk modify +~~~~~~~~~~~ + +Container-wide analogues of :func:`Variable.fix`, etc. + +.. autosummary:: + :toctree: generated/ + + variables.Variables.fix + variables.Variables.unfix + variables.Variables.relax + variables.Variables.unrelax + LinearExpression ---------------- @@ -276,6 +292,26 @@ Post-solve access expressions.LinearExpression.solution +QuadraticExpression +------------------- + +Quadratic combination of variables, returned when squared +``Variable`` / ``LinearExpression`` arithmetic is performed. + +.. autosummary:: + :toctree: generated/ + + expressions.QuadraticExpression + expressions.QuadraticExpression.vars + expressions.QuadraticExpression.coeffs + expressions.QuadraticExpression.const + expressions.QuadraticExpression.nterm + expressions.QuadraticExpression.to_constraint + expressions.QuadraticExpression.to_matrix + expressions.QuadraticExpression.to_polars + expressions.QuadraticExpression.solution + + Constraint ---------- @@ -316,6 +352,28 @@ Conversion constraints.Constraint.to_polars +CSRConstraint +------------- + +Memory-efficient, immutable constraint representation backed by a scipy +CSR sparse matrix. Opt in via ``Model(freeze_constraints=True)`` or +``Model.add_constraints(..., freeze=True)``. See the +:doc:`creating-constraints` guide for usage. + +.. autosummary:: + :toctree: generated/ + + constraints.CSRConstraint + constraints.CSRConstraint.coeffs + constraints.CSRConstraint.vars + constraints.CSRConstraint.sign + constraints.CSRConstraint.rhs + constraints.CSRConstraint.dual + constraints.CSRConstraint.ncons + constraints.CSRConstraint.nterm + constraints.CSRConstraint.to_polars + + Constraints ----------- @@ -447,78 +505,8 @@ Solver implementations solvers.cuPDLPx -Advanced -======== - -Surface that most users don't reach for. Listed for completeness. - -QuadraticExpression -------------------- - -Quadratic combination of variables, returned when squared -``Variable``/``LinearExpression`` arithmetic is performed. - -.. autosummary:: - :toctree: generated/ - - expressions.QuadraticExpression - expressions.QuadraticExpression.vars - expressions.QuadraticExpression.coeffs - expressions.QuadraticExpression.const - expressions.QuadraticExpression.nterm - expressions.QuadraticExpression.to_constraint - expressions.QuadraticExpression.to_matrix - expressions.QuadraticExpression.to_polars - expressions.QuadraticExpression.solution - - -CSRConstraint -------------- - -Memory-efficient, immutable constraint representation backed by a scipy -CSR sparse matrix. Opt in via ``Model(freeze_constraints=True)`` or -``Model.add_constraints(..., freeze=True)``. See the -:doc:`creating-constraints` guide for usage. - -.. autosummary:: - :toctree: generated/ - - constraints.CSRConstraint - constraints.CSRConstraint.coeffs - constraints.CSRConstraint.vars - constraints.CSRConstraint.sign - constraints.CSRConstraint.rhs - constraints.CSRConstraint.dual - constraints.CSRConstraint.ncons - constraints.CSRConstraint.nterm - constraints.CSRConstraint.to_polars - - -Bulk variable operations ------------------------- - -Container-wide analogues of :func:`Variable.fix`, etc. - -.. autosummary:: - :toctree: generated/ - - variables.Variables.fix - variables.Variables.unfix - variables.Variables.relax - variables.Variables.unrelax - - -Auto-reformulation ------------------- - -.. autosummary:: - :toctree: generated/ - - model.Model.reformulate_sos_constraints - - Remote solving --------------- +============== .. autosummary:: :toctree: generated/ @@ -527,7 +515,7 @@ Remote solving Warnings --------- +======== These warning classes can be silenced or filtered via :func:`warnings.filterwarnings`. From 1e33eddb7c0caacc057e2d081b4f52a8a60f8ac5 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 13:48:09 +0200 Subject: [PATCH 19/25] docs: keep page TOC depth 2; expand right-side TOC to L2 site-wide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline ``.. contents::`` directive on api.rst stays at depth 2 — the page-top TOC is the executive summary, not a complete map. For navigation into a section, bump sphinx-book-theme's ``show_toc_level`` to 2 in ``html_theme_options``. The right-side "On this page" panel now shows H3 entries by default rather than only expanding the section the user has scrolled into. Applies site-wide; the other pages have shallow H3 structures so this is a usability win across the board. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 12 ++++++------ doc/conf.py | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index f23d94af..68fd6b40 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -119,8 +119,8 @@ Top-level helpers options -Classes under the hood -====================== +Classes and types +================= Variable -------- @@ -475,7 +475,7 @@ composing chord-based bounds by hand, without going through Solver interface -================ +---------------- .. autosummary:: :toctree: generated/ @@ -486,7 +486,7 @@ Solver interface Solver implementations -====================== +---------------------- .. autosummary:: :toctree: generated/ @@ -506,7 +506,7 @@ Solver implementations Remote solving -============== +-------------- .. autosummary:: :toctree: generated/ @@ -515,7 +515,7 @@ Remote solving Warnings -======== +-------- These warning classes can be silenced or filtered via :func:`warnings.filterwarnings`. diff --git a/doc/conf.py b/doc/conf.py index e28bde83..ca8414ab 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -123,6 +123,7 @@ html_theme_options = { "repository_url": "https://github.com/pypsa/linopy", "use_repository_button": True, + "show_toc_level": 2, } From 89544379718f4039f89e5e94741dc8bda856f1ea Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 13:51:12 +0200 Subject: [PATCH 20/25] =?UTF-8?q?docs:=20rename=20"Classes=20and=20types"?= =?UTF-8?q?=20=E2=86=92=20"Other=20classes=20and=20types"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the structural role explicit: these are the classes and types not already covered by the task-oriented top sections. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 68fd6b40..84e4fab7 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -119,8 +119,8 @@ Top-level helpers options -Classes and types -================= +Other classes and types +======================= Variable -------- From 4c64672ebd7921fb4e400aa088a47a88c90a1a22 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 13:58:37 +0200 Subject: [PATCH 21/25] docs: standardise H3 subgroup vocabulary across api.rst class sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align the subgroup names that appear in multiple class sections so a reader who learns the scheme once can scan across classes. Six shared labels emerge — Structure, Construction, Modification, Operations, Conversion, Post-solve access — used wherever they fit. - Variable: "Modifying state" → "Modification" (match Variables, Constraints). - LinearExpression: "Building blocks" → "Structure" (match Constraint, QuadraticExpression below); "Manipulation" → "Operations" (match Variable). - QuadraticExpression: replace the flat list with the same subgroup shape as LinearExpression — Structure, Conversion, Post-solve access. - CSRConstraint: replace the flat list with the same subgroup shape as Constraint — Structure, Post-solve access, Conversion. Also move the solver-status enums out of Post-solve access into their own subsection (Solver status and result types) under Other classes and types: SolverStatus / TerminationCondition / Status / Solution / Result are types you compare Model.status against, not accessors. Model keeps its own task vocabulary (Building / Inspecting / Modifying / Solving / Diagnostics / IO / etc.) because it's structurally different from the data-type classes. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 77 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 84e4fab7..b8896145 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -79,11 +79,6 @@ Post-solve access model.Model.dual model.Model.status model.Model.termination_condition - constants.SolverStatus - constants.TerminationCondition - constants.Status - constants.Solution - constants.Result Diagnostics @@ -144,8 +139,8 @@ Attributes variables.Variable.type variables.Variable.solution -Modifying state -~~~~~~~~~~~~~~~ +Modification +~~~~~~~~~~~~ .. autosummary:: :toctree: generated/ @@ -242,8 +237,8 @@ Linear combination of variables. Arithmetic on ``Variable`` / expressions.LinearExpression -Building blocks -~~~~~~~~~~~~~~~ +Structure +~~~~~~~~~ .. autosummary:: :toctree: generated/ @@ -262,8 +257,8 @@ Construction expressions.LinearExpression.from_tuples expressions.merge -Manipulation -~~~~~~~~~~~~ +Operations +~~~~~~~~~~ .. autosummary:: :toctree: generated/ @@ -302,13 +297,34 @@ Quadratic combination of variables, returned when squared :toctree: generated/ expressions.QuadraticExpression + +Structure +~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + expressions.QuadraticExpression.vars expressions.QuadraticExpression.coeffs expressions.QuadraticExpression.const expressions.QuadraticExpression.nterm + +Conversion +~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + expressions.QuadraticExpression.to_constraint expressions.QuadraticExpression.to_matrix expressions.QuadraticExpression.to_polars + +Post-solve access +~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + expressions.QuadraticExpression.solution @@ -364,13 +380,34 @@ CSR sparse matrix. Opt in via ``Model(freeze_constraints=True)`` or :toctree: generated/ constraints.CSRConstraint + +Structure +~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + constraints.CSRConstraint.coeffs constraints.CSRConstraint.vars constraints.CSRConstraint.sign constraints.CSRConstraint.rhs - constraints.CSRConstraint.dual constraints.CSRConstraint.ncons constraints.CSRConstraint.nterm + +Post-solve access +~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + + constraints.CSRConstraint.dual + +Conversion +~~~~~~~~~~ + +.. autosummary:: + :toctree: generated/ + constraints.CSRConstraint.to_polars @@ -514,6 +551,22 @@ Remote solving remote.RemoteHandler +Solver status and result types +------------------------------ + +Types returned by or compared against :attr:`Model.status`, +:attr:`Model.termination_condition`, and :attr:`Model.solution`. + +.. autosummary:: + :toctree: generated/ + + constants.SolverStatus + constants.TerminationCondition + constants.Status + constants.Solution + constants.Result + + Warnings -------- From db02982e8e58b25c540a7de42ebbab2acf2678f0 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 13:59:52 +0200 Subject: [PATCH 22/25] docs: revert show_toc_level=2 in book-theme options Roll back to the default (1). The right-side page TOC will only expand the section the reader has scrolled into, not all H3 entries across the page. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index ca8414ab..e28bde83 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -123,7 +123,6 @@ html_theme_options = { "repository_url": "https://github.com/pypsa/linopy", "use_repository_button": True, - "show_toc_level": 2, } From 91777fb30f3b1f561f7d98fa561357dc0222fee7 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 14:18:22 +0200 Subject: [PATCH 23/25] docs: restructure api.rst as Model-first and tighten the curated surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes in one pass: (1) Make api.rst per-class throughout. Drop the artificial split between task-oriented top sections and "Other classes and types". Model becomes the first H2 (with its task labels — Building / Inspecting / Modifying / Solving / Post-solve / Diagnostics / IO — as H3 subsections), and every supporting class becomes a sibling H2 of Model. Preamble rewritten to frame the page: Model is the entry point; supporting classes document the types reached via ``model.`` accessors. (2) Tighten the surface further. Drop entries that are internal, implicit-by-arithmetic, or trivial: - Variables.add / .remove, Constraints.add / .remove — internal mechanisms used by model.add_*; users never call them. - Variable.sanitize, Constraints.sanitize_missings — internal cleanup helpers in the solver pipeline. - LinearExpression.to_constraint, QuadraticExpression.to_constraint — almost always implicit via <=, >=, == on expressions. - LinearExpression.to_quadexpr — niche conversion only meaningful if you're already deep in quadratic forms. - Model.get_problem_file / .get_solution_file — debugging-only temp-file accessors. - solvers.Solver (abstract base, never instantiated) and solvers.quadratic_solvers (trivial list). With Solver and quadratic_solvers removed, the "Solver interface" section only contained available_solvers, so collapse it into a single "Solvers" section with the implementation classes. Also reordered the H3 subgroups inside each class so the most-used entries appear first. Post-solve access leads on the expression / constraint classes (the .solution and .dual readers are typically what a reader is looking for); Aggregate access + Bulk modify lead on the Variables container. Top-level helpers moved to the bottom as "Utilities" (align, options) — they're stragglers, not entry points. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 248 ++++++++++++++++++++++------------------------------ 1 file changed, 106 insertions(+), 142 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index b8896145..10d32f11 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -4,9 +4,14 @@ API reference ############# -Reference for linopy's public API. Top sections are task-oriented -(creating, inspecting, modifying, solving, IO); supporting classes -are grouped below. Each entry links to a dedicated page with the full +Reference for linopy's public API. Most workflows start at +:class:`Model` — variables, constraints, expressions and the +objective are all built through ``model.add_*`` and accessed through +the matching ``model.`` accessor (``model.variables``, +``model.constraints``, ``model.objective``). The supporting classes +below cover the types those accessors return, grouped by what each +one is for (Structure / Modification / Conversion / Post-solve +access). Each entry links to a dedicated page with the full signature and docstring. .. contents:: @@ -14,13 +19,23 @@ signature and docstring. :depth: 2 -Creating a model -================ +Model +===== + +Central container for an optimization problem. Most of linopy's +surface lives here. .. autosummary:: :toctree: generated/ model.Model + +Building a model +---------------- + +.. autosummary:: + :toctree: generated/ + model.Model.add_variables model.Model.add_constraints model.Model.add_objective @@ -30,9 +45,8 @@ Creating a model piecewise.segments piecewise.Slopes - Inspecting a model -================== +------------------ .. autosummary:: :toctree: generated/ @@ -45,9 +59,8 @@ Inspecting a model model.Model.is_linear model.Model.is_quadratic - Modifying a model -================= +----------------- .. autosummary:: :toctree: generated/ @@ -59,18 +72,16 @@ Modifying a model model.Model.copy model.Model.reformulate_sos_constraints - Solving -======= +------- .. autosummary:: :toctree: generated/ model.Model.solve - Post-solve access -================= +----------------- .. autosummary:: :toctree: generated/ @@ -80,9 +91,8 @@ Post-solve access model.Model.status model.Model.termination_condition - Diagnostics -=========== +----------- .. autosummary:: :toctree: generated/ @@ -90,38 +100,22 @@ Diagnostics model.Model.compute_infeasibilities model.Model.format_infeasibilities - IO -== +-- .. autosummary:: :toctree: generated/ model.Model.to_file model.Model.to_netcdf - model.Model.get_problem_file - model.Model.get_solution_file io.read_netcdf -Top-level helpers -================= - -.. autosummary:: - :toctree: generated/ - - align - options - - -Other classes and types -======================= - Variable --------- +======== -``Variable`` is a subclass of ``xarray.DataArray`` and carries labels -for a multi-dimensional decision variable. +Subclass of ``xarray.DataArray`` carrying labels for a multi-dimensional +decision variable. .. autosummary:: :toctree: generated/ @@ -129,7 +123,7 @@ for a multi-dimensional decision variable. variables.Variable Attributes -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ @@ -140,7 +134,7 @@ Attributes variables.Variable.solution Modification -~~~~~~~~~~~~ +------------ .. autosummary:: :toctree: generated/ @@ -151,17 +145,16 @@ Modification variables.Variable.unrelax Operations -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ variables.Variable.sum variables.Variable.where - variables.Variable.sanitize Conversion -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ @@ -171,30 +164,18 @@ Conversion Variables ---------- +========= -``Variables`` is a container for the collection of variables on a -model. Accessed via ``model.variables``. +Container for the collection of variables on a model. Accessed via +``model.variables``. .. autosummary:: :toctree: generated/ variables.Variables -Inventory by type -~~~~~~~~~~~~~~~~~ - -.. autosummary:: - :toctree: generated/ - - variables.Variables.continuous - variables.Variables.binaries - variables.Variables.integers - variables.Variables.semi_continuous - variables.Variables.sos - Aggregate access -~~~~~~~~~~~~~~~~ +---------------- .. autosummary:: :toctree: generated/ @@ -203,17 +184,8 @@ Aggregate access variables.Variables.upper variables.Variables.solution -Modification -~~~~~~~~~~~~ - -.. autosummary:: - :toctree: generated/ - - variables.Variables.add - variables.Variables.remove - Bulk modify -~~~~~~~~~~~ +----------- Container-wide analogues of :func:`Variable.fix`, etc. @@ -225,9 +197,21 @@ Container-wide analogues of :func:`Variable.fix`, etc. variables.Variables.relax variables.Variables.unrelax +Inventory by type +----------------- + +.. autosummary:: + :toctree: generated/ + + variables.Variables.continuous + variables.Variables.binaries + variables.Variables.integers + variables.Variables.semi_continuous + variables.Variables.sos + LinearExpression ----------------- +================ Linear combination of variables. Arithmetic on ``Variable`` / ``LinearExpression`` returns a ``LinearExpression``. @@ -237,28 +221,16 @@ Linear combination of variables. Arithmetic on ``Variable`` / expressions.LinearExpression -Structure -~~~~~~~~~ - -.. autosummary:: - :toctree: generated/ - - expressions.LinearExpression.vars - expressions.LinearExpression.coeffs - expressions.LinearExpression.const - expressions.LinearExpression.nterm - -Construction -~~~~~~~~~~~~ +Post-solve access +----------------- .. autosummary:: :toctree: generated/ - expressions.LinearExpression.from_tuples - expressions.merge + expressions.LinearExpression.solution Operations -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ @@ -268,27 +240,37 @@ Operations expressions.LinearExpression.groupby expressions.LinearExpression.rolling +Structure +--------- + +.. autosummary:: + :toctree: generated/ + + expressions.LinearExpression.vars + expressions.LinearExpression.coeffs + expressions.LinearExpression.const + expressions.LinearExpression.nterm + Conversion -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ - expressions.LinearExpression.to_constraint - expressions.LinearExpression.to_quadexpr expressions.LinearExpression.to_polars -Post-solve access -~~~~~~~~~~~~~~~~~ +Construction +------------ .. autosummary:: :toctree: generated/ - expressions.LinearExpression.solution + expressions.LinearExpression.from_tuples + expressions.merge QuadraticExpression -------------------- +=================== Quadratic combination of variables, returned when squared ``Variable`` / ``LinearExpression`` arithmetic is performed. @@ -299,7 +281,7 @@ Quadratic combination of variables, returned when squared expressions.QuadraticExpression Structure -~~~~~~~~~ +--------- .. autosummary:: :toctree: generated/ @@ -310,17 +292,16 @@ Structure expressions.QuadraticExpression.nterm Conversion -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ - expressions.QuadraticExpression.to_constraint expressions.QuadraticExpression.to_matrix expressions.QuadraticExpression.to_polars Post-solve access -~~~~~~~~~~~~~~~~~ +----------------- .. autosummary:: :toctree: generated/ @@ -329,10 +310,10 @@ Post-solve access Constraint ----------- +========== -``Constraint`` is a subclass of ``xarray.DataArray`` and carries labels -for a multi-dimensional constraint. +Subclass of ``xarray.DataArray`` carrying labels for a multi-dimensional +constraint. .. autosummary:: :toctree: generated/ @@ -340,7 +321,7 @@ for a multi-dimensional constraint. constraints.Constraint Structure -~~~~~~~~~ +--------- .. autosummary:: :toctree: generated/ @@ -352,7 +333,7 @@ Structure constraints.Constraint.vars Post-solve access -~~~~~~~~~~~~~~~~~ +----------------- .. autosummary:: :toctree: generated/ @@ -360,7 +341,7 @@ Post-solve access constraints.Constraint.dual Conversion -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ @@ -369,7 +350,7 @@ Conversion CSRConstraint -------------- +============= Memory-efficient, immutable constraint representation backed by a scipy CSR sparse matrix. Opt in via ``Model(freeze_constraints=True)`` or @@ -382,7 +363,7 @@ CSR sparse matrix. Opt in via ``Model(freeze_constraints=True)`` or constraints.CSRConstraint Structure -~~~~~~~~~ +--------- .. autosummary:: :toctree: generated/ @@ -395,7 +376,7 @@ Structure constraints.CSRConstraint.nterm Post-solve access -~~~~~~~~~~~~~~~~~ +----------------- .. autosummary:: :toctree: generated/ @@ -403,7 +384,7 @@ Post-solve access constraints.CSRConstraint.dual Conversion -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ @@ -412,7 +393,7 @@ Conversion Constraints ------------ +=========== Container for the collection of constraints on a model. Accessed via ``model.constraints``. @@ -423,7 +404,7 @@ Container for the collection of constraints on a model. Accessed via constraints.Constraints Inventory -~~~~~~~~~ +--------- .. autosummary:: :toctree: generated/ @@ -432,7 +413,7 @@ Inventory constraints.Constraints.equalities Aggregate access -~~~~~~~~~~~~~~~~ +---------------- .. autosummary:: :toctree: generated/ @@ -443,25 +424,8 @@ Aggregate access constraints.Constraints.rhs constraints.Constraints.dual -Modification -~~~~~~~~~~~~ - -.. autosummary:: - :toctree: generated/ - - constraints.Constraints.add - constraints.Constraints.remove - -Cleanup -~~~~~~~ - -.. autosummary:: - :toctree: generated/ - - constraints.Constraints.sanitize_missings - Conversion -~~~~~~~~~~ +---------- .. autosummary:: :toctree: generated/ @@ -470,7 +434,7 @@ Conversion Objective ---------- +========= Wraps the objective expression on a model. Accessed via ``model.objective``. @@ -487,7 +451,7 @@ Wraps the objective expression on a model. Accessed via Piecewise ---------- +========= ``PiecewiseFormulation`` is returned by :func:`Model.add_piecewise_formulation` and exposes the resolved @@ -511,23 +475,13 @@ composing chord-based bounds by hand, without going through constants.PWL_CONVEXITIES -Solver interface ----------------- +Solvers +======= .. autosummary:: :toctree: generated/ solvers.available_solvers - solvers.quadratic_solvers - solvers.Solver - - -Solver implementations ----------------------- - -.. autosummary:: - :toctree: generated/ - solvers.CBC solvers.COPT solvers.Cplex @@ -543,7 +497,7 @@ Solver implementations Remote solving --------------- +============== .. autosummary:: :toctree: generated/ @@ -552,7 +506,7 @@ Remote solving Solver status and result types ------------------------------- +============================== Types returned by or compared against :attr:`Model.status`, :attr:`Model.termination_condition`, and :attr:`Model.solution`. @@ -567,8 +521,18 @@ Types returned by or compared against :attr:`Model.status`, constants.Result +Utilities +========= + +.. autosummary:: + :toctree: generated/ + + align + options + + Warnings --------- +======== These warning classes can be silenced or filtered via :func:`warnings.filterwarnings`. From 955ef0bd154520edc96f075facc56a84473fa71c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 14:24:06 +0200 Subject: [PATCH 24/25] docs: move piecewise construction helpers into the Piecewise section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The piecewise construction helpers (breakpoints, segments, Slopes) were previously listed under Model > Building a model because they are used alongside Model.add_piecewise_formulation. Move them into the Piecewise section instead — they live in linopy.piecewise and a reader looking for "how do I build a piecewise formulation" expects everything piecewise in one place. Split the Piecewise section into four small subsections (Construction helpers / PiecewiseFormulation / Low-level helper / Type aliases) so the helper functions, the return type, the standalone tangent_lines, and the PWL_METHOD / PWL_CONVEXITY type aliases all sit under clearly labelled groups instead of one mixed flat list. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 10d32f11..24ba6539 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -41,9 +41,6 @@ Building a model model.Model.add_objective model.Model.add_sos_constraints model.Model.add_piecewise_formulation - piecewise.breakpoints - piecewise.segments - piecewise.Slopes Inspecting a model ------------------ @@ -453,12 +450,20 @@ Wraps the objective expression on a model. Accessed via Piecewise ========= -``PiecewiseFormulation`` is returned by -:func:`Model.add_piecewise_formulation` and exposes the resolved -formulation method together with the auxiliary variables/constraints -that were generated. :func:`tangent_lines` is a standalone helper for -composing chord-based bounds by hand, without going through -:func:`Model.add_piecewise_formulation`. +Construction helpers +-------------------- + +.. autosummary:: + :toctree: generated/ + + piecewise.breakpoints + piecewise.segments + piecewise.Slopes + +PiecewiseFormulation +-------------------- + +Returned by :func:`Model.add_piecewise_formulation`. .. autosummary:: :toctree: generated/ @@ -468,7 +473,21 @@ composing chord-based bounds by hand, without going through piecewise.PiecewiseFormulation.convexity piecewise.PiecewiseFormulation.variables piecewise.PiecewiseFormulation.constraints + +Low-level helper +---------------- + +.. autosummary:: + :toctree: generated/ + piecewise.tangent_lines + +Type aliases +------------ + +.. autosummary:: + :toctree: generated/ + constants.PWL_METHOD constants.PWL_METHODS constants.PWL_CONVEXITY From 2455b6c64ff69cadbf825cd1eb8910488e444472 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 13 May 2026 20:27:29 +0200 Subject: [PATCH 25/25] docs: harmonise Variables container labels with Variable; fix preamble cross-refs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename Variables container subgroups so they match Variable's vocabulary where the role is the same: "Aggregate access" → "Attributes" "Bulk modify" → "Modification" (the "Bulk" qualifier is clear from context — the methods live on the container) "Inventory by type" → "Inventory" (matches Constraints' equivalent subsection) - Drop the redundant prose note "Container-wide analogues of Variable.fix, etc." — same reason; clear from context. Also fix the preamble cross-references. The autosummary entries register the documented entities under their full module paths (linopy.model.Model, linopy.variables.Variable, …), so the bare ``:class:`Model``` refs were rendering as plain styled code rather than hyperlinks. Use the qualified ``~linopy..`` form for class refs (display stays "Model" / "Variable" / "Constraint" / "Objective") and the ``Target `` form for methods and attributes so the link text reads "Model.add_variables" etc. Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/api.rst | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 24ba6539..d32c7089 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -5,14 +5,17 @@ API reference ############# Reference for linopy's public API. Most workflows start at -:class:`Model` — variables, constraints, expressions and the -objective are all built through ``model.add_*`` and accessed through -the matching ``model.`` accessor (``model.variables``, -``model.constraints``, ``model.objective``). The supporting classes -below cover the types those accessors return, grouped by what each -one is for (Structure / Modification / Conversion / Post-solve -access). Each entry links to a dedicated page with the full -signature and docstring. +:class:`~linopy.model.Model` — :class:`~linopy.variables.Variable`, +:class:`~linopy.constraints.Constraint`, and +:class:`~linopy.objective.Objective` are all built through +:meth:`Model.add_variables `, +:meth:`Model.add_constraints `, +:meth:`Model.add_objective `, +and accessed through the matching +:attr:`Model.variables `, +:attr:`Model.constraints `, and +:attr:`Model.objective ` accessors. +The supporting classes below cover those types in detail. .. contents:: :local: @@ -171,8 +174,8 @@ Container for the collection of variables on a model. Accessed via variables.Variables -Aggregate access ----------------- +Attributes +---------- .. autosummary:: :toctree: generated/ @@ -181,10 +184,8 @@ Aggregate access variables.Variables.upper variables.Variables.solution -Bulk modify ------------ - -Container-wide analogues of :func:`Variable.fix`, etc. +Modification +------------ .. autosummary:: :toctree: generated/ @@ -194,8 +195,8 @@ Container-wide analogues of :func:`Variable.fix`, etc. variables.Variables.relax variables.Variables.unrelax -Inventory by type ------------------ +Inventory +--------- .. autosummary:: :toctree: generated/