From 814da4b5f6d311e3b875dff57b02d455c844c988 Mon Sep 17 00:00:00 2001 From: Helldragon67 Date: Wed, 15 Jan 2025 17:11:29 +0100 Subject: [PATCH] working pulp --- backend/src/tests/test_main.py | 1 + backend/src/utils/optimizer.py | 47 +++++++++++++++++----------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/backend/src/tests/test_main.py b/backend/src/tests/test_main.py index 5d4e4a6..c95b6f8 100644 --- a/backend/src/tests/test_main.py +++ b/backend/src/tests/test_main.py @@ -81,6 +81,7 @@ def test_bellecour(client, request) : # pylint: disable=redefined-outer-name } ) result = response.json() + print(result) landmarks = load_trip_landmarks(client, result['first_landmark_uuid']) # Get computation time diff --git a/backend/src/utils/optimizer.py b/backend/src/utils/optimizer.py index c77b30b..7218971 100644 --- a/backend/src/utils/optimizer.py +++ b/backend/src/utils/optimizer.py @@ -2,6 +2,7 @@ import yaml, logging import numpy as np import pulp as pl from scipy.optimize import linprog +from scipy.sparse import lil_matrix, csr_matrix from collections import defaultdict, deque from ..structs.landmark import Landmark @@ -69,7 +70,7 @@ class Optimizer: for j in range(i+1, L) : if i !=j : t = get_time(spot1.location, landmarks[j].location) - A_ub[0, i*L + j] = t + spot1.duration + A_ub[0, i*L + j] = t + spot1.duration A_ub[0, j*L + i] = t + landmarks[j].duration # Expand 'c' to L*L for every decision variable @@ -255,7 +256,7 @@ class Optimizer: incr += 1 - # Prevent the use of a particular solution + # Prevent the use of a particular solution. TODO probably can be done faster just using resx def prevent_config(self, resx): """ Prevent the use of a particular solution by adding constraints to the optimization. @@ -330,9 +331,7 @@ class Optimizer: tuple[list[int], Optional[list[list[int]]]]: A tuple containing the visit order and a list of any detected circles. """ resx = np.round(resx).astype(np.int8) # round all elements and cast them to int - - print(f"resx = ") - # for r in resx : print(r) + N = len(resx) # length of res L = int(np.sqrt(N)) # number of landmarks. CAST INTO INT but should not be a problem because N = L**2 by def. @@ -509,14 +508,21 @@ class Optimizer: # SET CONSTRAINTS FOR INEQUALITY c, A_ub, b_ub = self.init_ub_time(landmarks, max_time) # Adds the distances from each landmark to the other. + print("ok1") self.respect_number(A_ub, b_ub, L, max_landmarks) # Respects max number of visits (no more possible stops than landmarks). + print("ok2") self.break_sym(A_ub, b_ub, L) # Breaks the 'zig-zag' symmetry. Avoids d12 and d21 but not larger cirlces. # SET CONSTRAINTS FOR EQUALITY + print("ok3") A_eq, b_eq = self.init_eq_not_stay(landmarks) # Force solution not to stay in same place + print("ok4") self.respect_start_finish(A_eq, b_eq, L) # Force start and finish positions + print("ok5") self.respect_order(A_eq, b_eq, L) # Respect order of visit (only works when max_time is limiting factor) + print("ok6") self.respect_user_must(A_eq, b_eq, landmarks) # Force to do/avoid landmarks set by user. + print("ok7") self.logger.debug(f"Optimizing with {A_ub.shape[0]} + {A_eq.shape[0]} = {A_ub.shape[0] + A_eq.shape[0]} constraints.") @@ -539,7 +545,7 @@ class Optimizer: prob += (pl.lpSum([A_eq[i][j] * x[j] for j in range(L*L)]) == b_eq[i]) # 6. Solve the problem - prob.solve(pl.PULP_CBC_CMD(msg=True)) + prob.solve(pl.PULP_CBC_CMD(msg=False)) # 7. Extract Results status = pl.LpStatus[prob.status] @@ -559,32 +565,25 @@ class Optimizer: i = 0 timeout = 80 - while circles is not None and i < timeout: + while circles is not None : i += 1 # print(f"Iteration {i} of fixing circles") - print("ok1") - A, b = self.prevent_config(solution) - print("ok2") - print(f"A: {A}") - print(f"b: {b}") - try : - prob += pl.lpSum([A[j] * x[j // L][j % L] for j in range(L * L)]) == b - except Exception as exc : - self.logger.error(f'Unexpected error occured', details=exc) - raise Exception from exc - - print("ok3") + # l, b = self.prevent_config(solution) + # prob += (pl.lpSum([l[j] * x[j] for j in range(L*L)]) == b) + for circle in circles : A, b = self.prevent_circle(circle, L) - prob += (pl.lpSum([A[0][j] * x[j // L][j % L] for j in range(L*L)]) == b[0]) - prob += (pl.lpSum([A[1][j] * x[j // L][j % L] for j in range(L*L)]) == b[1]) - print("ok4") - prob.solve(pl.PULP_CBC_CMD(msg=True)) + prob += (pl.lpSum([A[0][j] * x[j] for j in range(L*L)]) == b[0]) + prob += (pl.lpSum([A[1][j] * x[j] for j in range(L*L)]) == b[1]) + prob.solve(pl.PULP_CBC_CMD(msg=False)) status = pl.LpStatus[prob.status] solution = [pl.value(var) for var in x] # The values of the decision variables (will be 0 or 1) if status != 'Optimal' : + self.logger.error("The problem is overconstrained, no solution after {i} cycles.") + raise ArithmeticError("No solution could be found. Please try again with more time or different preferences.") + if i == timeout : self.logger.error(f'Unexpected error after {timeout} iterations of fixing circles.') raise ArithmeticError("Solving failed because of overconstrained problem") @@ -600,5 +599,5 @@ class Optimizer: order = self.get_order(solution) tour = [landmarks[i] for i in order] - self.logger.debug(f"Re-optimized {i} times, score: {int(pl.value(prob.objective))}") + self.logger.debug(f"Re-optimized {i} times, objective value : {int(pl.value(prob.objective))}") return tour