aboutsummaryrefslogtreecommitdiff
path: root/Carpet/LoopControl/src/lc_hill.c
blob: b0ddcc76275e3c1bc8916d8cceb8a589a3959a6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

#include <cctk.h>
#include <cctk_Parameters.h>

#include "loopcontrol.h"

#include "lc_hill.h"



#define debug_assert assert



static
double
drand (void)
{
  double const r = rand() / (RAND_MAX + 1.0);
  assert (r >= 0.0 && r < 1.0);
  return r;
}

static
int
irand (int const imaxval)
{
  assert (imaxval >= 0);
  int const i = drand() * imaxval;
  assert (i >= 0 && i < imaxval);
  return i;
}



static
double
time_for_stattime (lc_stattime_t const * restrict const lt)
{
  assert (lt);
  return lt->time_calc_sum / lt->time_count;
}

#if 0
/* unused */
static
double
time_for_state (lc_statset_t const * restrict const ls,
                lc_state_t const * restrict const state)
{
  lc_stattime_t const * restrict const lt = lc_stattime_find (ls, state);
  assert (lt);
  return time_for_stattime (lt);
}
#endif



void
lc_hill_init (lc_statset_t * restrict const ls,
              lc_state_t * const new_state)
{
  DECLARE_CCTK_PARAMETERS;
  
  lc_hill_state_t * restrict lh = ls->hill_state;
  
  /* Initialise state */
  if (! lh) {
    if (verbose) {
      CCTK_INFO ("Hill climbing: Initialising");
    }
    ls->hill_state = malloc (sizeof * ls->hill_state);
    lh = ls->hill_state;
    lh->iteration = 0;
    lh->have_best = 0;
    lh->excursion_start = 0;
    lh->have_previous = 0;
    lh->state = * new_state;
    return;
  }
  
  /* If the overhead has become too large, do nothing. */
  if (ls->time_setup_sum > maximum_setup_overhead * ls->time_calc_sum) {
    /* Stay at the old state. */
    * new_state = lh->state;
    return;
  }
  
  ++ lh->iteration;
  
  if (verbose) {
    CCTK_VInfo (CCTK_THORNSTRING,
                "Hill climbing: iter %d, state %2d/{%2d,%2d,%2d}, time %g",
                lh->iteration,
                lh->state.topology,
                lh->state.tiling[0],
                lh->state.tiling[1],
                lh->state.tiling[2],
                lh->time);
  }
  
  /* Test whether we have a new best time */
  if (! lh->have_best || lh->time < lh->best_time) {
    /* Remember this state */
    if (verbose) {
      CCTK_INFO ("Hill climbing: This is a new best time");
    }
    lh->best = lh->state;
    lh->best_time = lh->time;
    lh->have_best = 1;
    lh->excursion_start = lh->iteration;
  } else if (lh->have_best && lc_state_equal (& lh->state, & lh->best)) {
    /* Update time for best state */
    if (verbose) {
      CCTK_INFO ("Hill climbing: Updating best time");
    }
    lh->best_time = lh->time;
    /* Is there now a better state? */
    for (lc_stattime_t * lt = ls->stattime_list; lt; lt = lt->next) {
      double const time = lt->time_calc_sum / lt->time_count;
      if (time < lh->best_time) {
        lh->best = lt->state;
        lh->best_time = time;
      }
    }
  }
  
  /* Compare the time for the current state with the time for the
     previous state. If the previous state was better, backtrack. */
  if (lh->have_previous && lh->previous_time < lh->time) {
    if (verbose) {
      CCTK_INFO ("Hill climbing: Backtracking");
    }
    lh->state = lh->previous;
    lh->time = lh->previous_time;
    lh->have_previous = 0;
  }
    
  /* Give up if the current time is too bad */
  if (lh->have_best) {
    int const immediate_overhead = 
      lh->time > lh->best_time * (1.0 + immediate_overhead_threshold);
    int const delayed_overhead =
      lh->iteration > lh->excursion_start + overhead_threshold_delay &&
      lh->time > lh->best_time * (1.0 + delayed_overhead_threshold);
    if (immediate_overhead || delayed_overhead) {
      if (verbose) {
        CCTK_INFO ("Hill climbing: Reverting to best known state");
      }
      lh->excursion_start = lh->iteration;
      lh->state = lh->best;
      lh->time = lh->best_time;
    }
  }
  
  /* Age the state */
  lh->previous = lh->state;
  lh->previous_time = lh->time;
  lh->have_previous = 1;
  
 search:;
  
  /* Look which neighbours exist. */
  /* typedef enum { nb_boundary, nb_missing, nb_exists } neighbour_t; */
  /* neighbour_t  neighbours[3][2]; */
  lc_state_t   nb_state[3][2];
  double       nb_time[3][2];
  lc_state_t * nb_nonexist_state[6];
  int          num_nonexist_states = 0;
  lc_state_t * nb_minimum_time = NULL;
  double       minimum_time;
  for (int d=0; d<3; ++d) {
    for (int f=0; f<2; ++f) {
      nb_state[d][f] = lh->state;
      nb_state[d][f].tiling[d] += f ? + 1: -1;
      debug_assert (nb_state[d][f].topology >= 0);
      debug_assert (nb_state[d][f].topology < ls->ntopologies);
      int const ntilings = ls->topology_ntilings[d][nb_state[d][f].topology];
      if (nb_state[d][f].tiling[d] < 0 ||
          nb_state[d][f].tiling[d] >= ntilings)
      {
        /* neighbours[d][f] = nb_boundary; */
        /* do nothing */
      } else {
        lc_stattime_t const * restrict const nb_lt =
          lc_stattime_find (ls, & nb_state[d][f]);
        if (! nb_lt) {
          /* neighbours[d][f] = nb_missing; */
          debug_assert (num_nonexist_states < 6);
          nb_nonexist_state[num_nonexist_states++] = & nb_state[d][f];
        } else {
          /* neighbours[d][f] = nb_exists; */
          nb_time[d][f] = time_for_stattime (nb_lt);
          if (! nb_minimum_time || nb_time[d][f] < minimum_time) {
            nb_minimum_time = & nb_state[d][f];
            minimum_time = nb_time[d][f];
          }
        }
      }
    }
  }
  
  /* If not all neighbours exist, then choose a random neighbour and
     move there. */
  if (num_nonexist_states > 0) {
    if (verbose) {
      CCTK_INFO ("Hill climbing: Examining a new state");
    }
    int const choice = irand (num_nonexist_states);
    lh->state = * nb_nonexist_state[choice];
    * new_state = lh->state;
    return;
  }
  
  if (! nb_minimum_time) {
    /* There are no neighbours. Stay where we are. */
    if (verbose) {
      CCTK_INFO ("Hill climbing: No neighbours, staying put");
    }
    * new_state = lh->state;
    return;
  }
  
  /* All neighbours exist. Look whether we are in a local minimum. */
  assert (nb_minimum_time);
  if (minimum_time >= lh->time) {
    /* We are in a local minimum. */
    if (verbose) {
      CCTK_INFO ("Hill climbing: Local minimum reached");
    }
    
    /* Every so often take a small jump. */
    if (drand() < probability_small_jump) {
      /* Be curious, go somewhere nearby. */
      if (verbose) {
        CCTK_INFO ("Hill climbing: Making a small jump");
      }
      for (int ntries = 0; ntries < max_jump_attempts; ++ ntries) {
        lc_state_t try_state = lh->state;
        if (drand() < 0.25) {
          /* Change the topology, but not the tiling. */
          try_state.topology = irand (ls->ntopologies);
          for (int d=0; d<3; ++d) {
            if (try_state.tiling[d] >=
                ls->topology_ntilings[d][try_state.topology])
            {
              /* The tiling doesn't fit for this new topology; don't
                 choose this topology. */
              goto next_try;
            }
          }
        } else {
          /* Change the tiling a bit, but keep the topology */
          for (int d=0; d<3; ++d) {
            int const i0 =
              lc_max (try_state.tiling[d] - small_jump_distance, 0);
            int const i1 =
              lc_min (try_state.tiling[d] + small_jump_distance + 1,
                      ls->topology_ntilings[d][try_state.topology]);
            try_state.tiling[d] = i0 + irand (i1 - i0);
            debug_assert (try_state.tiling[d] >= 0);
            debug_assert (try_state.tiling[d] <
                          ls->topology_ntilings[d][try_state.topology]);
          }
        }
        if (! lc_stattime_find (ls, & try_state)) {
          lh->state = try_state;
          * new_state = lh->state;
          return;
        }
      next_try:;
      }
      /* Don't jump after all. */
    }
    
    /* Every so often take a random jump. */
    if (drand() < probability_random_jump) {
      /* Be adventurous, go somewhere unknown. */
      if (verbose) {
        CCTK_INFO ("Hill climbing: Jumping randomly");
      }
      for (int ntries = 0; ntries < max_jump_attempts; ++ ntries) {
        lc_state_t try_state;
        try_state.topology = irand (ls->ntopologies);
        for (int d=0; d<3; ++d) {
          try_state.tiling[d] =
            irand (ls->topology_ntilings[d][try_state.topology]);
        }
        if (! lc_stattime_find (ls, & try_state)) {
          /* The new state is hitherto unknown, use it. */
          lh->state = try_state;
          lh->excursion_start = lh->iteration;
          lh->have_previous = 0; /* disable backtracking */
          * new_state = lh->state;
          return;
        }
      }
      /* Don't jump after all. */
    }
    
    /* If the current state is not the best state, give up and go
       back. */
    if (! lc_state_equal (& lh->state, & lh->best)) {
      /* Revert to the best known state. */
      if (verbose) {
        CCTK_INFO ("Hill climbing: Reverting to best known state");
      }
      lh->state = lh->best;
      lh->excursion_start = lh->iteration;
      lh->have_previous = 0;
      * new_state = lh->best;
      return;
    }
    
    /* Be content, do nothing. */
    if (verbose) {
      CCTK_INFO ("Hill climbing: Resting");
    }
    * new_state = lh->state;
    return;
  }
  
  /* One of the neighbours is better. Move to this neighbour, and
     continue the search there. */
  if (verbose) {
    CCTK_INFO ("Hill climbing: Found a better neighbour, going there");
  }
  lh->state = * nb_minimum_time;
  lh->time = minimum_time;
  goto search;
}



void
lc_hill_finish (lc_statset_t * restrict const ls,
                lc_stattime_t const * restrict const lt)
{
  lc_hill_state_t * restrict const lh = ls->hill_state;
  
  lh->time = time_for_stattime (lt);
}