21 #include "SolverConfig.h"
35 mLines = move(reader.
mLines);
45 completeFillingOfMapping();
49 for (
size_t i = 0; i < min(mLines.size(), mMapping.size()); ++i) {
52 fillActivitiesRelations(mapping, line);
53 fillDiscretizedEnergyFunctions(mapping, line);
54 fillInterRobotOperations(mapping, line);
55 fillCollisionZones(mapping, line);
60 uint64_t numberOfMovements = 0;
61 for (
Robot* robot : line.robots()) {
62 for (
Activity* a : robot->activities()) {
65 numberOfMovements += da->movements().size();
72 for (
Robot* robot : line.robots()) {
73 for (
Activity* a : robot->activities()) {
76 for (
Movement *mv : da->movements()) {
78 Location *f = mv->from(), *t = mv->to();
84 throw_with_nested(
InvalidDatasetFile(caller(),
"Only one move is allowed between two points!"));
93 for (
Robot* robot : line.robots()) {
94 for (
Activity* a : robot->activities()) {
97 for (
Movement *mv : da->movements()) {
99 const double dFrom = mv->minDuration(), dTo = mv->maxDuration();
100 const double h = (dTo-dFrom)/(10.0*numberOfSegments), hsq = h*h, h2 = 2.0*h, h01 = 0.1*h;
103 vector<pair<double, double>> penalty;
104 double d1 = dFrom, d2 = dFrom+h, d3 = dFrom+h2;
105 double e1 = mv->energyConsumption(d1), e2 = mv->energyConsumption(d2), e3, secondDiff, sumOfPenalties = 0.0;
106 for (
double d = d3; d < dTo+h01; d += h) {
107 e3 = mv->energyConsumption(d);
108 secondDiff = abs(e3-2*e2+e1)/hsq;
109 penalty.emplace_back(d-h, secondDiff);
110 sumOfPenalties += secondDiff;
115 double penaltyForProlongation = sumOfPenalties/penalty.size();
116 for (
auto& p : penalty)
117 p.second += penaltyForProlongation;
118 sumOfPenalties *= 2.0;
121 vector<double> d = { dFrom };
122 vector<double> e = { mv->energyConsumption(dFrom) };
123 double accumulated = 0.0, toAccumulateForPoint = sumOfPenalties/numberOfSegments;
124 for (
const pair<double, double>& p : penalty) {
125 if (accumulated >= toAccumulateForPoint) {
126 d.push_back(p.first);
127 e.push_back(mv->energyConsumption(p.first));
128 accumulated -= toAccumulateForPoint;
130 accumulated += p.second;
133 e.push_back(mv->energyConsumption(dTo));
135 tuple<vector<double>, vector<double>> ef { d, e };
142 for (
Location *l : sa->locations()) {
145 vector<double> x = { 0.0, 1.0 }, y = { 0.0, inputPower };
146 tuple<vector<double>, vector<double>> ef { x, y };
156 const vector<Robot*>& robots = line.robots();
157 map<StaticActivity*, uint32_t> activityToRobotId;
158 for (uint32_t r = 0; r < robots.size(); ++r) {
159 for (
Activity *a : robots[r]->activities()) {
162 setValue(activityToRobotId, sa, r, caller());
167 for (
const pair<Location*, Location*>& p : op->spatialCompatibility()) {
168 assert(p.first !=
nullptr && p.second !=
nullptr && p.first->parent() !=
nullptr && p.second->parent() !=
nullptr &&
"Unexpected null pointers!");
169 Location *l1 = p.first, *l2 = p.second;
173 const auto& mit1 = mapping.
sptComp.find(a1);
174 if (mit1 != mapping.
sptComp.cend() && mit1->second.count(a2) == 0)
177 if (a1 !=
nullptr && a2 !=
nullptr && checkOk ==
true) {
178 mapping.
sptComp[a1][a2].push_back({l1, l2});
179 mapping.
sptComp[a2][a1].push_back({l2, l1});
182 if (checkOk ==
false) {
183 msg +=
"Spatial compatibility is only supported between pairs of static activities.";
185 msg +=
"Spatial compatibility must be between two static activities!\n";
186 msg +=
"Was the instance checked against the xml schema?!";
193 for (
TimeLag tl : op->timeLags()) {
199 set<pair<StaticActivity*, StaticActivity*>> uniquePairs;
200 for (
const auto& mit1 : mapping.
sptComp) {
202 uint32_t r1 = getValue(activityToRobotId, a1, caller());
203 for (
const auto& mit2 : mit1.second) {
205 uint32_t r2 = getValue(activityToRobotId, a2, caller());
206 if (uniquePairs.count({a1, a2}) == 0 && uniquePairs.count({a2, a1}) == 0) {
207 auto compatibilityPairs = mit2.second;
208 mapping.
sptCompVec.emplace_back(r1, r2, a1, a2, compatibilityPairs);
209 uniquePairs.insert({a1, a2});
216 for (
const pair<ActivityMode*, ActivityMode*>& collision : line.collisions()) {
223 for (
Robot* r : mLine.robots()) {
224 uint32_t lastInCycleCounter = 0;
225 for (
Activity* a : r->activities()) {
229 checkStaticActivity(sa);
230 if (sa->lastInCycle())
231 lastInCycleCounter++;
234 checkDynamicActivity(da);
238 if (lastInCycleCounter != 1)
239 throw InvalidDatasetFile(caller(),
"Robot '"+r->name()+
"' must have one static activity which ends the cycle!");
247 for (
const pair<ActivityMode*, ActivityMode*>& col : mLine.collisions())
252 if (sa->minAbsDuration() > sa->maxAbsDuration()) {
253 string msg =
"Invalid duration of static activity "+to_string(sa->aid())+
" or instance trivially infeasible!";
257 for (
Location* l : sa->locations()) {
258 set<uint32_t> pwrModeIds;
260 pwrModeIds.insert(ldpc.robotPowerMode()->pid());
262 assert(sa->parent() !=
nullptr &&
"Parent should be set by the XmlReader class!");
264 Robot *robot = sa->parent();
266 if (pwrModeIds.count(pm->pid()) == 0 && pm->expectedInputPower() < 0.0) {
267 string msg =
"Robot power mode "+to_string(pm->pid()) +
" has to have either defined expected input power or ";
268 msg +=
"if it is location depedent the power consumption for activity " + to_string(sa->aid());
269 msg +=
" must be stated in 'consumption' element of location " + to_string(l->lid()) +
"!";
277 for (
Movement *mv : da->movements()) {
278 if (mv->minDuration() > mv->maxDuration()) {
279 string msg =
"Invalid duration of dynamic activity " + to_string(da->aid()) +
"'s movement " + to_string(mv->mid()) +
"!";
283 for (
const Monomial& m : mv->energyFunction()) {
284 if (m.degree != 0 && m.degree != 1 && m.coeff < 0.0) {
285 string msg =
"Energy function of movement " + to_string(mv->mid()) +
" (activity ";
286 msg += to_string(da->aid()) +
") is not convex!\nPlease update coefficients!";
295 const vector<Activity*>& activities = r->activities();
297 const uint64_t numAct = activities.size();
300 for (uint64_t a = 0; a < numAct; ++a) {
302 const vector<Activity*>& pred = activities[a]->predecessors();
303 const vector<Activity*>& succ = activities[a]->successors();
306 for (
const vector<Activity*>& predOrSucc : {pred, succ}) {
308 if (ps->parent() == activities[a]->parent()) {
309 uint64_t psIdx = (uint64_t) (find(activities.cbegin(), activities.cend(), ps)-activities.cbegin());
311 distMatrix[psIdx][a] = 1.0;
313 distMatrix[a][psIdx] = 1.0;
315 string msg =
"Activity " + to_string(activities[a]->aid());
317 msg +=
" has predecessor ";
319 msg +=
" has successor ";
320 msg += to_string(ps->aid()) +
" from different robot!";
329 distMatrix =
floyd(distMatrix);
331 for (uint32_t i = 0; i < numAct; ++i) {
332 for (uint32_t j = 0; j < numAct; ++j) {
333 if (distMatrix[i][j] == F64_INF) {
334 string msg =
"Robot '" + r->name() +
"' has not enough connected graph of activities!";
342 for (
const TimeLag& lag : op->timeLags()) {
343 if (lag.from()->parent() == lag.to()->parent()) {
344 string msg =
"Time-lag from activity "+to_string(lag.from()->aid()) +
" to activity " + to_string(lag.to()->aid());
345 msg +=
" is invalid. The edge must be between two different robots!";
346 msg +=
"\nCheck operation " + to_string(op->oid()) +
".";
351 for (
const pair<Location*, Location*>& p : op->spatialCompatibility()) {
353 assert(p.first !=
nullptr && p.second !=
nullptr && p.first->parent() !=
nullptr && p.second->parent() !=
nullptr &&
"Unexpected null pointers!");
354 StaticActivity *sa1 = p.first->parent(), *sa2 = p.second->parent();
355 assert(sa1->parent() !=
nullptr && sa2->parent() !=
nullptr &&
"Unexpected null pointers, it should already be initialized!");
357 if (sa1->parent() == sa2->parent()) {
358 string msg =
"Static activities "+to_string(sa1->aid()) +
" and " + to_string(sa2->aid()) +
" must be located at different robots!";
359 msg +=
"\nPlease check operation " + to_string(op->oid()) +
"'s spatial compatibility pairs!";
366 assert(col.first !=
nullptr && col.second !=
nullptr &&
"Unexpected null pointers!");
367 const Activity *a1 = col.first->baseParent(), *a2 = col.second->baseParent();
368 assert(a1 !=
nullptr && a2 !=
nullptr && a1->parent() !=
nullptr && a2->parent() !=
nullptr &&
"Unexpected null pointers!");
369 Robot *r1 = a1->parent(), *r2 = a2->parent();
371 string msg =
"Collision can occur only between two different robots!";
372 msg +=
"\nPlease check the collision pair with activities " + to_string(a1->aid());
373 msg +=
" and " + to_string(a2->aid()) +
"!";
The instance of the class corresponds to a robot movement between two coordinates.
void fillInterRobotOperations(PrecalculatedMapping &mapping, const RoboticLine &line)
It fills the searching maps related to the robot synchronizations and spatial compatibility.
void fillCollisionZones(PrecalculatedMapping &mapping, const RoboticLine &line)
It fills the maps for fast searching of colliding movements or locations.
void checkOperation(InterRobotOperation *op) const
Checks that each time lag or spatial compatibility pair is defined for two different robots...
An instance of this class checks a dataset instance.
The base class incorporating common properties of robot operations and movements. ...
std::vector< RoboticLine > mLines
Vector of problem instances, i.e. robotic cells.
The instance of this class specifies input power of the robot for a particular robot configuration an...
Instance of this class includes all the data structures and methods related to a robot.
uint32_t NUMBER_OF_SEGMENTS
By how many segments (linear pieces) the energy function of the movement is approximated.
std::map< std::pair< Location *, RobotPowerMode * >, std::tuple< std::vector< double >, std::vector< double > > > locToEnergyFunc
Location and the selected power saving mode are mapped to the linear energy function, i.e. the size of each vector in the tuple is 2.
Collection of movements between two static activities.
std::map< Activity *, std::vector< TimeLag > > timeLagsFromAct
It searches for all the time lags starting from the given activity.
void fillActivitiesRelations(PrecalculatedMapping &mapping, const RoboticLine &line)
Maps related to activities relations (precedences) are filled.
void checkStaticActivity(StaticActivity *sa) const
It checks the activity duration and whether the input power of power saving modes is defined...
void readDatasetFromXmlFile()
It parses the XML dataset (specified in Settings::DATASET_FILE) and checks it against schema...
Checking and post-processing of parsed datasets.
std::vector< SpatialCmpTuple > sptCompVec
Enables to fast iterate through spatial compatibility elements.
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...
Various auxiliary functions used across the program.
Thrown if the dataset file contains ill-specified robotic cells.
The file defines extended exceptions for the better error handling in the program.
std::string mDatasetDescription
Description of the dataset.
void checkDynamicActivity(DynamicActivity *da) const
Verifies the activity duration and convexity of energy functions.
void checkCollision(const std::pair< ActivityMode *, ActivityMode * > &col) const
Checks that the collision is defined for two different robots.
std::map< ActivityMode *, std::set< ActivityMode * > > collisionSearch
It finds colliding movements and locations for a given location or movement.
Collection of locations in which a robot operation (or waiting) can be performed. ...
void checkInstance() const
The method verifies the correctness of the instance. If incorrect, then an exception is thrown...
The file declares XmlReader class which purpose is to parse and check XML datasets.
void checkRobotGraph(Robot *r) const
It checks that there is a hamiltonian circuit in the robot graph.
A part of the energy function of a movement.
It represents the power saving mode of the robot.
Instance of TimeLag class defines a time relation between two different robots.
Universal algorithms like Floyd-Warshall, Golden Search, etc.
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.
std::map< Movement *, std::tuple< std::vector< double >, std::vector< double > > > mvToEnergyFunc
It takes a pointer to a movement and returns the vector of precalculated coordinates corresponding to...
std::map< uint32_t, std::vector< uint32_t > > pointToPredecessorPoints
It finds all the starting coordinates of the movements entering the given coordinate, i.e. the point.
std::vector< PrecalculatedMapping > mMapping
Vector of precalculated mappings for fast access by a key.
double inputPower(const uint32_t &lid, const uint32_t &pid) const
std::map< uint32_t, std::vector< uint32_t > > pointToSuccessorPoints
It finds all the ending coordinates of the movements leaving the given coordinate, i.e. the point.
The inter-robot operation corresponding to the workpiece/weldment handling.
std::map< Activity *, std::vector< TimeLag > > timeLagsToAct
It searches for all the time lags ending in the given activity.
void completeFillingOfMapping()
A wrapper method calling all the related filling methods.
The file defines allowed inaccuracies in a solution and constants for floats.
std::vector< std::vector< T >> DistanceMatrix
Definition of the matrix data type.
void fillDiscretizedEnergyFunctions(PrecalculatedMapping &mapping, const RoboticLine &line)
Energy functions are piece-wise linearized, and vectors of coordinates are written to the mapping...
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.
void readInstances()
It processes the dataset specified in Settings::DATASET_FILE.
std::string mDatasetName
Name of the dataset.
std::unordered_map< std::pair< uint32_t, uint32_t >, Movement * > pointsToMovement
Two coordinates are mapped to the movement starting/ending at the first/second coordinate, respectively.
std::unordered_map< std::pair< Location *, Location * >, Movement * > locationsToMovement
It finds the movement between two locations if exists.