permafixed ?
This commit is contained in:
parent
4896e95617
commit
50bc8150c8
@ -1,290 +0,0 @@
|
||||
import networkx as nx
|
||||
from typing import List, Tuple
|
||||
from geopy.distance import geodesic
|
||||
from scipy.spatial import KDTree
|
||||
import numpy as np
|
||||
from itertools import combinations
|
||||
from structs.landmarks import Landmark
|
||||
from optimizer_v4 import print_res, link_list_simple, get_time
|
||||
import os
|
||||
import json
|
||||
import heapq
|
||||
|
||||
# Heuristic function: distance to the goal
|
||||
def heuristic(loc1: Tuple[float, float], loc2: Tuple[float, float], score2: int) -> float:
|
||||
return geodesic(loc1, loc2).meters
|
||||
|
||||
|
||||
|
||||
# A* planner to search through the graph
|
||||
def a_star2(G, start_id, end_id, max_walking_time, must_do_nodes, max_landmarks, detour, speed):
|
||||
open_set = []
|
||||
heapq.heappush(open_set, (0, start_id, 0, [start_id], set([start_id])))
|
||||
best_path = None
|
||||
max_attractiveness = 0
|
||||
visited_must_do = set()
|
||||
|
||||
while open_set:
|
||||
_, current_node, current_length, path, visited = heapq.heappop(open_set)
|
||||
|
||||
# If current node is a must_do node and hasn't been visited yet, mark it as visited
|
||||
if current_node in must_do_nodes and current_node not in visited_must_do:
|
||||
visited_must_do.add(current_node)
|
||||
|
||||
# Check if path includes all must_do nodes and reaches the end
|
||||
if current_node == end_id and all(node in visited for node in must_do_nodes):
|
||||
attractiveness = sum(G.nodes[node]['weight'] for node in path)
|
||||
if attractiveness > max_attractiveness:
|
||||
best_path = path
|
||||
max_attractiveness = attractiveness
|
||||
continue
|
||||
|
||||
if len(path) > max_landmarks + 1:
|
||||
continue
|
||||
|
||||
for neighbor in G.neighbors(current_node):
|
||||
if neighbor not in visited:
|
||||
#distance = int(geodesic(G.nodes[current_node]['pos'], G.nodes[neighbor]['pos']).meters * detour / (speed * 16.6666))
|
||||
distance = get_time(G.nodes[current_node]['pos'], G.nodes[neighbor]['pos'], detour, speed)
|
||||
if current_length + distance <= max_walking_time:
|
||||
new_path = path + [neighbor]
|
||||
new_visited = visited | {neighbor}
|
||||
estimated_cost = current_length + distance + get_time(G.nodes[neighbor]['pos'], G.nodes[end_id]['pos'], detour, speed)
|
||||
heapq.heappush(open_set, (estimated_cost, neighbor, current_length + distance, new_path, new_visited))
|
||||
|
||||
# Check if all must_do_nodes have been visited
|
||||
if all(node in visited_must_do for node in must_do_nodes):
|
||||
return best_path, max_attractiveness
|
||||
else:
|
||||
return None, 0
|
||||
|
||||
|
||||
def a_star(G, start_id, end_id, max_walking_time, must_do_nodes, max_landmarks, detour, speed):
|
||||
open_set = []
|
||||
heapq.heappush(open_set, (0, start_id, 0, [start_id], set([start_id])))
|
||||
best_path = None
|
||||
max_attractiveness = 0
|
||||
visited_must_do = set()
|
||||
|
||||
while open_set:
|
||||
_, current_node, current_length, path, visited = heapq.heappop(open_set)
|
||||
|
||||
# If current node is a must_do node and hasn't been visited yet, mark it as visited
|
||||
if current_node in must_do_nodes and current_node not in visited_must_do:
|
||||
visited_must_do.add(current_node)
|
||||
|
||||
# Check if path includes all must_do nodes and reaches the end
|
||||
if current_node == end_id and all(node in visited for node in must_do_nodes):
|
||||
attractiveness = sum(G.nodes[node]['weight'] for node in path)
|
||||
if attractiveness > max_attractiveness:
|
||||
best_path = path
|
||||
max_attractiveness = attractiveness
|
||||
continue
|
||||
|
||||
if len(path) > max_landmarks + 1:
|
||||
continue
|
||||
|
||||
for neighbor in G.neighbors(current_node):
|
||||
if neighbor not in visited:
|
||||
distance = get_time(G.nodes[current_node]['pos'], G.nodes[neighbor]['pos'], detour, speed)
|
||||
if current_length + distance <= max_walking_time:
|
||||
new_path = path + [neighbor]
|
||||
new_visited = visited | {neighbor}
|
||||
estimated_cost = current_length + distance + heuristic(G.nodes[neighbor]['pos'], G.nodes[end_id]['pos'], G.nodes[neighbor]['weight'])
|
||||
heapq.heappush(open_set, (estimated_cost, neighbor, current_length + distance, new_path, new_visited))
|
||||
|
||||
# Check if all must_do_nodes have been visited
|
||||
if all(node in visited_must_do for node in must_do_nodes):
|
||||
return best_path, max_attractiveness
|
||||
else:
|
||||
return None, 0
|
||||
|
||||
|
||||
|
||||
def find_path(G, start_id, finish_id, max_walking_time, must_do_nodes, max_landmarks) -> List[str]:
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
best_path, _ = a_star(G, start_id, finish_id, max_walking_time, must_do_nodes, max_landmarks, detour, speed)
|
||||
|
||||
return best_path if best_path else []
|
||||
|
||||
|
||||
|
||||
# Function to dynamically adjust theta
|
||||
def adjust_theta(num_nodes, theta_opt, target_ratio=2.0):
|
||||
# Start with an initial guess
|
||||
initial_theta = theta_opt
|
||||
# Adjust theta to aim for the target ratio of edges to nodes
|
||||
return initial_theta / (num_nodes ** (1 / target_ratio))
|
||||
|
||||
|
||||
# Create a graph using NetworkX and generate the path without must_do
|
||||
def generate_path(landmarks: List[Landmark], max_walking_time: float, max_landmarks: int) -> List[List[Landmark]]:
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
landmap = {}
|
||||
pos_dict = {}
|
||||
weight_dict = {}
|
||||
G = nx.Graph()
|
||||
|
||||
# Add nodes to the graph with attractiveness
|
||||
for i, landmark in enumerate(landmarks):
|
||||
pos_dict[i] = landmark.location
|
||||
weight_dict[i] = landmark.attractiveness
|
||||
landmap[i] = landmark
|
||||
G.add_node(i, pos=landmark.location, weight=landmark.attractiveness)
|
||||
if landmark.name == 'start' :
|
||||
start_id = i
|
||||
elif landmark.name == 'finish' :
|
||||
finish_id = i
|
||||
|
||||
coords = np.array(list(pos_dict.values()))
|
||||
kdtree = KDTree(coords)
|
||||
|
||||
k = 5
|
||||
if len(landmarks) <= k :
|
||||
k = len(landmarks)-1
|
||||
|
||||
for node, coord in pos_dict.items():
|
||||
indices = kdtree.query(coord, k + 1)[1] # k+1 because the closest neighbor is the node itself
|
||||
for idx in indices[1:]: # skip the first one (itself)
|
||||
neighbor = list(pos_dict.keys())[idx]
|
||||
distance = get_time(coord, pos_dict[neighbor], detour, speed)
|
||||
G.add_edge(node, neighbor, weight=distance)
|
||||
|
||||
print(f"Graph with {G.number_of_nodes()} nodes and {G.number_of_edges()} edges")
|
||||
print("Start computing path...")
|
||||
|
||||
# Find the valid path using the greedy algorithm
|
||||
valid_path = find_path(G, start_id, finish_id, max_walking_time, [], max_landmarks)
|
||||
|
||||
if not valid_path:
|
||||
return [] # No valid path found
|
||||
|
||||
lis = [landmap[id] for id in valid_path]
|
||||
|
||||
lis, _ = link_list_simple(lis)
|
||||
|
||||
print_res(lis, len(landmarks))
|
||||
|
||||
|
||||
return lis
|
||||
|
||||
|
||||
|
||||
# Create a graph using NetworkX and generate the path
|
||||
def generate_path2(landmarks: List[Landmark], max_walking_time: float, max_landmarks: int) -> List[List[Landmark]]:
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
|
||||
landmap = {}
|
||||
pos_dict = {}
|
||||
weight_dict = {}
|
||||
must_do_nodes = []
|
||||
G = nx.Graph()
|
||||
# Add nodes to the graph with attractiveness
|
||||
for i, landmark in enumerate(landmarks):
|
||||
pos_dict[i] = landmark.location
|
||||
weight_dict[i] = landmark.attractiveness
|
||||
landmap[i] = landmark
|
||||
if landmark.must_do :
|
||||
must_do_nodes.append(i)
|
||||
G.add_node(i, pos=landmark.location, weight=landmark.attractiveness)
|
||||
if landmark.name == 'start' :
|
||||
start_id = i
|
||||
elif landmark.name == 'finish' :
|
||||
finish_id = i
|
||||
|
||||
coords = np.array(list(pos_dict.values()))
|
||||
kdtree = KDTree(coords)
|
||||
|
||||
k = 3
|
||||
for node, coord in pos_dict.items():
|
||||
indices = kdtree.query(coord, k + 1)[1] # k+1 because the closest neighbor is the node itself
|
||||
for idx in indices[1:]: # skip the first one (itself)
|
||||
neighbor = list(pos_dict.keys())[idx]
|
||||
distance = get_time(coord, pos_dict[neighbor], detour, speed)
|
||||
G.add_edge(node, neighbor, weight=distance)
|
||||
|
||||
|
||||
|
||||
# Add special edges between must_do nodes
|
||||
if len(must_do_nodes) > 0 :
|
||||
for node1, node2 in combinations(must_do_nodes, 2):
|
||||
if not G.has_edge(node1, node2):
|
||||
distance = get_time(G.nodes[node1]['pos'], G.nodes[node2]['pos'], detour, speed)
|
||||
G.add_edge(node1, node2, weight=distance)
|
||||
|
||||
print(f"Graph with {G.number_of_nodes()} nodes and {G.number_of_edges()} edges")
|
||||
print("Computing path...")
|
||||
|
||||
# Find the valid path using the greedy algorithm
|
||||
valid_path = find_path(G, start_id, finish_id, max_walking_time, must_do_nodes, max_landmarks)
|
||||
|
||||
if not valid_path:
|
||||
return [] # No valid path found
|
||||
|
||||
lis = [landmap[id] for id in valid_path]
|
||||
|
||||
lis, tot_dist = link_list_simple(lis)
|
||||
|
||||
print_res(lis, len(landmarks))
|
||||
|
||||
|
||||
return lis
|
||||
|
||||
|
||||
|
||||
|
||||
def correct_path(tour: List[Landmark]) -> List[Landmark] :
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
G = nx.Graph()
|
||||
|
||||
coords = []
|
||||
landmap = {}
|
||||
for i, landmark in enumerate(tour) :
|
||||
coords.append(landmark.location)
|
||||
landmap[i] = landmark
|
||||
G.add_node(i, pos=landmark.location, weight=landmark.attractiveness)
|
||||
|
||||
kdtree = KDTree(coords)
|
||||
|
||||
k = 3
|
||||
for node, coord in coords:
|
||||
indices = kdtree.query(coord, k + 1)[1] # k+1 because the closest neighbor is the node itself
|
||||
for idx in indices[1:]: # skip the first one (itself)
|
||||
neighbor = list(coords)[idx]
|
||||
distance = get_time(coord, coords[neighbor], detour, speed)
|
||||
G.add_edge(node, neighbor, weight=distance)
|
||||
|
||||
path = nx.approximation.traveling_salesman_problem(G, weight='weight', cycle=True)
|
||||
|
||||
lis = [landmap[id] for id in path]
|
||||
|
||||
lis, tot_dist = link_list_simple(lis)
|
||||
|
||||
print_res(lis, len(tour))
|
||||
|
||||
return path
|
||||
|
@ -1,326 +0,0 @@
|
||||
import numpy as np
|
||||
import json, os
|
||||
|
||||
from typing import List, Tuple
|
||||
from itertools import combinations
|
||||
from scipy.optimize import linprog
|
||||
from math import radians, sin, cos, acos
|
||||
from shapely import Polygon
|
||||
from geopy.distance import geodesic
|
||||
|
||||
|
||||
from structs.landmarks import Landmark
|
||||
|
||||
|
||||
# Function to print the result
|
||||
def print_res(L: List[Landmark], L_tot):
|
||||
|
||||
if len(L) == L_tot:
|
||||
print('\nAll landmarks can be visited within max_steps, the following order is suggested : ')
|
||||
else :
|
||||
print('Could not visit all the landmarks, the following order is suggested : ')
|
||||
|
||||
dist = 0
|
||||
for elem in L :
|
||||
if elem.time_to_reach_next is not None :
|
||||
print('- ' + elem.name + ', time to reach next = ' + str(elem.time_to_reach_next))
|
||||
dist += elem.time_to_reach_next
|
||||
else :
|
||||
print('- ' + elem.name)
|
||||
|
||||
print("\nMinutes walked : " + str(dist))
|
||||
print(f"Visited {len(L)-2} out of {L_tot-2} landmarks")
|
||||
|
||||
|
||||
# Function that returns the distance in meters 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 m
|
||||
if p1 == p2 :
|
||||
return 0
|
||||
else:
|
||||
#dist = 1000 * 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).meters
|
||||
|
||||
# Consider the detour factor for average city to determine walking distance (in m)
|
||||
walk_dist = dist*detour
|
||||
|
||||
# Time to walk this distance (in minutes)
|
||||
walk_time = walk_dist/speed*(60/1000)
|
||||
|
||||
"""if walk_time > 15 :
|
||||
walk_time = 5*round(walk_time/5)
|
||||
else :
|
||||
walk_time = round(walk_time)"""
|
||||
|
||||
|
||||
return round(walk_time)
|
||||
|
||||
|
||||
# Checks if the path is connected, returns a circle if it finds one and the RESULT
|
||||
def is_connected(resx) -> bool:
|
||||
|
||||
|
||||
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.
|
||||
resx = resx[:L*L]
|
||||
|
||||
# first round the results to have only 0-1 values
|
||||
for i, elem in enumerate(resx):
|
||||
resx[i] = round(elem)
|
||||
|
||||
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 = []
|
||||
|
||||
edge1 = (ind_a[0], ind_b[0])
|
||||
edges_visited.append(edge1)
|
||||
vertices_visited.append(edge1[0])
|
||||
|
||||
for i, a in enumerate(ind_a) :
|
||||
edges.append((a, ind_b[i])) # Create the list of edges
|
||||
|
||||
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])
|
||||
|
||||
|
||||
if len(vertices_visited) == n_edges +1 :
|
||||
return vertices_visited, []
|
||||
else:
|
||||
return vertices_visited, edges_visited
|
||||
|
||||
|
||||
# Computes the time to reach from each landmark to the next
|
||||
def link_list(order: List[int], landmarks: List[Landmark])->List[Landmark] :
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour_factor = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
L = []
|
||||
j = 0
|
||||
total_dist = 0
|
||||
while j < len(order)-1 :
|
||||
elem = landmarks[order[j]]
|
||||
next = landmarks[order[j+1]]
|
||||
|
||||
d = get_time(elem.location, next.location, detour_factor, speed)
|
||||
elem.time_to_reach_next = d
|
||||
if elem.name not in ['start', 'finish'] :
|
||||
elem.must_do = True
|
||||
L.append(elem)
|
||||
j += 1
|
||||
total_dist += d
|
||||
|
||||
L.append(next)
|
||||
|
||||
return L, total_dist
|
||||
|
||||
|
||||
|
||||
# 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):
|
||||
|
||||
ones = [1]*L
|
||||
zeros = [0]*L
|
||||
for i in range(L) :
|
||||
h = zeros*i + ones + zeros*(L-1-i) + [0]*(L-1)
|
||||
A_ub.append(h)
|
||||
b_ub.append(1)
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
max_landmarks = parameters['max landmarks']
|
||||
|
||||
A_ub.append(ones*L + [0]*(L-1))
|
||||
b_ub.append(max_landmarks+1)
|
||||
|
||||
return A_ub, b_ub
|
||||
|
||||
|
||||
|
||||
def solve_optimizationv3(landmarks, max_walking_time):
|
||||
L = len(landmarks)
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
# Create distance matrix
|
||||
A = np.zeros((L, L))
|
||||
for i in range(L):
|
||||
for j in range(L):
|
||||
if i != j:
|
||||
A[i, j] = get_time(landmarks[i].location, landmarks[j].location, detour, speed)
|
||||
|
||||
# Define the linear program
|
||||
c = np.hstack((A.flatten(), [0]*(L-1)))
|
||||
bounds = [(0, 1) for _ in range(L*L+L-1)]
|
||||
|
||||
# Flow conservation constraints
|
||||
A_eq = []
|
||||
b_eq = []
|
||||
|
||||
# Each node (except start and end) has one incoming and one outgoing edge
|
||||
for i in range(L):
|
||||
if i == 0 or i == L-1:
|
||||
continue
|
||||
A_eq.append([1 if j // L == i else 0 for j in range(L*L)] + [0]*(L-1))
|
||||
b_eq.append(1)
|
||||
A_eq.append([1 if j % L == i else 0 for j in range(L*L)] + [0]*(L-1))
|
||||
b_eq.append(1)
|
||||
|
||||
# Start node constraint
|
||||
A_eq.append([1 if j // L == 0 else 0 for j in range(L*L)] + [0]*(L-1))
|
||||
b_eq.append(1)
|
||||
|
||||
# End node constraint
|
||||
A_eq.append([1 if j % L == L-1 else 0 for j in range(L*L)] + [0]*(L-1))
|
||||
b_eq.append(1)
|
||||
|
||||
# Subtour elimination constraints
|
||||
A_ub = []
|
||||
b_ub = []
|
||||
|
||||
# u_i - u_j + L*x_ij <= L-1 for all i != j
|
||||
for i in range(1, L):
|
||||
for j in range(1, L):
|
||||
if i != j:
|
||||
constraint = [0] * (L * L + L - 1)
|
||||
constraint[i * L + j] = L
|
||||
constraint[j * L + i] = -L
|
||||
A_ub.append(constraint)
|
||||
b_ub.append(L - 1)
|
||||
|
||||
|
||||
A_ub, b_ub = respect_number(L, A_ub, b_ub) # Respect max number of visits (no more possible stops than landmarks).
|
||||
|
||||
# Convert constraints to numpy arrays
|
||||
A_eq = np.array(A_eq)
|
||||
A_ub = np.array(A_ub)
|
||||
b_ub = np.array(b_ub)
|
||||
b_eq = np.array(b_eq)
|
||||
|
||||
# Solve the linear program
|
||||
result = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method='highs')
|
||||
|
||||
if result.success:
|
||||
x = result.x[:L*L].reshape((L, L))
|
||||
path = []
|
||||
for i in range(L):
|
||||
for j in range(L):
|
||||
if x[i, j] > 0.5:
|
||||
path.append((i, j))
|
||||
print(f"({i}, {j})")
|
||||
|
||||
order, _ = is_connected(result.x)
|
||||
L, _ = link_list(order, landmarks)
|
||||
|
||||
print_res(L, len(landmarks))
|
||||
print("\nTotal score : " + str(int(-result.fun)))
|
||||
|
||||
return L
|
||||
else:
|
||||
print("no results")
|
||||
return []
|
||||
|
||||
|
||||
|
||||
# Main optimization pipeline
|
||||
# def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_details: bool) :
|
||||
|
||||
# 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) # 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
|
||||
# #A_ub, b_ub = prevent_subtours(L, A_ub, b_ub)
|
||||
|
||||
# # 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)
|
||||
|
||||
# # SET BOUNDS FOR DECISION VARIABLE (x can only be 0 or 1)
|
||||
# x_bounds = [(0, 1)]*(L*L + L)
|
||||
|
||||
# # Solve linear programming problem
|
||||
# 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)
|
||||
|
||||
# # Raise error if no solution is found
|
||||
# if not res.success :
|
||||
# raise ArithmeticError("No solution could be found, the problem is overconstrained. Please adapt your must_dos")
|
||||
|
||||
# # If there is a solution, we're good to go, just check for connectiveness
|
||||
# else :
|
||||
# order, circle = is_connected(res.x)
|
||||
# i = 0
|
||||
# timeout = 80
|
||||
# """while len(circle) != 0 and i < timeout:
|
||||
# A_ub, b_ub = prevent_config(res.x, A_ub, b_ub)
|
||||
# #A_ub, b_ub = break_cricle(order, len(landmarks), A_ub, b_ub)
|
||||
# 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)
|
||||
# if not res.success :
|
||||
# raise ArithmeticError(f"No solution found after {timeout} iterations.")
|
||||
|
||||
# order, circle = is_connected(res.x)
|
||||
# if len(circle) == 0 :
|
||||
# break
|
||||
# print(i)
|
||||
# i += 1
|
||||
|
||||
# if i == timeout :
|
||||
# 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)
|
||||
|
||||
# if printing_details is True :
|
||||
# if i != 0 :
|
||||
# print(f"Neded to recompute paths {i} times because of unconnected loops...")
|
||||
# print_res(L, len(landmarks))
|
||||
# print("\nTotal score : " + str(int(-res.fun)))
|
||||
|
||||
# return L
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -11,12 +11,9 @@ from structs.landmarks import Landmark
|
||||
|
||||
|
||||
# Function to print the result
|
||||
def print_res(L: List[Landmark], L_tot):
|
||||
def print_res(L: List[Landmark]):
|
||||
|
||||
if len(L) == L_tot:
|
||||
print('\nAll landmarks can be visited within max_steps, the following order is suggested : ')
|
||||
else :
|
||||
print('Could not visit all the landmarks, the following order is suggested : ')
|
||||
print('The following order is suggested : ')
|
||||
|
||||
dist = 0
|
||||
for elem in L :
|
||||
@ -27,7 +24,7 @@ def print_res(L: List[Landmark], L_tot):
|
||||
print('- ' + elem.name)
|
||||
|
||||
print("\nMinutes walked : " + str(dist))
|
||||
print(f"Visited {len(L)-2} out of {L_tot-2} landmarks")
|
||||
print(f"Visited {len(L)-2} landmarks")
|
||||
|
||||
|
||||
# Prevent the use of a particular solution
|
||||
@ -178,7 +175,7 @@ def init_ub_dist(landmarks: List[Landmark], max_steps: int):
|
||||
closest = sorted(dist_table)[:22]
|
||||
for i, dist in enumerate(dist_table) :
|
||||
if dist not in closest :
|
||||
dist_table[i] = 10000000
|
||||
dist_table[i] = 32700
|
||||
A_ub += dist_table
|
||||
c = c*len(landmarks)
|
||||
|
||||
@ -243,7 +240,7 @@ def init_eq_not_stay(L: int):
|
||||
if j == i :
|
||||
l[j + i*L] = 1
|
||||
|
||||
l = np.array(np.array(l))
|
||||
l = np.array(np.array(l), dtype=np.int8)
|
||||
|
||||
return [l], [0]
|
||||
|
||||
@ -374,23 +371,23 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta
|
||||
# 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, b = respect_number(L, max_landmarks) # Respect max number of visits (no more possible stops than landmarks).
|
||||
A_ub = np.vstack((A_ub, A))
|
||||
A_ub = np.vstack((A_ub, A), dtype=np.int16)
|
||||
b_ub += b
|
||||
A, b = break_sym(L) # break the 'zig-zag' symmetry
|
||||
A_ub = np.vstack((A_ub, A))
|
||||
A_ub = np.vstack((A_ub, A), dtype=np.int16)
|
||||
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, 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))
|
||||
A_eq = np.vstack((A_eq, A), dtype=np.int8)
|
||||
b_eq += b
|
||||
A, b = respect_start_finish(L) # Force start and finish positions
|
||||
A_eq = np.vstack((A_eq, A))
|
||||
A_eq = np.vstack((A_eq, A), dtype=np.int8)
|
||||
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))
|
||||
A_eq = np.vstack((A_eq, A), dtype=np.int8)
|
||||
b_eq += b
|
||||
|
||||
# SET BOUNDS FOR DECISION VARIABLE (x can only be 0 or 1)
|
||||
@ -419,6 +416,9 @@ 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)
|
||||
if not res.success :
|
||||
print("Solving failed because of overconstrained problem")
|
||||
return None
|
||||
order, circles = is_connected(res.x)
|
||||
#nodes, edges = is_connected(res.x)
|
||||
if circles is None :
|
||||
@ -435,7 +435,7 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta
|
||||
if printing_details is True :
|
||||
if i != 0 :
|
||||
print(f"Neded to recompute paths {i} times because of unconnected loops...")
|
||||
print_res(L, len(landmarks))
|
||||
print_res(L)
|
||||
print("\nTotal score : " + str(int(-res.fun)))
|
||||
|
||||
return L
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"city bbox side" : 10,
|
||||
"city bbox side" : 3,
|
||||
"radius close to" : 50,
|
||||
"church coeff" : 0.9,
|
||||
"park coeff" : 1.2,
|
||||
|
@ -47,7 +47,7 @@ def is_close_to(location1: Tuple[float], location2: Tuple[float]):
|
||||
#return (round(location1[0], 3), round(location1[1], 3)) == (round(location2[0], 3), round(location2[1], 3))
|
||||
|
||||
|
||||
# Rearrange some landmarks in the order of visit
|
||||
# Rearrange some landmarks in the order of visit to group visit
|
||||
def rearrange(landmarks: List[Landmark]) -> List[Landmark]:
|
||||
|
||||
i = 1
|
||||
@ -171,45 +171,58 @@ def fix_using_polygon(tour: List[Landmark])-> List[Landmark] :
|
||||
# Append the finish back and correct the time to reach
|
||||
better_tour.append(tour[-1])
|
||||
|
||||
# Rearrange only if polygon
|
||||
better_tour = rearrange(better_tour)
|
||||
# Rearrange only if polygon still not simple
|
||||
if not better_tour_poly.is_simple :
|
||||
better_tour = rearrange(better_tour)
|
||||
|
||||
return better_tour
|
||||
|
||||
|
||||
# Second stage of the optimization. Use linear programming again to refine the path
|
||||
def refine_optimization(landmarks: List[Landmark], base_tour: List[Landmark], max_time: int, print_infos: bool) -> List[Landmark] :
|
||||
def refine_optimization(landmarks: List[Landmark], base_tour: List[Landmark], max_time: int, detour: int, print_infos: bool) -> List[Landmark] :
|
||||
|
||||
# Read from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
max_landmarks = parameters['max landmarks'] + 4
|
||||
|
||||
minor_landmarks = get_minor_landmarks(landmarks, base_tour, 200)
|
||||
# No need to refine if no detour is taken
|
||||
# if detour == 0 :
|
||||
if False :
|
||||
new_tour = base_tour
|
||||
|
||||
else :
|
||||
minor_landmarks = get_minor_landmarks(landmarks, base_tour, 200)
|
||||
|
||||
if print_infos : print("Using " + str(len(minor_landmarks)) + " minor landmarks around the predicted path")
|
||||
if print_infos : print("Using " + str(len(minor_landmarks)) + " minor landmarks around the predicted path")
|
||||
|
||||
# full set of visitable landmarks
|
||||
full_set = base_tour[:-1] + minor_landmarks # create full set of possible landmarks (without finish)
|
||||
full_set.append(base_tour[-1]) # add finish back
|
||||
# full set of visitable landmarks
|
||||
full_set = base_tour[:-1] + minor_landmarks # create full set of possible landmarks (without finish)
|
||||
full_set.append(base_tour[-1]) # add finish back
|
||||
|
||||
# get a new tour
|
||||
new_tour = solve_optimization(full_set, max_time, False, max_landmarks)
|
||||
# get a new tour
|
||||
new_tour = solve_optimization(full_set, max_time+detour, False, max_landmarks)
|
||||
if new_tour is None :
|
||||
new_tour = base_tour
|
||||
|
||||
# Link the new tour
|
||||
new_tour, new_dist = link_list_simple(new_tour)
|
||||
|
||||
# if the tour contains only one landmark, return
|
||||
# If the tour contains only one landmark, return
|
||||
if len(new_tour) < 4 :
|
||||
return new_tour
|
||||
|
||||
# find shortest path using the nearest neighbor heuristic
|
||||
# Find shortest path using the nearest neighbor heuristic
|
||||
better_tour, better_poly = find_shortest_path_through_all_landmarks(new_tour)
|
||||
|
||||
# Fix the tour using Polygons if the path looks weird
|
||||
if base_tour[0].location == base_tour[-1].location and not better_poly.is_valid :
|
||||
better_tour = fix_using_polygon(better_tour)
|
||||
|
||||
|
||||
# Link the tour again
|
||||
better_tour, better_dist = link_list_simple(better_tour)
|
||||
|
||||
# Choose the better tour depending on walked distance
|
||||
if new_dist < better_dist :
|
||||
final_tour = new_tour
|
||||
else :
|
||||
@ -217,7 +230,7 @@ def refine_optimization(landmarks: List[Landmark], base_tour: List[Landmark], ma
|
||||
|
||||
if print_infos :
|
||||
print("\n\n\nRefined tour (result of second stage optimization): ")
|
||||
print_res(final_tour, len(full_set))
|
||||
print_res(final_tour)
|
||||
total_score = 0
|
||||
for elem in final_tour :
|
||||
total_score += elem.attractiveness
|
||||
|
@ -1,306 +0,0 @@
|
||||
from collections import defaultdict
|
||||
from heapq import heappop, heappush
|
||||
from itertools import permutations
|
||||
import os, json
|
||||
|
||||
from shapely import buffer, LineString, Point, Polygon, MultiPoint, convex_hull, concave_hull, LinearRing
|
||||
from typing import List, Tuple
|
||||
from scipy.spatial import KDTree
|
||||
from math import pi
|
||||
import networkx as nx
|
||||
|
||||
from structs.landmarks import Landmark
|
||||
from landmarks_manager import take_most_important
|
||||
from optimizer_v4 import solve_optimization, link_list_simple, print_res, get_time
|
||||
from optimizer_v2 import generate_path, generate_path2
|
||||
|
||||
|
||||
def create_corridor(landmarks: List[Landmark], width: float) :
|
||||
|
||||
corrected_width = (180*width)/(6371000*pi)
|
||||
|
||||
path = create_linestring(landmarks)
|
||||
obj = buffer(path, corrected_width, join_style="mitre", cap_style="square", mitre_limit=2)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def create_linestring(landmarks: List[Landmark])->List[Point] :
|
||||
|
||||
points = []
|
||||
|
||||
for landmark in landmarks :
|
||||
points.append(Point(landmark.location))
|
||||
|
||||
return LineString(points)
|
||||
|
||||
|
||||
def is_in_area(area: Polygon, coordinates) -> bool :
|
||||
point = Point(coordinates)
|
||||
return point.within(area)
|
||||
|
||||
|
||||
def is_close_to(location1: Tuple[float], location2: Tuple[float]):
|
||||
"""Determine if two locations are close by rounding their coordinates to 3 decimals."""
|
||||
absx = abs(location1[0] - location2[0])
|
||||
absy = abs(location1[1] - location2[1])
|
||||
|
||||
return absx < 0.001 and absy < 0.001
|
||||
#return (round(location1[0], 3), round(location1[1], 3)) == (round(location2[0], 3), round(location2[1], 3))
|
||||
|
||||
|
||||
def rearrange(landmarks: List[Landmark]) -> List[Landmark]:
|
||||
|
||||
i = 1
|
||||
while i < len(landmarks):
|
||||
j = i+1
|
||||
while j < len(landmarks):
|
||||
if is_close_to(landmarks[i].location, landmarks[j].location) and landmarks[i].name not in ['start', 'finish'] and landmarks[j].name not in ['start', 'finish']:
|
||||
# If they are not adjacent, move the j-th element to be adjacent to the i-th element
|
||||
if j != i + 1:
|
||||
landmarks.insert(i + 1, landmarks.pop(j))
|
||||
break # Move to the next i-th element after rearrangement
|
||||
j += 1
|
||||
i += 1
|
||||
|
||||
return landmarks
|
||||
|
||||
def find_shortest_path_through_all_landmarks(landmarks: List[Landmark]) -> Tuple[List[Landmark], Polygon]:
|
||||
|
||||
# Read from data
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
# Step 1: Find 'start' and 'finish' landmarks
|
||||
start_idx = next(i for i, lm in enumerate(landmarks) if lm.name == 'start')
|
||||
finish_idx = next(i for i, lm in enumerate(landmarks) if lm.name == 'finish')
|
||||
|
||||
start_landmark = landmarks[start_idx]
|
||||
finish_landmark = landmarks[finish_idx]
|
||||
|
||||
|
||||
# Step 2: Create a list of unvisited landmarks excluding 'start' and 'finish'
|
||||
unvisited_landmarks = [lm for i, lm in enumerate(landmarks) if i not in [start_idx, finish_idx]]
|
||||
|
||||
# Step 3: Initialize the path with the 'start' landmark
|
||||
path = [start_landmark]
|
||||
coordinates = [landmarks[start_idx].location]
|
||||
|
||||
current_landmark = start_landmark
|
||||
|
||||
# Step 4: Use nearest neighbor heuristic to visit all landmarks
|
||||
while unvisited_landmarks:
|
||||
nearest_landmark = min(unvisited_landmarks, key=lambda lm: get_time(current_landmark.location, lm.location, detour, speed))
|
||||
path.append(nearest_landmark)
|
||||
coordinates.append(nearest_landmark.location)
|
||||
current_landmark = nearest_landmark
|
||||
unvisited_landmarks.remove(nearest_landmark)
|
||||
|
||||
# Step 5: Finally add the 'finish' landmark to the path
|
||||
path.append(finish_landmark)
|
||||
coordinates.append(landmarks[finish_idx].location)
|
||||
|
||||
path_poly = Polygon(coordinates)
|
||||
|
||||
return path, path_poly
|
||||
|
||||
def get_minor_landmarks(all_landmarks: List[Landmark], visited_landmarks: List[Landmark], width: float) -> List[Landmark] :
|
||||
|
||||
second_order_landmarks = []
|
||||
visited_names = []
|
||||
area = create_corridor(visited_landmarks, width)
|
||||
|
||||
for visited in visited_landmarks :
|
||||
visited_names.append(visited.name)
|
||||
|
||||
for landmark in all_landmarks :
|
||||
if is_in_area(area, landmark.location) and landmark.name not in visited_names:
|
||||
second_order_landmarks.append(landmark)
|
||||
|
||||
return take_most_important(second_order_landmarks, len(visited_landmarks))
|
||||
|
||||
|
||||
|
||||
"""def refine_optimization(landmarks: List[Landmark], base_tour: List[Landmark], max_time: int, print_infos: bool) -> List[Landmark] :
|
||||
|
||||
minor_landmarks = get_minor_landmarks(landmarks, base_tour, 200)
|
||||
|
||||
if print_infos : print("There are " + str(len(minor_landmarks)) + " minor landmarks around the predicted path")
|
||||
|
||||
full_set = base_tour[:-1] + minor_landmarks # create full set of possible landmarks (without finish)
|
||||
full_set.append(base_tour[-1]) # add finish back
|
||||
|
||||
new_tour = solve_optimization(full_set, max_time, print_infos)
|
||||
|
||||
return new_tour"""
|
||||
|
||||
|
||||
|
||||
def fix_using_polygon(tour: List[Landmark])-> List[Landmark] :
|
||||
|
||||
coords = []
|
||||
coords_dict = {}
|
||||
for landmark in tour :
|
||||
coords.append(landmark.location)
|
||||
if landmark.name != 'finish' :
|
||||
coords_dict[landmark.location] = landmark
|
||||
|
||||
tour_poly = Polygon(coords)
|
||||
|
||||
better_tour_poly = tour_poly.buffer(0)
|
||||
try :
|
||||
xs, ys = better_tour_poly.exterior.xy
|
||||
|
||||
if len(xs) != len(tour) :
|
||||
better_tour_poly = concave_hull(MultiPoint(coords)) # Create concave hull with "core" of tour leaving out start and finish
|
||||
xs, ys = better_tour_poly.exterior.xy
|
||||
|
||||
except :
|
||||
better_tour_poly = concave_hull(MultiPoint(coords)) # Create concave hull with "core" of tour leaving out start and finish
|
||||
xs, ys = better_tour_poly.exterior.xy
|
||||
|
||||
|
||||
# reverse the xs and ys
|
||||
xs.reverse()
|
||||
ys.reverse()
|
||||
|
||||
better_tour = [] # List of ordered visit
|
||||
name_index = {} # Maps the name of a landmark to its index in the concave polygon
|
||||
|
||||
# Loop through the polygon and generate the better (ordered) tour
|
||||
for i,x in enumerate(xs[:-1]) :
|
||||
y = ys[i]
|
||||
better_tour.append(coords_dict[tuple((x,y))])
|
||||
name_index[coords_dict[tuple((x,y))].name] = i
|
||||
|
||||
|
||||
# Scroll the list to have start in front again
|
||||
start_index = name_index['start']
|
||||
better_tour = better_tour[start_index:] + better_tour[:start_index]
|
||||
|
||||
# Append the finish back and correct the time to reach
|
||||
better_tour.append(tour[-1])
|
||||
|
||||
# Rearrange only if polygon
|
||||
better_tour = rearrange(better_tour)
|
||||
|
||||
return better_tour
|
||||
|
||||
|
||||
def refine_optimization(landmarks: List[Landmark], base_tour: List[Landmark], max_time: int, print_infos: bool) -> List[Landmark] :
|
||||
|
||||
# Read from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
max_landmarks = parameters['max landmarks'] + 4
|
||||
|
||||
minor_landmarks = get_minor_landmarks(landmarks, base_tour, 200)
|
||||
|
||||
if print_infos : print("Using " + str(len(minor_landmarks)) + " minor landmarks around the predicted path")
|
||||
|
||||
# full set of visitable landmarks
|
||||
full_set = base_tour[:-1] + minor_landmarks # create full set of possible landmarks (without finish)
|
||||
full_set.append(base_tour[-1]) # add finish back
|
||||
|
||||
# get a new tour
|
||||
new_tour = solve_optimization(full_set, max_time, False, max_landmarks)
|
||||
new_tour, new_dist = link_list_simple(new_tour)
|
||||
|
||||
better_tour, better_poly = find_shortest_path_through_all_landmarks(new_tour)
|
||||
|
||||
if base_tour[0].location == base_tour[-1].location and not better_poly.is_valid :
|
||||
better_tour = fix_using_polygon(better_tour)
|
||||
|
||||
|
||||
better_tour, better_dist = link_list_simple(better_tour)
|
||||
|
||||
if new_dist < better_dist :
|
||||
final_tour = new_tour
|
||||
else :
|
||||
final_tour = better_tour
|
||||
|
||||
if print_infos :
|
||||
print("\n\n\nRefined tour (result of second stage optimization): ")
|
||||
print_res(final_tour, len(full_set))
|
||||
total_score = 0
|
||||
for elem in final_tour :
|
||||
total_score += elem.attractiveness
|
||||
|
||||
print("\nTotal score : " + str(total_score))
|
||||
|
||||
|
||||
|
||||
return final_tour
|
||||
|
||||
|
||||
|
||||
def refine_path(landmarks: List[Landmark], base_tour: List[Landmark], max_time: int, print_infos: bool) -> List[Landmark] :
|
||||
|
||||
print("\nRefining the base tour...")
|
||||
# Read from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
max_landmarks = parameters['max landmarks'] + 3
|
||||
|
||||
"""if len(base_tour)-2 >= max_landmarks :
|
||||
return base_tour"""
|
||||
|
||||
minor_landmarks = get_minor_landmarks(landmarks, base_tour, 200)
|
||||
|
||||
if print_infos : print("Using " + str(len(minor_landmarks)) + " minor landmarks around the predicted path")
|
||||
|
||||
full_set = base_tour + minor_landmarks # create full set of possible landmarks
|
||||
|
||||
print("\nRefined tour (result of second stage optimization): ")
|
||||
|
||||
new_path = generate_path2(full_set, max_time, max_landmarks)
|
||||
|
||||
return new_path
|
||||
|
||||
|
||||
|
||||
|
||||
# If a tour is not connected
|
||||
def correct_path(tour: List[Landmark]) -> List[Landmark] :
|
||||
|
||||
# Read the parameters from the file
|
||||
with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f :
|
||||
parameters = json.loads(f.read())
|
||||
detour = parameters['detour factor']
|
||||
speed = parameters['average walking speed']
|
||||
|
||||
G = nx.Graph()
|
||||
|
||||
coords = []
|
||||
landmap = {}
|
||||
for i, landmark in enumerate(tour) :
|
||||
coords.append(landmark.location)
|
||||
landmap[i] = landmark
|
||||
G.add_node(i, pos=landmark.location, weight=landmark.attractiveness)
|
||||
|
||||
kdtree = KDTree(coords)
|
||||
|
||||
k = 3
|
||||
for node, coord in coords:
|
||||
indices = kdtree.query(coord, k + 1)[1] # k+1 because the closest neighbor is the node itself
|
||||
for idx in indices[1:]: # skip the first one (itself)
|
||||
neighbor = list(coords)[idx]
|
||||
distance = get_time(coord, coords[neighbor], detour, speed)
|
||||
G.add_edge(node, neighbor, weight=distance)
|
||||
|
||||
path = nx.approximation.traveling_salesman_problem(G, weight='weight', cycle=True)
|
||||
|
||||
if len(path) != len(tour) :
|
||||
print("nope")
|
||||
|
||||
lis = [landmap[id] for id in path]
|
||||
|
||||
lis, tot_dist = link_list_simple(lis)
|
||||
|
||||
print_res(lis, len(tour))
|
||||
|
||||
return path
|
||||
|
||||
|
@ -31,7 +31,7 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]:
|
||||
sightseeing=Preference(
|
||||
name='sightseeing',
|
||||
type=LandmarkType(landmark_type='sightseeing'),
|
||||
score = 0),
|
||||
score = 5),
|
||||
nature=Preference(
|
||||
name='nature',
|
||||
type=LandmarkType(landmark_type='nature'),
|
||||
@ -58,21 +58,22 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]:
|
||||
landmarks_short.insert(0, start)
|
||||
landmarks_short.append(finish)
|
||||
|
||||
max_walking_time = 50 # minutes
|
||||
max_walking_time = 480 # minutes
|
||||
detour = 0 # minutes
|
||||
|
||||
# First stage optimization
|
||||
base_tour = solve_optimization(landmarks_short, max_walking_time, True)
|
||||
|
||||
# Second stage using linear optimization
|
||||
if detour != 0 or len(base_tour) <= 4:
|
||||
refined_tour = refine_optimization(landmarks, base_tour, max_walking_time+detour, True)
|
||||
|
||||
refined_tour = refine_optimization(landmarks, base_tour, max_walking_time, detour, True)
|
||||
|
||||
|
||||
return refined_tour
|
||||
|
||||
|
||||
test4(tuple((48.8344400, 2.3220540))) # Café Chez César
|
||||
#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
|
||||
test4(tuple((45.7576485, 4.8330241))) # Lyon Bellecour
|
||||
#test4(tuple((48.5848435, 7.7332974))) # Strasbourg Gare
|
Loading…
x
Reference in New Issue
Block a user