diff --git a/backend/src/optimizer_v4.py b/backend/src/optimizer_v4.py index 15b31fd..d8edea6 100644 --- a/backend/src/optimizer_v4.py +++ b/backend/src/optimizer_v4.py @@ -55,6 +55,7 @@ def prevent_config(resx): return h, [len(vertices_visited)-1] +# Prevents the creation of the same circle (both directions) def prevent_circle(circle_vertices: list, L: int) : l1 = [0]*L*L @@ -73,138 +74,9 @@ def prevent_circle(circle_vertices: list, L: int) : 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): - if L-1 in circle_vertices : - circle_vertices.remove(L-1) - - h = [0]*L*L - for i in range(L) : - if i in circle_vertices : - h[i*L:i*L+L] = [1]*L - - A_ub = np.vstack((A_ub, h)) - b_ub.append(len(circle_vertices)-1) - - return A_ub, b_ub - -""" -def break_circles(circle_edges_list: list, L: int, A_ub: list, b_ub: list): - - for circle_edges in circle_edges_list : - if L-1 in circle_vertices : - circle_vertices.remove(L-1) - - h = [0]*L*L - for i in range(L) : - if i in circle_vertices : - h[i*L:i*L+L] = [1]*L - - A_ub = np.vstack((A_ub, h)) - b_ub.append(len(circle_vertices)-1) - - return A_ub, b_ub -""" - -# Checks if the path is connected, returns a circle if it finds one and the RESULT -def is_connected(resx): - - # first round the results to have only 0-1 values - for i, elem in enumerate(resx): - resx[i] = round(elem) - - 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. - n_edges = resx.sum() # number of edges - - nonzeroind = np.nonzero(resx)[0] # the return is a little funny so I use the [0] - - nonzero_tup = np.unravel_index(nonzeroind, (L,L)) - - ind_a = nonzero_tup[0].tolist() - ind_b = nonzero_tup[1].tolist() - - edges = [] - edges_visited = [] - vertices_visited = [] - - for i, a in enumerate(ind_a) : - edges.append((a, ind_b[i])) # Create the list of edges - - edge1 = (ind_a[0], ind_b[0]) - del ind_a[0] - - edges_visited.append(edge1) - vertices_visited.append(edge1[0]) - - remaining = edges - remaining.remove(edge1) - - break_flag = False - while len(remaining) > 0 and not break_flag: - for edge2 in remaining : - if edge2[0] == edge1[1] : - """if edge1[1] in vertices_visited : - ind_b.remove(edge2[1]) - #edges_visited.append(edge2) - break_flag = True - break - else : """ - vertices_visited.append(edge1[1]) - ind_a.remove(edge2[0]) - ind_b.remove(edge2[0]) - #edges_visited.append(edge2) - remaining.remove(edge2) - edge1 = edge2 - - elif edge1[1] == L-1 or edge1[1] in vertices_visited: - ind_b.remove(edge1[1]) - break_flag = True - break - - vertices_visited.append(edge1[1]) - - # Return order of visit if all good - if len(vertices_visited) == n_edges +1 : - return vertices_visited, [] - - """edge1 = (ind_a[0], ind_b[0]) - - vertices_visited.clear() - edges_visited.clear() - - edges_visited.append(edge1) - vertices_visited.append(edge1[0]) - - remaining = edges - remaining.remove(edge1) - - break_flag = False - while len(remaining) > 0 and not break_flag: - for edge2 in remaining : - if edge2[0] == edge1[1] : - # if edge1[1] in vertices_visited : - # edges_visited.append(edge2) - # break_flag = True - # break - # else : - vertices_visited.append(edge1[1]) - edges_visited.append(edge2) - remaining.remove(edge2) - edge1 = edge2 - - elif edge1[1] == L-1 or edge1[1] in vertices_visited: - break_flag = True - break - - vertices_visited.append(edge1[1]) - """ - - return vertices_visited, edges_visited - - -def is_connected2(resx) : +# Returns the order of visit as well as any circles if there are some +def is_connected(resx) : # first round the results to have only 0-1 values for i, elem in enumerate(resx): @@ -221,9 +93,6 @@ def is_connected2(resx) : ind_a = nonzero_tup[0].tolist() ind_b = nonzero_tup[1].tolist() - # print(f"ind_a = {ind_a}") - # print(f"ind_b = {ind_b}") - # Step 1: Create a graph representation graph = defaultdict(list) for a, b in zip(ind_a, ind_b): @@ -232,7 +101,6 @@ def is_connected2(resx) : # Step 2: Function to perform BFS/DFS to extract journeys def get_journey(start): journey_nodes = [] - #journey_edges = [] visited = set() stack = deque([start]) @@ -242,26 +110,21 @@ def is_connected2(resx) : visited.add(node) journey_nodes.append(node) for neighbor in graph[node]: - #journey_edges.append((node, neighbor)) if neighbor not in visited: stack.append(neighbor) - return journey_nodes#, journey_edges + return journey_nodes # Step 3: Extract all journeys all_journeys_nodes = [] - #all_journeys_edges = [] visited_nodes = set() for node in ind_a: if node not in visited_nodes: journey_nodes = get_journey(node) all_journeys_nodes.append(journey_nodes) - #all_journeys_edges.append(journey_edges) visited_nodes.update(journey_nodes) - - for l in all_journeys_nodes : if 0 in l : order = l @@ -274,16 +137,13 @@ def is_connected2(resx) : return order, all_journeys_nodes - - -# Function that returns the distance in meters from one location to another +# Function that returns the time in minutes from one location to another def get_time(p1: Tuple[float, float], p2: Tuple[float, float], detour: float, speed: float) : # Compute the straight-line distance in km if p1 == p2 : return 0 else: - #dist = 6371.01 * acos(sin(radians(p1[0]))*sin(radians(p2[0])) + cos(radians(p1[0]))*cos(radians(p2[0]))*cos(radians(p1[1]) - radians(p2[1]))) dist = geodesic(p1, p2).kilometers # Consider the detour factor for average cityto deterline walking distance (in km) @@ -292,12 +152,6 @@ def get_time(p1: Tuple[float, float], p2: Tuple[float, float], detour: float, sp # Time to walk this distance (in minutes) walk_time = walk_dist/speed*60 - """if walk_time > 15 : - walk_time = 5*round(walk_time/5) - else : - walk_time = round(walk_time)""" - - return round(walk_time) @@ -394,8 +248,8 @@ def init_eq_not_stay(L: int): return [l], [0] -# Go through the landmarks and force the optimizer to use landmarks where attractiveness is set to -1 -def respect_user_mustsee(landmarks: List[Landmark]) : +# Go through the landmarks and force the optimizer to use landmarks marked as must_do +def respect_user_must_do(landmarks: List[Landmark]) : L = len(landmarks) A = [0]*L*L @@ -482,6 +336,7 @@ def link_list(order: List[int], landmarks: List[Landmark])->List[Landmark] : return L, total_dist +# Same as link_list but does it on a already ordered list def link_list_simple(ordered_visit: List[Landmark])-> List[Landmark] : # Read the parameters from the file @@ -528,7 +383,7 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta # SET CONSTRAINTS FOR EQUALITY 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, b = respect_user_must_do(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 @@ -550,8 +405,8 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta # If there is a solution, we're good to go, just check for connectiveness else : - order, circles = is_connected2(res.x) - #nodes, edges = is_connected2(res.x) + order, circles = is_connected(res.x) + #nodes, edges = is_connected(res.x) i = 0 timeout = 80 while circles is not None and i < timeout: @@ -564,8 +419,8 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta 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) + order, circles = is_connected(res.x) + #nodes, edges = is_connected(res.x) if circles is None : break print(i) @@ -575,7 +430,7 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta raise TimeoutError(f"Optimization took too long. No solution found after {timeout} iterations.") # Add the times to reach and stop optimizing - L, total_dist = link_list(order, landmarks) + L, _ = link_list(order, landmarks) if printing_details is True : if i != 0 : diff --git a/backend/src/tester.py b/backend/src/tester.py index 0ad97c7..1b00985 100644 --- a/backend/src/tester.py +++ b/backend/src/tester.py @@ -1,6 +1,4 @@ import pandas as pd -import os -import json from typing import List from landmarks_manager import generate_landmarks @@ -25,45 +23,6 @@ def write_data(L: List[Landmark], file_name: str): data.to_json(file_name, indent = 2, force_ascii=False) -def test3(city_country: str) -> List[Landmark]: - - - preferences = Preferences( - sightseeing=Preference( - name='sightseeing', - type=LandmarkType(landmark_type='sightseeing'), - score = 5), - nature=Preference( - name='nature', - type=LandmarkType(landmark_type='nature'), - score = 0), - shopping=Preference( - name='shopping', - type=LandmarkType(landmark_type='shopping'), - score = 5)) - - coordinates = None - - landmarks, landmarks_short = generate_landmarks(preferences=preferences, city_country=city_country, coordinates=coordinates) - - #write_data(landmarks) - - start = Landmark(name='start', type=LandmarkType(landmark_type='start'), location=(48.2044576, 16.3870242), osm_type='start', osm_id=0, attractiveness=0, must_do=True, n_tags = 0) - finish = Landmark(name='finish', type=LandmarkType(landmark_type='finish'), location=(48.2044576, 16.3870242), osm_type='finish', osm_id=0, attractiveness=0, must_do=True, n_tags = 0) - - - test = landmarks_short - - test.insert(0, start) - test.append(finish) - - max_walking_time = 2 # hours - - visiting_list = solve_optimization(test, max_walking_time*60, True) - - - - def test4(coordinates: tuple[float, float]) -> List[Landmark]: @@ -116,5 +75,4 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]: test4(tuple((48.8344400, 2.3220540))) # Café Chez César #test4(tuple((48.8375946, 2.2949904))) # Point random #test4(tuple((47.377859, 8.540585))) # Zurich HB -#test4(tuple((45.7576485, 4.8330241))) # Lyon Bellecour -#test3('Vienna, Austria') \ No newline at end of file +#test4(tuple((45.7576485, 4.8330241))) # Lyon Bellecour \ No newline at end of file