PyPSA / linopy

Linear optimization with N-D labeled arrays in Python
https://linopy.readthedocs.io
MIT License
154 stars 42 forks source link

Missing dimension when initializing variable with boundaries of lower dimensions #255

Closed leuchtum closed 4 months ago

leuchtum commented 4 months ago

Issue

When initializing a variable over two dimensions and restricting it with bounds defined over only one dimension, the variable is missing a dimension. I'm not sure if this is intentional behavior, but it feels quite unintuitive to me.

Initialize example

import linopy
import xarray as xr

dimA = xr.DataArray(["a1", "a2", "a3"], dims=["dimA"])
dimB = xr.DataArray(["b1", "b2"], dims=["dimB"])

a_lb = xr.DataArray([-1, -2, -3], coords=[dimA])
a_ub = xr.DataArray([1, 2, 3], coords=[dimA])

Result using a_lb and a_ub -> Missing dimension dimB

m = linopy.Model()
x = m.add_variables(a_lb, a_ub, name="x", coords=[dimA, dimB])
print(x)
Variable (dimA: 3)
------------------
[a1]: x[a1] ∈ [-1, 1]
[a2]: x[a2] ∈ [-2, 2]
[a3]: x[a3] ∈ [-3, 3]

Fix 1: Add constraints later

m = linopy.Model()
x = m.add_variables(name="x", coords=[dimA, dimB])
m.add_constraints(x >= a_lb)
m.add_constraints(x <= a_ub)
print(x)
Variable (dimA: 3, dimB: 2)
---------------------------
[a1, b1]: x[a1, b1] ∈ [-inf, inf]
[a1, b2]: x[a1, b2] ∈ [-inf, inf]
[a2, b1]: x[a2, b1] ∈ [-inf, inf]
[a2, b2]: x[a2, b2] ∈ [-inf, inf]
[a3, b1]: x[a3, b1] ∈ [-inf, inf]
[a3, b2]: x[a3, b2] ∈ [-inf, inf]

Fix 2: Modify a_lb and a_ub

m = linopy.Model()
x = m.add_variables(
    a_lb.expand_dims(dimB=dimB),
    a_ub.expand_dims(dimB=dimB),
    name="x",
    coords=[dimA, dimB],
)
print(x)
Variable (dimB: 2, dimA: 3)
---------------------------
[b1, a1]: x[b1, a1] ∈ [-1, 1]
[b1, a2]: x[b1, a2] ∈ [-2, 2]
[b1, a3]: x[b1, a3] ∈ [-3, 3]
[b2, a1]: x[b2, a1] ∈ [-1, 1]
[b2, a2]: x[b2, a2] ∈ [-2, 2]
[b2, a3]: x[b2, a3] ∈ [-3, 3]
FabianHofmann commented 4 months ago

Hey @leuchtum, this behavior is intended. As soon as DataArray are passed as lower and upper bounds, the coords and dims arguments are ignored, as the objects already provide full information about the coordinates. Considering the coords and dims on top would lead to ambiguous information (what if objects contain an axis which is not in coords?).

So, problably the cleanest solution is to adjust the lower and upper bound as done in fix.

leuchtum commented 4 months ago

I can see how it can lead to such ambiguity. Thank you @FabianHofmann for the explanation. I am closing the issue in this case.