tensor-0.1.0
 All Data Structures Namespaces Functions Variables Typedefs Enumerations Enumerator Groups Pages
jobs.cc
1 // -*- mode: c++; fill-column: 80; c-basic-offset: 2; indent-tabs-mode: nil -*-
2 /*
3  Copyright (c) 2013 Juan Jose Garcia Ripoll
4 
5  Tensor is free software; you can redistribute it and/or modify it
6  under the terms of the GNU Library General Public License as published
7  by the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU Library General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License along
16  with this program; if not, write to the Free Software Foundation, Inc.,
17  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <tensor/jobs.h>
21 #include <fstream>
22 
23 using namespace jobs;
24 
25 static inline
26 bool is_separator(char c)
27 {
28  return (c == ' ') || (c == '\t') || (c == '\n') || (c == ',') || (c == ';');
29 }
30 
31 std::vector<std::string>
32 split_string(const std::string &s)
33 {
34  std::vector<std::string> output;
35  size_t i = 0, l = s.size();
36  while (i != l) {
37  while (is_separator(s[i])) {
38  if (++i == l)
39  return output;
40  }
41  size_t j;
42  for (j = i+1; (j < l) && !is_separator(s[j]); j++) {
43  (void)0;
44  }
45  output.push_back(s.substr(i, j-i));
46  i = j;
47  }
48  return output;
49 }
50 
51 const Job::Variable Job::no_variable;
52 
53 const Job::Variable
54 Job::parse_line(const std::string &s)
55 {
56  std::vector<std::string> data = split_string(s);
57  // Format:
58  // variable_name separator min_value separator max_value [separator n_steps]
59  // where
60  // variable_name is any string
61  // min_value, max_value are real
62  // n_steps is a non-negative integer, defaulting to 10
63  // separator is any number of spaces, tabs, newlines, commas or semicolons
64  if (data.size() == 0)
65  return no_variable;
66  if (data.size() == 1) {
67  std::cerr << "Missing minimum value for variable " << data[0] << std::endl;
68  return no_variable;
69  }
70  double max, min = atof(data[1].c_str());
71  tensor::index nsteps;
72  if (data.size() == 2) {
73  max = min;
74  nsteps = 1;
75  } else {
76  max = atof(data[2].c_str());
77  if (data.size() == 3) {
78  nsteps = 10;
79  } else if (data.size() > 4) {
80  std::cerr << "Too many arguments for variable " << data[0] << std::endl;
81  return no_variable;
82  } else {
83  nsteps = atoi(data[3].c_str());
84  }
85  }
86  return Variable(data[0], min, max, nsteps);
87 }
88 
89 int
90 Job::parse_file(std::istream &s, var_list &data)
91 {
92  std::string buffer;
93  int line;
94  for (line = 0; 1; line++) {
95  const Variable v = parse_line(buffer);
96  if (v.name().size() == 0)
97  return line;
98  else
99  data.push_back(v);
100  } while(1);
101  return 0;
102 }
103 
104 Job::Job(int argc, const char **argv) :
105  filename_("no file")
106 {
107  bool loaded = false;
108  bool print_jobs = false;
109  bool first_job_found = false;
110  bool last_job_found = false;
111  bool job_blocks_found = false;
112  bool this_job_found = false;
113  tensor::index blocks;
114  tensor::index current_job;
115  int i;
116  for (i = 0; i < argc; i++) {
117  if (!strcmp(argv[i], "--job")) {
118  if (++i == argc) {
119  std::cerr << "Missing argument after --job" << std::endl;
120  abort();
121  }
122  filename_ = std::string(argv[i]);
123  std::ifstream s(argv[i]);
124  int line = parse_file(s, variables_);
125  if (line) {
126  std::cerr << "Syntax error in line " << line << " of job file " << filename_
127  << std::endl;
128  abort();
129  }
130  loaded = true;
131  } else if (!strcmp(argv[i], "--print-jobs")) {
132  print_jobs = true;
133  } else if (!strcmp(argv[i], "--this-job")) {
134  if (++i == argc) {
135  std::cerr << "Missing argument to --this-job" << std::endl;
136  abort();
137  }
138  current_job = atoi(argv[i]);
139  this_job_found = true;
140  } else if (!strcmp(argv[i], "--job-blocks")) {
141  if (++i == argc) {
142  std::cerr << "Missing argument to --job-blocks" << std::endl;
143  abort();
144  }
145  blocks = atoi(argv[i]);
146  job_blocks_found = true;
147  } else if (!strcmp(argv[i], "--first-job")) {
148  if (this_job_found) {
149  std::cerr << "Cannot use --first-job and --this-job simultaneously." << std::endl;
150  abort();
151  }
152  if (++i == argc) {
153  std::cerr << "Missing argument to --first-job" << std::endl;
154  abort();
155  }
156  first_job_ = atoi(argv[i]);
157  first_job_found = true;
158  } else if (!strcmp(argv[i], "--last-job")) {
159  if (!first_job_found) {
160  std::cerr << "--last-job without preceding --first-job" << std::endl;
161  abort();
162  }
163  if (++i == argc) {
164  std::cerr << "Missing argument to --last-job" << std::endl;
165  abort();
166  }
167  last_job_ = atoi(argv[i]);
168  last_job_found = true;
169  } else if (!strcmp(argv[i], "--variable")) {
170  if (++i == argc) {
171  std::cerr << "Missing argument after --variable" << std::endl;
172  abort();
173  }
174  Variable v = parse_line(argv[i]);
175  if (v.name().size() == 0) {
176  std::cerr << "Syntax error parsing --variable argument:" << std::endl
177  << argv[i] << std::endl;
178  abort();
179  }
180  variables_.push_back(v);
181  loaded = true;
182  } else if (!strcmp(argv[i], "--help")) {
183  std::cout << "Arguments:\n"
184  "--help\n"
185  "\tShow this message and exit.\n"
186  "--print-jobs\n"
187  "\tShow number of jobs and exit.\n"
188  "--first-job / --last-job n\n"
189  "\tRun this program completing the selected interval of jobs.\n"
190  "--this-job n\n"
191  "\tSelect to run one job or one job block.\n"
192  "--jobs-blocks n\n"
193  "\tSplit the number of blocks into 'n' blocks and run the jobs in the\n"
194  "\tblock selected by --this-job.\n"
195  "--variable name,min[,max[,nsteps]]\n"
196  "\tDefine variable and the range it runs, including number of steps.\n"
197  "\t'min' and 'max' are floating point numbers; 'name' is a string\n"
198  "\twithout spaces; 'nsteps' is a positive (>0) integer denoting how\n"
199  "\tmany values in the interval [min,max] are used. There can be many\n"
200  "\t--variable arguments, as a substitute for a job file. If both 'max'\n"
201  "\tand 'nsteps' are missing, the variable takes a unique, constant\n"
202  "\tvalue, 'min'.\n"
203  "--job filename\n"
204  "\tParse file, loading variable ranges from them using the syntax above,\n"
205  "\tbut allowing spaces or tabs instead of commas as separators."
206  << std::endl;
207  exit(0);
208  }
209  }
210  number_of_jobs_ = compute_number_of_jobs();
211  if (print_jobs) {
212  std::cout << number_of_jobs_;
213  exit(0);
214  }
215  if (job_blocks_found) {
216  /*
217  * We split the number of jobs into sets and we are going to run the
218  * set indicated by --this-job (which defaults to 0)
219  */
220  if (first_job_found || last_job_found) {
221  std::cerr << "The options --job-blocks and --first/last-job cannot be used together."
222  << std::endl;
223  abort();
224  }
225  if (blocks <= 0) {
226  std::cerr << "The number of job blocks cannot be zero or negative.";
227  abort();
228  }
229  tensor::index delta = (number_of_jobs_ + blocks - 1) / blocks;
230  first_job_ = current_job = std::min(current_job * delta, number_of_jobs_);
231  last_job_ = std::min(number_of_jobs_ - 1, first_job_ + delta - 1);
232  } else if (first_job_found) {
233  /*
234  * --first-job and --last-job combined
235  */
236  if (first_job_ < 0) {
237  std::cerr << "--first-job is negative" << std::endl;
238  abort();
239  } else if (first_job_ >= number_of_jobs_) {
240  std::cerr << "--first-job exceeds the number of jobs" << std::endl;
241  abort();
242  }
243  if (last_job_found) {
244  if (last_job_ >= number_of_jobs_) {
245  std::cerr << "warning: --last-job exceeds the number of jobs" << std::endl;
246  last_job_ = std::max<tensor::index>(0, number_of_jobs_ - 1);
247  }
248  if (last_job_ < first_job_) {
249  std::cerr << "--last-job is smaller than --first-job" << std::endl;
250  abort();
251  }
252  }
253  current_job = first_job_;
254  } else if (this_job_found) {
255  /*
256  * Only --this-job, which allows us to do only one thing
257  */
258  first_job_ = last_job_ = current_job;
259  } else {
260  /*
261  * No options: we run over all jobs
262  */
263  first_job_ = current_job = 0;
264  last_job_ = number_of_jobs_ - 1;
265  }
266  select_job(current_job);
267 }
268 
269 tensor::index
270 Job::compute_number_of_jobs() const
271 {
272  tensor::index n = 1;
273  for (var_list::const_iterator it = variables_.begin();
274  it != variables_.end();
275  it++) {
276  n *= it->size();
277  }
278  return n;
279 }
280 
281 void
282 Job::select_job(tensor::index which)
283 {
284  assert(which >= 0);
285  tensor::index i = this_job_ = which;
286  if (which <= number_of_jobs_) {
287  for (var_list::iterator it = variables_.begin();
288  it != variables_.end();
289  it++) {
290  tensor::index n = it->size();
291  it->select(i % n);
292  i = i / n;
293  }
294  }
295 }
296 
297 void
298 Job::operator++()
299 {
300  select_job(this_job_ + 1);
301 }
302 
303 bool
304 Job::to_do()
305 {
306  return (this_job_ >= 0) &&
307  (this_job_ < number_of_jobs_) &&
308  (this_job_ >= first_job_) &&
309  (this_job_ <= last_job_);
310 }
311 
312 const Job::Variable *
313 Job::find_variable(const std::string &name) const
314 {
315  for (var_list::const_iterator it = variables_.begin(); it != variables_.end(); it++) {
316  if (it->name() == name) {
317  return &(*it);
318  }
319  }
320  return NULL;
321 }
322 
323 double
324 Job::get_value(const std::string &name) const
325 {
326  const Variable *which = find_variable(name);
327  if (!which) {
328  std::cerr << "Variable " << name << " not found in job file "
329  << filename_ << std::endl;
330  abort();
331  }
332  return which->value();
333 }
334 
335 
336 double
337 Job::get_value_with_default(const std::string &name, double def) const
338 {
339  const Variable *which = find_variable(name);
340  return which? which->value() : def;
341 }