Source code for discrete_optimization.maximum_independent_set.solvers.lns

import random
from collections.abc import Iterable
from typing import Any, Optional

import numpy as np
from ortools.sat.python.cp_model import Constraint

from discrete_optimization.generic_tools.hyperparameters.hyperparameter import (
    FloatHyperparameter,
    IntegerHyperparameter,
)
from discrete_optimization.generic_tools.lns_cp import OrtoolsCpSatConstraintHandler
from discrete_optimization.generic_tools.result_storage.result_storage import (
    ResultStorage,
)
from discrete_optimization.maximum_independent_set.problem import (
    MisProblem,
    MisSolution,
)
from discrete_optimization.maximum_independent_set.solvers.cpsat import CpSatMisSolver


[docs] class OrtoolsCpSatMisConstraintHandler(OrtoolsCpSatConstraintHandler): hyperparameters = [ FloatHyperparameter(name="fraction_to_fix", default=0.9, low=0.0, high=1.0), ] def __init__(self, problem: MisProblem, fraction_to_fix: float = 0.9): self.problem = problem self.fraction_to_fix = fraction_to_fix self.iter = 0
[docs] def adding_constraint_from_results_store( self, solver: CpSatMisSolver, result_storage: ResultStorage, last_result_store: Optional[ResultStorage] = None, **kwargs: Any, ) -> Iterable[Constraint]: if not isinstance(solver, CpSatMisSolver): raise ValueError("solver must a CpSatMisSolver for this constraint.") lns_constraints = [] current_solution = result_storage.get_best_solution() if current_solution is None: raise ValueError( "result_storage.get_best_solution() " "should not be None." ) if not isinstance(current_solution, MisSolution): raise ValueError( "result_storage.get_best_solution() " "should be a MisSolution." ) nb_chosen = sum(current_solution.chosen) idx_chosen = list(np.where(current_solution.chosen)[0]) subpart_chosen = set( random.sample( idx_chosen, int(self.fraction_to_fix * nb_chosen), ) ) in_set = solver.variables["in_set"] for idx in subpart_chosen: lns_constraints.append(solver.cp_model.Add(in_set[idx] == 1)) return lns_constraints
[docs] class DestroyOrtoolsCpSatMisConstraintHandler(OrtoolsCpSatConstraintHandler): hyperparameters = [ FloatHyperparameter(name="fraction_to_fix", default=0.9, low=0.0, high=1.0), ] def __init__(self, problem: MisProblem, fraction_to_fix: float = 0.9): self.problem = problem self.fraction_to_fix = fraction_to_fix self.iter = 0
[docs] def adding_constraint_from_results_store( self, solver: CpSatMisSolver, result_storage: ResultStorage, last_result_store: Optional[ResultStorage] = None, **kwargs: Any, ) -> Iterable[Constraint]: if not isinstance(solver, CpSatMisSolver): raise ValueError("solver must a CpSatMisSolver for this constraint.") lns_constraints = [] current_solution = result_storage.get_best_solution() if current_solution is None: raise ValueError( "result_storage.get_best_solution() " "should not be None." ) if not isinstance(current_solution, MisSolution): raise ValueError( "result_storage.get_best_solution() " "should be a MisSolution." ) nb_chosen = sum(current_solution.chosen) idx_chosen = list(np.where(current_solution.chosen)[0]) subpart_chosen = set( random.sample( idx_chosen, int(self.fraction_to_fix * nb_chosen), ) ) in_set = solver.variables["in_set"] for idx in subpart_chosen: lns_constraints.append(solver.cp_model.Add(in_set[idx] == 0)) # subpart_chosen_2 = set( # random.sample( # range(self.problem.number_nodes), # int(0.2 * self.problem.number_nodes), # ) # ) # for idx in subpart_chosen_2: # if idx not in subpart_chosen: # lns_constraints.append( # solver.cp_model.Add(in_set[idx] == current_solution.chosen[idx]) # ) return lns_constraints
[docs] class AllVarsOrtoolsCpSatMisConstraintHandler(OrtoolsCpSatConstraintHandler): """ Fix fraction of all variables, not only the ones already chosen. """ hyperparameters = [ FloatHyperparameter(name="fraction_to_fix", default=0.9, low=0.0, high=1.0), ] def __init__(self, problem: MisProblem, fraction_to_fix: float = 0.9): self.problem = problem self.fraction_to_fix = fraction_to_fix self.iter = 0
[docs] def adding_constraint_from_results_store( self, solver: CpSatMisSolver, result_storage: ResultStorage, last_result_store: Optional[ResultStorage] = None, **kwargs: Any, ) -> Iterable[Constraint]: if not isinstance(solver, CpSatMisSolver): raise ValueError("solver must a CpSatMisSolver for this constraint.") lns_constraints = [] current_solution, _ = result_storage.list_solution_fits[-1] if current_solution is None: raise ValueError( "result_storage.get_best_solution() " "should not be None." ) if not isinstance(current_solution, MisSolution): raise ValueError( "result_storage.get_best_solution() " "should be a MisSolution." ) subpart_chosen = set( random.sample( range(self.problem.number_nodes), int(self.fraction_to_fix * self.problem.number_nodes), ) ) in_set = solver.variables["in_set"] for idx in subpart_chosen: lns_constraints.append( solver.cp_model.Add(in_set[idx] == current_solution.chosen[idx]) ) return lns_constraints
[docs] class LocalMovesOrtoolsCpSatMisConstraintHandler(OrtoolsCpSatConstraintHandler): hyperparameters = [ IntegerHyperparameter(name="nb_moves", default=1, low=0, high=10), FloatHyperparameter(name="fraction_to_fix", default=0.9, low=0.0, high=1.0), ] def __init__( self, problem: MisProblem, nb_moves: int = 5, fraction_to_fix: float = 0.9 ): self.problem = problem self.nb_moves = nb_moves self.fraction_to_fix = fraction_to_fix
[docs] def adding_constraint_from_results_store( self, solver: CpSatMisSolver, result_storage: ResultStorage, last_result_store: Optional[ResultStorage] = None, **kwargs: Any, ) -> Iterable[Constraint]: if not isinstance(solver, CpSatMisSolver): raise ValueError("solver must a CpSatMisSolver for this constraint.") lns_constraints = [] current_solution, _ = result_storage.get_last_best_solution() if current_solution is None: raise ValueError( "result_storage.get_best_solution() " "should not be None." ) if not isinstance(current_solution, MisSolution): raise ValueError( "result_storage.get_best_solution() " "should be a MisSolution." ) change = [ solver.cp_model.NewBoolVar(name=f"change_{k}") for k in range(self.problem.number_nodes) ] in_set = solver.variables["in_set"] for k in range(self.problem.number_nodes): if current_solution.chosen[k] == 1: lns_constraints.append( solver.cp_model.AddImplication(change[k], in_set[k].Not()) ) lns_constraints.append( solver.cp_model.AddImplication(change[k].Not(), in_set[k]) ) # lns_constraints.append(solver.cp_model.AddImplication(in_set[k].Not(), change[k])) else: lns_constraints.append( solver.cp_model.AddImplication(change[k], in_set[k]) ) lns_constraints.append( solver.cp_model.AddImplication(change[k].Not(), in_set[k].Not()) ) idx_chosen = list(np.where(current_solution.chosen)[0]) nb_chosen = sum(current_solution.chosen) subpart_chosen = set( random.sample( idx_chosen, int(self.fraction_to_fix * nb_chosen), ) ) in_set = solver.variables["in_set"] for idx in subpart_chosen: lns_constraints.append(solver.cp_model.Add(in_set[idx] == 1)) lns_constraints.append(solver.cp_model.Add(sum(change) <= self.nb_moves)) return lns_constraints
# def remove_constraints_from_previous_iteration( # self, # solver: OrtoolsCpSatSolver, # previous_constraints: Iterable[Constraint], # **kwargs: Any, # ) -> None: # solver.init_model()