#include "lns.h" #include "lns/acceptance/acceptance_function.h" #include "lns/operators/selector/operator_selector.h" #include "output/solution_checker.h" #include <chrono> namespace { bool isBetterSolution(Solution const &candidateSolution, Solution const &bestKnownSol) { return candidateSolution.getCost() < bestKnownSol.getCost(); } using lns_clock = std::chrono::high_resolution_clock; using lns_time_point = std::chrono::time_point<lns_clock, std::chrono::nanoseconds>; struct LnsRuntimeData { Solution bestSolution; unsigned int numberOfIteration = 0; lns_time_point start = lns_clock::now(); }; /** * @return the number of seconds between point and now (the moment the function is called). */ unsigned long getTimeSinceInSec(lns_time_point point) { return std::chrono::duration_cast<std::chrono::seconds>(lns_clock::now() - point).count(); } unsigned long getTimeSinceInMs(lns_time_point point) { return std::chrono::duration_cast<std::chrono::milliseconds>(lns_clock::now() - point).count(); } bool triggerLogProgress(LnsRuntimeData const &) { static auto lastTrigger = lns_clock::now(); if (getTimeSinceInSec(lastTrigger) >= 10) { lastTrigger = lns_clock::now(); return true; } return false; } void logProgress(LnsRuntimeData const &runtime, Solution const &actualSolution) { if (triggerLogProgress(runtime)) { unsigned long actualTime = getTimeSinceInMs(runtime.start); double iterPerSecond = 1000 * runtime.numberOfIteration / static_cast<double>(actualTime); std::string speedLog; if (iterPerSecond < 1) { speedLog = fmt::format("{}s/100 iterations", 100 / iterPerSecond); } else { speedLog = fmt::format("{:.1f} i/s", iterPerSecond); } std::string missingRequestLog; long requestsMissing = runtime.bestSolution.missingPairCount(); //... log TO DO } } }// namespace output::LnsOutput lns::runLns(Solution const &initialSolution, OperatorSelector &opSelector, AcceptanceFunction const &acceptFunctor) { /** * The solution is the base solution used to create the neighbor solution. * It is constant unless we accept the candidate solution. */ Solution actualSolution = initialSolution; LnsRuntimeData runtime = {actualSolution}; // temporary fixed iteration int iterationMax = 200; while (iterationMax > 0) { // Init iteration ++runtime.numberOfIteration; logProgress(runtime, actualSolution); std::cout << "\n" << runtime.numberOfIteration << " : "; /** * The solution on which we apply the operators. * It is discarded at the end of each loop if it is not accepted by the Acceptance Function. */ Solution candidateSolution = actualSolution; // Chose operator pair auto destructReconstructPair = opSelector.getOperatorPair(); // Apply operators destructReconstructPair.destructor().destroySolution(candidateSolution); destructReconstructPair.reconstructor().reconstructSolution(candidateSolution, 0.01); candidateSolution.computeAndStoreSolutionCost(); // Update best solution if (isBetterSolution(candidateSolution, runtime.bestSolution)) { std::cout << "\n > new Best Solution \n"; checker::checkAll(candidateSolution, candidateSolution.getData(), false); runtime.bestSolution = candidateSolution; opSelector.betterSolutionFound(); } // Check if we use the candidate solution as the new actual solution // operator can force to take the new solution if (destructReconstructPair.forceTakeSolution() || acceptFunctor(candidateSolution, actualSolution, runtime.bestSolution) == AcceptationStatus::ACCEPT) { actualSolution = std::move(candidateSolution); } --iterationMax; } auto result = output::LnsOutput( std::move(runtime.bestSolution), runtime.numberOfIteration, getTimeSinceInSec(runtime.start)); return result; }