solver  1.0
ProjectSolver.cpp
Go to the documentation of this file.
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 
24 #include <iostream>
25 #include <fstream>
26 #include <string>
27 #include <sstream>
28 #include "Settings.h"
29 #include "SolverConfig.h"
30 #include "Shared/Exceptions.h"
32 #include "Solution/Solution.h"
37 
38 using namespace std;
39 
42  EXIT_WITH_SUCCESS = 0,
43  INVALID_PARAMETER = 1,
44  INPUT_OUTPUT_ERROR = 2,
45  RUNTIME_ERROR = 4,
46  UNKNOWN_ERROR = 8
47 };
48 
56 void printProgramHeader() {
58  cout<<"Energy optimizator of robotic cells."<<endl;
59  cout<<"Authors: Libor Bukata and Premysl Sucha"<<endl;
60  cout<<"Licence: GNU General Public License"<<endl;
61  cout<<"Program version: "<<PROGRAM_VERSION<<endl;
62  cout<<"ILP solver: "<<solverIdentification()<<endl;
63  cout<<"Build type: "<<BUILD_TYPE<<endl;
64 }
65 
71 void printProgramHelp(const string& progName) {
73  cout<<endl<<"Usage:"<<endl;
74  cout<<"\t"<<progName<<" [options] --dataset FILE"<<endl<<endl;
75  cout<<"General options:"<<endl;
76  cout<<"\t--dataset ARG, -d ARG, ARG: FILE"<<endl;
77  cout<<"\t\tThe input dataset to solve (an xml file)."<<endl;
78  cout<<"\t--verbose, -v"<<endl;
79  cout<<"\t\tAn additional information is printed (solver progress, runtime, etc.)."<<endl;
80  cout<<"\t--number-of-segments ARG, -nos ARG, ARG: INTEGER"<<endl;
81  cout<<"\t\tThe number of segments of each discretized energy function."<<endl;
82  cout<<"\t--number-of-threads ARG, -not ARG, ARG: INTEGER"<<endl;
83  cout<<"\t\tThe number of concurrent threads (default is autodetect)."<<endl;
84  cout<<"\t--help, -h"<<endl;
85  cout<<"\t\tIt prints this help."<<endl;
86  cout<<"\t--max-runtime ARG, -mr ARG, ARG: FLOAT"<<endl;
87  cout<<"\t\t"<<"It sets the time limit per instance for a selected algorithm (in seconds)."<<endl;
88  cout<<"\t--use-heuristic-algorithm, -uha"<<endl;
89  cout<<"\t\t"<<"A heuristic algorithm is employed to solve instances."<<endl;
90  cout<<"\t--use-exact-algorithm, -uea"<<endl;
91  cout<<"\t\t"<<"An exact algorithm is preferred as a problem solver."<<endl;
92  cout<<"\t--write-results ARG, -wr ARG, ARG: DIRECTORY"<<endl;
93  cout<<"\t\tIt specifies where the solutions, error logs, and performance logs will be written."<<endl<<endl;
94  cout<<"ILP solver options:"<<endl;
95  cout<<"\t--ilp-solver-relative-gap ARG, -isrg ARG, ARG: DECIMAL"<<endl;
96  cout<<"\t\tIt stops the solver after achieving the relative gap between the best integer solution and lower bound."<<endl;
97  cout<<"\t\tSetting the gap to 0.05 means that solver stops after proving 5 % maximal gap from the optimal solution."<<endl;
98  cout<<"\t--lower-bound-calculation, -lbc"<<endl;
99  cout<<"\t\tIt turns on the calculation of a tighter lower bound by using a problem decomposition and an ILP solver."<<endl;
100  cout<<"\t--lower-bound-runtime ARG, -lbr ARG, ARG: FLOAT"<<endl;
101  cout<<"\t\tIt sets a time limit for the tight lower bound calculation."<<endl<<endl;
102  cout<<"Heuristic options:"<<endl;
103  cout<<"\t--number-of-elite-solutions ARG, -noes ARG, ARG: INTEGER"<<endl;
104  cout<<"\t\tThe maximal number of elite solutions in the pool."<<endl;
105  cout<<"\t--max-number-of-alternatives ARG, -mnoa ARG, ARG: INTEGER"<<endl;
106  cout<<"\t\tThe maximal number of alternatives to consider for each robot."<<endl;
107  cout<<"\t--minimal-number-of-iters-per-tuple ARG, -mnoipt ARG, ARG: INTEGER"<<endl;
108  cout<<"\t\tThe minimal number of runs of sub-heuristics for each feasible solution. Sub-heuristics, i.e."<<endl;
109  cout<<"\t\t(de)select power mode, change locations, and change path, are executed in the round robin order."<<endl<<endl;
110  cout<<"Default settings can be modified at \"DefaultSettings.h\" file."<<endl;
111 }
112 
114 
130 template <class T>
131 T parsePositiveNumber(const string& number) {
132  if (number.empty() || number[0] == '-')
133  throw InvalidArgument(caller(), "Argument must be a positive number!");
134 
135  T readNumber = T();
136  istringstream istr(number, istringstream::in);
137  istr>>readNumber;
138  if (!istr.eof())
139  throw InvalidArgument(caller(), "Cannot parse the number!");
140 
141  return readNumber;
142 }
143 
154 bool getArgument(int& i, int argc, char* argv[], const string& fullName, const string& shortName, string& writeArgument) {
155  string arg = argv[i];
156  if (arg == fullName || arg == shortName) {
157  if (i+1 < argc) {
158  writeArgument = argv[++i];
159  return true;
160  } else {
161  throw InvalidArgument(caller(), "'"+fullName+"' requires the argument!");
162  }
163  }
164 
165  return false;
166 }
167 
178 bool processArg(int& i, int argc, char* argv[], const string& fullName, const string& shortName, bool* toWrite = nullptr) {
179  string arg = argv[i];
180  if (arg == fullName || arg == shortName) {
181  if (toWrite != nullptr)
182  *toWrite = true;
183  return true;
184  } else {
185  return false;
186  }
187 }
188 
199 bool processArg(int& i, int argc, char* argv[], const string& fullName, const string& shortName, string& toWrite) {
200  return getArgument(i, argc, argv, fullName, shortName, toWrite);
201 }
202 
215 template <class T>
216 bool processArg(int& i, int argc, char* argv[], const string& fullName, const string& shortName, T& toWrite, T minValue = 1) {
217  string strNumber;
218  if (getArgument(i, argc, argv, fullName, shortName, strNumber)) {
219  T number;
220  try {
221  number = parsePositiveNumber<T>(strNumber);
222  } catch (...) {
223  throw_with_nested(InvalidArgument(caller(), "Cannot read the parameter of '"+fullName+"' option!"));
224  }
225 
226  if (number >= minValue) {
227  toWrite = number;
228  return true;
229  } else {
230  throw InvalidArgument(caller(), "The '"+fullName+"' parameter needs to be a positive number!");
231  }
232  } else {
233  return false;
234  }
235 }
236 
243 bool processCommandLineArguments(int argc, char* argv[]) {
244 
245  for (int i = 1; i < argc; ++i) {
246  /* GENERAL OPTIONS */
247  if (processArg(i, argc, argv, "--dataset", "-d", Settings::DATASET_FILE)) continue;
248  if (processArg(i, argc, argv, "--verbose", "-v", &Settings::VERBOSE)) continue;
249  if (processArg(i, argc, argv, "--number-of-segments", "-nos", Settings::NUMBER_OF_SEGMENTS)) continue;
250  if (processArg(i, argc, argv, "--number-of-threads", "-not", Settings::NUMBER_OF_THREADS)) continue;
251  if (processArg(i, argc, argv, "--help", "-h") == true) {
252  printProgramHelp(argv[0]);
253  return true;
254  }
255  if (processArg(i, argc, argv, "--max-runtime", "-mr", Settings::MAX_RUNTIME, 0.0)) continue;
256  if (processArg(i, argc, argv, "--use-heuristic-algorithm", "-uha", &Settings::USE_HEURISTICS)) continue;
257  if (processArg(i, argc, argv, "--use-exact-algorithm", "-uea", &Settings::USE_EXACT_ALGORITHM)) continue;
258  if (processArg(i, argc, argv, "--write-results", "-wr", Settings::RESULTS_DIRECTORY)) continue;
259 
260  /* ILP SOLVER OPTIONS */
261  if (processArg(i, argc, argv, "--ilp-solver-relative-gap", "-isrg", Settings::ILP_RELATIVE_GAP, 0.0)) continue;
262  if (processArg(i, argc, argv, "--lower-bound-calculation", "-lbc", &Settings::CALCULATE_LOWER_BOUND)) continue;
263  if (processArg(i, argc, argv, "--lower-bound-runtime", "-lbr", Settings::RUNTIME_OF_LOWER_BOUND, 0.0)) continue;
264 
265  /* HEURISTIC OPTIONS */
266  if (processArg(i, argc, argv, "--number-of-elite-solutions", "-noes", Settings::MAX_ELITE_SOLUTIONS)) continue;
267  if (processArg(i, argc, argv, "--max-number-of-alternatives", "-mnoa", Settings::MAX_ALTERNATIVES)) continue;
268  if (processArg(i, argc, argv, "--minimal-number-of-iters-per-tuple", "-mnoipt", Settings::MIN_ITERS_PER_TUPLE)) continue;
269 
270  throw InvalidArgument(caller(), "Unknown argument \""+string(argv[i])+"\"!");
271  }
272 
273  if (Settings::DATASET_FILE.empty())
274  throw InvalidArgument(caller(), "Insufficient number of parameters! At least a dataset must be specified!");
275 
276  if (Settings::USE_HEURISTICS == false && Settings::USE_EXACT_ALGORITHM == false) {
279  }
280 
281  if (!Settings::RESULTS_DIRECTORY.empty())
283 
284  return false;
285 }
286 
288 
298 int main(int argc, char* argv[]) {
299 
300  try {
301  // It processes command line arguments.
302  if (argc > 1) {
303  bool exit = processCommandLineArguments(argc, argv);
304  if (exit == true)
305  return EXIT_WITH_SUCCESS;
306  } else {
307  printProgramHelp(argv[0]);
308  return EXIT_WITH_SUCCESS;
309  }
310 
311  if (Settings::VERBOSE)
313 
314  // Dataset parsing, and filling data structures.
315  InstancesReader reader;
316  reader.readInstances();
317  const vector<RoboticLine>& lines = reader.dataset();
318  const vector<PrecalculatedMapping>& mappings = reader.precalculatedMappings();
319 
320  // Each robotic cell in the dataset is optimized.
321  for (uint32_t lineId = 0; lineId < min(lines.size(), mappings.size()); ++lineId) {
322 
323  const RoboticLine& line = lines[lineId];
324  const PrecalculatedMapping& mapping = mappings[lineId];
325 
326  // Find a high quality solution.
327  Solution solution;
329  ParallelHeuristicSolver solver(line, mapping);
330  solution = solver.solve();
331  } else {
332  RoboticLineSolverILP solver(line, mapping);
333  solution = solver.solve();
334  }
335 
336  // Solution is checked for feasibility.
337  SolutionChecker checker(line, mapping, solution);
338  if (checker.checkAll()) {
339  // Calculate the tight lower bound if requested.
340  if (Settings::CALCULATE_LOWER_BOUND == true && (solution.status == OPTIMAL || solution.status == FEASIBLE))
341  solution.lowerBound = RoboticLineSolverILP::lowerBoundOnEnergy(line.robots(), mapping);
342 
343  // Print all the solution or only criterion value depending on the verbosity level.
344  if (Settings::VERBOSE) {
345  cout<<endl;
346  cout<<solution<<endl;
347  } else {
348  writeBriefResultToStream(cout, "instance "+to_string(lineId), solution);
349  }
350 
351  // Optionally, write optimization results to the specified directory.
352  if (!Settings::RESULTS_DIRECTORY.empty()) {
353  writeSolutionToCsvFile(Settings::RESULTS_DIRECTORY+"instance"+to_string(lineId)+".csv", solution, mapping);
354  writeBriefResultToCsvFile("instance "+to_string(lineId), Settings::RESULTS_DIRECTORY+"dataset_results.csv", solution);
355  }
356  } else {
357  if (Settings::VERBOSE)
358  cerr<<endl;
359 
360  // Solution is infeasible even though the algorithm declared it as feasible, i.e. an error in the algorithm.
361  cerr<<"Solution of instance "<<lineId<<" is invalid:"<<endl;
362  for (const string& errMsg : checker.errorMessages())
363  cerr<<errMsg<<endl;
364 
365  // Optionally, write error messages to the log file of the instance.
366  if (!Settings::RESULTS_DIRECTORY.empty()) {
367  string instanceErrorFile = Settings::RESULTS_DIRECTORY+"instance"+to_string(lineId)+".err";
368  ofstream errorLog(instanceErrorFile.c_str());
369  if (errorLog.good()) {
370  for (const string& errMsg : checker.errorMessages())
371  errorLog<<errMsg<<endl;
372  } else {
373  cerr<<"Cannot open an error log for instance "<<lineId<<"!"<<endl;
374  }
375 
376  errorLog.close();
377  }
378  }
379 
380  }
381 
382  } catch (const InvalidDatasetFile& e) {
383  cerr<<exceptionToString(e)<<endl;
384  cerr<<"\nProjectSolver: Cannot read the input dataset!"<<endl;
385  return INPUT_OUTPUT_ERROR;
386  } catch (const InvalidArgument& e) {
387  cerr<<exceptionToString(e)<<endl;
388  return INVALID_PARAMETER;
389  } catch (const SolverException& e) {
390  cerr<<exceptionToString(e)<<endl;
391  cerr<<"\nProjectSolver: A runtime error occurred, terminating..."<<endl;
392  return RUNTIME_ERROR;
393  } catch (const exception& e) {
394  cerr<<exceptionToString(e)<<endl;
395  return UNKNOWN_ERROR;
396  }
397 
398  // We are happy if we get here...
399  return EXIT_WITH_SUCCESS;
400 }
401 
bool USE_EXACT_ALGORITHM
The variable indicates whether the exact algorithm should be used.
Definition: Settings.cpp:33
double ILP_RELATIVE_GAP
If a given relative gap from the best known lower bound is achieved, then the solver stops...
Definition: Settings.cpp:37
bool processCommandLineArguments(int argc, char *argv[])
It parses the program arguments and sets the desired parameters for optimization. ...
The structure representing a solution found by an algorithm.
Definition: Solution.h:50
uint32_t MAX_ALTERNATIVES
The maximal number of alternative orders generated for each robot.
Definition: Settings.cpp:43
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...
Definition: Exceptions.cpp:49
uint32_t NUMBER_OF_SEGMENTS
By how many segments (linear pieces) the energy function of the movement is approximated.
Definition: Settings.cpp:29
string DATASET_FILE
Dataset with problems to be solved.
Definition: Settings.cpp:27
ProgramReturnedCodes
Return codes of the program with obvious meanings.
void printProgramHelp(const string &progName)
It prints the program header and brief help.
The class is intended to be used by users for the dataset parsing and checking.
STL namespace.
Solution solve()
It optimizes the robotic cell and returns the best found solution.
An instance of this class is devoted to the solution checking.
int main(int argc, char *argv[])
An entry point of the program, arguments are processed, dataset is parsed and solved, and results printed and optionally written to files.
Checking and post-processing of parsed datasets.
A general exception of the program.
Definition: Exceptions.h:58
bool CALCULATE_LOWER_BOUND
Indicates whether a tight lower bound should be calculated.
Definition: Settings.cpp:38
double RUNTIME_OF_LOWER_BOUND
Time limit for the tight lower bound.
Definition: Settings.cpp:39
Solution solve(double relGap=Settings::ILP_RELATIVE_GAP, double timLim=Settings::MAX_RUNTIME) const
ILP solver optimizes the energy consumption of the robotic cell until a stop criterion is reached...
std::vector< std::string > errorMessages() const
It returns error message(s) describing why the solution is invalid.
uint32_t MAX_ELITE_SOLUTIONS
The number of top solutions maintained by the heuristic.
Definition: Settings.cpp:42
bool VERBOSE
Boolean flag determining verbosity of the program.
Definition: Settings.cpp:28
uint32_t NUMBER_OF_THREADS
Maximal number of threads to be used.
Definition: Settings.cpp:30
bool checkAll()
It calls the private member methods to verify the solution.
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.
An exact solver for the energy optimization problem.
Solver-independent interface for solving Integer Linear Programming problems.
Exception is thrown if a method is given invalid parameters or a user provides invalid program argume...
Definition: Exceptions.h:120
State status
Enum specifying whether the solution is optimal, feasible, or infeasible...
Definition: Solution.h:55
#define DEFAULT_USE_HEURISTICS
It specifies whether the heuristic algorithm should be preferred by default.
void writeBriefResultToCsvFile(const std::string &instanceName, const std::string &pathToFile, const Solution &s)
It writes the solution to the file in the same format as writeBriefResultToStream function...
Definition: Solution.cpp:164
static double lowerBoundOnEnergy(const std::vector< Robot * > &robots, const PrecalculatedMapping &m)
A tight lower bound on the energy consumption.
std::string solverIdentification()
Returns an identification of the used solver, e.g. 'Gurobi 6.0.4'.
Definition: CplexSolver.cpp:30
bool processArg(int &i, int argc, char *argv[], const string &fullName, const string &shortName, bool *toWrite=nullptr)
It processes the arguments without parameters.
A parallel hybrid heuristic solving the energy optimization problem of robotic cells.
It declares the namespace for program settings.
A parallel heuristic for the energy optimization of robotic cells.
void printProgramHeader()
It prints a brief description of the program including the authors, license, ILP solver, and version.
string RESULTS_DIRECTORY
If not empty, then the optimization results will be written to this directory.
Definition: Settings.cpp:34
void writeSolutionToCsvFile(const string &file, const Solution &s, const PrecalculatedMapping &mapper)
It writes the solution to a csv file, the header is 'activity id, start time, duration, type, point, power saving mode, movement, energy'.
Definition: Solution.cpp:109
uint32_t MIN_ITERS_PER_TUPLE
The minimal number of optimization iterations per each tuple.
Definition: Settings.cpp:44
A representation of the solution that is algorithm independent.
double lowerBound
Lower bound on energy, i.e. there is not a solution consuming less energy than this number...
Definition: Solution.h:59
#define DEFAULT_USE_EXACT_ALGORITHM
It specifies whether the exact algorithm should be preferred by default.
The file declares a class responsible for checking of solutions.
void writeBriefResultToStream(std::ostream &OUT, const string &instanceName, const Solution &s)
It prints the solution as follows: 'instance name (state, runtime s): energy'.
Definition: Solution.cpp:152
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.
bool getArgument(int &i, int argc, char *argv[], const string &fullName, const string &shortName, string &writeArgument)
It processes the arguments with one parameter.
RoboticLineSolverILP class, declared in this file, addresses the energy optimization problem of robot...
T parsePositiveNumber(const string &number)
double MAX_RUNTIME
Maximal run time of the solver.
Definition: Settings.cpp:31
bool USE_HEURISTICS
The variable indicates whether the heuristic should be used.
Definition: Settings.cpp:32