Skip to content
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

using elkai for LKH #1

Merged
merged 2 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pip install -e .

The command line use of VeRyPy assumes TSPLIB formatted files (assuming VeRyPy is in your PYTHONPATH):
```bash
python -O VeRyPy.py -a all E-n51-k5.vrp
python -O VeRyPy.py -a all examples/E-n51-k5.vrp
```

> Note: running with `python -O` entirely disables `__debug__` and logging.
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ natsort
orderedset # compatible up to python 3.8
numpy
scipy
llist
llist
elkai
126 changes: 48 additions & 78 deletions verypy/tsp_solvers/tsp_solver_lkh.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,15 @@
from subprocess import Popen, PIPE
from tempfile import NamedTemporaryFile
from logging import log, DEBUG
from turtle import end_fill
import numpy as np
import sys
import os

from cvrp_io import write_TSPLIB_file
from verypy.config import LKH_EXACT_DISTANCES_PRECISION_DECIMALS

from verypy.config import LKH_EXE_PATH, LKH_EXACT_DISTANCES_PRECISION_DECIMALS
import elkai

# Make sure we have access to LKH executable (download, comiple, and modify config.py).
if not os.path.isfile(LKH_EXE_PATH):
raise ImportError("No LKH executable found at \"%s\" (the path is defined in config.py)"%LKH_EXE_PATH)
if not os.access(LKH_EXE_PATH, os.X_OK):
raise ImportError("LKH executable is not set executable")

def solve_tsp_lkh(D, selected_idxs,
float_accuracy = LKH_EXACT_DISTANCES_PRECISION_DECIMALS,
Expand Down Expand Up @@ -49,85 +45,59 @@ def solve_tsp_lkh(D, selected_idxs,
p=n
return sol, sol_f

# 1. Create a TSPLIB file
are_float_distances = issubclass(D.dtype.type, np.float)
if not are_float_distances:
float_accuracy = None
with NamedTemporaryFile( delete=False, suffix='.tsp') as tmpfile:
temp_problem_file_path = tmpfile.name
write_TSPLIB_file(temp_problem_file_path, D,
selected_idxs=selected_idxs,
float_to_int_precision=float_accuracy)

# 2. Create a parameter file for lkh.exe
with NamedTemporaryFile( delete=False, suffix='.par') as tmpfile:
temp_parameter_file_path = tmpfile.name
with NamedTemporaryFile( delete=False, suffix='.tspsol') as tmpfile:
temp_output_file_path = tmpfile.name
with open(temp_parameter_file_path, 'w') as problem_file:
problem_file.write("PROBLEM_FILE = %s\n" % temp_problem_file_path)
problem_file.write("OUTPUT_TOUR_FILE = %s\n" % temp_output_file_path)
problem_file.write("TRACE_LEVEL = 0\n")
#problem_file.write("SEED = 42\n")
if num_runs:
problem_file.write("RUNS = %d\n"%num_runs)


# 3. Call lkh
command = [LKH_EXE_PATH, temp_parameter_file_path]
p = Popen(command, stdout=PIPE, stdin=PIPE)

# In Python3 bytes go in and come out. Special handling is required.
# (not pretty, but allows smoketests to pass on Py3)
if sys.version_info[0] >= 3:
stdout_data = p.communicate(input=b' ')[0].decode('ascii')
else:
stdout_data = p.communicate(input=' ')[0]
M = D[selected_idxs, :][:, selected_idxs]

are_float_distances = issubclass(M.dtype.type, np.float)
if are_float_distances:
M = int(M * float_accuracy)
else:
M = M

if __debug__:
log(DEBUG-3, stdout_data)

#TODO: CHECK FOR LKH FAILURE?
#if "Press any key to continue" in stdout_data[-40:]:
# lkh_successfull = True

if num_runs is None:
num_runs = 1

output = elkai.solve_int_matrix(M, runs=num_runs)





# 4. Process output
sol = []
obj_f = 0
tail = []
with open(temp_output_file_path, 'r') as output_file:
skip = True
for l in output:
depot_found = False
for l in output_file.readlines():
l = l.strip()
if l == "EOF" or l == "-1":
skip = True
elif "Length = " in l:
obj_f = float(l.split()[-1])
elif not skip:
vrp_nid = selected_idxs[int(l)-1]
if vrp_nid==0:
tail.append(0)
depot_found = True
if depot_found:
sol.append(vrp_nid)
else:
tail.append(vrp_nid)
if l == "TOUR_SECTION":
skip = False
sol+=tail
if not depot_found:
sol+=[sol[0]]
os.remove(temp_problem_file_path)
os.remove(temp_parameter_file_path)
os.remove(temp_output_file_path)
if are_float_distances:
obj_f = obj_f/float_accuracy;

vrp_nid = selected_idxs[l]
if vrp_nid==0:
tail.append(0)
depot_found = True
if depot_found:
sol.append(vrp_nid)
else:
tail.append(vrp_nid)

sol += tail
if not depot_found:
sol+=[sol[0]]


# 5. Calculating the tour length
obj_f = 0
for i in range(len(sol)-1):
obj_f += D[sol[i], sol[i+1]]


#TODO: CHECK FOR LKH FAILURE?
#if "Press any key to continue" in stdout_data[-40:]:
# lkh_successfull = True




return sol, obj_f


if __name__=="__main__":
from shared_cli import tsp_cli
Expand Down