Skip to content

Commit

Permalink
Specify hyperparameters directly in generic LNS_CP
Browse files Browse the repository at this point in the history
- put hyperparameters definition directly in LNS_CP:
    cp_solver, initial_solution_provider, constraint_handler,
    postprocess (with cls + kwargs each time), but with empty choices
   (depending on problem type)
- update LNS_CP.__init__ to follow this logic
- add in Hyperparametrizable a class method to copy and update the
  hyperparameters (to be used by child classes like LnsCpColoring to
  reuse hyperparameters defined by LNS_CP)
- make LnsCpColoring and generic_rcpsp_tools lns wrapper derive from it
  and update __init__() and choices for hyperparameters (w/o redefining all hyperparameters)
- add hyperparameters to ConstraintHandler's and InitialSolution's
- use hyperparameters from build_xxx() functions for LargeNeighborhoodSearchScheduling, and thus
  - make ParamsConstraintBuilder parametrizable
  - add hyperparameters params_0 (_cls, _kwargs) and params_1 (_cls, _kwargs)
    to construct params_list from it in build_constraint_handler()
  • Loading branch information
nhuet authored and g-poveda committed May 13, 2024
1 parent 0832758 commit a152d76
Show file tree
Hide file tree
Showing 10 changed files with 498 additions and 187 deletions.
122 changes: 37 additions & 85 deletions discrete_optimization/coloring/solvers/coloring_cp_lns.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@
)
from discrete_optimization.coloring.solvers.coloring_solver import SolverColoring
from discrete_optimization.generic_tools.callbacks.callback import Callback
from discrete_optimization.generic_tools.cp_tools import ParametersCP
from discrete_optimization.generic_tools.cp_tools import MinizincCPSolver, ParametersCP
from discrete_optimization.generic_tools.do_problem import ParamsObjectiveFunction
from discrete_optimization.generic_tools.hyperparameters.hyperparameter import (
SubBrickHyperparameter,
SubBrickKwargsHyperparameter,
)
from discrete_optimization.generic_tools.lns_cp import (
LNS_CP,
ConstraintHandler,
TrivialPostProcessSolution,
)
from discrete_optimization.generic_tools.lns_mip import (
InitialSolution,
PostProcessSolution,
)
from discrete_optimization.generic_tools.result_storage.result_storage import (
ResultStorage,
)
Expand Down Expand Up @@ -73,155 +78,102 @@ def build_default_initial_solution(
)


class LnsCpColoring(SolverColoring):
class LnsCpColoring(LNS_CP, SolverColoring):
"""
Most easy way to use LNS-CP for coloring with some default parameters for constraint handler.
"""

