Edit on GitHub

boundml.solvers

1from .solvers import DefaultScipSolver, Solver, ScipSolver
2from .modular_solver import ModularSolver
3
4__all__ = ["DefaultScipSolver", "ModularSolver", "Solver", "ScipSolver"]
class DefaultScipSolver(boundml.solvers.ScipSolver):
155class DefaultScipSolver(ScipSolver):
156    """
157    Default scip solver.
158    Solve the instances based on the given scip parameters.
159    """
160    def __init__(self, branching_strategy: Optional[str] = None, *args, **kwargs):
161        """
162        Parameters
163        ----------
164        branching_strategy : Optional[str]
165            Branching strategy to use. Must be a default SCIP strategy, or a strategy included in the Model using
166            the configure callback.
167            If None (default). It will use the default strategy of SCIP as set in the parameters.
168        args :
169            Arguments to build the parent class ScipSolver
170        kwargs :
171            Arguments to build the parent class ScipSolver
172        """
173        super().__init__(*args, **kwargs)
174        self.branching_strategy = branching_strategy
175
176    def build_model(self):
177        super().build_model()
178        if self.branching_strategy is not None:
179            self.model.setParam(f"branching/{self.branching_strategy}/priority", 9999999)
180
181
182    def solve(self, instance: str):
183        self.build_model()
184        self.model.readProblem(instance)
185        self.model.optimize()
186
187    def __str__(self):
188        return self.branching_strategy

Default scip solver. Solve the instances based on the given scip parameters.

DefaultScipSolver(branching_strategy: Optional[str] = None, *args, **kwargs)
160    def __init__(self, branching_strategy: Optional[str] = None, *args, **kwargs):
161        """
162        Parameters
163        ----------
164        branching_strategy : Optional[str]
165            Branching strategy to use. Must be a default SCIP strategy, or a strategy included in the Model using
166            the configure callback.
167            If None (default). It will use the default strategy of SCIP as set in the parameters.
168        args :
169            Arguments to build the parent class ScipSolver
170        kwargs :
171            Arguments to build the parent class ScipSolver
172        """
173        super().__init__(*args, **kwargs)
174        self.branching_strategy = branching_strategy
Parameters
  • branching_strategy (Optional[str]): Branching strategy to use. Must be a default SCIP strategy, or a strategy included in the Model using the configure callback. If None (default). It will use the default strategy of SCIP as set in the parameters.
  • args :: Arguments to build the parent class ScipSolver
  • kwargs :: Arguments to build the parent class ScipSolver
branching_strategy
def build_model(self):
176    def build_model(self):
177        super().build_model()
178        if self.branching_strategy is not None:
179            self.model.setParam(f"branching/{self.branching_strategy}/priority", 9999999)
def solve(self, instance: str):
182    def solve(self, instance: str):
183        self.build_model()
184        self.model.readProblem(instance)
185        self.model.optimize()

Solves a instancse that is a file

Parameters
  • instance (str): Path to the instance
class ModularSolver(boundml.solvers.ScipSolver):
12class ModularSolver(ScipSolver):
13    """
14    A ModularSolver is  ScipSolver that is parametrized with a list of Component
15    """
16
17    def __init__(self, *components: Component, scip_params = None, configure: Callable[[Model], None] = None):
18        """
19        Parameters
20        ----------
21        components : [Component]
22             List of components that parametrized the solver. During a solving process, they are called depending on
23             their subtypes (e.g. BranchingComponent are called before making a branching strategy). If several
24             components have the same subtypes, they are called in the order they are given. Only the first component
25             of each subtype is allowed to perform an action (with passive=False). The other must remain passive.
26        scip_params : dict, optional
27            Dictionary of parameters to pass to the scip solver.
28        configure :  Callable[[Model], None], optional
29            Callback function to configure the solver (e.g. add branching strategies, ...)
30        """
31        super().__init__(scip_params, configure)
32
33        self.components = ComponentList(list(components))
34        self.branching_components = []
35
36        for component in components:
37            if isinstance(component, BranchingComponent):
38                self.branching_components.append(component)
39
40
41
42    def build_model(self):
43        super().build_model()
44        branchrule = BoundmlBranchrule(self.model, self.branching_components)
45        self.model.includeBranchrule(
46            branchrule,
47            "boundml",
48            "Custom branching rule for ModularSolver",
49            priority=10000000,
50            maxdepth=-1,
51            maxbounddist=1
52        )
53
54    def solve(self, instance: str):
55        self.build_model()
56
57        self.model.readProblem(instance)
58
59        self.components.reset(self.model)
60
61        self.model.optimize()
62
63        self.components.done(self.model)
64
65    def __str__(self):
66        return "+".join([str(c) for c in self.components])

A ModularSolver is ScipSolver that is parametrized with a list of Component

