desdeo 2.1.1__py3-none-any.whl → 2.2.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/api/models/nimbus.py +8 -4
- desdeo/api/routers/emo.py +75 -104
- desdeo/api/routers/generic.py +26 -58
- desdeo/api/routers/nimbus.py +108 -247
- desdeo/api/routers/problem.py +69 -56
- desdeo/api/routers/reference_point_method.py +29 -27
- desdeo/api/routers/session.py +15 -11
- desdeo/api/routers/user_authentication.py +27 -5
- desdeo/api/routers/utils.py +42 -37
- desdeo/api/routers/utopia.py +11 -12
- desdeo/api/tests/test_routes.py +6 -5
- desdeo/emo/__init__.py +2 -0
- desdeo/emo/operators/__init__.py +1 -1
- desdeo/emo/operators/generator.py +153 -2
- desdeo/emo/options/__init__.py +4 -0
- desdeo/emo/options/generator.py +24 -0
- {desdeo-2.1.1.dist-info → desdeo-2.2.0.dist-info}/METADATA +20 -10
- {desdeo-2.1.1.dist-info → desdeo-2.2.0.dist-info}/RECORD +20 -20
- {desdeo-2.1.1.dist-info → desdeo-2.2.0.dist-info}/WHEEL +1 -1
- {desdeo-2.1.1.dist-info → desdeo-2.2.0.dist-info}/licenses/LICENSE +0 -0
desdeo/api/routers/utopia.py
CHANGED
|
@@ -4,9 +4,8 @@ import json
|
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
|
|
6
6
|
from fastapi import APIRouter, Depends
|
|
7
|
-
from sqlmodel import
|
|
7
|
+
from sqlmodel import select
|
|
8
8
|
|
|
9
|
-
from desdeo.api.db import get_session
|
|
10
9
|
from desdeo.api.models import (
|
|
11
10
|
ForestProblemMetaData,
|
|
12
11
|
NIMBUSFinalState,
|
|
@@ -14,33 +13,33 @@ from desdeo.api.models import (
|
|
|
14
13
|
NIMBUSSaveState,
|
|
15
14
|
ProblemMetaDataDB,
|
|
16
15
|
StateDB,
|
|
17
|
-
User,
|
|
18
16
|
UtopiaRequest,
|
|
19
17
|
UtopiaResponse,
|
|
20
18
|
)
|
|
21
|
-
from desdeo.api.routers.
|
|
19
|
+
from desdeo.api.routers.utils import SessionContext, get_session_context
|
|
22
20
|
|
|
23
21
|
router = APIRouter(prefix="/utopia")
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
@router.post("/")
|
|
27
|
-
def get_utopia_data(
|
|
25
|
+
def get_utopia_data( # noqa: C901
|
|
28
26
|
request: UtopiaRequest,
|
|
29
|
-
|
|
30
|
-
session: Annotated[Session, Depends(get_session)],
|
|
27
|
+
context: Annotated[SessionContext, Depends(get_session_context)],
|
|
31
28
|
) -> UtopiaResponse:
|
|
32
29
|
"""Request and receive the Utopia map corresponding to the decision variables sent.
|
|
33
30
|
|
|
34
31
|
Args:
|
|
35
32
|
request (UtopiaRequest): the set of decision variables and problem for which the utopia forest map is requested
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
for.
|
|
34
|
+
context (Annotated[SessionContext, Depends(get_session_context)]): the current session context
|
|
35
|
+
|
|
39
36
|
Raises:
|
|
40
37
|
HTTPException:
|
|
41
38
|
Returns:
|
|
42
39
|
UtopiaResponse: the map for the forest, to be rendered in frontend
|
|
43
40
|
"""
|
|
41
|
+
session = context.db_session
|
|
42
|
+
|
|
44
43
|
empty_response = UtopiaResponse(is_utopia=False, map_name="", map_json={}, options={}, description="", years=[])
|
|
45
44
|
|
|
46
45
|
state = session.exec(select(StateDB).where(StateDB.id == request.solution.state_id)).first()
|
|
@@ -105,9 +104,9 @@ def get_utopia_data(
|
|
|
105
104
|
# The dict keys get converted to ints to strings when it's loaded from database
|
|
106
105
|
try:
|
|
107
106
|
treatments = forest_metadata.schedule_dict[key][str(decision_variables[key].index(1))]
|
|
108
|
-
except ValueError
|
|
107
|
+
except ValueError:
|
|
109
108
|
# if the optimization didn't choose any decision alternative, it's safe to assume
|
|
110
|
-
#
|
|
109
|
+
# that nothing is being done at that forest stand
|
|
111
110
|
treatments = forest_metadata.schedule_dict[key]["0"]
|
|
112
111
|
# print(e)
|
|
113
112
|
treatments_dict[key] = {forest_metadata.years[0]: 0, forest_metadata.years[1]: 0, forest_metadata.years[2]: 0}
|
desdeo/api/tests/test_routes.py
CHANGED
|
@@ -110,6 +110,7 @@ def test_refresh(client: TestClient):
|
|
|
110
110
|
response_refresh = client.post("/refresh")
|
|
111
111
|
|
|
112
112
|
assert "access_token" in response_refresh.json()
|
|
113
|
+
assert "access_token" in response_refresh.cookies
|
|
113
114
|
|
|
114
115
|
assert response_good.json()["access_token"] != response_refresh.json()["access_token"]
|
|
115
116
|
|
|
@@ -649,7 +650,7 @@ def test_nimbus_save_and_delete_save(client: TestClient):
|
|
|
649
650
|
assert len(solve_result.saved_solutions) > 0
|
|
650
651
|
|
|
651
652
|
# 4. Delete save
|
|
652
|
-
request: NIMBUSDeleteSaveRequest = NIMBUSDeleteSaveRequest(state_id=2, solution_index=1)
|
|
653
|
+
request: NIMBUSDeleteSaveRequest = NIMBUSDeleteSaveRequest(state_id=2, solution_index=1, problem_id=1)
|
|
653
654
|
response = post_json(client, "/method/nimbus/delete_save", request.model_dump(), access_token)
|
|
654
655
|
delete_save_result: NIMBUSDeleteSaveResponse = NIMBUSDeleteSaveResponse.model_validate(json.loads(response.content))
|
|
655
656
|
|
|
@@ -930,9 +931,9 @@ def test_preferred_solver(client: TestClient):
|
|
|
930
931
|
response = post_json(client, "/method/nimbus/initialize", request.model_dump(), access_token)
|
|
931
932
|
model = NIMBUSInitializationResponse.model_validate(response.json())
|
|
932
933
|
except Exception as e:
|
|
933
|
-
print(e)
|
|
934
|
-
print("^ This outcome is expected since pyomo_cbc doesn't support nonlinear problems.")
|
|
935
|
-
print(" As that solver is what we set it to be in the start, we can verify that they actually get used.")
|
|
934
|
+
print(e) # noqa: T201
|
|
935
|
+
print("^ This outcome is expected since pyomo_cbc doesn't support nonlinear problems.") # noqa: T201
|
|
936
|
+
print(" As that solver is what we set it to be in the start, we can verify that they actually get used.") # noqa: T201
|
|
936
937
|
|
|
937
938
|
|
|
938
939
|
def test_get_available_solvers(client: TestClient):
|
|
@@ -1027,7 +1028,7 @@ def test_gdm_score_bands(client: TestClient):
|
|
|
1027
1028
|
response = post_json(client=client, endpoint="/gdm/add_to_group", json=req, access_token=access_token)
|
|
1028
1029
|
assert response.status_code == 200
|
|
1029
1030
|
|
|
1030
|
-
access_token = login(client=client, username="dm", password="dm")
|
|
1031
|
+
access_token = login(client=client, username="dm", password="dm") # noqa: S106
|
|
1031
1032
|
|
|
1032
1033
|
# Now we have a group, so let's get on with making stuff with gdm score bands.
|
|
1033
1034
|
req = GDMScoreBandsInitializationRequest(
|
desdeo/emo/__init__.py
CHANGED
|
@@ -42,6 +42,7 @@ from .options.generator import (
|
|
|
42
42
|
RandomGeneratorOptions,
|
|
43
43
|
RandomIntegerGeneratorOptions,
|
|
44
44
|
RandomMixedIntegerGeneratorOptions,
|
|
45
|
+
SeededHybridGeneratorOptions,
|
|
45
46
|
)
|
|
46
47
|
from .options.mutation import (
|
|
47
48
|
BinaryFlipMutationOptions,
|
|
@@ -131,6 +132,7 @@ generator = SimpleNamespace(
|
|
|
131
132
|
RandomGeneratorOptions=RandomGeneratorOptions,
|
|
132
133
|
RandomIntegerGeneratorOptions=RandomIntegerGeneratorOptions,
|
|
133
134
|
RandomMixedIntegerGeneratorOptions=RandomMixedIntegerGeneratorOptions,
|
|
135
|
+
SeededHybridGeneratorOptions=SeededHybridGeneratorOptions,
|
|
134
136
|
)
|
|
135
137
|
|
|
136
138
|
templates = SimpleNamespace(
|
desdeo/emo/operators/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"""Exports of the 'operators' module."""
|
|
@@ -383,7 +383,7 @@ class ArchiveGenerator(BaseGenerator):
|
|
|
383
383
|
publisher: Publisher,
|
|
384
384
|
verbosity: int,
|
|
385
385
|
solutions: pl.DataFrame,
|
|
386
|
-
**kwargs, # just to dump seed
|
|
386
|
+
**kwargs: dict, # just to dump seed
|
|
387
387
|
):
|
|
388
388
|
"""Initialize the ArchiveGenerator class.
|
|
389
389
|
|
|
@@ -395,10 +395,11 @@ class ArchiveGenerator(BaseGenerator):
|
|
|
395
395
|
verbosity (int): The verbosity level of the generator. A verbosity of 2 is needed if you want to maintain
|
|
396
396
|
an external archive. Otherwise, a verbosity of 1 is sufficient.
|
|
397
397
|
solutions (pl.DataFrame): The decision variable vectors to use as the initial population.
|
|
398
|
+
kwargs (dict): Other keyword arguments to pass, e.g., a random seed.
|
|
398
399
|
"""
|
|
399
400
|
super().__init__(problem, verbosity=verbosity, publisher=publisher)
|
|
400
401
|
if not isinstance(solutions, pl.DataFrame):
|
|
401
|
-
raise
|
|
402
|
+
raise TypeError("The solutions must be a polars DataFrame.")
|
|
402
403
|
if solutions.shape[0] == 0:
|
|
403
404
|
raise ValueError("The solutions DataFrame is empty.")
|
|
404
405
|
self.solutions = solutions
|
|
@@ -457,3 +458,153 @@ class ArchiveGenerator(BaseGenerator):
|
|
|
457
458
|
|
|
458
459
|
def update(self, message) -> None:
|
|
459
460
|
"""Update the generator based on the message."""
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class SeededHybridGenerator(BaseGenerator):
|
|
464
|
+
"""Generates an initial population using a mix of seeded, perturbed, and random solutions."""
|
|
465
|
+
|
|
466
|
+
def __init__(
|
|
467
|
+
self,
|
|
468
|
+
problem,
|
|
469
|
+
evaluator,
|
|
470
|
+
publisher,
|
|
471
|
+
verbosity,
|
|
472
|
+
seed: int,
|
|
473
|
+
n_points: int,
|
|
474
|
+
seed_solution: pl.DataFrame,
|
|
475
|
+
perturb_fraction: float = 0.2,
|
|
476
|
+
sigma: float = 0.02,
|
|
477
|
+
flip_prob: float = 0.1,
|
|
478
|
+
):
|
|
479
|
+
"""Initialize the seeded hybrid generator.
|
|
480
|
+
|
|
481
|
+
The generator always includes the provided seed solution in the initial
|
|
482
|
+
population, fills a fraction of the population with small perturbations
|
|
483
|
+
around the seed, and fills the remainder with randomly generated solutions.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
problem (Problem): The optimization problem.
|
|
487
|
+
evaluator (EMOEvaluator): Evaluator used to compute objectives and constraints.
|
|
488
|
+
publisher (Publisher): Publisher used for emitting generator messages.
|
|
489
|
+
verbosity (int): Verbosity level of the generator.
|
|
490
|
+
seed (int): Seed used for random number generation.
|
|
491
|
+
n_points (int): Total size of the initial population.
|
|
492
|
+
seed_solution (pl.DataFrame): A single-row DataFrame containing a seed
|
|
493
|
+
decision variable vector.
|
|
494
|
+
perturb_fraction (float, optional): Fraction of the population generated
|
|
495
|
+
by perturbing the seed solution. Defaults to 0.2.
|
|
496
|
+
sigma (float, optional): Relative perturbation scale with respect to
|
|
497
|
+
variable ranges. Defaults to 0.02.
|
|
498
|
+
flip_prob (float, optional): Probability of flipping a binary variable
|
|
499
|
+
when perturbing the seed. Defaults to 0.1.
|
|
500
|
+
|
|
501
|
+
Raises:
|
|
502
|
+
TypeError: If ``seed_solution`` is not a polars DataFrame.
|
|
503
|
+
ValueError: If ``seed_solution`` does not contain exactly one row.
|
|
504
|
+
ValueError: If ``seed_solution`` columns do not match problem variables.
|
|
505
|
+
ValueError: If ``n_points`` is not positive.
|
|
506
|
+
ValueError: If ``perturb_fraction`` is outside ``[0, 1]``.
|
|
507
|
+
ValueError: If ``sigma`` is negative.
|
|
508
|
+
ValueError: If ``flip_prob`` is outside ``[0, 1]``.
|
|
509
|
+
"""
|
|
510
|
+
super().__init__(problem, verbosity=verbosity, publisher=publisher)
|
|
511
|
+
|
|
512
|
+
if not isinstance(seed_solution, pl.DataFrame):
|
|
513
|
+
raise TypeError("seed_solution must be a polars DataFrame.")
|
|
514
|
+
if seed_solution.shape[0] != 1:
|
|
515
|
+
raise ValueError("seed_solution must have exactly one row.")
|
|
516
|
+
if set(seed_solution.columns) != set(self.variable_symbols):
|
|
517
|
+
raise ValueError("seed_solution columns must match problem variables.")
|
|
518
|
+
|
|
519
|
+
if n_points <= 0:
|
|
520
|
+
raise ValueError("n_points must be > 0.")
|
|
521
|
+
if not (0.0 <= perturb_fraction <= 1.0):
|
|
522
|
+
raise ValueError("perturb_fraction must be in [0, 1].")
|
|
523
|
+
if sigma < 0:
|
|
524
|
+
raise ValueError("sigma must be >= 0.")
|
|
525
|
+
if not (0.0 <= flip_prob <= 1.0):
|
|
526
|
+
raise ValueError("flip_prob must be in [0, 1].")
|
|
527
|
+
|
|
528
|
+
self.n_points = n_points
|
|
529
|
+
self.seed_solution = seed_solution
|
|
530
|
+
self.perturb_fraction = perturb_fraction
|
|
531
|
+
self.sigma = sigma
|
|
532
|
+
self.flip_prob = flip_prob
|
|
533
|
+
|
|
534
|
+
self.evaluator = evaluator
|
|
535
|
+
self.seed = seed
|
|
536
|
+
self.rng = np.random.default_rng(self.seed)
|
|
537
|
+
|
|
538
|
+
self.population = None
|
|
539
|
+
self.out = None
|
|
540
|
+
|
|
541
|
+
def _random_population(self, n: int) -> pl.DataFrame:
|
|
542
|
+
tmp = {}
|
|
543
|
+
for var in self.problem.variables:
|
|
544
|
+
if var.variable_type in [VariableTypeEnum.binary, VariableTypeEnum.integer]:
|
|
545
|
+
vals = self.rng.integers(var.lowerbound, var.upperbound, size=n, endpoint=True).astype(float)
|
|
546
|
+
else:
|
|
547
|
+
vals = self.rng.uniform(var.lowerbound, var.upperbound, size=n).astype(float)
|
|
548
|
+
tmp[var.symbol] = vals
|
|
549
|
+
return pl.DataFrame(tmp)
|
|
550
|
+
|
|
551
|
+
def _perturb_seed(self, n: int) -> pl.DataFrame:
|
|
552
|
+
# includes the exact seed as first row
|
|
553
|
+
seed_row = self.seed_solution.select(self.variable_symbols).to_dict(as_series=False)
|
|
554
|
+
seed_vals = {k: float(v[0]) for k, v in seed_row.items()}
|
|
555
|
+
|
|
556
|
+
rows = [seed_vals] # ensure seed present
|
|
557
|
+
if n <= 1:
|
|
558
|
+
return pl.DataFrame(rows)
|
|
559
|
+
|
|
560
|
+
for _ in range(n - 1):
|
|
561
|
+
x = {}
|
|
562
|
+
for var in self.problem.variables:
|
|
563
|
+
lb, ub = float(var.lowerbound), float(var.upperbound)
|
|
564
|
+
r = ub - lb
|
|
565
|
+
|
|
566
|
+
v0 = seed_vals[var.symbol]
|
|
567
|
+
|
|
568
|
+
if var.variable_type == VariableTypeEnum.binary:
|
|
569
|
+
v = 1.0 - v0 if self.rng.random() < self.flip_prob else v0
|
|
570
|
+
elif var.variable_type == VariableTypeEnum.integer:
|
|
571
|
+
# scales integer nose
|
|
572
|
+
step = max(1, round(self.sigma * r)) if r >= 1 else 0
|
|
573
|
+
dv = self.rng.integers(-step, step + 1) if step > 0 else 0
|
|
574
|
+
v = float(int(np.clip(round(v0 + dv), lb, ub)))
|
|
575
|
+
else:
|
|
576
|
+
# continuous noise is proportional to range
|
|
577
|
+
dv = self.rng.normal(0.0, self.sigma * r if r > 0 else 0.0)
|
|
578
|
+
v = float(np.clip(v0 + dv, lb, ub))
|
|
579
|
+
|
|
580
|
+
x[var.symbol] = v
|
|
581
|
+
rows.append(x)
|
|
582
|
+
|
|
583
|
+
return pl.DataFrame(rows)
|
|
584
|
+
|
|
585
|
+
def do(self) -> tuple[pl.DataFrame, pl.DataFrame]:
|
|
586
|
+
"""Generate a population.
|
|
587
|
+
|
|
588
|
+
Returns:
|
|
589
|
+
tuple[pl.DataFrame, pl.DataFrame]: the population.
|
|
590
|
+
"""
|
|
591
|
+
if self.population is not None and self.out is not None:
|
|
592
|
+
self.notify()
|
|
593
|
+
return self.population, self.out
|
|
594
|
+
|
|
595
|
+
n_pert = max(1, round(self.perturb_fraction * self.n_points))
|
|
596
|
+
n_pert = min(n_pert, self.n_points)
|
|
597
|
+
n_rand = self.n_points - n_pert
|
|
598
|
+
|
|
599
|
+
pert = self._perturb_seed(n_pert)
|
|
600
|
+
rand = self._random_population(n_rand) if n_rand > 0 else pl.DataFrame({s: [] for s in self.variable_symbols})
|
|
601
|
+
|
|
602
|
+
self.population = pl.concat([pert, rand], how="vertical")
|
|
603
|
+
|
|
604
|
+
self.out = self.evaluator.evaluate(self.population)
|
|
605
|
+
self.notify()
|
|
606
|
+
|
|
607
|
+
return self.population, self.out
|
|
608
|
+
|
|
609
|
+
def update(self, message) -> None:
|
|
610
|
+
"""Update the generator based on the message."""
|
desdeo/emo/options/__init__.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Exports of the 'options' module."""
|
|
2
|
+
|
|
1
3
|
from .crossover import (
|
|
2
4
|
BlendAlphaCrossoverOptions,
|
|
3
5
|
BoundedExponentialCrossoverOptions,
|
|
@@ -17,6 +19,7 @@ from .generator import (
|
|
|
17
19
|
RandomGeneratorOptions,
|
|
18
20
|
RandomIntegerGeneratorOptions,
|
|
19
21
|
RandomMixedIntegerGeneratorOptions,
|
|
22
|
+
SeededHybridGeneratorOptions,
|
|
20
23
|
generator_constructor,
|
|
21
24
|
)
|
|
22
25
|
from .mutation import (
|
|
@@ -105,4 +108,5 @@ __all__ = [ # noqa: RUF022
|
|
|
105
108
|
"selection_constructor",
|
|
106
109
|
"RepairOptions",
|
|
107
110
|
"repair_constructor",
|
|
111
|
+
"SeededHybridGeneratorOptions",
|
|
108
112
|
]
|
desdeo/emo/options/generator.py
CHANGED
|
@@ -16,6 +16,7 @@ from desdeo.emo.operators.generator import (
|
|
|
16
16
|
RandomGenerator,
|
|
17
17
|
RandomIntegerGenerator,
|
|
18
18
|
RandomMixedIntegerGenerator,
|
|
19
|
+
SeededHybridGenerator,
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
if TYPE_CHECKING:
|
|
@@ -85,6 +86,27 @@ class ArchiveGeneratorOptions(BaseModel):
|
|
|
85
86
|
outputs: pl.DataFrame
|
|
86
87
|
"""The corresponding outputs of the initial solutions."""
|
|
87
88
|
|
|
89
|
+
|
|
90
|
+
class SeededHybridGeneratorOptions(BaseGeneratorOptions):
|
|
91
|
+
"""Options for the seeded hybrid generator."""
|
|
92
|
+
|
|
93
|
+
name: Literal["SeededHybridGenerator"] = Field(default="SeededHybridGenerator", frozen=True)
|
|
94
|
+
model_config = {"arbitrary_types_allowed": True, "use_attribute_docstrings": True}
|
|
95
|
+
|
|
96
|
+
seed_solution: pl.DataFrame
|
|
97
|
+
"""A dataframe with a single row representing the solution seed. The columns
|
|
98
|
+
must math the symbols of the variables in the problem being solved.
|
|
99
|
+
"""
|
|
100
|
+
perturb_fraction: float = Field(default=0.2, ge=0.0, le=1.0)
|
|
101
|
+
"""The desired fraction of perturbed vs random solutions in the generated population."""
|
|
102
|
+
|
|
103
|
+
sigma: float = Field(default=0.02, ge=0.0)
|
|
104
|
+
"""The relative perturbation scale with respect to variable ranges."""
|
|
105
|
+
|
|
106
|
+
flip_prob: float = Field(default=0.1, ge=0.0, le=1.0)
|
|
107
|
+
"""The flipping probability when perturbing binary variables."""
|
|
108
|
+
|
|
109
|
+
|
|
88
110
|
GeneratorOptions = (
|
|
89
111
|
LHSGeneratorOptions
|
|
90
112
|
| RandomBinaryGeneratorOptions
|
|
@@ -92,6 +114,7 @@ GeneratorOptions = (
|
|
|
92
114
|
| RandomIntegerGeneratorOptions
|
|
93
115
|
| RandomMixedIntegerGeneratorOptions
|
|
94
116
|
| ArchiveGeneratorOptions
|
|
117
|
+
| SeededHybridGeneratorOptions
|
|
95
118
|
)
|
|
96
119
|
|
|
97
120
|
|
|
@@ -123,6 +146,7 @@ def generator_constructor(
|
|
|
123
146
|
"RandomIntegerGenerator": RandomIntegerGenerator,
|
|
124
147
|
"RandomMixedIntegerGenerator": RandomMixedIntegerGenerator,
|
|
125
148
|
"ArchiveGenerator": ArchiveGenerator,
|
|
149
|
+
"SeededHybridGenerator": SeededHybridGenerator,
|
|
126
150
|
}
|
|
127
151
|
options: dict = options.model_dump()
|
|
128
152
|
name = options.pop("name")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: desdeo
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: DESDEO is a modular and open source framework for interactive multiobjective optimization.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -40,7 +40,12 @@ Project-URL: Homepage, https://github.com/industrial-optimization-group/DESDEO
|
|
|
40
40
|
Project-URL: Repository, https://github.com/industrial-optimization-group/DESDEO
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+

|
|
44
|
+
[](https://pypi.org/project/desdeo/)
|
|
45
|
+
[](https://desdeo.readthedocs.io/en/latest/)
|
|
46
|
+

|
|
47
|
+
[](https://discord.gg/uGCEgQTJyY)
|
|
48
|
+
|
|
44
49
|
|
|
45
50
|
# DESDEO: the open-source software framework for interactive multiobjective optimization
|
|
46
51
|
## Introduction
|
|
@@ -73,13 +78,17 @@ decision-support using the framework. __The
|
|
|
73
78
|
web-API is currently under heavy development, and is subject to changes.__
|
|
74
79
|
3. The __web-GUI__ (WIP), which implements a web-based interface for utilizing
|
|
75
80
|
the interactive methods and tools for modeling and solving multiobjective
|
|
76
|
-
optimization problems.
|
|
81
|
+
optimization problems.
|
|
82
|
+
|
|
83
|
+
> __The web-GUI relies heavily on the web-API, and is also being actively developed currently, and therefore subject to sudden changes.__
|
|
77
84
|
|
|
78
85
|
For developing and experimenting with interactive multiobjective optimization
|
|
79
86
|
methods on a "grass root" level, the __core-logic__ provides the necessary
|
|
80
|
-
tools. For deploying interactive methods, the __web-API__ and the
|
|
87
|
+
tools. For deploying interactive methods, the __web-API__ and the __web-GUI__
|
|
81
88
|
play a central role.
|
|
82
89
|
|
|
90
|
+
> Users interested in using or developing the web-API and/or web-GUI are highly encouraged to express such intentions on our [Discord server](https://discord.gg/uGCEgQTJyY)!.
|
|
91
|
+
|
|
83
92
|
DESDEO is an open-source project and everybody is welcome to contribute!
|
|
84
93
|
|
|
85
94
|
## Core-logic: key features
|
|
@@ -111,11 +120,12 @@ issue](https://github.com/industrial-optimization-group/DESDEO/issues/245).
|
|
|
111
120
|
|
|
112
121
|
## Web-GUI: key features
|
|
113
122
|
|
|
114
|
-
DESDEO's web-GUI is currently
|
|
115
|
-
|
|
116
|
-
|
|
123
|
+
DESDEO's web-GUI is currently under active development. Once it stabilized, its
|
|
124
|
+
key features will be listed here. In the meantime, the interested user can
|
|
125
|
+
follow (and contribute!) the development progress of the web-API in [this
|
|
126
|
+
issue](https://github.com/industrial-optimization-group/DESDEO/issues/251).
|
|
117
127
|
|
|
118
|
-
## Installation instructions
|
|
128
|
+
## Installation instructions (core-logic)
|
|
119
129
|
|
|
120
130
|
DESDEO is available on PyPI to be installed via pip:
|
|
121
131
|
|
|
@@ -174,12 +184,12 @@ this repository's master branch is considered to be _DESDEO 2.0_.
|
|
|
174
184
|
|
|
175
185
|
## Funding
|
|
176
186
|
|
|
177
|
-
Currently, DESDEO's development
|
|
187
|
+
Currently, DESDEO's development has been funded by projects granted by the
|
|
178
188
|
[Research Council of Finland](https://www.aka.fi/en/). The most recent ones
|
|
179
189
|
include:
|
|
180
190
|
|
|
181
191
|
- DESIDES (project 355346)
|
|
182
192
|
- UTOPIA (project 352784)
|
|
183
193
|
- DAEMON (project 322221)
|
|
184
|
-
|
|
194
|
+
- DESDEO (project 287496)
|
|
185
195
|
|
|
@@ -22,7 +22,7 @@ desdeo/api/models/gdm/gdm_score_bands.py,sha256=C2RMO8Q-kkaIlljBstqjkjY6kSzKLsA9
|
|
|
22
22
|
desdeo/api/models/gdm/gnimbus.py,sha256=W0WulzY4qWi510lnBnTUrN-VFfkn4cES5pL1iHi_HwI,4709
|
|
23
23
|
desdeo/api/models/generic.py,sha256=TxCtdNoR0lx5j2IBvItxhGiiSQz77orZRl1VmdHdQbQ,4345
|
|
24
24
|
desdeo/api/models/generic_states.py,sha256=vBEwE7l2s1U7fr-xOaePfsRBrL0CMaZRIVF7Y1_mJiY,12848
|
|
25
|
-
desdeo/api/models/nimbus.py,sha256=
|
|
25
|
+
desdeo/api/models/nimbus.py,sha256=w5Ba1i_5NRFYLsOhZnEjXiAFFY-tHQa-apOewMAuUbY,6172
|
|
26
26
|
desdeo/api/models/preference.py,sha256=BnsRR_JBEdJsz0C3aOpOtQ5FZU5cO4ZlzGAPTEe_nI8,4719
|
|
27
27
|
desdeo/api/models/problem.py,sha256=wO5j4Ytjmo1FE-KSL3elO0oB-7UnCY_7lJLjublmA7Q,26474
|
|
28
28
|
desdeo/api/models/reference_point_method.py,sha256=afEX6CKrgvrCh7yYBfEGTcKiB1cNDVEsJ6-KVB-T4-w,678
|
|
@@ -35,7 +35,7 @@ desdeo/api/routers/_NAUTILUS.py,sha256=rNrbWUjPGvslIpxybMi44vNCUMeewsVwNKcUwvBPw
|
|
|
35
35
|
desdeo/api/routers/_NAUTILUS_navigator.py,sha256=64i9Fsa-P69K39S_3iGfN5BHlNRgeC6Ha2g6__MsECw,9802
|
|
36
36
|
desdeo/api/routers/_NIMBUS.py,sha256=20IfUuKYc6SAmyLutmDPbPwJulnq-PyLLUYhFFqKz3o,28806
|
|
37
37
|
desdeo/api/routers/__init__.py,sha256=hJXYK27BbnIR_ShlYnHJOzHid0ZXABkDL3rB1SVuUgI,111
|
|
38
|
-
desdeo/api/routers/emo.py,sha256=
|
|
38
|
+
desdeo/api/routers/emo.py,sha256=2PAnNN_GrwqMf-VemFY0rUkMGz7ARwLaurL4eLY5vjo,17766
|
|
39
39
|
desdeo/api/routers/enautilus.py,sha256=-wUH4PJl_-Ko0ivOCTcGPfxPDA2iUn3jJc9IESYZ-Y4,9157
|
|
40
40
|
desdeo/api/routers/gdm/gdm_aggregate.py,sha256=QrT3FOCvJXGhDhlg22rqe3JeyRY3nHqJzTAGKAQR9oI,8956
|
|
41
41
|
desdeo/api/routers/gdm/gdm_base.py,sha256=5nEab8qPoIeRcyI3DybS4OncdE1bViGDI7oBZifoSsA,14164
|
|
@@ -43,44 +43,44 @@ desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py,sha256=NLiZdRJ
|
|
|
43
43
|
desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py,sha256=GYxjL3cpCgrqk-oRmC0wX61b9PB0mc0mE_0mVdpLZH4,15026
|
|
44
44
|
desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py,sha256=Nqk06HYVO6lM-g1hoihB_J6_Iw2oKAsF28xeTxRtj_4,26854
|
|
45
45
|
desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py,sha256=vObjUUemINEp5Brk1CPDCOYp4Hzrz2XbNYu_rPw9LNo,22913
|
|
46
|
-
desdeo/api/routers/generic.py,sha256=
|
|
47
|
-
desdeo/api/routers/nimbus.py,sha256=
|
|
48
|
-
desdeo/api/routers/problem.py,sha256=
|
|
49
|
-
desdeo/api/routers/reference_point_method.py,sha256=
|
|
50
|
-
desdeo/api/routers/session.py,sha256=
|
|
46
|
+
desdeo/api/routers/generic.py,sha256=MhCizImj9ukVqUEbd3K1dr-4Da41eApQzJ0R_uM85W8,7569
|
|
47
|
+
desdeo/api/routers/nimbus.py,sha256=Ex6igiEKO6-mYuoxhrK_B2q1aXjbYKCgadN_n1YeCwE,21216
|
|
48
|
+
desdeo/api/routers/problem.py,sha256=OvhNkKwSpGY3oyhTetiBaFreHx87dtOXZPSqSb96Bqw,10606
|
|
49
|
+
desdeo/api/routers/reference_point_method.py,sha256=pugEPSERTlGzwP66aGgYRFHnakP9bxUc7usD17VoTfw,2771
|
|
50
|
+
desdeo/api/routers/session.py,sha256=H6G1PtK5KIVpEvhpz0-RdIma5TN-S7Bedv_zu7E7WHs,3164
|
|
51
51
|
desdeo/api/routers/test.py,sha256=gvULBP3DvCaMwnnNQDaXSCxrav0IR5awQrsAoEn3dh0,434
|
|
52
|
-
desdeo/api/routers/user_authentication.py,sha256=
|
|
53
|
-
desdeo/api/routers/utils.py,sha256=
|
|
54
|
-
desdeo/api/routers/utopia.py,sha256=
|
|
52
|
+
desdeo/api/routers/user_authentication.py,sha256=LGcL-0SfT_BZveqIA4_u59LxUF7Ol7lQE5CSvHHPLLg,17730
|
|
53
|
+
desdeo/api/routers/utils.py,sha256=O3fi4Yj0fYM92uUmUUgA8tqG5omF9SvxGSiBTO3gWbI,7745
|
|
54
|
+
desdeo/api/routers/utopia.py,sha256=MWxoKNd5ah6X6W46BAQJNJt8tXFnrjy_vR52IQDJ6gs,8352
|
|
55
55
|
desdeo/api/schema.py,sha256=nIxVGlsUt5cvpk3FdeppfwBWVdB7OJDvb_jzjVwMm-I,2623
|
|
56
56
|
desdeo/api/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
57
|
desdeo/api/tests/conftest.py,sha256=gcjhWYcvBW1VpyAWCf-VyDW_VXbhYdeC18-lvsc5Y1U,4887
|
|
58
58
|
desdeo/api/tests/test_enautilus.py,sha256=Sc0ycVe_ckPmQI_SxiJUaIOQEtHohwBpb28btB9NeMA,15267
|
|
59
59
|
desdeo/api/tests/test_models.py,sha256=yNvemg43TmcAVHglB-CyhApCbOT1-DFYkQdH7q2efx8,37393
|
|
60
|
-
desdeo/api/tests/test_routes.py,sha256=
|
|
60
|
+
desdeo/api/tests/test_routes.py,sha256=ytYbO3yVzWe4NclQRLh1mcCelQJNdEBo0PjdnBInxRo,40953
|
|
61
61
|
desdeo/api/utils/_database.py,sha256=qJAdjCWmZpT8x7Sdnet9rWakXaFmBzQW0yNTZfSzsJE,7966
|
|
62
62
|
desdeo/api/utils/_logger.py,sha256=Zo163Zi9ZJc8hJEeMDzI7pGSeiJtRf79wRSidtK0ZXI,668
|
|
63
63
|
desdeo/api/utils/database.py,sha256=iQPHsa5HvJ0hSAi4980-tRV6PTbz9S54JIFeCIzHwaM,1162
|
|
64
64
|
desdeo/api/utils/emo_database.py,sha256=XKmS-O3bnK9q6OQ6klxVkhWbKnY0fku9dJe5X-RRLQ8,1316
|
|
65
65
|
desdeo/core.py,sha256=MruvDrZQhBarLi7x1UBPyaQ7WA_nZ5Gb6BPfSfCxBjI,1258
|
|
66
|
-
desdeo/emo/__init__.py,sha256=
|
|
66
|
+
desdeo/emo/__init__.py,sha256=BDCx_9gmglohRZdoQWFXrvWG_Pvpc-98y433404MV7k,5318
|
|
67
67
|
desdeo/emo/hooks/archivers.py,sha256=PZxAsuISikrnlESU0NlFdeZiOblvbNWxivrfmAaT1nI,7055
|
|
68
68
|
desdeo/emo/methods/EAs.py,sha256=2Wd-_QzCSWMX7coxFxo1gEVmL0PfmpuWT2XLlPQgj4g,23003
|
|
69
69
|
desdeo/emo/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
70
70
|
desdeo/emo/methods/bases.py,sha256=d506vIEwOhRtEXZ1Ali7Y0WB2_ASfHfSGm_Tv4KPDGg,341
|
|
71
71
|
desdeo/emo/methods/templates.py,sha256=KjUcnVs_-PGde_9GH6f9cpWdw3-3JNRoQenvdYBkWg4,5068
|
|
72
|
-
desdeo/emo/operators/__init__.py,sha256=
|
|
72
|
+
desdeo/emo/operators/__init__.py,sha256=EZ6wdIr2Wbs3e_oKgwYKIYqxGnGVXjojXFAI8tP3W-A,41
|
|
73
73
|
desdeo/emo/operators/crossover.py,sha256=6kXxtQxscpLHistgJzVMtg8mrI27YL9jcEZMD0vob68,49488
|
|
74
74
|
desdeo/emo/operators/evaluator.py,sha256=0WZbw48k9fY8AgIA7F7SgIJXThyNj4qKVwk3uv8-mGk,4074
|
|
75
|
-
desdeo/emo/operators/generator.py,sha256=
|
|
75
|
+
desdeo/emo/operators/generator.py,sha256=kfN0VX6VcEA5nA5YHwm2svynXUw8lv0CJkCqX6vXW4A,25415
|
|
76
76
|
desdeo/emo/operators/mutation.py,sha256=SSn2h9CKm2B34A0tNTTuJnCRCVLVyYKiDCRmSi8cqEs,47817
|
|
77
77
|
desdeo/emo/operators/scalar_selection.py,sha256=-li-AsjTtSDv6S-Q6epjKJ9ocsotgTZV3f_q_c-Z-wA,8774
|
|
78
78
|
desdeo/emo/operators/selection.py,sha256=sE1PJqkBh_Z6cdg4zNbDvXRLHxHhqBcvB0s4P4GmRG0,77468
|
|
79
79
|
desdeo/emo/operators/termination.py,sha256=zaAAmQZB-X6L5x8YRWLg7Yt11XxshQfHaBirlXCOKPw,11263
|
|
80
|
-
desdeo/emo/options/__init__.py,sha256=
|
|
80
|
+
desdeo/emo/options/__init__.py,sha256=IyDE-6a8W7Mi7Rj-aKdAkagZwbsSFZxxH2rh9g5_IiY,3282
|
|
81
81
|
desdeo/emo/options/algorithms.py,sha256=k88OHAxbsXKUjY1GcX8N6MbLxuQLlmFB2ttAELVqI5w,16756
|
|
82
82
|
desdeo/emo/options/crossover.py,sha256=ahvuuRPQ0XStI3IUrZX0qKEH13CFsdxA7g8Nk8atwOY,6365
|
|
83
|
-
desdeo/emo/options/generator.py,sha256=
|
|
83
|
+
desdeo/emo/options/generator.py,sha256=dg1JIvr801S_D_ZBjoXGkjps43P0ILsmcGWI_3cHkuA,5252
|
|
84
84
|
desdeo/emo/options/mutation.py,sha256=htBOSULDJ8TlUv5KoXwu1wb-9eTP-g_-d1ahR84bZlY,8854
|
|
85
85
|
desdeo/emo/options/repair.py,sha256=azC-_IpQQWD8SUEFpe6NKSwp8a8DZkBv0YcQw-TZBoA,2178
|
|
86
86
|
desdeo/emo/options/scalar_selection.py,sha256=9r9oEgTrf7-jfVyf16iuNAfwdaSV3S4N-ix5d2o6Snk,2462
|
|
@@ -174,7 +174,7 @@ desdeo/utopia_stuff/utopia_db_init.py,sha256=BlvJNxZE5Pgtr_aEUNBuCjPONi19FT4-73k
|
|
|
174
174
|
desdeo/utopia_stuff/utopia_problem.py,sha256=ZIoYDqvlspfcK0IFBZ3XgBB5qCB0BSSYRU5KmOwWZEM,15190
|
|
175
175
|
desdeo/utopia_stuff/utopia_problem_old.py,sha256=G-Qtrmg7Uip2M0QEEXKbIHlCBcM4WfWDvM1Rut5LzMU,16201
|
|
176
176
|
desdeo/utopia_stuff/utopia_reference_solutions.py,sha256=uU1FTOATg9Mx19ENidoSJF0hAzWvud4zCzR54qW_GO4,2536
|
|
177
|
-
desdeo-2.
|
|
178
|
-
desdeo-2.
|
|
179
|
-
desdeo-2.
|
|
180
|
-
desdeo-2.
|
|
177
|
+
desdeo-2.2.0.dist-info/METADATA,sha256=D2rhb3RWPTFobyc-rdqWff4K9ARDSCwxauaHBCt9j60,9261
|
|
178
|
+
desdeo-2.2.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
|
|
179
|
+
desdeo-2.2.0.dist-info/licenses/LICENSE,sha256=OWc9n9JaYCMc9dTTdOUv042049jOjm1cmQyJ0nkRbJk,1129
|
|
180
|
+
desdeo-2.2.0.dist-info/RECORD,,
|
|
File without changes
|