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,398 @@
|
|
|
1
|
+
"""GDM Score Bands manager implementation."""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
import polars as pl
|
|
6
|
+
from sqlmodel import Session, select
|
|
7
|
+
|
|
8
|
+
from desdeo.api.models import (
|
|
9
|
+
GDMSCOREBandFinalSelection,
|
|
10
|
+
GDMSCOREBandInformation,
|
|
11
|
+
Group,
|
|
12
|
+
GroupIteration,
|
|
13
|
+
ProblemDB,
|
|
14
|
+
User,
|
|
15
|
+
)
|
|
16
|
+
from desdeo.api.routers.gdm.gdm_base import GroupManager, ManagerError
|
|
17
|
+
from desdeo.gdm.score_bands import SCOREBandsGDMConfig, SCOREBandsGDMResult, score_bands_gdm
|
|
18
|
+
from desdeo.gdm.voting_rules import consensus_rule, majority_rule
|
|
19
|
+
from desdeo.tools.score_bands import score_json
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GDMScoreBandsManager(GroupManager):
|
|
23
|
+
"""The group manager implementation for GDM Score Bands."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, group_id: int, db_session: Session):
|
|
26
|
+
"""Initialize the group manager.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
group_id (int): id of the group of this manager.
|
|
30
|
+
db_session (Session): database session from which the discrete representation of a problem is fetched.
|
|
31
|
+
"""
|
|
32
|
+
super().__init__(group_id, db_session)
|
|
33
|
+
# LOAD THE DISCRETE REPRESENTATION
|
|
34
|
+
group: Group = db_session.exec(select(Group).where(Group.id == group_id)).first()
|
|
35
|
+
problem: ProblemDB = db_session.exec(select(ProblemDB).where(ProblemDB.id == group.problem_id)).first()
|
|
36
|
+
if problem.discrete_representation is None:
|
|
37
|
+
raise ManagerError("The group's discrete representation does not exist!")
|
|
38
|
+
self.discrete_representation = problem.discrete_representation
|
|
39
|
+
db_session.close()
|
|
40
|
+
|
|
41
|
+
async def run_method(self, user_id: int, data: str):
|
|
42
|
+
"""Method runner implementation for GDM Score Bands.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
user_id (int): The user's ID this data is from.
|
|
46
|
+
data (str): The data itself. To be validated.
|
|
47
|
+
WE MIGHT NOT EVEN NEED THIS!!
|
|
48
|
+
"""
|
|
49
|
+
async with self.lock:
|
|
50
|
+
await self.send_message(
|
|
51
|
+
"THIS METHOD IS USED THROUGH THE APPROPRIATE HTTP ENDPOINTS!", self.sockets[user_id]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
async def vote(self, user: User, group: Group, voted_index: int, session: Session):
|
|
55
|
+
"""A method for voting on a specific band.
|
|
56
|
+
|
|
57
|
+
Use this from an actual HTTP endpoint and not in a
|
|
58
|
+
stupid websocket way like in GNIMBUS. Every time this is
|
|
59
|
+
operated, send all connected websockets info on the voting.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
user (User): The user that sent the vote
|
|
63
|
+
group (Group): The group the user belongs to and generally the one we're dealing with
|
|
64
|
+
voted_index (int): the vote
|
|
65
|
+
session: (Session) the database session.
|
|
66
|
+
"""
|
|
67
|
+
async with self.lock: # not sure if async lock is all that necessary but here we go anyways.
|
|
68
|
+
group_iteration = session.exec(
|
|
69
|
+
select(GroupIteration).where(GroupIteration.id == group.head_iteration_id)
|
|
70
|
+
).first()
|
|
71
|
+
if group_iteration is None:
|
|
72
|
+
raise ManagerError("No such Group Iteration! Did you initialize this group?")
|
|
73
|
+
info_container = copy.deepcopy(group_iteration.info_container)
|
|
74
|
+
# if user.id in info_container.user_confirms:
|
|
75
|
+
# raise ManagerError("User has already confirmed they want to move on!")
|
|
76
|
+
info_container.user_votes[str(user.id)] = voted_index
|
|
77
|
+
group_iteration.info_container = info_container
|
|
78
|
+
session.add(group_iteration)
|
|
79
|
+
session.commit()
|
|
80
|
+
session.refresh(group_iteration)
|
|
81
|
+
|
|
82
|
+
# Then update preferences dictionary.
|
|
83
|
+
await self.broadcast("UPDATE: A vote has been cast.")
|
|
84
|
+
|
|
85
|
+
async def confirm(self, user: User, group: Group, session: Session):
|
|
86
|
+
"""A method for a user to indicate that we could move forward with clustering anew.
|
|
87
|
+
|
|
88
|
+
After everyone has hit this endpoint, do the following shenanigans:
|
|
89
|
+
1. Filter the active solution indices using the voting result.
|
|
90
|
+
2. Create a new GroupIteration.
|
|
91
|
+
3. Re-cluster the active solutions and put the result into the
|
|
92
|
+
info_container item of the newly created GroupIteration.
|
|
93
|
+
4. Send all connected websockets info that we've got some hot
|
|
94
|
+
new data to update the UI with. Then use some other endpoint to
|
|
95
|
+
fetch that data.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
user (User): The user
|
|
99
|
+
group (Group): The group
|
|
100
|
+
session (Session): The database session.
|
|
101
|
+
"""
|
|
102
|
+
async with self.lock:
|
|
103
|
+
if user.id not in group.user_ids:
|
|
104
|
+
raise ManagerError(
|
|
105
|
+
detail=f"User with ID {user.id} is not part of group with ID {group.id}",
|
|
106
|
+
)
|
|
107
|
+
group_iteration = session.exec(
|
|
108
|
+
select(GroupIteration).where(GroupIteration.id == group.head_iteration_id)
|
|
109
|
+
).first()
|
|
110
|
+
if group_iteration is None:
|
|
111
|
+
raise ManagerError("No group iterations! Did you initialize this group?")
|
|
112
|
+
|
|
113
|
+
info_container = copy.deepcopy(group_iteration.info_container)
|
|
114
|
+
if str(user.id) not in info_container.user_votes:
|
|
115
|
+
raise ManagerError("User hasn't voted! Cannot confirm!")
|
|
116
|
+
# if user.id in info_container.user_confirms:
|
|
117
|
+
raise ManagerError("User has already confirmed they want to move on!")
|
|
118
|
+
if user.id not in info_container.user_confirms:
|
|
119
|
+
info_container.user_confirms.append(user.id)
|
|
120
|
+
group_iteration.info_container = info_container
|
|
121
|
+
session.add(group_iteration)
|
|
122
|
+
session.commit()
|
|
123
|
+
session.refresh(group_iteration)
|
|
124
|
+
|
|
125
|
+
# After everyone's done, filter etc.
|
|
126
|
+
for uid in group.user_ids:
|
|
127
|
+
# Check if user is not in the list of confirms
|
|
128
|
+
if uid not in info_container.user_confirms:
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# We're in consensus reaching phase
|
|
132
|
+
if info_container.method == "gdm-score-bands":
|
|
133
|
+
# Seems like every user wants to move on.
|
|
134
|
+
statement = select(GroupIteration).where(GroupIteration.group_id == group.id)
|
|
135
|
+
iterations: list[GroupIteration] = session.exec(statement).all()
|
|
136
|
+
state: list[SCOREBandsGDMResult] = [
|
|
137
|
+
iteration.info_container.score_bands_result
|
|
138
|
+
for iteration in iterations
|
|
139
|
+
if iteration.info_container.method == "gdm-score-bands"
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
# USE Bhupinder's score bands stuff.
|
|
143
|
+
score_bands_config = SCOREBandsGDMConfig(
|
|
144
|
+
score_bands_config=info_container.score_bands_config.score_bands_config,
|
|
145
|
+
from_iteration=state[-1].iteration, # the ID from the latest iteration.
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Get them discrete reprs
|
|
149
|
+
discrete_repr = self.discrete_representation
|
|
150
|
+
|
|
151
|
+
# Make sure that there are enough solutions for re-clustering
|
|
152
|
+
votes = group_iteration.info_container.user_votes
|
|
153
|
+
winners = consensus_rule(votes, score_bands_config.minimum_votes)
|
|
154
|
+
relevant_ids = state[-1].relevant_ids
|
|
155
|
+
clustering = state[-1].score_bands_result.clusters
|
|
156
|
+
|
|
157
|
+
# threshold to decide whether to move to the decision phase
|
|
158
|
+
solution_number_threshold = 10
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
len([x[0] for x in zip(relevant_ids, clustering, strict=True) if x[1] in winners])
|
|
162
|
+
<= solution_number_threshold
|
|
163
|
+
):
|
|
164
|
+
# There are less than 10 solutions, so move to the decision phase.
|
|
165
|
+
|
|
166
|
+
obj_keys = list(discrete_repr.objective_values)
|
|
167
|
+
var_keys = list(discrete_repr.variable_values)
|
|
168
|
+
|
|
169
|
+
objs = pl.DataFrame(discrete_repr.objective_values).with_row_index(name="index_")
|
|
170
|
+
varis = pl.DataFrame(discrete_repr.variable_values).with_row_index(name="index_")
|
|
171
|
+
indices = pl.DataFrame({"index_": relevant_ids, "cluster_": clustering}).filter(
|
|
172
|
+
pl.col("cluster_").is_in(winners)
|
|
173
|
+
)
|
|
174
|
+
objs = indices.join(other=objs, how="left", left_on="index_", right_on="index_").select(obj_keys)
|
|
175
|
+
varis = indices.join(other=varis, how="left", left_on="index_", right_on="index_").select(var_keys)
|
|
176
|
+
|
|
177
|
+
info_container = GDMSCOREBandFinalSelection(
|
|
178
|
+
user_votes={},
|
|
179
|
+
user_confirms=[],
|
|
180
|
+
solution_variables=varis.to_dict(),
|
|
181
|
+
solution_objectives=objs.to_dict(),
|
|
182
|
+
winner_solution_variables={},
|
|
183
|
+
winner_solution_objectives={},
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
else:
|
|
187
|
+
# Case in which we have more than 10 solutions
|
|
188
|
+
discrete_repr = discrete_repr.objective_values
|
|
189
|
+
objective_keys = list(discrete_repr)
|
|
190
|
+
objs = pl.DataFrame(discrete_repr).with_row_index()
|
|
191
|
+
objs = objs.select(objective_keys)
|
|
192
|
+
|
|
193
|
+
result: list[SCOREBandsGDMResult] = score_bands_gdm(
|
|
194
|
+
data=objs, config=score_bands_config, state=state, votes=votes
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# store necessary data to the database. Currently all
|
|
198
|
+
# "voting" related is null bc no voting has happened
|
|
199
|
+
info_container = GDMSCOREBandInformation(
|
|
200
|
+
user_votes={},
|
|
201
|
+
user_confirms=[],
|
|
202
|
+
score_bands_config=score_bands_config,
|
|
203
|
+
score_bands_result=result[-1],
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Add group iteration and related stuff, then set new iteration to head.
|
|
207
|
+
new_iteration: GroupIteration = GroupIteration(
|
|
208
|
+
group_id=group.id,
|
|
209
|
+
problem_id=group.problem_id,
|
|
210
|
+
info_container=info_container,
|
|
211
|
+
notified={},
|
|
212
|
+
state_id=None,
|
|
213
|
+
parent_id=group.head_iteration_id,
|
|
214
|
+
parent=group_iteration,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
session.add(new_iteration)
|
|
218
|
+
session.commit()
|
|
219
|
+
session.refresh(new_iteration)
|
|
220
|
+
|
|
221
|
+
group.head_iteration_id = new_iteration.id
|
|
222
|
+
session.add(group)
|
|
223
|
+
session.commit()
|
|
224
|
+
session.refresh(group)
|
|
225
|
+
|
|
226
|
+
await self.broadcast("UPDATE: A new iteration has begun.")
|
|
227
|
+
|
|
228
|
+
# We're in decision phase.
|
|
229
|
+
elif info_container.method == "gdm-score-bands-final":
|
|
230
|
+
winner = majority_rule(info_container.user_votes) # A different rule perhaps?
|
|
231
|
+
# if len(winners) > 1:
|
|
232
|
+
# pass # TODO: minimize distance or whatever
|
|
233
|
+
|
|
234
|
+
varis = info_container.solution_variables
|
|
235
|
+
vari_keys = list(varis)
|
|
236
|
+
objs = info_container.solution_objectives
|
|
237
|
+
obj_keys = list(objs)
|
|
238
|
+
|
|
239
|
+
vari_d = {}
|
|
240
|
+
for key in vari_keys:
|
|
241
|
+
vari_d[key] = varis[key][winner]
|
|
242
|
+
|
|
243
|
+
obj_d = {}
|
|
244
|
+
for key in obj_keys:
|
|
245
|
+
obj_d[key] = objs[key][winner]
|
|
246
|
+
|
|
247
|
+
info_container.winner_solution_variables = vari_d
|
|
248
|
+
info_container.winner_solution_objectives = obj_d
|
|
249
|
+
|
|
250
|
+
group_iteration.info_container = info_container
|
|
251
|
+
|
|
252
|
+
session.add(group_iteration)
|
|
253
|
+
session.commit()
|
|
254
|
+
|
|
255
|
+
async def revert(self, user: User, group: Group, session: Session, group_iteration_number: int):
|
|
256
|
+
"""Revert to a different iteration.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
user (User): Current user
|
|
260
|
+
group (Group): Current group
|
|
261
|
+
session (Session): database session
|
|
262
|
+
group_iteration_number (int): the group iteration to which we want to revert.
|
|
263
|
+
|
|
264
|
+
Raises:
|
|
265
|
+
ManagerError
|
|
266
|
+
"""
|
|
267
|
+
async with self.lock:
|
|
268
|
+
group_iteration = session.exec(
|
|
269
|
+
select(GroupIteration).where(GroupIteration.id == group.head_iteration_id)
|
|
270
|
+
).first()
|
|
271
|
+
if group_iteration is None:
|
|
272
|
+
raise ManagerError("No group iterations! Did you initialize this group?")
|
|
273
|
+
|
|
274
|
+
statement = select(GroupIteration).where(GroupIteration.group_id == group.id)
|
|
275
|
+
iterations: list[GroupIteration] = session.exec(statement).all()
|
|
276
|
+
|
|
277
|
+
target_group_iteration: GroupIteration = next([i for i in iterations if i.id == group_iteration_number])
|
|
278
|
+
if target_group_iteration.info_container.method == "gdm-score-bands-final":
|
|
279
|
+
raise ManagerError("We can only revert to a score bands iteration.")
|
|
280
|
+
|
|
281
|
+
state: list[SCOREBandsGDMResult] = [
|
|
282
|
+
iteration.info_container.score_bands_result
|
|
283
|
+
for iteration in iterations
|
|
284
|
+
if iteration.info_container.method == "gdm-score-bands"
|
|
285
|
+
]
|
|
286
|
+
|
|
287
|
+
# The parent ID will be the latest state's iteration
|
|
288
|
+
prev_id = state[-1].iteration
|
|
289
|
+
# The results remain the same as in the target
|
|
290
|
+
result = target_group_iteration.info_container.score_bands_result
|
|
291
|
+
result.previous_iteration = prev_id
|
|
292
|
+
result.iteration = prev_id + 1
|
|
293
|
+
|
|
294
|
+
# Essentially create a new iteration.
|
|
295
|
+
info_container = GDMSCOREBandInformation(
|
|
296
|
+
user_votes={},
|
|
297
|
+
user_confirms=[],
|
|
298
|
+
score_bands_config=target_group_iteration.info_container.score_bands_config,
|
|
299
|
+
score_bands_result=result,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Add group iteration and related stuff, then set new iteration to head.
|
|
303
|
+
new_iteration: GroupIteration = GroupIteration(
|
|
304
|
+
group_id=group.id,
|
|
305
|
+
problem_id=group.problem_id,
|
|
306
|
+
info_container=info_container,
|
|
307
|
+
notified={},
|
|
308
|
+
state_id=None,
|
|
309
|
+
parent_id=group.head_iteration_id,
|
|
310
|
+
parent=group_iteration,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
session.add(new_iteration)
|
|
314
|
+
session.commit()
|
|
315
|
+
session.refresh(new_iteration)
|
|
316
|
+
|
|
317
|
+
group.head_iteration_id = new_iteration.id
|
|
318
|
+
session.add(group)
|
|
319
|
+
session.commit()
|
|
320
|
+
session.refresh(group)
|
|
321
|
+
|
|
322
|
+
await self.broadcast("UPDATE: Iteration reverted.")
|
|
323
|
+
|
|
324
|
+
async def configure(self, group: Group, config: SCOREBandsGDMConfig, session: Session):
|
|
325
|
+
"""Configure the SCORE Bands process.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
user (User): Actually might not be necessary...
|
|
329
|
+
group (Group): The group whom the configuration concerns.
|
|
330
|
+
config (SCOREBandsGDMConfig): The configuration.
|
|
331
|
+
session (Session): The database session.
|
|
332
|
+
"""
|
|
333
|
+
async with self.lock:
|
|
334
|
+
group_iteration = session.exec(
|
|
335
|
+
select(GroupIteration).where(GroupIteration.id == group.head_iteration_id)
|
|
336
|
+
).first()
|
|
337
|
+
if group_iteration is None:
|
|
338
|
+
raise ManagerError("No group iterations! Did you initialize this group?")
|
|
339
|
+
|
|
340
|
+
if group_iteration.info_container.method == "gdm-score-bands-final":
|
|
341
|
+
raise ManagerError("Cannot reconfigure in a non SCORE Bands phase!")
|
|
342
|
+
|
|
343
|
+
statement = select(GroupIteration).where(GroupIteration.group_id == group.id)
|
|
344
|
+
iterations: list[GroupIteration] = session.exec(statement).all()
|
|
345
|
+
state: list[SCOREBandsGDMResult] = [
|
|
346
|
+
iteration.info_container.score_bands_result
|
|
347
|
+
for iteration in iterations
|
|
348
|
+
if iteration.info_container.method == "gdm-score-bands"
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
relevant_indices = state[-1].relevant_ids
|
|
352
|
+
iteration_number = state[-1].iteration
|
|
353
|
+
|
|
354
|
+
index_df = pl.DataFrame({"index": relevant_indices})
|
|
355
|
+
|
|
356
|
+
discrete_repr = self.discrete_representation.objective_values
|
|
357
|
+
objective_keys = list(discrete_repr)
|
|
358
|
+
objs_df = pl.DataFrame(discrete_repr).with_row_index()
|
|
359
|
+
|
|
360
|
+
# Filter
|
|
361
|
+
objs_df = index_df.join(how="left", left_on="index", right_on="index", other=objs_df)
|
|
362
|
+
|
|
363
|
+
objs_df = objs_df.select(objective_keys)
|
|
364
|
+
|
|
365
|
+
score_bands_result = score_json(data=objs_df, options=config.score_bands_config)
|
|
366
|
+
score_bands_gdm_result = SCOREBandsGDMResult(
|
|
367
|
+
score_bands_result=score_bands_result,
|
|
368
|
+
relevant_ids=relevant_indices,
|
|
369
|
+
iteration=iteration_number + 1,
|
|
370
|
+
previous_iteration=iteration_number,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Essentially create a new iteration.
|
|
374
|
+
info_container = GDMSCOREBandInformation(
|
|
375
|
+
user_votes={}, user_confirms=[], score_bands_config=config, score_bands_result=score_bands_gdm_result
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Add group iteration and related stuff, then set new iteration to head.
|
|
379
|
+
new_iteration: GroupIteration = GroupIteration(
|
|
380
|
+
group_id=group.id,
|
|
381
|
+
problem_id=group.problem_id,
|
|
382
|
+
info_container=info_container,
|
|
383
|
+
notified={},
|
|
384
|
+
state_id=None,
|
|
385
|
+
parent_id=group.head_iteration_id,
|
|
386
|
+
parent=group_iteration,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
session.add(new_iteration)
|
|
390
|
+
session.commit()
|
|
391
|
+
session.refresh(new_iteration)
|
|
392
|
+
|
|
393
|
+
group.head_iteration_id = new_iteration.id
|
|
394
|
+
session.add(group)
|
|
395
|
+
session.commit()
|
|
396
|
+
session.refresh(group)
|
|
397
|
+
|
|
398
|
+
await self.broadcast("UPDATE: Reconfigured SCORE Bands.")
|