desdeo 1.2__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. desdeo/__init__.py +8 -8
  2. desdeo/api/README.md +73 -0
  3. desdeo/api/__init__.py +15 -0
  4. desdeo/api/app.py +40 -0
  5. desdeo/api/config.py +69 -0
  6. desdeo/api/config.toml +53 -0
  7. desdeo/api/db.py +25 -0
  8. desdeo/api/db_init.py +79 -0
  9. desdeo/api/db_models.py +164 -0
  10. desdeo/api/malaga_db_init.py +27 -0
  11. desdeo/api/models/__init__.py +66 -0
  12. desdeo/api/models/archive.py +34 -0
  13. desdeo/api/models/preference.py +90 -0
  14. desdeo/api/models/problem.py +507 -0
  15. desdeo/api/models/reference_point_method.py +18 -0
  16. desdeo/api/models/session.py +46 -0
  17. desdeo/api/models/state.py +96 -0
  18. desdeo/api/models/user.py +51 -0
  19. desdeo/api/routers/_NAUTILUS.py +245 -0
  20. desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
  21. desdeo/api/routers/_NIMBUS.py +762 -0
  22. desdeo/api/routers/__init__.py +5 -0
  23. desdeo/api/routers/problem.py +110 -0
  24. desdeo/api/routers/reference_point_method.py +117 -0
  25. desdeo/api/routers/session.py +76 -0
  26. desdeo/api/routers/test.py +16 -0
  27. desdeo/api/routers/user_authentication.py +366 -0
  28. desdeo/api/schema.py +94 -0
  29. desdeo/api/tests/__init__.py +0 -0
  30. desdeo/api/tests/conftest.py +59 -0
  31. desdeo/api/tests/test_models.py +701 -0
  32. desdeo/api/tests/test_routes.py +216 -0
  33. desdeo/api/utils/database.py +274 -0
  34. desdeo/api/utils/logger.py +29 -0
  35. desdeo/core.py +27 -0
  36. desdeo/emo/__init__.py +29 -0
  37. desdeo/emo/hooks/archivers.py +172 -0
  38. desdeo/emo/methods/EAs.py +418 -0
  39. desdeo/emo/methods/__init__.py +0 -0
  40. desdeo/emo/methods/bases.py +59 -0
  41. desdeo/emo/operators/__init__.py +1 -0
  42. desdeo/emo/operators/crossover.py +780 -0
  43. desdeo/emo/operators/evaluator.py +118 -0
  44. desdeo/emo/operators/generator.py +356 -0
  45. desdeo/emo/operators/mutation.py +1053 -0
  46. desdeo/emo/operators/selection.py +1036 -0
  47. desdeo/emo/operators/termination.py +178 -0
  48. desdeo/explanations/__init__.py +6 -0
  49. desdeo/explanations/explainer.py +100 -0
  50. desdeo/explanations/utils.py +90 -0
  51. desdeo/mcdm/__init__.py +19 -0
  52. desdeo/mcdm/nautili.py +345 -0
  53. desdeo/mcdm/nautilus.py +477 -0
  54. desdeo/mcdm/nautilus_navigator.py +655 -0
  55. desdeo/mcdm/nimbus.py +417 -0
  56. desdeo/mcdm/pareto_navigator.py +269 -0
  57. desdeo/mcdm/reference_point_method.py +116 -0
  58. desdeo/problem/__init__.py +79 -0
  59. desdeo/problem/evaluator.py +561 -0
  60. desdeo/problem/gurobipy_evaluator.py +562 -0
  61. desdeo/problem/infix_parser.py +341 -0
  62. desdeo/problem/json_parser.py +944 -0
  63. desdeo/problem/pyomo_evaluator.py +468 -0
  64. desdeo/problem/schema.py +1808 -0
  65. desdeo/problem/simulator_evaluator.py +298 -0
  66. desdeo/problem/sympy_evaluator.py +244 -0
  67. desdeo/problem/testproblems/__init__.py +73 -0
  68. desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
  69. desdeo/problem/testproblems/dtlz2_problem.py +102 -0
  70. desdeo/problem/testproblems/forest_problem.py +275 -0
  71. desdeo/problem/testproblems/knapsack_problem.py +163 -0
  72. desdeo/problem/testproblems/mcwb_problem.py +831 -0
  73. desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
  74. desdeo/problem/testproblems/momip_problem.py +172 -0
  75. desdeo/problem/testproblems/nimbus_problem.py +143 -0
  76. desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
  77. desdeo/problem/testproblems/re_problem.py +492 -0
  78. desdeo/problem/testproblems/river_pollution_problem.py +434 -0
  79. desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
  80. desdeo/problem/testproblems/simple_problem.py +351 -0
  81. desdeo/problem/testproblems/simulator_problem.py +92 -0
  82. desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
  83. desdeo/problem/testproblems/zdt_problem.py +271 -0
  84. desdeo/problem/utils.py +245 -0
  85. desdeo/tools/GenerateReferencePoints.py +181 -0
  86. desdeo/tools/__init__.py +102 -0
  87. desdeo/tools/generics.py +145 -0
  88. desdeo/tools/gurobipy_solver_interfaces.py +258 -0
  89. desdeo/tools/indicators_binary.py +11 -0
  90. desdeo/tools/indicators_unary.py +375 -0
  91. desdeo/tools/interaction_schema.py +38 -0
  92. desdeo/tools/intersection.py +54 -0
  93. desdeo/tools/iterative_pareto_representer.py +99 -0
  94. desdeo/tools/message.py +234 -0
  95. desdeo/tools/ng_solver_interfaces.py +199 -0
  96. desdeo/tools/non_dominated_sorting.py +133 -0
  97. desdeo/tools/patterns.py +281 -0
  98. desdeo/tools/proximal_solver.py +99 -0
  99. desdeo/tools/pyomo_solver_interfaces.py +464 -0
  100. desdeo/tools/reference_vectors.py +462 -0
  101. desdeo/tools/scalarization.py +3138 -0
  102. desdeo/tools/scipy_solver_interfaces.py +454 -0
  103. desdeo/tools/score_bands.py +464 -0
  104. desdeo/tools/utils.py +320 -0
  105. desdeo/utopia_stuff/__init__.py +0 -0
  106. desdeo/utopia_stuff/data/1.json +15 -0
  107. desdeo/utopia_stuff/data/2.json +13 -0
  108. desdeo/utopia_stuff/data/3.json +15 -0
  109. desdeo/utopia_stuff/data/4.json +17 -0
  110. desdeo/utopia_stuff/data/5.json +15 -0
  111. desdeo/utopia_stuff/from_json.py +40 -0
  112. desdeo/utopia_stuff/reinit_user.py +38 -0
  113. desdeo/utopia_stuff/utopia_db_init.py +212 -0
  114. desdeo/utopia_stuff/utopia_problem.py +403 -0
  115. desdeo/utopia_stuff/utopia_problem_old.py +415 -0
  116. desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
  117. desdeo-2.0.0.dist-info/LICENSE +21 -0
  118. desdeo-2.0.0.dist-info/METADATA +168 -0
  119. desdeo-2.0.0.dist-info/RECORD +120 -0
  120. {desdeo-1.2.dist-info → desdeo-2.0.0.dist-info}/WHEEL +1 -1
  121. desdeo-1.2.dist-info/METADATA +0 -16
  122. desdeo-1.2.dist-info/RECORD +0 -4
