this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2/*
3 * Main authors:
4 * Christian Schulte <schulte@gecode.org>
5 *
6 * Copyright:
7 * Christian Schulte, 2019
8 *
9 * This file is part of Gecode, the generic constraint
10 * development environment:
11 * http://www.gecode.org
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining
14 * a copy of this software and associated documentation files (the
15 * "Software"), to deal in the Software without restriction, including
16 * without limitation the rights to use, copy, modify, merge, publish,
17 * distribute, sublicense, and/or sell copies of the Software, and to
18 * permit persons to whom the Software is furnished to do so, subject to
19 * the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be
22 * included in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 *
32 */
33
34#include <gecode/driver.hh>
35
36#include <gecode/int.hh>
37#include <gecode/minimodel.hh>
38
39#include <algorithm>
40#include <iostream>
41#include <iomanip>
42#include <fstream>
43
44using namespace Gecode;
45
46/*
47 * The paper uses ideas from: D. Grimes and E. Hebrard, Solving Variants
48 * of the Job Shop Scheduling Problem through Conflict-Directed Search,
49 * INFORMS Journal of Computing, Volume 27, Issue 2, 2015.
50 *
51 * Warning: this solution is a sketch and not competitive as not all
52 * techniques from the paper have been implemented.
53 *
54 */
55
56/// Default configuration settings
57namespace JobShopConfig {
58 /// Whether to print verbose information
59 static const bool verbose = false;
60
61 /// How many probes to perform
62 static const unsigned int probes = 50U;
63 /// How many failures maximal per probe
64 static const unsigned int fail_probe = 10000U;
65
66 /// How much time to spend on probing
67 static const unsigned int time_probe = 30U * 1000U;
68 /// How much time to spend on adjusting
69 static const unsigned int time_adjust = 30U * 1000U;
70 /// How much time to spend on solving
71 static const unsigned int time_solve = 60U * 1000U;
72
73 /// Restart scale factor
74 static const double restart_scale = 5000.0;
75 /// Restart base
76 static const double restart_base = 1.3;
77}
78
79// Instance data
80namespace {
81
82 // Instances
83 extern const int* js[];
84 // Instance names
85 extern const char* name[];
86
87 /// A wrapper class for instance data
88 class Spec {
89 protected:
90 /// Raw instance data
91 const int* data;
92 /// Lower and upper bound
93 int l, u;
94 /// Name
95 const char* n;
96 public:
97 /// Whether a valid specification has been found
98 bool valid(void) const {
99 return data != nullptr;
100 }
101 /// Return number of jobs
102 int jobs(void) const {
103 return data[0];
104 }
105 /// Return number of machines
106 int machines(void) const {
107 return data[1];
108 }
109 /// Return machine of step \a j in job \a i
110 int machine(int i, int j) const {
111 return data[2 + i*machines()*2 + j*2];
112 }
113 /// Return duration of step \a j in job \a i
114 int duration(int i, int j) const {
115 return data[2 + i*machines()*2 + j*2 + 1];
116 }
117 protected:
118 /// Find instance by name \a s
119 static const int* find(const char* s) {
120 for (int i=0; ::name[i] != nullptr; i++)
121 if (!strcmp(s,::name[i]))
122 return js[i];
123 return nullptr;
124 }
125 /// Compute lower bound
126 int clower(void) const {
127 int l = 0;
128 Region r;
129 int* mach = r.alloc<int>(machines());
130 for (int j=0; j<machines(); j++)
131 mach[j]=0;
132 for (int i=0; i<jobs(); i++) {
133 int job = 0;
134 for (int j=0; j<machines(); j++) {
135 mach[machine(i,j)] += duration(i,j);
136 job += duration(i,j);
137 }
138 l = std::max(l,job);
139 }
140 for (int j=0; j<machines(); j++)
141 l = std::max(l,mach[j]);
142 return l;
143 }
144 /// Compute naive upper bound
145 int cupper(void) const {
146 int u = 0;
147 for (int i=0; i<jobs(); i++)
148 for (int j=0; j<machines(); j++)
149 u += duration(i,j);
150 return u;
151 }
152 public:
153 /// Initialize
154 Spec(const char* s) : data(find(s)), l(0), u(0), n(s) {
155 if (valid()) {
156 l = clower(); u = cupper();
157 }
158 }
159 /// Return lower bound
160 int lower(void) const {
161 return l;
162 }
163 /// Return upper bound
164 int upper(void) const {
165 return u;
166 }
167 /// Return name
168 const char* name(void) const {
169 return n;
170 }
171 };
172
173}
174
175/**
176 * \brief %Options for %JobShop problems
177 *
178 */
179class JobShopOptions : public InstanceOptions {
180private:
181 /// Whether to print schedule
182 Driver::BoolOption _verbose;
183 /// How many probes
184 Driver::UnsignedIntOption _probes;
185 /// How many failures per probe
186 Driver::UnsignedIntOption _fail_probe;
187 /// Time for probing
188 Driver::UnsignedIntOption _time_probe;
189 /// Time for adjusting
190 Driver::UnsignedIntOption _time_adjust;
191 /// Time for solving
192 Driver::UnsignedIntOption _time_solve;
193 /// Tie-breaking factor
194 Driver::DoubleOption _tbf;
195public:
196 /// Initialize options for example with name \a s
197 JobShopOptions(const char* s)
198 : InstanceOptions(s),
199 _verbose("verbose","whether to print schedule",
200 JobShopConfig::verbose),
201 _probes("probes","how many probes to perform",
202 JobShopConfig::probes),
203 _fail_probe("fail-probe","failure limit per probe",
204 JobShopConfig::fail_probe),
205 _time_probe("time-probe","time-out for probing (in milliseconds)",
206 JobShopConfig::time_probe),
207 _time_adjust("time-adjust","time-out for adjusting (in milliseconds)",
208 JobShopConfig::time_adjust),
209 _time_solve("time-solve","time-out for solving (in milliseconds)",
210 JobShopConfig::time_solve),
211 _tbf("tbf", "tie-breaking factor", 0.0) {
212 add(_verbose);
213 add(_probes);
214 add(_fail_probe);
215 add(_time_probe);
216 add(_time_adjust);
217 add(_time_solve);
218 add(_tbf);
219 }
220 /// Return whether to print schedule
221 bool verbose(void) const {
222 return _verbose.value();
223 }
224 /// Return number of probes
225 unsigned int probes(void) const {
226 return _probes.value();
227 }
228 /// Return number of failures per probe
229 unsigned int fail_probe(void) const {
230 return _fail_probe.value();
231 }
232 /// Return time-out for probe
233 unsigned int time_probe(void) const {
234 return _time_probe.value();
235 }
236 /// Return time-out for adjust
237 unsigned int time_adjust(void) const {
238 return _time_adjust.value();
239 }
240 /// Return time-out for solve
241 unsigned int time_solve(void) const {
242 return _time_solve.value();
243 }
244 /// Return tie-breaking factor
245 double tbf(void) const {
246 return _tbf.value();
247 }
248 /// Print help text for list of instances
249 virtual void help(void) {
250 InstanceOptions::help();
251 std::cerr << "\tAvailable instances:" << std::endl << "\t\t";
252 for (int i=1; ::name[i] != nullptr; i++) {
253 std::cerr << ::name[i] << ", ";
254 if (i % 4 == 0)
255 std::cerr << std::endl << "\t\t";
256 }
257 std::cerr << std::endl;
258 }
259};
260
261
262/**
263 * \brief %Example: Job Shop Scheduling
264 *
265 * \ingroup Example
266 *
267 */
268class JobShopBase : public IntMinimizeScript {
269protected:
270 /// Options
271 const JobShopOptions& opt;
272 /// Specification
273 const Spec spec;
274 /// Start times for each step in a job
275 IntVarArray start;
276 /// Makespan
277 IntVar makespan;
278public:
279 /// Actual model
280 JobShopBase(const JobShopOptions& o)
281 : IntMinimizeScript(o), opt(o), spec(opt.instance()),
282 start(*this, spec.machines() * spec.jobs(), 0, spec.upper()),
283 makespan(*this, spec.lower(), spec.upper()) {
284 // Number of jobs and machines/steps
285 int n = spec.jobs(), m = spec.machines();
286 // Endtimes for each job
287 IntVarArgs end(*this, n, 0, spec.upper());
288
289 // Precedence constraints and makespan
290 for (int i=0; i<n; i++) {
291 for (int j=1; j<m; j++)
292 rel(*this, start[i*m+j-1] + spec.duration(i,j) <= start[i*m+j]);
293 rel(*this, start[i*m+m-1] + spec.duration(i,m-1) <= makespan);
294 }
295 }
296 /// Do not overload machines
297 void nooverload(void) {
298 // Number of jobs and machines/steps
299 int n = spec.jobs(), m = spec.machines();
300
301 IntVarArgs jobs(m*n);
302 IntArgs dur(m*n);
303
304 for (int i=0; i<n; i++)
305 for (int j=0; j<m; j++) {
306 jobs[spec.machine(i,j)*n+i] = start[i*m+j];
307 dur[spec.machine(i,j)*n+i] = spec.duration(i,j);
308 }
309
310 for (int j=0; j<m; j++) {
311 IntVarArgs jpm(n);
312 IntArgs dpm(n);
313 for (int i=0; i<n; i++) {
314 jpm[i] = jobs[j*n+i]; dpm[i] = dur[j*n+i];
315 }
316 unary(*this, jpm, dpm);
317 }
318 }
319 /// Return cost
320 virtual IntVar cost(void) const {
321 return makespan;
322 }
323 /// Constructor for cloning \a s
324 JobShopBase(JobShopBase& s)
325 : IntMinimizeScript(s), opt(s.opt), spec(s.spec) {
326 start.update(*this, s.start);
327 makespan.update(*this, s.makespan);
328 }
329 /// Print solution
330 virtual void
331 print(std::ostream& os) const {
332 os << "\t\t" << spec.name()
333 << " [makespan: " << makespan << "]" << std::endl;
334 if (opt.verbose()) {
335 // Number of jobs
336 int n = spec.jobs();
337 // Number of machines/steps
338 int m = spec.machines();
339 for (int i=0; i<n; i++) {
340 os << "\t\t\t[" << i << "]: ";
341 for (int j=0; j<m; j++)
342 os << start[i*m+j] << " ";
343 os << std::endl;
344 }
345 }
346 }
347};
348
349/// Common command line options
350class CommonOptions : public Search::Options {
351public:
352 /// Initialize
353 CommonOptions(const JobShopOptions& opt) {
354 clone = false;
355 threads = opt.threads();
356 c_d = opt.c_d();
357 a_d = opt.a_d();
358 nogoods_limit = opt.nogoods() ? opt.nogoods_limit() : 0U;
359 }
360};
361
362/// Model for probing
363class JobShopProbe : public JobShopBase {
364public:
365 /// Actual model
366 JobShopProbe(const JobShopOptions& o)
367 : JobShopBase(o) {
368 nooverload();
369 }
370 void branch(unsigned int p, Rnd r) {
371 switch (p) {
372 case 0U:
373 Gecode::branch(*this, start, INT_VAR_MIN_MIN(), INT_VAL_MIN());
374 break;
375 case 1U:
376 Gecode::branch(*this, start, INT_VAR_MAX_MIN(), INT_VAL_MIN());
377 break;
378 case 2U:
379 Gecode::branch(*this, start, INT_VAR_SIZE_MIN(), INT_VAL_MIN());
380 break;
381 case 3U:
382 Gecode::branch(*this, start, tiebreak(INT_VAR_MIN_MIN(),
383 INT_VAR_RND(r)), INT_VAL_MIN());
384 break;
385 case 4U:
386 Gecode::branch(*this, start, tiebreak(INT_VAR_MAX_MIN(),
387 INT_VAR_RND(r)), INT_VAL_MIN());
388 break;
389 default:
390 if (p & 1U)
391 Gecode::branch(*this, start, INT_VAR_RND(r), INT_VAL_MIN());
392 else
393 Gecode::branch(*this, start, INT_VAR_RND(r), INT_VAL_SPLIT_MIN());
394 break;
395 }
396 assign(*this, makespan, INT_ASSIGN_MIN());
397 }
398 /// Constructor for cloning \a s
399 JobShopProbe(JobShopProbe& s)
400 : JobShopBase(s) {}
401 /// Copy during cloning
402 virtual Space*
403 copy(void) {
404 return new JobShopProbe(*this);
405 }
406};
407
408/// Model for solving
409class JobShopSolve : public JobShopBase {
410protected:
411 /// Step order variables
412 BoolVarArray sorder;
413 /// Record which step is first in order
414 IntSharedArray fst;
415 /// Record which step is second in order
416 IntSharedArray snd;
417 /// AFC information
418 IntAFC iafc;
419 /// AFC-based cost
420 double afc(BoolVar x, int i) const {
421 return ((x.afc() + start[fst[i]].afc() + start[snd[i]].afc()) /
422 (start[fst[i]].size() + start[fst[i]].size()));
423 }
424 /// Trampoline function for AFC-based cost
425 static double afcmerit(const Space& home, BoolVar x, int i) {
426 return static_cast<const JobShopSolve&>(home).afc(x,i);
427 }
428 /// Action information
429 IntAction iaction; BoolAction baction;
430 /// Action-based cost
431 double action(int i) const {
432 return ((baction[i] + iaction[fst[i]] + iaction[snd[i]]) /
433 (start[fst[i]].size() + start[fst[i]].size()));
434 }
435 /// Trampoline function for Action-based cost
436 static double actionmerit(const Space& home, BoolVar, int i) {
437 return static_cast<const JobShopSolve&>(home).action(i);
438 }
439 /// CHB information
440 IntCHB ichb; BoolCHB bchb;
441 /// CHB-based cost
442 double chb(int i) const {
443 return ((bchb[i] + ichb[fst[i]] + ichb[snd[i]]) /
444 (start[fst[i]].size() + start[fst[i]].size()));
445 }
446 /// Trampoline function for CHB-based cost
447 static double chbmerit(const Space& home, BoolVar, int i) {
448 return static_cast<const JobShopSolve&>(home).chb(i);
449 }
450 /// Random number generator for probing and relaxation
451 Rnd rnd;
452public:
453 /// Branching to use
454 enum {
455 BRANCH_AFC, ///< Branch using AFC
456 BRANCH_ACTION, ///< Branch using action
457 BRANCH_CHB ///< Branch using CHB
458 };
459 /// Propagation to use
460 enum {
461 PROP_ORDER, ///< Only propagate order constraints
462 PROP_UNARY ///< Also post unary constraints
463 };
464 /// Actual model
465 JobShopSolve(const JobShopOptions& o)
466 : JobShopBase(o),
467 sorder(*this, spec.machines()*spec.jobs()*(spec.jobs()-1)/2, 0, 1),
468 rnd(o.seed()) {
469 if (opt.propagation() == PROP_UNARY)
470 nooverload();
471
472 // Number of jobs and machines/steps
473 int n = spec.jobs(), m = spec.machines();
474
475 fst.init(m*n*(n-1)/2);
476 snd.init(m*n*(n-1)/2);
477
478 IntArgs jobs(m*n), dur(m*n);
479
480 for (int i=0; i<n; i++)
481 for (int j=0; j<m; j++) {
482 jobs[spec.machine(i,j)*n+i] = i*m+j;
483 dur[spec.machine(i,j)*n+i] = spec.duration(i,j);
484 }
485
486 int l=0;
487 for (int j=0; j<m; j++) {
488 for (int i1=0; i1<n; i1++)
489 for (int i2=i1+1; i2<n; i2++) {
490 if (dur[j*n+i1] > dur[j*n+i2]) {
491 order(*this,
492 start[jobs[j*n+i1]], dur[j*n+i1],
493 start[jobs[j*n+i2]], dur[j*n+i2],
494 sorder[l]);
495 fst[l] = j*n+i1; snd[l] = j*n+i2;
496 } else {
497 order(*this,
498 start[jobs[j*n+i2]], dur[j*n+i2],
499 start[jobs[j*n+i1]], dur[j*n+i1],
500 sorder[l]);
501 fst[l] = j*n+i2; snd[l] = j*n+i1;
502 }
503 l++;
504 }
505 assert(l == (j+1)*n*(n-1)/2);
506 }
507
508 double tbf = opt.tbf();
509 switch (opt.branching()) {
510 case BRANCH_AFC:
511 iafc.init(*this,start,opt.decay());
512 if (tbf > 0.0) {
513 auto tbl =
514 [tbf] (const Space&, double w, double b) {
515 assert(b >= w);
516 return b - (b - w) * tbf;
517 };
518 branch(*this, sorder, tiebreak(BOOL_VAR_MERIT_MAX(&afcmerit,tbl),
519 BOOL_VAR_RND(rnd)),
520 BOOL_VAL_MIN());
521 } else {
522 branch(*this, sorder, BOOL_VAR_MERIT_MAX(&afcmerit),
523 BOOL_VAL_MIN());
524 }
525 break;
526 case BRANCH_ACTION:
527 iaction.init(*this,start,opt.decay());
528 baction.init(*this,sorder,opt.decay());
529 if (tbf > 0.0) {
530 auto tbl =
531 [tbf] (const Space&, double w, double b) {
532 assert(b >= w);
533 return b - (b - w) * tbf;
534 };
535 branch(*this, sorder, tiebreak(BOOL_VAR_MERIT_MAX(&actionmerit,tbl),
536 BOOL_VAR_RND(rnd)),
537 BOOL_VAL_MIN());
538 } else {
539 branch(*this, sorder, BOOL_VAR_MERIT_MAX(&actionmerit),
540 BOOL_VAL_MIN());
541 }
542 break;
543 case BRANCH_CHB:
544 ichb.init(*this,start);
545 bchb.init(*this,sorder);
546 if (tbf > 0.0) {
547 auto tbl =
548 [tbf] (const Space&, double w, double b) {
549 assert(b >= w);
550 return b - (b - w) * tbf;
551 };
552 branch(*this, sorder, tiebreak(BOOL_VAR_MERIT_MAX(&chbmerit,tbl),
553 BOOL_VAR_RND(rnd)),
554 BOOL_VAL_MIN());
555 } else {
556 branch(*this, sorder, BOOL_VAR_MERIT_MAX(&chbmerit),
557 BOOL_VAL_MIN());
558 }
559 break;
560 }
561 assign(*this, start, INT_VAR_MIN_MIN(), INT_ASSIGN_MIN());
562 assign(*this, makespan, INT_ASSIGN_MIN());
563 }
564 /// Constructor for cloning \a s
565 JobShopSolve(JobShopSolve& s)
566 : JobShopBase(s), sorder(s.sorder), fst(s.fst), snd(s.snd),
567 iafc(s.iafc), iaction(s.iaction), baction(s.baction),
568 ichb(s.ichb), bchb(s.bchb), rnd(s.rnd) {}
569 /// Copy during cloning
570 virtual Space*
571 copy(void) {
572 return new JobShopSolve(*this);
573 }
574};
575
576/// Stop object combining time and failuresa
577class FailTimeStop : public Search::Stop {
578protected:
579 Search::FailStop* fs; ///< Used fail stop object
580 Search::TimeStop* ts; ///< Used time stop object
581public:
582 /// Initialize stop object
583 FailTimeStop(unsigned int fail, unsigned int time)
584 : fs(new Search::FailStop(fail)),
585 ts(new Search::TimeStop(time)) {}
586 /// Test whether search must be stopped
587 virtual bool stop(const Search::Statistics& s, const Search::Options& o) {
588 return fs->stop(s,o) || ts->stop(s,o);
589 }
590 /// Whether the stop was due to failures
591 bool fail(const Search::Statistics& s, const Search::Options& o) const {
592 return fs->stop(s,o);
593 }
594 /// Whether the stop was due to time
595 bool time(const Search::Statistics& s, const Search::Options& o) const {
596 return ts->stop(s,o);
597 }
598 /// Destructor
599 ~FailTimeStop(void) {
600 delete fs; delete ts;
601 }
602};
603
604/// Print statistics
605void
606print(const Search::Statistics& stat, bool restart) {
607 using namespace std;
608 cout << "\t\t\tnodes: " << stat.node << endl
609 << "\t\t\tfailures: " << stat.fail << endl;
610 if (restart)
611 cout << "\t\t\trestarts: " << stat.restart << endl
612 << "\t\t\tno-goods: " << stat.nogood << endl;
613 cout << "\t\t\tpeak depth: " << stat.depth << endl;
614}
615
616/// Solver
617void
618solve(const JobShopOptions& opt) {
619 Rnd rnd(opt.seed());
620
621 /*
622 * Invariant:
623 * - There is a solution with makespan u,
624 * - There is no solution with makespan l
625 */
626
627 int l, u;
628
629 {
630 Support::Timer t; t.start();
631 Search::Statistics stat;
632 JobShopProbe* master = new JobShopProbe(opt);
633
634 if (master->status() != SS_SOLVED) {
635 delete master;
636 std::cerr << "Error: has no solution..." << std::endl;
637 return;
638 }
639
640 l = master->cost().min();
641 u = master->cost().max();
642
643 FailTimeStop fts(opt.fail_probe(),opt.time_probe());
644 CommonOptions so(opt);
645 so.stop = &fts;
646 bool stopped = false;
647
648 std::cout << "\tProbing..." << std::endl;
649
650 std::cout << "\t\tBounds: [" << l << "," << u << "]"
651 << std::endl;
652
653 for (unsigned int p=0; p<opt.probes(); p++) {
654 JobShopProbe* jsp = static_cast<JobShopProbe*>(master->clone());
655 jsp->branch(p,rnd);
656 DFS<JobShopProbe> dfs(jsp,so);
657 JobShopProbe* s = dfs.next();
658 Search::Statistics statj = dfs.statistics();
659
660 if (s != nullptr) {
661 if (u > s->cost().val()) {
662 u = s->cost().val();
663 s->print(std::cout);
664 }
665 delete s;
666 } else if (fts.time(statj,so)) {
667 stopped = true;
668 break;
669 }
670 stat += statj;
671 }
672 delete master;
673
674 print(stat,false);
675 std::cout << "\t\t\truntime: ";
676 Driver::stop(t,std::cout);
677 std::cout << std::endl;
678
679 if (stopped) {
680 std::cout << "\t\t\t\tstopped due to time-out..." << std::endl;
681 }
682 }
683
684 std::cout << std::endl << "\tAdjusting..." << std::endl;
685
686 // Dichotomic search
687 {
688 JobShopSolve* master = new JobShopSolve(opt);
689
690
691 if (master->status() == SS_FAILED) {
692 delete master;
693 std::cerr << "Error: has no solution..." << std::endl;
694 return;
695 }
696
697 {
698 Support::Timer t; t.start();
699 Search::Statistics stat;
700 CommonOptions so(opt);
701 if (opt.time_adjust() > 0U)
702 so.stop = Search::Stop::time(opt.time_adjust());
703 bool stopped = false;
704 while (l < u) {
705 std::cout << "\t\tBounds: [" << l << "," << u << "]"
706 << std::endl;
707 JobShopSolve* jss = static_cast<JobShopSolve*>(master->clone());
708 int m = (l + u) / 2;
709 rel(*jss, jss->cost() >= l);
710 rel(*jss, jss->cost() <= m);
711 so.cutoff = Search::Cutoff::geometric(JobShopConfig::restart_scale,
712 JobShopConfig::restart_base);
713 RBS<JobShopSolve,DFS> rbs(jss,so);
714 JobShopSolve* s = rbs.next();
715
716 stat += rbs.statistics();
717
718 if (s != nullptr) {
719 s->print(std::cout);
720 u = s->cost().val();
721 delete s;
722 } else if (rbs.stopped()) {
723 stopped = true;
724 break;
725 } else {
726 l = m+1;
727 }
728 }
729
730 print(stat,true);
731 std::cout << "\t\t\truntime: ";
732 Driver::stop(t,std::cout);
733 std::cout << std::endl;
734
735 if (stopped) {
736 std::cout << "\t\t\t\tstopped due to time-out..." << std::endl;
737 }
738
739 }
740
741 if (l == u) {
742 delete master;
743 std::cout << std::endl
744 << "\tFound best solution and proved optimality."
745 << std::endl;
746 return;
747 }
748
749
750 {
751 Support::Timer t; t.start();
752 std::cout << std::endl << "\tSolving..." << std::endl;
753
754 rel(*master, master->cost() >= l);
755 rel(*master, master->cost() < u);
756
757 CommonOptions so(opt);
758 if (opt.time_solve() > 0U)
759 so.stop = Search::Stop::time(opt.time_solve());
760 so.cutoff = Search::Cutoff::geometric(JobShopConfig::restart_scale,
761 JobShopConfig::restart_base);
762 RBS<JobShopSolve,BAB> rbs(master,so);
763 while (JobShopSolve* s = rbs.next()) {
764 s->print(std::cout);
765 u = s->cost().val();
766 delete s;
767 }
768
769 print(rbs.statistics(),true);
770 std::cout << "\t\t\truntime: ";
771 Driver::stop(t,std::cout);
772 std::cout << std::endl;
773
774 if (rbs.stopped()) {
775 std::cout << "\t\t\t\tstopped due to time-out..." << std::endl;
776 std::cout << std::endl
777 << "\tSolution at most ";
778 double a = (static_cast<double>(u-l+1) / u) * 100.0;
779 std::cout << std::setprecision(2) << a
780 << "% away from optimum."
781 << std::endl;
782
783 } else {
784 std::cout << std::endl
785 << "\tFound best solution and proved optimality."
786 << std::endl;
787 }
788 }
789
790 }
791}
792
793
794
795
796
797/** \brief Main-function
798 * \relates JobShop
799 */
800int
801main(int argc, char* argv[]) {
802 JobShopOptions opt("JobShop");
803
804 opt.branching(JobShopSolve::BRANCH_AFC);
805 opt.branching(JobShopSolve::BRANCH_AFC, "afc");
806 opt.branching(JobShopSolve::BRANCH_ACTION, "action");
807 opt.branching(JobShopSolve::BRANCH_CHB, "chb");
808
809 opt.propagation(JobShopSolve::PROP_UNARY);
810 opt.propagation(JobShopSolve::PROP_ORDER,"order");
811 opt.propagation(JobShopSolve::PROP_UNARY,"unary");
812
813 opt.instance("ft06");
814
815 opt.restart_base(JobShopConfig::restart_base);
816 opt.restart_scale(JobShopConfig::restart_scale);
817 opt.nogoods(true);
818
819 opt.parse(argc,argv);
820 if (!Spec(opt.instance()).valid()) {
821 std::cerr << "Error: unkown instance" << std::endl;
822 return 1;
823 }
824 solve(opt);
825 return 0;
826}
827
828#include "examples/job-shop-instances.hpp"
829
830// STATISTICS: example-any
831