27 #include <unordered_map>
28 #include "SolverConfig.h"
36 static random_device rd;
40 uint32_t maxStaticActivities = 0u, maxLocations = 0u;
41 for (
Robot* r : l.robots()) {
42 uint32_t numberOfStaticActivities = 0u, numberOfLocations = 0u, numberOfPowerModes = r->powerModes().size();
43 for (
Activity *a : r->activities()) {
46 uint32_t numberOfActivityLocations = sa->locations().size();
47 maxLocations = max(maxLocations, numberOfActivityLocations);
48 numberOfLocations += numberOfActivityLocations;
49 ++numberOfStaticActivities;
53 maxStaticActivities = max(maxStaticActivities, numberOfStaticActivities);
75 throw_with_nested(
SolverException(caller(),
"Cannot get the solution, an exception occurred!"));
89 time_point<system_clock, duration<double>> startTime = high_resolution_clock::now();
90 time_point<system_clock, duration<double>> expectedFinishTime = startTime+duration<double>(
Settings::MAX_RUNTIME);
97 double initializationTime = duration_cast<duration<double>>(high_resolution_clock::now()-startTime).count();
99 clog<<endl<<
"Initial phase: "<<initializationTime<<
" s"<<endl;
105 vector<thread> threads;
111 duration<double> remainingTimeToWait = expectedFinishTime-high_resolution_clock::now();
112 if (remainingTimeToWait > timeToNextWakeUp)
113 remainingTimeToWait = timeToNextWakeUp;
115 this_thread::sleep_for(remainingTimeToWait);
117 mRuntime = duration_cast<duration<double>>(high_resolution_clock::now()-startTime).count();
126 }
catch (exception& e) {
134 for (thread& th : threads)
138 mRuntime = duration_cast<duration<double>>(high_resolution_clock::now()-startTime).count();
141 cout<<
"Total time: "<<
mRuntime<<
" s"<<endl;
145 string bigMessage =
"\n";
146 for (uint32_t m = 0; m < msgs.size(); ++m) {
147 bigMessage += msgs[m];
148 if (m+1 < msgs.size())
149 bigMessage +=
"\n\n";
159 const uint32_t numOfTuplesBatch = 50u;
166 vector<vector<Location*>> fixed;
167 Algo selAlgo = LP_INITIAL_PROBLEM;
186 selAlgo = POWER_MODE_HEURISTIC;
189 bool readNextTuple =
false, solutionChanged =
false;
191 constexpr uint32_t locHeurLength = 3, pwrmHeurLength = 5;
192 uint32_t numberOfIter = 0, infeasibilityConsecutiveCounter = 0;
193 double energyDiffEstimation = 0.0, bestCriterion = timing.
totalEnergy;
197 case POWER_MODE_HEURISTIC:
200 case LOCATION_CHANGE_HEURISTIC:
206 imprLocChg.clear(); imprPwrmChg.clear();
211 bool solutionInfeasible =
false;
212 double relativeImprovement = 0.0, heurRelEstError = 0.0;
216 bestCriterion = min(bestCriterion, timing.
totalEnergy);
217 double energyDiffExact = previousCriterion-timing.
totalEnergy;
219 prevPartSol = ps; prevOptTiming = timing;
220 imprLP.
addValue(relativeImprovement);
222 if (abs(energyDiffExact) > F64_EPS)
223 heurRelEstError = abs(energyDiffEstimation-energyDiffExact)/abs(energyDiffExact);
225 infeasibilityConsecutiveCounter = 0u;
229 solutionInfeasible =
true;
230 ps = prevPartSol; timing = prevOptTiming;
231 if (infeasibilityConsecutiveCounter >= 4)
232 readNextTuple =
true;
234 ++infeasibilityConsecutiveCounter;
238 case POWER_MODE_HEURISTIC:
240 imprPwrmChg.addValue(relativeImprovement);
241 if ((imprPwrmChg.filled() && imprPwrmChg.average() <
CRITERION_RTOL) || !solutionChanged || solutionInfeasible)
242 selAlgo = LOCATION_CHANGE_HEURISTIC;
244 case LOCATION_CHANGE_HEURISTIC:
246 imprLocChg.addValue(relativeImprovement);
247 if ((imprLocChg.filled() && imprLocChg.average() <
CRITERION_RTOL) || !solutionChanged || solutionInfeasible)
248 selAlgo = PATH_CHANGE;
251 selAlgo = POWER_MODE_HEURISTIC;
262 }
catch (exception& e) {
269 vector<StaticActivity*> idToActivity;
270 map<StaticActivity*, uint32_t> ident;
273 for (
Activity* a : r->activities()) {
276 setValue(ident, sa,
id++, caller());
277 idToActivity.push_back(sa);
279 if (sa->lastInCycle())
284 assert(home !=
nullptr &&
"Invalid checking of the correctness of instances!");
286 uint32_t startId =
id++, endId = getValue(ident, home, caller());
287 idToActivity.push_back(home);
289 vector<Edge> graphEdges;
290 for (
Activity *s : home->successors()) {
292 assert(da !=
nullptr &&
"Logic error, the successor of the static activity must be a dynamic activity!");
295 graphEdges.push_back({home, to, startId, getValue(ident, to, caller()), home->minAbsDuration()+da->minAbsDuration()});
299 for (
Activity* a : r->activities()) {
301 if (da !=
nullptr && da->predecessor() != home) {
303 graphEdges.push_back({from, to, getValue(ident, from, caller()), getValue(ident, to, caller()), from->minAbsDuration()+da->minAbsDuration()});
307 vector<vector<Edge>> idToSucs(idToActivity.size());
308 for (
const Edge& e : graphEdges) {
309 assert(e.i < idToActivity.size() &&
"Invalid generation of identifications!");
310 idToSucs[e.i].push_back(e);
313 return {graphEdges, startId, endId, idToActivity, idToSucs};
320 assert(e.
i < matrixSize && e.
j < matrixSize &&
"Invalid creation of the graph!");
324 for (uint32_t i = 0; i < matrixSize; ++i)
332 bool allFound =
false;
333 set<vector<uint32_t>> forbiddenPaths;
334 default_random_engine threadGenerator(rd());
335 vector<CircuitRecord> shortestAlternatives;
336 double cycleTime =
mLine.productionCycleTime();
341 while (allFound !=
true && numOfSol < maxCircuits &&
mFeasible) {
343 set<uint32_t> visited;
344 vector<double> edgeDistances;
346 vector<StaticActivity*> path = { home };
347 vector<uint32_t> nodePath = { currentNode };
349 while ((currentNode != g.
endIdent || nodePath.size() != numOfNodes) && allFound ==
false) {
351 bool backtrack =
false;
354 double pathLength = accumulate(edgeDistances.cbegin(), edgeDistances.cend(), 0.0);
357 for (uint32_t n = 0; n < numOfNodes && !backtrack; ++n) {
358 if (visited.count(n) == 0 && pathLength+m[currentNode][n]+m[n][g.
endIdent] > cycleTime)
362 if (backtrack ==
false) {
364 vector<Edge> feasEdges;
366 nodePath.push_back(e.j);
368 if (visited.count(e.j) == 0 && forbiddenPaths.count(nodePath) == 0)
369 feasEdges.push_back(e);
374 if (!feasEdges.empty()) {
376 uniform_int_distribution<uint32_t> edgeSelection(0, feasEdges.size()-1);
377 const Edge& edge = feasEdges[edgeSelection(threadGenerator)];
378 path.push_back(edge.t); nodePath.push_back(edge.j);
379 edgeDistances.push_back(edge.length);
380 visited.insert(edge.i); currentNode = edge.j;
388 if (backtrack ==
true) {
389 assert(path.size() == nodePath.size() &&
"Expected the same size! A bug in the algorithm.");
390 if (nodePath.size() >= 2) {
391 const vector<uint32_t>& prefixPath = nodePath;
392 auto p = forbiddenPaths.emplace(nodePath.cbegin(), nodePath.cend());
393 set<vector<uint32_t>>::iterator it = p.first, eraseStart = ++it, eraseStop = eraseStart;
394 while (it != forbiddenPaths.end()) {
395 const vector<uint32_t>& nextPath = *it;
396 if (prefixPath.size() < nextPath.size()) {
397 const auto& mitp = mismatch(prefixPath.cbegin(), prefixPath.cend(), nextPath.cbegin());
398 if (mitp.first == prefixPath.end())
406 forbiddenPaths.erase(eraseStart, eraseStop);
407 visited.erase(nodePath.back());
408 path.pop_back(); nodePath.pop_back(); edgeDistances.pop_back();
409 currentNode = nodePath.back();
423 shortestAlternatives.emplace_back(move(sc), move(cl));
427 forbiddenPaths.insert(nodePath);
431 return shortestAlternatives;
436 double bestDist = F64_INF;
437 vector<vector<int64_t>> bestPreds;
438 vector<vector<Location*>> bestLocs;
439 uint32_t closedPathSize = circuit.size();
440 double cycleTime =
mLine.productionCycleTime();
441 const double penaltyForViolation = cycleTime+1.0;
443 assert(circuit.size() >= 2 &&
"Unexpected empty path! Invalid algorithm!");
445 vector<StaticActivity*>::const_iterator bestStartIter = min_element(circuit.cbegin(), circuit.cend(),
447 return sa1->locations().size() < sa2->locations().size();
451 vector<StaticActivity*> closedPath;
452 closedPath.reserve(closedPathSize);
454 if (startAndEndAct != circuit.front()) {
455 closedPath.insert(closedPath.end(), bestStartIter, circuit.cend());
456 closedPath.insert(closedPath.end(), circuit.cbegin()+1, bestStartIter+1);
458 closedPath.insert(closedPath.end(), circuit.cbegin(), circuit.cend());
461 assert(closedPath.size() == circuit.size() &&
"Invalid rotation of the circuit, a bug in the algorithm!");
463 for (
Location* startAndEnd : startAndEndAct->locations()) {
465 mDist[threadId][0][0] = 0.0;
466 mPreds[threadId][0][0] = -1;
467 mLocs[threadId][0][0] = startAndEnd;
469 for (uint32_t i = 0; i+1 < closedPathSize; ++i) {
471 uint32_t fromSize = 1u, toSize = 1u;
472 Location **from = &startAndEnd, **to = &startAndEnd;
473 double staticActDur = closedPath[i]->minAbsDuration();
475 fromSize = closedPath[i]->mLocations.size();
476 from = closedPath[i]->mLocations.data();
479 if (i+2 < closedPathSize) {
480 toSize = closedPath[i+1]->mLocations.size();
481 to = closedPath[i+1]->mLocations.data();
484 pair<Location*, Location*> edge;
485 fill(
mDist[threadId][i+1].begin(),
mDist[threadId][i+1].begin()+toSize, F64_INF);
487 for (uint32_t f = 0; f < fromSize; ++f) {
488 edge.first = from[f];
489 double distance =
mDist[threadId][i][f]+staticActDur;
490 const auto& sit = find_if(fixed.cbegin(), fixed.cend(), [&](
Location *l) {
return l->parent() == edge.first->parent(); });
491 if (sit != fixed.cend() && *sit != edge.first)
492 distance += penaltyForViolation;
494 for (uint32_t t = 0; t < toSize; ++t) {
499 double newDist = distance+mv->minDuration();
500 if (newDist <
mDist[threadId][i+1][t]) {
501 mDist[threadId][i+1][t] = newDist;
502 mLocs[threadId][i+1][t] = to[t];
503 if (writeCircuit ==
true)
504 mPreds[threadId][i+1][t] = f;
511 double circuitLength =
mDist[threadId][closedPathSize-1][0];
512 if (circuitLength < bestDist) {
513 bestDist = circuitLength;
514 if (writeCircuit ==
true) {
515 bestPreds =
mPreds[threadId];
516 bestLocs =
mLocs[threadId];
524 if (writeCircuit ==
true && bestDist < F64_INF) {
526 vector<Location*> locs;
527 int64_t currentPred = 0;
528 locs.reserve(closedPathSize);
529 for (int64_t i = closedPathSize-1; i >= 0; --i) {
530 locs.push_back(bestLocs[i][currentPred]);
531 currentPred = bestPreds[i][currentPred];
532 assert((currentPred != -1 || i == 0) &&
"Cannot reconstruct the path! A bug in the algorithm!");
535 assert(locs.size() == closedPathSize &&
"Invalid reconstruction of the path! Please report a bug.");
537 reverse(locs.begin(), locs.end());
538 shortestCircuit.
circuit = move(locs);
542 return shortestCircuit;
546 const vector<Robot*>& robots =
mLine.robots();
547 uint32_t numberOfRobots = robots.size();
548 double cycleTime =
mLine.productionCycleTime();
549 constexpr
double tuplesPer1000s = 1000000.0;
550 const uint32_t maxAlternativesPerRobot = 4*ceil(pow(tuplesPer1000s, 1.0/numberOfRobots));
551 vector<vector<CircuitRecord>> shortestCircuits(numberOfRobots);
554 #pragma omp parallel num_threads(Settings::NUMBER_OF_THREADS)
560 #pragma omp for schedule(dynamic)
561 for (uint32_t r = 0; r < numberOfRobots; ++r) {
567 sort(allCircuits.begin(), allCircuits.end());
569 if (!allCircuits.empty()) {
570 shortestCircuits[r].reserve(maxAlternativesPerRobot+2);
571 if (allCircuits.size() > maxAlternativesPerRobot+2) {
573 double minCycleTime = allCircuits.front().hc.minLength;
574 double shortestAmount = 0.2+0.6*(
penaltyFunction(minCycleTime, cycleTime)/10.0);
575 uint32_t numOfShortest = ceil(maxAlternativesPerRobot*shortestAmount);
576 shortestCircuits[r].insert(shortestCircuits[r].end(), allCircuits.cbegin(), allCircuits.cbegin()+numOfShortest);
579 default_random_engine threadGenerator(rd());
580 uint32_t numOfRandom = ceil((1.0-shortestAmount)*maxAlternativesPerRobot);
581 shuffle(allCircuits.begin()+numOfShortest, allCircuits.end(), threadGenerator);
582 shortestCircuits[r].insert(shortestCircuits[r].end(), allCircuits.begin()+numOfShortest, allCircuits.begin()+numOfShortest+numOfRandom);
585 sort(shortestCircuits[r].begin(), shortestCircuits[r].end());
588 shortestCircuits[r] = allCircuits;
596 vector<Location*> fixed;
597 for (uint32_t l = 0; l+1 < cr.hc.circuit.size(); ++l) {
600 fixed.push_back(loc);
603 cr.sc.fixedLocations = move(fixed);
606 precalculatedCircuits.emplace(cr.sc, cr.hc);
615 uint32_t r11 = get<0>(t1), r12 = get<1>(t1);
616 uint32_t r21 = get<0>(t2), r22 = get<1>(t2);
617 double ct11 = shortestCircuits[r11].front().hc.minLength, ct12 = shortestCircuits[r12].front().hc.minLength;
618 double ct21 = shortestCircuits[r21].front().hc.minLength, ct22 = shortestCircuits[r22].front().hc.minLength;
621 return penalty1 > penalty2;
626 return shortestCircuits;
632 constexpr
double k1 = 10.0/99.0, k2 = 100.0/99.0;
633 if (minCycle <= demandedCycleTime)
634 penalty = k1/(k2-(minCycle/demandedCycleTime));
643 if (alternatives.empty())
646 default_random_engine threadGenerator(rd());
647 double cycleTime =
mLine.productionCycleTime();
648 uint32_t selIdx = 0, numberOfAlternatives = alternatives.size();
650 const double a = ha.
minLength/cycleTime, b = hb.minLength/cycleTime, minDiff =
TIME_TOL/cycleTime;
652 if (b-a >= minDiff) {
654 constexpr
double l = 2.0;
655 const double k = 2.0/exp(l);
656 const double sa = 2*a-(k/l)*exp(l*a);
657 const double sb = 2*b-(k/l)*exp(l*b);
658 const double n = 1/(sb-sa);
661 uniform_real_distribution<double> area(0.0, 1.0);
662 const double s = area(threadGenerator);
670 uint32_t iter = 0, maxIter = 10;
671 double c = s/n+sa, x0 = a+s*(b-a), x = x0;
674 double diff = k*exp(l*x)-2.0;
675 if (abs(diff) > 10.0*F64_EPS)
676 x = x-((k/l)*exp(l*x)-2*x+c)/diff;
679 }
while (x >= a && x <= b && ++iter < maxIter);
681 if (x < a || x > b) {
687 const double selIdxFloat = (x-a)/(b-a);
689 selIdx =
static_cast<uint32_t
>(selIdxFloat*numberOfAlternatives);
690 selIdx = (selIdx < numberOfAlternatives) ? selIdx : numberOfAlternatives-1;
693 uniform_int_distribution<uint32_t> selAlt(0, numberOfAlternatives-1);
694 selIdx = selAlt(threadGenerator);
703 const uint64_t& idx = find_if(fixed.cbegin(), fixed.cend(), [&toFix](
Location* l) {
return toFix->parent() == l->parent(); })-fixed.cbegin();
704 if (idx < fixed.size()) {
707 if (previous != toFix) {
711 sc.
circuit = move(alternative);
714 const auto& sit = precalculatedCircuits.find(sc);
715 if (sit != precalculatedCircuits.cend()) {
718 alternative = move(sc.
circuit);
719 return (sit->second).minLength;
727 return (*p.first).second.minLength;
731 return currentCycleTime;
734 throw InvalidArgument(caller(),
"Invalid toFix argument, it should belong to the related robot!");
741 high_resolution_clock::time_point start = high_resolution_clock::now();
743 default_random_engine threadGenerator(rd());
744 uint32_t numberOfAddedTuples = 0, iter = 0;
745 double cycleTime =
mLine.productionCycleTime();
746 uint32_t numberOfRobots =
mLine.robots().size();
747 const uint32_t maxTuples = numOfTuples, maxIter = 10*maxTuples;
749 while (numberOfAddedTuples < maxTuples && iter < maxIter) {
752 vector<double> currentLengths(numberOfRobots);
753 vector<CircuitRecord*> selectedCircuits(numberOfRobots);
754 vector<vector<Location*>> currentFixedLocations(numberOfRobots);
757 for (uint32_t r = 0; r < numberOfRobots; ++r) {
761 selectedCircuits[r] = cr;
765 bool feasibleTuple =
true;
770 uint32_t r1 = get<0>(*it), r2 = get<1>(*it);
771 const vector<pair<Location*, Location*>>& spatialCompatibility = get<4>(*it);
772 assert(!spatialCompatibility.empty() &&
"Invalid initialization of spatialCompatibility!");
775 double maxPenalty = 0.0;
776 vector<double> preferences;
777 vector<pair<double, double>> lengths;
778 lengths.reserve(spatialCompatibility.size());
779 preferences.reserve(spatialCompatibility.size());
781 for (
const pair<Location*, Location*>& p : spatialCompatibility) {
783 double penalty = 0.0;
784 array<double, 2> newLengths;
785 vector<pair<uint32_t, Location*>> vec = { {r1, p.first}, {r2, p.second}};
786 for (
const pair<uint32_t, Location*>& e : vec) {
787 uint32_t robotId = e.first;
789 double curLength = currentLengths[robotId];
791 double newMinCycleTime =
calculateNewCycleTime(threadId, curLength, selectedCircuits[robotId]->sc.circuit,
792 currentFixedLocations[robotId], toFix, precalculatedCircuits);
794 newLengths[i++] = newMinCycleTime;
797 maxPenalty = max(maxPenalty, penalty);
798 preferences.push_back(penalty);
799 lengths.emplace_back(newLengths[0], newLengths[1]);
803 double sumOfPreferences = 0.0;
804 vector<double>::iterator prefArray = preferences.begin();
805 for (uint32_t p = 0; p < preferences.size(); ++p) {
806 prefArray[p] = maxPenalty-prefArray[p];
807 sumOfPreferences += prefArray[p];
811 uniform_real_distribution<double> wheel(0.0, sumOfPreferences);
812 double wheelValue = wheel(threadGenerator), accumulatedValue = 0.0;
816 for (uint32_t p = 0; p < preferences.size(); ++p) {
817 double currentPref = prefArray[p];
818 if (accumulatedValue <= wheelValue && wheelValue <= accumulatedValue+currentPref) {
823 accumulatedValue += currentPref;
826 if (maxPenalty-prefArray[selIdx] <= 20.0) {
829 currentLengths[r1] = lengths[selIdx].first;
830 currentLengths[r2] = lengths[selIdx].second;
832 const pair<Location*, Location*>& cmpPair = spatialCompatibility[selIdx];
833 Location *newlyFixed1 = cmpPair.first, *newlyFixed2 = cmpPair.second;
834 vector<Location*> *fl1 = ¤tFixedLocations[r1], *fl2 = ¤tFixedLocations[r2];
836 return l->parent() == newlyFixed1->parent() || l->parent() == newlyFixed2->parent();
839 replace_if(fl1->begin(), fl1->end(), pred, newlyFixed1);
840 replace_if(fl2->begin(), fl2->end(), pred, newlyFixed2);
843 feasibleTuple =
false;
847 if (feasibleTuple ==
true) {
849 for (uint32_t r = 0; r < numberOfRobots; ++r) {
850 const vector<StaticActivity*> alternative = selectedCircuits[r]->sc.circuit;
851 const vector<Location*>& fixed = currentFixedLocations[r];
852 auto sit = precalculatedCircuits.find(
ShortestCircuit(alternative, fixed));
853 if (sit != precalculatedCircuits.cend()) {
854 if (sit->second.circuit.empty())
857 candidate.
tuple.emplace_back(sit->first, sit->second);
859 throw SolverException(caller(),
"A bug in the generation of tuples, it should be stored in the map!");
864 ++numberOfAddedTuples;
875 const uint32_t numOfPromisingTuples = 10u;
876 uint32_t numberOfRobots =
mLine.robots().size();
878 vector<vector<CircuitRecord>> initialCircuits(numberOfRobots);
880 for (
const pair<Solution, CircuitTuple>& p : elites) {
881 const vector<CircuitRecord>& circuits = p.second.tuple;
882 assert(circuits.size() == numberOfRobots &&
"Invalid tuple stored in the knowledge base!");
883 for (uint32_t r = 0; r < circuits.size(); ++r) {
884 initialCircuits[r].push_back(circuits[r]);
885 precalculated.emplace(circuits[r].sc, circuits[r].hc);
889 for (uint32_t r = 0; r < numberOfRobots; ++r)
890 sort(initialCircuits[r].begin(), initialCircuits[r].end());
892 if (elites.size() > 1u) {
899 double minCycleTime = 0.0;
900 const vector<Robot*>& robots =
mLine.robots();
901 uint32_t numberOfRobots = robots.size();
902 double cycleTime =
mLine.productionCycleTime();
903 for (uint32_t r = 0; r < numberOfRobots; ++r) {
904 vector<Location*> candidatesToFix;
905 for (
Activity *a : robots[r]->activities()) {
909 candidatesToFix.push_back(l);
913 shuffle(candidatesToFix.begin(), candidatesToFix.end(), default_random_engine(rd()));
915 assert(r < fixed.size() &&
"Invalid dimensions of the input argument!");
918 bool pathChanged =
false;
919 vector<Location*> toFix = fixed[r], path = ps.
locs[r];
920 const vector<StaticActivity*>& alternative = t.
tuple[r].sc.circuit;
921 while (!pathChanged && idx < candidatesToFix.size()) {
922 toFix.push_back(candidatesToFix[idx]);
926 minCycleTime = max(minCycleTime, hc.
minLength);
944 clog<<string(35,
'=')<<
" STAT INFO "<<string(35,
'=')<<endl;
945 clog<<
"current runtime: "<<currentRuntime<<endl;
946 clog<<string(81,
'-')<<endl;
947 clog<<
"generated tuples: "<<tupleGen.first<<
" ("<<tupleGen.second*1000.0<<
" ms per tuple)"<<endl;
949 clog<<
"average number of iters per tuple: "<<itersPerTuple.second<<endl;
950 clog<<string(81,
'-')<<endl;
951 clog<<
"Linear Programming solver: "<<LPInfo.first<<
" calls ("<<LPInfo.second*1000.0<<
" ms per call)"<<endl;
956 clog<<string(81,
'-')<<endl;
957 clog<<
"Change location heuristic: "<<infoLocHeur.first<<
" calls ("<<infoLocHeur.second*1000.0<<
" ms per call)"<<endl;
960 clog<<string(81,
'-')<<endl;
961 clog<<
"Change power mode heuristic: "<<infoPwrmHeur.first<<
" calls ("<<infoPwrmHeur.second*1000.0<<
" ms per call)"<<endl;
964 clog<<string(81,
'-')<<endl;
967 clog<<string(34,
'=')<<
" END OF INFO "<<string(34,
'=')<<endl;
973 bool exists =
fileExists(pathToPerformaceLog);
974 ofstream perfLog(pathToPerformaceLog.c_str(), ofstream::app);
975 if (perfLog.good()) {
977 perfLog<<
"initialization time [s], generated tuples, time per tuple [ms], processed tuples [%], average number of iters per tuple, ";
978 perfLog<<
"number of calls of Linear Programming, time per call [ms], infeasibility rate [%], LP calls to collisions resolution, ";
979 perfLog<<
"average deterioration after collisions resolution [%], feasibility breaks, number of calls of change location heuristic, ";
980 perfLog<<
"time per call [ms], estimation error [%], feasibility breaks, number of calls of power mode heuristic, time per call [ms], ";
981 perfLog<<
"estimation error [%], feasibility breaks, number of calls of change path, feasibility breaks, runtime [s]"<<endl;
986 perfLog<<initializationTime<<
", "<<tupleGen.first<<
", "<<1000.0*tupleGen.second<<
", "<<
mKB.
percentageOfProcessed()<<
", "<<itersPerTuple.second<<
", ";
994 cerr<<
"Warning: Cannot open '"<<pathToPerformaceLog<<
"' file for writing!"<<endl;
void initializeLocalEnvironments(int numberOfThreads)
It enables the solver to initialize all the data structures (e.g. expensive to construct) required fo...
The instance of the class corresponds to a robot movement between two coordinates.
std::vector< T * > circuit
Closed path through locations or static activities, each static activity is visited just once...
uint64_t numberOfChangePathBreaks() const
Returns how many times the change of paths resulted in infeasibility.
std::vector< SpatialCmpTuple > mSortedSptCmp
The vector of spatial compatibility pairs heuristically sorted in decreasing order of difficulty...
void addTuple(CircuitTuple &&t)
Add a tuple to the mutex protected queue of tuples.
RoboticLine mLine
Robotic cell to be optimized.
The base class incorporating common properties of robot operations and movements. ...
The structure representing a solution found by an algorithm.
uint64_t numberOfLocHeurBreaks() const
Returns how many times the change locations sub-heuristic caused the infeasibility of the solution...
void addRandomTuplesToKB(const uint32_t &threadId, const uint32_t &numOfTuples, std::vector< std::vector< CircuitRecord >> &initialCircuits, PrecalculatedCircuits &precalculatedCircuits)
It generates and adds a requested number of tuples to the KnowledgeBase.
ParallelHeuristicSolver(const RoboticLine &l, const PrecalculatedMapping &m)
It initializes the heuristic, i.e. preallocates some arrays, calculates the length of the tabu list...
uint32_t MAX_ALTERNATIVES
The maximal number of alternative orders generated for each robot.
Instance of this class includes all the data structures and methods related to a robot.
std::vector< StaticActivity * > idToActivity
Node index to static activity, note that dynamic activities are already incorporated in the lengths o...
std::vector< std::vector< Location * > > locs
Selected locations for each robot, time ordered.
std::string exceptionToString(const std::exception &e, uint32_t level=0)
The recursive method creates the formatted error message for the given exception and their nested sub...
std::unordered_map< ShortestCircuit, HamiltonianCircuit< Location >> PrecalculatedCircuits
Mapping of ShortestCircuit to the related shortest closed path through locations. ...
std::vector< std::vector< Location * > > initializePartialSolution(PartialSolution &ps) const
It appends the fixed movements (implied from fixed locations), and optionally fastest power saving mo...
std::vector< std::vector< CircuitRecord > > generateShortestCircuits(PrecalculatedCircuits &precalculatedCircuits)
It finds random alternatives for each robot and calculates their shortest closed paths through locati...
void workerThread(uint32_t threadId, std::vector< std::vector< CircuitRecord >> initialCircuits, PrecalculatedCircuits precalculatedCircuits)
The worker thread generates initial tuples, and optimizes them by local modifications proposed by sub...
double changeRobotPaths(uint32_t threadId, const CircuitTuple &t, PartialSolution &ps, const std::vector< std::vector< Location * >> &fixed)
It randomly modifies the robot paths to enable the heuristic to search otherwise unreachable parts of...
Collection of movements between two static activities.
double runTime
Run time (seconds) of the optimization algorithm required for obtaining this solution.
std::atomic< bool > mFeasible
Used to stop other OpenMP threads if the instance infeasibility is detected by a thread.
CircuitTuple getTuple()
Returns a partially fixed solution called tuple.
PrecalculatedMapping mMapping
Fast searching in the data structure of the robotic cell.
Shortest closed path through locations.
static double penaltyFunction(const double &minCycle, const double &demandedCycleTime)
The function expressing the risk of exceeding the robot cycle time depending on its lower estimation...
Hamiltonian circuit through static activities and the fixed locations.
std::list< std::pair< Solution, CircuitTuple > > eliteSolutions()
Returns a list of elite solutions and their tuples from which they have been calculated.
Solution solve()
It optimizes the robotic cell and returns the best found solution.
std::vector< CircuitRecord > generateRandomAlternatives(const uint32_t &threadId, const Graph &g, const DistanceMatrix< double > &m)
Generates some random alternatives, i.e. candidates for a feasible order, from a distance graph and d...
uint64_t numberOfLPBreaks() const
Returns how many times it was not possible to obtain an initial solution, i.e. timing.
std::pair< uint64_t, double > infoLocHeur()
Returns the total number of HeuristicAlgorithms::heuristicLocationChanges calls and average runtime f...
A general exception of the program.
std::vector< CircuitRecord > tuple
Tuple, i.e. a partially fixed problem.
double percentageOfProcessed()
Percentage of the processed tuples.
uint32_t selectAlternative(const std::vector< CircuitRecord > &alternatives) const
It randomly selects a robot alternative according to a distribution function that slightly prefers th...
bool filled() const
Returns whether the average is calculated from the full length.
ShortestCircuit sc
Selected alternative and fixed locations.
std::vector< SpatialCmpTuple > sptCompVec
Enables to fast iterate through spatial compatibility elements.
void controlThread()
Generates various alternatives, launches worker threads, prints progress info, joins the workers...
A graph edge in the distance graph.
std::vector< Edge > edges
Graph edges.
std::map< StaticActivity *, std::map< StaticActivity *, std::vector< std::pair< Location *, Location * > > > > sptComp
Two-step mapping taking two static activities and returning a list of compatible pairs of locations...
constexpr double CRITERION_RTOL
A maximal relative tolerance of the criterion error imposed by the piece-wise linearization of energy...
std::atomic< bool > mStopCondition
The control thread sets this flag to true to stop all the worker threads.
double heuristicPowerModeSelection(const OptimalTiming &ot, PartialSolution &ps, TabuList &tabuList, bool &solutionChanged) const
The sub-heuristic tries to switch from one power saving mode to another one for a location in order t...
A short-term memory, containing a list of forbidden moves, that mitigates the risk of cycling...
bool fileExists(const std::string &pathToFile)
It checks the existence of the file.
Various auxiliary functions used across the program.
bool VERBOSE
Boolean flag determining verbosity of the program.
std::pair< uint64_t, double > infoLP()
Returns the total number of Linear Programming calls and its average runtime for one worker thread...
Solution bestSolution()
Returns the best found solution, throws an exception if not available.
void recordChangePathCall()
Reports that robot paths were diversified, i.e. ParallelHeuristicSolver::changeRobotPaths method was ...
uint32_t mTabuListSize
The number of tabu list elements calculated in the constructor.
uint32_t NUMBER_OF_THREADS
Maximal number of threads to be used.
std::vector< std::vector< std::vector< Location * > > > mLocs
Preallocated 2-D array of locations for getShortestCircuit method, indexed by thread.
double mRuntime
The runtime of this heuristic, updated from time to time.
void addErrorMessage(const std::string &msg)
Add an error message to the mutex protected vector of messages.
constexpr double TIME_TOL
A minimal time difference that is considered significant for a solution.
double totalEnergy
Energy consumption of a robotic cell for this timing.
uint64_t pathChangeCalls() const
The total number of ParallelHeuristicSolver::changeRobotPaths calls.
A partially fixed problem, i.e. tuple.
Exception is thrown if a method is given invalid parameters or a user provides invalid program argume...
Thrown if no feasible solution is found by the heuristic.
double calculateNewCycleTime(const uint32_t &threadId, const double ¤tCycleTime, std::vector< StaticActivity * > &alternative, std::vector< Location * > &fixed, Location *toFix, PrecalculatedCircuits &precalculatedCircuits)
A highly optimized function calculates a lower estimation on the robot cycle time if toFix location i...
State status
Enum specifying whether the solution is optimal, feasible, or infeasible...
Collection of locations in which a robot operation (or waiting) can be performed. ...
std::vector< std::vector< std::vector< double > > > mDist
Preallocated 2-D array of distances for getShortestCircuit method, indexed by thread.
Fixed locations, power saving modes, and movements.
void recordLocHeurRelErr(double relativeEstError)
Reports a relative estimation error of the change locations sub-heuristic for an energy improvement...
void recordNumberOfItersPerTuple(uint64_t iters)
Reports the number of optimization iterations performed for a loaded tuple.
Graph constructMinimalDurationGraph(Robot *r) const
Generates a distance graph for the given robot.
DistanceMatrix< double > createInitialDistanceMatrix(const Graph &g) const
It creates an initial matrix of distances based on edge lengths.
void addValue(const T &val)
Add a new value to the moving average.
std::pair< uint64_t, double > infoItersPerTuple()
Returns the total number of tuples used for optimization and the average number of optimization itera...
void recordPwrmHeurRelErr(double relativeEstError)
Reports a relative estimation error of the (de)select power mode sub-heuristic for an energy improvem...
KnowledgeBase mKB
Various data of the heuristic, e.g. generated tuples, performance statistics, etc.
A parallel hybrid heuristic solving the energy optimization problem of robotic cells.
double averageErrOfLocHeur()
Returns an average estimation error of the change locations sub-heuristic.
void generatePromisingTuples(const uint32_t &threadId)
It generates promising tuples and adds them to the KnowledgeBase.
DistanceMatrix< T > floyd(DistanceMatrix< T > m, const C &cmp=std::greater< T >())
It calculates all-to-all shortest paths and returns the matrix with their lengths.
double minLength
The lower bound on the robot cycle time for this circuit (neglecting global constraints).
A graph in which random alternatives will be searched for.
double heuristicLocationChanges(OptimalTiming &ot, PartialSolution &ps, const std::vector< std::vector< Location * >> &fixed, bool &solutionChanged) const
The sub-heuristic locally optimizes the robot paths (change locations) to reduce energy consumption...
double averageNumberOfLPCallsForLPFix()
Returns an average number of added precedences required for collisions avoidance. ...
Algo
Defining constants for different states of the heuristic.
std::vector< std::vector< Edge > > idToSuccessors
Node index to leaving edges that enters to its (node) successors.
string RESULTS_DIRECTORY
If not empty, then the optimization results will be written to this directory.
uint32_t MIN_ITERS_PER_TUPLE
The minimal number of optimization iterations per each tuple.
std::pair< uint64_t, double > infoPwrmHeur()
Returns the total number of HeuristicAlgorithms::heuristicPowerModeSelection calls and average runtim...
uint32_t endIdent
An index of the end node.
void writePerformanceRecordToLogFile(const double &initializationTime, const double &finalRuntime)
It writes various statistics about the heuristic performance to a CSV file.
double averageErrOfPwrmHeur()
Returns an average estimation error of the (de)select power mode sub-heuristic.
HamiltonianCircuit< Location > hc
Shortest closed path through locations, fixed locations are included.
HamiltonianCircuit< Location > getShortestCircuit(const uint32_t &threadId, const std::vector< StaticActivity * > &circuit, const std::vector< Location * > &fixed, bool writeCircuit=true)
It calculates the shortest circuit through locations that visits each static activity in the same ord...
std::vector< std::vector< std::vector< int64_t > > > mPreds
Preallocated 2-D array of predecessors for getShortestCircuit method, indexed by thread.
void printProgressInfo(const double ¤tRuntime)
Prints various information about the performance of the sub-heuristics, generation of tuples...
uint32_t startIdent
An index of the start node.
The file defines allowed inaccuracies in a solution and constants for floats.
std::vector< std::vector< T >> DistanceMatrix
Definition of the matrix data type.
uint32_t j
An index assigned to the activity t, used for an access to the distance matrix.
Location of the robot used either during work (welding) or waiting.
The robotic cell corresponds to an instance of this class.
The structure contains the maps for fast searching in the robotic cell.
double averageLPFixDeterioration()
Returns an average quality deterioration caused by the collisions resolution.
std::pair< uint64_t, double > infoTuplesGeneration()
Returns the total number of generated tuples and the average generation time for one worker thread...
uint64_t numberOfPwrmHeurBreaks() const
Returns how many times the (de)select power mode sub-heuristic caused infeasibility.
std::vector< StaticActivity * > circuit
Selected alternative, i.e. order of operations.
double MAX_RUNTIME
Maximal run time of the solver.
std::unordered_map< std::pair< Location *, Location * >, Movement * > locationsToMovement
It finds the movement between two locations if exists.
OptimalTiming solvePartialProblem(const PartialSolution &ps, const CircuitTuple &t, Algo algo)
Employs Linear Programming to obtain energy-efficient timing for a given tuple.
void recordAddTuplesCall(double runtime)
Reports a real time (measured by a timer) required for ParallelHeuristicSolver::addRandomTuplesToKB c...
std::vector< Location * > fixedLocations
Locations required to be fixed to enforce the spatial compatibility between robots.
The class encapsulating the calculation of the moving average.
Obtained timing for a partial problem.
HeuristicAlgorithms algo
Optimization sub-heuristics and the related Linear Programming solver for partially fixed problems...
uint32_t i
An index assigned to the activity f, used for an access to the distance matrix.
double average() const
Current value of the running average.
double infeasibilityRate()
Infeasibility rate of Linear Programming, i.e. the proportion of infeasible solutions to feasible and...
Thrown if the best solution of the heuristic cannot be returned since the solution pool is empty...
std::tuple< uint32_t, uint32_t, StaticActivity *, StaticActivity *, std::vector< std::pair< Location *, Location * >>> SpatialCmpTuple
Defines the data type for an element of the spatial compatibility, i.e. (rob1 idx, rob2 idx, act1, act2, {{loc1, loc2}*}).
std::vector< std::string > errorMessages() const
Returns all the error messages that occurred in the threads of the heuristic.