From 30ed2bb9edf92f85772d49642e1aec29aff3e9a3 Mon Sep 17 00:00:00 2001 From: Helldragon67 Date: Mon, 8 Jul 2024 02:01:42 +0200 Subject: [PATCH] permafixed the optimizer ??? --- backend/src/amenities/nature.am | 2 +- backend/src/optimizer_v4.py | 136 ++++++++++++++++++-------------- backend/src/tester.py | 4 +- 3 files changed, 80 insertions(+), 62 deletions(-) diff --git a/backend/src/amenities/nature.am b/backend/src/amenities/nature.am index bcad9d1..5102c02 100644 --- a/backend/src/amenities/nature.am +++ b/backend/src/amenities/nature.am @@ -8,5 +8,5 @@ geological 'tourism'='alpine_hut' 'tourism'='viewpoint' 'tourism'='zoo' -#'tourism'='artwork' +'tourism'='artwork' 'waterway'='waterfall' \ No newline at end of file diff --git a/backend/src/optimizer_v4.py b/backend/src/optimizer_v4.py index 0a5d68c..15b31fd 100644 --- a/backend/src/optimizer_v4.py +++ b/backend/src/optimizer_v4.py @@ -31,7 +31,7 @@ def print_res(L: List[Landmark], L_tot): # Prevent the use of a particular solution -def prevent_config(resx, A_ub, b_ub): +def prevent_config(resx): for i, elem in enumerate(resx): resx[i] = round(elem) @@ -52,13 +52,10 @@ def prevent_config(resx, A_ub, b_ub): if i in vertices_visited : h[i*L:i*L+L] = ones - A_ub = np.vstack((A_ub, h)) - b_ub.append(len(vertices_visited)-1) - - return A_ub, b_ub + return h, [len(vertices_visited)-1] -def prevent_circle(circle_vertices: list, L: int, A_eq: list, b_eq: list) : +def prevent_circle(circle_vertices: list, L: int) : l1 = [0]*L*L l2 = [0]*L*L @@ -74,12 +71,7 @@ def prevent_circle(circle_vertices: list, L: int, A_eq: list, b_eq: list) : l1[g*L + s] = 1 l2[s*L + g] = 1 - A_eq = np.vstack((A_eq, l1)) - b_eq.append(0) - A_eq = np.vstack((A_eq, l2)) - b_eq.append(0) - - return A_eq, b_eq + return np.vstack((l1, l2)), [0, 0] # Prevent the possibility of a given solution bit def break_circle(circle_vertices: list, L: int, A_ub: list, b_ub: list): @@ -340,14 +332,16 @@ def init_ub_dist(landmarks: List[Landmark], max_steps: int): # Constraint to respect only one travel per landmark. Also caps the total number of visited landmarks -def respect_number(L: int, A_ub, b_ub, max_landmarks): +def respect_number(L: int, max_landmarks: int): ones = [1]*L zeros = [0]*L - for i in range(L) : - h = zeros*i + ones + zeros*(L-1-i) - A_ub = np.vstack((A_ub, h)) - b_ub.append(1) + A = ones + zeros*(L-1) + b = [1] + for i in range(L-1) : + h_new = zeros*i + ones + zeros*(L-1-i) + A = np.vstack((A, h_new)) + b.append(1) if max_landmarks is None : # Read the parameters from the file @@ -355,29 +349,35 @@ def respect_number(L: int, A_ub, b_ub, max_landmarks): parameters = json.loads(f.read()) max_landmarks = parameters['max landmarks'] - A_ub = np.vstack((A_ub, ones*L)) - b_ub.append(max_landmarks+1) + A = np.vstack((A, ones*L)) + b.append(max_landmarks+1) - return A_ub, b_ub + return A, b -# Constraint to not have d14 and d41 simultaneously. Does not prevent circular symmetry with more elements -def break_sym(L, A_ub, b_ub): +# Constraint to not have d14 and d41 simultaneously. Does not prevent cyclic paths with more elements +def break_sym(L): + upper_ind = np.triu_indices(L,0,L) up_ind_x = upper_ind[0] up_ind_y = upper_ind[1] - for i, _ in enumerate(up_ind_x) : + A = [0]*L*L # useless row to prevent overhead ? better solution welcomed + # A[up_ind_x[0]*L + up_ind_y[0]] = 1 + # A[up_ind_y[0]*L + up_ind_x[0]] = 1 + b = [1] + + for i, _ in enumerate(up_ind_x[1:]) : l = [0]*L*L if up_ind_x[i] != up_ind_y[i] : l[up_ind_x[i]*L + up_ind_y[i]] = 1 l[up_ind_y[i]*L + up_ind_x[i]] = 1 - A_ub = np.vstack((A_ub,l)) - b_ub.append(1) + A = np.vstack((A,l)) + b.append(1) - return A_ub, b_ub + return A, b # Constraint to not stay in position. Removes d11, d22, d33, etc. @@ -395,22 +395,24 @@ def init_eq_not_stay(L: int): # Go through the landmarks and force the optimizer to use landmarks where attractiveness is set to -1 -def respect_user_mustsee(landmarks: List[Landmark], A_eq: list, b_eq: list) : +def respect_user_mustsee(landmarks: List[Landmark]) : L = len(landmarks) - for i, elem in enumerate(landmarks) : + A = [0]*L*L + b = [0] + for i, elem in enumerate(landmarks[1:]) : if elem.must_do is True and elem.name not in ['finish', 'start']: l = [0]*L*L l[i*L:i*L+L] = [1]*L # set mandatory departures from landmarks tagged as 'must_do' - A_eq = np.vstack((A_eq,l)) - b_eq.append(1) + A = np.vstack((A,l)) + b.append(1) - return A_eq, b_eq + return A, b # Constraint to ensure start at start and finish at goal -def respect_start_finish(L: int, A_eq: list, b_eq: list): +def respect_start_finish(L: int): l_start = [1]*L + [0]*L*(L-1) # sets departures only for start (horizontal ones) l_start[L-1] = 0 # prevents the jump from start to finish l_goal = [0]*L*L # sets arrivals only for finish (vertical ones) @@ -421,32 +423,33 @@ def respect_start_finish(L: int, A_eq: list, b_eq: list): l_goal[k*L+L-1] = 1 - A_eq = np.vstack((A_eq,l_start)) - A_eq = np.vstack((A_eq,l_goal)) - A_eq = np.vstack((A_eq,l_L)) - b_eq.append(1) - b_eq.append(1) - b_eq.append(0) + A = np.vstack((l_start, l_goal)) + b = [1, 1] + A = np.vstack((A,l_L)) + b.append(0) - return A_eq, b_eq + return A, b # Constraint to tie the problem together. Necessary but not sufficient to avoid circles -def respect_order(N: int, A_eq, b_eq): - for i in range(N-1) : # Prevent stacked ones - if i == 0 or i == N-1: # Don't touch start or finish +def respect_order(L: int): + + A = [0]*L*L # useless row to reduce overhead ? better solution is welcome + b = [0] + for i in range(L-1) : # Prevent stacked ones + if i == 0 or i == L-1: # Don't touch start or finish continue else : - l = [0]*N + l = [0]*L l[i] = -1 - l = l*N - for j in range(N) : - l[i*N + j] = 1 + l = l*L + for j in range(L) : + l[i*L + j] = 1 - A_eq = np.vstack((A_eq,l)) - b_eq.append(0) + A = np.vstack((A,l)) + b.append(0) - return A_eq, b_eq + return A, b # Computes the time to reach from each landmark to the next @@ -514,16 +517,27 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta L = len(landmarks) # SET CONSTRAINTS FOR INEQUALITY - c, A_ub, b_ub = init_ub_dist(landmarks, max_steps) # Add the distances from each landmark to the other - A_ub, b_ub = respect_number(L, A_ub, b_ub, max_landmarks) # Respect max number of visits (no more possible stops than landmarks). - A_ub, b_ub = break_sym(L, A_ub, b_ub) # break the 'zig-zag' symmetry + c, A_ub, b_ub = init_ub_dist(landmarks, max_steps) # Add the distances from each landmark to the other + A, b = respect_number(L, max_landmarks) # Respect max number of visits (no more possible stops than landmarks). + A_ub = np.vstack((A_ub, A)) + b_ub += b + A, b = break_sym(L) # break the 'zig-zag' symmetry + A_ub = np.vstack((A_ub, A)) + b_ub += b + # SET CONSTRAINTS FOR EQUALITY - A_eq, b_eq = init_eq_not_stay(L) # Force solution not to stay in same place - A_eq, b_eq = respect_user_mustsee(landmarks, A_eq, b_eq) # Check if there are user_defined must_see. Also takes care of start/goal - A_eq, b_eq = respect_start_finish(L, A_eq, b_eq) # Force start and finish positions - A_eq, b_eq = respect_order(L, A_eq, b_eq) # Respect order of visit (only works when max_steps is limiting factor) - + A_eq, b_eq = init_eq_not_stay(L) # Force solution not to stay in same place + A, b = respect_user_mustsee(landmarks) # Check if there are user_defined must_see. Also takes care of start/goal + A_eq = np.vstack((A_eq, A)) + b_eq += b + A, b = respect_start_finish(L) # Force start and finish positions + A_eq = np.vstack((A_eq, A)) + b_eq += b + A, b = respect_order(L) # Respect order of visit (only works when max_steps is limiting factor) + A_eq = np.vstack((A_eq, A)) + b_eq += b + # SET BOUNDS FOR DECISION VARIABLE (x can only be 0 or 1) x_bounds = [(0, 1)]*L*L @@ -541,10 +555,14 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta i = 0 timeout = 80 while circles is not None and i < timeout: - A_ub, b_ub = prevent_config(res.x, A_ub, b_ub) + A, b = prevent_config(res.x) + A_ub = np.vstack((A_ub, A)) + b_ub += b #A_ub, b_ub = prevent_circle(order, len(landmarks), A_ub, b_ub) for circle in circles : - A_ub, b_ub = prevent_circle(circle, len(landmarks), A_ub, b_ub) + A, b = prevent_circle(circle, len(landmarks)) + A_eq = np.vstack((A_eq, A)) + b_eq += b res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq = b_eq, bounds=x_bounds, method='highs', integrality=3) order, circles = is_connected2(res.x) #nodes, edges = is_connected2(res.x) diff --git a/backend/src/tester.py b/backend/src/tester.py index eb7d36a..0ad97c7 100644 --- a/backend/src/tester.py +++ b/backend/src/tester.py @@ -99,8 +99,8 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]: landmarks_short.insert(0, start) landmarks_short.append(finish) - max_walking_time = 120 # minutes - detour = 30 # minutes + max_walking_time = 50 # minutes + detour = 0 # minutes # First stage optimization base_tour = solve_optimization(landmarks_short, max_walking_time, True)