Skip to content
Snippets Groups Projects
Commit 8b7f287a authored by awenjb's avatar awenjb
Browse files

Initial commit

- project base
- no build (see conan / cmake files)
- no data (to heavy)
parents
No related branches found
No related tags found
No related merge requests found
cmake_minimum_required(VERSION 3.25)
project(pdptw LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# link time optimisation (LTO) for release and release with debug info builds
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
# Finding dependencies
find_package(CLI11 REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(spdlog REQUIRED)
add_executable(pdptw src/main.cpp
src/input/location.cpp
src/input/pdptw_data.cpp
src/input/time_window.cpp
src/input/json_parser.cpp
src/lns/route.cpp
src/lns/solution.cpp
)
target_link_libraries(pdptw PRIVATE CLI11::CLI11 nlohmann_json::nlohmann_json spdlog::spdlog)
\ No newline at end of file
[requires]
spdlog/1.12.0
nlohmann_json/3.11.2
cli11/2.3.2
[generators]
CMakeDeps
CMakeToolchain
# ==========================================================
# Script : txt_to_json.jl
# Description: Read the PDPTW instance (.txt) LL01 of Li and Lim(2003) and SB19 of Sartori and Buriol (2020)
# and convert them into uniform JSON (.json) file
# /!\ Each directory needs to contain only one type of instance (LL01 or SB19) and no other files
#
# ==========================================================
# txt_to_json.jl
# Data_txt/
# | n100/
# | | bar-n100-1.txt
# | | ...
# | pdp_100/
# | | lc101.txt
# | | ...
# Data_json/
using DelimitedFiles
using JSON
using OrderedCollections
using Glob
struct Location
id::Int
longitude::Float64
latitude::Float64
demand::Int
timeWindow::Vector{Int64}
serviceDuration::Int
pairedLocation::Int
locType::String
end
# read LL01 instance and return nb_vehicles, capacity, locations
function read_pdptw_LL01_instance(filename)
open(filename, "r") do file
lines = readlines(file)
first_line = split(strip(lines[1]))
if length(first_line) < 3
error("Error: line 1")
end
capacity = parse(Int, first_line[2])
locations = Location[]
for i in 2:length(lines)
row = split(strip(lines[i]))
if length(row) < 9
error("Error: line $(i)")
end
timeWindow = [parse(Int, row[5]), parse(Int, row[6])]
# depot
if (parse(Int, row[8]) == 0) && (parse(Int, row[9]) == 0)
locType = "DEPOT"
pairedLocation = 0
elseif (parse(Int, row[8]) == 0)
locType = "PICKUP"
pairedLocation = parse(Int, row[9])
elseif (parse(Int, row[9]) == 0)
locType = "DELIVERY"
pairedLocation = parse(Int, row[8])
end
push!(locations, Location(parse(Int, row[1]),
parse(Float64, row[2]),
parse(Float64, row[3]),
parse(Int, row[4]),
timeWindow,
parse(Int, row[7]),
pairedLocation,
locType))
end
return capacity, locations
end
end
# read SB19 instance and return nb_vehicles, capacity, locations, distance_matrix
function read_pdptw_SB19_instance(filename)
open(filename, "r") do file
lines = readlines(file)
size = parse(Int, split(strip(lines[5]))[2])
capacity = parse(Int, split(strip(lines[10]))[2])
locations = Location[]
for i in 12:size+11
row = split(strip(lines[i]))
if length(row) < 9
error("Error: line $(i)")
end
timeWindow = [parse(Int, row[5]), parse(Int, row[6])]
# depot
if (parse(Int, row[8]) == 0) && (parse(Int, row[9]) == 0)
locType = "DEPOT"
pairedLocation = 0
elseif (parse(Int, row[8]) == 0)
locType = "PICKUP"
pairedLocation = parse(Int, row[9])
elseif (parse(Int, row[9]) == 0)
locType = "DELIVERY"
pairedLocation = parse(Int, row[8])
end
push!(locations, Location(parse(Int, row[1]),
parse(Float64, row[2]),
parse(Float64, row[3]),
parse(Int, row[4]),
timeWindow,
parse(Int, row[7]),
pairedLocation,
locType))
end
# travel time taken from a map by Sartori and Buriol
distance_matrix = Array{Float64}(undef, size, size)
# read the distance matrix (better way to do it but that is working...)
cpt = 1
for i in size+13:2*size+12
row = split(strip(lines[i]))
if length(row) < size
error("Error: line $(i)")
end
for j in eachindex(row)
distance_matrix[cpt, j] = parse(Float64, row[j])
end
cpt = cpt+1
end
return size, capacity, locations, distance_matrix
end
end
# given an output directory, a filename and the PDPTW data, create a json file
function save_to_json(output_dir, filename, size, capacity, depot, locations, distance_matrix)
# correction to store the matrix row by row and not column by column in the json file
matrix_row = collect(eachrow(distance_matrix))
data = OrderedDict(
"size" => size,
"capacity" => capacity,
"depot" =>
OrderedDict(
"id" => depot.id,
"longitude" => depot.longitude,
"latitude" => depot.latitude,
"demand" => depot.demand,
"timeWindow" => depot.timeWindow,
"serviceDuration" => depot.serviceDuration,
"pairedLocation" => depot.pairedLocation,
"locType" => depot.locType
)
,
"locations" => [
OrderedDict(
"id" => r.id,
"longitude" => r.longitude,
"latitude" => r.latitude,
"demand" => r.demand,
"timeWindow" => r.timeWindow,
"service_duration" => r.serviceDuration,
"pairedLocation" => r.pairedLocation,
"locType" => r.locType
) for r in locations
],
"distance_matrix" => matrix_row
)
output_file = joinpath(output_dir, replace(basename(filename), ".txt" => ".json"))
open(output_file, "w") do file
write(file, JSON.json(data, 4))
end
end
function euclidean_distance(x1, y1, x2, y2)
return round(sqrt((x2 - x1)^2 + (y2 - y1)^2), digits=2)
end
# Distance matrix
function compute_distance_matrix(locations)
n = length(locations)
distance_matrix = Array{Float64}(undef, n, n)
for i in 1:n
for j in 1:n
distance_matrix[i, j] = euclidean_distance(
locations[i].longitude, locations[i].latitude,
locations[j].longitude, locations[j].latitude
)
end
end
return distance_matrix
end
# convert all the files of a given type of PDPTW instances in input_dir and store them in output_dir
function process_all_files(input_dir, output_dir, type)
if !isdir(output_dir)
mkdir(output_dir)
end
txt_files = glob("*.txt", input_dir)
for file in txt_files
println("Reading $(file)...")
try
if type == "LL01"
capacity, locations = read_pdptw_LL01_instance(file)
size = length(locations)
distance_matrix = compute_distance_matrix(locations) # simple euclidian distance
depot = popfirst!(locations)
save_to_json(output_dir, file, size, capacity, depot, locations, distance_matrix)
end
if type == "SB19"
size, capacity, locations, distance_matrix = read_pdptw_SB19_instance(file)
depot = popfirst!(locations)
save_to_json(output_dir, file, size, capacity, depot, locations, distance_matrix)
end
catch e
println("Error $(file) : ", e)
end
end
println("Success $(input_dir).")
end
function find_all_directory(parent_dir)
return [folder for folder in glob("*/", parent_dir) if isdir(folder)]
end
########################################################
########################################################
# txt_to_json.jl
# Data_txt/
# | n100/
# | | bar-n100-1.txt
# | | ...
# | pdp_100/
# | | lc101.txt
# | | ...
# ...
# Data_json/
data_directory = "Data_txt"
all_directory = find_all_directory(data_directory)
for dir in all_directory
println("--- \n", dir)
if startswith(dir, "Data_txt/pdp_")
input_directory = dir
output_directory = replace(dir, "txt" => "json")
process_all_files(input_directory, output_directory, "LL01")
elseif startswith(dir, "Data_txt/n")
input_directory = dir
output_directory = replace(dir, "txt" => "json")
process_all_files(input_directory, output_directory, "SB19")
else
println("File not compatible")
end
end
#include "json_parser.h"
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <nlohmann/json.hpp>
// spdlog repris du code lns-framework mais pas utilisé ici -> TODO
namespace fs = std::filesystem;
using json = nlohmann::json;
bool checkFilePresence(std::string &filepath)
{
return fs::is_regular_file(filepath);
}
PDPTWData parsing::parseJson(std::string filepath)
{
if (!checkFilePresence(filepath))
{
spdlog::error("Data file \"{}\" does not exist", filepath);
exit(1);
}
std::ifstream jsonFile(filepath);
if (!jsonFile.is_open())
{
spdlog::error("Unable to open file: {}", filepath);
spdlog::default_logger()->flush();
exit(1);
}
try
{
json j;
jsonFile >> j;
return json_to_data(j);
}
catch (std::exception const &e)// catching all exceptions to properly log the error and quit gracefully
{
spdlog::error("Error while parsing the input json:");
spdlog::error("{}", e.what());
spdlog::default_logger()->flush();
exit(1);
}
}
PDPTWData json_to_data(const json& j)
{
unsigned int size = j.at("size").get<unsigned int>();
int capacity = j.at("capacity").get<int>();
auto depot_json = j.at("depot");
TimeWindow tw(depot_json.at("timeWindow").at(0), depot_json.at("timeWindow").at(1));
Location depot(
0,
depot_json.at("longitude"),
depot_json.at("latitude"),
0,
tw,
depot_json.at("serviceDuration"),
0,
LocType::DEPOT
);
std::vector<Location> locations;
for (const auto& loc : j.at("locations"))
{
LocType loc_type = (loc.at("locType") == "PICKUP") ? LocType::PICKUP : LocType::DELIVERY;
TimeWindow loc_tw(loc.at("timeWindow").at(0), loc.at("timeWindow").at(1));
locations.emplace_back(
loc.at("id"),
loc.at("longitude"),
loc.at("latitude"),
loc.at("demand"),
loc_tw,
loc.at("serviceDuration"),
loc.at("pairedLocation"),
loc_type
);
}
Matrix distance_matrix = j.at("distance_matrix").get<Matrix>();
return PDPTWData(size, capacity, depot, locations, distance_matrix);
}
#pragma once
#include "pdptw_data.h"
#include <string>
#include <spdlog/spdlog.h>
namespace parsing
{
PDPTWData parseJson(std::string filepath);
}
PDPTWData json_to_data(const json& j);
\ No newline at end of file
#include "location.h"
Location::Location(unsigned int id, double lon, double lat, int dem, TimeWindow tw, double service, unsigned int pairId, LocType type)
: id(id), longitude(lon), latitude(lat), demand(dem), timeWindow(tw), serviceDuration(service), pairedLocation(pairId), locType(type) {}
double Location::getLongitude() const {
return longitude;
}
double Location::getLatitude() const {
return latitude;
}
double Location::getServiceDuration() const {
return serviceDuration;
}
unsigned int Location::getId() const {
return id;
}
unsigned int Location::getPair() const {
return pairedLocation;
}
int Location::getDemand() const {
return demand;
}
LocType Location::getLocType() const {
return locType;
}
TimeWindow Location::getTimeWindow() const {
return timeWindow;
}
std::string Location::LocTypeToString(LocType type) const
{
switch (type) {
case LocType::DEPOT: return "Depot";
case LocType::DELIVERY: return "Delivery";
case LocType::PICKUP: return "Pickup";
default: return "Unknown";
}
}
void Location::print() const
{
std::cout << "Location ID: " << id << ", Coordinates: (" << longitude << ", " << latitude << ")\n";
std::cout << "Location Type : " << Location::LocTypeToString(locType) << ", Associated location : " << pairedLocation << "\n";
std::cout << "Demand : " << demand << "\n";
timeWindow.print();
}
\ No newline at end of file
#pragma once
#include<string>
#include <optional>
#include <iostream>
#include <vector>
#include "time_window.h"
/**
* A type of location
*/
enum class LocType
{
DEPOT,
PICKUP,
DELIVERY
};
/**
* Represent a location in space combined with a type and a time window
* @see Depot, Pickup, Delivery
*/
class Location
{
unsigned int id;
double longitude;
double latitude;
int demand;
double serviceDuration;
unsigned int pairedLocation;
LocType locType;
TimeWindow timeWindow;
public:
Location(unsigned int id, double lon, double lat, int dem, TimeWindow tw, double service, unsigned int pairId, LocType type);
double getLongitude() const;
double getLatitude() const;
double getServiceDuration() const;
unsigned getId() const;
unsigned int getPair() const;
int getDemand() const;
LocType getLocType() const;
TimeWindow getTimeWindow() const;
void print() const;
std::string LocTypeToString(LocType type) const;
// Json parsing
friend void from_json(nlohmann::json const &json, Location &location);
};
\ No newline at end of file
#include "pdptw_data.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <nlohmann/json.hpp>
unsigned int PDPTWData::getSize()
{
return size;
}
int PDPTWData::getCapacity()
{
return capacity;
}
std::vector<Location> const &PDPTWData::getLocations() const {
return locations;
}
Location const PDPTWData::getDepot() const{
return depot;
}
PDPTWData::PDPTWData(unsigned int size, int capacity, Location depot, std::vector<Location> location, Matrix distance_matrix)
: size(size), capacity(capacity), depot(depot), locations(std::move(location)), distance_matrix(std::move(distance_matrix)) {}
void PDPTWData::print() const {
std::cout << "Instance size: " << size << "\n";
std::cout << "Capacity: " << capacity << "\n";
std::cout << "Depot:\n";
depot.print();
std::cout << "Locations:\n";
for (const auto& loc : locations) {
loc.print();
}
std::cout << "Distance Matrix:\n";
for (const auto& row : distance_matrix) {
for (const auto& dist : row) {
std::cout << dist << " ";
}
std::cout << "\n";
}
}
\ No newline at end of file
#pragma once
#include "location.h"
#include <functional>
#include <nlohmann/json_fwd.hpp>
#include <vector>
using json = nlohmann::json;
using Matrix = std::vector<std::vector<double>>;
/**
* Throw this exception after errors in the input has been found.
*/
class InputJsonException : public std::exception
{
std::string reason;
public:
explicit InputJsonException(std::string_view reason);
char const *what() const noexcept override;
};
class PDPTWData
{
unsigned int size;
int capacity;
Location depot;
std::vector<Location> locations;
Matrix distance_matrix;
public:
PDPTWData(PDPTWData const &rhs) = delete;
PDPTWData(PDPTWData &&rhs) noexcept;
PDPTWData &operator=(PDPTWData &&rhs) noexcept;
PDPTWData &operator=(PDPTWData const &rhs) = delete;
/**
* Constructs an empty PDPTWData.
* @see parsing::parseJson
*/
PDPTWData(unsigned int size, int capacity, Location depot, std::vector<Location> requests, Matrix distance_matrix);
/**
* Checks some data coherence
*/
void checkData() const;
std::vector<Location> const &getLocations() const;
Location const getDepot() const;
unsigned int getSize();
int getCapacity();
void print() const;
};
#include "time_window.h"
TimeWindow::TimeWindow(TimeInteger s, TimeInteger e) : start(s), end(e) {}
TimeInteger TimeWindow::getStart() const
{
return start;
}
TimeInteger TimeWindow::getEnd() const
{
return end;
}
TimeInteger TimeWindow::getWidth() const
{
return std::max<TimeInteger>(0, getEnd() - getStart());
}
bool TimeWindow::isValid(TimeInteger t) const
{
return t <= getEnd();
}
bool TimeWindow::isIn(TimeInteger t) const
{
return start <= t && t <= end;
}
void TimeWindow::print() const
{
std::cout << "Time Window : [" << start << ", " << end << "] \n";
}
/*
TimeInteger TimeWindow::waitingTimeBefore(TimeInteger t) const
{
return std::max<TimeInteger>(0, t - start);
}
*/
#pragma once
#include <iostream>
#include <limits>
#include <nlohmann/json_fwd.hpp>
/**
* A point in time or a duration.
*/
using TimeInteger = double;
TimeInteger constexpr UNDEF_TIMESTAMP = std::numeric_limits<TimeInteger>::max();
/**
* Represents a time window [start, end] with some basic utilities.
*/
class TimeWindow
{
TimeInteger start, end;
public:
TimeWindow(TimeInteger s, TimeInteger e);
TimeInteger getStart() const;
TimeInteger getEnd() const;
TimeInteger getWidth() const;
//Checks whether the time t is inside the time window
bool isIn(TimeInteger t) const;
//Checks whether the time t is inside the time window OR before
bool isValid(TimeInteger t) const;
/**
* return the time to wait from t to the start of this time window.
* @return 0 if t is after start, or start - t
*/
//TimeInteger waitingTimeBefore(TimeInteger t) const;
void print() const;
friend void from_json(nlohmann::json const &json, TimeWindow &tw);
};
\ No newline at end of file
#include "route.h"
Route::Route(std::vector<int> route, int cost) : route(route), cost(cost) {}
int Route::getCost() const
{
return cost;
}
const std::vector<int>& Route::getRoute() const
{
return route;
}
\ No newline at end of file
#pragma once
#include <vector>
/**
* Represent a route for the PDPTW
*/
class Route
{
private:
std::vector<int> route;
int cost;
public:
Route(std::vector<int> route, int cost);
int getCost() const;
const std::vector<int>& getRoute() const;
};
\ No newline at end of file
#include "solution.h"
Solution::Solution(RequestBank bank, std::vector<Route> routes, int totalCost)
: bank(bank), routes(routes), totalCost(totalCost) {}
const std::vector<int> & Solution::getBank() const
{
return bank;
}
const std::vector<Route> & Solution::getRoute() const
{
return routes;
}
int Solution::getCost()
{
return totalCost;
}
\ No newline at end of file
#pragma once
#include <vector>
#include "route.h"
/**
* Represent a solution of PDPTW
*/
class Solution
{
public:
using RequestBank = std::vector<int>;
private:
RequestBank bank;
std::vector<Route> routes;
int totalCost;
public:
Solution(RequestBank bank, std::vector<Route> routes, int totalCost);
const RequestBank & getBank() const;
const std::vector<Route> & getRoute() const;
int getCost();
};
\ No newline at end of file
#include <nlohmann/json.hpp>
#include <vector>
#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
#include <spdlog/spdlog.h>
#include "input/location.h"
#include "input/time_window.h"
#include "input/json_parser.h"
namespace fs = std::filesystem;
using json = nlohmann::json;
int main(int argc, char const *argv[])
{
/* code */
/* test */
TimeWindow tw = TimeWindow(3,4);
Location pos = Location(1, 1, 20, 3, tw, 4, 5, LocType::DEPOT);
// test parser
std::string filepath = "/home/a24jacqb/Documents/Code/pdptw-main/data_in/test_inst.json";
std::cout << filepath << "\n";
PDPTWData data = parsing::parseJson(filepath);
data.print();
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment