Skip to content

Commit

Permalink
Improved detection of backends and solvers
Browse files Browse the repository at this point in the history
  • Loading branch information
pablormier committed Aug 30, 2022
1 parent f26ebac commit 3b0d4e5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ conda activate corneto
pip install git+https://github.com/saezlab/corneto.git@0.9.0-alpha.3
```

Alternatively you can download the wheel file from https://github.com/saezlab/corneto/releases/download/0.9.0-alpha.3/corneto-0.9.0a3-py3-none-any.whl and install it with `pip install corneto-0.9.0a3-py3-none-any.whl`.
Alternatively you can download the wheel (.whl) file from https://github.com/saezlab/corneto/releases and install it with `pip install file.whl`.

> :warning: Please note that without any backend, you can't do much with CORNETO. There are two supported backends right now: [PICOS](https://picos-api.gitlab.io/picos/tutorial.html) and [CVXPY](https://www.cvxpy.org/). Both backends allow symbolic manipulation of expressions in matrix notation.
Expand Down
2 changes: 1 addition & 1 deletion corneto/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from corneto._constants import *
from corneto._core import ReNet
from corneto.backend import available_backends, DEFAULT_BACKEND
from corneto.backend import available_backends, DEFAULT_BACKEND, DEFAULT_SOLVER

__version__ = '0.9.0-alpha.4'
22 changes: 22 additions & 0 deletions corneto/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from importlib.util import find_spec
from corneto.backend._base import Backend, VarType
from corneto.backend._cvxpy_backend import CvxpyBackend
from corneto.backend._picos_backend import PicosBackend
Expand All @@ -9,10 +10,31 @@
def available_backends():
return [b for b in supported_backends if b.is_available()]

_cvxpy_solvers = ['gurobi', 'cplex', 'mosek', 'scip', 'cbc', 'glpk_mi']
_picos_solvers = ['gurobi', 'cplex', 'mosek', 'scip', 'glpk']


DEFAULT_BACKEND = available_backends()[0] if len(available_backends()) > 0 else None
DEFAULT_SOLVER = None

if not DEFAULT_BACKEND:
s.LOGGER.warn(
"None of the supported backends found. Please install CVXPY or PICOS to create and solve optimization problems."
)
else:
if isinstance(DEFAULT_BACKEND, CvxpyBackend):
from cvxpy import installed_solvers
available = [name.lower() for name in installed_solvers()]
for solver in _cvxpy_solvers:
if solver in available:
DEFAULT_SOLVER = solver
break
else:
import picos as pc
available = [name.lower() for name in pc.available_solvers()]
for solver in _picos_solvers:
if solver in available:
DEFAULT_BACKEND = solver
break
if not DEFAULT_SOLVER:
s.LOGGER.warn(f"MIP solver not found for {DEFAULT_BACKEND}")
14 changes: 10 additions & 4 deletions corneto/backend/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
from turtle import pos
import numpy as np
import numbers
from typing import Any, Dict, Iterable, Optional, Set, Tuple, Union, List
from typing import Any, Dict, Iterable, Optional, Tuple, Union, List
import corneto as cnt
from corneto._constants import *
from corneto._core import ReNet
from corneto._decorators import _proxy
#from corneto.backend import DEFAULT_SOLVER


def _eq_shape(a: np.ndarray, b: np.ndarray) -> bool:
Expand Down Expand Up @@ -330,14 +332,16 @@ def get_symbols(self, *args) -> List[CtProxySymbol]:

def solve(
self,
solver: Solver = Solver.COIN_OR_CBC,
solver: Optional[Union[str, Solver]] = None,
max_seconds: int = None,
warm_start: bool = False,
verbosity: int = 0,
**options,
) -> Any:
if self._backend is None:
raise ValueError("No backend assigned.")
if not solver:
solver = cnt.DEFAULT_SOLVER
return self._backend.solve(
self,
solver=solver,
Expand Down Expand Up @@ -492,12 +496,14 @@ def Problem(
def solve(
self,
p: ProblemDef,
solver: Solver = Solver.COIN_OR_CBC,
solver: Optional[Union[str, Solver]] = None,
max_seconds: int = None,
warm_start: bool = False,
verbosity: int = 0,
**options,
):
if not solver:
solver = cnt.DEFAULT_SOLVER
o: Optional[CtProxyExpression]
if p.objectives is not None and len(p.objectives) > 1:
if len(p.weights) != len(p.objectives):
Expand Down Expand Up @@ -526,7 +532,7 @@ def _solve(
self,
p: ProblemDef,
objective: Optional[CtProxyExpression] = None,
solver: Solver = Solver.COIN_OR_CBC,
solver: Optional[Union[str, Solver]] = None,
max_seconds: int = None,
warm_start: bool = False,
verbosity: int = 0,
Expand Down
17 changes: 12 additions & 5 deletions corneto/backend/_cvxpy_backend.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from multiprocessing.sharedctypes import Value
from typing import Any, List, Optional, Tuple, Union
from corneto.backend._base import (
CtProxyExpression,
Expand All @@ -8,7 +9,8 @@
)
import numpy as np
from corneto._constants import *
import warnings
from corneto._settings import LOGGER


try:
import cvxpy as cp
Expand Down Expand Up @@ -49,6 +51,9 @@ def _load(self):

cvxpy

def __str__(self) -> str:
return "CVXPY Backend"

def Variable(
self,
name: Optional[str] = None,
Expand All @@ -75,15 +80,17 @@ def _solve(
self,
p: ProblemDef,
objective: Optional[CtProxyExpression] = None,
solver: Solver = Solver.COIN_OR_CBC,
solver: Optional[Union[str, Solver]] = None,
max_seconds: int = None,
warm_start: bool = False,
verbosity: int = 0,
**options,
) -> Any:
if not solver:
raise ValueError("A solver must be provided")
o: Union[cp.Minimize, cp.Maximize]
if objective is not None:
obj = objective.e if hasattr(objective, '_expr') else objective
obj = objective.e if hasattr(objective, "_expr") else objective
if p._direction == Direction.MIN:
o = cp.Minimize(obj)
elif p._direction == Direction.MAX:
Expand Down Expand Up @@ -117,12 +124,12 @@ def _solve(
elif s == "CPLEX":
options["cplex_params"] = {"timelimit": max_seconds}
elif s == "GLPK_MI":
warnings.warn("Timelimit for GLPK_MI is not supported")
LOGGER.warn("Timelimit for GLPK_MI is not supported")
elif s == "SCIP":
# https://www.scipopt.org/doc/html/PARAMETERS.php
options["scip_params"] = {"limits/time": max_seconds}
elif s == "MOSEK":
# https://docs.mosek.com/latest/cxxfusion/parameters.html
options["mosek_params"] = {"mioMaxTime": max_seconds}
P.solve(solver=s, verbose=verbosity > 0, warm_start=warm_start, **options)
return P
return P
12 changes: 9 additions & 3 deletions corneto/backend/_picos_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(
ub: Optional[Union[float, np.ndarray]] = None,
vartype: VarType = VarType.CONTINUOUS,
) -> None:
super().__init__(expr, name, lb, ub, vartype)
super().__init__(expr, name, lb, ub, vartype)


class PicosBackend(Backend):
Expand All @@ -52,6 +52,9 @@ def _load(self):

picos

def __str__(self) -> str:
return "PICOS Backend"

def Variable(
self,
name: Optional[str] = None,
Expand All @@ -76,23 +79,26 @@ def _solve(
self,
p: ProblemDef,
objective: Optional[CtProxyExpression] = None,
solver: Solver = Solver.GLPK_MI,
solver: Optional[Union[str, Solver]] = None,
max_seconds: int = None,
warm_start: bool = False,
verbosity: int = 0,
**options,
):
if not solver:
raise ValueError("A solver must be provided")
P = pc.Problem()
for c in p.constraints:
P += c.e
if objective is not None:
obj = objective.e if hasattr(objective, '_expr') else objective
obj = objective.e if hasattr(objective, "_expr") else objective
if p._direction == Direction.MIN:
P.minimize = obj
else:
P.maximize = obj
if warm_start:
from corneto._settings import LOGGER

LOGGER.warn("warm_start is not supported yet, ignored")
P.solve(
timelimit=max_seconds,
Expand Down

0 comments on commit 3b0d4e5

Please sign in to comment.