desdeo 2.1.0__tar.gz → 2.2.0__tar.gz
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-2.1.0 → desdeo-2.2.0}/PKG-INFO +21 -12
- {desdeo-2.1.0 → desdeo-2.2.0}/README.md +20 -11
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/nimbus.py +8 -4
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/emo.py +75 -104
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/generic.py +26 -58
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/nimbus.py +108 -247
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/problem.py +69 -56
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/reference_point_method.py +29 -27
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/session.py +15 -11
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/user_authentication.py +27 -5
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/utils.py +42 -37
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/utopia.py +11 -12
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/tests/test_routes.py +6 -5
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/__init__.py +2 -0
- desdeo-2.2.0/desdeo/emo/operators/__init__.py +1 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/operators/generator.py +153 -2
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/__init__.py +4 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/generator.py +24 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/__init__.py +12 -11
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/evaluator.py +4 -5
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/gurobipy_evaluator.py +37 -12
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/infix_parser.py +1 -16
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/json_parser.py +7 -11
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/schema.py +6 -9
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/utils.py +1 -1
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/pyomo_solver_interfaces.py +1 -1
- {desdeo-2.1.0 → desdeo-2.2.0}/pyproject.toml +28 -48
- desdeo-2.1.0/desdeo/emo/operators/__init__.py +0 -1
- {desdeo-2.1.0 → desdeo-2.2.0}/LICENSE +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/adm/ADMAfsar.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/adm/ADMChen.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/adm/BaseADM.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/adm/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/README.md +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/app.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/config.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/config.toml +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/db.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/db_init.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/db_models.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/malaga_db_init.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/archive.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/emo.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/enautilus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/gdm/gdm_aggregate.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/gdm/gdm_base.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/gdm/gdm_score_bands.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/gdm/gnimbus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/generic.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/generic_states.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/preference.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/reference_point_method.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/session.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/state.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/user.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/models/utopia.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/_EMO.backup +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/_NAUTILUS.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/_NAUTILUS_navigator.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/_NIMBUS.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/enautilus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/gdm/gdm_aggregate.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/gdm/gdm_base.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/routers/test.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/schema.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/tests/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/tests/conftest.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/tests/test_enautilus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/tests/test_models.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/utils/_database.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/utils/_logger.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/utils/database.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/api/utils/emo_database.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/core.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/hooks/archivers.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/methods/EAs.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/methods/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/methods/bases.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/methods/templates.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/operators/crossover.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/operators/evaluator.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/operators/mutation.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/operators/scalar_selection.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/operators/selection.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/operators/termination.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/algorithms.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/crossover.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/mutation.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/repair.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/scalar_selection.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/selection.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/templates.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/emo/options/termination.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/explanations/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/explanations/explainer.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/explanations/utils.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/gdm/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/gdm/gdmtools.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/gdm/score_bands.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/gdm/voting_rules.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/enautilus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/gnimbus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/nautili.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/nautilus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/nautilus_navigator.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/nimbus.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/pareto_navigator.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/mcdm/reference_point_method.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/external/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/external/core.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/external/pymoo_provider.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/external/runtime.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/pyomo_evaluator.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/simulator_evaluator.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/sympy_evaluator.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/benchmarks_server.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/binh_and_korn_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/cake_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/dtlz2_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/forest_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/knapsack_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/mcwb_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/momip_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/multi_valued_constraints.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/nimbus_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/pareto_navigator_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/re_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/river_pollution_problems.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/rocket_injector_design_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/simple_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/simulator_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/single_objective.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/spanish_sustainability_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/problem/testproblems/zdt_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/GenerateReferencePoints.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/desc_gen.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/generics.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/group_scalarization.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/gurobipy_solver_interfaces.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/indicators_binary.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/indicators_unary.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/interaction_schema.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/intersection.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/iterative_pareto_representer.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/message.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/ng_solver_interfaces.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/non_dominated_sorting.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/patterns.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/proximal_solver.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/reference_vectors.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/scalarization.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/scipy_solver_interfaces.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/score_bands.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/utils.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/tools/visualizations.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/__init__.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/data/1.json +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/data/2.json +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/data/3.json +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/data/4.json +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/data/5.json +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/from_json.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/reinit_user.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/utopia_db_init.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/utopia_problem.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/utopia_problem_old.py +0 -0
- {desdeo-2.1.0 → desdeo-2.2.0}/desdeo/utopia_stuff/utopia_reference_solutions.py +0 -0
|
@@ -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,10 +40,14 @@ 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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+

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

