permafixed the optimizer ???
This commit is contained in:
		@@ -8,5 +8,5 @@ geological
 | 
				
			|||||||
'tourism'='alpine_hut'
 | 
					'tourism'='alpine_hut'
 | 
				
			||||||
'tourism'='viewpoint'
 | 
					'tourism'='viewpoint'
 | 
				
			||||||
'tourism'='zoo'
 | 
					'tourism'='zoo'
 | 
				
			||||||
#'tourism'='artwork'
 | 
					'tourism'='artwork'
 | 
				
			||||||
'waterway'='waterfall'
 | 
					'waterway'='waterfall'
 | 
				
			||||||
@@ -31,7 +31,7 @@ def print_res(L: List[Landmark], L_tot):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Prevent the use of a particular solution
 | 
					# 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):
 | 
					    for i, elem in enumerate(resx):
 | 
				
			||||||
        resx[i] = round(elem)
 | 
					        resx[i] = round(elem)
 | 
				
			||||||
@@ -52,13 +52,10 @@ def prevent_config(resx, A_ub, b_ub):
 | 
				
			|||||||
        if i in vertices_visited :
 | 
					        if i in vertices_visited :
 | 
				
			||||||
            h[i*L:i*L+L] = ones
 | 
					            h[i*L:i*L+L] = ones
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    A_ub = np.vstack((A_ub, h))
 | 
					    return h, [len(vertices_visited)-1]
 | 
				
			||||||
    b_ub.append(len(vertices_visited)-1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return A_ub, b_ub
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
 | 
					    l1 = [0]*L*L
 | 
				
			||||||
    l2 = [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
 | 
					    l1[g*L + s] = 1
 | 
				
			||||||
    l2[s*L + g] = 1
 | 
					    l2[s*L + g] = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    A_eq = np.vstack((A_eq, l1))
 | 
					    return np.vstack((l1, l2)), [0, 0]
 | 
				
			||||||
    b_eq.append(0)
 | 
					 | 
				
			||||||
    A_eq = np.vstack((A_eq, l2))
 | 
					 | 
				
			||||||
    b_eq.append(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return A_eq, b_eq
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Prevent the possibility of a given solution bit
 | 
					# Prevent the possibility of a given solution bit
 | 
				
			||||||
def break_circle(circle_vertices: list, L: int, A_ub: list, b_ub: list):
 | 
					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
 | 
					# 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
 | 
					    ones = [1]*L
 | 
				
			||||||
    zeros = [0]*L
 | 
					    zeros = [0]*L
 | 
				
			||||||
    for i in range(L) :
 | 
					    A = ones + zeros*(L-1)
 | 
				
			||||||
        h = zeros*i + ones + zeros*(L-1-i)
 | 
					    b = [1]
 | 
				
			||||||
        A_ub = np.vstack((A_ub, h))
 | 
					    for i in range(L-1) :
 | 
				
			||||||
        b_ub.append(1)
 | 
					        h_new = zeros*i + ones + zeros*(L-1-i)
 | 
				
			||||||
 | 
					        A = np.vstack((A, h_new))
 | 
				
			||||||
 | 
					        b.append(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if max_landmarks is None :
 | 
					    if max_landmarks is None :
 | 
				
			||||||
        # Read the parameters from the file
 | 
					        # 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())
 | 
					            parameters = json.loads(f.read())
 | 
				
			||||||
            max_landmarks = parameters['max landmarks']
 | 
					            max_landmarks = parameters['max landmarks']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    A_ub = np.vstack((A_ub, ones*L))
 | 
					    A = np.vstack((A, ones*L))
 | 
				
			||||||
    b_ub.append(max_landmarks+1)
 | 
					    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
 | 
					# Constraint to not have d14 and d41 simultaneously. Does not prevent cyclic paths with more elements
 | 
				
			||||||
def break_sym(L, A_ub, b_ub):
 | 
					def break_sym(L):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upper_ind = np.triu_indices(L,0,L)
 | 
					    upper_ind = np.triu_indices(L,0,L)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    up_ind_x = upper_ind[0]
 | 
					    up_ind_x = upper_ind[0]
 | 
				
			||||||
    up_ind_y = upper_ind[1]
 | 
					    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
 | 
					        l = [0]*L*L
 | 
				
			||||||
        if up_ind_x[i] != up_ind_y[i] :
 | 
					        if up_ind_x[i] != up_ind_y[i] :
 | 
				
			||||||
            l[up_ind_x[i]*L + up_ind_y[i]] = 1
 | 
					            l[up_ind_x[i]*L + up_ind_y[i]] = 1
 | 
				
			||||||
            l[up_ind_y[i]*L + up_ind_x[i]] = 1
 | 
					            l[up_ind_y[i]*L + up_ind_x[i]] = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            A_ub = np.vstack((A_ub,l))
 | 
					            A = np.vstack((A,l))
 | 
				
			||||||
            b_ub.append(1)
 | 
					            b.append(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return A_ub, b_ub
 | 
					    return A, b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Constraint to not stay in position. Removes d11, d22, d33, etc.
 | 
					# 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
 | 
					# 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)
 | 
					    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']:
 | 
					        if elem.must_do is True and elem.name not in ['finish', 'start']:
 | 
				
			||||||
            l = [0]*L*L
 | 
					            l = [0]*L*L
 | 
				
			||||||
            l[i*L:i*L+L] = [1]*L        # set mandatory departures from landmarks tagged as 'must_do'
 | 
					            l[i*L:i*L+L] = [1]*L        # set mandatory departures from landmarks tagged as 'must_do'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            A_eq = np.vstack((A_eq,l))
 | 
					            A = np.vstack((A,l))
 | 
				
			||||||
            b_eq.append(1)
 | 
					            b.append(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return A_eq, b_eq
 | 
					    return A, b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Constraint to ensure start at start and finish at goal
 | 
					# 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 = [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_start[L-1] = 0                # prevents the jump from start to finish
 | 
				
			||||||
    l_goal = [0]*L*L                # sets arrivals only for finish (vertical ones)
 | 
					    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 
 | 
					            l_goal[k*L+L-1] = 1 
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    A_eq = np.vstack((A_eq,l_start))
 | 
					    A = np.vstack((l_start, l_goal))
 | 
				
			||||||
    A_eq = np.vstack((A_eq,l_goal))
 | 
					    b = [1, 1]
 | 
				
			||||||
    A_eq = np.vstack((A_eq,l_L))
 | 
					    A = np.vstack((A,l_L))
 | 
				
			||||||
    b_eq.append(1)
 | 
					    b.append(0)
 | 
				
			||||||
    b_eq.append(1)
 | 
					 | 
				
			||||||
    b_eq.append(0)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return A_eq, b_eq
 | 
					    return A, b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Constraint to tie the problem together. Necessary but not sufficient to avoid circles
 | 
					# Constraint to tie the problem together. Necessary but not sufficient to avoid circles
 | 
				
			||||||
def respect_order(N: int, A_eq, b_eq): 
 | 
					def respect_order(L: int): 
 | 
				
			||||||
    for i in range(N-1) :           # Prevent stacked ones
 | 
					
 | 
				
			||||||
        if i == 0 or i == N-1:      # Don't touch start or finish
 | 
					    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
 | 
					            continue
 | 
				
			||||||
        else : 
 | 
					        else : 
 | 
				
			||||||
            l = [0]*N
 | 
					            l = [0]*L
 | 
				
			||||||
            l[i] = -1
 | 
					            l[i] = -1
 | 
				
			||||||
            l = l*N
 | 
					            l = l*L
 | 
				
			||||||
            for j in range(N) :
 | 
					            for j in range(L) :
 | 
				
			||||||
                l[i*N + j] = 1
 | 
					                l[i*L + j] = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            A_eq = np.vstack((A_eq,l))
 | 
					            A = np.vstack((A,l))
 | 
				
			||||||
            b_eq.append(0)
 | 
					            b.append(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return A_eq, b_eq
 | 
					    return A, b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Computes the time to reach from each landmark to the next
 | 
					# Computes the time to reach from each landmark to the next
 | 
				
			||||||
@@ -514,15 +517,26 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta
 | 
				
			|||||||
    L = len(landmarks)
 | 
					    L = len(landmarks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # SET CONSTRAINTS FOR INEQUALITY
 | 
					    # SET CONSTRAINTS FOR INEQUALITY
 | 
				
			||||||
    c, A_ub, b_ub = init_ub_dist(landmarks, max_steps)              # Add the distances from each landmark to the other
 | 
					    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, b = respect_number(L, 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
 | 
					    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
 | 
					    # 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 = 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, b = respect_user_mustsee(landmarks)                      # 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 = np.vstack((A_eq, A))
 | 
				
			||||||
    A_eq, b_eq = respect_order(L, A_eq, b_eq)                       # Respect order of visit (only works when max_steps is limiting factor)
 | 
					    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)
 | 
					    # SET BOUNDS FOR DECISION VARIABLE (x can only be 0 or 1)
 | 
				
			||||||
    x_bounds = [(0, 1)]*L*L
 | 
					    x_bounds = [(0, 1)]*L*L
 | 
				
			||||||
@@ -541,10 +555,14 @@ def solve_optimization (landmarks :List[Landmark], max_steps: int, printing_deta
 | 
				
			|||||||
        i = 0
 | 
					        i = 0
 | 
				
			||||||
        timeout = 80
 | 
					        timeout = 80
 | 
				
			||||||
        while circles is not None and i < timeout:
 | 
					        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)
 | 
					            #A_ub, b_ub = prevent_circle(order, len(landmarks), A_ub, b_ub)
 | 
				
			||||||
            for circle in circles :
 | 
					            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)
 | 
					            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)
 | 
					            order, circles = is_connected2(res.x)
 | 
				
			||||||
            #nodes, edges = is_connected2(res.x)
 | 
					            #nodes, edges = is_connected2(res.x)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,8 +99,8 @@ def test4(coordinates: tuple[float, float]) -> List[Landmark]:
 | 
				
			|||||||
    landmarks_short.insert(0, start)
 | 
					    landmarks_short.insert(0, start)
 | 
				
			||||||
    landmarks_short.append(finish)
 | 
					    landmarks_short.append(finish)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    max_walking_time = 120      # minutes
 | 
					    max_walking_time = 50      # minutes
 | 
				
			||||||
    detour = 30                 # minutes
 | 
					    detour = 0                 # minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # First stage optimization
 | 
					    # First stage optimization
 | 
				
			||||||
    base_tour = solve_optimization(landmarks_short, max_walking_time, True)
 | 
					    base_tour = solve_optimization(landmarks_short, max_walking_time, True)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user