// ch13_ant_colony.cpp // Companion code for Chapter 13: Optimization and Data Fitting. // Standalone C++11; standard library only; console output without plotting. // Compile: c++ -std=c++11 -O2 -Wall -Wextra -pedantic ch13_ant_colony.cpp -o ch13_ant_colony // Edit the example settings in main(); routines above main() can be reused. // Fixed seeds reproduce runs with the same standard-library implementation. // Random distributions need not give identical sequences across compilers/languages. #include #include #include #include #include #include #include #include #include #include #include #include #include using Vec = std::vector; using Mat = std::vector; using Function = std::function; using ScalarFunction = std::function; const double pi = std::acos(-1.0); void require(bool condition, const std::string& message) { if (!condition) throw std::invalid_argument(message); } void finite_vector(const Vec& x) { require(!x.empty(), "A nonempty vector is required."); for (double v : x) require(std::isfinite(v), "Data must be finite."); } void check_bounds(const Vec& lo, const Vec& hi) { finite_vector(lo); finite_vector(hi); require(lo.size() == hi.size(), "Bounds must have matching dimensions."); for (std::size_t j=0; jlo[j] && std::isfinite(hi[j]-lo[j]), "Require finite upper > lower."); } double objective_value(double value) { require(std::isfinite(value), "Objective must return a finite real scalar."); return value; } double clip(double x, double lo, double hi) { return std::max(lo,std::min(hi,x)); } Vec linspace(double a, double b, int n) { require(n>=2, "Grid needs at least two points."); Vec x(n); for (int i=0; i uniform{0.0,1.0}; std::normal_distribution normal{0.0,1.0}; explicit Random(unsigned int seed) : engine(seed) {} double u() { return uniform(engine); } double z() { return normal(engine); } int index(int n) { return std::uniform_int_distribution(0,n-1)(engine); } }; struct Result { Vec point; double value=0.0; long long evaluations=0; int iterations=0; std::string status; }; void print_result(const Result& r) { std::cout << std::setprecision(12); for (std::size_t j=0; j tour; double length; long long evaluations; int iterations; }; // D is symmetric with positive off-diagonal distances. Indices are zero based. TourResult ant_colony(const Mat& D, Random& rng, int ants, int iterations, double alpha, double beta, double rho, double Q, double tau0, double elite) { int n=static_cast(D.size()); require(n>=3 && ants>0 && iterations>0 && std::isfinite(alpha+beta+Q+tau0+elite) && alpha>=0 && beta>=0 && elite>=0 && Q>0 && tau0>0 && rho>0 && rho<1, "Invalid ant-colony settings."); for (const Vec& row:D) require(row.size()==D.size(), "Distances must form a square matrix."); for (int i=0;i0), "Distances must be symmetric, zero on diagonal, and positive otherwise."); Mat tau(n,Vec(n,tau0)); TourResult r; r.length=std::numeric_limits::infinity(); auto deposit=[&](const std::vector& tour,double amount) { for (int j=0;j> tours; Vec lengths; for (int m=0;m tour{rng.index(n)}; std::vector visited(n,false); visited[tour[0]]=true; while (static_cast(tour.size()) available; Vec logw; for (int j=0;j0, "Tour-length overflow; rescale distances."); if (length::min()); } } std::rotate(r.tour.begin(),std::find(r.tour.begin(),r.tour.end(),0),r.tour.end()); r.evaluations=static_cast(ants)*iterations; r.iterations=iterations; return r; } int main() { try { const unsigned int seed = 7; Random rng(seed); // Alternative: comment out the previous line and seed from system entropy. // Random rng(std::random_device{}()); Mat xy={{.10,.20},{.85,.15},{.65,.75},{.10,.70},{.55,.70},{.60,.35}, {.20,.55},{.80,.50},{.42,.12},{.50,.50},{.35,.35},{.40,.60}}; int n=static_cast(xy.size()); Mat D(n,Vec(n,0)); for (int i=0;i "; std::cout << r.tour[0]+1 << "\nTours evaluated: " << r.evaluations << "\nIterations: " << r.iterations << "\nStatus: Search budget completed.\nRepeat with other seeds; global optimality is not guaranteed.\n"; return 0; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << '\n'; return 1; } }