From 106d5733c672fd0cf86876186ae76fc835e34c6a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 19 May 2026 16:33:30 +0200 Subject: [PATCH] ci: swap aflc/pre-commit-jupyter for kynan/nbstripout The old hook (`aflc/pre-commit-jupyter` `jupyter-notebook-cleanup`) was in place but wasn't stripping the per-cell `ExecuteTime` / `execution` timestamps that PyCharm and Jupyter add on every run. Those fields bloated notebook diffs (~180 lines on a recent PR) without adding review value. Swap to `kynan/nbstripout`, configured to also strip `cell.metadata.ExecuteTime` and `cell.metadata.execution`. Default nbstripout behavior (clear outputs + execution counts) is fine here because notebooks are executed in CI by nbsphinx (`nbsphinx_execute = "auto"` in doc/conf.py), so outputs are regenerated for the rendered docs. `examples/solve-on-remote.ipynb` keeps its existing exclusion (its outputs document the remote-handler interaction and can't be regenerated by docs CI without an SSH setup). One-time cleanup applied to all non-excluded notebooks. Co-Authored-By: Claude Opus 4.7 (1M context) --- .pre-commit-config.yaml | 8 +- benchmark/notebooks/plot-benchmarks.py.ipynb | 8 +- examples/coordinate-alignment.ipynb | 42 ++++- .../create-a-model-with-coordinates.ipynb | 34 ++-- examples/create-a-model.ipynb | 48 ++--- examples/creating-constraints.ipynb | 92 ++++++---- examples/creating-expressions.ipynb | 74 ++++---- examples/creating-variables.ipynb | 120 ++++++------- examples/infeasible-model.ipynb | 22 ++- examples/migrating-from-pyomo.ipynb | 18 +- examples/piecewise-inequality-bounds.ipynb | 35 +--- examples/piecewise-linear-constraints.ipynb | 170 ++++++------------ examples/solve-on-oetc.ipynb | 16 +- examples/testing-framework.ipynb | 3 +- examples/transport-tutorial.ipynb | 20 +-- 15 files changed, 333 insertions(+), 377 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e7ddb4b..d011526d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,8 +32,10 @@ repos: - id: codespell types_or: [python, rst, markdown] files: ^(linopy|doc)/ -- repo: https://github.com/aflc/pre-commit-jupyter - rev: v1.2.1 +- repo: https://github.com/kynan/nbstripout + rev: 0.8.1 hooks: - - id: jupyter-notebook-cleanup + - id: nbstripout + args: + - --extra-keys=cell.metadata.ExecuteTime cell.metadata.execution exclude: examples/solve-on-remote.ipynb diff --git a/benchmark/notebooks/plot-benchmarks.py.ipynb b/benchmark/notebooks/plot-benchmarks.py.ipynb index f1099a0b..f61d12e5 100644 --- a/benchmark/notebooks/plot-benchmarks.py.ipynb +++ b/benchmark/notebooks/plot-benchmarks.py.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9a85db47", + "id": "0", "metadata": {}, "outputs": [], "source": [ @@ -19,7 +19,7 @@ { "cell_type": "code", "execution_count": null, - "id": "709bdf49", + "id": "1", "metadata": {}, "outputs": [], "source": [ @@ -31,7 +31,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f36897fb", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -65,7 +65,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c5c93666", + "id": "3", "metadata": {}, "outputs": [], "source": [ diff --git a/examples/coordinate-alignment.ipynb b/examples/coordinate-alignment.ipynb index 1547bd9d..54de243a 100644 --- a/examples/coordinate-alignment.ipynb +++ b/examples/coordinate-alignment.ipynb @@ -125,7 +125,11 @@ { "cell_type": "markdown", "metadata": {}, - "source": "### Same-Shape Operands: Positional Alignment\n\nWhen two operands have the **same shape** on a shared dimension, linopy uses **positional alignment** by default — coordinate labels are ignored and the left operand's labels are kept. This is a performance optimization but can be surprising:" + "source": [ + "### Same-Shape Operands: Positional Alignment\n", + "\n", + "When two operands have the **same shape** on a shared dimension, linopy uses **positional alignment** by default — coordinate labels are ignored and the left operand's labels are kept. This is a performance optimization but can be surprising:" + ] }, { "cell_type": "code", @@ -142,7 +146,9 @@ { "cell_type": "markdown", "metadata": {}, - "source": "Even though ``offset_const`` has coordinates ``[5, 6, 7, 8, 9]`` and ``x`` has ``[0, 1, 2, 3, 4]``, the result uses ``x``'s labels. The values are aligned by **position**, not by label. The same applies when adding two variables or expressions of identical shape:" + "source": [ + "Even though ``offset_const`` has coordinates ``[5, 6, 7, 8, 9]`` and ``x`` has ``[0, 1, 2, 3, 4]``, the result uses ``x``'s labels. The values are aligned by **position**, not by label. The same applies when adding two variables or expressions of identical shape:" + ] }, { "cell_type": "code", @@ -157,7 +163,11 @@ { "cell_type": "markdown", "metadata": {}, - "source": "``x`` (time 0–4) and ``z`` (time 5–9) share no coordinate labels, yet the result has 5 entries under ``x``'s coordinates — because they have the same shape, positions are matched directly.\n\nTo force **label-based** alignment, pass an explicit ``join``:" + "source": [ + "``x`` (time 0–4) and ``z`` (time 5–9) share no coordinate labels, yet the result has 5 entries under ``x``'s coordinates — because they have the same shape, positions are matched directly.\n", + "\n", + "To force **label-based** alignment, pass an explicit ``join``:" + ] }, { "cell_type": "code", @@ -171,7 +181,9 @@ { "cell_type": "markdown", "metadata": {}, - "source": "With ``join=\"outer\"``, the result spans all 10 time steps (union of 0–4 and 5–9), filling missing positions with zeros. This is the correct label-based alignment. The same-shape positional shortcut is equivalent to ``join=\"override\"`` — see below." + "source": [ + "With ``join=\"outer\"``, the result spans all 10 time steps (union of 0–4 and 5–9), filling missing positions with zeros. This is the correct label-based alignment. The same-shape positional shortcut is equivalent to ``join=\"override\"`` — see below." + ] }, { "cell_type": "markdown", @@ -271,7 +283,9 @@ { "cell_type": "markdown", "metadata": {}, - "source": "**Override** — positional alignment, ignore coordinate labels. The result uses the left operand's coordinates. Here ``a`` has i=[0, 1, 2] and ``b`` has i=[1, 2, 3], so positions are matched as 0↔1, 1↔2, 2↔3:" + "source": [ + "**Override** — positional alignment, ignore coordinate labels. The result uses the left operand's coordinates. Here ``a`` has i=[0, 1, 2] and ``b`` has i=[1, 2, 3], so positions are matched as 0↔1, 1↔2, 2↔3:" + ] }, { "cell_type": "code", @@ -369,7 +383,11 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## Practical Example\n\nConsider a generation dispatch model where solar availability follows a daily profile and a minimum demand constraint only applies during peak hours." + "source": [ + "## Practical Example\n", + "\n", + "Consider a generation dispatch model where solar availability follows a daily profile and a minimum demand constraint only applies during peak hours." + ] }, { "cell_type": "code", @@ -405,7 +423,9 @@ { "cell_type": "markdown", "metadata": {}, - "source": "For solar, we build a full 24-hour availability profile — zero at night, sine-shaped during daylight (hours 6–18). Since this covers all hours, standard alignment works directly and solar is properly constrained to zero at night:" + "source": [ + "For solar, we build a full 24-hour availability profile — zero at night, sine-shaped during daylight (hours 6–18). Since this covers all hours, standard alignment works directly and solar is properly constrained to zero at night:" + ] }, { "cell_type": "code", @@ -424,7 +444,9 @@ { "cell_type": "markdown", "metadata": {}, - "source": "Now suppose a minimum demand of 120 MW must be met, but only during peak hours (8–20). The demand array covers a subset of hours, so we use ``join=\"inner\"`` to restrict the constraint to just those hours:" + "source": [ + "Now suppose a minimum demand of 120 MW must be met, but only during peak hours (8–20). The demand array covers a subset of hours, so we use ``join=\"inner\"`` to restrict the constraint to just those hours:" + ] }, { "cell_type": "code", @@ -444,7 +466,9 @@ { "cell_type": "markdown", "metadata": {}, - "source": "The demand constraint only applies during peak hours (8–20). Outside that range, no minimum generation is required." + "source": [ + "The demand constraint only applies during peak hours (8–20). Outside that range, no minimum generation is required." + ] }, { "cell_type": "markdown", diff --git a/examples/create-a-model-with-coordinates.ipynb b/examples/create-a-model-with-coordinates.ipynb index e8021a35..56c37481 100644 --- a/examples/create-a-model-with-coordinates.ipynb +++ b/examples/create-a-model-with-coordinates.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "4db583af", + "id": "0", "metadata": {}, "source": [ "# Use Coordinates\n", @@ -16,7 +16,7 @@ }, { "cell_type": "markdown", - "id": "comparable-talent", + "id": "1", "metadata": {}, "source": [ "Minimize:\n", @@ -36,7 +36,7 @@ }, { "cell_type": "markdown", - "id": "proprietary-receipt", + "id": "2", "metadata": {}, "source": [ "In order to formulate the new problem with linopy, we start again by initializing a model." @@ -45,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "close-maximum", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -56,7 +56,7 @@ }, { "cell_type": "markdown", - "id": "positive-appearance", + "id": "4", "metadata": {}, "source": [ "Again, we define `x` and `y` using the `add_variables` function, but now we are adding a `coords` argument. This automatically creates optimization variables for all coordinates, in this case time-steps." @@ -65,7 +65,7 @@ { "cell_type": "code", "execution_count": null, - "id": "included-religious", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -83,7 +83,7 @@ }, { "cell_type": "markdown", - "id": "terminal-ethernet", + "id": "6", "metadata": {}, "source": [ "Following the previous example, we write the constraints out using the syntax from above, while multiplying the rhs with `t`. Note that the coordinates from the lhs and the rhs have to match. \n", @@ -95,7 +95,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c24d120a", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -106,7 +106,7 @@ }, { "cell_type": "markdown", - "id": "f09803f4", + "id": "8", "metadata": {}, "source": [ "It always helps to write out the constraints before adding them to the model. Since they look good, let's assign them." @@ -115,7 +115,7 @@ { "cell_type": "code", "execution_count": null, - "id": "comprehensive-blend", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -126,7 +126,7 @@ }, { "cell_type": "markdown", - "id": "induced-professor", + "id": "10", "metadata": {}, "source": [ "Now, when it comes to the objective, we use the `sum` function of `linopy.LinearExpression`. This stacks all terms all terms of the `time` dimension and writes them into one big expression. " @@ -135,7 +135,7 @@ { "cell_type": "code", "execution_count": null, - "id": "alternate-story", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -146,7 +146,7 @@ { "cell_type": "code", "execution_count": null, - "id": "outer-presence", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -155,7 +155,7 @@ }, { "cell_type": "markdown", - "id": "495cd082", + "id": "13", "metadata": {}, "source": [ "In order to inspect the solution. You can go via the variables, i.e. `y.solution` or via the `solution` aggregator of the model, which combines the solution of all variables. This can sometimes be helpful." @@ -164,7 +164,7 @@ { "cell_type": "code", "execution_count": null, - "id": "monthly-census", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -173,7 +173,7 @@ }, { "cell_type": "markdown", - "id": "owned-europe", + "id": "15", "metadata": {}, "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." @@ -181,7 +181,7 @@ }, { "cell_type": "markdown", - "id": "4db583af", + "id": "16", "metadata": {}, "source": [ "## Where to next\n", diff --git a/examples/create-a-model.ipynb b/examples/create-a-model.ipynb index b6fc9705..943029d6 100644 --- a/examples/create-a-model.ipynb +++ b/examples/create-a-model.ipynb @@ -3,7 +3,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "4db583af", + "id": "0", "metadata": {}, "source": [ "# Solve a Basic Model\n", @@ -14,7 +14,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "together-ocean", + "id": "1", "metadata": {}, "source": [ "Minimize:\n", @@ -31,7 +31,7 @@ { "cell_type": "code", "execution_count": null, - "id": "dramatic-cannon", + "id": "2", "metadata": {}, "outputs": [], "source": [] @@ -39,7 +39,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "43949d36", + "id": "3", "metadata": {}, "source": [ "### Initializing a `Model`\n", @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "technical-conducting", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -62,7 +62,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "e5b16d53", + "id": "5", "metadata": {}, "source": [ "This creates a new Model object, which you can then use to define your optimization problem." @@ -71,7 +71,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "rolled-delicious", + "id": "6", "metadata": {}, "source": [ "\n", @@ -84,7 +84,7 @@ { "cell_type": "code", "execution_count": null, - "id": "protecting-power", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -95,7 +95,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "featured-maria", + "id": "8", "metadata": {}, "source": [ "`x` and `y` are linopy variables of the class `linopy.Variable`. Each of them contain all relevant information that define it. The `name` parameter is optional but can be useful for referencing the variables later." @@ -104,7 +104,7 @@ { "cell_type": "code", "execution_count": null, - "id": "virtual-anxiety", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +114,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "sonic-rebate", + "id": "10", "metadata": {}, "source": [ "Since both `x` and `y` are scalar variables (meaning they don't have any dimensions), their underlying data contain only one optimization variable each. \n", @@ -128,7 +128,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fbb46cad", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -138,7 +138,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "f4666bee", + "id": "12", "metadata": {}, "source": [ "Note, we can also mix the constant and the variable expression, like this" @@ -147,7 +147,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60f41b76", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -157,7 +157,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "02abd938", + "id": "14", "metadata": {}, "source": [ "... and linopy will automatically take over the separation of variables expression on the lhs, and constant values on the rhs.\n", @@ -168,7 +168,7 @@ { "cell_type": "code", "execution_count": null, - "id": "hollywood-production", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -179,7 +179,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "global-maple", + "id": "16", "metadata": {}, "source": [ "## Adding the Objective \n", @@ -190,7 +190,7 @@ { "cell_type": "code", "execution_count": null, - "id": "overall-exhibition", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -200,7 +200,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "6f9692aa", + "id": "18", "metadata": {}, "source": [ "## Solving the Model\n", @@ -211,7 +211,7 @@ { "cell_type": "code", "execution_count": null, - "id": "pressing-copying", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -221,7 +221,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "preceding-limit", + "id": "20", "metadata": {}, "source": [ "The solution of the linear problem assigned to the variables under `solution` in form of a `xarray.Dataset`. " @@ -230,7 +230,7 @@ { "cell_type": "code", "execution_count": null, - "id": "electric-duration", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -240,7 +240,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e6d31751", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -250,7 +250,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "e296f641", + "id": "23", "metadata": {}, "source": [ "Well done! You solved your first linopy model!" diff --git a/examples/creating-constraints.ipynb b/examples/creating-constraints.ipynb index 05e2a899..1b792b14 100644 --- a/examples/creating-constraints.ipynb +++ b/examples/creating-constraints.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "e8249281", + "id": "0", "metadata": {}, "source": [ "# Creating Constraints\n", @@ -18,7 +18,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e0c196e4", + "id": "1", "metadata": {}, "outputs": [], "source": [ @@ -29,7 +29,7 @@ }, { "cell_type": "markdown", - "id": "043c0b06", + "id": "2", "metadata": {}, "source": [ "Given a variable `x` which has to by lower than 10/3, the constraint would be formulated as \n", @@ -51,7 +51,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6b496b92", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -60,7 +60,7 @@ }, { "cell_type": "markdown", - "id": "73541c03", + "id": "4", "metadata": {}, "source": [ "When applying one of the operators `<=`, `>=`, `==` to the expression, an unassigned constraint is built:" @@ -69,7 +69,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4c8aba7e", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -79,7 +79,7 @@ }, { "cell_type": "markdown", - "id": "0d75781d", + "id": "6", "metadata": {}, "source": [ "Unasssigned means, it is not yet added to the model. We can inspect the elements of the anonymous constraint: " @@ -88,7 +88,7 @@ { "cell_type": "code", "execution_count": null, - "id": "01f182b5", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -98,7 +98,7 @@ { "cell_type": "code", "execution_count": null, - "id": "783287b3", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -107,7 +107,7 @@ }, { "cell_type": "markdown", - "id": "aac468c3", + "id": "9", "metadata": {}, "source": [ "We can now add the constraint to the model by passing the unassigned `Constraint` to the `.add_constraint` function. " @@ -116,7 +116,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0adf929b", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -126,7 +126,7 @@ }, { "cell_type": "markdown", - "id": "e78c2635", + "id": "11", "metadata": {}, "source": [ "The same output would be generated if passing lhs, sign and rhs as separate arguments to the function:" @@ -135,7 +135,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c084adec", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -144,7 +144,7 @@ }, { "cell_type": "markdown", - "id": "2b4db4d5", + "id": "13", "metadata": {}, "source": [ "Note that the return value of the operation is a `Constraint` which contains the reference labels to the constraints in the optimization model. Also is redirects to its lhs, sign and rhs, for example we can call" @@ -153,7 +153,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ea6e990c", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -162,7 +162,7 @@ }, { "cell_type": "markdown", - "id": "e6ae2a19", + "id": "15", "metadata": {}, "source": [ "to inspect the lhs of a defined constraint." @@ -170,7 +170,7 @@ }, { "cell_type": "markdown", - "id": "efb74da3", + "id": "16", "metadata": {}, "source": [ "When moving the constant value to the left hand side in the initialization, it will be pulled to the right hand side as soon as the constraint is defined" @@ -179,7 +179,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e582051e", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -189,7 +189,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e2c2dbb3", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -198,7 +198,7 @@ }, { "cell_type": "markdown", - "id": "15909055", + "id": "19", "metadata": {}, "source": [ "Like this, the all defined constraints have a clear separation between variable on the left, and constants on the right. " @@ -206,7 +206,7 @@ }, { "cell_type": "markdown", - "id": "b9d31509", + "id": "20", "metadata": {}, "source": [ "All constraints are added to the `.constraints` container from where all assigned constraints can be accessed." @@ -215,7 +215,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d205e695", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -225,7 +225,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cc5baaf4", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -234,13 +234,17 @@ }, { "cell_type": "markdown", - "id": "r0wxi7v1m7l", + "id": "23", "metadata": {}, - "source": "## Coordinate Alignment in Constraints\n\nAs an alternative to the ``<=``, ``>=``, ``==`` operators, linopy provides ``.le()``, ``.ge()``, and ``.eq()`` methods on variables and expressions. These methods accept a ``join`` parameter (``\"inner\"``, ``\"outer\"``, ``\"left\"``, ``\"right\"``) for explicit control over how coordinates are aligned when creating constraints. See the :doc:`coordinate-alignment` guide for details." + "source": [ + "## Coordinate Alignment in Constraints\n", + "\n", + "As an alternative to the ``<=``, ``>=``, ``==`` operators, linopy provides ``.le()``, ``.ge()``, and ``.eq()`` methods on variables and expressions. These methods accept a ``join`` parameter (``\"inner\"``, ``\"outer\"``, ``\"left\"``, ``\"right\"``) for explicit control over how coordinates are aligned when creating constraints. See the :doc:`coordinate-alignment` guide for details." + ] }, { "cell_type": "markdown", - "id": "csr-backend-intro", + "id": "24", "metadata": {}, "source": [ "## CSR Backend (Advanced)\n", @@ -254,7 +258,7 @@ }, { "cell_type": "markdown", - "id": "csr-per-constraint", + "id": "25", "metadata": {}, "source": [ "### Freezing individual constraints\n", @@ -265,7 +269,7 @@ { "cell_type": "code", "execution_count": null, - "id": "csr-per-constraint-code", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -284,7 +288,7 @@ }, { "cell_type": "markdown", - "id": "csr-global", + "id": "27", "metadata": {}, "source": [ "### Freezing all constraints globally\n", @@ -295,7 +299,7 @@ { "cell_type": "code", "execution_count": null, - "id": "csr-global-code", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -310,7 +314,7 @@ }, { "cell_type": "markdown", - "id": "csr-roundtrip", + "id": "29", "metadata": {}, "source": [ "### Converting between representations\n", @@ -321,7 +325,7 @@ { "cell_type": "code", "execution_count": null, - "id": "csr-roundtrip-code", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -337,13 +341,29 @@ }, { "cell_type": "markdown", - "id": "7843d42c", - "source": "### API differences from `Constraint`\n\n`CSRConstraint` deliberately exposes a narrower API than the xarray-backed `Constraint`:\n\n- **No in-place mutation.** Setters such as `con.coeffs = ...`, `con.vars = ...`, `con.sign = ...`, `con.rhs = ...`, and `con.lhs = ...` are only available on `Constraint`.\n- **No label-based indexing.** `con.loc[...]` is only available on `Constraint`.\n- **Accessing `.coeffs` / `.vars` triggers reconstruction.** On a `CSRConstraint` these properties rebuild the full xarray `Dataset` on demand and emit a `PerformanceWarning`. For solver-oriented workflows prefer `con.to_matrix()` or work with the CSR data directly.\n\nIf you need any of the above, call `.mutable()` first to get a `Constraint`:\n\n```python\ncon = m.constraints[\"my_constraint\"].mutable()\ncon.loc[{\"time\": 0}] # label-based indexing now available\ncon.rhs = 5 # mutation now available\n```", - "metadata": {} + "id": "31", + "metadata": {}, + "source": [ + "### API differences from `Constraint`\n", + "\n", + "`CSRConstraint` deliberately exposes a narrower API than the xarray-backed `Constraint`:\n", + "\n", + "- **No in-place mutation.** Setters such as `con.coeffs = ...`, `con.vars = ...`, `con.sign = ...`, `con.rhs = ...`, and `con.lhs = ...` are only available on `Constraint`.\n", + "- **No label-based indexing.** `con.loc[...]` is only available on `Constraint`.\n", + "- **Accessing `.coeffs` / `.vars` triggers reconstruction.** On a `CSRConstraint` these properties rebuild the full xarray `Dataset` on demand and emit a `PerformanceWarning`. For solver-oriented workflows prefer `con.to_matrix()` or work with the CSR data directly.\n", + "\n", + "If you need any of the above, call `.mutable()` first to get a `Constraint`:\n", + "\n", + "```python\n", + "con = m.constraints[\"my_constraint\"].mutable()\n", + "con.loc[{\"time\": 0}] # label-based indexing now available\n", + "con.rhs = 5 # mutation now available\n", + "```" + ] }, { "cell_type": "markdown", - "id": "csr-when-to-use", + "id": "32", "metadata": {}, "source": [ "### When to use the CSR backend\n", diff --git a/examples/creating-expressions.ipynb b/examples/creating-expressions.ipynb index d0bf0db4..370f3f74 100644 --- a/examples/creating-expressions.ipynb +++ b/examples/creating-expressions.ipynb @@ -3,7 +3,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "4db583af", + "id": "0", "metadata": {}, "source": [ "# Creating Expressions\n", @@ -25,7 +25,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "f3712718", + "id": "1", "metadata": {}, "source": [ ".. hint::\n", @@ -35,7 +35,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "95b25d13", + "id": "2", "metadata": {}, "source": [ "Let's start by creating a model." @@ -44,7 +44,7 @@ { "cell_type": "code", "execution_count": null, - "id": "close-maximum", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -65,7 +65,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "582a0cad", + "id": "4", "metadata": {}, "source": [ "## Arithmetic Operations\n", @@ -78,7 +78,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0aec195a", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -89,7 +89,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "8c4b9ea6", + "id": "6", "metadata": {}, "source": [ ".. note::\n", @@ -99,7 +99,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "a1de2b9a", + "id": "7", "metadata": {}, "source": [ "Similarly, you can subtract `y` from `x` or multiply `x` and `y` as follows:" @@ -108,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c56761cd", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +119,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b59fa397", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -130,7 +130,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "0ca00a73", + "id": "10", "metadata": {}, "source": [ "In all cases, the returned shape is the same. Note that, the output type of the multiplication is a `QuadraticExpression` and not a `LinearExpression`.\n" @@ -139,7 +139,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "3ff0d1cd", + "id": "11", "metadata": {}, "source": [ "The `z` expression, which carries along `x` and `y`, has different attributes such as `coord_dims`, `dims`, `size`." @@ -148,7 +148,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35c7331f", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -158,7 +158,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "f7578221", + "id": "13", "metadata": {}, "source": [ ".. important::\n", @@ -168,7 +168,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8c511f35", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -180,7 +180,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "59f43468", + "id": "15", "metadata": {}, "source": [ "`b` has the same shape as `x`, but they have different coordinates. When we combine `x` and `b` the coordinates on dimension `time` will be taken from the first object and the coordinates of the subsequent object will be ignored:" @@ -189,7 +189,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26edd6ab", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -198,17 +198,17 @@ }, { "cell_type": "markdown", - "id": "a8xsfdqrcrn", + "id": "17", + "metadata": {}, "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": {} + ] }, { "attachments": {}, "cell_type": "markdown", - "id": "de6d3073", + "id": "18", "metadata": {}, "source": [ "## Using `.loc` to select a subset\n", @@ -221,7 +221,7 @@ { "cell_type": "code", "execution_count": null, - "id": "93119cfc", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -231,7 +231,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ae6a1b29", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -241,7 +241,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "14b02f7d", + "id": "21", "metadata": {}, "source": [ "which is the same as" @@ -250,7 +250,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7281f08c", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -261,7 +261,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "c3c97abf", + "id": "23", "metadata": {}, "source": [ "In combination with the overwrite of the coordinates, this is useful when you need to combine different selections, like" @@ -270,7 +270,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27063ea9", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -280,7 +280,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "cdb53f49", + "id": "25", "metadata": {}, "source": [ "## Using `.where` to select active variables or expressions\n", @@ -293,7 +293,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ab8f59fd", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -304,7 +304,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "3ee10a58", + "id": "27", "metadata": {}, "source": [ "We can use this to make a conditional summation:" @@ -313,7 +313,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21fa9664", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -323,7 +323,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "652973ea", + "id": "29", "metadata": {}, "source": [ "## Using `.shift` to shift the Variable along one dimension\n", @@ -336,7 +336,7 @@ { "cell_type": "code", "execution_count": null, - "id": "organized-hampshire", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -346,7 +346,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "eaf3f38c", + "id": "31", "metadata": {}, "source": [ "## Using `.groupby` to group by a key and apply operations on the groups\n", @@ -359,7 +359,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5170d187", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -370,7 +370,7 @@ { "attachments": {}, "cell_type": "markdown", - "id": "7ded9a54", + "id": "33", "metadata": {}, "source": [ "## Using `.rolling` to perform a rolling operation\n", @@ -383,7 +383,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d703fb70", + "id": "34", "metadata": {}, "outputs": [], "source": [ diff --git a/examples/creating-variables.ipynb b/examples/creating-variables.ipynb index 9179a31a..1c8b53d0 100644 --- a/examples/creating-variables.ipynb +++ b/examples/creating-variables.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "e8249281", + "id": "0", "metadata": {}, "source": [ "# Creating Variables\n", @@ -18,7 +18,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e0c196e4", + "id": "1", "metadata": {}, "outputs": [], "source": [ @@ -32,14 +32,14 @@ ] }, { - "metadata": {}, "cell_type": "markdown", - "source": "", - "id": "46c4f2824a2ed8aa" + "id": "2", + "metadata": {}, + "source": [] }, { "cell_type": "markdown", - "id": "6c6420a7", + "id": "3", "metadata": {}, "source": [ "First of all it is crucial to know, that the return value of the `.add_variables` function is a `linopy.Variable` which itself contains all important information and provides helpful functions. It can have an arbitrary number of labeled dimensions. For each combination of coordinates, exactly one representative scalar variable is defined and, in the end, passed to the solver. \n", @@ -69,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "a2283b9a", + "id": "4", "metadata": {}, "source": [ "Let's start by creating a simple variable:\n", @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ee589323", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "markdown", - "id": "708920a3", + "id": "6", "metadata": {}, "source": [ "which is a variable without any coordinates and with just one optimization variable. The variable name is set by `name = 'x'`. " @@ -98,7 +98,7 @@ }, { "cell_type": "markdown", - "id": "b276e45d", + "id": "7", "metadata": {}, "source": [ "Like this the variable appears with its name when defining expression with it:" @@ -107,7 +107,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68d7e7da", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -116,7 +116,7 @@ }, { "cell_type": "markdown", - "id": "528a7c40", + "id": "9", "metadata": {}, "source": [ "We can alter the lower and upper bounds of the variable by assigning scalar values to them." @@ -125,7 +125,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a1c080e0", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -134,7 +134,7 @@ }, { "cell_type": "markdown", - "id": "885ac764", + "id": "11", "metadata": {}, "source": [ "### Variable Types\n", @@ -145,7 +145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8a5d4543", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -154,7 +154,7 @@ }, { "cell_type": "markdown", - "id": "3af58bc4", + "id": "13", "metadata": {}, "source": [ ".. note::\n", @@ -167,7 +167,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ff64db81", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -176,7 +176,7 @@ }, { "cell_type": "markdown", - "id": "7b432107", + "id": "15", "metadata": {}, "source": [ "### Working with dimensions\n", @@ -187,7 +187,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b4dfc46d", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -198,7 +198,7 @@ }, { "cell_type": "markdown", - "id": "1ff347f9", + "id": "17", "metadata": {}, "source": [ "The returned `Variable` now has the same shape as the `lower` bound that we passed to the initialization. Since we did not specify any dimension name, it defaults to `dim_0`. In order to give the dimension a proper name we can use the `dims` argument. " @@ -207,7 +207,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f93e5c08", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -217,7 +217,7 @@ }, { "cell_type": "markdown", - "id": "d4b20bb5", + "id": "19", "metadata": {}, "source": [ "You can arbitrarily broadcast dimensions when passing DataArray's with different set of dimensions. Let's do it and give `lower` another dimension than `upper`:" @@ -226,7 +226,7 @@ { "cell_type": "code", "execution_count": null, - "id": "71584630", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -237,7 +237,7 @@ }, { "cell_type": "markdown", - "id": "3e4e48c0", + "id": "21", "metadata": {}, "source": [ "Now instead of a single dimension, we end up with two dimensions `my-dim` and `my-dim-2` in the variable. This kind of **broadcasting** is a deeply incorporated in the functionality of linopy. " @@ -245,7 +245,7 @@ }, { "cell_type": "markdown", - "id": "41893f11", + "id": "22", "metadata": {}, "source": [ "We recall that, in order to improve the inspection, it is encouraged to define a `name` when creating a variable. So in your model you would rather write something like:" @@ -254,7 +254,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e8857233", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -265,7 +265,7 @@ }, { "cell_type": "markdown", - "id": "437cc7a1", + "id": "24", "metadata": {}, "source": [ "#### Initializing variables with numpy arrays\n", @@ -276,10 +276,8 @@ { "cell_type": "code", "execution_count": null, - "id": "0fe33c34", - "metadata": { - "scrolled": true - }, + "id": "25", + "metadata": {}, "outputs": [], "source": [ "lower = np.array([1, 2])\n", @@ -289,7 +287,7 @@ }, { "cell_type": "markdown", - "id": "2ab6d301", + "id": "26", "metadata": {}, "source": [ "This is equivalent to the following" @@ -298,7 +296,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b8313ce0", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -310,7 +308,7 @@ }, { "cell_type": "markdown", - "id": "5052b9b5", + "id": "28", "metadata": {}, "source": [ "Note that \n", @@ -329,7 +327,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5f0994da", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -339,7 +337,7 @@ }, { "cell_type": "markdown", - "id": "dff8126d", + "id": "30", "metadata": {}, "source": [ "The dimension is now called `dim_0`, any new assignment of variable without dimension names, will also use that dimension name. When combining the variables to expressions it is important that you make sure that dimension names represent what they should. \n", @@ -351,7 +349,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d133a7a4", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -364,7 +362,7 @@ }, { "cell_type": "markdown", - "id": "9203ff16", + "id": "32", "metadata": {}, "source": [ "#### Initializing variables with Pandas objects\n", @@ -375,7 +373,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2cf719be", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -386,7 +384,7 @@ }, { "cell_type": "markdown", - "id": "3a4cf2d4", + "id": "34", "metadata": {}, "source": [ "or naming the indexes and columns of the pandas objects directly, e.g." @@ -395,7 +393,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61896a6f", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -406,7 +404,7 @@ }, { "cell_type": "markdown", - "id": "2b462ff9", + "id": "36", "metadata": {}, "source": [ ".. note::\n", @@ -416,10 +414,8 @@ { "cell_type": "code", "execution_count": null, - "id": "6ffd5a4e", - "metadata": { - "scrolled": true - }, + "id": "37", + "metadata": {}, "outputs": [], "source": [ "lower = pd.Series([1, 1]).rename_axis(\"my-dim\")\n", @@ -429,7 +425,7 @@ }, { "cell_type": "markdown", - "id": "31bbdbab", + "id": "38", "metadata": {}, "source": [ "Now instead of 2 variables, 4 variables were defined. \n", @@ -440,7 +436,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fa2adc81", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -451,7 +447,7 @@ }, { "cell_type": "markdown", - "id": "8b1734df", + "id": "40", "metadata": {}, "source": [ "Again, one is always safer when explicitly naming the dimensions:" @@ -460,7 +456,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21f7db15", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -471,7 +467,7 @@ }, { "cell_type": "markdown", - "id": "e8249281", + "id": "42", "metadata": {}, "source": [ ".. note::\n", @@ -481,7 +477,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d0fc67cf", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -491,7 +487,7 @@ }, { "cell_type": "markdown", - "id": "49de1cc3", + "id": "44", "metadata": {}, "source": [ "### Masking Arrays\n", @@ -506,7 +502,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9d802903", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -522,7 +518,7 @@ { "cell_type": "code", "execution_count": null, - "id": "447d8a8a", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -534,7 +530,7 @@ }, { "cell_type": "markdown", - "id": "df1c551c", + "id": "47", "metadata": {}, "source": [ "Now the diagonal values, for example at the variable at [a,a], are `None`. " @@ -542,7 +538,7 @@ }, { "cell_type": "markdown", - "id": "23a040d4", + "id": "48", "metadata": {}, "source": [ "### Accessing assigned variables\n", @@ -553,7 +549,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2946a80c", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -562,7 +558,7 @@ }, { "cell_type": "markdown", - "id": "45cf0755", + "id": "50", "metadata": {}, "source": [ "You can always access the variables from the `.variables` container either by get-item, i.e." @@ -571,7 +567,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d974727d", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -580,7 +576,7 @@ }, { "cell_type": "markdown", - "id": "03182836", + "id": "52", "metadata": {}, "source": [ "or by attribute accessing" @@ -589,7 +585,7 @@ { "cell_type": "code", "execution_count": null, - "id": "308b879a", + "id": "53", "metadata": {}, "outputs": [], "source": [ diff --git a/examples/infeasible-model.ipynb b/examples/infeasible-model.ipynb index def2113c..8766ac78 100644 --- a/examples/infeasible-model.ipynb +++ b/examples/infeasible-model.ipynb @@ -19,7 +19,24 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": "import pandas as pd\n\nimport linopy\n\nm = linopy.Model()\n\ntime = pd.RangeIndex(10, name=\"time\")\nx = m.add_variables(lower=0, coords=[time], name=\"x\")\ny = m.add_variables(lower=0, coords=[time], name=\"y\")\n\nm.add_constraints(x <= 5)\nm.add_constraints(y <= 5)\nm.add_constraints(x + y >= 12)\n\n# A trivial objective is required; the model is solved purely to check feasibility.\nm.add_objective(0 * x)" + "source": [ + "import pandas as pd\n", + "\n", + "import linopy\n", + "\n", + "m = linopy.Model()\n", + "\n", + "time = pd.RangeIndex(10, name=\"time\")\n", + "x = m.add_variables(lower=0, coords=[time], name=\"x\")\n", + "y = m.add_variables(lower=0, coords=[time], name=\"y\")\n", + "\n", + "m.add_constraints(x <= 5)\n", + "m.add_constraints(y <= 5)\n", + "m.add_constraints(x + y >= 12)\n", + "\n", + "# A trivial objective is required; the model is solved purely to check feasibility.\n", + "m.add_objective(0 * x)" + ] }, { "attachments": {}, @@ -108,8 +125,7 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.3" - }, - "orig_nbformat": 4 + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/migrating-from-pyomo.ipynb b/examples/migrating-from-pyomo.ipynb index 3d34ce60..c3535a40 100644 --- a/examples/migrating-from-pyomo.ipynb +++ b/examples/migrating-from-pyomo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "ded90143", + "id": "0", "metadata": {}, "source": [ "## Migrating from Pyomo\n", @@ -13,7 +13,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19f3b954", + "id": "1", "metadata": {}, "outputs": [], "source": [ @@ -30,7 +30,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2bbfd13b", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -39,7 +39,7 @@ }, { "cell_type": "markdown", - "id": "a1631a76", + "id": "3", "metadata": {}, "source": [ ".. important::\n", @@ -53,7 +53,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4ed6eafb", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "4faecead", + "id": "5", "metadata": {}, "source": [ "Note that the function's first argument has to be the model itself, even though it might not be used in the function." @@ -78,7 +78,7 @@ }, { "cell_type": "markdown", - "id": "d7368607", + "id": "6", "metadata": {}, "source": [ "This functionality is also supported by the `.add_constraints` function. When passing a function as a first argument, `.add_constraints` expects `coords` to by non-empty. The function itself has to return a `AnonymousScalarConstraint`, as done by " @@ -87,7 +87,7 @@ { "cell_type": "code", "execution_count": null, - "id": "eeebb710", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -97,7 +97,7 @@ { "cell_type": "code", "execution_count": null, - "id": "087203ad", + "id": "8", "metadata": {}, "outputs": [], "source": [ diff --git a/examples/piecewise-inequality-bounds.ipynb b/examples/piecewise-inequality-bounds.ipynb index d1ca4e79..59ec23b9 100644 --- a/examples/piecewise-inequality-bounds.ipynb +++ b/examples/piecewise-inequality-bounds.ipynb @@ -33,12 +33,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2026-04-22T19:56:59.320352Z", - "start_time": "2026-04-22T19:56:58.210364Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "import warnings\n", @@ -66,12 +61,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2026-04-22T19:56:59.427867Z", - "start_time": "2026-04-22T19:56:59.325080Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "power_pts = np.array([0.0, 30.0, 60.0, 100.0])\n", @@ -98,12 +88,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2026-04-22T19:56:59.813355Z", - "start_time": "2026-04-22T19:56:59.434516Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "def solve(method, power_val):\n", @@ -156,12 +141,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2026-04-22T19:57:00.004147Z", - "start_time": "2026-04-22T19:56:59.819631Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "def in_epigraph(px, fy):\n", @@ -221,12 +201,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2026-04-22T19:57:00.225061Z", - "start_time": "2026-04-22T19:57:00.167623Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "# 1. Non-convex curve: auto falls back (LP relaxation would be loose)\n", diff --git a/examples/piecewise-linear-constraints.ipynb b/examples/piecewise-linear-constraints.ipynb index 8b4f56ca..ef26a55b 100644 --- a/examples/piecewise-linear-constraints.ipynb +++ b/examples/piecewise-linear-constraints.ipynb @@ -31,12 +31,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:54.620516Z", - "start_time": "2026-05-11T18:01:54.613427Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "import warnings\n", "\n", @@ -62,9 +59,7 @@ " ax.set(xlabel=xlabel, ylabel=ylabel)\n", " ax.legend()\n", " return ax" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -77,12 +72,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:54.730Z", - "start_time": "2026-05-11T18:01:54.625751Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "demand = xr.DataArray([50, 80, 30], coords=[time])\n", "\n", @@ -99,23 +91,16 @@ "\n", "print(pwf) # inspect the auto-resolved method\n", "m.solution[[\"power\", \"fuel\"]].to_pandas()" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:54.780034Z", - "start_time": "2026-05-11T18:01:54.735021Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "plot_curve(x_pts, y_pts, m.solution[\"power\"].values, m.solution[\"fuel\"].values);" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -132,12 +117,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.102903Z", - "start_time": "2026-05-11T18:01:54.783092Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "def solve_method(method):\n", " m = linopy.Model()\n", @@ -151,9 +133,7 @@ "\n", "\n", "pd.DataFrame({m: solve_method(m) for m in [\"auto\", \"sos2\", \"incremental\"]})" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -170,13 +150,9 @@ }, { "cell_type": "code", - "metadata": { - "scrolled": true, - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.257839Z", - "start_time": "2026-05-11T18:01:55.114836Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "pumps = pd.Index([\"p1\", \"p2\"], name=\"pump\")\n", "\n", @@ -197,9 +173,7 @@ "sol = m.solution[[\"flow\", \"power\"]].to_dataframe().unstack(\"pump\")\n", "sol.columns = [f\"{var}_{p}\" for var, p in sol.columns]\n", "sol" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -227,12 +201,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.331357Z", - "start_time": "2026-05-11T18:01:55.269Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "m = linopy.Model()\n", "power = m.add_variables(name=\"power\", lower=0, upper=100, coords=[time])\n", @@ -249,18 +220,13 @@ "\n", "print(f\"resolved method={pwf.method}, curvature={pwf.convexity}\")\n", "m.solution[[\"power\", \"fuel\"]].to_pandas()" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.381548Z", - "start_time": "2026-05-11T18:01:55.337053Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "plot_curve(\n", " [0, 30, 60, 100],\n", @@ -268,9 +234,7 @@ " m.solution[\"power\"].values,\n", " m.solution[\"fuel\"].values,\n", ");" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -286,12 +250,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.558321Z", - "start_time": "2026-05-11T18:01:55.386257Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "m = linopy.Model()\n", "p_min, p_max = 30, 100\n", @@ -313,23 +274,16 @@ "m.add_objective(fuel.sum() + 50 * commit.sum() + 200 * backup.sum())\n", "m.solve(solver_name=\"highs\", reformulate_sos=\"auto\", output_flag=False)\n", "m.solution[[\"commit\", \"power\", \"fuel\", \"backup\"]].to_pandas()" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.609973Z", - "start_time": "2026-05-11T18:01:55.564366Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "plot_curve(x_pts, y_pts, m.solution[\"power\"].values, m.solution[\"fuel\"].values);" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -342,12 +296,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.731111Z", - "start_time": "2026-05-11T18:01:55.619583Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "m = linopy.Model()\n", "power = m.add_variables(name=\"power\", lower=0, upper=100, coords=[time])\n", @@ -366,18 +317,13 @@ "m.add_objective(power.sum())\n", "m.solve(solver_name=\"highs\", reformulate_sos=\"auto\", output_flag=False)\n", "m.solution[[\"power\", \"fuel\", \"heat\"]].to_pandas().round(2)" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.811271Z", - "start_time": "2026-05-11T18:01:55.738346Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "fig, axes = plt.subplots(1, 2, figsize=(8, 3))\n", "plot_curve(\n", @@ -391,9 +337,7 @@ " ylabel=\"heat\",\n", " ax=axes[1],\n", ");" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -406,12 +350,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:55.957075Z", - "start_time": "2026-05-11T18:01:55.820261Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "gens = pd.Index([\"gas\", \"coal\"], name=\"gen\")\n", "x_gen = linopy.breakpoints(\n", @@ -429,9 +370,7 @@ "m.add_objective(fuel.sum())\n", "m.solve(solver_name=\"highs\", reformulate_sos=\"auto\", output_flag=False)\n", "m.solution[[\"power\", \"fuel\"]].to_dataframe()" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -444,12 +383,9 @@ }, { "cell_type": "code", - "metadata": { - "ExecuteTime": { - "end_time": "2026-05-11T18:01:56.057514Z", - "start_time": "2026-05-11T18:01:55.964137Z" - } - }, + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "m = linopy.Model()\n", "power = m.add_variables(name=\"power\", lower=0, upper=100, coords=[time])\n", @@ -464,9 +400,7 @@ "m.solve(solver_name=\"highs\", reformulate_sos=\"auto\", output_flag=False)\n", "\n", "m.solution[[\"power\", \"fuel\"]].to_pandas()" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", diff --git a/examples/solve-on-oetc.ipynb b/examples/solve-on-oetc.ipynb index f6c5c67d..28e1c04d 100644 --- a/examples/solve-on-oetc.ipynb +++ b/examples/solve-on-oetc.ipynb @@ -88,8 +88,8 @@ "\n", "There are two ways to configure OETC settings:\n", "\n", - "1. **Manual construction** \u2014 build `OetcCredentials` and `OetcSettings` explicitly\n", - "2. **`OetcSettings.from_env()`** \u2014 resolve credentials and options from environment variables\n", + "1. **Manual construction** — build `OetcCredentials` and `OetcSettings` explicitly\n", + "2. **`OetcSettings.from_env()`** — resolve credentials and options from environment variables\n", "\n", "### Option 1: Manual Construction" ] @@ -161,6 +161,7 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -174,8 +175,7 @@ " cpu_cores=8,\n", " disk_space_gb=50,\n", ")" - ], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -277,8 +277,8 @@ "\n", "Solver name and options can be configured at two levels:\n", "\n", - "1. **Settings level** \u2014 defaults stored in `OetcSettings.solver` and `OetcSettings.solver_options`\n", - "2. **Call level** \u2014 passed via `m.solve(solver_name=..., **solver_options)`\n", + "1. **Settings level** — defaults stored in `OetcSettings.solver` and `OetcSettings.solver_options`\n", + "2. **Call level** — passed via `m.solve(solver_name=..., **solver_options)`\n", "\n", "Call-level options **override** settings-level options. The two dicts are\n", "merged (call-time takes precedence), and the original settings are never\n", @@ -287,6 +287,7 @@ }, { "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -316,8 +317,7 @@ " TimeLimit=300,\n", " Threads=4,\n", ")" - ], - "execution_count": null + ] }, { "cell_type": "markdown", diff --git a/examples/testing-framework.ipynb b/examples/testing-framework.ipynb index e8181330..5517557d 100644 --- a/examples/testing-framework.ipynb +++ b/examples/testing-framework.ipynb @@ -132,8 +132,7 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.3" - }, - "orig_nbformat": 4 + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/examples/transport-tutorial.ipynb b/examples/transport-tutorial.ipynb index cd5cdccd..8fdfbe9b 100644 --- a/examples/transport-tutorial.ipynb +++ b/examples/transport-tutorial.ipynb @@ -76,9 +76,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Import of linopy and related modules\n", @@ -114,9 +112,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "## Define sets ##\n", @@ -222,9 +218,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Parameter c(i,j) transport cost in thousands of dollars per case ;\n", @@ -313,9 +307,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "## Define contraints ##\n", @@ -360,9 +352,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "## Define Objective and solve ##\n",