This commit is contained in:
parent
82a864e57f
commit
2b31ce5f6b
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,20 +259,8 @@ 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)))
|
||||
|
||||
|
||||
|
||||
# CONSTRAINT TO RESPECT MAX NUMBER OF STEPS
|
||||
max_steps = 16
|
||||
# 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
|
||||
@ -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)
|
||||
circle = has_circle(res.x)
|
||||
i = 0
|
||||
|
||||
# Break the circular symmetry if needed
|
||||
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)
|
||||
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 :
|
||||
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
|
||||
@ -351,11 +303,18 @@ if not res.success :
|
||||
# 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")
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user