pyvrp / vrplib Goto Github PK
View Code? Open in Web Editor NEWPython package to read and write vehicle routing problem instances.
Home Page: https://github.com/PyVRP/VRPLIB
License: MIT License
Python package to read and write vehicle routing problem instances.
Home Page: https://github.com/PyVRP/VRPLIB
License: MIT License
The CI is pretty slow.
In the ALNS project, caching was added to the CI and reduced the CI to below 1 minute for each Python version. See N-Wouda/ALNS@dcff19a.
I forgot to update the README about the new download interface.
Good to start early with this :-)
It seems reading a large instance is rather slow, most likely due to computing the distance matrix using a Python for loop. E.g. for GH1000 instance, running on Colab:
!pip install vrplib
import vrplib
vrplib.download_instance("RC2_10_8", "RC2_10_8.vrp")
%%timeit
instance = vrplib.read_instance("RC2_10_8.vrp", instance_format="solomon")
3.35 s ± 178 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
I have uploaded the instances at https://github.com/VRPLIB/VRPTW to experiment with this idea.
TODOs
The -1
in the depot section does not seem to serve any purpose. I think it's OK if we do not raise in that case.
It would be nice to have a function that could also write VRP instances. This was provided in the EURO-NeurIPS competition.
Users must pass a path
argument to download_instance
.
I have been using standard assert
statements and numpy.testing
assertions. I think it's good to make this more consistent, and to use numpy.testing
everywhere.
The CVRPLIB website has been offline the entire day. The code currently just hangs waiting for a response. I think we should throw an error if downloading hasn't finished after 20 seconds.
The key names should not be altered but instead follow the exact names from the instance data.
# TODO: check must be done by VRPLIB
# ("data/DepotSectionDoesNotEndInMinusOne.txt", RuntimeError),
We should provide some simple plotting functionalities at some point.
It should not be necessary to make a request to the library to obtain all instance names. The instance names don't change over time, only the best known solutions do.
We can simply add a .txt file to this library, which gets updated at every new instances set.
list_instances
to list_names
Downloading an instance currently does not save the instance locally. We should rewrite the code to actually download the files to save them locally.
The user would then have to write this:
import vrplib
# Download the instance and solution locally
vrplib.download_instance("X-n101-k25", "/path/to/X-n101-k25.vrp")
vrplib.download_solution("X-n101-k25", "/path/to/X-n101-k25.sol")
# Read the downloaded instance and solution
instance = vrplib.read_instance("/path/to/X-n101-k25.vrp")
solution = vrplib.read_solution("/path/to/X-n101-k25.sol")
To support more instances, it's best to standardize the output to a dictionary. Currently I have added extra attributes etc. which I might want to remove as well later.
Would be nice to download the full instance and solution sets in one function call. I'm not sure yet how to implement this. Some ideas:
download_set
, which can be used likedownload_set("X", output_dir="instances/")
- Two directions here:
- This function downloads the corresponding "X" set zip and unzips it.
- It downloads all "X" instances and solutions.
download_instance
and download_solution
.For the non-integral CVRP instances and VRPTW instances, I am unable to compute the same cost as given by the solution.
The write_solution
function only accept solution as dictionary types, which is odd. We should accept solutions of type List[List[int]]
instead.
Except for some edge weight format and types, the current code does not check if the instance data are correct. For example, time windows can be checked for early <= late
. Or that all data sections should have enough entries.
As of now, the library is just really about reading existing instances, so there should be no need to checking validity of the instances. But at some point it might be useful to include this as well, combined with #34 and #14.
cvrplib.read
currently supports instances from CVPRLIB. I want to extend this to be able to read instances for LKH-3 as well. The formatting is not much different I think, so it's mostly about how the codebase is organized.
The output does need to change. I currently use CVRP
and VRPTW
classes, but I think I want only to output a dictionary.
Not sure if better..
route = [int(cust) for cust in re.match(r"Route #\d+: (.*)", line).group(1).split()]
Caching no longer makes sense since the files are saved locally.
To make the code more explicit, I want to make separate functions for reading instances and solutions, and also downloading instances and solutions.
Example:
import cvrplib
# Read
instance = cvrplib.read_instance('/path/to/A-n32-k5.vrp')
solution = cvrplib.read_solution('/path/to/A-n32-k5.sol')
# Download
instance = cvrplib.download_instance('/path/to/A-n32-k5.vrp')
solution = cvrplib.download_solution('/path/to/A-n32-k5.sol')
After finishing #14, I also want to have separate functions for writing instances and solutions:
# Write
cvrplib.write_instance('/path/to/A-n32-k5.vrp', specifications, sections)
cvrplib.write_solution('/path/to/A-n32-k5.sol', routes)
It's currently just a wall of data. It would be nice to have all fields displayed.
I have been sloppy in writing my tests. Especially with the module rehaul, most of the tests are not in the right place, or not even implemented at all. I think that coverage
can be very helpful to detect the missing spots.
coverage
parse
module========================================================================================================== warnings summary ==========================================================================================================
tests/download/test_list_names.py::test_list_names[case0]
/Users/leonlan/Dropbox/cvrplib/vrplib/download/list_names.py:52: DeprecationWarning: read_text is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
fi = pkg_resource.read_text(__package__, "instance_data.csv")
tests/download/test_list_names.py::test_list_names[case0]
/opt/homebrew/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/importlib/resources/_legacy.py:80: DeprecationWarning: open_text is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
with open_text(package, resource, encoding, errors) as fp:
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
---------- coverage: platform darwin, python 3.11.3-final-0 ----------
Coverage XML written to file coverage.xml
=================================================================================================== 86 passed, 2 warnings in 3.39s ===================================================================================================
leonlan@Leons-Air cvrplib %
A fast Python linter, looks promising and can be used to replace flake8
and isort
.
It currently takes about 2 seconds to read a 1000-customer instance without explicit matrix:
In [73]: %timeit vrplib.read_instance(f"tmp/X-n1001-k43.txt")
2.14 s ± 44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
This is slow. I think it's mainly due to computing the Euclidean distances. For example, another 1000 customer instance with explicit matrix reads much faster:
In [76]: %timeit vrplib.read_instance(f"tmp/Loggi-n1001-k31.txt")
246 ms ± 8.88 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Just found a bug in the distance calculation from flattened distance matrices.
The function 'from_flattened' (line 141 in cvrp.py) assumes that
" The numbers in a flattened list correspond the matrix element indices
(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), ..."
However, for the instance E-n13-k4 the flattened list correponds to indices
(0, 1), (0, 2), (0, 3), (0, 4), ... (11,12).
Changing line 150
indices = sorted([(i, j) for (j, i) in combinations(range(n), r=2)])
to
indices = sorted([(i, j) for (i, j) in combinations(range(n), r=2)])
resolves this issue.
However, I do not know whether the original sorting might be correct for some other instances.
It would be nice to provide functions that can verify any VRP-type solution. Of course, we need to make a verification function for each type of VRP-type problem.
I have no idea yet how the interface will look like. But here's an idea:
from cvrplib import read_instance, read_solution, verify
instance = read_instance(instance_path)
solution = read_solution(solution_path)
# Pass the solution and all relevant instance attributes
verify.cvrp(solution, instance['capacity'])
verify.vrptw(solution, instance['capacity'], instance['duration_matrix'], instance['time_windows'])
There are plenty of descriptions in the literature to generate new instances. See among others:
Would be interesting to provide functions that could do that at some point.
CVRPLIB has an XXL instance set (e.g., http://vrp.atd-lab.inf.puc-rio.br/index.php/en/plotted-instances?data=Flanders2). vrplib
takes very long to parse these large instances due to the distance matrix (size $O(n^2)$). For Brussels1 (11K nodes), it takes about 1 minute on my laptop and 99.9% time is spent on computing the distance matrix.
I think it's unreasonable to compute the distances matrix for such large instances, so we should probably add an argument to disable the computation of distance matrices.
The array-like fields in the Instance
and Solution
classes are current of type List
or List[List]
. I prefer to change this to numpy
arrays, as they provide more useful methods when solving VRP problems. The only hurdle is that I'm not sure how to annotate numpy
arrays in a meaningful way. Maybe refer to this?
Our package currently does not support reading and downloading VRPTW instances, i.e., instances that belong to the Solomon
and Homberger and Gehring
set. Support for this will be in a later version. A new parser function needs to be written for these instances.
The download
and list_instances
functions take several seconds to complete. The parsing of the file itself goes very fast, but receiving the response from the server takes a while. I believe it has to do with the server that hosts the CVRPLIB library. If you know how to improve this, please let me know.
The library currently supports reading and downloading instances from the CVRPLIB library. I'm going to work towards a V1 version of this library to make it support more general VRPLIB instances, particularly those from the LKH-3 library. I think that the LKH-3 and CVRPLIB library are currently the best maintained VRP libraries with a consistent format. Moreover, LKH-3 has way more instances than CVRPLIB, which only has CVRP and VRPTW instances.
New features that I want to introduce:
Some edge cases (mostly about how to support CVRPLIB):
download
feature for now because I think it's quite useful for running quick instances on Colab.read_instance
will take the style
argument which allows to parse different instance formatting styles (i.e., vrplib
and solomon
).Issues
I think it'll be common for users to use the read_instance
function without explicitly specifying instance_format='solomon'
.
What will happen is that the function returns an empty dictionary. There are no verifications (yet) in parse_vrplib
#51, so it might be helpful to issue a warning instead.
The current implementation always rounds the distances to the nearest integer. It might be helpful for the non-integral instances to allow for different rounding functions.
Related issue: #7
I thought it would be helpful to name the package to pycvrplib
to make it apparant that the library is written for Python, but I think that's being overly pedantic. I should rename the package to cvrplib
, just like tsplib95
In the long term, we could make a separate repository to maintain the instances and best known solutions.
We should verify that an instance is in VRPLIB format. I don't know yet what needs to be checked (is there a minimum requirement?), but that's something we can figure out along the way.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.