ModularSolver( *components: boundml.components.Component, scip_params=None, configure: Callable[[pyscipopt.scip.Model], NoneType] = None)
17    def __init__(self, *components: Component, scip_params = None, configure: Callable[[Model], None] = None):
18        """
19        Parameters
20        ----------
21        components : [Component]
22             List of components that parametrized the solver. During a solving process, they are called depending on
23             their subtypes (e.g. BranchingComponent are called before making a branching strategy). If several
24             components have the same subtypes, they are called in the order they are given. Only the first component
25             of each subtype is allowed to perform an action (with passive=False). The other must remain passive.
26        scip_params : dict, optional
27            Dictionary of parameters to pass to the scip solver.
28        configure :  Callable[[Model], None], optional
29            Callback function to configure the solver (e.g. add branching strategies, ...)
30        """
31        super().__init__(scip_params, configure)
32
33        self.components = ComponentList(list(components))
34        self.branching_components = []
35
36        for component in components:
37            if isinstance(component, BranchingComponent):
38                self.branching_components.append(component)
Parameters
  • components ([Component]): List of components that parametrized the solver. During a solving process, they are called depending on their subtypes (e.g. BranchingComponent are called before making a branching strategy). If several components have the same subtypes, they are called in the order they are given. Only the first component of each subtype is allowed to perform an action (with passive=False). The other must remain passive.
  • scip_params (dict, optional): Dictionary of parameters to pass to the scip solver.
  • configure (Callable[[Model], None], optional): Callback function to configure the solver (e.g. add branching strategies, ...)
components
branching_components
def build_model(self):
42    def build_model(self):
43        super().build_model()
44        branchrule = BoundmlBranchrule(self.model, self.branching_components)
45        self.model.includeBranchrule(
46            branchrule,
47            "boundml",
48            "Custom branching rule for ModularSolver",
49            priority=10000000,
50            maxdepth=-1,
51            maxbounddist=1
52        )
def solve(self, instance: str):
54    def solve(self, instance: str):
55        self.build_model()
56
57        self.model.readProblem(instance)
58
59        self.components.reset(self.model)
60
61        self.model.optimize()
62
63        self.components.done(self.model)

Solves a instancse that is a file

Parameters
  • instance (str): Path to the instance
class Solver(abc.ABC):
 8class Solver(ABC):
 9    """
10    An abstract base class for solvers.
11    """
12
13    def __init__(self):
14        self.seed = 0
15        self.items_collectors = {}
16
17
18    def set_seed(self, seed):
19        """
20        Seed used by the underground solver.
21        Parameters
22        ----------
23        seed : int
24            The new seed
25        """
26        self.seed = seed
27
28    @abstractmethod
29    def solve(self, instance: str):
30        """
31        Solves a instancse that is a file
32        Parameters
33        ----------
34        instance : str
35            Path to the instance
36        """
37        raise NotImplementedError("Subclasses must implement this method.")
38
39    def solve_model(self, model: Model):
40        """
41        Solve directly a pyscipopt model.
42        Can work even if the underlying solver is not SCIP, as it only write the corresponding problem to a file in
43        order to use the method solve
44        Parameters
45        ----------
46        model : Model
47            model to solve.
48        """
49        model.setParam("display/verblevel", 0)
50        prob_file = tempfile.NamedTemporaryFile(suffix=".lp")
51        model.writeProblem(prob_file.name, verbose=False)
52
53        self.solve(prob_file.name)
54        prob_file.close()
55
56    def add_item(self, name: str, callback):
57        """
58        Add a callback to compute a specific metric. Used when accessing solver["metric"]
59        Parameters
60        ----------
61        name : Name of th metric added
62        callback : Function that return the metric value given the Solver
63        """
64        self.items_collectors[name] = callback
65
66    def __getitem__(self, item: str) -> float:
67        """
68        Get an attribute from the solver after a solving process.
69
70        Parameters
71        ----------
72        item : str
73            Name of the attribute to get. Depends on the subtype of the solver.
74
75        Returns
76        -------
77        Value of the attribute.
78        """
79        if item in self.items_collectors:
80            return self.items_collectors[item](self)
81        raise KeyError(item)

An abstract base class for solvers.

seed
items_collectors
def set_seed(self, seed):
18    def set_seed(self, seed):
19        """
20        Seed used by the underground solver.
21        Parameters
22        ----------
23        seed : int
24            The new seed
25        """
26        self.seed = seed

Seed used by the underground solver.

Parameters
  • seed (int): The new seed
@abstractmethod
def solve(self, instance: str):
28    @abstractmethod
29    def solve(self, instance: str):
30        """
31        Solves a instancse that is a file
32        Parameters
33        ----------
34        instance : str
35            Path to the instance
36        """
37        raise NotImplementedError("Subclasses must implement this method.")

Solves a instancse that is a file

Parameters
  • instance (str): Path to the instance
