solver  1.0
InstancesReader.cpp
1 /*
2  This file is part of the EnergyOptimizatorOfRoboticCells program.
3 
4  EnergyOptimizatorOfRoboticCells is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  EnergyOptimizatorOfRoboticCells is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with EnergyOptimizatorOfRoboticCells. If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include <algorithm>
19 #include <set>
20 #include <utility>
21 #include "SolverConfig.h"
22 #include "Shared/Utils.h"
23 #include "Shared/Exceptions.h"
24 #include "Shared/Algorithms.h"
25 #include "Shared/Exceptions.h"
29 
30 using namespace std;
31 
33  XmlReader reader;
34  reader.readDatasetFromXmlFile();
35  mLines = move(reader.mLines);
36  mMapping = move(reader.mMapping);
37  mDatasetName = move(reader.mDatasetName);
38  mDatasetDescription = move(reader.mDatasetDescription);
39 
40  for (const RoboticLine& l : mLines) {
41  InstanceChecker checker(l);
42  checker.checkInstance();
43  }
44 
45  completeFillingOfMapping();
46 }
47 
49  for (size_t i = 0; i < min(mLines.size(), mMapping.size()); ++i) {
50  const RoboticLine& line = mLines[i];
51  PrecalculatedMapping& mapping = mMapping[i];
52  fillActivitiesRelations(mapping, line);
53  fillDiscretizedEnergyFunctions(mapping, line);
54  fillInterRobotOperations(mapping, line);
55  fillCollisionZones(mapping, line);
56  }
57 }
58 
60  uint64_t numberOfMovements = 0;
61  for (Robot* robot : line.robots()) {
62  for (Activity* a : robot->activities()) {
63  DynamicActivity *da = dynamic_cast<DynamicActivity*>(a);
64  if (da != nullptr)
65  numberOfMovements += da->movements().size();
66  }
67  }
68 
69  mapping.locationsToMovement.reserve(numberOfMovements);
70  mapping.pointsToMovement.reserve(numberOfMovements);
71 
72  for (Robot* robot : line.robots()) {
73  for (Activity* a : robot->activities()) {
74  DynamicActivity *da = dynamic_cast<DynamicActivity*>(a);
75  if (da != nullptr) {
76  for (Movement *mv : da->movements()) {
77  try {
78  Location *f = mv->from(), *t = mv->to();
79  setValue(mapping.locationsToMovement, {f, t}, mv, caller());
80  setValue(mapping.pointsToMovement, {f->point(), t->point()}, mv, caller());
81  mapping.pointToSuccessorPoints[f->point()].push_back(t->point());
82  mapping.pointToPredecessorPoints[t->point()].push_back(f->point());
83  } catch (...) {
84  throw_with_nested(InvalidDatasetFile(caller(), "Only one move is allowed between two points!"));
85  }
86  }
87  }
88  }
89  }
90 }
91 
93  for (Robot* robot : line.robots()) {
94  for (Activity* a : robot->activities()) {
95  DynamicActivity *da = dynamic_cast<DynamicActivity*>(a);
96  if (da != nullptr) {
97  for (Movement *mv : da->movements()) {
98  const uint32_t numberOfSegments = Settings::NUMBER_OF_SEGMENTS;
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;
101 
102  // Calculate second derivates (direction changes) of the energy function.
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;
111  e1 = e2; e2 = e3;
112  }
113 
114  // Add extra penalty (higher penalty smaller distance between points) for prolongation.
115  double penaltyForProlongation = sumOfPenalties/penalty.size();
116  for (auto& p : penalty)
117  p.second += penaltyForProlongation;
118  sumOfPenalties *= 2.0;
119 
120  // Discretize the function according to the penalty.
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;
129  }
130  accumulated += p.second;
131  }
132  d.push_back(dTo);
133  e.push_back(mv->energyConsumption(dTo));
134 
135  tuple<vector<double>, vector<double>> ef { d, e };
136  setValue(mapping.mvToEnergyFunc, mv, ef, caller());
137  }
138  }
139 
140  StaticActivity *sa = dynamic_cast<StaticActivity*>(a);
141  if (sa != nullptr) {
142  for (Location *l : sa->locations()) {
143  for (RobotPowerMode *pwrm : robot->powerModes()) {
144  double inputPower = l->inputPower(pwrm);
145  vector<double> x = { 0.0, 1.0 }, y = { 0.0, inputPower };
146  tuple<vector<double>, vector<double>> ef { x, y };
147  setValue(mapping.locToEnergyFunc, {l, pwrm}, ef, caller());
148  }
149  }
150  }
151  }
152  }
153 }
154 
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()) {
160  StaticActivity *sa = dynamic_cast<StaticActivity*>(a);
161  if (sa != nullptr)
162  setValue(activityToRobotId, sa, r, caller());
163  }
164  }
165 
166  for (InterRobotOperation* op : line.interRobotOperations()) {
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;
170  StaticActivity *a1 = l1->parent(), *a2 = l2->parent();
171 
172  bool checkOk = true;
173  const auto& mit1 = mapping.sptComp.find(a1);
174  if (mit1 != mapping.sptComp.cend() && mit1->second.count(a2) == 0)
175  checkOk = false; // Spatial compatibility supported only between pairs of activities...
176 
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});
180  } else {
181  string msg;
182  if (checkOk == false) {
183  msg += "Spatial compatibility is only supported between pairs of static activities.";
184  } else {
185  msg += "Spatial compatibility must be between two static activities!\n";
186  msg += "Was the instance checked against the xml schema?!";
187  }
188 
189  throw InvalidDatasetFile(caller(), msg);
190  }
191  }
192 
193  for (TimeLag tl : op->timeLags()) {
194  mapping.timeLagsFromAct[tl.from()].push_back(tl);
195  mapping.timeLagsToAct[tl.to()].push_back(tl);
196  }
197  }
198 
199  set<pair<StaticActivity*, StaticActivity*>> uniquePairs;
200  for (const auto& mit1 : mapping.sptComp) {
201  StaticActivity *a1 = mit1.first;
202  uint32_t r1 = getValue(activityToRobotId, a1, caller());
203  for (const auto& mit2 : mit1.second) {
204  StaticActivity *a2 = mit2.first;
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});
210  }
211  }
212  }
213 }
214 
216  for (const pair<ActivityMode*, ActivityMode*>& collision : line.collisions()) {
217  mapping.collisionSearch[collision.first].insert(collision.second);
218  mapping.collisionSearch[collision.second].insert(collision.first);
219  }
220 }
221 
223  for (Robot* r : mLine.robots()) {
224  uint32_t lastInCycleCounter = 0;
225  for (Activity* a : r->activities()) {
226  StaticActivity *sa = dynamic_cast<StaticActivity*>(a);
227  DynamicActivity *da = dynamic_cast<DynamicActivity*>(a);
228  if (sa != nullptr) {
229  checkStaticActivity(sa);
230  if (sa->lastInCycle())
231  lastInCycleCounter++;
232  }
233  if (da != nullptr) {
234  checkDynamicActivity(da);
235  }
236  }
237 
238  if (lastInCycleCounter != 1)
239  throw InvalidDatasetFile(caller(), "Robot '"+r->name()+"' must have one static activity which ends the cycle!");
240 
241  checkRobotGraph(r);
242  }
243 
244  for (InterRobotOperation* op : mLine.interRobotOperations())
245  checkOperation(op);
246 
247  for (const pair<ActivityMode*, ActivityMode*>& col : mLine.collisions())
248  checkCollision(col);
249 }
250 
252  if (sa->minAbsDuration() > sa->maxAbsDuration()) {
253  string msg = "Invalid duration of static activity "+to_string(sa->aid())+" or instance trivially infeasible!";
254  throw InvalidDatasetFile(caller(), msg);
255  }
256 
257  for (Location* l : sa->locations()) {
258  set<uint32_t> pwrModeIds;
259  for (const LocationDependentPowerConsumption& ldpc : l->locationDependentPowerConsumption())
260  pwrModeIds.insert(ldpc.robotPowerMode()->pid());
261 
262  assert(sa->parent() != nullptr && "Parent should be set by the XmlReader class!");
263 
264  Robot *robot = sa->parent();
265  for (const RobotPowerMode *pm : robot->powerModes()) {
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()) + "!";
270  throw InvalidDatasetFile(caller(), msg);
271  }
272  }
273  }
274 }
275 
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()) + "!";
280  throw InvalidDatasetFile(caller(), msg);
281  }
282 
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!";
287  throw InvalidDatasetFile(caller(), msg);
288  }
289  }
290  }
291 }
292 
294 
295  const vector<Activity*>& activities = r->activities();
296 
297  const uint64_t numAct = activities.size();
298  DistanceMatrix<double> distMatrix(numAct, vector<double>(numAct, F64_INF));
299 
300  for (uint64_t a = 0; a < numAct; ++a) {
301 
302  const vector<Activity*>& pred = activities[a]->predecessors();
303  const vector<Activity*>& succ = activities[a]->successors();
304 
305  uint8_t phase = 0;
306  for (const vector<Activity*>& predOrSucc : {pred, succ}) {
307  for (Activity *ps : predOrSucc) {
308  if (ps->parent() == activities[a]->parent()) {
309  uint64_t psIdx = (uint64_t) (find(activities.cbegin(), activities.cend(), ps)-activities.cbegin());
310  if (phase == 0)
311  distMatrix[psIdx][a] = 1.0;
312  else
313  distMatrix[a][psIdx] = 1.0;
314  } else {
315  string msg = "Activity " + to_string(activities[a]->aid());
316  if (phase == 0)
317  msg += " has predecessor ";
318  else
319  msg += " has successor ";
320  msg += to_string(ps->aid()) + " from different robot!";
321 
322  throw InvalidDatasetFile(caller(), msg);
323  }
324  }
325  ++phase;
326  }
327  }
328 
329  distMatrix = floyd(distMatrix);
330 
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!";
335  throw InvalidDatasetFile(caller(), msg);
336  }
337  }
338  }
339 }
340 
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()) + ".";
347  throw InvalidDatasetFile(caller(), msg);
348  }
349  }
350 
351  for (const pair<Location*, Location*>& p : op->spatialCompatibility()) {
352 
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!");
356 
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!";
360  throw InvalidDatasetFile(caller(), msg);
361  }
362  }
363 }
364 
365 void InstanceChecker::checkCollision(const pair<ActivityMode*, ActivityMode*>& col) const {
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();
370  if (r1 == r2) {
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()) + "!";
374  throw InvalidDatasetFile(caller(), msg);
375  }
376 }
377 
The instance of the class corresponds to a robot movement between two coordinates.
Definition: RoboticLine.h:133
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. ...
Definition: RoboticLine.h:68
std::vector< RoboticLine > mLines
Vector of problem instances, i.e. robotic cells.
Definition: XmlReader.h:120
The instance of this class specifies input power of the robot for a particular robot configuration an...
Definition: RoboticLine.h:403
Instance of this class includes all the data structures and methods related to a robot.
Definition: RoboticLine.h:432
uint32_t NUMBER_OF_SEGMENTS
By how many segments (linear pieces) the energy function of the movement is approximated.
Definition: Settings.cpp:29
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.
Definition: RoboticLine.h:261
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...
Definition: XmlReader.cpp:39
STL namespace.
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.
Definition: Exceptions.h:99
The file defines extended exceptions for the better error handling in the program.
std::string mDatasetDescription
Description of the dataset.
Definition: XmlReader.h:126
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. ...
Definition: RoboticLine.h:304
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.
Definition: RoboticLine.h:125
It represents the power saving mode of the robot.
Definition: RoboticLine.h:359
Instance of TimeLag class defines a time relation between two different robots.
Definition: RoboticLine.h:481
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.
Definition: Algorithms.h:50
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.
Definition: XmlReader.h:122
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.
Definition: RoboticLine.h:518
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.
Parser of XML datasets.
Definition: XmlReader.h:45
The file defines allowed inaccuracies in a solution and constants for floats.
std::vector< std::vector< T >> DistanceMatrix
Definition of the matrix data type.
Definition: Algorithms.h:39
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.
Definition: RoboticLine.h:192
The robotic cell corresponds to an instance of this class.
Definition: RoboticLine.h:563
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.
Definition: XmlReader.h:124
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.