fixed optimizer and added a member to Landmark class
This commit is contained in:
parent
53a5a9e873
commit
af4d68f36f
@ -9,12 +9,12 @@ from typing import Tuple
|
|||||||
|
|
||||||
|
|
||||||
BBOX_SIDE = 10 # size of bbox in *km* for general area, 10km
|
BBOX_SIDE = 10 # size of bbox in *km* for general area, 10km
|
||||||
RADIUS_CLOSE_TO = 25 # size of area in *m* for close features, 25m radius
|
RADIUS_CLOSE_TO = 27.5 # size of area in *m* for close features, 30m radius
|
||||||
MIN_SCORE = 30 # DEPRECIATED. discard elements with score < 30
|
MIN_SCORE = 30 # DEPRECIATED. discard elements with score < 30
|
||||||
MIN_TAGS = 5 # DEPRECIATED. discard elements withs less than 5 tags
|
MIN_TAGS = 5 # DEPRECIATED. discard elements withs less than 5 tags
|
||||||
CHURCH_PENALTY = 0.1 # penalty to reduce score of curches
|
CHURCH_PENALTY = 0.6 # penalty to reduce score of curches
|
||||||
PARK_COEFF = 10 # multiplier for parks
|
PARK_COEFF = 1.4 # multiplier for parks
|
||||||
N_IMPORTANT = 30 # take the 30 most important landmarks
|
N_IMPORTANT = 40 # take the 30 most important landmarks
|
||||||
|
|
||||||
SIGHTSEEING = LandmarkType(landmark_type='sightseeing')
|
SIGHTSEEING = LandmarkType(landmark_type='sightseeing')
|
||||||
NATURE = LandmarkType(landmark_type='nature')
|
NATURE = LandmarkType(landmark_type='nature')
|
||||||
@ -74,7 +74,7 @@ def generate_landmarks(preferences: Preferences, city_country: str = None, coord
|
|||||||
L += L3
|
L += L3
|
||||||
|
|
||||||
|
|
||||||
return L, take_most_important(L)
|
return remove_duplicates(L), take_most_important(L)
|
||||||
#return L, cleanup_list(L)
|
#return L, cleanup_list(L)
|
||||||
|
|
||||||
# Determines if two locations are close to each other
|
# Determines if two locations are close to each other
|
||||||
@ -86,7 +86,7 @@ def is_close_to(loc1: Tuple[float, float], loc2: Tuple[float, float])->bool :
|
|||||||
else :
|
else :
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Take the most important landmarks from the list
|
||||||
def take_most_important(L: List[Landmark])->List[Landmark] :
|
def take_most_important(L: List[Landmark])->List[Landmark] :
|
||||||
L_copy = []
|
L_copy = []
|
||||||
L_clean = []
|
L_clean = []
|
||||||
@ -110,9 +110,7 @@ def take_most_important(L: List[Landmark])->List[Landmark] :
|
|||||||
for old in L_copy :
|
for old in L_copy :
|
||||||
if old.name == elem.name :
|
if old.name == elem.name :
|
||||||
old.attractiveness = L[t].attractiveness
|
old.attractiveness = L[t].attractiveness
|
||||||
|
scores = [0]*len(L_copy)
|
||||||
continue
|
|
||||||
|
|
||||||
for i, elem in enumerate(L_copy) :
|
for i, elem in enumerate(L_copy) :
|
||||||
scores[i] = elem.attractiveness
|
scores[i] = elem.attractiveness
|
||||||
|
|
||||||
@ -124,23 +122,14 @@ def take_most_important(L: List[Landmark])->List[Landmark] :
|
|||||||
|
|
||||||
return L_clean
|
return L_clean
|
||||||
|
|
||||||
|
|
||||||
# Remove duplicate elements and elements with low score
|
# Remove duplicate elements and elements with low score
|
||||||
def cleanup_list(L: List[Landmark])->List[Landmark] :
|
def remove_duplicates(L: List[Landmark])->List[Landmark] :
|
||||||
L_clean = []
|
L_clean = []
|
||||||
names = []
|
names = []
|
||||||
|
|
||||||
for landmark in L :
|
for landmark in L :
|
||||||
|
|
||||||
if landmark.name in names : # Remove duplicates
|
if landmark.name in names : # Remove duplicates
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif landmark.attractiveness < MIN_SCORE : # Remove uninteresting
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif landmark.n_tags < MIN_TAGS : # Remove uninteresting 2.0
|
|
||||||
continue
|
|
||||||
|
|
||||||
else :
|
else :
|
||||||
names.append(landmark.name)
|
names.append(landmark.name)
|
||||||
L_clean.append(landmark)
|
L_clean.append(landmark)
|
||||||
@ -157,7 +146,6 @@ def correct_score(L: List[Landmark], preference: Preference) :
|
|||||||
raise TypeError(f"LandmarkType {preference.type} does not match the type of Landmark {L[0].name}")
|
raise TypeError(f"LandmarkType {preference.type} does not match the type of Landmark {L[0].name}")
|
||||||
|
|
||||||
for elem in L :
|
for elem in L :
|
||||||
elem.attractiveness = int(elem.attractiveness) + elem.n_tags*100 # arbitrary correction of the balance score vs number of tags
|
|
||||||
elem.attractiveness = int(elem.attractiveness*preference.score/500) # arbitrary computation
|
elem.attractiveness = int(elem.attractiveness*preference.score/500) # arbitrary computation
|
||||||
|
|
||||||
# Correct the score of a list of landmarks by taking into account preferences and the number of tags
|
# Correct the score of a list of landmarks by taking into account preferences and the number of tags
|
||||||
@ -264,14 +252,11 @@ def get_landmarks_coords(coordinates: Tuple[float, float], l: List[Landmark], la
|
|||||||
|
|
||||||
# Add score of given landmark based on the number of surrounding elements. Penalty for churches as there are A LOT
|
# Add score of given landmark based on the number of surrounding elements. Penalty for churches as there are A LOT
|
||||||
if amenity == "'amenity'='place_of_worship'" :
|
if amenity == "'amenity'='place_of_worship'" :
|
||||||
score = int(count_elements_within_radius(location)*CHURCH_PENALTY)
|
score = int((count_elements_within_radius(location) + n_tags*100 )*CHURCH_PENALTY)
|
||||||
elif amenity == "'leisure'='park'" :
|
elif amenity == "'leisure'='park'" :
|
||||||
score = int(count_elements_within_radius(location)*PARK_COEFF)
|
score = int((count_elements_within_radius(location) + n_tags*100 )*PARK_COEFF)
|
||||||
else :
|
else :
|
||||||
score = count_elements_within_radius(location)
|
score = count_elements_within_radius(location) + n_tags*100
|
||||||
|
|
||||||
if name == "Jardin du Luxembourg" :
|
|
||||||
continue
|
|
||||||
|
|
||||||
if score is not None :
|
if score is not None :
|
||||||
# Generate the landmark and append it to the list
|
# Generate the landmark and append it to the list
|
||||||
|
@ -69,9 +69,7 @@ def untangle(resx: list) -> list:
|
|||||||
|
|
||||||
|
|
||||||
# Just to print the result
|
# Just to print the result
|
||||||
def print_res(res, order, landmarks: List[Landmark], P) -> list:
|
def print_res(L: List[Landmark], landmarks: List[Landmark], P) -> list:
|
||||||
|
|
||||||
things = []
|
|
||||||
|
|
||||||
"""N = int(np.sqrt(len(X)))
|
"""N = int(np.sqrt(len(X)))
|
||||||
for i in range(N):
|
for i in range(N):
|
||||||
@ -81,20 +79,24 @@ def print_res(res, order, landmarks: List[Landmark], P) -> list:
|
|||||||
for i,x in enumerate(X) : X[i] = round(x,0)
|
for i,x in enumerate(X) : X[i] = round(x,0)
|
||||||
print(order)"""
|
print(order)"""
|
||||||
|
|
||||||
if len(order) == len(landmarks):
|
if len(L) == len(landmarks):
|
||||||
print('\nAll landmarks can be visited within max_steps, the following order is suggested : ')
|
print('\nAll landmarks can be visited within max_steps, the following order is suggested : ')
|
||||||
else :
|
else :
|
||||||
print('Could not visit all the landmarks, the following order is suggested : ')
|
print('Could not visit all the landmarks, the following order is suggested : ')
|
||||||
|
|
||||||
for idx in order :
|
dist = 0
|
||||||
print('- ' + landmarks[idx].name)
|
for elem in L :
|
||||||
things.append(landmarks[idx].name)
|
if elem.name != 'start' :
|
||||||
|
print('- ' + elem.name + ', time to reach = ' + str(elem.time_to_reach))
|
||||||
|
dist += elem.time_to_reach
|
||||||
|
else :
|
||||||
|
print('- ' + elem.name)
|
||||||
|
|
||||||
steps = path_length(P, abs(res.x))
|
#steps = path_length(P, abs(res.x))
|
||||||
print("\nMinutes walked : " + str(steps))
|
print("\nMinutes walked : " + str(dist))
|
||||||
print(f"\nVisited {len(order)} out of {len(landmarks)} landmarks")
|
print(f"\nVisited {len(L)} out of {len(landmarks)} landmarks")
|
||||||
|
|
||||||
return things
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -366,21 +368,26 @@ def init_eq_not_stay(N: int):
|
|||||||
return [l], [0]
|
return [l], [0]
|
||||||
|
|
||||||
# Constraint to ensure start at start and finish at goal
|
# Constraint to ensure start at start and finish at goal
|
||||||
def respect_start_finish(N, A_eq: list, b_eq: list):
|
def respect_start_finish(L: int, A_eq: list, b_eq: list):
|
||||||
ls = [1]*N + [0]*N*(N-1) # sets only horizontal ones for start (go from)
|
ls = [1]*L + [0]*L*(L-1) # sets only horizontal ones for start (go from)
|
||||||
ljump = [0]*N*N
|
ljump = [0]*L*L
|
||||||
ljump[N-1] = 1 # Prevent start finish jump
|
ljump[L-1] = 1 # Prevent start finish jump
|
||||||
lg = [0]*N*N
|
lg = [0]*L*L
|
||||||
for k in range(N-1) : # sets only vertical ones for goal (go to)
|
ll = [0]*L*(L-1) + [1]*L
|
||||||
|
for k in range(L-1) : # sets only vertical ones for goal (go to)
|
||||||
|
ll[k*L] = 1
|
||||||
if k != 0 : # Prevent the shortcut start -> finish
|
if k != 0 : # Prevent the shortcut start -> finish
|
||||||
lg[k*N+N-1] = 1
|
lg[k*L+L-1] = 1
|
||||||
|
|
||||||
|
|
||||||
A_eq = np.vstack((A_eq,ls))
|
A_eq = np.vstack((A_eq,ls))
|
||||||
A_eq = np.vstack((A_eq,ljump))
|
A_eq = np.vstack((A_eq,ljump))
|
||||||
A_eq = np.vstack((A_eq,lg))
|
A_eq = np.vstack((A_eq,lg))
|
||||||
|
A_eq = np.vstack((A_eq,ll))
|
||||||
b_eq.append(1)
|
b_eq.append(1)
|
||||||
b_eq.append(0)
|
b_eq.append(0)
|
||||||
b_eq.append(1)
|
b_eq.append(1)
|
||||||
|
b_eq.append(0)
|
||||||
|
|
||||||
return A_eq, b_eq
|
return A_eq, b_eq
|
||||||
|
|
||||||
@ -390,19 +397,19 @@ def init_ub_dist(landmarks: List[Landmark], max_steps: int):
|
|||||||
# Objective function coefficients. a*x1 + b*x2 + c*x3 + ...
|
# Objective function coefficients. a*x1 + b*x2 + c*x3 + ...
|
||||||
c = []
|
c = []
|
||||||
# Coefficients of inequality constraints (left-hand side)
|
# Coefficients of inequality constraints (left-hand side)
|
||||||
A = []
|
A_ub = []
|
||||||
for i, spot1 in enumerate(landmarks) :
|
for i, spot1 in enumerate(landmarks) :
|
||||||
dist_table = [0]*len(landmarks)
|
dist_table = [0]*len(landmarks)
|
||||||
c.append(-spot1.attractiveness)
|
c.append(-spot1.attractiveness)
|
||||||
for j, spot2 in enumerate(landmarks) :
|
for j, spot2 in enumerate(landmarks) :
|
||||||
d, t = get_distance(spot1.location, spot2.location)
|
d, t = get_distance(spot1.location, spot2.location)
|
||||||
dist_table[j] = t
|
dist_table[j] = t
|
||||||
A.append(dist_table)
|
A_ub += dist_table
|
||||||
c = c*len(landmarks)
|
c = c*len(landmarks)
|
||||||
A_ub = []
|
"""A_ub = []
|
||||||
for line in A :
|
for line in A :
|
||||||
#print(line)
|
#print(line)
|
||||||
A_ub += line
|
A_ub += line"""
|
||||||
return c, A_ub, [max_steps]
|
return c, A_ub, [max_steps]
|
||||||
|
|
||||||
# Go through the landmarks and force the optimizer to use landmarks where attractiveness is set to -1
|
# Go through the landmarks and force the optimizer to use landmarks where attractiveness is set to -1
|
||||||
@ -438,7 +445,7 @@ def path_length(P: list, resx: list) :
|
|||||||
return np.dot(P, resx)
|
return np.dot(P, resx)
|
||||||
|
|
||||||
# Main optimization pipeline
|
# Main optimization pipeline
|
||||||
def solve_optimization (landmarks, max_steps, printing_details) :
|
def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_details: bool) :
|
||||||
|
|
||||||
N = len(landmarks)
|
N = len(landmarks)
|
||||||
|
|
||||||
@ -499,14 +506,31 @@ def solve_optimization (landmarks, max_steps, printing_details) :
|
|||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
t, order, [] = is_connected(res.x, landmarks)
|
t, order, [] = is_connected(res.x, landmarks)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
prev = landmarks[order[0]]
|
||||||
|
i = 0
|
||||||
|
L = []
|
||||||
|
#prev = landmarks[order[i]]
|
||||||
|
while(len(L) != len(order)) :
|
||||||
|
elem = landmarks[order[i]]
|
||||||
|
if elem != prev :
|
||||||
|
d, t = get_distance(elem.location, prev.location)
|
||||||
|
elem.time_to_reach = t
|
||||||
|
L.append(elem)
|
||||||
|
prev = elem
|
||||||
|
i += 1
|
||||||
|
|
||||||
if printing_details is True :
|
if printing_details is True :
|
||||||
if i != 0 :
|
if i != 0 :
|
||||||
print(f"Neded to recompute paths {i} times because of unconnected loops...")
|
print(f"Neded to recompute paths {i} times because of unconnected loops...")
|
||||||
|
|
||||||
print_res(res, order, landmarks, P)
|
print_res(L, landmarks, P)
|
||||||
|
|
||||||
return order
|
print(np.dot(P, res.x))
|
||||||
|
|
||||||
|
return L
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,16 +79,16 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]:
|
|||||||
|
|
||||||
|
|
||||||
test = landmarks_short
|
test = landmarks_short
|
||||||
test.append(finish)
|
|
||||||
test.insert(0, start)
|
test.insert(0, start)
|
||||||
|
test.append(finish)
|
||||||
|
|
||||||
max_walking_time = 4 # hours
|
max_walking_time = 4 # hours
|
||||||
|
|
||||||
visiting_order = solve_optimization(test, max_walking_time*60, True)
|
visiting_list = solve_optimization(test, max_walking_time*60, True)
|
||||||
|
|
||||||
|
|
||||||
return len(visiting_order)
|
return visiting_list
|
||||||
|
|
||||||
|
|
||||||
test4(tuple((48.834378, 2.322113)))
|
test4(tuple((48.8795156, 2.3660204)))
|
10179
landmarks.txt
10179
landmarks.txt
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user