import inspect
from flopt.solvers.base import BaseSearch
from flopt.solvers.selector import (
mip,
ising,
qp,
permutation,
blackbox,
blackbox_mip,
nonlinear,
nonlinear_mip,
MipSelector,
IsingSelector,
QpSelector,
PermutationSelector,
BlackBoxSelector,
BlackBoxMipSelector,
NonlinearSelector,
NonlinearMipSelector,
BaseSelector,
ModelNotFound,
)
from flopt.constants import VariableType, ExpressionType
import flopt.error
from flopt.env import setup_logger
logger = setup_logger(__name__)
[docs]class AutoSearch(BaseSearch):
"""Auto Solver Selector
This automatically selects and runs solver according to the problem and settings.
.. code-block:: python
from flopt import Variable, Problem, Solver
# Variables
a = Variable("a", lowBound=0, upBound=1, cat="Integer")
b = Variable("b", lowBound=1, upBound=2, cat="Continuous")
c = Variable("c", lowBound=1, upBound=3, cat="Continuous")
prob = Problem(name="Test")
prob += 2*(3*a+b)*c**2+3
Then, we use Solver(algo="auto") and solve.
.. code-block:: python
solver = Solver(algo="auto")
solver.setParams({"timelimit": 10})
prob.solve(solver, msg=True)
>>> # - - - - - - - - - - - - - - #
>>> Welcome to the flopt Solver
>>> Version 0.5.4
>>> Date: September 1, 2022
>>> # - - - - - - - - - - - - - - #
>>>
>>> Algorithm: Scipy
>>> Params: {'timelimit': 10}
See the log, you can see the Random algorithm is used for this problem.
Executing .select(), we can check which solver will be select.
.. code-block:: python
solver = Solver(algo="auto")
solver.setParams({"timelimit": 10})
solver = solver.select(prob)
print(solver.name)
>>> Scipy
"""
name = "auto"
can_solve_problems = {
"Variable": VariableType.Any,
"Objective": ExpressionType.Any,
"Constraint": ExpressionType.Any,
}
def __init__(self):
self.selector_msg = False
def available(self, prob, verbose=False):
"""
Parameters
----------
prob : Problem
verbose : bool
Returns
-------
bool
return true if it can solve the problem else false
"""
from flopt import Solver, Solver_list
return any(
Solver(algo=algo).available(prob, verbose)
for algo in (set(Solver_list()) - {"auto"})
)
def select(self, prob):
"""select solver
Parameters
----------
prob : Problem
problem
"""
from flopt import Solver
problem_type = prob.toProblemType()
selector = self.getSelector(problem_type)
algo = selector(prob, self)
solver = Solver(algo=algo)
# set params
attrobjs = [
(attr, obj)
for (attr, obj) in inspect.getmembers(self)
if not callable(obj)
and attr[:2] != "__"
and attr[-2:] != "__"
and attr != "name"
]
for attr, obj in attrobjs:
setattr(solver, attr, obj)
return solver
def getSelector(self, problem_type):
def check(problem_type, problem_class, class_str):
is_problem_class = (
problem_type["Variable"].expand() <= problem_class["Variable"].expand()
and problem_type["Objective"].expand()
<= problem_class["Objective"].expand()
and problem_type["Constraint"].expand()
<= problem_class["Constraint"].expand()
)
if is_problem_class and self.selector_msg:
logger.info(f"This problem is identified as {class_str}.")
return is_problem_class
try:
if check(problem_type, mip, "MIP"):
return MipSelector()
elif check(problem_type, ising, "Ising"):
return IsingSelector()
elif check(problem_type, qp, "Quadratic programming"):
return QpSelector()
elif check(problem_type, permutation, "Permutation programming"):
return PermutationSelector()
elif check(problem_type, nonlinear, "Nonlinear optimization"):
return NonlinearSelector()
elif check(
problem_type,
nonlinear_mip,
"Nonlinear optimization with integer variables",
):
return NonlinearMipSelector()
elif check(problem_type, blackbox, "Blackbox optimization"):
return BlackBoxSelector()
elif check(
problem_type,
blackbox_mip,
"Blackbox optimization with integer variables",
):
return BlackBoxMipSelector()
except ModelNotFound as e:
logger.warning(e)
return BaseSelector()
except ValueError as e:
logger.warning(e)
return BaseSelector()
return BaseSelector()
def solve(self, solution, objective, constraints, prob, *args, **kwargs):
"""
select solver and solve the problem of (solution, obj)
Parameters
----------
solution : Solution
solution object
objective : Expression
objective object
constraints : list of Constraint
list of constriants objects
prob : Problem
problem
msg :
if it is true, message about search is outputed
Returns
-------
status : flopt.SolverTerminateState
Log : Log
running_time : float
"""
solver = self.select(prob)
status, self.log, running_time = solver.solve(
solution, objective, constraints, prob, *args, **kwargs
)
self.best_solution = solver.best_solution
return status, self.log, running_time