832 lines
23 KiB
C++
Executable File
832 lines
23 KiB
C++
Executable File
/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
/*
|
|
* Main authors:
|
|
* Christian Schulte <schulte@gecode.org>
|
|
*
|
|
* Copyright:
|
|
* Christian Schulte, 2019
|
|
*
|
|
* This file is part of Gecode, the generic constraint
|
|
* development environment:
|
|
* http://www.gecode.org
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include <gecode/driver.hh>
|
|
|
|
#include <gecode/int.hh>
|
|
#include <gecode/minimodel.hh>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <fstream>
|
|
|
|
using namespace Gecode;
|
|
|
|
/*
|
|
* The paper uses ideas from: D. Grimes and E. Hebrard, Solving Variants
|
|
* of the Job Shop Scheduling Problem through Conflict-Directed Search,
|
|
* INFORMS Journal of Computing, Volume 27, Issue 2, 2015.
|
|
*
|
|
* Warning: this solution is a sketch and not competitive as not all
|
|
* techniques from the paper have been implemented.
|
|
*
|
|
*/
|
|
|
|
/// Default configuration settings
|
|
namespace JobShopConfig {
|
|
/// Whether to print verbose information
|
|
static const bool verbose = false;
|
|
|
|
/// How many probes to perform
|
|
static const unsigned int probes = 50U;
|
|
/// How many failures maximal per probe
|
|
static const unsigned int fail_probe = 10000U;
|
|
|
|
/// How much time to spend on probing
|
|
static const unsigned int time_probe = 30U * 1000U;
|
|
/// How much time to spend on adjusting
|
|
static const unsigned int time_adjust = 30U * 1000U;
|
|
/// How much time to spend on solving
|
|
static const unsigned int time_solve = 60U * 1000U;
|
|
|
|
/// Restart scale factor
|
|
static const double restart_scale = 5000.0;
|
|
/// Restart base
|
|
static const double restart_base = 1.3;
|
|
}
|
|
|
|
// Instance data
|
|
namespace {
|
|
|
|
// Instances
|
|
extern const int* js[];
|
|
// Instance names
|
|
extern const char* name[];
|
|
|
|
/// A wrapper class for instance data
|
|
class Spec {
|
|
protected:
|
|
/// Raw instance data
|
|
const int* data;
|
|
/// Lower and upper bound
|
|
int l, u;
|
|
/// Name
|
|
const char* n;
|
|
public:
|
|
/// Whether a valid specification has been found
|
|
bool valid(void) const {
|
|
return data != nullptr;
|
|
}
|
|
/// Return number of jobs
|
|
int jobs(void) const {
|
|
return data[0];
|
|
}
|
|
/// Return number of machines
|
|
int machines(void) const {
|
|
return data[1];
|
|
}
|
|
/// Return machine of step \a j in job \a i
|
|
int machine(int i, int j) const {
|
|
return data[2 + i*machines()*2 + j*2];
|
|
}
|
|
/// Return duration of step \a j in job \a i
|
|
int duration(int i, int j) const {
|
|
return data[2 + i*machines()*2 + j*2 + 1];
|
|
}
|
|
protected:
|
|
/// Find instance by name \a s
|
|
static const int* find(const char* s) {
|
|
for (int i=0; ::name[i] != nullptr; i++)
|
|
if (!strcmp(s,::name[i]))
|
|
return js[i];
|
|
return nullptr;
|
|
}
|
|
/// Compute lower bound
|
|
int clower(void) const {
|
|
int l = 0;
|
|
Region r;
|
|
int* mach = r.alloc<int>(machines());
|
|
for (int j=0; j<machines(); j++)
|
|
mach[j]=0;
|
|
for (int i=0; i<jobs(); i++) {
|
|
int job = 0;
|
|
for (int j=0; j<machines(); j++) {
|
|
mach[machine(i,j)] += duration(i,j);
|
|
job += duration(i,j);
|
|
}
|
|
l = std::max(l,job);
|
|
}
|
|
for (int j=0; j<machines(); j++)
|
|
l = std::max(l,mach[j]);
|
|
return l;
|
|
}
|
|
/// Compute naive upper bound
|
|
int cupper(void) const {
|
|
int u = 0;
|
|
for (int i=0; i<jobs(); i++)
|
|
for (int j=0; j<machines(); j++)
|
|
u += duration(i,j);
|
|
return u;
|
|
}
|
|
public:
|
|
/// Initialize
|
|
Spec(const char* s) : data(find(s)), l(0), u(0), n(s) {
|
|
if (valid()) {
|
|
l = clower(); u = cupper();
|
|
}
|
|
}
|
|
/// Return lower bound
|
|
int lower(void) const {
|
|
return l;
|
|
}
|
|
/// Return upper bound
|
|
int upper(void) const {
|
|
return u;
|
|
}
|
|
/// Return name
|
|
const char* name(void) const {
|
|
return n;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
* \brief %Options for %JobShop problems
|
|
*
|
|
*/
|
|
class JobShopOptions : public InstanceOptions {
|
|
private:
|
|
/// Whether to print schedule
|
|
Driver::BoolOption _verbose;
|
|
/// How many probes
|
|
Driver::UnsignedIntOption _probes;
|
|
/// How many failures per probe
|
|
Driver::UnsignedIntOption _fail_probe;
|
|
/// Time for probing
|
|
Driver::UnsignedIntOption _time_probe;
|
|
/// Time for adjusting
|
|
Driver::UnsignedIntOption _time_adjust;
|
|
/// Time for solving
|
|
Driver::UnsignedIntOption _time_solve;
|
|
/// Tie-breaking factor
|
|
Driver::DoubleOption _tbf;
|
|
public:
|
|
/// Initialize options for example with name \a s
|
|
JobShopOptions(const char* s)
|
|
: InstanceOptions(s),
|
|
_verbose("verbose","whether to print schedule",
|
|
JobShopConfig::verbose),
|
|
_probes("probes","how many probes to perform",
|
|
JobShopConfig::probes),
|
|
_fail_probe("fail-probe","failure limit per probe",
|
|
JobShopConfig::fail_probe),
|
|
_time_probe("time-probe","time-out for probing (in milliseconds)",
|
|
JobShopConfig::time_probe),
|
|
_time_adjust("time-adjust","time-out for adjusting (in milliseconds)",
|
|
JobShopConfig::time_adjust),
|
|
_time_solve("time-solve","time-out for solving (in milliseconds)",
|
|
JobShopConfig::time_solve),
|
|
_tbf("tbf", "tie-breaking factor", 0.0) {
|
|
add(_verbose);
|
|
add(_probes);
|
|
add(_fail_probe);
|
|
add(_time_probe);
|
|
add(_time_adjust);
|
|
add(_time_solve);
|
|
add(_tbf);
|
|
}
|
|
/// Return whether to print schedule
|
|
bool verbose(void) const {
|
|
return _verbose.value();
|
|
}
|
|
/// Return number of probes
|
|
unsigned int probes(void) const {
|
|
return _probes.value();
|
|
}
|
|
/// Return number of failures per probe
|
|
unsigned int fail_probe(void) const {
|
|
return _fail_probe.value();
|
|
}
|
|
/// Return time-out for probe
|
|
unsigned int time_probe(void) const {
|
|
return _time_probe.value();
|
|
}
|
|
/// Return time-out for adjust
|
|
unsigned int time_adjust(void) const {
|
|
return _time_adjust.value();
|
|
}
|
|
/// Return time-out for solve
|
|
unsigned int time_solve(void) const {
|
|
return _time_solve.value();
|
|
}
|
|
/// Return tie-breaking factor
|
|
double tbf(void) const {
|
|
return _tbf.value();
|
|
}
|
|
/// Print help text for list of instances
|
|
virtual void help(void) {
|
|
InstanceOptions::help();
|
|
std::cerr << "\tAvailable instances:" << std::endl << "\t\t";
|
|
for (int i=1; ::name[i] != nullptr; i++) {
|
|
std::cerr << ::name[i] << ", ";
|
|
if (i % 4 == 0)
|
|
std::cerr << std::endl << "\t\t";
|
|
}
|
|
std::cerr << std::endl;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* \brief %Example: Job Shop Scheduling
|
|
*
|
|
* \ingroup Example
|
|
*
|
|
*/
|
|
class JobShopBase : public IntMinimizeScript {
|
|
protected:
|
|
/// Options
|
|
const JobShopOptions& opt;
|
|
/// Specification
|
|
const Spec spec;
|
|
/// Start times for each step in a job
|
|
IntVarArray start;
|
|
/// Makespan
|
|
IntVar makespan;
|
|
public:
|
|
/// Actual model
|
|
JobShopBase(const JobShopOptions& o)
|
|
: IntMinimizeScript(o), opt(o), spec(opt.instance()),
|
|
start(*this, spec.machines() * spec.jobs(), 0, spec.upper()),
|
|
makespan(*this, spec.lower(), spec.upper()) {
|
|
// Number of jobs and machines/steps
|
|
int n = spec.jobs(), m = spec.machines();
|
|
// Endtimes for each job
|
|
IntVarArgs end(*this, n, 0, spec.upper());
|
|
|
|
// Precedence constraints and makespan
|
|
for (int i=0; i<n; i++) {
|
|
for (int j=1; j<m; j++)
|
|
rel(*this, start[i*m+j-1] + spec.duration(i,j) <= start[i*m+j]);
|
|
rel(*this, start[i*m+m-1] + spec.duration(i,m-1) <= makespan);
|
|
}
|
|
}
|
|
/// Do not overload machines
|
|
void nooverload(void) {
|
|
// Number of jobs and machines/steps
|
|
int n = spec.jobs(), m = spec.machines();
|
|
|
|
IntVarArgs jobs(m*n);
|
|
IntArgs dur(m*n);
|
|
|
|
for (int i=0; i<n; i++)
|
|
for (int j=0; j<m; j++) {
|
|
jobs[spec.machine(i,j)*n+i] = start[i*m+j];
|
|
dur[spec.machine(i,j)*n+i] = spec.duration(i,j);
|
|
}
|
|
|
|
for (int j=0; j<m; j++) {
|
|
IntVarArgs jpm(n);
|
|
IntArgs dpm(n);
|
|
for (int i=0; i<n; i++) {
|
|
jpm[i] = jobs[j*n+i]; dpm[i] = dur[j*n+i];
|
|
}
|
|
unary(*this, jpm, dpm);
|
|
}
|
|
}
|
|
/// Return cost
|
|
virtual IntVar cost(void) const {
|
|
return makespan;
|
|
}
|
|
/// Constructor for cloning \a s
|
|
JobShopBase(JobShopBase& s)
|
|
: IntMinimizeScript(s), opt(s.opt), spec(s.spec) {
|
|
start.update(*this, s.start);
|
|
makespan.update(*this, s.makespan);
|
|
}
|
|
/// Print solution
|
|
virtual void
|
|
print(std::ostream& os) const {
|
|
os << "\t\t" << spec.name()
|
|
<< " [makespan: " << makespan << "]" << std::endl;
|
|
if (opt.verbose()) {
|
|
// Number of jobs
|
|
int n = spec.jobs();
|
|
// Number of machines/steps
|
|
int m = spec.machines();
|
|
for (int i=0; i<n; i++) {
|
|
os << "\t\t\t[" << i << "]: ";
|
|
for (int j=0; j<m; j++)
|
|
os << start[i*m+j] << " ";
|
|
os << std::endl;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/// Common command line options
|
|
class CommonOptions : public Search::Options {
|
|
public:
|
|
/// Initialize
|
|
CommonOptions(const JobShopOptions& opt) {
|
|
clone = false;
|
|
threads = opt.threads();
|
|
c_d = opt.c_d();
|
|
a_d = opt.a_d();
|
|
nogoods_limit = opt.nogoods() ? opt.nogoods_limit() : 0U;
|
|
}
|
|
};
|
|
|
|
/// Model for probing
|
|
class JobShopProbe : public JobShopBase {
|
|
public:
|
|
/// Actual model
|
|
JobShopProbe(const JobShopOptions& o)
|
|
: JobShopBase(o) {
|
|
nooverload();
|
|
}
|
|
void branch(unsigned int p, Rnd r) {
|
|
switch (p) {
|
|
case 0U:
|
|
Gecode::branch(*this, start, INT_VAR_MIN_MIN(), INT_VAL_MIN());
|
|
break;
|
|
case 1U:
|
|
Gecode::branch(*this, start, INT_VAR_MAX_MIN(), INT_VAL_MIN());
|
|
break;
|
|
case 2U:
|
|
Gecode::branch(*this, start, INT_VAR_SIZE_MIN(), INT_VAL_MIN());
|
|
break;
|
|
case 3U:
|
|
Gecode::branch(*this, start, tiebreak(INT_VAR_MIN_MIN(),
|
|
INT_VAR_RND(r)), INT_VAL_MIN());
|
|
break;
|
|
case 4U:
|
|
Gecode::branch(*this, start, tiebreak(INT_VAR_MAX_MIN(),
|
|
INT_VAR_RND(r)), INT_VAL_MIN());
|
|
break;
|
|
default:
|
|
if (p & 1U)
|
|
Gecode::branch(*this, start, INT_VAR_RND(r), INT_VAL_MIN());
|
|
else
|
|
Gecode::branch(*this, start, INT_VAR_RND(r), INT_VAL_SPLIT_MIN());
|
|
break;
|
|
}
|
|
assign(*this, makespan, INT_ASSIGN_MIN());
|
|
}
|
|
/// Constructor for cloning \a s
|
|
JobShopProbe(JobShopProbe& s)
|
|
: JobShopBase(s) {}
|
|
/// Copy during cloning
|
|
virtual Space*
|
|
copy(void) {
|
|
return new JobShopProbe(*this);
|
|
}
|
|
};
|
|
|
|
/// Model for solving
|
|
class JobShopSolve : public JobShopBase {
|
|
protected:
|
|
/// Step order variables
|
|
BoolVarArray sorder;
|
|
/// Record which step is first in order
|
|
IntSharedArray fst;
|
|
/// Record which step is second in order
|
|
IntSharedArray snd;
|
|
/// AFC information
|
|
IntAFC iafc;
|
|
/// AFC-based cost
|
|
double afc(BoolVar x, int i) const {
|
|
return ((x.afc() + start[fst[i]].afc() + start[snd[i]].afc()) /
|
|
(start[fst[i]].size() + start[fst[i]].size()));
|
|
}
|
|
/// Trampoline function for AFC-based cost
|
|
static double afcmerit(const Space& home, BoolVar x, int i) {
|
|
return static_cast<const JobShopSolve&>(home).afc(x,i);
|
|
}
|
|
/// Action information
|
|
IntAction iaction; BoolAction baction;
|
|
/// Action-based cost
|
|
double action(int i) const {
|
|
return ((baction[i] + iaction[fst[i]] + iaction[snd[i]]) /
|
|
(start[fst[i]].size() + start[fst[i]].size()));
|
|
}
|
|
/// Trampoline function for Action-based cost
|
|
static double actionmerit(const Space& home, BoolVar, int i) {
|
|
return static_cast<const JobShopSolve&>(home).action(i);
|
|
}
|
|
/// CHB information
|
|
IntCHB ichb; BoolCHB bchb;
|
|
/// CHB-based cost
|
|
double chb(int i) const {
|
|
return ((bchb[i] + ichb[fst[i]] + ichb[snd[i]]) /
|
|
(start[fst[i]].size() + start[fst[i]].size()));
|
|
}
|
|
/// Trampoline function for CHB-based cost
|
|
static double chbmerit(const Space& home, BoolVar, int i) {
|
|
return static_cast<const JobShopSolve&>(home).chb(i);
|
|
}
|
|
/// Random number generator for probing and relaxation
|
|
Rnd rnd;
|
|
public:
|
|
/// Branching to use
|
|
enum {
|
|
BRANCH_AFC, ///< Branch using AFC
|
|
BRANCH_ACTION, ///< Branch using action
|
|
BRANCH_CHB ///< Branch using CHB
|
|
};
|
|
/// Propagation to use
|
|
enum {
|
|
PROP_ORDER, ///< Only propagate order constraints
|
|
PROP_UNARY ///< Also post unary constraints
|
|
};
|
|
/// Actual model
|
|
JobShopSolve(const JobShopOptions& o)
|
|
: JobShopBase(o),
|
|
sorder(*this, spec.machines()*spec.jobs()*(spec.jobs()-1)/2, 0, 1),
|
|
rnd(o.seed()) {
|
|
if (opt.propagation() == PROP_UNARY)
|
|
nooverload();
|
|
|
|
// Number of jobs and machines/steps
|
|
int n = spec.jobs(), m = spec.machines();
|
|
|
|
fst.init(m*n*(n-1)/2);
|
|
snd.init(m*n*(n-1)/2);
|
|
|
|
IntArgs jobs(m*n), dur(m*n);
|
|
|
|
for (int i=0; i<n; i++)
|
|
for (int j=0; j<m; j++) {
|
|
jobs[spec.machine(i,j)*n+i] = i*m+j;
|
|
dur[spec.machine(i,j)*n+i] = spec.duration(i,j);
|
|
}
|
|
|
|
int l=0;
|
|
for (int j=0; j<m; j++) {
|
|
for (int i1=0; i1<n; i1++)
|
|
for (int i2=i1+1; i2<n; i2++) {
|
|
if (dur[j*n+i1] > dur[j*n+i2]) {
|
|
order(*this,
|
|
start[jobs[j*n+i1]], dur[j*n+i1],
|
|
start[jobs[j*n+i2]], dur[j*n+i2],
|
|
sorder[l]);
|
|
fst[l] = j*n+i1; snd[l] = j*n+i2;
|
|
} else {
|
|
order(*this,
|
|
start[jobs[j*n+i2]], dur[j*n+i2],
|
|
start[jobs[j*n+i1]], dur[j*n+i1],
|
|
sorder[l]);
|
|
fst[l] = j*n+i2; snd[l] = j*n+i1;
|
|
}
|
|
l++;
|
|
}
|
|
assert(l == (j+1)*n*(n-1)/2);
|
|
}
|
|
|
|
double tbf = opt.tbf();
|
|
switch (opt.branching()) {
|
|
case BRANCH_AFC:
|
|
iafc.init(*this,start,opt.decay());
|
|
if (tbf > 0.0) {
|
|
auto tbl =
|
|
[tbf] (const Space&, double w, double b) {
|
|
assert(b >= w);
|
|
return b - (b - w) * tbf;
|
|
};
|
|
branch(*this, sorder, tiebreak(BOOL_VAR_MERIT_MAX(&afcmerit,tbl),
|
|
BOOL_VAR_RND(rnd)),
|
|
BOOL_VAL_MIN());
|
|
} else {
|
|
branch(*this, sorder, BOOL_VAR_MERIT_MAX(&afcmerit),
|
|
BOOL_VAL_MIN());
|
|
}
|
|
break;
|
|
case BRANCH_ACTION:
|
|
iaction.init(*this,start,opt.decay());
|
|
baction.init(*this,sorder,opt.decay());
|
|
if (tbf > 0.0) {
|
|
auto tbl =
|
|
[tbf] (const Space&, double w, double b) {
|
|
assert(b >= w);
|
|
return b - (b - w) * tbf;
|
|
};
|
|
branch(*this, sorder, tiebreak(BOOL_VAR_MERIT_MAX(&actionmerit,tbl),
|
|
BOOL_VAR_RND(rnd)),
|
|
BOOL_VAL_MIN());
|
|
} else {
|
|
branch(*this, sorder, BOOL_VAR_MERIT_MAX(&actionmerit),
|
|
BOOL_VAL_MIN());
|
|
}
|
|
break;
|
|
case BRANCH_CHB:
|
|
ichb.init(*this,start);
|
|
bchb.init(*this,sorder);
|
|
if (tbf > 0.0) {
|
|
auto tbl =
|
|
[tbf] (const Space&, double w, double b) {
|
|
assert(b >= w);
|
|
return b - (b - w) * tbf;
|
|
};
|
|
branch(*this, sorder, tiebreak(BOOL_VAR_MERIT_MAX(&chbmerit,tbl),
|
|
BOOL_VAR_RND(rnd)),
|
|
BOOL_VAL_MIN());
|
|
} else {
|
|
branch(*this, sorder, BOOL_VAR_MERIT_MAX(&chbmerit),
|
|
BOOL_VAL_MIN());
|
|
}
|
|
break;
|
|
}
|
|
assign(*this, start, INT_VAR_MIN_MIN(), INT_ASSIGN_MIN());
|
|
assign(*this, makespan, INT_ASSIGN_MIN());
|
|
}
|
|
/// Constructor for cloning \a s
|
|
JobShopSolve(JobShopSolve& s)
|
|
: JobShopBase(s), sorder(s.sorder), fst(s.fst), snd(s.snd),
|
|
iafc(s.iafc), iaction(s.iaction), baction(s.baction),
|
|
ichb(s.ichb), bchb(s.bchb), rnd(s.rnd) {}
|
|
/// Copy during cloning
|
|
virtual Space*
|
|
copy(void) {
|
|
return new JobShopSolve(*this);
|
|
}
|
|
};
|
|
|
|
/// Stop object combining time and failuresa
|
|
class FailTimeStop : public Search::Stop {
|
|
protected:
|
|
Search::FailStop* fs; ///< Used fail stop object
|
|
Search::TimeStop* ts; ///< Used time stop object
|
|
public:
|
|
/// Initialize stop object
|
|
FailTimeStop(unsigned int fail, unsigned int time)
|
|
: fs(new Search::FailStop(fail)),
|
|
ts(new Search::TimeStop(time)) {}
|
|
/// Test whether search must be stopped
|
|
virtual bool stop(const Search::Statistics& s, const Search::Options& o) {
|
|
return fs->stop(s,o) || ts->stop(s,o);
|
|
}
|
|
/// Whether the stop was due to failures
|
|
bool fail(const Search::Statistics& s, const Search::Options& o) const {
|
|
return fs->stop(s,o);
|
|
}
|
|
/// Whether the stop was due to time
|
|
bool time(const Search::Statistics& s, const Search::Options& o) const {
|
|
return ts->stop(s,o);
|
|
}
|
|
/// Destructor
|
|
~FailTimeStop(void) {
|
|
delete fs; delete ts;
|
|
}
|
|
};
|
|
|
|
/// Print statistics
|
|
void
|
|
print(const Search::Statistics& stat, bool restart) {
|
|
using namespace std;
|
|
cout << "\t\t\tnodes: " << stat.node << endl
|
|
<< "\t\t\tfailures: " << stat.fail << endl;
|
|
if (restart)
|
|
cout << "\t\t\trestarts: " << stat.restart << endl
|
|
<< "\t\t\tno-goods: " << stat.nogood << endl;
|
|
cout << "\t\t\tpeak depth: " << stat.depth << endl;
|
|
}
|
|
|
|
/// Solver
|
|
void
|
|
solve(const JobShopOptions& opt) {
|
|
Rnd rnd(opt.seed());
|
|
|
|
/*
|
|
* Invariant:
|
|
* - There is a solution with makespan u,
|
|
* - There is no solution with makespan l
|
|
*/
|
|
|
|
int l, u;
|
|
|
|
{
|
|
Support::Timer t; t.start();
|
|
Search::Statistics stat;
|
|
JobShopProbe* master = new JobShopProbe(opt);
|
|
|
|
if (master->status() != SS_SOLVED) {
|
|
delete master;
|
|
std::cerr << "Error: has no solution..." << std::endl;
|
|
return;
|
|
}
|
|
|
|
l = master->cost().min();
|
|
u = master->cost().max();
|
|
|
|
FailTimeStop fts(opt.fail_probe(),opt.time_probe());
|
|
CommonOptions so(opt);
|
|
so.stop = &fts;
|
|
bool stopped = false;
|
|
|
|
std::cout << "\tProbing..." << std::endl;
|
|
|
|
std::cout << "\t\tBounds: [" << l << "," << u << "]"
|
|
<< std::endl;
|
|
|
|
for (unsigned int p=0; p<opt.probes(); p++) {
|
|
JobShopProbe* jsp = static_cast<JobShopProbe*>(master->clone());
|
|
jsp->branch(p,rnd);
|
|
DFS<JobShopProbe> dfs(jsp,so);
|
|
JobShopProbe* s = dfs.next();
|
|
Search::Statistics statj = dfs.statistics();
|
|
|
|
if (s != nullptr) {
|
|
if (u > s->cost().val()) {
|
|
u = s->cost().val();
|
|
s->print(std::cout);
|
|
}
|
|
delete s;
|
|
} else if (fts.time(statj,so)) {
|
|
stopped = true;
|
|
break;
|
|
}
|
|
stat += statj;
|
|
}
|
|
delete master;
|
|
|
|
print(stat,false);
|
|
std::cout << "\t\t\truntime: ";
|
|
Driver::stop(t,std::cout);
|
|
std::cout << std::endl;
|
|
|
|
if (stopped) {
|
|
std::cout << "\t\t\t\tstopped due to time-out..." << std::endl;
|
|
}
|
|
}
|
|
|
|
std::cout << std::endl << "\tAdjusting..." << std::endl;
|
|
|
|
// Dichotomic search
|
|
{
|
|
JobShopSolve* master = new JobShopSolve(opt);
|
|
|
|
|
|
if (master->status() == SS_FAILED) {
|
|
delete master;
|
|
std::cerr << "Error: has no solution..." << std::endl;
|
|
return;
|
|
}
|
|
|
|
{
|
|
Support::Timer t; t.start();
|
|
Search::Statistics stat;
|
|
CommonOptions so(opt);
|
|
if (opt.time_adjust() > 0U)
|
|
so.stop = Search::Stop::time(opt.time_adjust());
|
|
bool stopped = false;
|
|
while (l < u) {
|
|
std::cout << "\t\tBounds: [" << l << "," << u << "]"
|
|
<< std::endl;
|
|
JobShopSolve* jss = static_cast<JobShopSolve*>(master->clone());
|
|
int m = (l + u) / 2;
|
|
rel(*jss, jss->cost() >= l);
|
|
rel(*jss, jss->cost() <= m);
|
|
so.cutoff = Search::Cutoff::geometric(JobShopConfig::restart_scale,
|
|
JobShopConfig::restart_base);
|
|
RBS<JobShopSolve,DFS> rbs(jss,so);
|
|
JobShopSolve* s = rbs.next();
|
|
|
|
stat += rbs.statistics();
|
|
|
|
if (s != nullptr) {
|
|
s->print(std::cout);
|
|
u = s->cost().val();
|
|
delete s;
|
|
} else if (rbs.stopped()) {
|
|
stopped = true;
|
|
break;
|
|
} else {
|
|
l = m+1;
|
|
}
|
|
}
|
|
|
|
print(stat,true);
|
|
std::cout << "\t\t\truntime: ";
|
|
Driver::stop(t,std::cout);
|
|
std::cout << std::endl;
|
|
|
|
if (stopped) {
|
|
std::cout << "\t\t\t\tstopped due to time-out..." << std::endl;
|
|
}
|
|
|
|
}
|
|
|
|
if (l == u) {
|
|
delete master;
|
|
std::cout << std::endl
|
|
<< "\tFound best solution and proved optimality."
|
|
<< std::endl;
|
|
return;
|
|
}
|
|
|
|
|
|
{
|
|
Support::Timer t; t.start();
|
|
std::cout << std::endl << "\tSolving..." << std::endl;
|
|
|
|
rel(*master, master->cost() >= l);
|
|
rel(*master, master->cost() < u);
|
|
|
|
CommonOptions so(opt);
|
|
if (opt.time_solve() > 0U)
|
|
so.stop = Search::Stop::time(opt.time_solve());
|
|
so.cutoff = Search::Cutoff::geometric(JobShopConfig::restart_scale,
|
|
JobShopConfig::restart_base);
|
|
RBS<JobShopSolve,BAB> rbs(master,so);
|
|
while (JobShopSolve* s = rbs.next()) {
|
|
s->print(std::cout);
|
|
u = s->cost().val();
|
|
delete s;
|
|
}
|
|
|
|
print(rbs.statistics(),true);
|
|
std::cout << "\t\t\truntime: ";
|
|
Driver::stop(t,std::cout);
|
|
std::cout << std::endl;
|
|
|
|
if (rbs.stopped()) {
|
|
std::cout << "\t\t\t\tstopped due to time-out..." << std::endl;
|
|
std::cout << std::endl
|
|
<< "\tSolution at most ";
|
|
double a = (static_cast<double>(u-l+1) / u) * 100.0;
|
|
std::cout << std::setprecision(2) << a
|
|
<< "% away from optimum."
|
|
<< std::endl;
|
|
|
|
} else {
|
|
std::cout << std::endl
|
|
<< "\tFound best solution and proved optimality."
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** \brief Main-function
|
|
* \relates JobShop
|
|
*/
|
|
int
|
|
main(int argc, char* argv[]) {
|
|
JobShopOptions opt("JobShop");
|
|
|
|
opt.branching(JobShopSolve::BRANCH_AFC);
|
|
opt.branching(JobShopSolve::BRANCH_AFC, "afc");
|
|
opt.branching(JobShopSolve::BRANCH_ACTION, "action");
|
|
opt.branching(JobShopSolve::BRANCH_CHB, "chb");
|
|
|
|
opt.propagation(JobShopSolve::PROP_UNARY);
|
|
opt.propagation(JobShopSolve::PROP_ORDER,"order");
|
|
opt.propagation(JobShopSolve::PROP_UNARY,"unary");
|
|
|
|
opt.instance("ft06");
|
|
|
|
opt.restart_base(JobShopConfig::restart_base);
|
|
opt.restart_scale(JobShopConfig::restart_scale);
|
|
opt.nogoods(true);
|
|
|
|
opt.parse(argc,argv);
|
|
if (!Spec(opt.instance()).valid()) {
|
|
std::cerr << "Error: unkown instance" << std::endl;
|
|
return 1;
|
|
}
|
|
solve(opt);
|
|
return 0;
|
|
}
|
|
|
|
#include "examples/job-shop-instances.hpp"
|
|
|
|
// STATISTICS: example-any
|
|
|