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.