Cleanup and created main
This commit is contained in:
		
							
								
								
									
										
											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 dataclasses import dataclass | ||||
| from optimizer import solve_optimization | ||||
| from optimizer import landmark | ||||
|  | ||||
| def main(): | ||||
|      | ||||
|     # CONSTRAINT TO RESPECT MAX NUMBER OF STEPS | ||||
|     max_steps = 16 | ||||
|  | ||||
|  | ||||
| @dataclass | ||||
| class Destination: | ||||
|     name: str | ||||
|     location: tuple | ||||
|     attractiveness: int | ||||
|     # Initialize all landmarks (+ start and goal). Order matters here | ||||
|     landmarks = [] | ||||
|     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))) | ||||
|  | ||||
|  | ||||
|     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__": | ||||
|     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 | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| def untangle2(resx: list) : | ||||
| # Convert the solution of the optimization into the list of edges to follow. Order is taken into account | ||||
| 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_edges = resx.sum()      # number of edges | ||||
| @@ -40,65 +39,31 @@ def untangle2(resx: list) : | ||||
|  | ||||
|     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 | ||||
| def print_res(res: list, landmarks: list, P) : | ||||
| def print_res(res, landmarks: list, P) : | ||||
|     X = abs(res.x) | ||||
|     order = untangle(X) | ||||
|  | ||||
|     N = int(np.sqrt(len(X))) | ||||
|     """N = int(np.sqrt(len(X))) | ||||
|     for i in range(N): | ||||
|         print(X[i*N:i*N+N]) | ||||
|  | ||||
|     order = untangle2(X) | ||||
|  | ||||
|     order_ideal = [0, 0, 0, 0, 0, 0, 1, 0] | ||||
|  | ||||
|     # 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) | ||||
|     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) :  | ||||
|         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 : | ||||
|         print('Could not visit all the landmarks, the following order could be the fastest but not sure') | ||||
|     print("Order of visit :") | ||||
|         print('Could not visit all the landmarks, the following order is suggested : ') | ||||
|  | ||||
|     for idx in order :  | ||||
|         print('- ' + landmarks[idx].name) | ||||
|  | ||||
|     steps = path_length(P, abs(res.x)) | ||||
|     print("\nSteps walked : " + str(steps)) | ||||
|  | ||||
|     return order | ||||
|  | ||||
| # Checks for cases of circular symmetry in the result | ||||
| def has_circle(resx: list) : | ||||
| @@ -164,8 +129,8 @@ def break_sym(landmarks, A_ub, b_ub): | ||||
|  | ||||
|     return A_ub, b_ub | ||||
|  | ||||
|  | ||||
| def prevent_circle(landmarks, A_ub, b_ub, circle) : | ||||
| # Constraint to not have circular paths. Want to go from start -> finish without unconnected loops | ||||
| def break_circle(landmarks, A_ub, b_ub, circle) : | ||||
|     N = len(landmarks) | ||||
|     l = [0]*N*N | ||||
|  | ||||
| @@ -195,7 +160,7 @@ def respect_number(landmarks, A_ub, b_ub): | ||||
|         print("\n")""" | ||||
|     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):  | ||||
|     N = len(landmarks) | ||||
|     for i in range(N-1) :     # Prevent stacked ones | ||||
| @@ -294,68 +259,62 @@ def respect_user_mustsee(landmarks: list, A_eq: list, b_eq: list) : | ||||
| def path_length(P: list, resx: list) : | ||||
|     return np.dot(P, resx) | ||||
|  | ||||
| # Initialize all landmarks (+ start and goal). Order matters here | ||||
| landmarks = [] | ||||
| 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))) | ||||
| # Main optimization pipeline | ||||
| def solve_optimization (landmarks, max_steps, printing) : | ||||
|  | ||||
|     # SET CONSTRAINTS FOR INEQUALITY | ||||
|     c, A_ub, b_ub = init_ub_dist(landmarks, max_steps)              # Add the distances from each landmark to the other | ||||
|     P = A_ub                                                        # store the paths for later. Needed to compute path length | ||||
|     A_ub, b_ub = respect_number(landmarks, A_ub, b_ub)              # Respect max number of visits.  | ||||
|  | ||||
|     # TODO : Problems with circular symmetry | ||||
|     A_ub, b_ub = break_sym(landmarks, A_ub, b_ub)                  # break the symmetry. Only use the upper diagonal values | ||||
|  | ||||
| # CONSTRAINT TO RESPECT MAX NUMBER OF STEPS | ||||
| max_steps = 16 | ||||
|     # SET CONSTRAINTS FOR EQUALITY | ||||
|     A_eq, b_eq = init_eq_not_stay(landmarks)                       # Force solution not to stay in same place | ||||
|     A_eq, b_eq, H = respect_user_mustsee(landmarks, A_eq, b_eq)       # Check if there are user_defined must_see. Also takes care of start/goal | ||||
|  | ||||
| # SET CONSTRAINTS FOR INEQUALITY | ||||
| c, A_ub, b_ub = init_ub_dist(landmarks, max_steps)              # Add the distances from each landmark to the other | ||||
| P = A_ub                                                        # store the paths for later. Needed to compute path length | ||||
| A_ub, b_ub = respect_number(landmarks, A_ub, b_ub)              # Respect max number of visits.  | ||||
|     A_eq, b_eq = respect_order(landmarks, A_eq, b_eq)              # Respect order of visit (only works when max_steps is limiting factor) | ||||
|  | ||||
| # TODO : Problems with circular symmetry | ||||
| A_ub, b_ub = break_sym(landmarks, A_ub, b_ub)                  # break the symmetry. Only use the upper diagonal values | ||||
|     # Bounds for variables (x can only be 0 or 1) | ||||
|     x_bounds = [(0, 1)] * len(c) | ||||
|  | ||||
| # SET CONSTRAINTS FOR EQUALITY | ||||
| A_eq, b_eq = init_eq_not_stay(landmarks)                       # Force solution not to stay in same place | ||||
| A_eq, b_eq, H = respect_user_mustsee(landmarks, A_eq, b_eq)       # Check if there are user_defined must_see. Also takes care of start/goal | ||||
|     # Solve linear programming problem | ||||
|  | ||||
| A_eq, b_eq = respect_order(landmarks, A_eq, b_eq)              # Respect order of visit (only works when max_steps is limiting factor) | ||||
|  | ||||
| # Bounds for variables (x can only be 0 or 1) | ||||
| x_bounds = [(0, 1)] * len(c) | ||||
|  | ||||
| # 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) | ||||
| circle = has_circle(res.x) | ||||
|  | ||||
| while len(circle) != 0 : | ||||
|     print("The solution has a circular path. Not interpretable.") | ||||
|     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) | ||||
|     circle = has_circle(res.x) | ||||
|     i = 0 | ||||
|  | ||||
|     # Break the circular symmetry if needed | ||||
|     while len(circle) != 0 : | ||||
|         A_ub, b_ub = break_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) | ||||
|         circle = has_circle(res.x) | ||||
|         i += 1 | ||||
|  | ||||
|  | ||||
|     # Raise error if no solution is found | ||||
|     if not res.success : | ||||
|  | ||||
| # Raise error if no solution is found | ||||
| 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 | ||||
|     for i, val in enumerate(b_ub) : | ||||
|         if val == max_steps : b_ub[i] = H | ||||
|         # Override the max_steps using the heuristic | ||||
|         for i, val in enumerate(b_ub) : | ||||
|             if val == max_steps : b_ub[i] = H | ||||
|  | ||||
|     # 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) | ||||
|  | ||||
|  | ||||
| # Print result | ||||
| print_res(res, landmarks, P) | ||||
|         # 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) | ||||
|  | ||||
|         if not res.success : | ||||
|             raise ValueError("No solution could be found, even when increasing max_steps using the heuristic") | ||||
|      | ||||
|  | ||||
|     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