pmm  1.0.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
pmm_selector.c
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008-2010 Robert Higgins
3  Author: Robert Higgins <robert.higgins@ucd.ie>
4 
5  This file is part of PMM.
6 
7  PMM is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  PMM is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with PMM. If not, see <http://www.gnu.org/licenses/>.
19 */
20 /*!
21  * @file pmm_selector.c
22  * @brief Code for chosing benchmark points
23  *
24  * This is the core of the application, implementing many methods to choose
25  * benchmarking points to minimise the construction time of a model
26  */
27 #if HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include <stdlib.h> // for malloc, free, rand, exit
32 
33 #include "pmm_model.h"
34 #include "pmm_selector.h"
35 #include "pmm_interval.h"
36 #include "pmm_param.h"
37 #include "pmm_load.h"
38 #include "pmm_log.h"
39 
40 #ifdef HAVE_MUPARSER
41 #include "pmm_muparse.h"
42 #endif
43 /*******************************************************************************
44  * gbbp_select_new_bench
45  *
46  * this function uses the current construction state of the model to select
47  * a new point in the model to benchmark.
48  *
49  * State Description:
50  *
51  * EMPTY - the model is empty, the selected benchmark will be the smallest
52  * problem size
53  * CLIMB - the model is in the initial stages of construction and the speed
54  * of execution is getting faster for larger problem sizes
55  * BISECT - we are in the main phase of model construction, we bisect intervals
56  * that are retrieved from a stack. The stack contains intervals of the
57  * performance model in which we have not finalised construction
58  *
59  * The Stack:
60  *
61  * The construction stack contains intervals of the model where the model has
62  * not yet been fully constructed. New intervals are added to the stack and old
63  * ones removed after a benchmark has been completed and is being added to the
64  * model. This process is taken care of in a seperate stage by the
65  * gbbp_insert_benchmark function.
66  *
67  * gbbp_insert_benchmark also handles the changing of the construction state
68  * and that is described there
69  *
70  * this function reads an interval off the top of the stack and selects a new
71  * benchmark point based on that interval.
72  */
73 /*
74 int gbbp_select_new_bench(struct pmm_routine *r) {
75 
76  struct pmm_interval *i;
77 
78  /// read construction interval from top of stack
79  if(read_top_interval(r->model->interval_list, i) == 0) {
80  printf("[gbbp_select_new_bench]: Error reading interval from stack.\n");
81  return -1;
82  }
83 
84  // return new benchmark based on that interval
85  return gbbp_bench_from_interval(r, i);
86 
87 }
88 */
89 
90 int
91 check_benchmarking_minimums(struct pmm_routine *r, double t, int n);
92 int
93 rand_between(int min, int max);
94 
95 int
97 int
99 int
101 int
103 
104 int
106 int
107 process_interval_list(struct pmm_routine *r, struct pmm_benchmark *b,
108  struct pmm_loadhistory *h);
109 int
110 process_interval(struct pmm_routine *r, struct pmm_interval *i,
111  struct pmm_benchmark *b, struct pmm_loadhistory *h);
112 int
113 process_it_gbbp_empty(struct pmm_routine *r, struct pmm_interval *i);
114 int
115 process_it_gbbp_climb(struct pmm_routine *r, struct pmm_interval *i,
116  struct pmm_benchmark *b, struct pmm_loadhistory *h);
117 int
118 process_it_gbbp_bisect(struct pmm_routine *r, struct pmm_interval *i,
119  struct pmm_benchmark *b, struct pmm_loadhistory *h);
120 int
122  struct pmm_benchmark *b, struct pmm_loadhistory *h);
123 int
124 process_it_point(struct pmm_routine *r, struct pmm_interval *i);
125 
126 int
127 naive_step_interval(struct pmm_routine *r, struct pmm_interval *interval);
128 int
130  struct pmm_paramdef_set *pd_set);
131 int
133  struct pmm_paramdef_set *pd_set);
134 int
135 set_params_step_along_climb_interval(int *params, int step,
136  struct pmm_interval *i,
137  struct pmm_paramdef_set *pd_set);
138 void
139 set_params_interval_midpoint(int *p, struct pmm_interval *i);
140 int
142  struct pmm_paramdef_set *pd_set);
143 int
144 is_interval_divisible(struct pmm_interval *i, struct pmm_routine *r);
145 int
147 
148 struct pmm_interval*
149 new_projection_interval(int *p, struct pmm_paramdef *pd, int d,
150  int n);
151 int
153  struct pmm_loadhistory *h,
154  struct pmm_interval **found_i);
155 
156 int
158  struct pmm_interval *interval,
159  int *params);
160 
161 void mesh_boundary_models(struct pmm_model *m);
162 void recurse_mesh(struct pmm_model *m, int *p, int plane, int n_p);
163 /*
164  * functions to write:
165  *
166  * bench_cut_greater(a,b) - takes benchmarks, uses history to calculate cuts and
167  * works out if a is greater than b
168  *
169  * bench_cut_contains(a,b) - takes benchmarks, uses history to calculate cuts
170  * and works out if b is contained by a
171  *
172  * bench_cut_intersects(a,b) - takes benchmarks, uses history to calculate cuts
173  * and works of it a intersects b
174  *
175  */
176 
177 
178 /*!
179  * Select a new benchmark following a naive construction method where every
180  * possible point in the model is benchmarked.
181  *
182  * This action is simply taking the top interval from the construction stack
183  * and returning the parameters it describes.
184  *
185  * @param r pointer to the routine who's model is under construction
186  *
187  * @return pointer to newly allocated parameter array describing next bench
188  * point or NULL on error
189  *
190  */
191 int*
193 {
194  int *params;
195 
196  struct pmm_interval_list *i_list;
197  struct pmm_interval *new_i, *top_i;
198  struct pmm_model *m;
199 #ifdef HAVE_MUPARSER
200  int i;
201  double pc;
202 #endif
203 
204 
205  m = r->model;
206  i_list = m->interval_list;
207 
208  // if interval list is empty initialise with a point interval at the start
209  // parameter point
210  if(isempty_interval_list(i_list) == 1) {
211  DBGPRINTF("Interval list is empty, initialising new interval list.\n");
212 
213  new_i = new_interval();
214  new_i->type = IT_POINT;
215  new_i->n_p = r->pd_set->n_p;
216  new_i->start = init_param_array_start(r->pd_set);
217 
218  if(new_i->start == NULL) {
219  ERRPRINTF("Error initialising start parameter array.\n");
220 
221  free_interval(&new_i);
222 
223  return NULL;
224  }
225 
226  // if pc min/max is defined
227  if(r->pd_set->pc_max != -1 || r->pd_set->pc_min != -1) {
228 #ifdef HAVE_MUPARSER
229  i = 0;
230  while(i != new_i->n_p ) { // while not finished the naive space
231 
232  if(evaluate_constraint_with_params(r->pd_set->pc_parser,
233  new_i->start, &pc) < 0)
234  {
235  ERRPRINTF("Error evalutating parameter constraint.\n");
236 
237  free_interval(&new_i);
238 
239  return NULL;
240  }
241 
242  if(r->pd_set->pc_max != -1 &&
243  r->pd_set->pc_min == -1)
244  { //test only max
245  if(pc > r->pd_set->pc_max) {
246  i = naive_step_interval(r, new_i);
247  }
248  else {
249  break; //finished
250  }
251  }
252  else if(r->pd_set->pc_max == -1 &&
253  r->pd_set->pc_min != -1)
254  { //test only min
255  if(pc < r->pd_set->pc_min) {
256  i = naive_step_interval(r, new_i);
257  }
258  else {
259  break; //finished
260  }
261  }
262  else
263  { //test both
264  if(pc < r->pd_set->pc_min || pc > r->pd_set->pc_max) {
265  i = naive_step_interval(r, new_i);
266  }
267  else {
268  break;
269  }
270  }
271  }
272  if(i == new_i->n_p) {
273  ERRPRINTF("Could not initialise naive start point given pc "
274  "min/max restrictions.\n");
275 
276  free_interval(&new_i);
277 
278  return NULL;
279  }
280 #else
281  ERRPRINTF("muParser not enabled at configure.\n");
282 
283  free_interval(&new_i);
284 
285  return NULL;
286 #endif /* HAVE_MUPARSER */
287  }
288 
289  add_top_interval(i_list, new_i);
290 
291  }
292 
293 
294  if((top_i = read_top_interval(i_list)) == NULL) {
295  ERRPRINTF("Error reading interval from head of list\n");
296  return NULL;
297  }
298 
299  DBGPRINTF("Choosing benchmark based on following interval:\n");
300  print_interval(PMM_DBG, top_i);
301 
302  switch(top_i->type) {
303  case IT_POINT :
304 
305  DBGPRINTF("assigning copy of interval 'point'.\n");
306 
307  params = init_param_array_copy(top_i->start, top_i->n_p);
308  if(params == NULL) {
309  ERRPRINTF("Error copying interval parameter point.\n");
310  return NULL;
311  }
312 
313  align_params(params, r->pd_set);
314 
315  return params;
316 
317  default :
318  ERRPRINTF("Invalid interval type: %s\n",
319  interval_type_to_string(top_i->type));
320  return NULL;
321  }
322 }
323 
324 /*!
325  * Process the insertion of a new benchmark into a model being constructed
326  * with a naive method.
327  */
328 int
330 {
331 
332  double time_spend;
333  int num_execs;
334  int ret;
335 
336  ret = 0;
337 
338  //DBGPRINTF("benchmark params:\n");
339  //print_params(PMM_DBG, b->p, b->n_p);
340 
341  if(insert_bench(r->model, b) < 0) {
342  ERRPRINTF("Error inserting benchmark.\n");
343  ret = -2;
344  }
345 
346  //first check time spent benchmarking at this point, if it is less than
347  //the threshold, we won't bother processing the interval list (so that
348  //the benchmark may be executed again, until it exceeds the threshold
349  calc_bench_exec_stats(r->model, b->p, &time_spend, &num_execs);
350 
351  DBGPRINTF("total time spent benchmarking point: %f\n", time_spend);
352  DBGPRINTF("total number executions at benchmarking point: %d\n", num_execs);
353 
354  if(check_benchmarking_minimums(r, time_spend, num_execs))
355  {
356  DBGPRINTF("benchmarking threshold exceeeded (t:%d, n:%d), processing intervals.\n", r->min_sample_time, r->min_sample_num);
357 
358 
359 
360  if(naive_process_interval_list(r, b) < 0) {
361  ERRPRINTF("Error proccessing intervals, inserting bench anyway\n");
362  ret = -1;
363  }
364 
365 
366 
367  } else {
368  DBGPRINTF("benchmarking threshold not exceeded.\n");
369  }
370 
371 
372  // after intervals have been processed ...
373 
374  return ret;
375 }
376 
377 /*!
378  * check benchmark execution statistics against minimum requirements
379  * in routine configuration
380  *
381  * @param r pointer to the routine
382  * @param t time spent benchmarking as a double
383  * @param n number of benchmarks taken
384  *
385  * @return 0 if minimums are not satisfied, 1 if they are
386  */
387 int
388 check_benchmarking_minimums(struct pmm_routine *r, double t, int n)
389 {
390  if((r->min_sample_time != -1 && t >= (double)r->min_sample_time) ||
391  (r->min_sample_num != -1 && n >= r->min_sample_num) ||
392  (r->min_sample_time == -1 && r->min_sample_num == -1))
393  {
394  return 1;
395  }
396  else {
397  return 0;
398  }
399 }
400 
401 /*!
402  * Process the interval list of the naive construction method, after a new
403  * benchmark point has been aquired.
404  *
405  * Check that the top interval corresponds to the new benchmark and if it
406  * does, step the parameter point of the top interval along to the next
407  * target point.
408  *
409  * In the naive construction method we benchmark every possible point in the
410  * parameter space described by the parameter definitions. In general, this
411  * set of points P is given by the n-ary Cartesian product of the sets of
412  * each of the n parameters, p_0, p_1, p_n, where such sets are defined by
413  * our parameter definitions in terms of start values, end values, stride
414  * and offsets.
415  *
416  * We iterate through such a set of points in lexicographical order, i.e.
417  * given points a and b in P, they are successive iff:
418  *
419  * \f$(a_1, a_2, \dots, a_n) <^d (b_1,b_2, \dots, b_n) \iff
420  * (\exists\ m > 0) \ (\forall\ i < m) (a_i = b_i) \land (a_m <_m b_m) \f$
421  *
422  * Given a point p, incrementing it to p' involves the following: Increment
423  * the first term, if this incremented value is greater than the end defined
424  * for the first term (i.e. it has overflowed), set the term to the start
425  * value and equal start_1 and apply this overflow by incrementing the next
426  * term, testing that the next term has not also overflowed, and letting any
427  * overflow cascade through terms of the point. You may recognise this kind
428  * of process in the natural way one counts :-)
429  *
430  * @param r pointer to the routine
431  * @param b pointer to the new benchmark
432  *
433  * @return 0 on success, -1 on failure
434  */
435 int
437 {
438  struct pmm_model *m;
439  struct pmm_interval *interval;
440  struct pmm_interval *new_i;
441  int *aligned_params;
442  int i;
443 #ifdef HAVE_MUPARSER
444  double pc;
445 #endif
446 
447  m = r->model;
448  interval = m->interval_list->top;
449 
450  aligned_params = init_aligned_params(interval->start, r->pd_set);
451  if(aligned_params == NULL) {
452  ERRPRINTF("Error initialising aligned param array.\n");
453  return -1;
454  }
455 
456 
457 
458  //if the benchmark parameters do not match ...
459  if(params_cmp(b->p, aligned_params, b->n_p) != 0) {
460  LOGPRINTF("unexpected benchmark ... expected:\n");
461  print_params(PMM_LOG, interval->start, interval->n_p);
462  LOGPRINTF("got:\n");
463  print_params(PMM_LOG, b->p, b->n_p);
464 
465  return 0; //no error
466  }
467 
468  // step the parameters the interval describes along by 'stride'
469  //
470  // iterate over all parameters
471  if(r->pd_set->pc_max != -1 || r->pd_set->pc_min != -1) {
472 #ifdef HAVE_MUPARSER
473  i = naive_step_interval(r, interval);
474  while(i != interval->n_p ) { // while not finished the naive space
475 
476  if(evaluate_constraint_with_params(r->pd_set->pc_parser,
477  interval->start, &pc) < 0)
478  {
479  ERRPRINTF("Error evalutating parameter constraint.\n");
480  return -1;
481  }
482 
483  if(r->pd_set->pc_max != -1 &&
484  r->pd_set->pc_min == -1)
485  { //test only max
486  if(pc > r->pd_set->pc_max) {
487  i = naive_step_interval(r, interval);
488  }
489  else {
490  break; //finished
491  }
492  }
493  else if(r->pd_set->pc_max == -1 &&
494  r->pd_set->pc_min != -1)
495  { //test only min
496  if(pc < r->pd_set->pc_min) {
497  i = naive_step_interval(r, interval);
498  }
499  else {
500  break; //finished
501  }
502  }
503  else
504  { //test both
505  if(pc < r->pd_set->pc_min || pc > r->pd_set->pc_max) {
506  i = naive_step_interval(r, interval);
507  }
508  else {
509  break;
510  }
511  }
512  }
513 #else
514  ERRPRINTF("muParser not enabled at configure.\n");
515  return -1;
516 #endif /* HAVE_MUPARSER */
517  }
518  else {
519  i = naive_step_interval(r, interval);
520  }
521 
522  //if the most signicant parameter/digit also overflowed that means we
523  //have gone through all parameters as defined by our constraints,
524  //construction is now complete.
525  if(i==interval->n_p) {
526 
527  LOGPRINTF("Naive construction complete.\n");
528  remove_interval(m->interval_list, interval);
529 
530  if(isempty_interval_list(m->interval_list) == 1) {
531  new_i = new_interval();
532  new_i->type = IT_COMPLETE;
533  add_top_interval(m->interval_list, new_i);
534 
535  m->complete = 1;
536  }
537  else {
538  ERRPRINTF("Expected empty construction interval list but found:\n");
540  return -1;
541  }
542  }
543 
544  return 0;
545 }
546 
547 /*!
548  * Step the start point of a naive interval to the next benchmark point.
549  *
550  * @param r pointer to the routine
551  * @param interval pointer to the interval
552  *
553  * @return index of the last parameter that was incremented or N (the number of
554  * parameters) when all paramters overflowed, i.e. the start point could not
555  * be stepped and the last naive benchmark has been reached.
556  */
557 int
558 naive_step_interval(struct pmm_routine *r, struct pmm_interval *interval)
559 {
560  int i;
561  int direction;
562 
563  for(i=0; i<interval->n_p; i++) {
564 
565  if(r->pd_set->pd_array[i].start > r->pd_set->pd_array[i].end) {
566  direction = -1;
567  }
568  else {
569  direction = 1;
570  }
571 
572  // if stepping a parameter does not cause it to leave its boundaries
573  if(param_within_paramdef(interval->start[i] +
574  direction*r->pd_set->pd_array[i].stride,
575  &(r->pd_set->pd_array[i])) == 1)
576  {
577  // increment the parameter as tested and finish
578  interval->start[i] += direction*r->pd_set->pd_array[i].stride;
579 
580  print_params(PMM_DBG, interval->start, r->pd_set->n_p);
581 
582  break; // we are done
583  }
584  else {
585  //otherwise overflow would occur so set this parameter to
586  //it's start value and allow the overflow to cascade into the
587  //next parameter by continuing through the loop (and trying to
588  //iterate the next parameter)
589  interval->start[i] = r->pd_set->pd_array[i].start;
590  }
591  }
592 
593  return i;
594 }
595 
596 /*!
597  * Returns a new point to benchmark using the naive bisection in 1 dimension
598  * method. Briefly, this method initialises a bisection construction interval
599  * across the problem space (1-d only). Then recursively bisects this interval
600  * and benchmarks at bisection points, until it is no longer divisible.
601  *
602  * @param r pointer to the routine for which we will find a new benchmark
603  * point
604  *
605  * @return pointer to an array describing the new benchmark point or NULL on
606  * failure
607  */
608 int*
610 {
611  int *params;
612 
613  struct pmm_interval_list *i_list;
614  struct pmm_interval *top_i;
615  struct pmm_model *m;
616 
617  m = r->model;
618  i_list = m->interval_list;
619 
620 
621  if(r->pd_set->n_p != 1) {
622  ERRPRINTF("Error, Naive 1D Bisection method cannot be used to construct"
623  " for multi-dimensional problem.\n");
624  return NULL;
625  }
626 
627 
628 
629  //if model construction has not started, i_list will be empty
630  if(isempty_interval_list(i_list) == 1) {
631 
632  DBGPRINTF("Interval list is empty, initializing new interval list.\n");
633 
634  // create a new bisection interval type from start to end
635  // create point intervals a the end points of the the bisection interval
636  if(init_naive_1d_intervals(r) < 0) {
637  ERRPRINTF("Error initialising new construction intervals.\n");
638  return NULL;
639  }
640 
641  }
642 
643  params = malloc(r->pd_set->n_p * sizeof *params);
644  if(params == NULL) {
645  ERRPRINTF("Error allocating memory.\n");
646  return NULL;
647  }
648 
649  if((top_i = read_top_interval(i_list)) == NULL) {
650  ERRPRINTF("Error reading interval from head of list\n");
651 
652  free(params);
653  params = NULL;
654 
655  return NULL;
656  }
657 
658  DBGPRINTF("Choosing benchmark based on following interval:\n");
659  print_interval(PMM_DBG, top_i);
660 
661  switch(top_i->type) {
662  case IT_GBBP_BISECT :
663 
664  DBGPRINTF("Applying step of GBBP to benchmark selection.\n");
665 
666  //apply GBBP to the bisection interval (we will get midpoint)
667  multi_gbbp_bench_from_interval(r, top_i, params);
668  break;
669 
670 
671  case IT_POINT :
672 
673  DBGPRINTF("Benchmarking at point ...\n");
674  //benchmark at this point
675  multi_gbbp_bench_from_interval(r, top_i, params);
676  break;
677 
678  default :
679  ERRPRINTF("Invalid interval type: %s (%d)\n",
680  interval_type_to_string(top_i->type), top_i->type);
681  print_interval(PMM_ERR, top_i);
682 
683  free(params);
684  params = NULL;
685 
686  break;
687  }
688 
689  return params;
690 }
691 
692 
693 /*!
694  * Function initialises a construction intervals for 1d naive bisection. A
695  * interval covering the problem space with bisection type is created. Two
696  * further 'point' type intervals are created at the end points of the
697  * bisection interval.
698  *
699  * @param r pointer to the routine
700  *
701  * @return 0 on success or -1 on failure
702  */
703 int
705 {
706  struct pmm_model *m;
707  struct pmm_interval_list *i_list;
708  struct pmm_interval *bisect_i, *point_i;
709 
710  m = r->model;
711  i_list = m->interval_list;
712 
713  // first initialize a bisection interval
714  bisect_i = new_interval();
715  bisect_i->type = IT_GBBP_BISECT;
716 
717  bisect_i->n_p = r->pd_set->n_p;
718 
719  bisect_i->start = init_param_array_start(r->pd_set);
720  bisect_i->end = init_param_array_end(r->pd_set);
721  if(bisect_i->start == NULL || bisect_i->end == NULL) {
722  ERRPRINTF("Error initializing start/end parameter array.\n");
723 
724  free_interval(&bisect_i);
725 
726  return -1;
727  }
728 
729  // adjust the diagonal for pc_max (but not pc_min)
730  if(r->pd_set->pc_max != -1) {
732  }
733  if(r->pd_set->pc_min != -1) {
735  }
736 
737  if(is_interval_divisible(bisect_i, r) == 1) {
738  add_top_interval(m->interval_list, bisect_i);
739  }
740  else {
741  DBGPRINTF("Interval not divisible, not adding.\n");
742  print_interval(PMM_DBG, bisect_i);
743  free_interval(&bisect_i);
744 
745  return -1;
746  }
747 
748  // add benchmark at end of bisection interval
749  point_i = new_interval();
750  point_i->type = IT_POINT;
751  point_i->n_p = r->pd_set->n_p;
752 
753  point_i->start = init_param_array_copy(bisect_i->end, point_i->n_p);
754  if(point_i->start == NULL) {
755  ERRPRINTF("Error initialising parameter array copy.\n");
756  free_interval(&point_i);
757  return -1;
758  }
759  add_top_interval(i_list, point_i);
760 
761 
762  // add benchmark at start of bisection interval
763  point_i = new_interval();
764  point_i->type = IT_POINT;
765  point_i->n_p = r->pd_set->n_p;
766 
767  point_i->start = init_param_array_copy(bisect_i->start, point_i->n_p);
768  if(point_i->start == NULL) {
769  ERRPRINTF("Error initialising parameter array copy.\n");
770  free_interval(&point_i);
771  return -1;
772  }
773  add_top_interval(i_list, point_i);
774 
775  return 0; //success
776 }
777 
778 /*!
779  * builds a multi-parameter piece-wise performance model using a random
780  * selection of benchmark points
781  *
782  * Alogirthm description:
783  *
784  * if model is empty
785  * select start values for all parameters and return benchmark point
786  * else
787  * seed random generator
788  *
789  * for each parameter
790  * select a random parameter size based on the paramdef limits and return
791  *
792  * @param r pointer to routine for which the model is being built
793  *
794  * @return pointer to newly allocated array describing parameters for the
795  * benchmark
796  */
798 {
799  int *params;
800  int i;
801 
802 
803  //allocate parameter return array
804  params = malloc(r->pd_set->n_p * sizeof *params);
805  if(params == NULL) {
806  ERRPRINTF("Error allocating memory.\n");
807  return NULL;
808  }
809 
810 
811 
812  //if model is empty
813  if(isempty_model(r->model)) {
814 
815  //set paremeter reutrn array to the origin point
816  set_param_array_start(params, r->pd_set);
817 
818  }
819  else {
820 
821  //seed
822  srand(time(NULL));
823 
824  //choose a random set of params within the parmdef limits
825  //make sure choosen random params are not already in the model
826  //do {
827  // for(i=0; i<r->pd_set->n_p; i++) {
828  // params[i] = rand_between(r->pd_set->pd_array[i].start,
829  // r->pd_set->pd_array[i].end);
830  // }
831  //} while(get_bench(r->model, params) != NULL); //test in model already
832 
833  for(i=0; i<r->pd_set->n_p; i++) {
834  params[i] = rand_between(r->pd_set->pd_array[i].start,
835  r->pd_set->pd_array[i].end);
836  }
837 
838  align_params(params, r->pd_set);
839 
840  }
841 
842  //return benchmark parameters
843  return params;
844 }
845 
846 
847 /*!
848  * find a random integer between two values (inclusive)
849  *
850  * @param min first integer
851  * @param max second integer
852  *
853  * @return random integer between the two values passed
854  */
855 int
856 rand_between(int min, int max)
857 {
858  //TODO static/inline
859  if(min==max) {
860  return min;
861  }
862 
863  // if min is greater than max, swap them around
864  if(min>max) {
865  max = max+min;
866  min = max-min; //min now equals original max
867  max = max-min; //max now equals original min
868  }
869 
870  return (int)(rand()%(max-min))+min;
871 }
872 
873 
874 /*!
875  * Returns a new point to benchmark using the Multidimensional Naive GBBP
876  * method. Briefly, this method initialises construction intervals in a grid
877  * form, through all possible points as defined by the parameter definitions.
878  * Then GBBP is applied to all construction intervals to select benchmark
879  * points, until the model has been built along all lines in the grid.
880  *
881  * @param r pointer to the routine for which we will find a new benchmark
882  * point
883  *
884  * @return pointer to an array describing the new benchmark point or NULL on
885  * failure
886  */
887 int*
889 {
890  int *params;
891 
892  struct pmm_interval_list *i_list;
893  struct pmm_interval *top_i;
894  struct pmm_model *m;
895 
896  m = r->model;
897  i_list = m->interval_list;
898 
899  //if model construction has not started, i_list will be empty
900  if(isempty_interval_list(i_list) == 1) {
901 
902  DBGPRINTF("Interval list is empty, initializing new interval list.\n");
903 
904  if(init_gbbp_naive_intervals(r) < 0) {
905  ERRPRINTF("Error initialising new construction intervals.\n");
906  return NULL;
907  }
908  }
909 
910  params = malloc(r->pd_set->n_p * sizeof *params);
911  if(params == NULL) {
912  ERRPRINTF("Error allocating memory.\n");
913  return NULL;
914  }
915 
916  if((top_i = read_top_interval(i_list)) == NULL) {
917  ERRPRINTF("Error reading interval from head of list\n");
918 
919  free(params);
920  params = NULL;
921 
922  return NULL;
923  }
924 
925  DBGPRINTF("Choosing benchmark based on following interval:\n");
926  print_interval(PMM_DBG, top_i);
927 
928  switch(top_i->type) {
929  case IT_GBBP_EMPTY :
930  case IT_GBBP_CLIMB :
931  case IT_GBBP_BISECT :
932  case IT_GBBP_INFLECT :
933 
934  DBGPRINTF("Applying step of GBBP to benchmark selection.\n");
935 
936  //apply GBBP
937  multi_gbbp_bench_from_interval(r, top_i, params);
938  break;
939 
940 
941  case IT_POINT :
942 
943  DBGPRINTF("Benchmarking at point ...\n");
944  //benchmark at this point
945  multi_gbbp_bench_from_interval(r, top_i, params);
946  break;
947 
948  default :
949  ERRPRINTF("Invalid interval type: %s (%d)\n",
950  interval_type_to_string(top_i->type), top_i->type);
951  print_interval(PMM_ERR, top_i);
952 
953  free(params);
954  params = NULL;
955 
956  break;
957  }
958 
959  return params;
960 }
961 
962 /*!
963  * Function initialises a construction intervals in a grid form across the
964  * whole parameter space. To form the grid, for each parameter, for all
965  * possible parameter values on that axis, intervals are projected parallel to
966  * all other axes.
967  *
968  * Other intervals are added to benchmark neccessary extremeties of the
969  * parameter space and to tag the completion of the diagonal and the whole
970  * model in general.
971  *
972  * @param r pointer to the routine
973  *
974  * @return 0 on success or -1 on failure
975  */
976 int
978 {
979  int j;
980  struct pmm_model *m;
981  struct pmm_interval_list *i_list;
982  struct pmm_interval *diag_i, *proj_i, *point_i;
983  struct pmm_benchmark *zero_b;
984 
985  int ret;
986  int *step_params;
987  int step;
988 
989  m = r->model;
990  i_list = m->interval_list;
991 
992  step_params = malloc(r->pd_set->n_p * sizeof *step_params);
993  if(step_params == NULL) {
994  ERRPRINTF("Error allocating memory.\n");
995  return -1;
996  }
997 
998  // first initialize a diagonal interval
999  diag_i = new_interval();
1000  diag_i->type = IT_GBBP_EMPTY;
1001 
1002  diag_i->n_p = r->pd_set->n_p;
1003 
1004  diag_i->start = init_param_array_start(r->pd_set);
1005  diag_i->end = init_param_array_end(r->pd_set);
1006  if(diag_i->start == NULL || diag_i->end == NULL) {
1007  ERRPRINTF("Error initializing start/end parameter array.\n");
1008 
1009  free_interval(&diag_i);
1010 
1011  free(step_params);
1012  step_params = NULL;
1013 
1014  return -1;
1015  }
1016 
1017  // adjust the diagonal for pc_max (but not pc_min)
1018  if(r->pd_set->pc_max != -1) {
1019 
1021 
1022  }
1023 
1024 
1025  // now step along this diagonal
1026  step = 0;
1027  while((ret = set_params_step_along_climb_interval(step_params, step, diag_i,
1028  r->pd_set)) == 0)
1029  {
1030 
1031  // at each point, iterate through all parameters and project intervals
1032  // perpendicular to all planes
1033  for(j=0; j<r->pd_set->n_p; j++)
1034  {
1035 
1036 
1037  proj_i = new_projection_interval(step_params,
1038  &(r->pd_set->pd_array[j]),
1039  j, r->pd_set->n_p);
1040 
1041  if(proj_i == NULL) {
1042  ERRPRINTF("Error creating projection interval.\n");
1043 
1044  free_interval(&diag_i);
1045 
1046  free(step_params);
1047  step_params = NULL;
1048 
1049  return -1;
1050  }
1051 
1052  //adjust interval for pc_max / pc_min
1053  if(r->pd_set->pc_max != -1) {
1055  }
1056  if(r->pd_set->pc_min != -1) {
1058  }
1059 
1060  // only add if the interval is not zero length and divisible
1061  if(proj_i->start[j] != proj_i->end[j] &&
1062  is_interval_divisible(proj_i, r) == 1)
1063  {
1064  DBGPRINTF("adding interval:\n");
1065  print_interval(PMM_DBG, proj_i);
1066  add_bottom_interval(i_list, proj_i);
1067 
1068  }
1069  else {
1070  DBGPRINTF("Interval not divisible, not adding.\n");
1071  print_interval(PMM_DBG, proj_i);
1072  free_interval(&proj_i);
1073  }
1074 
1075 
1076  // if the projection to a non zero end point (and we did not
1077  // empty the proj_i because it was non-divisible ....
1078  if(r->pd_set->pd_array[j].nonzero_end == 1 && proj_i != NULL)
1079  {
1080  //add an IT_POINT interval at the endpoint of the projected
1081  //interval
1082  point_i = init_interval(0, r->pd_set->n_p, IT_POINT, proj_i->end,
1083  NULL);
1084  if(point_i == NULL) {
1085  ERRPRINTF("Error creating new point interval.\n");
1086  return -1;
1087  }
1088 
1089  DBGPRINTF("Adding non zero end interval naive projection\n");
1090  print_interval(PMM_DBG, point_i);
1091 
1092  add_top_interval(i_list, point_i);
1093 
1094  }
1095  else if(proj_i != NULL)
1096  {
1097  // create a zero speed point at end ...
1098  zero_b = init_zero_benchmark(proj_i->end, r->pd_set->n_p);
1099  if(zero_b == NULL) {
1100  ERRPRINTF("Error allocating new benchmark.\n");
1101  return -1;
1102  }
1103 
1104  //add to model
1105  if(insert_bench(r->model, zero_b) < 0) {
1106  ERRPRINTF("Error inserting a zero benchmark.\n");
1107  free_benchmark(&zero_b);
1108 
1109  return -1;
1110  }
1111  }
1112  }
1113 
1114  step++;
1115  }
1116 
1117  if(ret != -1) {
1118  ERRPRINTF("Error stepping along diagonal interval.\n");
1119 
1120  free_interval(&diag_i);
1121 
1122  free(step_params);
1123  step_params = NULL;
1124 
1125  return -1;
1126  }
1127 
1128 
1129  free_interval(&diag_i);
1130 
1131  free(step_params);
1132  step_params = NULL;
1133 
1134  return 0; //success
1135 }
1136 
1137 
1138 /*!
1139  * Returns a new point to benchmark using the Multidimensional Diagonal GBBP
1140  * method. Briefly, this method first constructs, using the GBBP algorithm,
1141  * on an interval from the start point of all parameter definitions to the end
1142  * point of those definitions. After construction along this interval is
1143  * complete, all points that were measured along it are used to project new
1144  * construction intervals. From each point N construction intervals are
1145  * projected parallel to each of the N parameter axes. GBBP is then applied to
1146  * these new construction intervals, completing the model.
1147  *
1148  * @param r pointer to the routine for which we will find a new benchmark
1149  * point
1150  *
1151  * @return pointer to an array describing the new benchmark point or NULL on
1152  * failure
1153  */
1154 int*
1156 {
1157  int *params;
1158 
1159  struct pmm_interval_list *i_list;
1160  struct pmm_interval *top_i, *new_i;
1161  struct pmm_model *m;
1162 
1163  m = r->model;
1164  i_list = m->interval_list;
1165 
1166  //if model construction has not started, i_list will be empty
1167  if(isempty_interval_list(i_list) == 1) {
1168 
1169  DBGPRINTF("Interval list is empty, initializing new interval list.\n");
1170 
1171  if(init_gbbp_diagonal_interval(r) < 0) {
1172  ERRPRINTF("Error initialising new construction intervals.\n");
1173  return NULL;
1174  }
1175  }
1176 
1177  params = malloc(r->pd_set->n_p * sizeof *params);
1178  if(params == NULL) {
1179  ERRPRINTF("Error allocating memory.\n");
1180  return NULL;
1181  }
1182 
1183  if((top_i = read_top_interval(i_list)) == NULL) {
1184  ERRPRINTF("Error reading interval from head of list\n");
1185 
1186  free(params);
1187  params = NULL;
1188 
1189  return NULL;
1190  }
1191 
1192  DBGPRINTF("Choosing benchmark based on following interval:\n");
1193  print_interval(PMM_DBG, top_i);
1194 
1195  switch(top_i->type) {
1196  case IT_GBBP_EMPTY :
1197  case IT_GBBP_CLIMB :
1198  case IT_GBBP_BISECT :
1199  case IT_GBBP_INFLECT :
1200 
1201  DBGPRINTF("Applying step of GBBP to benchmark selection.\n");
1202 
1203  //apply GBBP
1204  multi_gbbp_bench_from_interval(r, top_i, params);
1205  break;
1206 
1207  case IT_BOUNDARY_COMPLETE :
1208 
1209  DBGPRINTF("GBBP stage of construction complete. Meshing ...\n");
1210 
1211  //remove top interval
1212  remove_interval(i_list, top_i);
1213 
1214  if(r->pd_set->n_p > 1) {
1215  //mesh boundary models to create points
1216  if(project_diagonal_intervals(m) < 0) {
1217  ERRPRINTF("Error projecting new intervals from diagonal\n");
1218 
1219  free(params);
1220  params = NULL;
1221 
1222  return NULL;
1223  }
1224 
1225  //read another top interval
1226  if((top_i = read_top_interval(i_list)) == NULL) {
1227  ERRPRINTF("Error reading interval from head of list\n");
1228 
1229  free(params);
1230  params = NULL;
1231 
1232  return NULL;
1233  }
1234 
1235  DBGPRINTF("Meshing complete, fetching benchmark ...\n");
1236 
1237  //get the benchmark from the interval
1238  multi_gbbp_bench_from_interval(r, top_i, params);
1239  }
1240  else {
1241  ERRPRINTF("Invalid IT_BOUNDARY_COMPLETE in 1 param model.\n");
1242 
1243  // must check to see if interval list is empty now, if
1244  // completion is not tagged here then construction will restart
1246  LOGPRINTF("Multidensional Diagonal GBBP Construction "
1247  "complete.\n");
1248 
1249  new_i = new_interval();
1250  new_i->type = IT_COMPLETE;
1251  add_top_interval(m->interval_list, new_i);
1252 
1253  m->complete = 1;
1254  }
1255 
1256  free(params);
1257  params = NULL;
1258  }
1259 
1260  break;
1261 
1262  case IT_POINT :
1263 
1264  DBGPRINTF("Benchmarking at point ...\n");
1265  //benchmark at this point
1266  multi_gbbp_bench_from_interval(r, top_i, params);
1267  break;
1268 
1269  default :
1270  ERRPRINTF("Invalid interval type: %s (%d)\n",
1271  interval_type_to_string(top_i->type), top_i->type);
1272  print_interval(PMM_ERR, top_i);
1273 
1274  free(params);
1275  params = NULL;
1276 
1277  break;
1278  }
1279 
1280  return params;
1281 }
1282 
1283 /*!
1284  * Function initialises a construction interval between two points which form
1285  * a diagonal through the parameter space defined by the parameter definitions.
1286  * For $n$ parameters with defintions of start points $s_i$ and end points
1287  * $e_i$, the diagonal starts at:
1288  *
1289  * $(s_0, s_1, ..., s_n)$
1290  *
1291  * and ends at:
1292  *
1293  * $(e_0, e_1, ..., e_n)$
1294  *
1295  * @param r pointer to the routine for which the interval will be
1296  * initialised
1297  *
1298  * Other intervals are added to benchmark neccessary extremeties of the
1299  * parameter space and to tag the completion of the diagonal and the whole
1300  * model in general.
1301  *
1302  * @return 0 on success or -1 on failure
1303  */
1304 int
1306 {
1307  int j;
1308  int all_nonzero_end;
1309  struct pmm_model *m;
1310  struct pmm_interval_list *i_list;
1311  struct pmm_interval *new_i, *diag_i;
1312  struct pmm_benchmark *zero_b;
1313 
1314  m = r->model;
1315  i_list = m->interval_list;
1316 
1317  //models with more than 1 boundary need a tag to indicate mesh stage
1318  //of construction
1319  if(r->pd_set->n_p > 1) {
1320  new_i = new_interval();
1321  new_i->type = IT_BOUNDARY_COMPLETE;
1322  add_top_interval(i_list, new_i);
1323  }
1324 
1325 
1326 
1327  // add an empty interval from start -> end along a diagonal through
1328  // the parameter space
1329  diag_i = new_interval();
1330  diag_i->type = IT_GBBP_EMPTY;
1331 
1332  diag_i->n_p = r->pd_set->n_p;
1333 
1334  diag_i->start = init_param_array_start(r->pd_set);
1335  if(diag_i->start == NULL) {
1336  ERRPRINTF("Error initialising start parameter array.\n");
1337  free_interval(&diag_i);
1338  return -1;
1339  }
1340 
1341  diag_i->end = init_param_array_end(r->pd_set);
1342  if(diag_i->end == NULL) {
1343  ERRPRINTF("Error initialising end parameter array.\n");
1344  free_interval(&diag_i);
1345  return -1;
1346  }
1347 
1348  // if parameter product max is set, we must adjust the new interval so
1349  // it satisfies the constraint
1350  if(r->pd_set->pc_max != -1) {
1352  }
1353  if(r->pd_set->pc_min != -1) {
1355  }
1356 
1357  if(is_interval_divisible(diag_i, r) == 1) {
1358  add_top_interval(m->interval_list, diag_i);
1359  }
1360  else {
1361  DBGPRINTF("Interval not divisible, not adding.\n");
1362  print_interval(PMM_DBG, diag_i);
1363  free_interval(&diag_i);
1364  }
1365 
1366 
1367 
1368  // nonzero_end is set there will be no zero-speed benchmarks that
1369  // bound the parameter space of the model. So we must benchmark at these
1370  // points so that or model has a fully bounded space before the GBBP
1371  // algorithm begins
1372  //
1373  // for each parameter, if nonzer end is set, create a POINT interval at
1374  // a end value for that parameter along its axis, i.e.
1375  // start_0,start_1,...,end_i,...start_n,
1376  //
1377  // Also, if nonzero_end is NOT set, set all_nonzer_end to 0/false
1378  all_nonzero_end = 1;
1379  for(j=0; j<r->pd_set->n_p; j++) {
1380  if(r->pd_set->pd_array[j].nonzero_end == 1) {
1381  // add benchmark at end on axis boundary (nonzero end)
1382  new_i = new_interval();
1383  new_i->type = IT_POINT;
1384  new_i->n_p = r->pd_set->n_p;
1385 
1386  new_i->start = init_param_array_start(r->pd_set);
1387  if(new_i->start == NULL) {
1388  ERRPRINTF("Error initialising start parameter array.\n");
1389  free_interval(&new_i);
1390  return -1;
1391  }
1392 
1393  new_i->start[j] = r->pd_set->pd_array[j].end;
1394 
1395  add_top_interval(i_list, new_i);
1396  }
1397  else {
1398  all_nonzero_end = 0;
1399  }
1400  }
1401 
1402 
1403  // if nonzero_end is set on all parameters the then we will benchmark
1404  // at the end point of the diagonal interval. This should be added
1405  // to the top of the interval stack so that it is measured before
1406  // GBBP diagonal construction proceeds
1407  if(all_nonzero_end == 1 && diag_i != NULL) {
1408  DBGPRINTF("nonzero end on all params\n");
1409 
1410  // add benchmark at end of diagonal
1411  new_i = new_interval();
1412  new_i->type = IT_POINT;
1413  new_i->n_p = r->pd_set->n_p;
1414 
1415  new_i->start = init_param_array_copy(diag_i->end, r->pd_set->n_p);
1416  if(new_i->start == NULL) {
1417  ERRPRINTF("Error initialising end point parameter array.\n");
1418  free_interval(&new_i);
1419  return -1;
1420  }
1421 
1422  add_top_interval(i_list, new_i);
1423 
1424  }
1425  else if(diag_i != NULL) {
1426  // if we made an adjustment to max,max,...max via the param
1427  // constraints
1428  if(r->pd_set->pc_max != -1) {
1429 
1430  // create a zero speed point at interval end ...
1431  zero_b = init_zero_benchmark(diag_i->end, r->pd_set->n_p);
1432  if(zero_b == NULL) {
1433  ERRPRINTF("Error allocating new benchmark.\n");
1434  return -1;
1435  }
1436 
1437  //add to model
1438  if(insert_bench(r->model, zero_b) < 0) {
1439  ERRPRINTF("Error inserting a zero benchmark.\n");
1440  free_benchmark(&zero_b);
1441 
1442  return -1;
1443  }
1444  }
1445  }
1446 
1447  DBGPRINTF("New Intervals initialized:\n");
1448  print_interval_list(PMM_DBG, i_list);
1449 
1450  return 0; //success
1451 }
1452 
1453 /*!
1454  * Step along an interval until the product of the parameters at the end/start
1455  * point of the interval matches the minium parameter product
1456  *
1457  * @param i pointer to the interval that is to be adjusted
1458  * @param pd_set pointer to the parameter definition set
1459  *
1460  * @return 0 on success or -1 on failure
1461  */
1462 int
1464  struct pmm_paramdef_set *pd_set)
1465 {
1466 #ifdef HAVE_MUPARSER
1467  int *pc_min_params;
1468  double pp, start_pc, end_pc;
1469  int step;
1470  int ret;
1471 
1472  // test to see if interval end point or start point exceed the pc_min
1473  // if start exceeds, we adjust the start point, if end exceeds, we adjust
1474  // the endpoint, if neither exceed, no adjustments need to be made, and
1475  // if both exceed, there is an error
1476 
1477  if(evaluate_constraint_with_params(pd_set->pc_parser, i->start, &start_pc)
1478  < 0)
1479  {
1480  ERRPRINTF("Error evaluating parameter constraint.\n");
1481  return -1;
1482  }
1483  if(evaluate_constraint_with_params(pd_set->pc_parser, i->end, &end_pc)
1484  < 0)
1485  {
1486  ERRPRINTF("Error evaluating parameter constraint.\n");
1487  return -1;
1488  }
1489 
1490  if(start_pc < pd_set->pc_min && end_pc < pd_set->pc_min) {
1491  ERRPRINTF("Interval cannot be adjusted to fit parameter product "
1492  "constraint.\n");
1493  return -1;
1494  }
1495  else if(start_pc < pd_set->pc_min) {
1496  }
1497  else if(end_pc < pd_set->pc_min) {
1498  }
1499  else {
1500  DBGPRINTF("Interval already satisfies parameter product constraint.\n");
1501  }
1502 
1503  pc_min_params = malloc(pd_set->n_p * sizeof *pc_min_params);
1504  if(pc_min_params == NULL) {
1505  ERRPRINTF("Error allocating memory.\n");
1506  return -1;
1507  }
1508 
1509 
1510 
1511  step = 0;
1512  while(1) {
1513 
1514  // if it is the start point we are adjusting step along start->end
1515  if(start_pc < pd_set->pc_min) {
1516  ret = set_params_step_between_params(pc_min_params,
1517  i->start, i->end, step, pd_set);
1518  }
1519  // otherwise set along end->start
1520  else {
1521  ret = set_params_step_between_params(pc_min_params,
1522  i->end, i->start, step, pd_set);
1523  }
1524 
1525  if(ret < 0) {
1526  break;
1527  }
1528 
1529  // now calculate product at the stepped point
1530  if(evaluate_constraint_with_params(pd_set->pc_parser, pc_min_params,
1531  &pp) < 0)
1532  {
1533  ERRPRINTF("Error evaluating parameter constraint.\n");
1534 
1535  free(pc_min_params);
1536  pc_min_params = NULL;
1537 
1538  return -1;
1539  }
1540 
1541  DBGPRINTF("param product:%f, param_product_min:%d\n", pp,
1542  pd_set->pc_min);
1543 
1544  // stop when pp is greater or equal to pc_min, later we will set
1545  // the start/end point to pc_min_params
1546  if(pp >= pd_set->pc_min) {
1547  break;
1548  }
1549 
1550  step++;
1551  }
1552 
1553  // if we never found a pp that satisfied pc_min, but ran out of steps, then
1554  // just keep the interval as it is ...
1555  if(ret < 0) {
1556  DBGPRINTF("Parameter product was not exceeded by interval.\n");
1557  }
1558  // otherwise lets set the end/start point to the value at the step
1559  // TODO couldn't we just set start/end to the current pc_min_params array?
1560  else {
1561  if(start_pc < pd_set->pc_min) {
1563  i->start, i->end, step, pd_set);
1564  }
1565  else {
1567  i->end, i->start, step, pd_set);
1568  }
1569  }
1570 
1571 
1572  free(pc_min_params);
1573  pc_min_params = NULL;
1574 
1575  return 0;
1576 #else
1577  (void)i; //TODO unused
1578  (void)pd_set;
1579  ERRPRINTF("muParser not enabled at configure.\n");
1580  return -1;
1581 #endif /* HAVE_MUPARSER */
1582 }
1583 
1584 /*!
1585  * Step along an interval until the product of the parameters at the end
1586  * point of the interval matches the maximum parameter product (equal or less
1587  * than if nonzero_max is set, or equal to or 1 step great than if nonzero_max
1588  * is not set).
1589  *
1590  * @param i pointer to the interval that is to be adjusted
1591  * @param pd_set pointer to the parameter definition set
1592  *
1593  * @return 0 on success or -1 on failure
1594  */
1595 int
1597  struct pmm_paramdef_set *pd_set)
1598 {
1599 #ifdef HAVE_MUPARSER
1600  int *pc_max_params;
1601  double pp, start_pc, end_pc;
1602  int step;
1603  int nonzero_end;
1604  int ret;
1605 
1606  // check if end point is fuzzy
1607  nonzero_end = isnonzero_at_interval_end(i, pd_set);
1608 
1609  // test to see if interval end point or start point exceed the pc_max
1610  // if start exceeds, we adjust the start point, if end exceeds, we adjust
1611  // the endpoint, if neither exceed, no adjustments need to be made, and
1612  // if both exceed, there is an error
1613  if(evaluate_constraint_with_params(pd_set->pc_parser, i->start, &start_pc)
1614  < 0)
1615  {
1616  ERRPRINTF("Error evaluating parameter constraint.\n");
1617  return -1;
1618  }
1619  if(evaluate_constraint_with_params(pd_set->pc_parser, i->end, &end_pc)
1620  < 0)
1621  {
1622  ERRPRINTF("Error evaluating parameter constraint.\n");
1623  return -1;
1624  }
1625 
1626  if(start_pc > pd_set->pc_max && end_pc > pd_set->pc_max) {
1627  ERRPRINTF("Interval cannot be adjusted to fit parameter product "
1628  "constraint.\n");
1629  return -1;
1630  }
1631  else if(start_pc > pd_set->pc_max) {
1632  }
1633  else if(end_pc > pd_set->pc_max) {
1634  }
1635  else {
1636  DBGPRINTF("Interval already satisfies parameter product constraint.\n");
1637  }
1638 
1639  pc_max_params = malloc(pd_set->n_p * sizeof *pc_max_params);
1640  if(pc_max_params == NULL) {
1641  ERRPRINTF("Error allocating memory.\n");
1642  return -1;
1643  }
1644 
1645 
1646 
1647  step = 0;
1648  while(1) {
1649 
1650  if(start_pc > pd_set->pc_max) {
1651  ret = set_params_step_between_params(pc_max_params,
1652  i->end, i->start, step, pd_set);
1653  }
1654  else {
1655  ret = set_params_step_between_params(pc_max_params,
1656  i->start, i->end, step, pd_set);
1657  }
1658 
1659  if(ret < 0) {
1660  break;
1661  }
1662 
1663  // now calculate product at the stepped point
1664  if(evaluate_constraint_with_params(pd_set->pc_parser, pc_max_params,
1665  &pp) < 0)
1666  {
1667  ERRPRINTF("Error evaluating parameter constraint.\n");
1668 
1669  free(pc_max_params);
1670  pc_max_params = NULL;
1671 
1672  return -1;
1673  }
1674 
1675  DBGPRINTF("param product:%f, param_product_max%d\n", pp,
1676  pd_set->pc_max);
1677 
1678  // if fuzzy end (i.e. we bench at adjusted end point)
1679  if(nonzero_end == 1) {
1680 
1681  // stop when pp exceeds pc_max and take previous step. I.e. end
1682  // point will be <= pc_max
1683  if(pp > pd_set->pc_max) {
1684  step--;
1685  break;
1686  }
1687  }
1688  // if fuzzy_end is not set and end point will have zero speed
1689  else {
1690 
1691  // stop when pp exceeds or is equal to pc_max, this will now be
1692  // the end point and have zero speed (step-1 will be benchmark-able
1693  // and with a pp less than pc_max)
1694  if(pp >= pd_set->pc_max) {
1695  break;
1696  }
1697  }
1698 
1699  step++;
1700  }
1701 
1702  // if we never found a pp that exceeded pc_max, but ran out of steps, then
1703  // just keep the interval as it is ...
1704  if(ret < 0) {
1705  DBGPRINTF("Parameter product was not exceeded by interval.\n");
1706  }
1707  // otherwise lets set the end/start point to the value at the step
1708  else {
1709  if(start_pc > pd_set->pc_max) {
1711  i->end, i->start, step, pd_set);
1712  }
1713  else {
1715  i->start, i->end, step, pd_set);
1716  }
1717  }
1718 
1719 
1720  free(pc_max_params);
1721  pc_max_params = NULL;
1722 
1723  return 0;
1724 #else
1725  (void)i; //TODO unused
1726  (void)pd_set;
1727 
1728  ERRPRINTF("muParser not enabled at configure.\n");
1729  return -1;
1730 #endif /* HAVE_MUPARSER */
1731 }
1732 
1733 /*!
1734  * Test if the end point of an interval is nonzero or not. A nonzero end point
1735  * means we will benchmark at this point rather than set its speed to be zero.
1736  *
1737  * The line between start and end points may be perpendicular to some axes
1738  * of the parameter space, or to none at all. Any parameter-axis that the
1739  * interval-line is perpendicular to, is not considered in the determination of
1740  * the end point 'fuzziness'.
1741  *
1742  * The end point is declared nonzero then, if all parameters that may be
1743  * considered have nonzero property set to true. Other wise, the end point
1744  * is not fuzzy and should have a speed set to be zero in the model.
1745  *
1746  *
1747  * @param i pointer to the interval
1748  * @param pd_set pointer to the parameter defintion set
1749  *
1750  * @return 0 if not zero end point, 1 if nonzero end point
1751  *
1752  */
1753 int
1755  struct pmm_paramdef_set *pd_set)
1756 {
1757  int j;
1758  int all_nonzero_end;
1759 
1760  all_nonzero_end = 1;
1761 
1762  for(j=0; j<pd_set->n_p; j++) {
1763  if(i->start[j] != i->end[j]) { // if interval is not perpendicular to
1764  // this axis
1765 
1766  if(pd_set->pd_array[j].nonzero_end != 1) {
1767  all_nonzero_end = 0;
1768  break;
1769  }
1770 
1771  }
1772  }
1773 
1774  return all_nonzero_end;
1775 }
1776 
1777 /*!
1778  * Assuming all points in the model are along a diagonal constructed by
1779  * GBBP from start_0, start_1, ..., start_n to end_0, end_1, ..., end_n, project
1780  * construction intervals through each diagonal point, along mutually
1781  * perpendicular lines.
1782  *
1783  * Or more formally, given a set n parameter definitions, describing start
1784  * and end values values:
1785  * START = start_0, start_1, ..., start_n
1786  * END = end_0, end_1, ..., end_n
1787  *
1788  * For each point p along a diagonal from a point (start_0,start_1,...,start_n)
1789  * to (end_0, end_1, ..., end_n)
1790  * For each element p_i of a point p = (p_0, p_1, ..., p_n)
1791  * find a point a by replacing p_i in the point p with start_i
1792  * find a point b by replacing p_i in the point p with end_i
1793  * create a construction interval between a and b
1794  *
1795  * @param m pointer to the model
1796  *
1797  * @return 0 on success, -1 on failure
1798  */
1799 int
1801 {
1802 
1803  int j;
1804  struct pmm_routine *r;
1805  struct pmm_benchmark *b;
1806  struct pmm_benchmark *zero_b;
1807  struct pmm_benchmark *zero_list_head, *prev_b;
1808  struct pmm_interval *new_i;
1809 
1810  r = m->parent_routine;
1811  zero_list_head = NULL;
1812 
1813 
1814  //all other relevant benchmarks will be contained in the bench list
1815  b = m->bench_list->first;
1816  while(b != NULL) {
1817 
1818  if(benchmark_on_axis(m, b) < 0) {
1819  // don't skip, bench is at origin or not on an axis
1820 
1821  }
1822  else {
1823  // bench is on axis, skip it
1824  do {
1825  b = get_next_different_bench(b);
1826  } while(b != NULL && b->flops == 0.0);
1827 
1828  continue;
1829  }
1830 
1831  for(j=0; j<m->n_p; j++) {
1832 
1833  new_i = new_projection_interval(b->p,
1834  &(m->parent_routine->pd_set->pd_array[j]),
1835  j, b->n_p);
1836  if(new_i == NULL) {
1837  ERRPRINTF("Error creating new projection interval.\n");
1838  return -1;
1839  }
1840 
1841  // if parameter product max is set, we adjust the new interval so
1842  // it satisfies the constraint
1843  if(r->pd_set->pc_max != -1) {
1845 
1846  }
1847  if(r->pd_set->pc_min != -1) {
1849  }
1850 
1851 
1852  if(is_interval_divisible(new_i, r) == 1) {
1853  DBGPRINTF("Adding diagonal projection interval.\n");
1854  print_interval(PMM_DBG, new_i);
1855  add_top_interval(m->interval_list, new_i);
1856  }
1857  else {
1858  DBGPRINTF("Interval not divisible, not adding.\n");
1859  print_interval(PMM_DBG, new_i);
1860  free_interval(&new_i);
1861  }
1862 
1863  if(r->pd_set->pd_array[j].nonzero_end == 1 && new_i != NULL)
1864  {
1865  //add an IT_POINT interval at the endpoint of the projected
1866  //interval ... careful re-use of new_i pointer!
1867  new_i = init_interval(0, b->n_p, IT_POINT, new_i->end, NULL);
1868  if(new_i == NULL) {
1869  ERRPRINTF("Error creating new point interval.\n");
1870  return -1;
1871  }
1872 
1873  DBGPRINTF("Adding nonzero end interval in diagonal "
1874  "projection\n");
1875 
1876  print_interval(PMM_DBG, new_i);
1877 
1878  add_top_interval(m->interval_list, new_i);
1879 
1880  }
1881  else {
1882  // create a zero speed point at end ...
1883  //
1884  zero_b = init_zero_benchmark(b->p, b->n_p);
1885  if(zero_b == NULL) {
1886  ERRPRINTF("Error allocating new benchmark.\n");
1887  return -1;
1888  }
1889 
1890  //set parameter of the projection direction to end
1891  zero_b->p[j] = r->pd_set->pd_array[j].end;
1892 
1893  //in this phase of this projection of intervals, the model
1894  //must only contain benchmarks on the diagonal. So we will add
1895  //all zero benchmarks to a list which will be added to the
1896  //model later.
1897  if(zero_list_head == NULL) {
1898  zero_list_head = zero_b;
1899  }
1900  else {
1901  zero_list_head->next = zero_b;
1902  zero_b->previous = zero_list_head;
1903  zero_list_head = zero_b;
1904  }
1905 
1906  }
1907  }
1908 
1909  //for this to work, the model must contain only benchmarks that are
1910  //along the diagonal (it assumes all benchmarks are ones along the
1911  //diagonal that should be projected).
1912 
1913  //fetch the next benchmark with non-zero execution speed. Zero speed
1914  //benchmarks represent the bounding region of the parameter space
1915  //and we do not need to pay attention to these benchmarks
1916  //
1917  // TODO this will also work around our issue that inspired the zero_list
1918  // above ... remove zero_list at some stage
1919  do {
1920  b = get_next_different_bench(b);
1921  } while(b != NULL && b->flops == 0.0);
1922  }
1923 
1924 
1925  //now add zero benchmarks to the model
1926  zero_b = zero_list_head;
1927  while(zero_b != NULL) {
1928  prev_b = zero_b->previous; //when zero_b is added to the list its
1929  //its next/prev pointers are modified
1930 
1931  DBGPRINTF("Inserting zero benchmark from saved list:\n");
1932  print_benchmark(PMM_DBG, zero_b);
1933  if(insert_bench(m, zero_b) < 0) {
1934  ERRPRINTF("Error inserting a zero benchmark.\n");
1935 
1936  free_benchmark(&zero_b);
1938 
1939  return -1;
1940  }
1941 
1942 
1943  zero_b = prev_b;
1944  }
1945 
1946 
1947  return 0;
1948 }
1949 
1950 /*!
1951  * Create a new interval that is a projection through a point p, perpendicular
1952  * to the plane of the d-th element of the point, which is described by
1953  * the parameter defintion pd
1954  *
1955  * @param p pointer to the parameter array describing the point
1956  * @param pd pointer to the parameter definition relating to the
1957  * perpendicular plane
1958  * @param d index of the perpendicular plane
1959  * @param n number of elements in the point
1960  *
1961  * @return pointer to a new interval going from p_0,p_1, ... start_d, ... p_n
1962  * to p_0, p_1, .... end_d, ... p_n
1963  *
1964  */
1965 struct pmm_interval*
1966 new_projection_interval(int *p, struct pmm_paramdef *pd, int d, int n)
1967 {
1968  struct pmm_interval *new_i;
1969 
1970 
1971  new_i = new_interval();
1972  if(new_i == NULL) {
1973  ERRPRINTF("Error creating new interval structure.\n");
1974  return NULL; //failure
1975  }
1976 
1977  new_i->type = IT_GBBP_EMPTY;
1978  new_i->n_p = n;
1979 
1980  new_i->start = init_param_array_copy(p, n);
1981  if(new_i->start == NULL) {
1982  ERRPRINTF("Error initialising interval start parameter.\n");
1983  free_interval(&new_i);
1984  return NULL;
1985  }
1986  new_i->start[d] = pd->start;
1987 
1988  new_i->end = init_param_array_copy(p, n);
1989  if(new_i->end == NULL) {
1990  ERRPRINTF("Error initialising interval end parameter.\n");
1991  free_interval(&new_i);
1992  return NULL;
1993  }
1994  new_i->end[d] = pd->end;
1995 
1996  return new_i;
1997 }
1998 
1999 /*!
2000  * builds multi-parameter piece-wise performance models using the
2001  * GBBP optimisation
2002  *
2003  * Alorithm description:
2004  *
2005  * init
2006  * - push boundary_complete onto empty interval stack
2007  * - for each parameter of the model
2008  * - push boundary model of that parameter onto interval stack
2009  *
2010  * main
2011  * - read interval from top of stack
2012  * - if interval is of of a boundary type
2013  * - apply GBBP to the construction of the boundary model
2014  * - if interval is tagged boundary_complete
2015  * - mesh points on each boundary model to create interior benchmark points.
2016  * Push each of these points onto the interval stack with a point_inteval
2017  * tag.
2018  * - pop the top point_interval and execute a benchmark on it
2019  * - if interval is of the point_interval type
2020  * - execute a benchmark on the point and add to model
2021  * - if stack is empty
2022  * - mark model construction as complete
2023  *
2024  * @param r pointer to the routine to build for
2025  *
2026  * @return pointer to a newly allocated array containing the parameters of
2027  * the selected benchmark
2028  *
2029  */
2030 int*
2032 {
2033  int *params;
2034 
2035  struct pmm_interval_list *i_list;
2036  struct pmm_interval *top_i, *new_i;
2037  struct pmm_model *m;
2038 
2039  m = r->model;
2040  i_list = m->interval_list;
2041 
2042  //if model construction has not started, i_list will be empty
2043  if(isempty_interval_list(i_list) == 1) {
2044 
2045  DBGPRINTF("Interval list is empty, initializing new interval list.\n");
2046 
2047  if(init_gbbp_boundary_intervals(r) < 0) {
2048  ERRPRINTF("Error initialising new construction intervals.\n");
2049  return NULL;
2050  }
2051  }
2052 
2053  params = malloc(r->pd_set->n_p * sizeof *params);
2054  if(params == NULL) {
2055  ERRPRINTF("Error allocating memory.\n");
2056  return NULL;
2057  }
2058 
2059  if((top_i = read_top_interval(i_list)) == NULL) {
2060  ERRPRINTF("Error reading interval from head of list\n");
2061 
2062  free(params);
2063  params = NULL;
2064 
2065  return NULL;
2066  }
2067 
2068  DBGPRINTF("Choosing benchmark based on following interval:\n");
2069  print_interval(PMM_DBG, top_i);
2070 
2071  switch(top_i->type) {
2072  case IT_GBBP_EMPTY :
2073  case IT_GBBP_CLIMB :
2074  case IT_GBBP_BISECT :
2075  case IT_GBBP_INFLECT :
2076 
2077  DBGPRINTF("Applying step of GBBP to benchmark selection.\n");
2078 
2079  //apply GBBP
2080  multi_gbbp_bench_from_interval(r, top_i, params);
2081  break;
2082 
2083  case IT_BOUNDARY_COMPLETE :
2084 
2085  DBGPRINTF("GBBP stage of construction complete. Meshing ...\n");
2086 
2087  //remove top interval
2088  remove_interval(i_list, top_i);
2089 
2090  if(r->pd_set->n_p > 1) {
2091  //mesh boundary models to create points
2093 
2094  //read another top interval
2095  if((top_i = read_top_interval(i_list)) == NULL) {
2096  ERRPRINTF("Error reading interval from head of list\n");
2097 
2098  free(params);
2099  params = NULL;
2100 
2101  return NULL;
2102  }
2103 
2104  DBGPRINTF("Meshing complete, fetching benchmark ...\n");
2105 
2106  //get the benchmark from the interval
2107  multi_gbbp_bench_from_interval(r, top_i, params);
2108  }
2109  else {
2110  ERRPRINTF("Invalid IT_BOUNDARY_COMPLETE in 1 param model.\n");
2111 
2112  // must check to see if interval list is empty now, if
2113  // completion is not tagged here then construction will restart
2115  LOGPRINTF("GBBP Construction complete.\n");
2116 
2117  new_i = new_interval();
2118  new_i->type = IT_COMPLETE;
2119  add_top_interval(m->interval_list, new_i);
2120 
2121  m->complete = 1;
2122  }
2123 
2124  free(params);
2125  params = NULL;
2126  }
2127 
2128  break;
2129 
2130  case IT_POINT :
2131 
2132  DBGPRINTF("Benchmarking at point ...\n");
2133  //benchmark at this point
2134  multi_gbbp_bench_from_interval(r, top_i, params);
2135  break;
2136 
2137  default :
2138  ERRPRINTF("Invalid interval type: %s (%d)\n",
2139  interval_type_to_string(top_i->type), top_i->type);
2140  print_interval(PMM_ERR, top_i);
2141 
2142  free(params);
2143  params = NULL;
2144 
2145  break;
2146  }
2147 
2148  return params;
2149 }
2150 
2151 /*!
2152  * intialize the interval stack for an empty model that will be built using a
2153  * GBBP algorithm
2154  *
2155  * @param r pointer to the routine which is having its intervals intiialized
2156  *
2157  * @return o on success, -1 on failure
2158  */
2159 int
2161 {
2162  //TODO rename function
2163  int j;
2164  struct pmm_model *m;
2165  struct pmm_interval_list *i_list;
2166  struct pmm_interval *new_i;
2167 
2168  m = r->model;
2169  i_list = m->interval_list;
2170 
2171  //models with more than 1 boundary need a tag to indicate mesh stage
2172  //of construction
2173  if(r->pd_set->n_p > 1) {
2174  new_i = new_interval();
2175  new_i->type = IT_BOUNDARY_COMPLETE;
2176  add_top_interval(i_list, new_i);
2177  }
2178 
2179  // for each parameter of the routine
2180  for(j=0; j<r->pd_set->n_p; j++) {
2181  //push boundary model of that parameter onto the interval stack
2182 
2183 
2184  // if nonzero_end is set then we will benchmark at end and build
2185  // the model between this point and start. EMPTY interval will range
2186  // from start to end and a POINT interval at end will be added to
2187  // the top of the interval stack so that it is measured before GBBP
2188  // construction proceeds
2189  if(r->pd_set->pd_array[j].nonzero_end == 1) {
2190 
2191  DBGPRINTF("nonzero_end on param:%d\n", j);
2192 
2193  // add an empty interval from start -> end along boundary
2194  new_i = new_interval();
2195  new_i->type = IT_GBBP_EMPTY;
2196 
2197  new_i->plane = j;
2198  new_i->n_p = r->pd_set->n_p;
2199 
2200  new_i->start = init_param_array_start(r->pd_set);
2201  if(new_i->start == NULL) {
2202  ERRPRINTF("Error initialising start parameter array.\n");
2203  free_interval(&new_i);
2204  return -1;
2205  }
2206 
2207  new_i->end = init_param_array_start(r->pd_set);
2208  if(new_i->end == NULL) {
2209  ERRPRINTF("Error initialising start parameter array.\n");
2210  free_interval(&new_i);
2211  return -1;
2212  }
2213  new_i->end[j] = r->pd_set->pd_array[j].end;
2214 
2215  if(is_interval_divisible(new_i, r) == 1) {
2216  add_top_interval(m->interval_list, new_i);
2217  }
2218  else {
2219  DBGPRINTF("Interval not divisible, not adding.\n");
2220  print_interval(PMM_DBG, new_i);
2221  free_interval(&new_i);
2222  }
2223 
2224 
2225  // add benchmark at end on boundary (nonzero end)
2226  new_i = new_interval();
2227  new_i->type = IT_POINT;
2228  new_i->n_p = r->pd_set->n_p;
2229 
2230  new_i->start = init_param_array_start(r->pd_set);
2231  if(new_i->start == NULL) {
2232  ERRPRINTF("Error initialising start parameter array.\n");
2233  free_interval(&new_i);
2234  return -1;
2235  }
2236 
2237  new_i->start[j] = r->pd_set->pd_array[j].end;
2238 
2239  add_top_interval(i_list, new_i);
2240 
2241 
2242  }
2243  else {
2244  // add an empty interval from start -> end along boundary
2245  new_i = new_interval();
2246  new_i->type = IT_GBBP_EMPTY;
2247 
2248  new_i->plane = j;
2249  new_i->n_p = r->pd_set->n_p;
2250 
2251  new_i->start = init_param_array_start(r->pd_set);
2252  if(new_i->start == NULL) {
2253  ERRPRINTF("Error initialising start parameter array.\n");
2254  free_interval(&new_i);
2255  return -1; //failure
2256  }
2257 
2258  new_i->end = init_param_array_start(r->pd_set);
2259  if(new_i->end == NULL) {
2260  ERRPRINTF("Error initialising start parameter array.\n");
2261  free_interval(&new_i);
2262  return -1; //failure
2263  }
2264  new_i->end[j] = r->pd_set->pd_array[j].end;
2265 
2266  if(is_interval_divisible(new_i, r) == 1) {
2267  add_top_interval(m->interval_list, new_i);
2268  }
2269  else {
2270  DBGPRINTF("Interval not divisible, not adding.\n");
2271  print_interval(PMM_DBG, new_i);
2272  free_interval(&new_i);
2273  }
2274 
2275  }
2276 
2277  }
2278 
2279  return 0; //success
2280 }
2281 
2282 /*!
2283  * given a set of complete models along the parameter boundaries, creat a
2284  * mech of new benchmarking points for the interior of the model
2285  *
2286  * @param m pointer to the model
2287  */
2289 {
2290  int *params;
2291  int n_p;
2292  int plane = 0;
2293 
2294 
2295  n_p = m->parent_routine->pd_set->n_p;
2296 
2297  //no need to build a mesh of boundary models when there is only 1 boundary!
2298  if(n_p == 1) {
2299  LOGPRINTF("1 parameter models have no mesh.\n");
2300  return;
2301  }
2302 
2303  params = malloc(n_p * sizeof *params);
2304  if(params == NULL) {
2305  ERRPRINTF("Error allocating memory.\n");
2306  exit(EXIT_FAILURE);
2307  }
2308 
2309  recurse_mesh(m, params, plane, n_p);
2310 
2311  free(params);
2312 
2313  return;
2314 }
2315 
2316 //TODO prevent boundary benchmarks from being added as they are already
2317 //measured
2318 /*!
2319  * recurse through each plane of a model creating a mesh of
2320  * benchmark points using the completed parameter boundaries
2321  *
2322  * @param m pointer the model
2323  * @param p pointer to a parameter array
2324  * @param plane current plane
2325  * @param n_p number of parameters/planes
2326  */
2327 void recurse_mesh(struct pmm_model *m, int *p, int plane, int n_p)
2328 {
2329 
2330  /* TODO fix this to work without boundary models
2331  struct pmm_benchmark *b;
2332  struct pmm_interval *interval;
2333 
2334  if(plane<n_p) {
2335 
2336  b = m->boundary_model_array[plane].last;
2337 
2338  while(b != NULL) {
2339 
2340  if(b->flops <= 0) {
2341  b = get_previous_different_bench(b); //skip 0/-1 speed benches
2342  continue;
2343  }
2344 
2345  //skip benchmarks at the 'origin' of the model as meshing these only
2346  //create benchmarks along the boundaries which have already been
2347  //measured
2348  if(is_benchmark_at_origin(n_p, m->parent_routine->pd_set->pd_array,
2349  b) == 1)
2350  {
2351  b = get_previous_different_bench(b); //this should return a NULL
2352  continue;
2353  }
2354 
2355  p[plane] = b->p[plane];
2356  if(plane<n_p-1) {
2357  recurse_mesh(m, p, plane+1, n_p);
2358  }
2359  else {
2360  interval = init_interval(-1, n_p, IT_POINT, p, NULL);
2361  if(interval == NULL) {
2362  ERRPRINTF("Error initialising interval.\n");
2363  //TODO this error is not caught
2364  }
2365 
2366  add_top_interval(m->interval_list, interval);
2367 
2368  printf("plane: %d n_p:%d\n", plane, n_p);
2369  print_params(PMM_DBG, interval->start, n_p);
2370  print_params(PMM_DBG, b->p, n_p);
2371  }
2372 
2373  b = get_previous_different_bench(b);
2374  }
2375  }
2376  */
2377 
2378 }
2379 
2380 /*!
2381  * Insert a benchmark into a 1 parameter model being constructed with the
2382  * naive bisection method.
2383  *
2384  * @param r pointer to the routine forwhich the model is being constructed
2385  * @param b benchmark to insert
2386  *
2387  * @return 0 on success, -1 on failure to process intervals, -2 on failure
2388  * to insert benchmark
2389  */
2390 int
2392 {
2393 
2394  double time_spend;
2395  int num_execs;
2396  int ret;
2397  struct pmm_interval *interval, *new_i;;
2398  int *midpoint;
2399 
2400  ret = 0;
2401 
2402  DBGPRINTF("benchmark params:\n");
2403  print_params(PMM_DBG, b->p, b->n_p);
2404 
2405  if(insert_bench(r->model, b) < 0) {
2406  ERRPRINTF("Error inserting benchmark.\n");
2407  ret = -2;
2408  }
2409 
2410  //first check time spent benchmarking at this point, if it is less than
2411  //the threshold, we won't bother processing the interval list (so that
2412  //the benchmark may be executed again, until it exceeds the threshold
2413  calc_bench_exec_stats(r->model, b->p, &time_spend, &num_execs);
2414 
2415  DBGPRINTF("total time spent benchmarking point: %f\n", time_spend);
2416  DBGPRINTF("total number executions at benchmarking point: %d\n", num_execs);
2417 
2418  if(check_benchmarking_minimums(r, time_spend, num_execs))
2419  {
2420 
2421  DBGPRINTF("benchmarking threshold exceeeded (t:%d, n:%d), processing intervals.\n", r->min_sample_time, r->min_sample_num);
2422 
2423  //find interval
2424  if(find_interval_matching_bench(r, b, NULL, &interval) < 0) {
2425  ERRPRINTF("Error searching intervals.\n");
2426  return -1;
2427  }
2428 
2429  if(interval != NULL) {
2430  if(interval->type == IT_GBBP_BISECT) {
2431 
2432  // find midpoint
2433  midpoint = malloc(interval->n_p * sizeof *midpoint);
2434  if(midpoint == NULL) {
2435  ERRPRINTF("Error allocating memory.\n");
2436  return -1;
2437  }
2438  set_params_interval_midpoint(midpoint, interval);
2439 
2440 
2441  // create new interval from start to midpoint
2442  new_i = init_interval(0, interval->n_p, IT_GBBP_BISECT,
2443  interval->start, midpoint);
2444  if(new_i == NULL) {
2445  ERRPRINTF("Error initialising new interval.\n");
2446  return -1;
2447  }
2448 
2449  // don't add it unless it is divisible
2450  if(is_interval_divisible(new_i, r) == 1) {
2452  }
2453  else {
2454  DBGPRINTF("Interval not divisible, not adding.\n");
2455  print_interval(PMM_DBG, new_i);
2456  free_interval(&new_i);
2457  }
2458 
2459  // create second new interval from midpoint to end
2460  new_i = init_interval(0, interval->n_p, IT_GBBP_BISECT,
2461  midpoint, interval->end);
2462  if(new_i == NULL) {
2463  ERRPRINTF("Error initialising new interval.\n");
2464  return -1;
2465  }
2466 
2467  // don't add it unless it is divisible
2468  if(is_interval_divisible(new_i, r) == 1) {
2470  }
2471  else {
2472  DBGPRINTF("Interval not divisible, not adding.\n");
2473  print_interval(PMM_DBG, new_i);
2474  free_interval(&new_i);
2475  }
2476 
2477  // remove the old interval
2478  remove_interval(r->model->interval_list, interval);
2479 
2480  }
2481  else if(interval->type == IT_POINT) {
2482 
2483  // remove old interval
2484  remove_interval(r->model->interval_list, interval);
2485 
2486  }
2487  else {
2488  ERRPRINTF("Unexpected interval type.\n");
2489  return -1;
2490  }
2491 
2493  DBGPRINTF("interval list now empty.\n");
2494 
2495  new_i = new_interval();
2496  new_i->type = IT_COMPLETE;
2497  add_top_interval(r->model->interval_list, new_i);
2498 
2499  r->model->complete = 1;
2500  }
2501  }
2502 
2503  } else {
2504  DBGPRINTF("benchmarking threshold not exceeded.\n");
2505  }
2506 
2507 
2508  // after intervals have been processed ...
2509 
2510  return ret;
2511 }
2512 
2513 /*!
2514  * Insert a benchmark into a multi-parameter model being constructed with the
2515  * GBBP method.
2516  *
2517  * The second half of the gbbp proceedure. After a benchmark has been made it
2518  * must be added to the model and the state of the building proceedure must be
2519  * adjusted according to the new shape of the model.
2520  *
2521  * The rules that govern this adjustment and the specific adjustments are as
2522  * follows:
2523  *
2524  * if the model is empty, we add the benchmark to the model and set the state
2525  * to be climbing.
2526  *
2527  * if the model is climbing we test the new benchmark to see if the model is
2528  * still climbing, or has levelled out, or has begun to decrease. If the
2529  * model is not still climbing or levelled out, we change the state to
2530  * bisection. The bisection state permits the optimal selection of new
2531  * benchmarking points.
2532  *
2533  * In this state, any new benchmark being inserted is comparted to the existing
2534  * model. If the model already accurately approximates the benchmark the state
2535  * is set to inflection.
2536  *
2537  * The inflection state is a second level of bisection, in this state if a new
2538  * benchmark is again accurately approximated by the existing model we deem the
2539  * model to be complete in this region
2540  *
2541  * Most of this functionality is actually implemented in a deeper function
2542  * process_interval(), process_it_gbbp_climb(), process_it_gbbp_bisect(), etc.
2543  *
2544  * @return 0 on success, -1 on failure to process intervals, -2 on failure
2545  * to insert benchmark
2546  *
2547  * @param h pointer to the load history
2548  * @param r pointer to the routine to which the benchmark is added
2549  * @param b pointer to the benchmark to be added
2550  *
2551  * @return 0 onsucces, -1 on failure to process intervals, -2 on complete failure
2552  * to add benchmark to model
2553  */
2554 int
2556  struct pmm_benchmark *b)
2557 {
2558 
2559  double time_spend;
2560  int num_execs;
2561  int ret;
2562 
2563  ret = 0;
2564 
2565  DBGPRINTF("benchmark params:\n");
2566  print_params(PMM_DBG, b->p, b->n_p);
2567 
2568  if(insert_bench(r->model, b) < 0) {
2569  ERRPRINTF("Error inserting benchmark.\n");
2570  ret = -2;
2571  }
2572 
2573  //first check time spent benchmarking at this point, if it is less than
2574  //the threshold, we won't bother processing the interval list (so that
2575  //the benchmark may be executed again, until it exceeds the threshold
2576  calc_bench_exec_stats(r->model, b->p, &time_spend, &num_execs);
2577 
2578  DBGPRINTF("total time spent benchmarking point: %f\n", time_spend);
2579  DBGPRINTF("total number executions at benchmarking point: %d\n", num_execs);
2580 
2581  if(check_benchmarking_minimums(r, time_spend, num_execs))
2582  {
2583  DBGPRINTF("benchmarking threshold exceeeded (t:%d, n:%d), processing intervals.\n", r->min_sample_time, r->min_sample_num);
2584 
2585  if(process_interval_list(r, b, h) < 0) {
2586  ERRPRINTF("Error proccessing intervals, inserting bench anyway\n");
2587  ret = -1;
2588  }
2589 
2590  } else {
2591  DBGPRINTF("benchmarking threshold not exceeded.\n");
2592  }
2593 
2594 
2595  // after intervals have been processed ...
2596 
2597  return ret;
2598 }
2599 
2600 /*!
2601  * Find the interval in an interval stack that corresponds to a given benchmark.
2602  *
2603  * This permits adding benchmarks to the model out of the order deemed by the
2604  * construction intervals.
2605  *
2606  * @param r pointer to routine containing model and parameter definitions
2607  * @param b pointer to benchmark whos interval we are searching for
2608  * @param h pointer to load history
2609  * @param found_i pointer to address that will hold the found interval or point
2610  * to NULL
2611  *
2612  * @return 0 on successful search (found or not found), -1 on failure
2613  */
2614 int
2616  struct pmm_loadhistory *h,
2617  struct pmm_interval **found_i)
2618 {
2619 
2620  struct pmm_interval *i, *i_prev;
2621  int *temp_params;
2622  int done = 0, ret;
2623 
2624  (void)h; //TODO unused
2625 
2626  // set found to NULL incase we don't find anything and in case of error
2627  *found_i = NULL;
2628 
2629  i = r->model->interval_list->top;
2630 
2631  temp_params = malloc(r->pd_set->n_p * sizeof *temp_params);
2632  if(temp_params == NULL) {
2633  ERRPRINTF("Error allocating memory.\n");
2634  return -1;
2635  }
2636 
2637  // interate over intervals
2638  while(i != NULL && !done) {
2639 
2640  i_prev = i->previous; //store this as the interval will be deallocated
2641  //if it is processed by 'process_interval'
2642 
2643 
2644  // get the appropriate gbbp point from the interval
2645  ret = multi_gbbp_bench_from_interval(r, i, temp_params);
2646  if(ret < -1) {
2647  ERRPRINTF("Error getting benchmark from interval.\n");
2648 
2649  free(temp_params);
2650  temp_params = NULL;
2651 
2652  return -1;
2653  }
2654 
2655  // if a gbbp benchmark point was set by multi_gbbp_bench .... then
2656  // check that the benchmark b is at the appropriate gbbp point
2657  if(ret == 0 && params_cmp(b->p, temp_params, b->n_p) == 0) {
2658 
2659  DBGPRINTF("Interval found:\n");
2660  print_interval(PMM_DBG, i);
2661  print_params(PMM_DBG, temp_params, r->pd_set->n_p);
2662 
2663  free(temp_params);
2664  temp_params = NULL;
2665 
2666  *found_i = i;
2667 
2668  return 0;
2669 
2670  }
2671  // benchmark is not at appropriate gbbp point
2672  else {
2673  // TODO try to reduce construction time by processing benchmarks
2674  // TODO that are not lying on appropriate GBBP points somehow.
2675  }
2676 
2677  // work backwards from the top/front of the list
2678  i = i_prev;
2679  }
2680 
2681  // if we got here, no interval was found
2682 
2683  free(temp_params);
2684  temp_params = NULL;
2685 
2686  return 0;
2687 }
2688 
2689 /*!
2690  * process the interval list, with a newly aquired benchmark
2691  *
2692  * @param r pointer to routine
2693  * @param b pointer to benchmark
2694  * @param h load history
2695  *
2696  * @return 0 on successful processing of interval, -1 on failure
2697  */
2698 int
2700  struct pmm_loadhistory *h)
2701 {
2702 
2703  struct pmm_interval *i, *i_prev;
2704  int *temp_params;
2705  int done = 0, ret;
2706 
2707  i = r->model->interval_list->top;
2708 
2709  // allocate a parameter array so we can compare interval benchmark points
2710  // to the benchmark that is being processed
2711  temp_params = malloc(r->pd_set->n_p * sizeof *temp_params);
2712  if(temp_params == NULL) {
2713  ERRPRINTF("Error allocating memory.\n");
2714  return -1;
2715  }
2716 
2717  // interate over intervals
2718  while(i != NULL && !done) {
2719 
2720  i_prev = i->previous; //store this as the interval will be deallocated
2721  //if it is processed by 'process_interval'
2722 
2723 
2724  // get the appropriate gbbp benchmarking point from the interval
2725  ret = multi_gbbp_bench_from_interval(r, i, temp_params);
2726  if(ret < -1) {
2727  ERRPRINTF("Error getting benchmark from interval.\n");
2728 
2729  free(temp_params);
2730  temp_params = NULL;
2731 
2732  return -1;
2733  }
2734 
2735  // if a gbbp benchmark point was set by multi_gbbp_bench .... then
2736  // check that the benchmark b is at the appropriate gbbp point
2737  if(ret == 0 && params_cmp(b->p, temp_params, b->n_p) == 0) {
2738 
2739  DBGPRINTF("Interval found:\n");
2740  print_interval(PMM_DBG, i);
2741  print_params(PMM_DBG, temp_params, r->pd_set->n_p);
2742 
2743  done = process_interval(r, i, b, h);
2744  if(done < 0) {
2745 
2746  ERRPRINTF("Error processing interval.\n");
2747 
2748  free(temp_params);
2749  temp_params = NULL;
2750 
2751  return -1;
2752 
2753  }
2754  }
2755  // benchmark is not at appropriate gbbp point, no need to modify the
2756  // construction intervals
2757  else {
2758  // TODO try to reduce construction time by processing benchmarks
2759  // TODO that are not lying on appropriate GBBP points somehow.
2760  }
2761 
2762  // work backwards from the top/front of the list
2763  i = i_prev;
2764  }
2765 
2766  free(temp_params);
2767  temp_params = NULL;
2768 
2769  return 0;
2770 }
2771 
2772 /*!
2773  * Process an interval based on its type.
2774  *
2775  * @param r pointer to the routine who's model is being constructed
2776  * @param i pointer to the interval to process
2777  * @param b pointer to the benchmark that is being inserted to the model
2778  * @param h pointer to the load history defining performance fluctuation
2779  *
2780  * @return 1 if processing is successful, and no further intervals should be
2781  * processed, 0 if processing is successful but further intervals should be
2782  * processed, -1 if an error occurs.
2783  */
2784 int
2786  struct pmm_benchmark *b, struct pmm_loadhistory *h)
2787 {
2788  int done;
2789 
2790  switch(i->type) {
2791  case IT_GBBP_EMPTY :
2792 
2793  // set return to 0 so calling loop will continue to search for
2794  // IT_GBBP_EMPTY intervals
2795  done = process_it_gbbp_empty(r, i) < 0 ? -1 : 0;
2796 
2797  break;
2798 
2799  case IT_GBBP_CLIMB :
2800 
2801  //if process_ returns failure (<0) set done to -1, otherwise 1
2802  done = process_it_gbbp_climb(r, i, b, h) < 0 ? -1 : 1;
2803 
2804  break;
2805 
2806  case IT_GBBP_BISECT :
2807 
2808  done = process_it_gbbp_bisect(r, i, b, h) < 0 ? -1 : 1;
2809 
2810  break;
2811 
2812  case IT_GBBP_INFLECT :
2813 
2814  done = process_it_gbbp_inflect(r, i, b, h) < 0 ? -1 : 1;
2815 
2816  break;
2817 
2818  case IT_POINT :
2819  done = process_it_point(r, i) < 0 ? -1 : 1;
2820 
2821  break;
2822 
2823  default :
2824  ERRPRINTF("Invalid interval type:%d\n", i->type);
2825  done = -1;
2826  break;
2827 
2828  }
2829 
2830  return done;
2831 }
2832 
2833 /*!
2834  * Process a gbbp_empty type interval. Replace interval with an IT_GBBP_CLIMB
2835  * interval that starts and ends at the same points as the GBBP_EMPTY interval
2836  * it replaces.
2837  *
2838  * Note, the new interval must be inserted inplace of the old one, not at the
2839  * top of the interval list. The start,start,...,start set of parameters match
2840  * all IT_GBBP_EMPTY intervals for all different boundary planes. We will
2841  * process all of these intervals, not just the first occurance in the interval
2842  * list. However, not all of the planes are ready for construction (e.g. their
2843  * nonzero_end benchmark may not be executed yet). Inserting the next interval
2844  * (IT_GBBP_CLIMP) inplace of the IT_GBBP_EMPTY interval resolves this issue.
2845  *
2846  * @param r pointer to the parent routine
2847  * @param i pointer to the interval we are processing
2848  *
2849  * @pre i as the type IT_GBBP_EMPTY
2850  *
2851  * @return 0 on success
2852  */
2853 int
2855 {
2856 
2857  i->type = IT_GBBP_CLIMB;
2858 
2859  //check interval is divisible and remove if not
2860  if(is_interval_divisible(i, r) != 1) {
2861  DBGPRINTF("Interval not divisible, removing.\n");
2863  }
2864 
2865  return 0;
2866 }
2867 
2868 /*!
2869  * Process a IT_GBBP_CLIMB interval type and associated benchmark. If the
2870  * new benchmark has a higher speed than the benchmark at the start/left-most
2871  * point of the interval, then the performance is still climbing. Replace the
2872  * interval with one that ranges from the new benchmark point to the old
2873  * old interval end point. The new interval will have the same GBBP_CLIMB type.
2874  * Otherwise, if the performance is no longer climbing, replace the old
2875  * interval with a new interval ranging from the new benchmark point to the
2876  * end of the old interval, but with a GBBP_BISECT type.
2877  *
2878  * @param r pointer to the routine that cotains the model
2879  * @param i pointer to the interval that the benchmark belongs to
2880  * @param b pointer to the benchmark
2881  * @param h pointer to the load history (describes performance
2882  * variance)
2883  *
2884  * @pre interval i and benchmark b match, m is the correct model, etc.
2885  *
2886  * @return 0 on success, -1 on failure
2887  */
2888 int
2890  struct pmm_benchmark *b, struct pmm_loadhistory *h)
2891 
2892 {
2893  struct pmm_model *m;
2894  struct pmm_interval *new_i;
2895  struct pmm_benchmark *b_avg, *b_left_0, *b_left_1, *b_left_2;
2896  int *tmp_params;
2897 
2898  m = r->model;
2899 
2900 
2901  //if we have not had more than 2 climb steps there will not be enough
2902  //points in the model to test for end of climbing ...
2903  if(i->climb_step < 2) {
2904  //not enough points
2905  DBGPRINTF("not enough points to test climbing, keep climbing...\n");
2906 
2907  i->climb_step = i->climb_step+1;
2908 
2909  //check interval is divisible and remove if not
2910  if(is_interval_divisible(i, r) != 1) {
2911  DBGPRINTF("Interval not divisible, removing.\n");
2913  }
2914 
2915  }
2916  else {
2917  //get the average benchmark at the model where b is positioned
2918  b_avg = get_avg_bench(m, b->p);
2919  if(b_avg == NULL) {
2920  ERRPRINTF("No benchmark found for current point.\n");
2921  return -1;
2922  }
2923 
2924 
2925  //need to create a param array to look up model for
2926  //starting points of intervals (on boundaries)
2927  tmp_params = malloc(b->n_p * sizeof *tmp_params);
2928  if(tmp_params == NULL) {
2929  ERRPRINTF("Error allocating parameter array.\n");
2930  return -1;
2931  }
2932 
2933  // get avg of benchmarks to the left of the new benchmark, set params
2934  // to the current climb step ...
2936  r->pd_set);
2937  b_left_0 = get_avg_bench(m, tmp_params);
2938 
2939  //get parameters for previous climb step to current
2941  r->pd_set);
2942  b_left_1 = get_avg_bench(m, tmp_params);
2943 
2944  //get parameters for 2nd previous climb step to current
2946  r->pd_set);
2947  b_left_2 = get_avg_bench(m, tmp_params);
2948 
2949  if(b_left_0 == NULL || b_left_1 == NULL || b_left_2 == NULL) {
2950  ERRPRINTF("Error, no benchmark @ a previous climb point\n");
2951 
2952  free_benchmark(&b_avg);
2953 
2954  if(b_left_0 != NULL)
2955  free_benchmark(&b_left_0);
2956  if(b_left_1 != NULL)
2957  free_benchmark(&b_left_1);
2958  if(b_left_2 != NULL)
2959  free_benchmark(&b_left_2);
2960 
2961  free(tmp_params);
2962  tmp_params = NULL;
2963 
2964  return -1;
2965  }
2966 
2967  DBGPRINTF("b_left_0:\n");
2968  print_benchmark(PMM_DBG, b_left_0);
2969  DBGPRINTF("b_left_1\n");
2970  print_benchmark(PMM_DBG, b_left_1);
2971  DBGPRINTF("b_left_2:\n");
2972  print_benchmark(PMM_DBG, b_left_2);
2973 
2974 
2975  // if the 3 previous benchmarks along the climb interval
2976  // (b_left_0 -> b_left_2) are each successively less-than OR
2977  // equal to their previous neighbour, then end the CLIMB stage of
2978  // construction
2979  //
2980  // i.e. if the performance has been decreasing consistantly for the last
2981  // three benchmarks ...
2982  if((bench_cut_greater(h, b_left_0, b_avg) ||
2983  bench_cut_contains(h, b_left_0, b_avg)) &&
2984  (bench_cut_greater(h, b_left_1, b_left_0) ||
2985  bench_cut_contains(h, b_left_1, b_left_0)) &&
2986  (bench_cut_greater(h, b_left_2, b_left_1) ||
2987  bench_cut_contains(h, b_left_2, b_left_1)))
2988  {
2989  DBGPRINTF("performance no longer climbing\n");
2990  DBGPRINTF("previous bench speeds:\n");
2991  DBGPRINTF("%f@%d %f@%d %f@%d\n",
2992  b_left_0->flops, b_left_0->p[i->plane],
2993  b_left_1->flops, b_left_1->p[i->plane],
2994  b_left_2->flops, b_left_2->p[i->plane]);
2995 
2996  DBGPRINTF("this bench:\n");
2997  print_benchmark(PMM_DBG, b_avg);
2998 
2999  // setup new interval, add and remove old
3000  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3001  b->p, i->end);
3002  if(new_i == NULL) {
3003  ERRPRINTF("Error initialising new interval.\n");
3004  return -1;
3005  }
3006 
3007  if(is_interval_divisible(new_i, r) == 1) {
3008  add_top_interval(m->interval_list, new_i);
3009  }
3010  else {
3011  DBGPRINTF("Interval not divisible, not adding.\n");
3012  print_interval(PMM_DBG, i);
3013  free_interval(&new_i);
3014  }
3015 
3017  }
3018  // otherwise if the benchmark b is greater than any of the 3 previous
3019  // benchmarks, continue with the CLIMB phase of construction,
3020  // increment step
3021  else {
3022  DBGPRINTF("performance still climbing\n");
3023  DBGPRINTF("previous bench speeds:\n");
3024  DBGPRINTF("%f@%d %f@%d %f@%d\n",
3025  b_left_0->flops, b_left_0->p[i->plane],
3026  b_left_1->flops, b_left_1->p[i->plane],
3027  b_left_2->flops, b_left_2->p[i->plane]);
3028  DBGPRINTF("this bench:\n");
3029  print_benchmark(PMM_DBG, b_avg);
3030 
3031  i->climb_step = i->climb_step+1;
3032 
3033  //check interval is divisible and remove if not
3034  if(is_interval_divisible(i, r) != 1) {
3035  DBGPRINTF("Interval not divisible, removing.\n");
3037  }
3038  }
3039 
3040  free_benchmark(&b_avg);
3041  free_benchmark(&b_left_0);
3042  free_benchmark(&b_left_1);
3043  free_benchmark(&b_left_2);
3044 
3045  free(tmp_params);
3046  tmp_params = NULL;
3047  }
3048 
3049 
3051  DBGPRINTF("interval list now empty.\n");
3052 
3053  new_i = new_interval();
3054  new_i->type = IT_COMPLETE;
3055  add_top_interval(m->interval_list, new_i);
3056 
3057  m->complete = 1;
3058  }
3059 
3060  return 0;
3061 }
3062 
3063 /*!
3064  * Step along the climb interval, from the start point towards the end point,
3065  * forwards or backwards, a number of times.
3066  *
3067  * @param params pointer to an array to store the parameters at the n-th
3068  * step along the interval
3069  * @param step number of steps to take along the interval (- to step
3070  * backwards, + to step forwards)
3071  * @param i pointer to the interval to step along
3072  * @param pd_set pointer to the parameter definition set
3073  *
3074  * @pre interval should have start and end points set (i.e. be of the type
3075  * IT_EMTPY, IT_GBBP_CLIMB, IT_GBBP_BISECT, IT_GBBP_INFLECT) though the
3076  * function is only intended to operate on IT_GBBP_CLIMB interval types
3077  *
3078  * @return 0 on success, -1 if the step will exceed the end-point of the
3079  * interval or -2 on error
3080  */
3081 int
3083  struct pmm_interval *i,
3084  struct pmm_paramdef_set *pd_set)
3085 {
3086 
3087 
3088  return set_params_step_between_params(params,i->start, i->end, step,
3089  pd_set);
3090 
3091 }
3092 
3093 /*!
3094  * process a GBBP_BISECT interval
3095  *
3096  * test if the model approximates the new benchmark and in what way,
3097  *
3098  * If the benchmark at the same level as the its left and right neighbours:
3099  * assume the model is flat in the region of the interaval and finalized this
3100  * area of the model by removing it.
3101  *
3102  * If the benchmark is at the same level as the left neighbour only, assume that
3103  * the model is flat to the left of the new benchmark, finalize this area by
3104  * remoing the interval and replacing it with a new interval representing the
3105  * area to the right of the benchmark.
3106  *
3107  * In the benchmark is at thte same level as the right neighbour only, preform
3108  * the same action, but in the inverse, replace the interval with a new interval
3109  * representing the area to the left of the benchmark
3110  *
3111  * In other cases, look up the current model at the position of the new
3112  * benchmark.
3113  *
3114  * If the current model accurately represents the new benchmark, replace the
3115  * construction interval with a new GBBP_INFLECT interval covering the same
3116  * area.
3117  *
3118  * If the current model does not accurately represent the new benchmark,
3119  * replace the interval with two new GGBP_BISECT intervals covering the left
3120  * and right areas between the new benchmark and the previous interval,
3121  * allowing further refinement of the model
3122  *
3123  *
3124  * @param r pointer to the routine
3125  * @param i pointer to the interval being processed
3126  * @param b pointer to the new benchmark
3127  * @param h pointer to the load history
3128  *
3129  * @return 0 on success, -1 on failure
3130  */
3131 int
3133  struct pmm_benchmark *b, struct pmm_loadhistory *h)
3134 {
3135 
3136  struct pmm_interval *new_i;
3137  struct pmm_model *m;
3138  struct pmm_benchmark *b_avg, *b_oldapprox, *b_left, *b_right;
3139  int *midpoint;
3140  int intersects_left, intersects_right;
3141 
3142  m = r->model;
3143 
3144 
3145  //get the average benchmark at the model where b is positioned
3146  b_avg = get_avg_bench(m, b->p);
3147 
3148 
3149  //find the unaligned midpoint of the interval
3150  midpoint = malloc(i->n_p * sizeof *midpoint);
3151  if(midpoint == NULL) {
3152  ERRPRINTF("Error allocating memory.\n");
3153  free_benchmark(&b_avg);
3154  return -1;
3155  }
3156  set_params_interval_midpoint(midpoint, i);
3157 
3158  // look up benchmarks at aligned interval->start
3159  b_left = get_avg_aligned_bench(m, i->start);
3160  if(b_left == NULL) {
3161  ERRPRINTF("Error, no benchmark @ interval start\n");
3162 
3163  free(midpoint);
3164  midpoint = NULL;
3165 
3166  free_benchmark(&b_avg);
3167  return -1;
3168  }
3169 
3170  // look up benchmark at aligned interval->end
3171  b_right = get_avg_aligned_bench(m, i->end);
3172  if(b_right == NULL) {
3173  ERRPRINTF("Error, no benchmark @ interval end\n");
3174  free_benchmark(&b_left);
3175 
3176  free(midpoint);
3177  midpoint = NULL;
3178 
3179  free_benchmark(&b_avg);
3180  return -1;
3181  }
3182 
3183 
3184  //
3185  intersects_left = bench_cut_intersects(h, b_left, b_avg);
3186  intersects_right = bench_cut_intersects(h, b_right, b_avg);
3187 
3188  if(intersects_left == 1 && intersects_right == 1) {
3189  // new benchmark cut intersets l.h.s. and r.h.s.
3190  // of previous model approximation, we are finished
3191  // building from range b_left to b_right so
3192  // remove the old construction interval and do
3193  // not replace it with anything
3194 
3195  DBGPRINTF("finished building from:\n");
3196  print_params(PMM_DBG, i->start, i->n_p);
3197  DBGPRINTF("to:\n");
3198  print_params(PMM_DBG, i->end, i->n_p);
3199 
3201  }
3202  else if(intersects_left == 1 && intersects_right != 1) {
3203  //
3204  // new bench cut intersects only l.h.s. of previous
3205  // model approximation, we are finished building in
3206  // range b_left to b, so we remove old construction
3207  // interval and replace it with one ranging from b
3208  // to b_right OR midpoint to interval end
3209  //
3210  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3211  midpoint, i->end);
3212  if(new_i == NULL) {
3213  ERRPRINTF("Error initialising new interval.\n");
3214  return -1;
3215  }
3216 
3217  if(is_interval_divisible(new_i, r) == 1) {
3218  add_top_interval(m->interval_list, new_i);
3219  }
3220  else {
3221  DBGPRINTF("Interval not divisible, not adding.\n");
3222  print_interval(PMM_DBG, new_i);
3223  free_interval(&new_i);
3224  }
3225 
3226  DBGPRINTF("finished building from:\n");
3227  print_params(PMM_DBG, i->start, i->n_p);
3228  DBGPRINTF("to:\n");
3229  print_params(PMM_DBG, midpoint, i->n_p);
3230 
3232 
3233  }
3234  else if(intersects_right == 1 && intersects_left != 1) {
3235  //
3236  // intersects only r.h.s. of old approximation we
3237  // are finished building in the range b to b_right, so
3238  // remove old construction interval and replace it with
3239  // one ranging from b_left to b OR interval start to
3240  // midpoint
3241  //
3242  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3243  i->start, midpoint);
3244  if(new_i == NULL) {
3245  ERRPRINTF("Error initialising new interval.\n");
3246  return -1;
3247  }
3248 
3249  if(is_interval_divisible(new_i, r) == 1) {
3250  add_top_interval(m->interval_list, new_i);
3251  }
3252  else {
3253  DBGPRINTF("Interval not divisible, not adding.\n");
3254  print_interval(PMM_DBG, new_i);
3255  free_interval(&new_i);
3256  }
3257 
3258  DBGPRINTF("finished building from:\n");
3259  print_params(PMM_DBG, midpoint, i->n_p);
3260  DBGPRINTF("to:\n");
3261  print_params(PMM_DBG, i->end, i->n_p);
3262 
3264  }
3265  else {
3266  //intersets neither r.h.s. or l.h.s. of old approx
3267  DBGPRINTF("intersects neither left or right of"
3268  " interval, finding old approximation.\n");
3269 
3270  // lookup the existing model at new benchmark size
3271  b_oldapprox = find_oldapprox(r->model, b->p);
3272  if(b_oldapprox == NULL) {
3273  ERRPRINTF("Error finding old approximation.\n");
3274  return -1;
3275  }
3276 
3277  if(bench_cut_intersects(h, b_oldapprox, b_avg)) {
3278  //
3279  // insersects old approximation, change to
3280  // recurisve bisect with INFLECT tag, create
3281  // new intervals from b_left to b and b to b_right
3282  // OR start to midpoint and midpoint to end
3283  //
3284 
3285  DBGPRINTF("new bench intersects old approx.\n");
3286 
3287  new_i = init_interval(i->plane, i->n_p, IT_GBBP_INFLECT,
3288  i->start, midpoint);
3289  if(new_i == NULL) {
3290  ERRPRINTF("Error initialising new interval.\n");
3291  return -1;
3292  }
3293 
3294  if(is_interval_divisible(new_i, r) == 1) {
3295  add_top_interval(m->interval_list, new_i);
3296  }
3297  else {
3298  DBGPRINTF("Interval not divisible, not adding.\n");
3299  print_interval(PMM_DBG, new_i);
3300  free_interval(&new_i);
3301  }
3302 
3303  new_i = init_interval(i->plane, i->n_p, IT_GBBP_INFLECT,
3304  midpoint, i->end);
3305  if(new_i == NULL) {
3306  ERRPRINTF("Error initialising new interval.\n");
3307  return -1;
3308  }
3309 
3310  if(is_interval_divisible(new_i, r) == 1) {
3311  add_top_interval(m->interval_list, new_i);
3312  }
3313  else {
3314  DBGPRINTF("Interval not divisible, not adding.\n");
3315  print_interval(PMM_DBG, new_i);
3316  free_interval(&new_i);
3317  }
3318 
3320  }
3321  else {
3322  //
3323  // doesn't intersect old approx, l.h.s or r.h.s. replace
3324  // interval with two further BISECT intervals from
3325  // b_left to b and b to b_right OR start to midpoint and
3326  // midpoint to end
3327  //
3328 
3329  DBGPRINTF("does not intersect left/right or "
3330  "old approximation, splitting...\n");
3331 
3332  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3333  i->start, midpoint);
3334  if(new_i == NULL) {
3335  ERRPRINTF("Error initialising new interval.\n");
3336  return -1;
3337  }
3338 
3339  if(is_interval_divisible(new_i, r) == 1) {
3340  add_top_interval(m->interval_list, new_i);
3341  }
3342  else {
3343  DBGPRINTF("Interval not divisible, not adding.\n");
3344  print_interval(PMM_DBG, new_i);
3345  free_interval(&new_i);
3346  }
3347 
3348  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3349  midpoint, i->end);
3350  if(new_i == NULL) {
3351  ERRPRINTF("Error initialising new interval.\n");
3352  return -1;
3353  }
3354 
3355  if(is_interval_divisible(new_i, r) == 1) {
3356  add_top_interval(m->interval_list, new_i);
3357  }
3358  else {
3359  DBGPRINTF("Interval not divisible, not adding.\n");
3360  print_interval(PMM_DBG, new_i);
3361  free_interval(&new_i);
3362  }
3363 
3365  }
3366 
3367  if(b_oldapprox != NULL) {
3368  free_benchmark(&b_oldapprox);
3369  }
3370  }
3371 
3372  free_benchmark(&b_avg);
3373 
3374  free(midpoint);
3375  midpoint = NULL;
3376 
3377  if(b_left != NULL) {
3378  free_benchmark(&b_left);
3379  }
3380  if(b_right != NULL) {
3381  free_benchmark(&b_right);
3382  }
3383 
3385  DBGPRINTF("interval list now empty.\n");
3386 
3387  new_i = new_interval();
3388  new_i->type = IT_COMPLETE;
3389  add_top_interval(m->interval_list, new_i);
3390 
3391  m->complete = 1;
3392  }
3393 
3394  return 0;
3395 }
3396 
3397 /*!
3398  * process a GBBP_INFLECT interval
3399  *
3400  * test if the model approximates the new benchmark and in what way,
3401  *
3402  * Look up the current model at the position of the new benchmark, if the model
3403  * accurately represents the new benchmark then the model is complete in this area,
3404  * remove the construction interval.
3405  *
3406  * Otherwise, construction must contine in this area, but:
3407  *
3408  * If the benchmark is at the same level as the left neighbour only, assume that
3409  * the model is flat to the left of the new benchmark, finalize this area by
3410  * remoing the interval and replacing it with a new BISECT interval representing the
3411  * area to the right of the benchmark.
3412  *
3413  * In the benchmark is at thte same level as the right neighbour only, preform
3414  * the same action, but in the inverse, replace the interval with a new interval
3415  * representing the area to the left of the benchmark
3416  *
3417  * Otherwise replace the interval with two new GGBP_BISECT intervals covering
3418  * the left and right areas between the new benchmark and the previous
3419  * interval, allowing further refinement of the model.
3420  *
3421  *
3422  * @param r pointer to the routine
3423  * @param i pointer to the interval being processed
3424  * @param b pointer to the new benchmark
3425  * @param h pointer to the load history
3426  *
3427  * @return 0 on success, -1 on failure
3428  */
3429 int
3431  struct pmm_benchmark *b, struct pmm_loadhistory *h)
3432 {
3433 
3434  struct pmm_interval *new_i;
3435  struct pmm_model *m;
3436  struct pmm_benchmark *b_avg, *b_left, *b_right, *b_oldapprox;
3437  int *midpoint;
3438  int intersects_left, intersects_right;
3439 
3440  m = r->model;
3441 
3442 
3443  //get the average benchmark at the model where b is positioned
3444  b_avg = get_avg_bench(m, b->p);
3445 
3446  // lookup the model's approximation of b, with no benchmarks at b
3447  // contributing
3448  b_oldapprox = find_oldapprox(r->model, b->p);
3449  if(b_oldapprox == NULL) {
3450  ERRPRINTF("Error finding old approximation.\n");
3451  free_benchmark(&b_avg);
3452  return -1;
3453  }
3454 
3455  //find the unaligned midpoint of the interval, we only use this for
3456  //creating the next intervals
3457  midpoint = malloc(i->n_p * sizeof *midpoint);
3458  if(midpoint == NULL) {
3459  ERRPRINTF("Error allocating memory.\n");
3460  free_benchmark(&b_oldapprox);
3461  free_benchmark(&b_avg);
3462  return -1;
3463  }
3464  set_params_interval_midpoint(midpoint, i);
3465 
3466  // look up benchmarks at aligned interval->start
3467  b_left = get_avg_aligned_bench(m, i->start);
3468  if(b_left == NULL) {
3469  ERRPRINTF("Error, no benchmark @ interval start\n");
3470 
3471  free(midpoint);
3472  midpoint = NULL;
3473 
3474  free_benchmark(&b_oldapprox);
3475  free_benchmark(&b_avg);
3476  return -1;
3477  }
3478 
3479  // look up benchmark at aligned interval->end
3480  b_right = get_avg_aligned_bench(r->model, i->end);
3481  if(b_right == NULL) {
3482  ERRPRINTF("Error, no benchmark @ interval end\n");
3483  free_benchmark(&b_left);
3484 
3485  free(midpoint);
3486  midpoint = NULL;
3487 
3488  free_benchmark(&b_oldapprox);
3489  free_benchmark(&b_avg);
3490  return -1;
3491  }
3492 
3493 
3494  if(bench_cut_intersects(h, b_oldapprox, b_avg)) {
3495  //
3496  // we are on a straight line section of the model
3497  // no need to add new construction intervals, can
3498  // finalise in the region of the current interval
3499  //
3501  }
3502  else {
3503  intersects_left = bench_cut_intersects(h, b_left, b_avg);
3504  intersects_right = bench_cut_intersects(h, b_right, b_avg);
3505 
3506  // this should never happen, as it would mean model already
3507  // accurately approximated the benchmark above
3508  if(intersects_left == 1 && intersects_right == 1) {
3510  }
3511  else if(intersects_left == 1) {
3512  //
3513  // new bench cut intersects l.h.s. of previous
3514  // model approx, we are finished building in
3515  // the range b_left to b, so we remove the old
3516  // construction interval and replace it with one
3517  // ranging from b to b_right OR interval midpoint
3518  // to interval end.
3519  //
3520  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3521  midpoint, i->end);
3522  if(new_i == NULL) {
3523  ERRPRINTF("Error initialising new interval.\n");
3524  free_benchmark(&b_right);
3525  free_benchmark(&b_left);
3526 
3527  free(midpoint);
3528  midpoint = NULL;
3529 
3530  free_benchmark(&b_oldapprox);
3531  free_benchmark(&b_avg);
3532  return -1;
3533  }
3534 
3535  if(is_interval_divisible(new_i, r) == 1) {
3536  add_top_interval(m->interval_list, new_i);
3537  }
3538  else {
3539  DBGPRINTF("Interval not divisible, not adding.\n");
3540  print_interval(PMM_DBG, new_i);
3541  free_interval(&new_i);
3542  }
3543 
3545  }
3546  else if(intersects_right == 1) {
3547  // intersects r.h.s. of old approximation, replace
3548  // with interval start to interval midpoint OR
3549  // b_left to b
3550  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3551  i->start, midpoint);
3552 
3553  if(new_i == NULL) {
3554  ERRPRINTF("Error initialising new interval.\n");
3555  free_benchmark(&b_right);
3556  free_benchmark(&b_left);
3557 
3558  free(midpoint);
3559  midpoint = NULL;
3560 
3561  free_benchmark(&b_oldapprox);
3562  free_benchmark(&b_avg);
3563  return -1;
3564  }
3565 
3566  if(is_interval_divisible(new_i, r) == 1) {
3567  add_top_interval(m->interval_list, new_i);
3568  }
3569  else {
3570  DBGPRINTF("Interval not divisible, not adding.\n");
3571  print_interval(PMM_DBG, new_i);
3572  free_interval(&new_i);
3573  }
3574 
3576  }
3577  else {
3578  // intersects neither r.h.s. or l.h.s., replace with
3579  // intervals of BISECT type from interval start to interval
3580  // midpoint, and interval midpoint to interval end. OR
3581  // b_left to b, and b to b_right.
3582  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3583  i->start, midpoint);
3584 
3585  if(new_i == NULL) {
3586  ERRPRINTF("Error initialising new interval.\n");
3587  free_benchmark(&b_right);
3588  free_benchmark(&b_left);
3589 
3590  free(midpoint);
3591  midpoint = NULL;
3592 
3593  free_benchmark(&b_oldapprox);
3594  free_benchmark(&b_avg);
3595  return -1;
3596  }
3597 
3598  if(is_interval_divisible(new_i, r) == 1) {
3599  add_top_interval(m->interval_list, new_i);
3600  }
3601  else {
3602  DBGPRINTF("Interval not divisible, not adding.\n");
3603  print_interval(PMM_DBG, new_i);
3604  free_interval(&new_i);
3605  }
3606 
3607  new_i = init_interval(i->plane, i->n_p, IT_GBBP_BISECT,
3608  midpoint, i->end);
3609 
3610  if(new_i == NULL) {
3611  ERRPRINTF("Error initialising new interval.\n");
3612  free_benchmark(&b_right);
3613  free_benchmark(&b_left);
3614 
3615  free(midpoint);
3616  midpoint = NULL;
3617 
3618  free_benchmark(&b_oldapprox);
3619  free_benchmark(&b_avg);
3620  return -1;
3621  }
3622 
3623  if(is_interval_divisible(new_i, r) == 1) {
3624  add_top_interval(m->interval_list, new_i);
3625  }
3626  else {
3627  DBGPRINTF("Interval not divisible, not adding.\n");
3628  print_interval(PMM_DBG, new_i);
3629  free_interval(&new_i);
3630  }
3631 
3633  }
3634  }
3635 
3636  free(midpoint);
3637  midpoint = NULL;
3638 
3639  free_benchmark(&b_avg);
3640 
3641  if(b_oldapprox != NULL) {
3642  free_benchmark(&b_oldapprox);
3643  }
3644  if(b_left != NULL) {
3645  free_benchmark(&b_left);
3646  }
3647  if(b_right != NULL) {
3648  free_benchmark(&b_right);
3649  }
3650 
3652  DBGPRINTF("interval list now empty.\n");
3653 
3654  new_i = new_interval();
3655  new_i->type = IT_COMPLETE;
3656  add_top_interval(m->interval_list, new_i);
3657 
3658  m->complete = 1;
3659  }
3660 
3661  return 0;
3662 }
3663 
3664 /*!
3665  * Process a point type interval. Remove interval from the interval list
3666  * and if the list is now empty, add a IT_COMPLETE interval to the list to
3667  * tag that construction has been completed, also set the complete parameter
3668  * of the model to 1
3669  *
3670  * @param r pointer to the routine the benchmark belongs to
3671  * @param i pointer to the interval
3672  *
3673  * @pre interval i has a type IT_POINT
3674  *
3675  * @return 0 on success, -1 on failure
3676  */
3677 int
3679 {
3680  struct pmm_interval *new_i;
3681  struct pmm_model *m;
3682 
3683  m = r->model;
3684 
3686 
3688  DBGPRINTF("interval list now empty.\n");
3689 
3690  new_i = new_interval();
3691  new_i->type = IT_COMPLETE;
3692  add_top_interval(m->interval_list, new_i);
3693 
3694  m->complete = 1;
3695  }
3696 
3697  return 0;
3698 }
3699 
3700 /*!
3701  * Tests if an interval can be processed to return a benchmark from it. I.e.
3702  * if the 'length' of the interval is enough for a new benchmark point to
3703  * be returned when GBBP is applied to that interval.
3704  *
3705  * This is achieved by applying GBBP to the interval, then aligning the start
3706  * and end points of the interval and comparing them to the GBBP point.
3707  *
3708  * If the GBBP point is equal to either the aligned start/end points, we
3709  * determine that the interval is not divisible.
3710  *
3711  * @param i pointer to the interval to test
3712  * @param r pointer to the routine that the interval belongs to
3713  *
3714  * @return 1 if interval is divisible, 0 if it is not or -1 on error
3715  *
3716  * @pre interval is of type IT_GBBP_CLIMB, IT_GBBP_BISECT or IT_GBBP_INFLECT
3717  *
3718  * TODO 'divisible' is the wrong adjective to describe this function
3719  */
3720 int
3722 {
3723  int *params;
3724  int *start_aligned, *end_aligned;
3725 
3726 
3727  switch (i->type) {
3728  case IT_GBBP_EMPTY:
3729  return 1;
3730  case IT_GBBP_CLIMB:
3731  case IT_GBBP_BISECT:
3732  case IT_GBBP_INFLECT:
3733 
3734  // allocate/init some memory
3735  params = malloc(r->pd_set->n_p * sizeof *params);
3736  start_aligned = init_param_array_copy(i->start, r->pd_set->n_p);
3737  end_aligned = init_param_array_copy(i->end, r->pd_set->n_p);
3738  if(params == NULL || start_aligned == NULL || end_aligned == NULL) {
3739  ERRPRINTF("Error allocating memory.\n");
3740  return -1;
3741  }
3742 
3743  //align start/end
3744  align_params(start_aligned, r->pd_set);
3745  align_params(end_aligned, r->pd_set);
3746 
3747  // find the GBBP point of the interval
3748  if(multi_gbbp_bench_from_interval(r, i, params) < 0) {
3749  ERRPRINTF("Error getting GBBP bench point from interval.\n");
3750 
3751  free(start_aligned);
3752  start_aligned = NULL;
3753  free(end_aligned);
3754  end_aligned = NULL;
3755  free(params);
3756  params = NULL;
3757 
3758  return -1;
3759  }
3760 
3761  // TODO maybe test if the point is > start and < end
3762 
3763  // test that point is not equal to aligned start or end of interval
3764  if(params_cmp(start_aligned, params, r->pd_set->n_p) == 0 ||
3765  params_cmp(end_aligned, params, r->pd_set->n_p) == 0)
3766  {
3767  free(start_aligned);
3768  start_aligned = NULL;
3769  free(end_aligned);
3770  end_aligned = NULL;
3771  free(params);
3772  params = NULL;
3773 
3774  return 0; //not divisible
3775  }
3776  else {
3777  free(start_aligned);
3778  start_aligned = NULL;
3779  free(end_aligned);
3780  end_aligned = NULL;
3781  free(params);
3782  params = NULL;
3783 
3784  return 1; //divisible
3785  }
3786 
3787  break;
3788 
3789  case IT_BOUNDARY_COMPLETE:
3790  case IT_COMPLETE:
3791  case IT_POINT:
3792  default:
3793  return 0;
3794  break;
3795  }
3796 }
3797 
3798 /*!
3799  * Finds the parameters of the next benchmarking point based on a
3800  * parameter interval and the GBBP algorithm.
3801  *
3802  * If the interval is _EMPTY, the model is empty and the starting
3803  * point of the interval is returned for benchmarking
3804  *
3805  * If the interval is _CLIMB, the model is in the initial building
3806  * phase and an increment on the starting point is returned for benchmarking
3807  *
3808  * If the interval is either _BISECT or _INFLECT then the main phase
3809  * of building is in progress and the mid point between the start and
3810  * end of the interval is returned for benchmarking
3811  *
3812  * If the interval is _POINT, then the interval describes a distingt point
3813  * rather than an interval and this point is returned for benchmarking
3814  *
3815  * The benchmarking point is aligned to the stride/offset of parameter
3816  * definitions.
3817  *
3818  * @param r pointer to the relevant routine
3819  * @param interval pointer to the interval
3820  * @param params pointer to an array where the next point should be
3821  * stored
3822  *
3823  * @return 0 on success, -1 if no benchmark is associated with the interval
3824  * type (i.e. IT_COMPLETE) or -2 on an error
3825  *
3826  * @pre params is a pointer to allocated memory of the correct size
3827  */
3828 int
3830  struct pmm_interval *interval,
3831  int *params)
3832 {
3833 
3834  //depending on interval type, set the param of the boundary plane
3835  switch (interval->type) {
3836  case IT_GBBP_EMPTY : // empty, return the interval's start
3837 
3838  DBGPRINTF("assigning start problem size.\n");
3839 
3840  set_param_array_copy(params, interval->start, r->pd_set->n_p);
3841 
3842  return 0;
3843 
3844  case IT_GBBP_CLIMB : // climbing, return increment on interval's start
3845 
3846  DBGPRINTF("assigning increment on interval start point.\n");
3847 
3848 
3850  interval, r->pd_set);
3851 
3852  align_params(params, r->pd_set);
3853 
3854  return 0;
3855 
3856  case IT_GBBP_BISECT : // interval bisect, return midpoint
3857  case IT_GBBP_INFLECT : // interval inflect, return midpoint
3858 
3859  //set params to be midpoint between start and end of interval
3860  set_params_interval_midpoint(params, interval);
3861 
3862  //align midpoint
3863  align_params(params, r->pd_set);
3864 
3865  DBGPRINTF("assigned midpoint of interval from start:\n");
3866  print_params(PMM_DBG, interval->start, interval->n_p);
3867  DBGPRINTF("to end:\n");
3868  print_params(PMM_DBG, interval->end, interval->n_p);
3869  DBGPRINTF("at:\n");
3870  print_params(PMM_DBG, params, interval->n_p);
3871 
3872  return 0;
3873 
3874  case IT_POINT : // 'point' type interval from mesh, return copied point
3875 
3876  DBGPRINTF("assigning copy of interval 'point'.\n");
3877 
3878  set_param_array_copy(params, interval->start, r->pd_set->n_p);
3879 
3880  return 0;
3881 
3882  case IT_COMPLETE :
3883  case IT_NULL :
3884  case IT_BOUNDARY_COMPLETE :
3885  return -1;
3886 
3887  default :
3888  ERRPRINTF("Invalid interval type: %s\n",
3889  interval_type_to_string(interval->type));
3890  return -2;
3891  }
3892 }
3893 
3894 /*!
3895  * Sets a param array to the midpoint between start and end points of an
3896  * interval.
3897  *
3898  * Precision is limited to the integer type of the point definition, round
3899  * up is carried out on the division.
3900  *
3901  * @param p pointer to the parameter array
3902  * @param i pointer to the interval
3903  *
3904  * @pre p is allocated and i is an interval of the correct type, with start
3905  * and end points set (IT_BISECT, IT_INFLECT)
3906  */
3907 void
3909 {
3910  int j;
3911 
3912  DBGPRINTF("i->n_p:%d\n", i->n_p);
3913 
3914  for(j=0; j<i->n_p; j++) {
3915  //integer division by two with round up
3916  //round of x/y = (x+y/2)/y ...
3917  p[j] = ((i->start[j]+i->end[j])+1)/2;
3918  }
3919 
3920  return;
3921 }
3922