hyperparameters = [
SubBrickHyperparameter("cp_solver_cls", choices=[ColoringCP], default=None),
SubBrickKwargsHyperparameter(
"cp_solver_kwargs", subbrick_hyperparameter="cp_solver_cls"
),
SubBrickHyperparameter(
"initial_solution_provider_cls", choices=[InitialColoring], default=None
),
SubBrickKwargsHyperparameter(
"initial_solution_provider_kwargs",
subbrick_hyperparameter="initial_solution_provider_cls",
),
SubBrickHyperparameter(
"constraint_handler_cls",
choices=[ConstraintHandlerFixColorsCP],
default=None,
hyperparameters = LNS_CP.copy_and_update_hyperparameters(
cp_solver_cls=dict(choices=[ColoringCP]),
initial_solution_provider_cls=dict(choices=[InitialColoring]),
constraint_handler_cls=dict(choices=[ConstraintHandlerFixColorsCP]),
post_process_solution_cls=dict(
choices=[TrivialPostProcessSolution, PostProcessSolutionColoring]
),
SubBrickKwargsHyperparameter(
"constraint_handler_kwargs",
subbrick_hyperparameter="constraint_handler_cls",
),
SubBrickHyperparameter(
"post_process_solution_cls",
choices=[TrivialPostProcessSolution, PostProcessSolutionColoring],
default=None,
),
SubBrickKwargsHyperparameter(
"post_process_solution_kwargs",
subbrick_hyperparameter="post_process_solution_cls",
),
]
)

def __init__(
self,
problem: ColoringProblem,
cp_solver: Optional[MinizincCPSolver] = None,
initial_solution_provider: Optional[InitialSolution] = None,
constraint_handler: Optional[ConstraintHandler] = None,
post_process_solution: Optional[PostProcessSolution] = None,
params_objective_function: Optional[ParamsObjectiveFunction] = None,
**kwargs
):
super().__init__(
problem=problem, params_objective_function=params_objective_function
SolverColoring.__init__(
self, problem=problem, params_objective_function=params_objective_function
)

kwargs = self.complete_with_default_hyperparameters(kwargs)

solver = kwargs.get("cp_solver", None)
if solver is None:
if cp_solver is None:
if kwargs["cp_solver_kwargs"] is None:
cp_solver_kwargs = kwargs
else:
cp_solver_kwargs = kwargs["cp_solver_kwargs"]
if kwargs["cp_solver_cls"] is None:
solver = build_default_cp_model(
cp_solver = build_default_cp_model(
coloring_model=self.problem, **cp_solver_kwargs
)
else:
cp_solver_cls = kwargs["cp_solver_cls"]
solver = cp_solver_cls(problem=self.problem, **cp_solver_kwargs)
solver.init_model(**cp_solver_kwargs)
self.cp_solver = solver

self.parameters_cp = kwargs.get("parameters_cp", ParametersCP.default())
cp_solver = cp_solver_cls(problem=self.problem, **cp_solver_kwargs)
cp_solver.init_model(**cp_solver_kwargs)
self.cp_solver = cp_solver

self.constraint_handler = kwargs.get("constraint_handler", None)
if self.constraint_handler is None:
if constraint_handler is None:
if kwargs["constraint_handler_kwargs"] is None:
constraint_handler_kwargs = kwargs
else:
constraint_handler_kwargs = kwargs["constraint_handler_kwargs"]
if kwargs["constraint_handler_cls"] is None:
self.constraint_handler = build_default_constraint_handler(
constraint_handler = build_default_constraint_handler(
coloring_model=self.problem, **constraint_handler_kwargs
)
else:
constraint_handler_cls = kwargs["constraint_handler_cls"]
self.constraint_handler = constraint_handler_cls(
constraint_handler = constraint_handler_cls(
problem=self.problem, **constraint_handler_kwargs
)
self.constraint_handler = constraint_handler

self.post_pro = kwargs.get("post_process_solution", None)
if self.post_pro is None:
if post_process_solution is None:
if kwargs["post_process_solution_kwargs"] is None:
post_process_solution_kwargs = kwargs
else:
post_process_solution_kwargs = kwargs["post_process_solution_kwargs"]
if kwargs["post_process_solution_cls"] is None:
self.post_pro = build_default_postprocess(
post_process_solution = build_default_postprocess(
coloring_model=self.problem,
params_objective_function=self.params_objective_function,
)
else:
post_process_solution_cls = kwargs["post_process_solution_cls"]
self.post_pro = post_process_solution_cls(
post_process_solution = post_process_solution_cls(
problem=self.problem,
params_objective_function=self.params_objective_function,
**post_process_solution_kwargs
)
self.post_process_solution = post_process_solution

self.initial_solution_provider = kwargs.get("initial_solution_provider", None)
if self.initial_solution_provider is None:
if initial_solution_provider is None:
if kwargs["initial_solution_provider_kwargs"] is None:
initial_solution_provider_kwargs = kwargs
else:
initial_solution_provider_kwargs = kwargs[
"initial_solution_provider_kwargs"
]
if kwargs["initial_solution_provider_cls"] is None:
self.initial_solution_provider = build_default_initial_solution(
initial_solution_provider = build_default_initial_solution(
coloring_model=self.problem,
params_objective_function=self.params_objective_function,
)
else:
initial_solution_provider_cls = kwargs["initial_solution_provider_cls"]
self.initial_solution_provider = initial_solution_provider_cls(
initial_solution_provider = initial_solution_provider_cls(
problem=self.problem,
params_objective_function=self.params_objective_function,
**initial_solution_provider_kwargs
)

self.lns_solver = LNS_CP(
problem=self.problem,
cp_solver=self.cp_solver,
initial_solution_provider=self.initial_solution_provider,
constraint_handler=self.constraint_handler,
post_process_solution=self.post_pro,
params_objective_function=self.params_objective_function,
)

def solve(
self,
nb_iteration_lns: int,
parameters_cp: Optional[ParametersCP] = None,
nb_iteration_no_improvement: Optional[int] = None,
skip_first_iteration: bool = False,
stop_first_iteration_if_optimal: bool = True,
callbacks: Optional[List[Callback]] = None,
**args
) -> ResultStorage:
if parameters_cp is None:
parameters_cp = ParametersCP.default()
return self.lns_solver.solve_lns(
parameters_cp=parameters_cp,
skip_first_iteration=skip_first_iteration,
stop_first_iteration_if_optimal=stop_first_iteration_if_optimal,
nb_iteration_no_improvement=nb_iteration_no_improvement,
nb_iteration_lns=nb_iteration_lns,
callbacks=callbacks,
)
self.initial_solution_provider = initial_solution_provider
10 changes: 10 additions & 0 deletions discrete_optimization/facility/solvers/facility_lp_lns_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
ParamsObjectiveFunction,
build_aggreg_function_and_params_objective,
)
from discrete_optimization.generic_tools.hyperparameters.hyperparameter import (
EnumHyperparameter,
)
from discrete_optimization.generic_tools.lns_mip import (
ConstraintHandler,
InitialSolution,
Expand All @@ -47,6 +50,13 @@ class InitialFacilitySolution(InitialSolution):
initial_method (InitialFacilityMethod): the method to use to provide the initial solution.
"""

hyperparameters = [
EnumHyperparameter(
name="initial_method",
enum=InitialFacilityMethod,
),
]

def __init__(
self,
problem: FacilityProblem,
Expand Down
Loading

0 comments on commit a152d76

Please sign in to comment.