ECF 1.5
flowshop.cpp
1/* flowshop.c
2 *
3 * (C) 2018 Eva Tuba <etuba@ieee.org> and Carlos M. Fonseca <cmfonsec@dei.uc.pt>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or (at
8 * your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include "flowshop.h"
24
25
26
27/**********************************/
28/* ----- Utility functions ----- */
29/**********************************/
30
31#if 1
32/*
33 * Random integer x such that 0 <= x <= n_max
34 * Status: FINAL
35 */
36static int randint(int n_max) {
37 int x;
38 if (n_max < 1)
39 return 0;
40 div_t y = div(-RAND_MAX-1, -n_max-1);
41 do
42 x = rand();
43 while (x > RAND_MAX + y.rem);
44 return x / y.quot;
45}
46#else
47#include <gsl/gsl_rng.h>
48extern gsl_rng *rng; /* The single rng instance used by the whole code */
49
50static int randint(int n_max) {
51 return gsl_rng_uniform_int(rng, n_max+1);
52}
53#endif
54
55/*
56 * Random permutation of size n
57 * Status: FINAL
58 * Notes:
59 * Algorithm described verbally in Knuth (1997), The Art of Computer
60 * Programming, Volume 2, Seminumerical Algorithms, 3rd ed., p. 145.
61 * See also https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_%22inside-out%22_algorithm
62 */
63static int *randperm(int *p, int n) {
64 /* Inside-out algorithm */
65 int i, j;
66 if (n > 0) {
67 p[0] = 0;
68 for (i = 1; i < n; i++) {
69 j = randint(i);
70 p[i] = p[j];
71 p[j] = i; /* = source[i] */
72 }
73 }
74 return p;
75}
76
77/*
78 * Evaluate schedule makespan
79 * Status: FINAL
80 */
81static void makespan(struct solution *s) {
82 /* order is jobs x machines - job order changes, machine order does not */
83 int j, n = s->n; /* jobs */
84 int i, m = s->m; /* machines */
85 int k; /* auxiliary running index */
86 int *t = s->times; /* time array */
87 int *pd = s->prob->data; /* processing times */
88 int *sd = s->data; /* job order */
89 /* first row and column of s->times initialised to 0 by allocSolution() */
90 /* evaluate from first job that changed since last evaluation */
91 k = (m+1)*(s->eval+1) + 1;
92 for(j = s->eval; j < n; j++, k++)
93 for(i = 0; i < m; i++, k++)
94 t[k] = pd[m*sd[j]+i] + ((t[k-1] >= t[k-m-1]) ? t[k-1] : t[k-m-1]);
95 s->objvalue = t[k-2];
96 s->eval = n; /* fully evaluated */
97}
98
99/*
100 * Longest Increasing Subsequence
101 * Status: INTERIM
102 * Notes:
103 * Based on https://en.wikipedia.org/wiki/Longest_increasing_subsequence
104 * This implementation determines the lexicographically smallest LIS.
105 */
106static int lis(int *P, int *M, int *X, int n) {
107 int i, lo, mid, hi;
108 int newL, L = 0;
109 for (i = 0; i < n; i++) {
110 lo = 1;
111 hi = L;
112 while (lo <= hi) {
113 mid = (lo + hi + 1) / 2;
114 if (X[M[mid-1]] < X[i])
115 lo = mid + 1;
116 else
117 hi = mid - 1;
118 }
119 newL = lo;
120 P[i] = (newL > 1) ? M[newL-2] : -1;
121 M[newL-1] = i;
122 if (newL > L)
123 L = newL;
124 }
125 return L;
126}
127
128/*
129 * Binary search
130 * Status: FINAL
131 * Notes:
132 * After Cormen et al. (2009), Introduction to Algorithms, 3rd ed., p. 799.
133 */
134static int binary_search(int *A, int *I, int x, int low, int high) {
135 int mid;
136 while (low < high) {
137 mid = (low + high) / 2;
138 if (x <= A[I[mid]])
139 high = mid;
140 else
141 low = mid + 1;
142 }
143 return high;
144}
145
146/**********************************************/
147/* ----- Problem-specific instantiation ----- */
148/**********************************************/
149
150/*
151 * PFSP instantiation
152 * Status: TENTATIVE
153 * Notes:
154 * Reads files in "standard" PFSP format
155 * Needs better error checking
156 */
157struct problem *newProblem(const char *filename) {
158 struct problem *p = NULL;
159 FILE *infile;
160 int j, i, n=-1, m=-1;
161 infile = fopen(filename, "r");
162 if (infile) {
163 fscanf(infile, "%d", &n);
164 fscanf(infile, "%d", &m);
165 if (n > 0 && m > 0) {
166 p = (problem*) malloc(sizeof (struct problem));
167 p->data = (int *) malloc(n * m * sizeof (int));
168 for (j = 0; j < n; j++)
169 for (i = 0; i < m; i++) {
170 fscanf(infile, "%*d %d", p->data+m*j+i);
171 }
172 p->n = n;
173 p->m = m;
174 } else
175 fprintf(stderr, "Invalid knapsack instance %s\n", filename);
176 fclose(infile);
177 } else
178 fprintf(stderr, "Cannot open file %s\n", filename);
179 return p;
180}
181
182/*****************************/
183/* ----- API functions ----- */
184/*****************************/
185
186/* Memory management */
187
188/*
189 * Allocate memory for a solution
190 * Status: FINAL
191 */
192struct solution *allocSolution(struct problem *p) {
193 int i;
194 struct solution *s = (solution*) malloc(sizeof (struct solution));
195 s->prob = p;
196 s->data = (int*) malloc(p->n * sizeof (int));
197 s->n = p->n;
198 s->m = p->m;
199 s->times = (int*) malloc((p->n+1) * (p->m+1) * sizeof (int));
200 /* init first row and column of s->times - makespan() depends on this */
201 for(i = 0; i <= p->m; i++)
202 s->times[i] = 0;
203 for(i = 1; i <= p->n; i++)
204 s->times[(p->m+1)*i] = 0;
205 return s;
206}
207
208/*
209 * Allocate memory for a move
210 * Status: FINAL
211 */
212struct move *allocMove(struct problem *p) {
213 struct move *v = (move*) malloc(sizeof (struct move));
214 v->prob = p;
215 return v;
216}
217
218/*
219 * Allocate memory for a path
220 * Status: FINAL
221 */
222struct pathState *allocPathState(struct problem *p) {
223 struct pathState *ps = (pathState*) malloc (sizeof (struct pathState));
224 ps->prob = p;
225 ps->data = (int*) malloc(p->n * sizeof (int));
226 ps->pred = (int*) malloc(p->n * sizeof (int));
227 ps->pos = (int*) malloc(p->n * sizeof (int));
228 ps->n = p->n;
229 return ps;
230}
231
232/*
233 * Free the memory used by a move
234 * Status: FINAL
235 */
236void freeProblem(struct problem *p) {
237 free(p->data);
238 free(p);
239}
240
241/*
242 * Free the memory used by a solution
243 * Status: FINAL
244 */
245void freeSolution(struct solution *s) {
246 free(s->data);
247 free(s->times);
248 free(s);
249}
250
251/*
252 * Free the memory used by a move
253 * Status: FINAL
254 */
255void freeMove(struct move *v) {
256 free(v);
257}
258
259/*
260 * Free the memory used by a path
261 * Status: FINAL
262 */
263void freePathState(struct pathState *ps) {
264 free(ps->data);
265 free(ps->pred);
266 free(ps->pos);
267 free(ps);
268}
269
270/* I/O */
271void printProblem(struct problem *p) {
272 int i,j;
273 int n = p->n;
274 int m = p->m;
275 printf("# Permutation Flowshop Problem (%d jobs, %d machines)\n", n, m);
276 printf("%d %d\n", n, m);
277 for (j = 0; j < n; j++) {
278 for (i = 0; i < m; i++)
279 printf(" %d",p->data[j*m+i]);
280 printf("\n");
281 }
282}
283
284void printSolution(struct solution *s) {
285 int i;
286 printf("%dx%d Flowshop solution\n p:", s->n, s->m);
287 for(i = 0; i < s->n; i++)
288 printf(" %d",s->data[i]);
289 printf("\n");
290#if 0
291 for(i = 0; i < (s->eval+1)*(s->m+1); i++) {
292 printf(" %d",s->times[i]);
293 if ((i+1) % (s->m+1) == 0)
294 printf("\n");
295 }
296#endif
297}
298
299void printMove(struct move *v) {
300 printf("%dx%d Flowshop move: %d, %d\n", v->prob->n, v->prob->m, v->data[0], v->data[1]);
301}
302
303void printPathState(struct pathState *ps) {
304 int i;
305 printf("%dx%d Flowshop path state\n", ps->n, ps->prob->m);
306 printf(" Path length: %d\n Permutation:", ps->n - ps->in_order);
307 for (i = 0; i < ps->n; i++)
308 printf(" %d",ps->data[i]);
309 printf("\n In order: %d elements\n Positions:",ps->in_order);
310 for (i = 0; i < ps->n; i++)
311 printf(" %d",ps->pos[i]);
312 printf("\n");
313}
314
315
316/* Solution generation */
317
318/*
319 * Generate solutions uniformly at random
320 * Status: FINAL
321 */
322struct solution *randomSolution(struct solution *s) {
323 /* solution s must have been allocated with allocSolution() */
324 randperm(s->data, s->n);
325 s->eval = 0; /* Not evaluated yet */
326 return s;
327}
328
329/* Solution inspection */
330
331/*
332 * Generate solutions uniformly at random
333 * Status: FINAL
334 * Notes:
335 * Supports incremental evaluation by makespan()
336 */
337double getObjectiveValue(struct solution *s) {
338 if (s->eval < s->n)
339 makespan(s);
340 return (double)s->objvalue;
341}
342
343/* Operations on solutions*/
344struct solution *copySolution(struct solution *dest, const struct solution *src) {
345 dest->prob = src->prob;
346 dest->n = src->n;
347 dest->m = src->m;
348 memcpy(dest->data, src->data, src->n * sizeof (int));
349 memcpy(dest->times, src->times, (src->n+1) * (src->m+1) * sizeof (int));
350 dest->objvalue = src->objvalue;
351 dest->eval = src->eval;
352 return dest;
353}
354
355/*
356 * Apply a move to a solution
357 * Status: FINAL
358 * Notes:
359 * Supports incremental evaluation by makespan()
360 */
361struct solution *applyMove(struct solution *s, const struct move *v) {
362 int i, j, el;
363 /* int k; */
364 i = v->data[0];
365 j = v->data[1];
366 if (i == j) /* do nothing */
367 return s;
368 el = s->data[i];
369 /* Would it be worth using memmove() here? It doesn't seem to matter much. */
370 if (i < j) {
371#if 0
372 for (k = i; k < j; k++)
373 s->data[k] = s->data[k+1];
374#else
375 memmove(s->data+i, s->data+i+1, (j-i) * sizeof (int));
376#endif
377 if (i < s->eval)
378 s->eval = i;
379 } else {
380#if 0
381 for (k = i; k > j; k--)
382 s->data[k] = s->data[k-1];
383#else
384 memmove(s->data+j+1, s->data+j, (i-j) * sizeof (int));
385#endif
386 if (j < s->eval)
387 s->eval = j;
388 }
389 s->data[j] = el;
390 return s;
391}
392
393/* Move generation */
394
395/*
396 * Generate moves uniformly at random
397 * Status: FINAL
398 */
399struct move *randomMove(struct move *v, const struct solution *s) {
400 /* move v must have been allocated with allocMove() */
401 int n, r, c;
402 n = s->n;
403 r = randint(n-2);
404 c = randint(n-2);
405 /* convert to actual indices */
406 if(r <= c) {
407 v->data[0] = r;
408 v->data[1] = c+1;
409 } else {
410 v->data[0] = r+1;
411 v->data[1] = c;
412 }
413 return v;
414}
415
416/*
417 * Generate the next random move in a path towards a solution
418 * Status: TENTATIVE, NEEDS_TESTING
419 * Notes:
420 * Move distribution is not uniform
421 */
422struct move *nextRandomMove(struct move *v, struct pathState *ps) {
423 int max = ps->in_order;
424 int i, j, k, el, r, s, n = ps->n;
425 int *pos = ps->pos, *data = ps->data;
426 int low, high;
427
428 if (max >= n) { /* end of path has been reached, no move is possible */
429 v->data[0] = v->data[1] = 0; /* null move */
430 return v;
431 }
432
433 /* Draw at random a position to move */
434 r = max + randint(n-max-1);
435 i = v->data[0] = pos[r];
436 /* Find where it can be inserted */
437#if 0
438 for (s = 0; s < max; s++)
439 if (data[i] <= data[pos[s]])
440 break;
441 printf("data[i] = %d, data[pos[s]] = %d, max = %d, s = %d\n", data[i], data[pos[s]], max, s);
442 printf("s = %d, bs = %d, bss = %d\n\n", s, binary_search(data, pos, data[i], 0, max), binary_search(data, pos, data[pos[s]], 0, max));
443#else
444 s = binary_search(data, pos, data[i], 0, max);
445#endif
446 if (s == 0) {
447 low = 0;
448 high = pos[s];
449 } else if (s == max) {
450 low = pos[s-1];
451 high = n-1;
452 } else if (i < pos[s]) {
453 low = pos[s-1];
454 high = pos[s]-1;
455 } else {
456 low = pos[s-1]+1;
457 high = pos[s];
458 }
459 j = v->data[1] = low + randint(high-low);
460
461 /* Update pathState */
462 ps->in_order++;
463 /* replicated from applyMove() */
464 el = data[i];
465 if (i < j) {
466 for (k = i; k < j; k++)
467 data[k] = data[k+1];
468#if 0
469 for (k = 0; k < n; k++) /* should be split into two for loops */
470 if (pos[k] > i && pos[k] <= j)
471 pos[k]--;
472#else
473 for (k = s-1; k >= 0 && pos[k] > i; k--)
474 pos[k]--;
475 for (k = r+1; k < n && pos[k] <= j; k++)
476 pos[k]--;
477#endif
478 } else {
479 for (k = i; k > j; k--)
480 data[k] = data[k-1];
481#if 0
482 for (k = 0; k < n; k++) /* should be split into two for loops */
483 if (pos[k] >= j && pos[k] < i)
484 pos[k]++;
485#else
486 for (k = s; k < max && pos[k] < i; k++)
487 pos[k]++;
488 for (k = r-1; k >= max && pos[k] >= j; k--)
489 pos[k]++;
490#endif
491 }
492 data[j] = el;
493 /* insert the new position into pos */
494#if 0
495 for (k = r; k > s; k--)
496 pos[k] = pos[k-1];
497#else
498 memmove(pos+s+1, pos+s, (r-s) * sizeof (int));
499#endif
500 pos[s] = j;
501
502 return v;
503}
504
505/* Path generation */
506
507/*
508 * Set up a path from one solution to another
509 * Status: TENTATIVE, NEEDS_TESTING
510 */
511struct pathState *initPathTo(struct pathState *ps, const struct solution *s1, const struct solution *s2) {
512 int i, j, k, max;
513 int n = ps->n;
514
515 /* Compute reference permutation p2i[p1] using ps->pos as a buffer */
516 for (i = 0; i < n; i++)
517 ps->pos[s2->data[i]] = i;
518 for (i = 0; i < n; i++)
519 ps->data[i] = ps->pos[s1->data[i]];
520
521 /* Compute LIS using ps->pos as a buffer */
522 ps->in_order = max = lis(ps->pred, ps->pos, ps->data, n);
523 k = ps->pos[max-1];
524
525 /* Store LIS positions in ps->pos */
526 for (i = j = n; i > 0;) {
527 i--;
528 if (i == k) {
529 ps->pos[--max] = k;
530 k = ps->pred[k];
531 } else
532 ps->pos[--j] = i;
533 }
534 return ps;
535}
536
537/*
538 * Set up a path away from a solution
539 * Status: NOT IMPLEMENTED
540 * Notes:
541 */
542struct pathState *initPathAwayFrom(struct pathState *ps, const struct solution *s) {
543 return NULL;
544}
545
546/* Path inspection */
547
548/*
549 * Current length of path
550 * Status: FINAL
551 */
552int getPathLength(const struct pathState *ps) {
553 return ps->n - ps->in_order;
554}
555
556
Definition: flowshop.h:78