UI elements using the new structs #8
							
								
								
									
										
											BIN
										
									
								
								backend/src/__pycache__/optimizer.cpython-310.pyc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								backend/src/__pycache__/optimizer.cpython-310.pyc
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1,23 +1,26 @@
 | 
				
			|||||||
import fastapi
 | 
					from optimizer import solve_optimization
 | 
				
			||||||
from dataclasses import dataclass
 | 
					from optimizer import landmark
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # CONSTRAINT TO RESPECT MAX NUMBER OF STEPS
 | 
				
			||||||
 | 
					    max_steps = 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@dataclass
 | 
					    # Initialize all landmarks (+ start and goal). Order matters here
 | 
				
			||||||
class Destination:
 | 
					    landmarks = []
 | 
				
			||||||
    name: str
 | 
					    landmarks.append(landmark("départ", -1, (0, 0)))
 | 
				
			||||||
    location: tuple
 | 
					    landmarks.append(landmark("tour eiffel", 99, (0,2)))                           # PUT IN JSON
 | 
				
			||||||
    attractiveness: int
 | 
					    landmarks.append(landmark("arc de triomphe", 99, (0,4)))
 | 
				
			||||||
 | 
					    landmarks.append(landmark("louvre", 99, (0,6)))
 | 
				
			||||||
 | 
					    landmarks.append(landmark("montmartre", 99, (0,10)))
 | 
				
			||||||
 | 
					    landmarks.append(landmark("concorde", 99, (0,8)))
 | 
				
			||||||
 | 
					    landmarks.append(landmark("arrivée", -1, (0, 0)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    visiting_order = solve_optimization(landmarks, max_steps, True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
d = Destination()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def get_route() -> list[Destination]:
 | 
					 | 
				
			||||||
    return {"route": "Hello World"}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
endpoint = ("/get_route", get_route)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    fastapi.run()
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										23
									
								
								backend/src/main_example.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								backend/src/main_example.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					import fastapi
 | 
				
			||||||
 | 
					from dataclasses import dataclass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@dataclass
 | 
				
			||||||
 | 
					class Destination:
 | 
				
			||||||
 | 
					    name: str
 | 
				
			||||||
 | 
					    location: tuple
 | 
				
			||||||
 | 
					    attractiveness: int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					d = Destination()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_route() -> list[Destination]:
 | 
				
			||||||
 | 
					    return {"route": "Hello World"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					endpoint = ("/get_route", get_route)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    fastapi.run()
 | 
				
			||||||
@@ -11,9 +11,8 @@ class landmark :
 | 
				
			|||||||
        self.loc = loc
 | 
					        self.loc = loc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Convert the solution of the optimization into the list of edges to follow. Order is taken into account
 | 
				
			||||||
 | 
					def untangle(resx: list) :
 | 
				
			||||||
def untangle2(resx: list) :
 | 
					 | 
				
			||||||
    N = len(resx)                   # length of res
 | 
					    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.
 | 
					    L = int(np.sqrt(N))             # number of landmarks. CAST INTO INT but should not be a problem because N = L**2 by def.
 | 
				
			||||||
    n_edges = resx.sum()      # number of edges
 | 
					    n_edges = resx.sum()      # number of edges
 | 
				
			||||||
@@ -40,65 +39,31 @@ def untangle2(resx: list) :
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return order
 | 
					    return order
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Convert the result (edges from j to k like d_25 = edge between vertex 2 and vertex 5) into the list of indices corresponding to the landmarks
 | 
					 | 
				
			||||||
def untangle(resx: list) :
 | 
					 | 
				
			||||||
    N = len(resx)                # length of res
 | 
					 | 
				
			||||||
    L = int(np.sqrt(N))         # number of landmarks. CAST INTO INT but should not be a problem because N = L**2 by def.
 | 
					 | 
				
			||||||
    n_landmarks = resx.sum()     # number of edges
 | 
					 | 
				
			||||||
    visit_order = []
 | 
					 | 
				
			||||||
    cnt = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if n_landmarks % 2 == 1 :                                     # if odd number of visited checkpoints
 | 
					 | 
				
			||||||
        for i in range(L) :
 | 
					 | 
				
			||||||
            for j in range(L) :
 | 
					 | 
				
			||||||
                if res[i*L + j] == 1 :              # if index is 1
 | 
					 | 
				
			||||||
                    cnt += 1                        # increment counter
 | 
					 | 
				
			||||||
                    if cnt % 2 == 1 :               # if counter odd
 | 
					 | 
				
			||||||
                        visit_order.append(i)
 | 
					 | 
				
			||||||
                        visit_order.append(j)
 | 
					 | 
				
			||||||
    else :                                   # if even number of ones
 | 
					 | 
				
			||||||
        for i in range(L) :
 | 
					 | 
				
			||||||
            for j in range(L) :
 | 
					 | 
				
			||||||
                if res[i*L + j] == 1 :              # if index is one
 | 
					 | 
				
			||||||
                    cnt += 1                        # increment counter
 | 
					 | 
				
			||||||
                    if j % (L-1) == 0 :             # if last node
 | 
					 | 
				
			||||||
                        visit_order.append(j)       # append only the last index
 | 
					 | 
				
			||||||
                        return visit_order          # return
 | 
					 | 
				
			||||||
                    if cnt % 2 == 1 : 
 | 
					 | 
				
			||||||
                        visit_order.append(i)
 | 
					 | 
				
			||||||
                        visit_order.append(j)
 | 
					 | 
				
			||||||
    return visit_order
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Just to print the result
 | 
					# Just to print the result
 | 
				
			||||||
def print_res(res: list, landmarks: list, P) :
 | 
					def print_res(res, landmarks: list, P) :
 | 
				
			||||||
    X = abs(res.x)
 | 
					    X = abs(res.x)
 | 
				
			||||||
 | 
					    order = untangle(X)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    N = int(np.sqrt(len(X)))
 | 
					    """N = int(np.sqrt(len(X)))
 | 
				
			||||||
    for i in range(N):
 | 
					    for i in range(N):
 | 
				
			||||||
        print(X[i*N:i*N+N])
 | 
					        print(X[i*N:i*N+N])
 | 
				
			||||||
 | 
					    print("Optimal value:", -res.fun)  # Minimization, so we negate to get the maximum
 | 
				
			||||||
    order = untangle2(X)
 | 
					    print("Optimal point:", res.x)
 | 
				
			||||||
 | 
					    for i,x in enumerate(X) : X[i] = round(x,0)
 | 
				
			||||||
    order_ideal = [0, 0, 0, 0, 0, 0, 1, 0]
 | 
					    print(order)"""
 | 
				
			||||||
 | 
					 | 
				
			||||||
    # print("Optimal value:", -res.fun)  # Minimization, so we negate to get the maximum
 | 
					 | 
				
			||||||
    # print("Optimal point:", res.x)
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    #for i,x in enumerate(X) : X[i] = round(x,0)
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    #print(order)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (X.sum()+1)**2 == len(X) : 
 | 
					    if (X.sum()+1)**2 == len(X) : 
 | 
				
			||||||
        print('\nAll landmarks can be visited within max_steps, the following order is most likely not the fastest')
 | 
					        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 could be the fastest but not sure')
 | 
					        print('Could not visit all the landmarks, the following order is suggested : ')
 | 
				
			||||||
    print("Order of visit :")
 | 
					
 | 
				
			||||||
    for idx in order : 
 | 
					    for idx in order : 
 | 
				
			||||||
        print('- ' + landmarks[idx].name)
 | 
					        print('- ' + landmarks[idx].name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    steps = path_length(P, abs(res.x))
 | 
					    steps = path_length(P, abs(res.x))
 | 
				
			||||||
    print("\nSteps walked : " + str(steps))
 | 
					    print("\nSteps walked : " + str(steps))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return order
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Checks for cases of circular symmetry in the result
 | 
					# Checks for cases of circular symmetry in the result
 | 
				
			||||||
def has_circle(resx: list) :
 | 
					def has_circle(resx: list) :
 | 
				
			||||||
@@ -164,8 +129,8 @@ def break_sym(landmarks, A_ub, b_ub):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return A_ub, b_ub
 | 
					    return A_ub, b_ub
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Constraint to not have circular paths. Want to go from start -> finish without unconnected loops
 | 
				
			||||||
def prevent_circle(landmarks, A_ub, b_ub, circle) :
 | 
					def break_circle(landmarks, A_ub, b_ub, circle) :
 | 
				
			||||||
    N = len(landmarks)
 | 
					    N = len(landmarks)
 | 
				
			||||||
    l = [0]*N*N
 | 
					    l = [0]*N*N
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -195,7 +160,7 @@ def respect_number(landmarks, A_ub, b_ub):
 | 
				
			|||||||
        print("\n")"""
 | 
					        print("\n")"""
 | 
				
			||||||
    return np.vstack((A_ub, T)), b_ub + [1]*len(landmarks)
 | 
					    return np.vstack((A_ub, T)), b_ub + [1]*len(landmarks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Constraint to tie the problem together and have a connected path
 | 
					# Constraint to tie the problem together. Necessary but not sufficient to avoid circles
 | 
				
			||||||
def respect_order(landmarks: list, A_eq, b_eq): 
 | 
					def respect_order(landmarks: list, A_eq, b_eq): 
 | 
				
			||||||
    N = len(landmarks)
 | 
					    N = len(landmarks)
 | 
				
			||||||
    for i in range(N-1) :     # Prevent stacked ones
 | 
					    for i in range(N-1) :     # Prevent stacked ones
 | 
				
			||||||
@@ -294,20 +259,8 @@ def respect_user_mustsee(landmarks: list, A_eq: list, b_eq: list) :
 | 
				
			|||||||
def path_length(P: list, resx: list) :
 | 
					def path_length(P: list, resx: list) :
 | 
				
			||||||
    return np.dot(P, resx)
 | 
					    return np.dot(P, resx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Initialize all landmarks (+ start and goal). Order matters here
 | 
					# Main optimization pipeline
 | 
				
			||||||
landmarks = []
 | 
					def solve_optimization (landmarks, max_steps, printing) :
 | 
				
			||||||
landmarks.append(landmark("départ", -1, (0, 0)))
 | 
					 | 
				
			||||||
landmarks.append(landmark("tour eiffel", 99, (0,2)))                           # PUT IN JSON
 | 
					 | 
				
			||||||
landmarks.append(landmark("arc de triomphe", 99, (0,4)))
 | 
					 | 
				
			||||||
landmarks.append(landmark("louvre", 99, (0,6)))
 | 
					 | 
				
			||||||
landmarks.append(landmark("montmartre", 99, (0,10)))
 | 
					 | 
				
			||||||
landmarks.append(landmark("concorde", 99, (0,8)))
 | 
					 | 
				
			||||||
landmarks.append(landmark("arrivée", -1, (0, 0)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# CONSTRAINT TO RESPECT MAX NUMBER OF STEPS
 | 
					 | 
				
			||||||
max_steps = 16
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 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
 | 
				
			||||||
@@ -330,20 +283,19 @@ x_bounds = [(0, 1)] * len(c)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    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)
 | 
				
			||||||
    circle = has_circle(res.x)
 | 
					    circle = has_circle(res.x)
 | 
				
			||||||
 | 
					    i = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Break the circular symmetry if needed
 | 
				
			||||||
    while len(circle) != 0 :
 | 
					    while len(circle) != 0 :
 | 
				
			||||||
    print("The solution has a circular path. Not interpretable.")
 | 
					        A_ub, b_ub = break_circle(landmarks, A_ub, b_ub, circle)
 | 
				
			||||||
    print("Need to add constraints until no circle ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    A_ub, b_ub = prevent_circle(landmarks, A_ub, b_ub, circle)
 | 
					 | 
				
			||||||
        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)
 | 
				
			||||||
        circle = has_circle(res.x)
 | 
					        circle = has_circle(res.x)
 | 
				
			||||||
 | 
					        i += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Raise error if no solution is found
 | 
					    # Raise error if no solution is found
 | 
				
			||||||
    if not res.success :
 | 
					    if not res.success :
 | 
				
			||||||
    print(f"No solution has been found within given timeframe.\nMinimum steps to visit all must_see is : {H}")
 | 
					
 | 
				
			||||||
        # Override the max_steps using the heuristic
 | 
					        # Override the max_steps using the heuristic
 | 
				
			||||||
        for i, val in enumerate(b_ub) :
 | 
					        for i, val in enumerate(b_ub) :
 | 
				
			||||||
            if val == max_steps : b_ub[i] = H
 | 
					            if val == max_steps : b_ub[i] = H
 | 
				
			||||||
@@ -351,11 +303,18 @@ if not res.success :
 | 
				
			|||||||
        # Solve problem again :
 | 
					        # Solve problem again :
 | 
				
			||||||
        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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not res.success :
 | 
				
			||||||
 | 
					            raise ValueError("No solution could be found, even when increasing max_steps using the heuristic")
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
# Print result
 | 
					
 | 
				
			||||||
print_res(res, landmarks, P)
 | 
					    if printing is True :
 | 
				
			||||||
 | 
					        if i != 0 :
 | 
				
			||||||
 | 
					            print(f"Neded to recompute paths {i} times because of unconnected loops...")
 | 
				
			||||||
 | 
					            X = print_res(res, landmarks, P)
 | 
				
			||||||
 | 
					            return X
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    else :
 | 
				
			||||||
 | 
					        return untangle(res.x)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user