|
|
47
|
+
[](https://discord.gg/uGCEgQTJyY)
|
|
46
48
|
|
|
49
|
+
|
|
50
|
+
# DESDEO: the open-source software framework for interactive multiobjective optimization
|
|
47
51
|
## Introduction
|
|
48
52
|
|
|
49
53
|
DESDEO is an open-source framework for interactive multiobjective optimization
|
|
@@ -74,13 +78,17 @@ decision-support using the framework. __The
|
|
|
74
78
|
web-API is currently under heavy development, and is subject to changes.__
|
|
75
79
|
3. The __web-GUI__ (WIP), which implements a web-based interface for utilizing
|
|
76
80
|
the interactive methods and tools for modeling and solving multiobjective
|
|
77
|
-
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.__
|
|
78
84
|
|
|
79
85
|
For developing and experimenting with interactive multiobjective optimization
|
|
80
86
|
methods on a "grass root" level, the __core-logic__ provides the necessary
|
|
81
|
-
tools. For deploying interactive methods, the __web-API__ and the
|
|
87
|
+
tools. For deploying interactive methods, the __web-API__ and the __web-GUI__
|
|
82
88
|
play a central role.
|
|
83
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
|
+
|
|
84
92
|
DESDEO is an open-source project and everybody is welcome to contribute!
|
|
85
93
|
|
|
86
94
|
## Core-logic: key features
|
|
@@ -112,11 +120,12 @@ issue](https://github.com/industrial-optimization-group/DESDEO/issues/245).
|
|
|
112
120
|
|
|
113
121
|
## Web-GUI: key features
|
|
114
122
|
|
|
115
|
-
DESDEO's web-GUI is currently
|
|
116
|
-
|
|
117
|
-
|
|
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).
|
|
118
127
|
|
|
119
|
-
## Installation instructions
|
|
128
|
+
## Installation instructions (core-logic)
|
|
120
129
|
|
|
121
130
|
DESDEO is available on PyPI to be installed via pip:
|
|
122
131
|
|
|
@@ -175,12 +184,12 @@ this repository's master branch is considered to be _DESDEO 2.0_.
|
|
|
175
184
|
|
|
176
185
|
## Funding
|
|
177
186
|
|
|
178
|
-
Currently, DESDEO's development
|
|
187
|
+
Currently, DESDEO's development has been funded by projects granted by the
|
|
179
188
|
[Research Council of Finland](https://www.aka.fi/en/). The most recent ones
|
|
180
189
|
include:
|
|
181
190
|
|
|
182
191
|
- DESIDES (project 355346)
|
|
183
192
|
- UTOPIA (project 352784)
|
|
184
193
|
- DAEMON (project 322221)
|
|
185
|
-
|
|
194
|
+
- DESDEO (project 287496)
|
|
186
195
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+

|
|
2
|
+
[](https://pypi.org/project/desdeo/)
|
|
3
|
+
[](https://desdeo.readthedocs.io/en/latest/)
|
|
4
|
+

|
|
5
|
+
[](https://discord.gg/uGCEgQTJyY)
|
|
4
6
|
|
|
7
|
+
|
|
8
|
+
# DESDEO: the open-source software framework for interactive multiobjective optimization
|
|
5
9
|
## Introduction
|
|
6
10
|
|
|
7
11
|
DESDEO is an open-source framework for interactive multiobjective optimization
|
|
@@ -32,13 +36,17 @@ decision-support using the framework. __The
|
|
|
32
36
|
web-API is currently under heavy development, and is subject to changes.__
|
|
33
37
|
3. The __web-GUI__ (WIP), which implements a web-based interface for utilizing
|
|
34
38
|
the interactive methods and tools for modeling and solving multiobjective
|
|
35
|
-
optimization problems.
|
|
39
|
+
optimization problems.
|
|
40
|
+
|
|
41
|
+
> __The web-GUI relies heavily on the web-API, and is also being actively developed currently, and therefore subject to sudden changes.__
|
|
36
42
|
|
|
37
43
|
For developing and experimenting with interactive multiobjective optimization
|
|
38
44
|
methods on a "grass root" level, the __core-logic__ provides the necessary
|
|
39
|
-
tools. For deploying interactive methods, the __web-API__ and the
|
|
45
|
+
tools. For deploying interactive methods, the __web-API__ and the __web-GUI__
|
|
40
46
|
play a central role.
|
|
41
47
|
|
|
48
|
+
> 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)!.
|
|
49
|
+
|
|
42
50
|
DESDEO is an open-source project and everybody is welcome to contribute!
|
|
43
51
|
|
|
44
52
|
## Core-logic: key features
|
|
@@ -70,11 +78,12 @@ issue](https://github.com/industrial-optimization-group/DESDEO/issues/245).
|
|
|
70
78
|
|
|
71
79
|
## Web-GUI: key features
|
|
72
80
|
|
|
73
|
-
DESDEO's web-GUI is currently
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
DESDEO's web-GUI is currently under active development. Once it stabilized, its
|
|
82
|
+
key features will be listed here. In the meantime, the interested user can
|
|
83
|
+
follow (and contribute!) the development progress of the web-API in [this
|
|
84
|
+
issue](https://github.com/industrial-optimization-group/DESDEO/issues/251).
|
|
76
85
|
|
|
77
|
-
## Installation instructions
|
|
86
|
+
## Installation instructions (core-logic)
|
|
78
87
|
|
|
79
88
|
DESDEO is available on PyPI to be installed via pip:
|
|
80
89
|
|
|
@@ -133,11 +142,11 @@ this repository's master branch is considered to be _DESDEO 2.0_.
|
|
|
133
142
|
|
|
134
143
|
## Funding
|
|
135
144
|
|
|
136
|
-
Currently, DESDEO's development
|
|
145
|
+
Currently, DESDEO's development has been funded by projects granted by the
|
|
137
146
|
[Research Council of Finland](https://www.aka.fi/en/). The most recent ones
|
|
138
147
|
include:
|
|
139
148
|
|
|
140
149
|
- DESIDES (project 355346)
|
|
141
150
|
- UTOPIA (project 352784)
|
|
142
151
|
- DAEMON (project 322221)
|
|
143
|
-
|
|
152
|
+
- DESDEO (project 287496)
|
|
@@ -36,11 +36,13 @@ class NIMBUSSaveRequest(SQLModel):
|
|
|
36
36
|
|
|
37
37
|
solution_info: list[SolutionInfo]
|
|
38
38
|
|
|
39
|
+
|
|
39
40
|
class NIMBUSDeleteSaveRequest(SQLModel):
|
|
40
41
|
"""Request model for deletion of a saved solution."""
|
|
41
42
|
|
|
42
|
-
state_id
|
|
43
|
+
state_id: int = Field(description="The ID of the save state.")
|
|
43
44
|
solution_index: int = Field(description="The ID of the solution within the above state.")
|
|
45
|
+
problem_id: int = Field(description="The ID of the problem.")
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
class NIMBUSFinalizeRequest(SQLModel):
|
|
@@ -50,7 +52,7 @@ class NIMBUSFinalizeRequest(SQLModel):
|
|
|
50
52
|
session_id: int | None = Field(default=None)
|
|
51
53
|
parent_state_id: int | None = Field(default=None)
|
|
52
54
|
|
|
53
|
-
solution_info: SolutionInfo
|
|
55
|
+
solution_info: SolutionInfo # the final solution
|
|
54
56
|
|
|
55
57
|
|
|
56
58
|
class NIMBUSClassificationResponse(SQLModel):
|
|
@@ -98,12 +100,14 @@ class NIMBUSSaveResponse(SQLModel):
|
|
|
98
100
|
|
|
99
101
|
state_id: int | None = Field(description="The id of the newest state")
|
|
100
102
|
|
|
103
|
+
|
|
101
104
|
class NIMBUSDeleteSaveResponse(SQLModel):
|
|
102
105
|
"""Response of NIMBUS save deletion."""
|
|
103
106
|
|
|
104
107
|
response_type: str = "nimbus.delete_save"
|
|
105
108
|
|
|
106
|
-
message: str | None
|
|
109
|
+
message: str | None = None
|
|
110
|
+
|
|
107
111
|
|
|
108
112
|
class NIMBUSFinalizeResponse(SQLModel):
|
|
109
113
|
"""The response from NIMBUS finish endpoint."""
|
|
@@ -144,7 +148,7 @@ class NIMBUSIntermediateSolutionResponse(SQLModel):
|
|
|
144
148
|
reference_solution_1: dict[str, float] = Field(
|
|
145
149
|
sa_column=Column(JSON), description="The first solution used when computing intermediate points."
|
|
146
150
|
)
|
|
147
|
-
reference_solution_2: dict[str, float]= Field(
|
|
151
|
+
reference_solution_2: dict[str, float] = Field(
|
|
148
152
|
sa_column=Column(JSON), description="The second solution used when computing intermediate points."
|
|
149
153
|
)
|
|
150
154
|
current_solutions: list[SolutionReferenceResponse] = Field(
|
|
@@ -15,30 +15,24 @@ import polars as pl
|
|
|
15
15
|
from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisconnect, status
|
|
16
16
|
from fastapi.encoders import jsonable_encoder
|
|
17
17
|
from fastapi.responses import StreamingResponse
|
|
18
|
-
from sqlmodel import
|
|
18
|
+
from sqlmodel import select
|
|
19
19
|
from websockets.asyncio.client import connect
|
|
20
20
|
|
|
21
21
|
from desdeo.api.db import get_session
|
|
22
|
-
from desdeo.api.models import
|
|
22
|
+
from desdeo.api.models import StateDB
|
|
23
23
|
from desdeo.api.models.emo import (
|
|
24
24
|
EMOFetchRequest,
|
|
25
|
-
EMOFetchResponse,
|
|
26
25
|
EMOIterateRequest,
|
|
27
26
|
EMOIterateResponse,
|
|
28
|
-
EMOSaveRequest,
|
|
29
27
|
EMOScoreRequest,
|
|
30
28
|
EMOScoreResponse,
|
|
31
|
-
Solution,
|
|
32
29
|
)
|
|
33
|
-
from desdeo.api.models.
|
|
34
|
-
from desdeo.api.models.state import EMOFetchState, EMOIterateState, EMOSaveState, EMOSCOREState
|
|
35
|
-
from desdeo.api.models.user import User
|
|
36
|
-
from desdeo.api.routers.user_authentication import get_current_user
|
|
30
|
+
from desdeo.api.models.state import EMOIterateState, EMOSCOREState
|
|
37
31
|
from desdeo.emo.options.templates import EMOOptions, PreferenceOptions, TemplateOptions, emo_constructor
|
|
38
32
|
from desdeo.problem import Problem
|
|
39
|
-
from desdeo.tools.score_bands import SCOREBandsConfig,
|
|
33
|
+
from desdeo.tools.score_bands import SCOREBandsConfig, score_json
|
|
40
34
|
|
|
41
|
-
from .utils import
|
|
35
|
+
from .utils import SessionContext, get_session_context
|
|
42
36
|
|
|
43
37
|
router = APIRouter(prefix="/method/emo", tags=["EMO"])
|
|
44
38
|
|
|
@@ -113,7 +107,6 @@ async def websocket_endpoint(
|
|
|
113
107
|
try:
|
|
114
108
|
while True:
|
|
115
109
|
data = await websocket.receive_json()
|
|
116
|
-
print(data)
|
|
117
110
|
if "send_to" in data:
|
|
118
111
|
try:
|
|
119
112
|
await ws_manager.send_private_message(data, data["send_to"])
|
|
@@ -153,71 +146,55 @@ def get_templates() -> list[TemplateOptions]:
|
|
|
153
146
|
@router.post("/iterate")
|
|
154
147
|
def iterate(
|
|
155
148
|
request: EMOIterateRequest,
|
|
156
|
-
|
|
157
|
-
session: Annotated[Session, Depends(get_session)],
|
|
149
|
+
context: Annotated[SessionContext, Depends(get_session_context)],
|
|
158
150
|
) -> EMOIterateResponse:
|
|
159
|
-
"""
|
|
151
|
+
"""Fetches results from a completed EMO method.
|
|
160
152
|
|
|
161
|
-
Args:
|
|
162
|
-
|
|
163
|
-
user (Annotated[User, Depends]): The current user.
|
|
164
|
-
session (Annotated[Session, Depends]): The database session.
|
|
153
|
+
Args: request (EMOIterateRequest): The request object containing parameters for fetching results.
|
|
154
|
+
context (Annotated[SessionContext, Depends]): The session context.
|
|
165
155
|
|
|
166
|
-
Raises:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Returns:
|
|
170
|
-
IterateResponse: A response object containing a list of IDs to be used for websocket communication.
|
|
171
|
-
Also contains the StateDB id where the results will be stored.
|
|
156
|
+
Raises: HTTPException: If the request is invalid or the EMO method fails.
|
|
157
|
+
Returns: IterateResponse: A response object containing a list of IDs to be used for websocket communication.
|
|
158
|
+
Also contains the StateDB id where the results will be stored.
|
|
172
159
|
"""
|
|
173
|
-
|
|
160
|
+
# Get context objects
|
|
161
|
+
db_session = context.db_session
|
|
162
|
+
interactive_session = context.interactive_session
|
|
163
|
+
parent_state = context.parent_state
|
|
174
164
|
|
|
175
|
-
|
|
165
|
+
# Ensure problem exists
|
|
166
|
+
if context.problem_db is None:
|
|
167
|
+
raise HTTPException(status_code=404, detail="Problem not found")
|
|
168
|
+
|
|
169
|
+
problem_db = context.problem_db
|
|
176
170
|
problem = Problem.from_problemdb(problem_db)
|
|
177
171
|
|
|
178
|
-
|
|
172
|
+
# Templates
|
|
173
|
+
templates = request.template_options or get_templates()
|
|
179
174
|
|
|
180
|
-
|
|
181
|
-
|
|
175
|
+
web_socket_ids = [
|
|
176
|
+
f"{template.algorithm_name.lower()}_{datetime.now().strftime('%Y%m%d%H%M%S%f')}" for template in templates
|
|
177
|
+
]
|
|
182
178
|
|
|
183
|
-
web_socket_ids = []
|
|
184
|
-
for template in templates:
|
|
185
|
-
# Ensure unique names
|
|
186
|
-
web_socket_ids.append(f"{template.algorithm_name.lower()}_{datetime.now().strftime('%Y%m%d%H%M%S%f')}")
|
|
187
179
|
client_id = f"client_{datetime.now().strftime('%Y%m%d%H%M%S%f')}"
|
|
188
|
-
client_id = "client"
|
|
189
|
-
|
|
190
|
-
# Save request (incomplete and EAs have not finished running yet)
|
|
191
|
-
|
|
192
|
-
# Handle parent state
|
|
193
|
-
if request.parent_state_id is None:
|
|
194
|
-
parent_state = None
|
|
195
|
-
else:
|
|
196
|
-
statement = select(StateDB).where(StateDB.id == request.parent_state_id)
|
|
197
|
-
parent_state = session.exec(statement).first()
|
|
198
|
-
|
|
199
|
-
if parent_state is None:
|
|
200
|
-
raise HTTPException(
|
|
201
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
202
|
-
detail=f"Could not find state with id={request.parent_state_id}",
|
|
203
|
-
)
|
|
204
180
|
|
|
181
|
+
# 4) Create incomplete state
|
|
205
182
|
emo_iterate_state = EMOIterateState(
|
|
206
183
|
template_options=jsonable_encoder(templates),
|
|
207
184
|
preference_options=jsonable_encoder(request.preference_options),
|
|
208
185
|
)
|
|
209
186
|
|
|
210
187
|
incomplete_db_state = StateDB.create(
|
|
211
|
-
database_session=
|
|
188
|
+
database_session=db_session,
|
|
212
189
|
problem_id=problem_db.id,
|
|
213
190
|
session_id=interactive_session.id if interactive_session else None,
|
|
214
191
|
parent_id=parent_state.id if parent_state else None,
|
|
215
192
|
state=emo_iterate_state,
|
|
216
193
|
)
|
|
217
194
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
195
|
+
db_session.add(incomplete_db_state)
|
|
196
|
+
db_session.commit()
|
|
197
|
+
db_session.refresh(incomplete_db_state)
|
|
221
198
|
|
|
222
199
|
state_id = incomplete_db_state.id
|
|
223
200
|
if state_id is None:
|
|
@@ -225,10 +202,8 @@ def iterate(
|
|
|
225
202
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
226
203
|
detail="Failed to create a new state in the database.",
|
|
227
204
|
)
|
|
228
|
-
# Close db session
|
|
229
|
-
session.close()
|
|
230
205
|
|
|
231
|
-
#
|
|
206
|
+
# Start process
|
|
232
207
|
Process(
|
|
233
208
|
target=_spawn_emo_process,
|
|
234
209
|
args=(
|
|
@@ -319,7 +294,7 @@ def _spawn_emo_process(
|
|
|
319
294
|
session.close()
|
|
320
295
|
|
|
321
296
|
|
|
322
|
-
def _ea_sync(
|
|
297
|
+
def _ea_sync(
|
|
323
298
|
problem: Problem,
|
|
324
299
|
template: TemplateOptions,
|
|
325
300
|
preference_options: PreferenceOptions | None,
|
|
@@ -352,7 +327,7 @@ def _ea_sync( # noqa: PLR0913
|
|
|
352
327
|
)
|
|
353
328
|
|
|
354
329
|
|
|
355
|
-
async def _ea_async(
|
|
330
|
+
async def _ea_async(
|
|
356
331
|
problem: Problem,
|
|
357
332
|
websocket_id: str,
|
|
358
333
|
client_id: str,
|
|
@@ -388,33 +363,29 @@ async def _ea_async( # noqa: PLR0913
|
|
|
388
363
|
@router.post("/fetch")
|
|
389
364
|
async def fetch_results(
|
|
390
365
|
request: EMOFetchRequest,
|
|
391
|
-
|
|
392
|
-
session: Annotated[Session, Depends(get_session)],
|
|
366
|
+
context: Annotated[SessionContext, Depends(get_session_context)],
|
|
393
367
|
) -> StreamingResponse:
|
|
394
368
|
"""Fetches results from a completed EMO method.
|
|
395
369
|
|
|
396
370
|
Args:
|
|
397
371
|
request (EMOFetchRequest): The request object containing parameters for fetching results.
|
|
398
|
-
|
|
399
|
-
session (Annotated[Session, Depends]): The database session.
|
|
372
|
+
context (Annotated[SessionContext, Depends]): The session context.
|
|
400
373
|
|
|
401
|
-
Raises:
|
|
402
|
-
HTTPException: If the request is invalid or the EMO method has not completed.
|
|
374
|
+
Raises: HTTPException: If the request is invalid or the EMO method has not completed.
|
|
403
375
|
|
|
404
|
-
Returns:
|
|
405
|
-
StreamingResponse: A streaming response containing the results of the EMO method.
|
|
376
|
+
Returns: StreamingResponse: A streaming response containing the results of the EMO method.
|
|
406
377
|
"""
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
378
|
+
# Use context instead of manual fetch
|
|
379
|
+
state = context.parent_state
|
|
380
|
+
|
|
410
381
|
if state is None:
|
|
411
382
|
raise HTTPException(status_code=404, detail="Parent state not found.")
|
|
412
383
|
|
|
413
384
|
if not isinstance(state.state, EMOIterateState):
|
|
414
|
-
raise TypeError(f"State with id={
|
|
385
|
+
raise TypeError(f"State with id={request.parent_state_id} is not of type EMOIterateState.")
|
|
415
386
|
|
|
416
387
|
if not (state.state.objective_values and state.state.decision_variables):
|
|
417
|
-
raise ValueError(
|
|
388
|
+
raise ValueError("State does not contain results yet.")
|
|
418
389
|
|
|
419
390
|
# Convert objs: dict[str, list[float]] to objs: list[dict[str, float]]
|
|
420
391
|
raw_objs: dict[str, list[float]] = state.state.objective_values
|
|
@@ -422,14 +393,15 @@ async def fetch_results(
|
|
|
422
393
|
objs: list[dict[str, float]] = [{k: v[i] for k, v in raw_objs.items()} for i in range(n_solutions)]
|
|
423
394
|
|
|
424
395
|
raw_decs: dict[str, list[float]] = state.state.decision_variables
|
|
425
|
-
|
|
426
396
|
decs: list[dict[str, float]] = [{k: v[i] for k, v in raw_decs.items()} for i in range(n_solutions)]
|
|
427
397
|
|
|
428
|
-
response: list[Solution] = []
|
|
429
|
-
|
|
430
398
|
def result_stream():
|
|
431
399
|
for i in range(n_solutions):
|
|
432
|
-
item = {
|
|
400
|
+
item = {
|
|
401
|
+
"solution_id": i,
|
|
402
|
+
"objective_values": objs[i],
|
|
403
|
+
"decision_variables": decs[i],
|
|
404
|
+
}
|
|
433
405
|
yield json.dumps(item) + "\n"
|
|
434
406
|
|
|
435
407
|
return StreamingResponse(result_stream())
|
|
@@ -438,16 +410,13 @@ async def fetch_results(
|
|
|
438
410
|
@router.post("/fetch_score")
|
|
439
411
|
async def fetch_score_bands(
|
|
440
412
|
request: EMOScoreRequest,
|
|
441
|
-
|
|
442
|
-
session: Annotated[Session, Depends(get_session)],
|
|
413
|
+
context: Annotated[SessionContext, Depends(get_session_context)],
|
|
443
414
|
) -> EMOScoreResponse:
|
|
444
415
|
"""Fetches results from a completed EMO method.
|
|
445
416
|
|
|
446
|
-
Args:
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
user (Annotated[User, Depends]): The current user.
|
|
450
|
-
session (Annotated[Session, Depends]): The database session.
|
|
417
|
+
Args: request (EMOFetchRequest): The request object containing parameters for fetching
|
|
418
|
+
results and of the SCORE bands visualization.
|
|
419
|
+
context (Annotated[SessionContext, Depends]): The session context.
|
|
451
420
|
|
|
452
421
|
Raises:
|
|
453
422
|
HTTPException: If the request is invalid or the EMO method has not completed.
|
|
@@ -455,24 +424,23 @@ async def fetch_score_bands(
|
|
|
455
424
|
Returns:
|
|
456
425
|
SCOREBandsResult: The results of the SCORE bands visualization.
|
|
457
426
|
"""
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
state = session.exec(statement).first()
|
|
465
|
-
if state is None:
|
|
427
|
+
# Use context instead of manual fetch
|
|
428
|
+
parent_state = context.parent_state
|
|
429
|
+
db_session = context.db_session
|
|
430
|
+
problem_db = context.problem_db
|
|
431
|
+
|
|
432
|
+
if parent_state is None:
|
|
466
433
|
raise HTTPException(status_code=404, detail="Parent state not found.")
|
|
467
434
|
|
|
468
|
-
if not isinstance(
|
|
469
|
-
raise TypeError(f"State with id={
|
|
435
|
+
if not isinstance(parent_state.state, EMOIterateState):
|
|
436
|
+
raise TypeError(f"State with id={request.parent_state_id} is not of type EMOIterateState.")
|
|
470
437
|
|
|
471
|
-
if not (
|
|
472
|
-
raise ValueError(
|
|
438
|
+
if not (parent_state.state.objective_values and parent_state.state.decision_variables):
|
|
439
|
+
raise ValueError("State does not contain results yet.")
|
|
473
440
|
|
|
474
|
-
|
|
475
|
-
|
|
441
|
+
score_config = SCOREBandsConfig() if request.config is None else request.config
|
|
442
|
+
|
|
443
|
+
raw_objs: dict[str, list[float]] = parent_state.state.objective_values
|
|
476
444
|
objs = pl.DataFrame(raw_objs)
|
|
477
445
|
|
|
478
446
|
results = score_json(
|
|
@@ -482,16 +450,19 @@ async def fetch_score_bands(
|
|
|
482
450
|
|
|
483
451
|
score_state = EMOSCOREState(result=results.model_dump())
|
|
484
452
|
|
|
453
|
+
# Use the session + problem from context instead of request directly
|
|
485
454
|
score_db_state = StateDB.create(
|
|
486
|
-
database_session=
|
|
487
|
-
problem_id=
|
|
488
|
-
session_id=
|
|
489
|
-
parent_id=parent_state,
|
|
455
|
+
database_session=db_session,
|
|
456
|
+
problem_id=problem_db.id,
|
|
457
|
+
session_id=parent_state.session_id,
|
|
458
|
+
parent_id=parent_state.id,
|
|
490
459
|
state=score_state,
|
|
491
460
|
)
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
461
|
+
|
|
462
|
+
db_session.add(score_db_state)
|
|
463
|
+
db_session.commit()
|
|
464
|
+
db_session.refresh(score_db_state)
|
|
465
|
+
|
|
495
466
|
state_id = score_db_state.id
|
|
496
467
|
|
|
497
468
|
return EMOScoreResponse(result=results, state_id=state_id)
|