solver  1.0
RoboticLineSolverLP.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 <cmath>
20 #include <chrono>
21 #include <map>
22 #include <string>
23 #include "Settings.h"
24 #include "SolverConfig.h"
25 #include "Shared/Utils.h"
28 
29 using namespace std;
30 using namespace std::chrono;
31 
32 RoboticLineSolverLP::RoboticLineSolverLP(Robot* r, const PartialSolution& ps, const PrecalculatedMapping& m) : mMapper(r), mGenerator(r, &m), mPartialSolution(ps) {
33  #ifndef NDEBUG
34  checkArguments(m);
35  #endif
37  construct(m, false);
38 }
39 
40 RoboticLineSolverLP::RoboticLineSolverLP(const RoboticLine& l, const PartialSolution& ps, const PrecalculatedMapping& m) : mMapper(l), mGenerator(l, &m), mPartialSolution(ps) {
41  size_t numOfRobots = l.robots().size();
42  #ifndef NDEBUG
43  checkArguments(m, numOfRobots); // Filled by the heuristics - should be OK.
44  #endif
46  construct(m, numOfRobots > 1);
47 }
48 
50 
51  Solution s;
52  high_resolution_clock::time_point start = high_resolution_clock::now();
53  SolutionILP sLP = solveILP(mModel, false);
54  s.runTime = duration_cast<duration<double>>(high_resolution_clock::now()-start).count();
55  s.status = convert(sLP.status);
56 
57  if (sLP.status == ILP_OPTIMAL) {
58 
59  s.totalEnergy = sLP.criterion;
60 
61  try {
62  double minStartTime = F64_MAX;
63  map<uint32_t, double> idToStart = inverseMapping(sLP.solution, mMapper.s);
64  map<uint32_t, double> idToDuration = inverseMapping(sLP.solution, mMapper.d);
65 
66  if (idToStart.size() != idToDuration.size())
67  throw SolverException(caller(), "LP model is invalid probably!");
68 
69  for (const auto& p : idToStart)
70  minStartTime = min(minStartTime, p.second);
71 
72  for (const auto& p : idToStart) {
73  uint32_t aid = p.first;
74  double start = p.second-minStartTime, duration = getValue(idToDuration, aid, caller());
75  setValue(s.startTimeAndDuration, aid, {start, duration}, caller());
76  }
77 
78  uint32_t numberOfRobots = mPartialSolution.locs.size();
79  for (uint32_t r = 0; r < numberOfRobots; ++r) {
80  const vector<Location*>& circuit = mPartialSolution.locs[r];
81  const vector<RobotPowerMode*>& modes = mPartialSolution.pwrms[r];
82  for (uint32_t i = 0; i+1 < min(circuit.size(), modes.size()); ++i) {
83  Location *l = circuit[i];
84  RobotPowerMode *m = modes[i];
85  assert(l != nullptr && m != nullptr && l->parent() != nullptr && "Pointers should be initialized!");
86  uint32_t point = l->point(), pid = m->pid(), aid = l->parent()->aid();
87  setValue(s.pointAndPowerMode, aid, {point, pid}, caller());
88  }
89  }
90 
91  } catch (...) {
92  throw_with_nested(SolverException(caller(), "Cannot map LP solution to the problem solution!\nCheck the validity of the model..."));
93  }
94  }
95 
96  return s;
97 }
98 
99 void RoboticLineSolverLP::addCollisionResolution(Activity* i, Activity* j, const int32_t& multipleOfCycleTime) {
100  mGenerator.addCollisionResolution(mMapper.s, mMapper.d, i->aid(), j->aid(), multipleOfCycleTime);
102 }
103 
105  vector<Activity*> selDynActs;
106  for (const vector<Movement*>& mvs : mPartialSolution.mvs) {
107  for (Movement *mv : mvs) {
108  assert(mv != nullptr && mv->parent() != nullptr && "Invalid initialization of the RoboticLine data-structure!");
109  Activity *da = mv->parent();
110  if (da->optional())
111  selDynActs.push_back(da);
112  }
113  }
114 
115  #ifdef GUROBI_SOLVER
116  mMapper.addActivities(selDynActs, false, false);
117  #else
118  mMapper.addActivities(selDynActs, false, true);
119  #endif
120 }
121 
122 void RoboticLineSolverLP::construct(const PrecalculatedMapping& m, bool addTimeLags) {
123  try {
124  uint64_t numVars = mMapper.numberOfVariables();
125  mModel.c.resize(numVars, 0.0);
126  assert(!mModel.c.empty() && "Default coefficient values must be set even for Gurobi!");
127 
128  #ifdef GUROBI_SOLVER
129  mModel.gurobiC.resize(numVars, nullptr);
130  #else
131  for (const auto& p : mMapper.W)
132  mModel.c[p.second] = 1.0;
133  #endif
134 
135  uint32_t numberOfRobots = mPartialSolution.locs.size();
136  for (uint32_t r = 0; r < numberOfRobots; ++r) {
137  const vector<Location*>& locs = mPartialSolution.locs[r];
138  const vector<RobotPowerMode*>& pwrms = mPartialSolution.pwrms[r];
139  for (uint32_t i = 0; i+1 < locs.size(); ++i) {
140  assert(locs[i] != nullptr && locs[i]->parent() != nullptr && pwrms[i] != nullptr && "Invalid data structures!");
141 
142  uint32_t mappedIdx;
143  StaticActivity *sa = locs[i]->parent();
144  const auto& dit = mMapper.d.find(sa->aid());
145  if (dit != mMapper.d.cend())
146  mappedIdx = dit->second;
147  else
148  throw SolverException(caller(), "Invalid mapping of variables!");
149 
150  #ifdef GUROBI_SOLVER
151  const auto& sit = m.locToEnergyFunc.find({locs[i], pwrms[i]});
152  if (sit != m.locToEnergyFunc.cend())
153  mModel.gurobiC[mappedIdx] = &sit->second;
154  else
155  throw SolverException(caller(), "Cannot find the discretized energy function for the location and power saving mode!");
156  #else
157  mGenerator.addEnergyFunction(mMapper.d, mMapper.W, locs[i], pwrms[i]);
158  #endif
159 
160  Variable& d = mMapper.variables[mappedIdx];
161  d.lowerBound = max(d.lowerBound, pwrms[i]->minimalDelay());
162  assert(d.lowerBound <= d.upperBound && "Invalid variable bound (location)!");
163  }
164  }
165 
166 
167  for (uint32_t r = 0; r < numberOfRobots; ++r) {
168  const vector<Movement*> mvs = mPartialSolution.mvs[r];
169  for (Movement* mv : mvs) {
170  assert(mv->parent() != nullptr && "Movement should belong to a dynamic activity!");
171 
172  uint32_t mappedIdx;
173  DynamicActivity *da = mv->parent();
174  const auto& dit = mMapper.d.find(da->aid());
175  if (dit != mMapper.d.cend())
176  mappedIdx = dit->second;
177  else
178  throw SolverException(caller(), "Invalid mapping of variables!");
179 
180  #ifdef GUROBI_SOLVER
181  const auto& sit = m.mvToEnergyFunc.find(mv);
182  if (sit != m.mvToEnergyFunc.cend())
183  mModel.gurobiC[mappedIdx] = &sit->second;
184  else
185  throw SolverException(caller(), "Cannot find discretized movement "+to_string(mv->mid())+"!");
186  #else
188  #endif
189 
190  Variable& d = mMapper.variables[mappedIdx];
191  d.lowerBound = max(d.lowerBound, mv->minDuration());
192  d.upperBound = min(d.upperBound, mv->maxDuration());
193  assert(d.lowerBound <= d.upperBound && "Invalid variable bound (movement)!");
194  }
195 
197  }
198 
199 
200  if (addTimeLags)
202 
203  } catch (...) {
204  string msg = "Cannot add constraints to the LP model!\nEither variable mapping or RoboticLine data-structure is corrupted!";
205  throw_with_nested(SolverException(caller(), msg));
206  }
207 
208  mModel.x = move(mMapper.variables);
209  mModel.varDesc = move(mMapper.varDesc);
211  #ifndef NDEBUG
213  #endif
214 }
215 
216 void RoboticLineSolverLP::checkArguments(const PrecalculatedMapping& m, const uint32_t& numberOfRobots) const {
217  const PartialSolution& ps = mPartialSolution;
218  if (ps.locs.size() != numberOfRobots || ps.pwrms.size() != numberOfRobots || ps.mvs.size() != numberOfRobots)
219  throw InvalidArgument(caller(), "Invalid argument 'ps', dimensions mismatch!");
220 
221  for (uint32_t r = 0; r < numberOfRobots; ++r) {
222  if (ps.locs[r].size() != ps.pwrms[r].size() || ps.locs[r].empty())
223  throw InvalidArgument(caller(), "The number of locations must be the same as the number of power saving modes!");
224  if (ps.locs[r].size() != ps.mvs[r].size()+1 || ps.mvs[r].empty())
225  throw InvalidArgument(caller(), "The number of movements should be about one less than the number of locations!");
226 
227  const vector<Location*>& locs = ps.locs[r];
228  const vector<RobotPowerMode*>& pwrms = ps.pwrms[r];
229  const vector<Movement*>& mvs = ps.mvs[r];
230  if (count(locs.cbegin(), locs.cend(), nullptr) != 0 || count(pwrms.cbegin(), pwrms.cend(), nullptr) != 0 || count(mvs.cbegin(), mvs.cend(), nullptr) != 0)
231  throw InvalidArgument(caller(), "Null pointer in the given PartialSolution data-structure!");
232 
233  try {
234  for (uint32_t i = 0; i+1 < locs.size(); ++i) {
235  Location *from = locs[i], *to = locs[i+1];
236  Movement *mv = mvs[i];
237  Movement *mvRight = getValue(m.locationsToMovement, {from, to}, caller());
238  if (mv != mvRight)
239  throw InvalidArgument(caller(), "Given invalid partial solution to the LP solver!");
240  }
241  } catch (InvalidArgument& e) {
242  throw e;
243  } catch (SolverException& e) {
244  throw_with_nested(InvalidArgument(caller(), "Invalid partial solution, a non-existing movement between two locations!"));
245  }
246  }
247 }
248 
The instance of the class corresponds to a robot movement between two coordinates.
Definition: RoboticLine.h:133
void construct(const PrecalculatedMapping &m, bool addTimeLags=true)
Complete construction of the Linear Programming problem for the energy optimization.
double totalEnergy
The amount of energy required per robot cycle.
Definition: Solution.h:57
void addCollisionResolution(Activity *i, Activity *j, const int32_t &multipleOfCycleTime)
It adds a constraint to resolve a collision where and are start time and duration of the activity ...
The base class incorporating common properties of robot operations and movements. ...
Definition: RoboticLine.h:68
void addEnergyFunction(const map1to1 &d, const map1to1 &W, Location *loc, RobotPowerMode *pwrm)
Adds energy function for a static activity, its location, and its applied power saving mode...
The structure representing a solution found by an algorithm.
Definition: Solution.h:50
Instance of this class includes all the data structures and methods related to a robot.
Definition: RoboticLine.h:432
std::vector< std::vector< Location * > > locs
Selected locations for each robot, time ordered.
ConstraintsGenerator mGenerator
Generator of the constraints.
void addActivities(const std::vector< Activity * > &activities, bool mandatory, bool mapW=true)
It adds the mapping for extra activities.
std::vector< Variable > variables
Continuous variables of the problem with the well-specified domains.
void addCollisionResolution(const map1to1 &s, const map1to1 &d, uint32_t i, uint32_t j, int32_t multipleOfCycleTime)
It adds a constraint to resolve a collision, i.e. it adds a precedence for a given multiple of the r...
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.
std::vector< std::vector< RobotPowerMode * > > pwrms
Selected power saving modes of related locations.
Collection of movements between two static activities.
Definition: RoboticLine.h:261
double runTime
Run time (seconds) of the optimization algorithm required for obtaining this solution.
Definition: Solution.h:61
PartialSolution mPartialSolution
Partially fixed problem, or in other words, a partial solution.
Declares LP solver which deals with partially fixed energy optimization problems created by the paral...
State convert(const Status &ilpStatus)
Conversion between the ILP solver status and the solution state.
Definition: Solution.cpp:29
STL namespace.
A general exception of the program.
Definition: Exceptions.h:58
std::vector< std::string > varDesc
Description of the continuous variables.
std::vector< double > solution
The best found solution.
void addTimeLags(const map1to1 &s, bool allRequired=true)
Time lags enforcing correct time synchronizations between robots are added.
Various auxiliary functions used across the program.
Structure storing a solution of an Integer Linear Programming problem.
std::vector< std::string > varDesc
Optional description of the variables.
Definition: ILPModel.h:93
void addSelectedPrecedences(const map1to1 &s, const map1to1 &d, const std::vector< Movement * > &mvs)
Each has-to-be movement imposes a fixed precedence that is added to the formulation in the form of a ...
std::vector< const std::tuple< std::vector< double >, std::vector< double > > * > gurobiC
Convex functions in the criterion in the format suitable for Gurobi solver.
Definition: ILPModel.h:85
std::map< uint32_t, std::pair< uint32_t, uint32_t > > pointAndPowerMode
It maps the identification of a static activity to its assigned coordinate and used power saving mode...
Definition: Solution.h:67
Solver-independent interface for solving Integer Linear Programming problems.
map1to1 s
Maps activity identification to the index of variable corresponding to the start time of this activit...
Exception is thrown if a method is given invalid parameters or a user provides invalid program argume...
Definition: Exceptions.h:120
Collection of locations in which a robot operation (or waiting) can be performed. ...
Definition: RoboticLine.h:304
State status
Enum specifying whether the solution is optimal, feasible, or infeasible...
Definition: Solution.h:55
double criterion
The criterion value of the solution.
Fixed locations, power saving modes, and movements.
RoboticLineSolverLP(Robot *r, const PartialSolution &ps, const PrecalculatedMapping &m)
It builds a Linear Programming problem (variables, constraints, criterion) to be solved.
VariableMappingLP mMapper
Mapping of the continuous variables.
It represents the power saving mode of the robot.
Definition: RoboticLine.h:359
It declares the namespace for program settings.
std::map< uint32_t, uint32_t > inverseMapping(const std::vector< double > &solution, const map2to1 &mapping)
It extracts the variables set to true and returns the ,,selected" indices.
std::vector< double > c
Vector of the criterion coefficients.
Definition: ILPModel.h:83
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...
void addVariablesForSelectedDynamicActivities()
It adds additional float variables for optional activities.
ILPModel mModel
Linear Programming problem to be solved.
double upperBound
The maximal value of the variable.
Definition: ILPModel.h:56
Solution solve() const
The energy optimal timing is determined by solving Linear Programming problem and the solution is ret...
map1to1 W
Maps activity identification to the index of variable corresponding to the energy consumption of this...
map1to1 d
Maps activity identification to the index of variable corresponding to the duration of this activity...
Status status
Solution status, see Status enum.
std::vector< Variable > x
Variables of the problem.
Definition: ILPModel.h:91
std::vector< std::vector< Movement * > > mvs
Selected movements, implied from the selected locations and their order.
SolutionILP solveILP(const ILPModel &m, bool verbose, double gap=0.0, double timeLimit=0.0, int numberOfThreads=1, int threadId=0)
Integer Linear Programming solver is called to solve the problem and the solution is returned...
Definition: CplexSolver.cpp:39
void checkFormulation() const
It checks that sizes of vectors and the matrix are valid.
Definition: ILPModel.cpp:27
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.
double lowerBound
The minimal value of the variable.
Definition: ILPModel.h:54
void checkArguments(const PrecalculatedMapping &m, const uint32_t &numberOfRobots=1u) const
It checks that the arguments passed to the constructor are valid and correct.
Structure containing information about the variable.
Definition: ILPModel.h:48
std::unordered_map< std::pair< Location *, Location * >, Movement * > locationsToMovement
It finds the movement between two locations if exists.
uint64_t moveConstraintsToModel(ILPModel &m)
It efficiently moves all the generated constraints to the given model.
std::map< uint32_t, std::pair< double, double > > startTimeAndDuration
Mapping of the activity identification to its start time and duration.
Definition: Solution.h:65