RouteOpt Core Macros, Constants, and Utilities¶
Overview¶
The file packages/common/config/route_opt_macro.hpp
is a core header of the RouteOpt project.
It defines a set of macros, constants, utility functions, and custom data structures
that standardize operations such as file management, numerical comparisons, timing,
and logging throughout the application. This header is critical for ensuring consistency,
robust error handling, and efficient execution in RouteOpt. All of the packages, i.e.,
Branching, Rank-1 Cuts, Rounded Capacity Cuts, and DeLuxing packages, include this header
somewhere to leverage its functionalities.
License¶
This software is licensed under GPL-3.0.
File Contents¶
The header file includes the following key components:
Constants and Typedefs¶
``MAX_NUM_CUSTOMERS`` Maximum number of customers (used for sizing bitsets).
Note
If you need to solve an instance with more than
MAX_NUM_CUSTOMERS
customers, you must modify this constant. It must be at leastdim + 1
, wheredim
is the instance dimension (i.e., number of customers plus the depot).Tolerance Constants The following constants are used for various numerical comparisons:
``TOLERANCE``: For general floating-point comparisons.
``SOL_X_TOLERANCE``: For LP solution values; determines if a solution qualifies as an integer solution.
``RC_TOLERANCE``: For reduced cost (RC) comparisons; only values smaller than this threshold are considered negative.
``DUAL_TOLERANCE``: For dual values; only differences exceeding this threshold are considered non-zero.
Type Alias: ``routeOptLong`` A fixed-size
std::bitset
used to represent customer routes. It is primarily used to represent Ng-memory [Baldacci et al., 2011] in labeling but can also be applied in cut generation.Separation Line Strings
``SMALL_PHASE_SEPARATION``
``MID_PHASE_SEPARATION``
``BIG_PHASE_SEPARATION``
These strings are used to print visually distinct separation lines in console output.
Custom Data Structures¶
PairHasher
A functor that provides a hash function for a pair of integers, enabling their use as keys in unordered containers. This hash function is primarily used to identify identical edges in a graph, which is beneficial for both branching and RCC package management.
SequenceInfo
A structure that holds:
A vector of customer indices (excluding 0), representing the sequence of customers in a route.
An indicator, forward_concatenate_pos, for indicating the position of the last forward customer in the sequence.
This is one of the most important data structures in RouteOpt. It is widely used to store customer routes, and the extra information in forward_concatenate_pos is essential. It indicates how the route is generated by the bidirectional labeling algorithm, which is crucial for calculating the coefficients of the Rank-1 cuts, especially when
no-symmetry
memory, i.e., arc memory, is used [Pessoa et al., 2020].
Utility Functions and Classes¶
equalFloat: A constexpr function for comparing two double values within a specified tolerance.
checkFrac: Determines whether a number has a fractional component.
mkDir: Creates a directory if it does not already exist using C++17 filesystem operations (So you need to use C++17 to compile the code).
TimeSetter: A templated utility class to measure the execution time of functions.
printTimeMessage and printHeadLines: Functions to print formatted messages to the console.
GlobTimer: A timer class that tracks elapsed time; a global instance
glob_timer
is provided.roundTo: Rounds a floating-point value to a specified number of decimal places.
Logging and Exception Handling¶
SAFE_EIGEN Macro: Safely wraps an Eigen function call in a try-catch block, reporting any errors before exiting.
ErrorType Enumeration: Defines different logging types (ERROR_EXIT, WARNING, DEBUG, REMIND).
logMessage Template Function: Logs messages with contextual details such as file name, line number, and function name.
Convenience macros:
THROW_RUNTIME_ERROR
,PRINT_WARNING
,PRINT_DEBUG
, andPRINT_REMIND
simplify logging with built-in context.
Header Codes¶
/*
* Copyright (c) 2025 Zhengzhong (Ricky) You.
* All rights reserved.
* Software: RouteOpt
* License: GPL-3.0
*/
/*
* @file route_opt_macro.hpp
* @brief Macros, constants, and utility functions for RouteOpt.
*
* This header provides definitions for constants (such as maximum customer count and tolerances),
* utility functions (e.g., for directory creation and timing), custom data structures (e.g., SequenceInfo),
* and logging macros for the RouteOpt project.
*/
#ifndef ROUTE_OPT_ROUTE_OPT_MACRO_HPP
#define ROUTE_OPT_ROUTE_OPT_MACRO_HPP
#include <bitset>
#include <cmath>
#include <vector>
#include <string>
#include <filesystem>
#include <iostream>
#include <iomanip>
#include <sstream>
namespace RouteOpt {
constexpr int MAX_NUM_CUSTOMERS = 1002;
constexpr double TOLERANCE = 1e-6;
constexpr double SOL_X_TOLERANCE = 1e-8;
constexpr double RC_TOLERANCE = -1e-6;
constexpr double DUAL_TOLERANCE = 1e-6;
using routeOptLong = std::bitset<MAX_NUM_CUSTOMERS>;
constexpr auto SMALL_PHASE_SEPARATION =
"-----------------------------------------------------------------------------------------\n";
constexpr auto MID_PHASE_SEPARATION =
"*****************************************************************************************\n";
constexpr auto BIG_PHASE_SEPARATION =
"#########################################################################################\n";
struct PairHasher {
size_t operator()(const std::pair<int, int> &V) const {
return V.first * MAX_NUM_CUSTOMERS + V.second;
}
};
struct SequenceInfo {
std::vector<int> col_seq;
int forward_concatenate_pos{};
bool operator==(const SequenceInfo &other) const {
return col_seq == other.col_seq &
forward_concatenate_pos == other.forward_concatenate_pos;
}
};
constexpr bool equalFloat(double x, double y, double tolerance = TOLERANCE) {
return std::fabs(x - y) < tolerance;
}
constexpr bool checkFrac(double x, double tolerance = TOLERANCE) {
return !equalFloat(std::fmod(x, 1.), 0, tolerance);
}
inline void mkDir(const std::string &path1) {
namespace fs = std::filesystem;
if (fs::exists(path1) && fs::is_directory(path1)) {
std::cout << path1 + " already exists" << std::endl;
} else {
fs::create_directory(path1);
std::cout << path1 + " created" << std::endl;
}
}
struct TimeSetter {
template<typename Func, typename... Args>
static double measure(Func f, Args &&... args) {
auto start = std::chrono::high_resolution_clock::now();
f(std::forward<Args>(args)...);
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration<double>(end - start).count();
}
};
#define SAFE_EIGEN(func) \
try { \
func; \
} catch (const std::exception &e) { \
std::ostringstream oss; \
oss << "\x1b[91mError in file " << __FILE__ \
<< " at line " << __LINE__ << ": " << e.what() << "\x1b[0m"; \
std::cerr << oss.str() << std::endl; \
std::exit(EXIT_FAILURE); \
}
inline void printTimeMessage(const std::string &message, double time) {
std::ios init(nullptr);
init.copyfmt(std::cout);
std::cout << message << std::fixed << std::setprecision(2) << " time= " << time << " s." << std::endl;
std::cout.copyfmt(init);
}
inline void printHeadLines(const std::string &message) {
std::cout << " ****** " << message << " ****** " << std::endl;
}
struct GlobTimer {
std::chrono::time_point<std::chrono::high_resolution_clock> start;
GlobTimer() {
start = std::chrono::high_resolution_clock::now();
}
void report() const {
auto now = std::chrono::high_resolution_clock::now();
double elapsed = std::chrono::duration<double>(now - start).count();
std::cout << SMALL_PHASE_SEPARATION;
printTimeMessage("elapsed", elapsed);
std::cout << SMALL_PHASE_SEPARATION;
}
[[nodiscard]] double getTime() const {
return std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - start).count();
}
};
inline GlobTimer glob_timer{};
inline double roundTo(double value, int decimals = 1) {
double factor = std::pow(10.0, decimals);
return std::round(value * factor) / factor;
}
enum class ErrorType { ERROR_EXIT, WARNING, DEBUG, REMIND };
template<ErrorType type>
void logMessage(const std::string &msg, const char *file, int line, const char *func) {
std::ostringstream oss;
oss << msg << " in function " << func << " at " << file << ":" << line;
if constexpr (type == ErrorType::ERROR_EXIT) {
std::cerr << "\x1b[91m[FATAL ERROR] " << oss.str() << "\x1b[0m" << std::endl;
std::exit(EXIT_FAILURE);
} else if constexpr (type == ErrorType::WARNING) {
std::cerr << "\x1b[1;35m[WARNING] " << oss.str() << "\x1b[0m" << std::endl;
} else if constexpr (type == ErrorType::DEBUG) {
std::cerr << "\x1b[94m[DEBUG] " << oss.str() << "\x1b[0m" << std::endl;
} else if constexpr (type == ErrorType::REMIND) {
std::cout << "\x1b[1;36m[REMIND] " << oss.str() << "\x1b[0m" << std::endl;
}
}
#define THROW_RUNTIME_ERROR(msg) logMessage<ErrorType::ERROR_EXIT>((msg), __FILE__, __LINE__, __func__);
#define PRINT_WARNING(msg) logMessage<ErrorType::WARNING>((msg), __FILE__, __LINE__, __func__);
#define PRINT_DEBUG(msg) logMessage<ErrorType::DEBUG>((msg), __FILE__, __LINE__, __func__);
#define PRINT_REMIND(msg) logMessage<ErrorType::REMIND>((msg), __FILE__, __LINE__, __func__);
} // namespace RouteOpt
#endif // ROUTE_OPT_ROUTE_OPT_MACRO_HPP
Usage Example¶
Below is an example illustrating the use of some utilities defined in route_opt_macro.hpp
:
#include <iostream>
#include "route_opt_macro.hpp"
int main() {
// Create a directory named "output" if it doesn't exist.
RouteOpt::mkDir("output");
// Measure the execution time of a simple loop.
double elapsed = RouteOpt::TimeSetter::measure([](){
for (volatile int i = 0; i < 1000000; ++i);
});
RouteOpt::printTimeMessage("Loop completed", elapsed);
// Report the elapsed time using the global timer.
RouteOpt::glob_timer.report();
// Log a debug message.
PRINT_DEBUG("Debugging the main function.");
// Round a value to 2 decimal places.
double rounded = RouteOpt::roundTo(3.14159, 2);
std::cout << "Rounded value: " << rounded << std::endl;
return 0;
}
Conclusion¶
The route_opt_macro.hpp
header is a foundational component of the RouteOpt project.
It encapsulates a variety of macros, constants, utility functions, and data structures that simplify
common programming tasks, enforce consistency, and enhance error handling and logging. By
centralizing these functionalities, the header promotes maintainable and robust code throughout
the RouteOpt codebase.
Further Reading¶
For more detailed information, please refer to:
Roberto Baldacci, Aristide Mingozzi, and Roberto Roberti. New route relaxation and pricing strategies for the vehicle routing problem. Operations Research, 59(5):1269–1283, 2011.
Artur Pessoa, Ruslan Sadykov, Eduardo Uchoa, and François Vanderbeck. A generic exact solver for vehicle routing and related problems. Mathematical Programming, 183:483–523, 2020.