working pulp

This commit is contained in:
Helldragon67 2025-01-15 17:11:29 +01:00
parent 3fe6056f3c
commit 814da4b5f6
2 changed files with 24 additions and 24 deletions

View File

@ -81,6 +81,7 @@ def test_bellecour(client, request) : # pylint: disable=redefined-outer-name
} }
) )
result = response.json() result = response.json()
print(result)
landmarks = load_trip_landmarks(client, result['first_landmark_uuid']) landmarks = load_trip_landmarks(client, result['first_landmark_uuid'])
# Get computation time # Get computation time

View File

@ -2,6 +2,7 @@ import yaml, logging
import numpy as np import numpy as np
import pulp as pl import pulp as pl
from scipy.optimize import linprog from scipy.optimize import linprog
from scipy.sparse import lil_matrix, csr_matrix
from collections import defaultdict, deque from collections import defaultdict, deque
from ..structs.landmark import Landmark from ..structs.landmark import Landmark
@ -255,7 +256,7 @@ class Optimizer:
incr += 1 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): def prevent_config(self, resx):
""" """
Prevent the use of a particular solution by adding constraints to the optimization. Prevent the use of a particular solution by adding constraints to the optimization.
@ -331,8 +332,6 @@ class Optimizer:
""" """
resx = np.round(resx).astype(np.int8) # round all elements and cast them to int 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 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. 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 # 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. 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). 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. 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 # SET CONSTRAINTS FOR EQUALITY
print("ok3")
A_eq, b_eq = self.init_eq_not_stay(landmarks) # Force solution not to stay in same place 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 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) 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. 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.") 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]) prob += (pl.lpSum([A_eq[i][j] * x[j] for j in range(L*L)]) == b_eq[i])
# 6. Solve the problem # 6. Solve the problem
prob.solve(pl.PULP_CBC_CMD(msg=True)) prob.solve(pl.PULP_CBC_CMD(msg=False))
# 7. Extract Results # 7. Extract Results
status = pl.LpStatus[prob.status] status = pl.LpStatus[prob.status]
@ -559,32 +565,25 @@ class Optimizer:
i = 0 i = 0
timeout = 80 timeout = 80
while circles is not None and i < timeout: while circles is not None :
i += 1 i += 1
# print(f"Iteration {i} of fixing circles") # print(f"Iteration {i} of fixing circles")
print("ok1") # l, b = self.prevent_config(solution)
A, b = self.prevent_config(solution) # prob += (pl.lpSum([l[j] * x[j] for j in range(L*L)]) == b)
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")
for circle in circles : for circle in circles :
A, b = self.prevent_circle(circle, L) 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[0][j] * x[j] 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]) prob += (pl.lpSum([A[1][j] * x[j] for j in range(L*L)]) == b[1])
print("ok4") prob.solve(pl.PULP_CBC_CMD(msg=False))
prob.solve(pl.PULP_CBC_CMD(msg=True))
status = pl.LpStatus[prob.status] status = pl.LpStatus[prob.status]
solution = [pl.value(var) for var in x] # The values of the decision variables (will be 0 or 1) solution = [pl.value(var) for var in x] # The values of the decision variables (will be 0 or 1)
if status != 'Optimal' : 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.') self.logger.error(f'Unexpected error after {timeout} iterations of fixing circles.')
raise ArithmeticError("Solving failed because of overconstrained problem") raise ArithmeticError("Solving failed because of overconstrained problem")
@ -600,5 +599,5 @@ class Optimizer:
order = self.get_order(solution) order = self.get_order(solution)
tour = [landmarks[i] for i in order] 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 return tour