@@ -0,0 +1,464 @@
1
+ """Defines solver interfaces for pyomo."""
2
+
3
+ import itertools
4
+
5
+ import numpy as np
6
+ import pyomo.environ as pyomo
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+ from pyomo.opt import SolverResults as _pyomo_SolverResults
9
+ from pyomo.opt import SolverStatus as _pyomo_SolverStatus
10
+ from pyomo.opt import TerminationCondition as _pyomo_TerminationCondition
11
+
12
+ from desdeo.problem import Problem, PyomoEvaluator, TensorVariable
13
+ from desdeo.tools.generics import BaseSolver, SolverError, SolverResults
14
+
15
+
16
+ class BonminOptions(BaseModel):
17
+ """Defines a pydantic model to store and pass options to the Bonmin solver.
18
+
19
+ Because Bonmin utilizes many sub-solver, the options specific to Bonmin
20
+ must be prefixed in their name with 'bonmin.{option_name}',
21
+ e.g., `bonmin.integer_tolerance`. For a list of options, see
22
+ https://www.coin-or.org/Bonmin/options_list.html
23
+
24
+ Note:
25
+ Not all options are available through this model.
26
+ Please add options as they are needed and make a pull request.
27
+ """
28
+
29
+ tol: float = Field(description="Sets the convergence tolerance of ipopt. Defaults to 1e-8.", default=1e-8)
30
+ """Sets the convergence tolerance of ipopt. Defaults to 1e-8."""
31
+
32
+ bonmin_integer_tolerance: float = Field(
33
+ description="Numbers within this value of an integer are considered integers. Defaults to 1e-6.", default=1e-6
34
+ )
35
+ """Numbers within this value of an integer are considered integers. Defaults to 1e-6."""
36
+
37
+ bonmin_algorithm: str = Field(
38
+ description=(
39
+ "Presets some of the options in Bonmin based on the algorithm choice. Defaults to 'B-BB'. "
40
+ "A good first option to try is 'B-Hyb'."
41
+ ),
42
+ default="B-BB",
43
+ )
44
+ """Presets some of the options in Bonmin based on the algorithm choice. Defaults to 'B-BB'.
45
+ A good first option to try is 'B-Hyb'.
46
+ """
47
+
48
+ def asdict(self) -> dict[str, float]:
49
+ """Converts the Pydantic model into a dict so that Bonmin specific options are in the correct format.
50
+
51
+ This means that the attributes starting with `bonmin_optionname` will be
52
+ converted to keys in the format `bonmin.optionname` in the returned dict.
53
+ """
54
+ output = {}
55
+ for field_name, _ in BonminOptions.model_fields.items():
56
+ if (rest := field_name.split(sep="_"))[0] == "bonmin":
57
+ # Convert to Bonmin specific format
58
+ output[f"bonmin.{'_'.join(rest[1:])}"] = getattr(self, field_name)
59
+ else:
60
+ # Keep the field as is
61
+ output[field_name] = getattr(self, field_name)
62
+
63
+ return output
64
+
65
+
66
+ class IpoptOptions(BaseModel):
67
+ """Defines a pydantic dataclass to pass options to the Ipopt solver.
68
+
69
+ For more information and documentation on the options,
70
+ see https://coin-or.github.io/Ipopt/
71
+
72
+ Note:
73
+ Not all options are available through this model.
74
+ Please add options as they are needed and make a pull request.
75
+ """
76
+
77
+ tol: float = Field(description="The desired relative convergence tolerance. Defaults to 1e-8.", default=1e-8)
78
+ """The desired relative convergence tolerance. Defaults to 1e-8."""
79
+
80
+ max_iter: int = Field(description="Maximum number of iterations. Must be >1. Defaults to 3000.", default=3000)
81
+ """Maximum number of iterations. Must be >1. Defaults to 3000."""
82
+
83
+ print_level: str = Field(
84
+ description="The verbosity level of the solver's output. Ranges between 0 and 12. Defaults to 5.", default=5
85
+ )
86
+ """The verbosity level of the solver's output. Ranges between 0 and 12."""
87
+
88
+
89
+ class CbcOptions(BaseModel):
90
+ """Defines a pydantic dataclass to pass options to the CBC solver.
91
+
92
+ For more information and documentation on the options,
93
+ see https://github.com/coin-or/Cbc
94
+
95
+ Note:
96
+ Not all options are available through this model.
97
+ Please add options as they are needed and make a pull request.
98
+ """
99
+
100
+ model_config = ConfigDict(frozen=True, populate_by_name=True)
101
+
102
+ sec: int = Field(
103
+ description="The maximum amount of time (in seconds) the solver should run. Defaults to None.", default=None
104
+ )
105
+ """The maximum amount of time (in seconds) the solver should run. Defaults to None."""
106
+
107
+ threads: int = Field(
108
+ description="Number of threads (cores) to use for solving the problem. Defaults to 1.", default=1
109
+ )
110
+ """Number of threads (cores) to use for solving the problem. Defaults to 1."""
111
+
112
+ log_level: int = Field(
113
+ alias="logLevel",
114
+ description=(
115
+ "Controls the level of logging output. Values range from 0 (no output) to 5 (very detailed output)."
116
+ " Defaults to 1."
117
+ ),
118
+ default=1,
119
+ )
120
+ """Controls the level of logging output. Values range from 0 (no output) to 5 (very detailed output).
121
+ Defaults to 1.
122
+ """
123
+
124
+ max_solutions: int = Field(
125
+ alias="maxSolutions",
126
+ description="Limits the number of feasible solutions found by the solver. Defaults to None.",
127
+ default=None,
128
+ )
129
+ """Limits the number of feasible solutions found by the solver. Defaults to None."""
130
+
131
+ max_nodes: int = Field(
132
+ alias="maxNodes",
133
+ description="Sets the maximum number of branch-and-bound nodes to explore. Defaults to None.",
134
+ default=None,
135
+ )
136
+ """Sets the maximum number of branch-and-bound nodes to explore. Defaults to None."""
137
+
138
+ ratio_gap: float = Field(
139
+ alias="ratioGap",
140
+ description=(
141
+ "Sets the relative MIP gap (as a fraction of the optimal solution value) at which the solver will"
142
+ " terminate. Defaults to None."
143
+ ),
144
+ default=None,
145
+ )
146
+ """Sets the relative MIP gap (as a fraction of the optimal solution value) at which the solver will terminate.
147
+ Defaults to None.
148
+ """
149
+
150
+ absolute_gap: float = Field(
151
+ alias="absoluteGap",
152
+ description=(
153
+ "Sets the absolute MIP gap (an absolute value) at which the solver will terminate. Defaults to None."
154
+ ),
155
+ default=None,
156
+ )
157
+ """Sets the absolute MIP gap (an absolute value) at which the solver will terminate. Defaults to None."""
158
+
159
+ solve: str = Field(
160
+ description=(
161
+ "Determines the strategy to use for solving the problem (e.g., 'branchAndCut', 'tree', 'trunk')."
162
+ " Defaults to 'branchAndCut'."
163
+ ),
164
+ default="branchAndCut",
165
+ )
166
+ """Determines the strategy to use for solving the problem (e.g., 'branchAndCut', 'tree', 'trunk').
167
+ Defaults to 'branchAndCut'.
168
+ """
169
+
170
+ presolve: int = Field(
171
+ description="Controls the presolve level (0: no presolve, 1: default, 2: aggressive). Defaults to 1.", default=1
172
+ )
173
+ """Controls the presolve level (0: no presolve, 1: default, 2: aggressive). Defaults to 1."""
174
+
175
+ feasibility_tolerance: float = Field(
176
+ alias="feasibilityTolerance",
177
+ description="Sets the feasibility tolerance for constraints. Defaults to 1e-6.",
178
+ default=1e-6,
179
+ )
180
+ """Sets the feasibility tolerance for constraints. Defaults to 1e-6."""
181
+
182
+ integer_tolerance: float = Field(
183
+ alias="integerTolerance",
184
+ description="Sets the tolerance for integrality of integer variables. Defaults to 1e-5.",
185
+ default=1e-5,
186
+ )
187
+ """Sets the tolerance for integrality of integer variables. Defaults to 1e-5."""
188
+
189
+
190
+ _default_cbc_options = CbcOptions(
191
+ sec=600,
192
+ threads=4,
193
+ logLevel=2,
194
+ maxSolutions=10,
195
+ maxNodes=1000,
196
+ ratioGap=0.01,
197
+ absoluteGap=1.0,
198
+ solve="branchAndCut",
199
+ presolve=2,
200
+ feasibilityTolerance=1e-6,
201
+ integerTolerance=1e-5,
202
+ )
203
+ """Defines CBC options with default values."""
204
+
205
+ _default_bonmin_options = BonminOptions()
206
+ """Defines Bonmin options with default values."""
207
+
208
+ _default_ipopt_options = IpoptOptions()
209
+ """Defines Ipopt optins with default values."""
210
+
211
+
212
+ def parse_pyomo_optimizer_results(
213
+ opt_res: _pyomo_SolverResults, problem: Problem, evaluator: PyomoEvaluator
214
+ ) -> SolverResults:
215
+ """Parses pyomo SolverResults into DESDEO SolverResults.
216
+
217
+ Args:
218
+ opt_res (SolverResults): the pyomo solver results.
219
+ problem (Problem): the problem being solved.
220
+ evaluator (PyomoEvaluator): the evaluator utilized to get the pyomo solver results.
221
+
222
+ Returns:
223
+ SolverResults: DESDEO solver results.
224
+ """
225
+ results = evaluator.get_values()
226
+
227
+ variable_values = {}
228
+ for var in problem.variables:
229
+ if isinstance(var, TensorVariable):
230
+ # handle tensor variables
231
+ # 1-indexing in Pyomo...
232
+ values_list = np.zeros(var.shape)
233
+ for indices in itertools.product(*(range(1, dim + 1) for dim in var.shape)):
234
+ values_list[*[idx - 1 for idx in indices]] = results[var.symbol][
235
+ indices if len(indices) > 1 else indices[0]
236
+ ]
237
+ variable_values[var.symbol] = values_list.tolist()
238
+ else:
239
+ # variable_values = {var.symbol: results[var.symbol] for var in problem.variables}
240
+ variable_values[var.symbol] = results[var.symbol]
241
+
242
+ objective_values = {obj.symbol: results[obj.symbol] for obj in problem.objectives}
243
+ constraint_values = (
244
+ {con.symbol: results[con.symbol] for con in problem.constraints} if problem.constraints else None
245
+ )
246
+ extra_func_values = (
247
+ {extra.symbol: results[extra.symbol] for extra in problem.extra_funcs}
248
+ if problem.extra_funcs is not None
249
+ else None
250
+ )
251
+ scalarization_values = (
252
+ {scal.symbol: results[scal.symbol] for scal in problem.scalarization_funcs}
253
+ if problem.scalarization_funcs is not None
254
+ else None
255
+ )
256
+ success = (
257
+ opt_res.solver.status == _pyomo_SolverStatus.ok
258
+ and opt_res.solver.termination_condition == _pyomo_TerminationCondition.optimal
259
+ )
260
+ msg = (
261
+ f"Pyomo solver status is: '{opt_res.solver.status}', with termination condition: "
262
+ f"'{opt_res.solver.termination_condition}'."
263
+ )
264
+
265
+ return SolverResults(
266
+ optimal_variables=variable_values,
267
+ optimal_objectives=objective_values,
268
+ constraint_values=constraint_values,
269
+ extra_func_values=extra_func_values,
270
+ scalarization_values=scalarization_values,
271
+ success=success,
272
+ message=msg,
273
+ )
274
+
275
+
276
+ class PyomoBonminSolver(BaseSolver):
277
+ """Creates pyomo solvers that utilize bonmin."""
278
+
279
+ def __init__(self, problem: Problem, options: BonminOptions | None = _default_bonmin_options):
280
+ """The solver is initialized with a problem and solver options.
281
+
282
+ Suitable for mixed-integer problems. The objective function being minimized
283
+ (target) and the constraint functions must be twice continuously
284
+ differentiable. When the objective functions and constraints are convex, the
285
+ solution is exact. When the objective or any of the constraints, or both,
286
+ are non-convex, then the solution is based on heuristics.
287
+
288
+ For more info about bonmin, see: https://www.coin-or.org/Bonmin/
289
+
290
+ Note:
291
+ Bonmin must be installed on the system running DESDEO, and its executable
292
+ must be defined in the PATH.
293
+
294
+ Args:
295
+ problem (Problem): the problem to be solved.
296
+ options (BonminOptions, optional): options to be passed to the Bonmin solver.
297
+ If `None` is passed, defaults to `_default_bonmin_options` defined in
298
+ this source file. Defaults to `None`.
299
+ """
300
+ if not problem.is_twice_differentiable:
301
+ raise SolverError("Problem must be twice differentiable.")
302
+ self.problem = problem
303
+ self.evaluator = PyomoEvaluator(problem)
304
+
305
+ if options is None:
306
+ self.options = _default_bonmin_options
307
+ else:
308
+ self.options = options
309
+
310
+ def solve(self, target: str) -> SolverResults:
311
+ """Solve the problem for a given target.
312
+
313
+ Args:
314
+ target (str): the symbol of the objective function to be optimized.
315
+
316
+ Returns:
317
+ SolverResults: the results of the optimization.
318
+ """
319
+ self.evaluator.set_optimization_target(target)
320
+
321
+ opt = pyomo.SolverFactory("bonmin", tee=True)
322
+
323
+ # set solver options
324
+ for key, value in self.options.asdict().items():
325
+ opt.options[key] = value
326
+ opt_res = opt.solve(self.evaluator.model)
327
+
328
+ return parse_pyomo_optimizer_results(opt_res, self.problem, self.evaluator)
329
+
330
+
331
+ class PyomoIpoptSolver(BaseSolver):
332
+ """Create a pyomo solver that utilizes Ipopt."""
333
+
334
+ def __init__(self, problem: Problem, options: IpoptOptions | None = _default_ipopt_options):
335
+ """The solver is initialized with a problem and solver options.
336
+
337
+ Suitable for non-linear, twice differentiable constrained problems.
338
+ The problem may be convex or non-convex.
339
+
340
+ For more information, see https://coin-or.github.io/Ipopt/
341
+
342
+ Note:
343
+ Ipopt must be installed on the system running DESDEO, and its executable
344
+ must be defined in the PATH.
345
+
346
+ Args:
347
+ problem (Problem): the problem being solved.
348
+ options (IpoptOptions, optional): options to be passed to the Ipopt solver.
349
+ If `None` is passed, defaults to `_default_ipopt_options` defined in
350
+ this source file. Defaults to `None`.
351
+ """
352
+ if not problem.is_twice_differentiable:
353
+ raise SolverError("Problem must be twice differentiable.")
354
+ self.problem = problem
355
+ self.evaluator = PyomoEvaluator(problem)
356
+
357
+ if options is None:
358
+ self.options = _default_ipopt_options
359
+ else:
360
+ self.options = options
361
+
362
+ def solve(self, target: str) -> SolverResults:
363
+ """Solve the problem for a given target.
364
+
365
+ Args:
366
+ target (str): the symbol of the objective function to be optimized.
367
+
368
+ Returns:
369
+ SolverResults: results of the Optimization.
370
+ """
371
+ self.evaluator.set_optimization_target(target)
372
+
373
+ opt = pyomo.SolverFactory("ipopt", tee=True, options=self.options.model_dump())
374
+ opt_res = opt.solve(self.evaluator.model)
375
+ return parse_pyomo_optimizer_results(opt_res, self.problem, self.evaluator)
376
+
377
+
378
+ class PyomoGurobiSolver(BaseSolver):
379
+ """Creates a pyomo solver that utilized Gurobi."""
380
+
381
+ def __init__(self, problem: Problem, options: dict[str, any] | None = None):
382
+ """Creates a pyomo solver that utilizes gurobi.
383
+
384
+ You need to have gurobi installed on your system for this to work.
385
+
386
+ Suitable for solving mixed-integer linear and quadratic optimization
387
+ problems.
388
+
389
+ Args:
390
+ problem (Problem): the problem to be solved.
391
+ options (GurobiOptions): Dictionary of Gurobi parameters to set.
392
+ This is passed to pyomo as is, so it works the same as options
393
+ would for calling pyomo SolverFactory directly.
394
+ See https://www.gurobi.com/documentation/current/refman/parameters.html
395
+ for information on the available options
396
+ """
397
+ self.problem = problem
398
+ self.evaluator = PyomoEvaluator(problem)
399
+
400
+ if options is None:
401
+ self.options = {}
402
+ else:
403
+ self.options = options
404
+
405
+ def solve(self, target: str) -> SolverResults:
406
+ """Solve the problem for a given target.
407
+
408
+ Args:
409
+ target (str): the symbol of the objective function to be optimized.
410
+
411
+ Returns:
412
+ SolverResults: the results of the optimization.
413
+ """
414
+ self.evaluator.set_optimization_target(target)
415
+
416
+ with pyomo.SolverFactory("gurobi", solver_io="python") as opt:
417
+ opt_res = opt.solve(self.evaluator.model)
418
+ return parse_pyomo_optimizer_results(opt_res, self.problem, self.evaluator)
419
+
420
+
421
+ class PyomoCBCSolver(BaseSolver):
422
+ """Create a pyomo solver that utilizes CBC."""
423
+
424
+ def __init__(self, problem: Problem, options: CbcOptions | None = _default_cbc_options):
425
+ """The solver is initialized with a problem and solver options.
426
+
427
+ Suitable for combinatorial and large-scale mixed-integer linear problems.
428
+
429
+ For more information, see https://coin-or.github.io/Ipopt/
430
+
431
+ Note:
432
+ CBC must be installed on the system running DESDEO, and its executable
433
+ must be defined in the PATH.
434
+
435
+ Args:
436
+ problem (Problem): the problem being solved.
437
+ options (CbcOptions, optional): options to be passed to the CBC solver.
438
+ If `None` is passed, defaults to `_default_cbc_options` defined in
439
+ this source file. Defaults to `None`.
440
+ """
441
+ if not problem.is_linear:
442
+ raise SolverError("Nonlinear problems not supported.")
443
+ self.problem = problem
444
+ self.evaluator = PyomoEvaluator(problem)
445
+
446
+ if options is None:
447
+ self.options = _default_cbc_options
448
+ else:
449
+ self.options = options
450
+
451
+ def solve(self, target: str) -> SolverResults:
452
+ """Solve the problem for a given target.
453
+
454
+ Args:
455
+ target (str): the symbol of the objective function to be optimized.
456
+
457
+ Returns:
458
+ SolverResults: results of the Optimization.
459
+ """
460
+ self.evaluator.set_optimization_target(target)
461
+
462
+ opt = pyomo.SolverFactory("cbc", tee=True, options=self.options.model_dump())
463
+ opt_res = opt.solve(self.evaluator.model)
464
+ return parse_pyomo_optimizer_results(opt_res, self.problem, self.evaluator)