Skip to content

Commit aab609b

Browse files
committed
feat: move set_int_index to solution assignment in model.py
1 parent 00023e2 commit aab609b

File tree

5 files changed

+23
-43
lines changed

5 files changed

+23
-43
lines changed

doc/release_notes.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
Release Notes
22
=============
33

4-
.. Upcoming Version
5-
.. ----------------
4+
Upcoming Version
5+
----------------
6+
7+
* The internal handling of `Solution` objects was improved for more consistency. Solution objects created from solver calls now preserve the exact index names from the input file, ensuring consistent behavior across all solver functions.
68

79
Version 0.4.4
810
--------------

linopy/common.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@
3838
from linopy.variables import Variable
3939

4040

41+
def set_int_index(series: pd.Series) -> pd.Series:
42+
"""
43+
Convert string index to int index.
44+
"""
45+
if not series.empty and not series.index.is_integer():
46+
cutoff = count_initial_letters(str(series.index[0]))
47+
series.index = series.index.str[cutoff:].astype(int)
48+
return series
49+
50+
4151
def maybe_replace_sign(sign: str) -> str:
4252
"""
4353
Replace the sign with an alternative sign if available.

linopy/model.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
best_int,
3131
maybe_replace_signs,
3232
replace_by_map,
33+
set_int_index,
3334
to_path,
3435
)
3536
from linopy.constants import (
@@ -1189,6 +1190,7 @@ def solve(
11891190

11901191
# map solution and dual to original shape which includes missing values
11911192
sol = result.solution.primal.copy()
1193+
sol = set_int_index(sol)
11921194
sol.loc[-1] = nan
11931195

11941196
for name, var in self.variables.items():
@@ -1201,6 +1203,7 @@ def solve(
12011203

12021204
if not result.solution.dual.empty:
12031205
dual = result.solution.dual.copy()
1206+
dual = set_int_index(dual)
12041207
dual.loc[-1] = nan
12051208

12061209
for name, con in self.constraints.items():

linopy/solvers.py

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919

2020
import numpy as np
2121
import pandas as pd
22-
from pandas.core.series import Series
2322

24-
from linopy.common import count_initial_letters
2523
from linopy.constants import (
2624
Result,
2725
Solution,
@@ -135,16 +133,6 @@
135133
)
136134

137135

138-
def set_int_index(series: Series) -> Series:
139-
"""
140-
Convert string index to int index.
141-
"""
142-
if not series.empty:
143-
cutoff = count_initial_letters(str(series.index[0]))
144-
series.index = series.index.str[cutoff:].astype(int)
145-
return series
146-
147-
148136
# using enum to match solver subclasses with names
149137
class SolverName(enum.Enum):
150138
CBC = "cbc"
@@ -466,8 +454,8 @@ def get_solver_solution():
466454
)
467455
variables_b = df.index.str[0] == "x"
468456

469-
sol = df[variables_b][2].pipe(set_int_index)
470-
dual = df[~variables_b][3].pipe(set_int_index)
457+
sol = df[variables_b][2]
458+
dual = df[~variables_b][3]
471459
return Solution(sol, dual, objective)
472460

473461
solution = self.safe_get_solution(status=status, func=get_solver_solution)
@@ -624,11 +612,7 @@ def get_solver_solution() -> Solution:
624612
dual_io = io.StringIO("".join(read_until_break(f))[:-2])
625613
dual_ = pd.read_fwf(dual_io)[1:].set_index("Row name")
626614
if "Marginal" in dual_:
627-
dual = (
628-
pd.to_numeric(dual_["Marginal"], "coerce")
629-
.fillna(0)
630-
.pipe(set_int_index)
631-
)
615+
dual = pd.to_numeric(dual_["Marginal"], "coerce").fillna(0)
632616
else:
633617
logger.warning("Dual values of MILP couldn't be parsed")
634618
dual = pd.Series(dtype=float)
@@ -638,7 +622,6 @@ def get_solver_solution() -> Solution:
638622
pd.read_fwf(sol_io)[1:]
639623
.set_index("Column name")["Activity"]
640624
.astype(float)
641-
.pipe(set_int_index)
642625
)
643626
f.close()
644627
return Solution(sol, dual, objective)
@@ -860,12 +843,8 @@ def get_solver_solution() -> Solution:
860843
sol = pd.Series(solution.col_value, model.matrices.vlabels, dtype=float)
861844
dual = pd.Series(solution.row_dual, model.matrices.clabels, dtype=float)
862845
else:
863-
sol = pd.Series(
864-
solution.col_value, h.getLp().col_names_, dtype=float
865-
).pipe(set_int_index)
866-
dual = pd.Series(
867-
solution.row_dual, h.getLp().row_names_, dtype=float
868-
).pipe(set_int_index)
846+
sol = pd.Series(solution.col_value, h.getLp().col_names_, dtype=float)
847+
dual = pd.Series(solution.row_dual, h.getLp().row_names_, dtype=float)
869848

870849
return Solution(sol, dual, objective)
871850

@@ -1084,13 +1063,11 @@ def get_solver_solution() -> Solution:
10841063
objective = m.ObjVal
10851064

10861065
sol = pd.Series({v.VarName: v.x for v in m.getVars()}, dtype=float)
1087-
sol = set_int_index(sol)
10881066

10891067
try:
10901068
dual = pd.Series(
10911069
{c.ConstrName: c.Pi for c in m.getConstrs()}, dtype=float
10921070
)
1093-
dual = set_int_index(dual)
10941071
except AttributeError:
10951072
logger.warning("Dual values of MILP couldn't be parsed")
10961073
dual = pd.Series(dtype=float)
@@ -1229,15 +1206,13 @@ def get_solver_solution() -> Solution:
12291206
solution = pd.Series(
12301207
m.solution.get_values(), m.variables.get_names(), dtype=float
12311208
)
1232-
solution = set_int_index(solution)
12331209

12341210
if is_lp:
12351211
dual = pd.Series(
12361212
m.solution.get_dual_values(),
12371213
m.linear_constraints.get_names(),
12381214
dtype=float,
12391215
)
1240-
dual = set_int_index(dual)
12411216
else:
12421217
logger.warning("Dual values of MILP couldn't be parsed")
12431218
dual = pd.Series(dtype=float)
@@ -1366,15 +1341,13 @@ def get_solver_solution() -> Solution:
13661341
sol.drop(
13671342
["quadobjvar", "qmatrixvar"], errors="ignore", inplace=True, axis=0
13681343
)
1369-
sol = set_int_index(sol)
13701344

13711345
cons = m.getConss()
13721346
if len(cons) != 0:
13731347
dual = pd.Series({c.name: m.getDualSolVal(c) for c in cons})
13741348
dual = dual[
13751349
dual.index.str.startswith("c") & ~dual.index.str.startswith("cf")
13761350
]
1377-
dual = set_int_index(dual)
13781351
else:
13791352
logger.warning("Dual values of MILP couldn't be parsed")
13801353
dual = pd.Series(dtype=float)
@@ -1504,12 +1477,10 @@ def get_solver_solution() -> Solution:
15041477
var = [str(v) for v in m.getVariable()]
15051478

15061479
sol = pd.Series(m.getSolution(var), index=var, dtype=float)
1507-
sol = set_int_index(sol)
15081480

15091481
try:
15101482
dual_ = [str(d) for d in m.getConstraint()]
15111483
dual = pd.Series(m.getDual(dual_), index=dual_, dtype=float)
1512-
dual = set_int_index(dual)
15131484
except (xpress.SolverError, xpress.ModelError, SystemError):
15141485
logger.warning("Dual values of MILP couldn't be parsed")
15151486
dual = pd.Series(dtype=float)
@@ -1833,13 +1804,11 @@ def get_solver_solution() -> Solution:
18331804
sol = m.getxx(soltype)
18341805
sol = {m.getvarname(i): sol[i] for i in range(m.getnumvar())}
18351806
sol = pd.Series(sol, dtype=float)
1836-
sol = set_int_index(sol)
18371807

18381808
try:
18391809
dual = m.gety(soltype)
18401810
dual = {m.getconname(i): dual[i] for i in range(m.getnumcon())}
18411811
dual = pd.Series(dual, dtype=float)
1842-
dual = set_int_index(dual)
18431812
except (mosek.Error, AttributeError):
18441813
logger.warning("Dual values of MILP couldn't be parsed")
18451814
dual = pd.Series(dtype=float)
@@ -1975,11 +1944,9 @@ def get_solver_solution() -> Solution:
19751944
objective = m.BestObj if m.ismip else m.LpObjVal
19761945

19771946
sol = pd.Series({v.name: v.x for v in m.getVars()}, dtype=float)
1978-
sol = set_int_index(sol)
19791947

19801948
try:
19811949
dual = pd.Series({v.name: v.pi for v in m.getConstrs()}, dtype=float)
1982-
dual = set_int_index(dual)
19831950
except (coptpy.CoptError, AttributeError):
19841951
logger.warning("Dual values of MILP couldn't be parsed")
19851952
dual = pd.Series(dtype=float)
@@ -2119,11 +2086,9 @@ def get_solver_solution() -> Solution:
21192086
objective = m.objval
21202087

21212088
sol = pd.Series({v.varname: v.X for v in m.getVars()}, dtype=float)
2122-
sol = set_int_index(sol)
21232089

21242090
try:
21252091
dual = pd.Series({c.constrname: c.DualSoln for c in m.getConstrs()})
2126-
dual = set_int_index(dual)
21272092
except (mindoptpy.MindoptError, AttributeError):
21282093
logger.warning("Dual values of MILP couldn't be parsed")
21292094
dual = pd.Series(dtype=float)

linopy/variables.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@
4747
print_coord,
4848
print_single_variable,
4949
save_join,
50+
set_int_index,
5051
to_dataframe,
5152
to_polars,
5253
)
5354
from linopy.config import options
5455
from linopy.constants import HELPER_DIMS, TERM_DIM
55-
from linopy.solvers import set_int_index
5656
from linopy.types import NotImplementedType
5757

5858
if TYPE_CHECKING:

0 commit comments

Comments
 (0)