-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can I solve a TSP problem with this solver using a distance matrix instead of inputing the latitude and longitude nodes? #28
Comments
For the moment, you will have to use the standalone version of Concorde. The Python bindings currently only support passing the coordinates of the nodes. I have some plans to improve the interface, but I am not sure when I can get round to fixing this. |
@hachinoone If you're interested in the bleeding edge, I've just pushed a PR with a new API that will give you the functionality that you are looking for. However, this new API is radically different from the old one, so you may have to do some work.
from concorde import Problem, run_concorde
distances = # ...
problem = Problem.from_matrix(distances)
solution = run_concorde(problem)
print(solution.tour) In the above, I would be quite happy to hear about your experiences with this API. In the coming weeks, I plan to improve the API so that it is easier to use. Feedback is very welcome. |
Well, I will have a try. Thanks a lot. |
This is a terrific tool. I used the
This was the result:
May I ask if you're planning to release this updated API formally? Using a distance matrix was the only option for me, since I'm using walking durations between points using OSM data. I calculated my matrix using an OSM routing library. Lats/longs would not work, since the program has to take into account actual footpaths, tracks, roads etc, in calculating the distances. However, with your One final note: for a while I was getting a solution of 0.00, which I knew couldn't be right. After experimentation I realised that the matrix had to contain only integer values. After rounding all my distances to the nearest integer, it worked. |
Thanks @mikedbjones for the informative feedback and the nice visualization. I do want to update the code to support the subprocess API formally and your comment has given me the stimulus that I need to do this. Regarding your other comment, about integer distances, that is something that the API could warn about. |
@jvkersch , it looks like you've made the changes to the main branch. Thank you for this, I can now get pyconcorde to solve from a symmetric matrix. Would you mind if I make a PR to update the readme with this use case? Secondly, I have made a function in my own code to make an asymmetric matrix into a symmetric one; my understanding is that Concorde needs a symmetric problem? If you wish I could make a PR to bring this into the code?
|
@mikedbjones Both sound great, thanks for picking this up!
|
Thank you for the implementation. May I know how to recover the original asymmetric tsp solution from the symmetric solution? Is there an existing API for that? |
Good question @bairuofei , I've submitted a pull request (currently open), which gets you back the solution. Basically you pick alternate elements of the symmetric solution. Eg, if you had a 10 node asymmetric problem, with indices numbered In my pull request #59 you'll see the code for this. Also see an article I recently wrote about geographic asymmetric TSP which explains the logic behind picking alternate elements of the solution: Solving Geographic Travelling Salesman Problems. |
@mikedbjones Thank you for the prompt reply. I have tried your implementation and found a problem. I use it to solve an open tsp problem, i.e., the path starts at vertex 0 and traverses all remaining vertices without going back to vertex 0. To achieve this, I set the first column of the distance matrix to 0. The following code gives a simple example. import numpy as np
from concorde import Problem, run_concorde
def symmetricize(matrix, k=None):
"""
Jonker-Volgenant method of transforming (n x n) asymmetric TSP, C into (2n x 2n) symmetric TSP, S.
Let C be an asymmetric TSP matrix.
Let k be a very large number, ie k >> C.max()
Let U = (u_ij) with u_ii = 0, u_ij = k for i != j.
Construct the (2n x 2n) matrix:
+-------+
| U |C^T|
S = |---+---|
| C | U |
+-------+
S is a symmetric TSP problem on 2n nodes.
There is a one-to-one correspondence between solutions of S and solutions of C.
"""
# if k not provided, make it equal to 10 times the max value:
if k is None:
k = round(10*matrix.max())
matrix_bar = matrix.copy()
np.fill_diagonal(matrix_bar, 0)
u = np.matrix(np.ones(matrix.shape).astype(int) * k)
np.fill_diagonal(u, 0)
matrix_symm_top = np.concatenate((u, np.transpose(matrix_bar)), axis=1)
matrix_symm_bottom = np.concatenate((matrix_bar, u), axis=1)
matrix_symm = np.concatenate((matrix_symm_top, matrix_symm_bottom), axis=0)
return matrix_symm
if __name__ == "__main__":
distance1 = np.array([[0, 2, 4],
[0, 0, 3],
[0, 3, 0]])
# 0
# / \
# (2) (4)
# / \
# 1 - (3) - 2
distance2 = np.array([[0, 5, 4],
[0, 0, 3],
[0, 3, 0]])
# 0
# / \
# (5) (4)
# / \
# 1 - (3) - 2
symm_distance1 = symmetricize(distance1)
problem1 = Problem.from_matrix(symm_distance1)
solution1 = run_concorde(problem1)
print(solution1.tour) # [0, 5, 2, 4, 1, 3]
symm_distance2 = symmetricize(distance2)
problem2 = Problem.from_matrix(symm_distance2)
solution2 = run_concorde(problem2)
print(solution2.tour) # [0, 3, 2, 5, 1, 4] Clearly, solution 2 follows your reply and we can find the shortest path is |
@bairuofei instead of setting the column to zero, try setting it to a very high value (apart from 0 in position 0, 0). This should place a very high cost on returning to node zero.
Can you try this? Apologies, I'm not on my Linux machine at the moment to try it myself. |
Only solution 2 is correct. The cost of solution 1 seems also correct: 104 = 2 +3 + 99, meaning the path is [0 1 2 0]. But the returned path of solution 1 is still confusing. # Solution 1
Optimal Solution: 104.00
Total Running Time: 0.00 (seconds)
[0, 5, 2, 4, 1, 3]
# Solution 2
Optimal Solution: 106.00
Total Running Time: 0.00 (seconds)
[0, 3, 2, 5, 1, 4] |
Try setting |
I found that maybe the problem is caused by my definition of the distance matrix. In my implementation, each row represents its distance to all other vertices. For example, distance1 = np.array([[0, 2, 4],
[99, 0, 3],
[99, 3, 0]]) The first row means the distance from vertex-0 to vertex-1 is 2, from vertex-0 to vertex-2 is 4. While in your blog, the first row represents the distances from all other vertices to vertex-0. So I just transpose the distance1 as follows:
Now the solution is: Optimal Solution: 104.00
Total Running Time: 0.00 (seconds)
[0, 4, 1, 5, 2, 3] It is
|
Several things here:
|
It works. After setting k = 99 and keeping the original distance matrix unchanged, the code outputs the correct answer. May I know why k = 99 works and 990 does not work? 😄 # Solution 1
Optimal Solution: 104.00
Total Running Time: 0.00 (seconds)
[0, 3, 1, 4, 2, 5]
# Solution 2
Optimal Solution: 106.00
Total Running Time: 0.00 (seconds)
[0, 3, 2, 5, 1, 4] |
Because, in the absence of setting |
Hi @jvkersch , anything I can do to help with the PR for the symmetrization? |
I am trying to do the same thing for my project. Could you please share how to run the solver on distance matrix? |
Hi. I am working with a time dependent traveling salesman problem, where the distance between the nodes are changing over time. Since I did not find the APIs in the readme, could you please tell me how to solve a TSP problem using a distance matrix between nodes with this solver? Thanks.
The text was updated successfully, but these errors were encountered: