desdeo 1.2__py3-none-any.whl → 2.1.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.
- desdeo/__init__.py +8 -8
- desdeo/adm/ADMAfsar.py +551 -0
- desdeo/adm/ADMChen.py +414 -0
- desdeo/adm/BaseADM.py +119 -0
- desdeo/adm/__init__.py +11 -0
- desdeo/api/README.md +73 -0
- desdeo/api/__init__.py +15 -0
- desdeo/api/app.py +50 -0
- desdeo/api/config.py +90 -0
- desdeo/api/config.toml +64 -0
- desdeo/api/db.py +27 -0
- desdeo/api/db_init.py +85 -0
- desdeo/api/db_models.py +164 -0
- desdeo/api/malaga_db_init.py +27 -0
- desdeo/api/models/__init__.py +266 -0
- desdeo/api/models/archive.py +23 -0
- desdeo/api/models/emo.py +128 -0
- desdeo/api/models/enautilus.py +69 -0
- desdeo/api/models/gdm/gdm_aggregate.py +139 -0
- desdeo/api/models/gdm/gdm_base.py +69 -0
- desdeo/api/models/gdm/gdm_score_bands.py +114 -0
- desdeo/api/models/gdm/gnimbus.py +138 -0
- desdeo/api/models/generic.py +104 -0
- desdeo/api/models/generic_states.py +401 -0
- desdeo/api/models/nimbus.py +158 -0
- desdeo/api/models/preference.py +128 -0
- desdeo/api/models/problem.py +717 -0
- desdeo/api/models/reference_point_method.py +18 -0
- desdeo/api/models/session.py +49 -0
- desdeo/api/models/state.py +463 -0
- desdeo/api/models/user.py +52 -0
- desdeo/api/models/utopia.py +25 -0
- desdeo/api/routers/_EMO.backup +309 -0
- desdeo/api/routers/_NAUTILUS.py +245 -0
- desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
- desdeo/api/routers/_NIMBUS.py +765 -0
- desdeo/api/routers/__init__.py +5 -0
- desdeo/api/routers/emo.py +497 -0
- desdeo/api/routers/enautilus.py +237 -0
- desdeo/api/routers/gdm/gdm_aggregate.py +234 -0
- desdeo/api/routers/gdm/gdm_base.py +420 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +398 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +377 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +698 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +591 -0
- desdeo/api/routers/generic.py +233 -0
- desdeo/api/routers/nimbus.py +705 -0
- desdeo/api/routers/problem.py +307 -0
- desdeo/api/routers/reference_point_method.py +93 -0
- desdeo/api/routers/session.py +100 -0
- desdeo/api/routers/test.py +16 -0
- desdeo/api/routers/user_authentication.py +520 -0
- desdeo/api/routers/utils.py +187 -0
- desdeo/api/routers/utopia.py +230 -0
- desdeo/api/schema.py +100 -0
- desdeo/api/tests/__init__.py +0 -0
- desdeo/api/tests/conftest.py +151 -0
- desdeo/api/tests/test_enautilus.py +330 -0
- desdeo/api/tests/test_models.py +1179 -0
- desdeo/api/tests/test_routes.py +1075 -0
- desdeo/api/utils/_database.py +263 -0
- desdeo/api/utils/_logger.py +29 -0
- desdeo/api/utils/database.py +36 -0
- desdeo/api/utils/emo_database.py +40 -0
- desdeo/core.py +34 -0
- desdeo/emo/__init__.py +159 -0
- desdeo/emo/hooks/archivers.py +188 -0
- desdeo/emo/methods/EAs.py +541 -0
- desdeo/emo/methods/__init__.py +0 -0
- desdeo/emo/methods/bases.py +12 -0
- desdeo/emo/methods/templates.py +111 -0
- desdeo/emo/operators/__init__.py +1 -0
- desdeo/emo/operators/crossover.py +1282 -0
- desdeo/emo/operators/evaluator.py +114 -0
- desdeo/emo/operators/generator.py +459 -0
- desdeo/emo/operators/mutation.py +1224 -0
- desdeo/emo/operators/scalar_selection.py +202 -0
- desdeo/emo/operators/selection.py +1778 -0
- desdeo/emo/operators/termination.py +286 -0
- desdeo/emo/options/__init__.py +108 -0
- desdeo/emo/options/algorithms.py +435 -0
- desdeo/emo/options/crossover.py +164 -0
- desdeo/emo/options/generator.py +131 -0
- desdeo/emo/options/mutation.py +260 -0
- desdeo/emo/options/repair.py +61 -0
- desdeo/emo/options/scalar_selection.py +66 -0
- desdeo/emo/options/selection.py +127 -0
- desdeo/emo/options/templates.py +383 -0
- desdeo/emo/options/termination.py +143 -0
- desdeo/explanations/__init__.py +6 -0
- desdeo/explanations/explainer.py +100 -0
- desdeo/explanations/utils.py +90 -0
- desdeo/gdm/__init__.py +22 -0
- desdeo/gdm/gdmtools.py +45 -0
- desdeo/gdm/score_bands.py +114 -0
- desdeo/gdm/voting_rules.py +50 -0
- desdeo/mcdm/__init__.py +41 -0
- desdeo/mcdm/enautilus.py +338 -0
- desdeo/mcdm/gnimbus.py +484 -0
- desdeo/mcdm/nautili.py +345 -0
- desdeo/mcdm/nautilus.py +477 -0
- desdeo/mcdm/nautilus_navigator.py +656 -0
- desdeo/mcdm/nimbus.py +417 -0
- desdeo/mcdm/pareto_navigator.py +269 -0
- desdeo/mcdm/reference_point_method.py +186 -0
- desdeo/problem/__init__.py +83 -0
- desdeo/problem/evaluator.py +561 -0
- desdeo/problem/external/__init__.py +18 -0
- desdeo/problem/external/core.py +356 -0
- desdeo/problem/external/pymoo_provider.py +266 -0
- desdeo/problem/external/runtime.py +44 -0
- desdeo/problem/gurobipy_evaluator.py +562 -0
- desdeo/problem/infix_parser.py +341 -0
- desdeo/problem/json_parser.py +944 -0
- desdeo/problem/pyomo_evaluator.py +487 -0
- desdeo/problem/schema.py +1829 -0
- desdeo/problem/simulator_evaluator.py +348 -0
- desdeo/problem/sympy_evaluator.py +244 -0
- desdeo/problem/testproblems/__init__.py +88 -0
- desdeo/problem/testproblems/benchmarks_server.py +120 -0
- desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
- desdeo/problem/testproblems/cake_problem.py +185 -0
- desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
- desdeo/problem/testproblems/dtlz2_problem.py +102 -0
- desdeo/problem/testproblems/forest_problem.py +283 -0
- desdeo/problem/testproblems/knapsack_problem.py +163 -0
- desdeo/problem/testproblems/mcwb_problem.py +831 -0
- desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
- desdeo/problem/testproblems/momip_problem.py +172 -0
- desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
- desdeo/problem/testproblems/nimbus_problem.py +143 -0
- desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
- desdeo/problem/testproblems/re_problem.py +492 -0
- desdeo/problem/testproblems/river_pollution_problems.py +440 -0
- desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
- desdeo/problem/testproblems/simple_problem.py +351 -0
- desdeo/problem/testproblems/simulator_problem.py +92 -0
- desdeo/problem/testproblems/single_objective.py +289 -0
- desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
- desdeo/problem/testproblems/zdt_problem.py +274 -0
- desdeo/problem/utils.py +245 -0
- desdeo/tools/GenerateReferencePoints.py +181 -0
- desdeo/tools/__init__.py +120 -0
- desdeo/tools/desc_gen.py +22 -0
- desdeo/tools/generics.py +165 -0
- desdeo/tools/group_scalarization.py +3090 -0
- desdeo/tools/gurobipy_solver_interfaces.py +258 -0
- desdeo/tools/indicators_binary.py +117 -0
- desdeo/tools/indicators_unary.py +362 -0
- desdeo/tools/interaction_schema.py +38 -0
- desdeo/tools/intersection.py +54 -0
- desdeo/tools/iterative_pareto_representer.py +99 -0
- desdeo/tools/message.py +265 -0
- desdeo/tools/ng_solver_interfaces.py +199 -0
- desdeo/tools/non_dominated_sorting.py +134 -0
- desdeo/tools/patterns.py +283 -0
- desdeo/tools/proximal_solver.py +99 -0
- desdeo/tools/pyomo_solver_interfaces.py +477 -0
- desdeo/tools/reference_vectors.py +229 -0
- desdeo/tools/scalarization.py +2065 -0
- desdeo/tools/scipy_solver_interfaces.py +454 -0
- desdeo/tools/score_bands.py +627 -0
- desdeo/tools/utils.py +388 -0
- desdeo/tools/visualizations.py +67 -0
- desdeo/utopia_stuff/__init__.py +0 -0
- desdeo/utopia_stuff/data/1.json +15 -0
- desdeo/utopia_stuff/data/2.json +13 -0
- desdeo/utopia_stuff/data/3.json +15 -0
- desdeo/utopia_stuff/data/4.json +17 -0
- desdeo/utopia_stuff/data/5.json +15 -0
- desdeo/utopia_stuff/from_json.py +40 -0
- desdeo/utopia_stuff/reinit_user.py +38 -0
- desdeo/utopia_stuff/utopia_db_init.py +212 -0
- desdeo/utopia_stuff/utopia_problem.py +403 -0
- desdeo/utopia_stuff/utopia_problem_old.py +415 -0
- desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
- desdeo-2.1.0.dist-info/METADATA +186 -0
- desdeo-2.1.0.dist-info/RECORD +180 -0
- {desdeo-1.2.dist-info → desdeo-2.1.0.dist-info}/WHEEL +1 -1
- desdeo-2.1.0.dist-info/licenses/LICENSE +21 -0
- desdeo-1.2.dist-info/METADATA +0 -16
- desdeo-1.2.dist-info/RECORD +0 -4
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""Database utils for the API."""
|
|
2
|
+
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
from sqlalchemy.engine import URL, Result
|
|
6
|
+
from sqlalchemy.ext.asyncio import (
|
|
7
|
+
AsyncEngine,
|
|
8
|
+
AsyncScalarResult,
|
|
9
|
+
AsyncSession,
|
|
10
|
+
create_async_engine,
|
|
11
|
+
)
|
|
12
|
+
from sqlalchemy.future import select as sa_select
|
|
13
|
+
from sqlalchemy.orm import DeclarativeMeta, declarative_base, sessionmaker
|
|
14
|
+
from sqlalchemy.pool import NullPool
|
|
15
|
+
from sqlalchemy.sql import Executable
|
|
16
|
+
from sqlalchemy.sql.expression import Delete
|
|
17
|
+
from sqlalchemy.sql.expression import delete as sa_delete
|
|
18
|
+
from sqlalchemy.sql.expression import exists as sa_exists
|
|
19
|
+
from sqlalchemy.sql.functions import count
|
|
20
|
+
from sqlalchemy.sql.selectable import Exists, Select
|
|
21
|
+
|
|
22
|
+
from desdeo.api.config import DatabaseConfig as DBConfig
|
|
23
|
+
|
|
24
|
+
from .logger import get_logger
|
|
25
|
+
|
|
26
|
+
T = TypeVar("T")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def select(entity) -> Select:
|
|
30
|
+
"""Shortcut for :meth:`sqlalchemy.future.select`.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
entity (sqlalchemy.sql.expression.FromClause): Entities / DB model to SELECT from.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Select: A Select class object
|
|
37
|
+
"""
|
|
38
|
+
return sa_select(entity)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def filter_by(cls, *args, **kwargs) -> Select:
|
|
42
|
+
"""Shortcut for :meth:`sqlalchemy.future.Select.filter_by`.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
cls (sqlalchemy.sql.expression.FromClause): Entities / DB model to SELECT from.
|
|
46
|
+
*args (tuple): Positional arguments.
|
|
47
|
+
**kwargs (dict): Keyword arguments.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Select: A Select class object with a WHERE clause
|
|
51
|
+
"""
|
|
52
|
+
return select(cls, *args).filter_by(**kwargs)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def exists(*entities, **kwargs) -> Exists:
|
|
56
|
+
"""Shortcut for :meth:`sqlalchemy.sql.expression.exists`.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
*entities (tuple): Positional arguments.
|
|
60
|
+
**kwargs (dict): Keyword arguments.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Exists: A new Exists construct
|
|
64
|
+
"""
|
|
65
|
+
return sa_exists(*entities, **kwargs)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def delete(table) -> Delete:
|
|
69
|
+
"""Shortcut for :meth:`sqlalchemy.sql.expression.delete`.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
table (sqlalchemy.sql.expression.FromClause): Entities / DB model.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Delete: A Delete object
|
|
76
|
+
"""
|
|
77
|
+
return sa_delete(table)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class DB:
|
|
81
|
+
"""An async SQLAlchemy ORM wrapper."""
|
|
82
|
+
|
|
83
|
+
Base: DeclarativeMeta
|
|
84
|
+
_engine: AsyncEngine
|
|
85
|
+
_session: AsyncSession
|
|
86
|
+
|
|
87
|
+
def __init__(self, driver: str, options: dict | None = None, **kwargs):
|
|
88
|
+
"""Init the class.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
driver (str): Drivername for db url.
|
|
92
|
+
options (dict, optional): Options for AsyncEngine instance.
|
|
93
|
+
**kwargs (dict): Keyword arguments.
|
|
94
|
+
"""
|
|
95
|
+
if options is None:
|
|
96
|
+
options = {"pool_size": 20, "max_overflow": 20}
|
|
97
|
+
|
|
98
|
+
url: str = URL.create(drivername=driver, **kwargs)
|
|
99
|
+
self._engine = create_async_engine(url, echo=True, pool_pre_ping=True, pool_recycle=300, **options)
|
|
100
|
+
self.Base = declarative_base()
|
|
101
|
+
self._session: AsyncSession = sessionmaker(self._engine, expire_on_commit=False, class_=AsyncSession)()
|
|
102
|
+
|
|
103
|
+
async def create_tables(self):
|
|
104
|
+
"""Creates all Model Tables."""
|
|
105
|
+
async with self._engine.begin() as conn:
|
|
106
|
+
await conn.run_sync(self.Base.metadata.create_all)
|
|
107
|
+
|
|
108
|
+
async def add(self, obj: T) -> T:
|
|
109
|
+
"""Adds an Row to the Database.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
obj (T): Object to add.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
T: Added object.
|
|
116
|
+
"""
|
|
117
|
+
self._session.add(obj)
|
|
118
|
+
return obj
|
|
119
|
+
|
|
120
|
+
async def delete(self, obj: T) -> T:
|
|
121
|
+
"""Deletes a Row from the Database.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
obj (T): Object to delete.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
T: Deleted object.
|
|
128
|
+
"""
|
|
129
|
+
await self._session.delete(obj)
|
|
130
|
+
return obj
|
|
131
|
+
|
|
132
|
+
async def exec(self, statement: Executable, *args, **kwargs) -> Result:
|
|
133
|
+
"""Executes a SQL Statement.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
statement (Executable): SQL statement.
|
|
137
|
+
*args (tuple): Positional arguments.
|
|
138
|
+
**kwargs (dict): Keyword arguments.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Result: A buffered Result object.
|
|
142
|
+
"""
|
|
143
|
+
return await self._session.execute(statement, *args, **kwargs)
|
|
144
|
+
|
|
145
|
+
async def stream(self, statement: Executable, *args, **kwargs) -> AsyncScalarResult:
|
|
146
|
+
"""Returns an Stream of Query Results.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
statement (Executable): SQL statement.
|
|
150
|
+
*args (tuple): Positional arguments.
|
|
151
|
+
**kwargs (dict): Keyword arguments.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
AsyncScalarResult: An AsyncScalarResult filtering object.
|
|
155
|
+
"""
|
|
156
|
+
return (await self._session.stream(statement, *args, **kwargs)).scalars()
|
|
157
|
+
|
|
158
|
+
async def all(self, statement: Executable, *args, **kwargs) -> list[T]:
|
|
159
|
+
"""Returns all matches for a Query.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
statement (Executable): SQL statement.
|
|
163
|
+
*args (tuple): Positional arguments.
|
|
164
|
+
**kwargs (dict): Keyword arguments.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
list[T]: List of rows.
|
|
168
|
+
"""
|
|
169
|
+
return [x async for x in await self.stream(statement, *args, **kwargs)]
|
|
170
|
+
|
|
171
|
+
async def first(self, *args, **kwargs) -> dict | None:
|
|
172
|
+
"""Returns first match for a Query.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
*args (tuple): Positional arguments.
|
|
176
|
+
**kwargs (dict): Keyword arguments.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
dict | None: First match for the Query, or None if there is no match.
|
|
180
|
+
"""
|
|
181
|
+
return (await self.exec(*args, **kwargs)).scalar()
|
|
182
|
+
|
|
183
|
+
async def exists(self, *args, **kwargs) -> bool:
|
|
184
|
+
"""Checks if there is a match for this Query.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
*args (tuple): Positional arguments.
|
|
188
|
+
**kwargs (dict): Keyword arguments.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
bool: Whether there is a match for this Query
|
|
192
|
+
"""
|
|
193
|
+
return await self.first(exists(*args, **kwargs).select())
|
|
194
|
+
|
|
195
|
+
async def count(self, *args, **kwargs) -> int:
|
|
196
|
+
"""Counts matches for a Query.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
*args (tuple): Positional arguments.
|
|
200
|
+
**kwargs (dict): Keyword arguments.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
int: Number of matches for a Query
|
|
204
|
+
"""
|
|
205
|
+
return await self.first(select(count()).select_from(*args, **kwargs))
|
|
206
|
+
|
|
207
|
+
async def commit(self):
|
|
208
|
+
"""Commits/Saves changes to Database."""
|
|
209
|
+
await self._session.commit()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
logger = get_logger(__name__)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class DatabaseDependency:
|
|
216
|
+
"""A class to represent a database dependency."""
|
|
217
|
+
|
|
218
|
+
db: DB
|
|
219
|
+
database_url_options: dict
|
|
220
|
+
engine_options: dict
|
|
221
|
+
initialized: bool = False
|
|
222
|
+
|
|
223
|
+
def __init__(self):
|
|
224
|
+
"""Initialize the class."""
|
|
225
|
+
self.database_url_options = {
|
|
226
|
+
"host": DBConfig.db_host,
|
|
227
|
+
"port": DBConfig.db_port,
|
|
228
|
+
"database": DBConfig.db_database,
|
|
229
|
+
"username": DBConfig.db_username,
|
|
230
|
+
"password": DBConfig.db_password,
|
|
231
|
+
}
|
|
232
|
+
self.database_url_options = {k: v for k, v in self.database_url_options.items() if v != ""}
|
|
233
|
+
self.engine_options: dict = {
|
|
234
|
+
# "pool_size": DB_POOL_SIZE, ## not used by sqlite
|
|
235
|
+
# "max_overflow": DB_MAX_OVERFLOW, ## not used by sqlite
|
|
236
|
+
"poolclass": DBConfig.db_pool,
|
|
237
|
+
}
|
|
238
|
+
self.engine_options = {k: int(v) for k, v in self.engine_options.items() if v != ""}
|
|
239
|
+
if self.engine_options["poolclass"] == 0:
|
|
240
|
+
self.engine_options["poolclass"] = NullPool
|
|
241
|
+
else:
|
|
242
|
+
del self.engine_options["poolclass"]
|
|
243
|
+
self.db = DB(DBConfig.db_driver, options=self.engine_options, **self.database_url_options)
|
|
244
|
+
logger.info("Connected to Database")
|
|
245
|
+
|
|
246
|
+
async def init(self) -> None:
|
|
247
|
+
"""Set whether the dependency is initialized or not."""
|
|
248
|
+
if self.initialized:
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
# logger.info("Create Tables")
|
|
252
|
+
self.initialized = True
|
|
253
|
+
# await self.db.create_tables()
|
|
254
|
+
|
|
255
|
+
async def __call__(self) -> DB:
|
|
256
|
+
"""Define __call__."""
|
|
257
|
+
if not self.initialized:
|
|
258
|
+
await self.init()
|
|
259
|
+
return self.db
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
database_dependency: DatabaseDependency = DatabaseDependency()
|
|
263
|
+
Base: DeclarativeMeta = database_dependency.db.Base
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
from os import getenv
|
|
5
|
+
|
|
6
|
+
load_dotenv()
|
|
7
|
+
LOG_LEVEL = getenv("LOG_LEVEL", "INFO")
|
|
8
|
+
|
|
9
|
+
logging_formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
|
|
10
|
+
|
|
11
|
+
logging_handler = logging.StreamHandler(sys.stdout)
|
|
12
|
+
logging_handler.setFormatter(logging_formatter)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_logger(name: str) -> logging.Logger:
|
|
16
|
+
"""Get a logger with a given name.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
name (str): Logger name.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
logging.Logger: logging.Logger object.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
logger: logging.Logger = logging.getLogger(name)
|
|
26
|
+
logger.addHandler(logging_handler)
|
|
27
|
+
logger.setLevel(LOG_LEVEL.upper())
|
|
28
|
+
|
|
29
|
+
return logger
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Utilities related to handling the database."""
|
|
2
|
+
|
|
3
|
+
from sqlmodel import Session
|
|
4
|
+
|
|
5
|
+
from desdeo.api.models import StateDB, UserSavedSolutionDB
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def user_save_solutions(
|
|
9
|
+
state_db: StateDB,
|
|
10
|
+
results: list,
|
|
11
|
+
user_id: int,
|
|
12
|
+
session: Session,
|
|
13
|
+
):
|
|
14
|
+
"""Save solutions to the user's archive and create new state in the database.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
state_db: The state containing the solutions
|
|
18
|
+
results: List of solutions to save
|
|
19
|
+
user_id: ID of the user saving the solutions
|
|
20
|
+
session: Database session
|
|
21
|
+
"""
|
|
22
|
+
# Create archive entries for selected solutions
|
|
23
|
+
for solution in results:
|
|
24
|
+
archive_entry = UserSavedSolutionDB(
|
|
25
|
+
name=solution.name if solution.name else None,
|
|
26
|
+
objective_values=solution.objective_values,
|
|
27
|
+
address_state=solution.address_state,
|
|
28
|
+
state_id=state_db.id,
|
|
29
|
+
address_result=solution.address_result,
|
|
30
|
+
user_id=user_id,
|
|
31
|
+
problem_id=state_db.problem_id,
|
|
32
|
+
state=state_db,
|
|
33
|
+
)
|
|
34
|
+
session.add(archive_entry)
|
|
35
|
+
# state is already set in UserSavedSolutionDB, so no need to add it explictly
|
|
36
|
+
session.commit()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Utility functions for EMO database operations."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _convert_dataframe_to_dict_list(df):
|
|
5
|
+
"""Convert DataFrame to list of dictionaries, handling different DataFrame types."""
|
|
6
|
+
if df is None:
|
|
7
|
+
return []
|
|
8
|
+
|
|
9
|
+
# Check if it's a pandas DataFrame
|
|
10
|
+
if hasattr(df, "iterrows"):
|
|
11
|
+
return [row.to_dict() for _, row in df.iterrows()]
|
|
12
|
+
|
|
13
|
+
# Check if it's a Polars DataFrame
|
|
14
|
+
elif hasattr(df, "iter_rows"):
|
|
15
|
+
# Get column names
|
|
16
|
+
columns = df.columns
|
|
17
|
+
return [dict(zip(columns, row)) for row in df.iter_rows()]
|
|
18
|
+
|
|
19
|
+
# Check if it's already a list of dictionaries
|
|
20
|
+
elif isinstance(df, list):
|
|
21
|
+
return df
|
|
22
|
+
|
|
23
|
+
# Check if it has to_dict method (pandas Series)
|
|
24
|
+
elif hasattr(df, "to_dict"):
|
|
25
|
+
return [df.to_dict()]
|
|
26
|
+
|
|
27
|
+
# Try to convert to dict if it's a single row
|
|
28
|
+
elif hasattr(df, "__iter__") and not isinstance(df, (str, dict)):
|
|
29
|
+
try:
|
|
30
|
+
# Assume it's iterable with column names
|
|
31
|
+
if hasattr(df, "columns"):
|
|
32
|
+
return [dict(zip(df.columns, row)) for row in df]
|
|
33
|
+
else:
|
|
34
|
+
# Generic conversion
|
|
35
|
+
return [dict(enumerate(row)) for row in df]
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
# If all else fails, try to convert to string representation
|
|
40
|
+
return [{"data": str(df)}]
|
desdeo/core.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Functionalities related to managing and building the project."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import warnings
|
|
5
|
+
from os import getenv
|
|
6
|
+
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _check_executables(executable_list):
|
|
11
|
+
missing_executables = []
|
|
12
|
+
load_dotenv()
|
|
13
|
+
if getenv("CHECK_EXECUTABLES", "true").lower() == "false":
|
|
14
|
+
return
|
|
15
|
+
|
|
16
|
+
for executable in executable_list:
|
|
17
|
+
if shutil.which(executable) is None:
|
|
18
|
+
missing_executables.append(executable)
|
|
19
|
+
|
|
20
|
+
if missing_executables:
|
|
21
|
+
warnings.warn(
|
|
22
|
+
f"""
|
|
23
|
+
\nThe following required highly recommended executables cannot be found: {", ".join(missing_executables)}\n
|
|
24
|
+
DESDEO relies on powerful 3rd party solvers to solve multiobjective
|
|
25
|
+
optimization problems. Without these solvers, sub-optimal defaults
|
|
26
|
+
will be used instead, which can lead to not optimal, or even wrong,
|
|
27
|
+
results.\n
|
|
28
|
+
It is highly recommended to have these solvers available
|
|
29
|
+
in the environment DESDEO is utilized!\n
|
|
30
|
+
For more information, see DESDEO's documentation: https://desdeo.readthedocs.io/en/latest/howtoguides/installing/#third-party-optimizers
|
|
31
|
+
""",
|
|
32
|
+
UserWarning,
|
|
33
|
+
stacklevel=2,
|
|
34
|
+
)
|
desdeo/emo/__init__.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Imports available from the desdeo emo."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"algorithms",
|
|
5
|
+
"crossover",
|
|
6
|
+
"generator",
|
|
7
|
+
"mutation",
|
|
8
|
+
"other",
|
|
9
|
+
"preference_handling",
|
|
10
|
+
"repair",
|
|
11
|
+
"scalar_selection",
|
|
12
|
+
"selection",
|
|
13
|
+
"templates",
|
|
14
|
+
"termination",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
from types import SimpleNamespace
|
|
18
|
+
|
|
19
|
+
from .options.algorithms import (
|
|
20
|
+
emo_constructor,
|
|
21
|
+
ibea_mixed_integer_options,
|
|
22
|
+
ibea_options,
|
|
23
|
+
nsga2_options,
|
|
24
|
+
nsga3_mixed_integer_options,
|
|
25
|
+
nsga3_options,
|
|
26
|
+
rvea_mixed_integer_options,
|
|
27
|
+
rvea_options,
|
|
28
|
+
)
|
|
29
|
+
from .options.crossover import (
|
|
30
|
+
BlendAlphaCrossoverOptions,
|
|
31
|
+
BoundedExponentialCrossoverOptions,
|
|
32
|
+
LocalCrossoverOptions,
|
|
33
|
+
SimulatedBinaryCrossoverOptions,
|
|
34
|
+
SingleArithmeticCrossoverOptions,
|
|
35
|
+
SinglePointBinaryCrossoverOptions,
|
|
36
|
+
UniformIntegerCrossoverOptions,
|
|
37
|
+
UniformMixedIntegerCrossoverOptions,
|
|
38
|
+
)
|
|
39
|
+
from .options.generator import (
|
|
40
|
+
LHSGeneratorOptions,
|
|
41
|
+
RandomBinaryGeneratorOptions,
|
|
42
|
+
RandomGeneratorOptions,
|
|
43
|
+
RandomIntegerGeneratorOptions,
|
|
44
|
+
RandomMixedIntegerGeneratorOptions,
|
|
45
|
+
)
|
|
46
|
+
from .options.mutation import (
|
|
47
|
+
BinaryFlipMutationOptions,
|
|
48
|
+
BoundedPolynomialMutationOptions,
|
|
49
|
+
IntegerRandomMutationOptions,
|
|
50
|
+
MixedIntegerRandomMutationOptions,
|
|
51
|
+
MPTMutationOptions,
|
|
52
|
+
NonUniformMutationOptions,
|
|
53
|
+
PowerMutationOptions,
|
|
54
|
+
SelfAdaptiveGaussianMutationOptions,
|
|
55
|
+
)
|
|
56
|
+
from .options.repair import ClipRepairOptions, NoRepairOptions
|
|
57
|
+
from .options.scalar_selection import RouletteWheelSelectionOptions, TournamentSelectionOptions
|
|
58
|
+
from .options.selection import (
|
|
59
|
+
IBEASelectorOptions,
|
|
60
|
+
NSGA2SelectorOptions,
|
|
61
|
+
NSGA3SelectorOptions,
|
|
62
|
+
ReferenceVectorOptions,
|
|
63
|
+
RVEASelectorOptions,
|
|
64
|
+
)
|
|
65
|
+
from .options.templates import (
|
|
66
|
+
DesirableRangesOptions,
|
|
67
|
+
NonPreferredSolutionsOptions,
|
|
68
|
+
PreferredSolutionsOptions,
|
|
69
|
+
ReferencePointOptions,
|
|
70
|
+
Template1Options,
|
|
71
|
+
Template2Options,
|
|
72
|
+
)
|
|
73
|
+
from .options.termination import (
|
|
74
|
+
CompositeTerminatorOptions,
|
|
75
|
+
ExternalCheckTerminatorOptions,
|
|
76
|
+
MaxEvaluationsTerminatorOptions,
|
|
77
|
+
MaxGenerationsTerminatorOptions,
|
|
78
|
+
MaxTimeTerminatorOptions,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Just didn't want to have a thousand imports in the main namespace
|
|
82
|
+
algorithms = SimpleNamespace(
|
|
83
|
+
rvea_options=rvea_options,
|
|
84
|
+
nsga2_options=nsga2_options,
|
|
85
|
+
nsga3_options=nsga3_options,
|
|
86
|
+
ibea_options=ibea_options,
|
|
87
|
+
rvea_mixed_integer_options=rvea_mixed_integer_options,
|
|
88
|
+
nsga3_mixed_integer_options=nsga3_mixed_integer_options,
|
|
89
|
+
ibea_mixed_integer_options=ibea_mixed_integer_options,
|
|
90
|
+
emo_constructor=emo_constructor,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
termination = SimpleNamespace(
|
|
94
|
+
MaxEvaluationsTerminatorOptions=MaxEvaluationsTerminatorOptions,
|
|
95
|
+
MaxGenerationsTerminatorOptions=MaxGenerationsTerminatorOptions,
|
|
96
|
+
MaxTimeTerminatorOptions=MaxTimeTerminatorOptions,
|
|
97
|
+
CompositeTerminatorOptions=CompositeTerminatorOptions,
|
|
98
|
+
ExternalCheckTerminatorOptions=ExternalCheckTerminatorOptions,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
selection = SimpleNamespace(
|
|
102
|
+
IBEASelectorOptions=IBEASelectorOptions,
|
|
103
|
+
NSGA3SelectorOptions=NSGA3SelectorOptions,
|
|
104
|
+
NSGA2SelectorOptions=NSGA2SelectorOptions,
|
|
105
|
+
RVEASelectorOptions=RVEASelectorOptions,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
other = SimpleNamespace(
|
|
109
|
+
ReferenceVectorOptions=ReferenceVectorOptions,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
scalar_selection = SimpleNamespace(
|
|
113
|
+
TournamentSelectionOptions=TournamentSelectionOptions,
|
|
114
|
+
RouletteWheelSelectionOptions=RouletteWheelSelectionOptions,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
mutation = SimpleNamespace(
|
|
118
|
+
BinaryFlipMutationOptions=BinaryFlipMutationOptions,
|
|
119
|
+
BoundedPolynomialMutationOptions=BoundedPolynomialMutationOptions,
|
|
120
|
+
IntegerRandomMutationOptions=IntegerRandomMutationOptions,
|
|
121
|
+
MixedIntegerRandomMutationOptions=MixedIntegerRandomMutationOptions,
|
|
122
|
+
MPTMutationOptions=MPTMutationOptions,
|
|
123
|
+
NonUniformMutationOptions=NonUniformMutationOptions,
|
|
124
|
+
PowerMutationOptions=PowerMutationOptions,
|
|
125
|
+
SelfAdaptiveGaussianMutationOptions=SelfAdaptiveGaussianMutationOptions,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
generator = SimpleNamespace(
|
|
129
|
+
LHSGeneratorOptions=LHSGeneratorOptions,
|
|
130
|
+
RandomBinaryGeneratorOptions=RandomBinaryGeneratorOptions,
|
|
131
|
+
RandomGeneratorOptions=RandomGeneratorOptions,
|
|
132
|
+
RandomIntegerGeneratorOptions=RandomIntegerGeneratorOptions,
|
|
133
|
+
RandomMixedIntegerGeneratorOptions=RandomMixedIntegerGeneratorOptions,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
templates = SimpleNamespace(
|
|
137
|
+
Template1Options=Template1Options,
|
|
138
|
+
Template2Options=Template2Options,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
repair = SimpleNamespace(NoRepairOptions=NoRepairOptions, ClipRepairOptions=ClipRepairOptions)
|
|
142
|
+
|
|
143
|
+
preference_handling = SimpleNamespace(
|
|
144
|
+
ReferencePointOptions=ReferencePointOptions,
|
|
145
|
+
DesirableRangesOptions=DesirableRangesOptions,
|
|
146
|
+
PreferredSolutionsOptions=PreferredSolutionsOptions,
|
|
147
|
+
NonPreferredSolutionsOptions=NonPreferredSolutionsOptions,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
crossover = SimpleNamespace(
|
|
151
|
+
BlendAlphaCrossoverOptions=BlendAlphaCrossoverOptions,
|
|
152
|
+
BoundedExponentialCrossoverOptions=BoundedExponentialCrossoverOptions,
|
|
153
|
+
LocalCrossoverOptions=LocalCrossoverOptions,
|
|
154
|
+
SimulatedBinaryCrossoverOptions=SimulatedBinaryCrossoverOptions,
|
|
155
|
+
SingleArithmeticCrossoverOptions=SingleArithmeticCrossoverOptions,
|
|
156
|
+
SinglePointBinaryCrossoverOptions=SinglePointBinaryCrossoverOptions,
|
|
157
|
+
UniformIntegerCrossoverOptions=UniformIntegerCrossoverOptions,
|
|
158
|
+
UniformMixedIntegerCrossoverOptions=UniformMixedIntegerCrossoverOptions,
|
|
159
|
+
)
|