def solve_model(self, model: pyscipopt.scip.Model):
39    def solve_model(self, model: Model):
40        """
41        Solve directly a pyscipopt model.
42        Can work even if the underlying solver is not SCIP, as it only write the corresponding problem to a file in
43        order to use the method solve
44        Parameters
45        ----------
46        model : Model
47            model to solve.
48        """
49        model.setParam("display/verblevel", 0)
50        prob_file = tempfile.NamedTemporaryFile(suffix=".lp")
51        model.writeProblem(prob_file.name, verbose=False)
52
53        self.solve(prob_file.name)
54        prob_file.close()

Solve directly a pyscipopt model. Can work even if the underlying solver is not SCIP, as it only write the corresponding problem to a file in order to use the method solve

Parameters
  • model (Model): model to solve.
def add_item(self, name: str, callback):
56    def add_item(self, name: str, callback):
57        """
58        Add a callback to compute a specific metric. Used when accessing solver["metric"]
59        Parameters
60        ----------
61        name : Name of th metric added
62        callback : Function that return the metric value given the Solver
63        """
64        self.items_collectors[name] = callback

Add a callback to compute a specific metric. Used when accessing solver["metric"]

Parameters
  • name (Name of th metric added):

  • callback (Function that return the metric value given the Solver):

class ScipSolver(boundml.solvers.Solver):
 83class ScipSolver(Solver):
 84    def __init__(self, scip_params: dict = None, configure: Callable[[Model], None] = None):
 85        """
 86        Parameters
 87        ----------
 88        scip_params : dict, optional
 89            Dictionary of parameters to pass to the scip solver.
 90        configure :  Callable[[Model], None], optional
 91            Callback function to configure the solver (e.g. add branching strategies, ...)
 92        """
 93        super().__init__()
 94        self.scip_params = scip_params or {}
 95        self.configure = configure
 96        self.model = None
 97
 98    def build_model(self):
 99        self.model = Model()
100        self.model.hideOutput()
101        self.model.setParams(self.scip_params)
102        if self.configure is not None:
103            self.configure(self.model)
104        self.model.setParam("randomization/randomseedshift", self.seed)
105        self.model.setParam("randomization/permutationseed", self.seed)
106        self.model.setParam("randomization/lpseed", self.seed)
107
108    def set_params(self, params):
109        self.scip_params = params
110
111    def __getitem__(self, item: str) -> float:
112        """
113        Get an attribute from the solver after a solving process.
114
115        Parameters
116        ----------
117        item : str
118            Name of the attribute to get. Either:
119                time: time in seconds used by the solver
120                nnodes: number of nodes used by the solver
121                obj: Final objective value
122                sol: Best solution found
123                estimate_nnodes: Estimated number of nodes used by the solver to go to optimality
124
125        Returns
126        -------
127        Value of the attribute.
128        """
129        match item:
130            case "time":
131                return self.model.getSolvingTime()
132            case "nnodes":
133                # return self.model.getNNodes()
134                return self.model.getNTotalNodes()
135            case "obj":
136                return self.model.getObjVal()
137            case "gap":
138                return self.model.getGap()
139            case "sol":
140                return self.model.getBestSol()
141            case "estimate_nnodes":
142                if self.model.getGap() == 0:
143                    return self["nnodes"]
144                else:
145                    return self.model.getTreesizeEstimation()
146            case _:
147                return super().__getitem__(item)
148
149    def __getstate__(self):
150        state = self.__dict__.copy()
151        state['model'] = None
152
153        return state

An abstract base class for solvers.

ScipSolver( scip_params: dict = None, configure: Callable[[pyscipopt.scip.Model], NoneType] = None)
84    def __init__(self, scip_params: dict = None, configure: Callable[[Model], None] = None):
85        """
86        Parameters
87        ----------
88        scip_params : dict, optional
89            Dictionary of parameters to pass to the scip solver.
90        configure :  Callable[[Model], None], optional
91            Callback function to configure the solver (e.g. add branching strategies, ...)
92        """
93        super().__init__()
94        self.scip_params = scip_params or {}
95        self.configure = configure
96        self.model = None
Parameters
  • scip_params (dict, optional): Dictionary of parameters to pass to the scip solver.
  • configure (Callable[[Model], None], optional): Callback function to configure the solver (e.g. add branching strategies, ...)
scip_params
configure
model
def build_model(self):
 98    def build_model(self):
 99        self.model = Model()
100        self.model.hideOutput()
101        self.model.setParams(self.scip_params)
102        if self.configure is not None:
103            self.configure(self.model)
104        self.model.setParam("randomization/randomseedshift", self.seed)
105        self.model.setParam("randomization/permutationseed", self.seed)
106        self.model.setParam("randomization/lpseed", self.seed)
def set_params(self, params):
108    def set_params(self, params):
109        self.scip_params = params