fixed optimizer and added a member to Landmark class
This commit is contained in:
		@@ -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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -500,13 +507,30 @@ def solve_optimization (landmarks, max_steps, printing_details) :
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        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
											
										
									
								
							
		Reference in New Issue
	
	Block a user