Skip to content

Commit 7e2d3c1

Browse files
Xpress 9.9+ backward compatibility and other updates (#701)
1 parent c75c55a commit 7e2d3c1

1 file changed

Lines changed: 99 additions & 26 deletions

File tree

linopy/solvers.py

Lines changed: 99 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2180,34 +2180,76 @@ def _build_solver_model(
21802180
entind = None
21812181
coltype = None
21822182

2183-
problem.loadproblem(
2183+
objcoef = np.asarray(M.c, dtype=float)
2184+
has_q = objqcol1 is not None
2185+
has_int = coltype is not None
2186+
base_kwargs: dict[str, Any] = dict(
21842187
probname="linopy",
21852188
rowtype=rowtype,
21862189
rhs=rhs,
21872190
rng=None,
2188-
objcoef=np.asarray(M.c, dtype=float),
2191+
objcoef=objcoef,
21892192
start=start,
21902193
collen=None,
21912194
rowind=rowind,
21922195
rowcoef=rowcoef,
21932196
lb=lb,
21942197
ub=ub,
2195-
objqcol1=objqcol1,
2196-
objqcol2=objqcol2,
2197-
objqcoef=objqcoef,
2198-
qrowind=None,
2199-
nrowqcoefs=None,
2200-
rowqcol1=None,
2201-
rowqcol2=None,
2202-
rowqcoef=None,
2203-
coltype=coltype,
2204-
entind=entind,
2205-
limit=None,
2206-
settype=None,
2207-
setstart=None,
2208-
setind=None,
2209-
refval=None,
22102198
)
2199+
try: # Try new API first (Xpress 9.8+)
2200+
if has_q and has_int:
2201+
problem.loadMIQP(
2202+
**base_kwargs,
2203+
objqcol1=objqcol1,
2204+
objqcol2=objqcol2,
2205+
objqcoef=objqcoef,
2206+
coltype=coltype,
2207+
entind=entind,
2208+
)
2209+
elif has_q:
2210+
problem.loadQP(
2211+
**base_kwargs,
2212+
objqcol1=objqcol1,
2213+
objqcol2=objqcol2,
2214+
objqcoef=objqcoef,
2215+
)
2216+
elif has_int:
2217+
problem.loadMIP(
2218+
**base_kwargs,
2219+
coltype=coltype,
2220+
entind=entind,
2221+
)
2222+
else:
2223+
problem.loadLP(**base_kwargs)
2224+
except AttributeError: # Fallback to old API
2225+
problem.loadproblem(
2226+
probname="linopy",
2227+
rowtype=rowtype,
2228+
rhs=rhs,
2229+
rng=None,
2230+
objcoef=objcoef,
2231+
start=start,
2232+
collen=None,
2233+
rowind=rowind,
2234+
rowcoef=rowcoef,
2235+
lb=lb,
2236+
ub=ub,
2237+
objqcol1=objqcol1,
2238+
objqcol2=objqcol2,
2239+
objqcoef=objqcoef,
2240+
qrowind=None,
2241+
nrowqcoefs=None,
2242+
rowqcol1=None,
2243+
rowqcol2=None,
2244+
rowqcoef=None,
2245+
coltype=coltype,
2246+
entind=entind,
2247+
limit=None,
2248+
settype=None,
2249+
setstart=None,
2250+
setind=None,
2251+
refval=None,
2252+
)
22112253

22122254
if model.objective.sense == "max":
22132255
problem.chgobjsense(xpress.maximize)
@@ -2218,10 +2260,20 @@ def _build_solver_model(
22182260
)
22192261
vnames = print_variable(M.vlabels)
22202262
if vnames:
2221-
problem.addnames(xpress_Namespaces.COLUMN, vnames, 0, len(vnames) - 1)
2263+
try: # Try new API first (Xpress 9.8+)
2264+
problem.addNames(
2265+
xpress_Namespaces.COLUMN, vnames, 0, len(vnames) - 1
2266+
)
2267+
except AttributeError: # Fallback to old API
2268+
problem.addnames(
2269+
xpress_Namespaces.COLUMN, vnames, 0, len(vnames) - 1
2270+
)
22222271
cnames = print_constraint(M.clabels)
22232272
if cnames:
2224-
problem.addnames(xpress_Namespaces.ROW, cnames, 0, len(cnames) - 1)
2273+
try: # Try new API first (Xpress 9.8+)
2274+
problem.addNames(xpress_Namespaces.ROW, cnames, 0, len(cnames) - 1)
2275+
except AttributeError: # Fallback to old API
2276+
problem.addnames(xpress_Namespaces.ROW, cnames, 0, len(cnames) - 1)
22252277

22262278
if model.variables.sos:
22272279
for var_name in model.variables.sos:
@@ -2285,7 +2337,10 @@ def _run_file(
22852337
sense = read_sense_from_problem_file(problem_fn)
22862338

22872339
m = xpress.problem()
2288-
m.read(path_to_string(problem_fn))
2340+
try: # Try new API first
2341+
m.readProb(path_to_string(problem_fn))
2342+
except AttributeError: # Fallback to old API
2343+
m.read(path_to_string(problem_fn))
22892344

22902345
return self._solve(
22912346
m,
@@ -2321,25 +2376,40 @@ def _solve(
23212376
m.setControl(self.solver_options)
23222377

23232378
if log_fn is not None:
2324-
m.setlogfile(path_to_string(log_fn))
2379+
try: # Try new API first
2380+
m.setLogFile(path_to_string(log_fn))
2381+
except AttributeError: # Fallback to old API
2382+
m.setlogfile(path_to_string(log_fn))
23252383

23262384
if warmstart_fn is not None:
2327-
m.readbasis(path_to_string(warmstart_fn))
2385+
try: # Try new API first
2386+
m.readBasis(path_to_string(warmstart_fn))
2387+
except AttributeError: # Fallback to old API
2388+
m.readbasis(path_to_string(warmstart_fn))
23282389

23292390
m.optimize()
23302391

23312392
if m.attributes.solvestatus == xpress.enums.SolveStatus.STOPPED:
2332-
m.postsolve()
2393+
try: # Try new API first
2394+
m.postSolve()
2395+
except AttributeError: # Fallback to old API
2396+
m.postsolve()
23332397

23342398
if basis_fn is not None:
23352399
try:
2336-
m.writebasis(path_to_string(basis_fn))
2400+
try: # Try new API first
2401+
m.writeBasis(path_to_string(basis_fn))
2402+
except AttributeError: # Fallback to old API
2403+
m.writebasis(path_to_string(basis_fn))
23372404
except (xpress.SolverError, xpress.ModelError) as err: # pragma: no cover
23382405
logger.info("No model basis stored. Raised error: %s", err)
23392406

23402407
if solution_fn is not None:
23412408
try:
2342-
m.writebinsol(path_to_string(solution_fn))
2409+
try: # Try new API first
2410+
m.writeBinSol(path_to_string(solution_fn))
2411+
except AttributeError: # Fallback to old API
2412+
m.writebinsol(path_to_string(solution_fn))
23432413
except (xpress.SolverError, xpress.ModelError) as err: # pragma: no cover
23442414
logger.info("Unable to save solution file. Raised error: %s", err)
23452415

@@ -2365,7 +2435,10 @@ def get_solver_solution() -> Solution:
23652435
if m.attributes.rows == 0:
23662436
dual = np.array([], dtype=float)
23672437
else:
2368-
dual_values = np.asarray(m.getDual(), dtype=float)
2438+
try: # getDuals introduced in 9.5; fallback for 9.4
2439+
dual_values = np.asarray(m.getDuals(), dtype=float)
2440+
except AttributeError:
2441+
dual_values = np.asarray(m.getDual(), dtype=float)
23692442
if from_file:
23702443
dual = _solution_from_names(
23712444
dual_values,

0 commit comments

Comments
 (0)