1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker 35a3110598 Squashed 'software/chuffed/' content from commit 2ed0c015
git-subtree-dir: software/chuffed
git-subtree-split: 2ed0c01558d2a5c49c1ce57e048d32c17adf92d3
2021-06-18 09:36:35 +10:00

387 lines
9.4 KiB
C++

#include <map>
#include <chuffed/vars/int-var.h>
#include <chuffed/mip/mip.h>
#include <sstream>
// When set, branch variable (first_fail) and value (indomain_median,
// indomain_split, indomain_reverse_split) specifications will count domain
// sizes by the number of active values rather than the bounds (max-min).
// (There is not too much penalty if INT_DOMAIN_LIST enabled in int-var.h).
#define INT_BRANCH_HOLES 0
using namespace std;
map<int,IntVar*> ic_map;
extern std::map<IntVar*, std::string> intVarString;
IntVar::IntVar(int _min, int _max) :
var_id(engine.vars.size())
, min(_min)
, max(_max)
, min0(_min)
, max0(_max)
, shadow_val(0)
, in_scip(false)
, all_in_scip(true)
, should_be_learnable(true)
, should_be_decidable(true)
, vals(NULL)
, preferred_val(PV_MIN)
, activity(0)
, in_queue(false)
{
assert(min_limit <= min && min <= max && max <= max_limit);
engine.vars.push(this);
changes = EVENT_C | EVENT_L | EVENT_U;
if (isFixed()) changes |= EVENT_F;
}
// Allocate enough memory to specialise IntVar later using the same memory block
IntVar* newIntVar(int min, int max) {
size_t size = sizeof(IntVar);
if (sizeof(IntVarEL) > size) size = sizeof(IntVarEL);
if (sizeof(IntVarLL) > size) size = sizeof(IntVarLL);
if (sizeof(IntVarSL) > size) size = sizeof(IntVarSL);
void *mem = malloc(size);
IntVar *var = new (mem) IntVar(min, max);
return var;
}
IntVar* getConstant(int v) {
map<int,IntVar*>::iterator it = ic_map.find(v);
if (it != ic_map.end()) return it->second;
IntVar *var = newIntVar(v,v);
std::stringstream ss;
ss << "constant_" << v;
intVarString[var] = ss.str();
var->specialiseToEL();
ic_map.insert(pair<int,IntVar*>(v, var));
return var;
}
void IntVar::specialiseToEL() {
switch (getType()) {
case INT_VAR_EL: return;
case INT_VAR_SL: return;
case INT_VAR: new (this) IntVarEL(*((IntVar*) this)); break;
default: NEVER;
}
}
void IntVar::specialiseToLL() {
switch (getType()) {
case INT_VAR_EL: return;
case INT_VAR_SL: return;
case INT_VAR: new (this) IntVarLL(*((IntVar*) this)); break;
default: NEVER;
}
}
void IntVar::specialiseToSL(vec<int>& values) {
if (getType() == INT_VAR_EL) return;
if (getType() == INT_VAR_SL) return;
assert(getType() == INT_VAR);
vec<int> v = values;
std::sort((int*) v, (int*) v + v.size());
int i, j;
for (i = j = 0; i < v.size(); i++) {
if (i == 0 || v[i] != v[i-1]) v[j++] = v[i];
}
v.resize(j);
// for (int i = 0; i < values.size(); i++) printf("%d ", values[i]);
// printf("\n");
// determine whether it is sparse or dense
if (v.last()-v[0] >= v.size() * mylog2(v.size())) {
// fprintf(stderr, "SL\n");
new (this) IntVarSL(*((IntVar*) this), v);
} else {
new (this) IntVarEL(*((IntVar*) this));
if (!allowSet(v)) TL_FAIL();
}
}
void IntVar::initVals(bool optional) {
if (vals) return;
if (min == min_limit || max == max_limit) {
if (optional) return;
CHUFFED_ERROR("Cannot initialise vals in unbounded IntVar\n");
}
vals = (Tchar*) malloc((max-min+2) * sizeof(Tchar));
if (!vals) { perror("malloc()"); exit(1); }
memset(vals, 1, max-min+2);
if (!(vals -= min)) vals++; // Hack to make vals != NULL whenever it's allocated
#if INT_DOMAIN_LIST
vals_list = (Tint*) malloc(2*(max-min) * sizeof(Tint));
if (!vals_list) { perror("malloc()"); exit(1); }
vals_list -= 2*min+1;
for (int i = min; i < max; ++i) {
vals_list[2*i+1].v = i+1; // forward link
vals_list[2*i+2].v = i; // backward link from next value
}
vals_count.v = max+1-min;
#endif
}
void IntVar::attach(Propagator *p, int pos, int eflags) {
if (isFixed()) p->wakeup(pos, eflags);
else pinfo.push(PropInfo(p, pos, eflags));
}
void IntVar::wakePropagators() {
for (int i = pinfo.size(); i--; ) {
PropInfo& pi = pinfo[i];
if ((pi.eflags & changes) == 0) continue;
if (pi.p->satisfied) continue;
if (pi.p == engine.last_prop) continue;
pi.p->wakeup(pi.pos, changes);
}
clearPropState();
}
int IntVar::simplifyWatches() {
int i, j;
for (i = j = 0; i < pinfo.size(); i++) {
if (!pinfo[i].p->satisfied) pinfo[j++] = pinfo[i];
}
pinfo.resize(j);
return j;
}
//-----
// Branching stuff
double IntVar::getScore(VarBranch vb) {
switch(vb) {
case VAR_MIN_MIN : return -min;
case VAR_MIN_MAX : return min;
case VAR_MAX_MIN : return -max;
case VAR_MAX_MAX : return max;
#if INT_BRANCH_HOLES
// note slight inconsistency, if INT_BRANCH_HOLES=0 then we
// use the domain size-1, same behaviour but more efficient?
case VAR_SIZE_MIN : return vals ? -size() : min - (max + 1);
case VAR_SIZE_MAX : return vals ? size() : max + 1 - min;
#else
case VAR_SIZE_MIN : return min-max;
case VAR_SIZE_MAX : return max-min;
#endif
case VAR_DEGREE_MIN : return -pinfo.size();
case VAR_DEGREE_MAX : return pinfo.size();
case VAR_REDUCED_COST : return mip->getRC(this);
case VAR_ACTIVITY : return activity;
case VAR_REGRET_MIN_MAX: return isFixed() ? 0 : (vals ? *++begin() - *begin() : 1);
default: NOT_SUPPORTED;
}
}
DecInfo* IntVar::branch() {
// vec<int> possible;
// for (int i = min; i <= max; i++) if (indomain(i)) possible.push(i);
// return new DecInfo(this, possible[rand()%possible.size()], 1);
switch (preferred_val) {
case PV_MIN : return new DecInfo(this, min, 1);
case PV_MAX : return new DecInfo(this, max, 1);
#if INT_BRANCH_HOLES
// note slight inconsistency, if INT_BRANCH_HOLES=0 then we
// round down rather than up (vice versa for PV_SPLIT_MAX),
// should probably revisit this and make them consistent
case PV_SPLIT_MIN : {
if (!vals)
return new DecInfo(this, min + (max - min) / 2, 3);
int values = (size()- 1) / 2;
iterator j = begin();
for (int i = 0; i < values; ++i)
++j;
return new DecInfo(this, *j, 3);
}
case PV_SPLIT_MAX : {
if (!vals)
return new DecInfo(this, min + (max - min - 1) / 2, 2);
int values = size() / 2;
iterator j = begin();
for (int i = 0; i < values; ++i)
++j;
return new DecInfo(this, *j, 2);
}
case PV_MEDIAN: {
if (!vals)
return new DecInfo(this, min + (max - min) / 2, 1);
int values = (size() - 1) / 2;
iterator j = begin();
for (int i = 0; i < values; ++i)
++j;
return new DecInfo(this, *j, 1);
}
#else
case PV_SPLIT_MIN:
return new DecInfo(this, min+(max-min-1)/2, 3);
case PV_SPLIT_MAX:
return new DecInfo(this, min+(max-min)/2, 2);
case PV_MEDIAN:
if (!vals) {
CHUFFED_ERROR("Median value selection is not supported this variable.\n");
}
else {
int values = (size() - 1) / 2;
iterator j = begin();
for (int i = 0; i < values; ++i)
++j;
return new DecInfo(this, *j, 1);
}
#endif
default: NEVER;
}
}
//-----
// Domain change stuff
#if !INT_DOMAIN_LIST
inline void IntVar::updateMin() {
int v = min;
if (!vals[v]) {
while (!vals[v]) v++;
min = v;
changes |= EVENT_C | EVENT_L;
}
}
inline void IntVar::updateMax() {
int v = max;
if (!vals[v]) {
while (!vals[v]) v--;
max = v;
changes |= EVENT_C | EVENT_U;
}
}
#endif
inline void IntVar::updateFixed() {
if (isFixed()) changes |= EVENT_F;
}
bool IntVar::setMin(int64_t v, Reason r, bool channel) {
assert(setMinNotR(v));
if (v > max) return false;
#if INT_DOMAIN_LIST
if (vals) {
int i;
int j = vals_count;
for (i = min; i < v; i = vals_list[2*i+1])
--j;
min = i;
vals_count = j;
}
else
min = v;
changes |= EVENT_C | EVENT_L;
#else
min = v; changes |= EVENT_C | EVENT_L;
if (vals) updateMin();
#endif
updateFixed();
pushInQueue();
return true;
}
bool IntVar::setMax(int64_t v, Reason r, bool channel) {
assert(setMaxNotR(v));
if (v < min) return false;
#if INT_DOMAIN_LIST
if (vals) {
int i;
int j = vals_count;
for (i = max; i > v; i = vals_list[2*i])
--j;
max = i;
vals_count = j;
}
else
max = v;
changes |= EVENT_C | EVENT_U;
#else
max = v; changes |= EVENT_C | EVENT_U;
if (vals) updateMax();
#endif
updateFixed();
pushInQueue();
return true;
}
bool IntVar::setVal(int64_t v, Reason r, bool channel) {
assert(setValNotR(v));
if (!indomain(v)) return false;
if (min < v) { min = v; changes |= EVENT_C | EVENT_L | EVENT_F; }
if (max > v) { max = v; changes |= EVENT_C | EVENT_U | EVENT_F; }
#if INT_DOMAIN_LIST
if (vals)
vals_count = 1;
#endif
pushInQueue();
return true;
}
bool IntVar::remVal(int64_t v, Reason r, bool channel) {
assert(remValNotR(v));
if (isFixed()) return false;
if (!vals) {
if (!engine.finished_init) NEVER;
return true;
}
#if INT_DOMAIN_LIST
if (v == min) {
min = vals_list[2*min+1];
changes |= EVENT_C | EVENT_L;
}
else if (v == max) {
max = vals_list[2*max];
changes |= EVENT_C | EVENT_U;
}
else {
vals[v] = 0;
vals_list[vals_list[2*v]*2+1] = vals_list[2*v+1];
vals_list[vals_list[2*v+1]*2] = vals_list[2*v];
changes |= EVENT_C;
}
--vals_count;
#else
vals[v] = 0; changes |= EVENT_C;
updateMin();
updateMax();
#endif
updateFixed();
pushInQueue();
return true;
}
// Assumes v is sorted
bool IntVar::allowSet(vec<int>& v, Reason r, bool channel) {
initVals();
if (!vals && !engine.finished_init) NOT_SUPPORTED;
int i = 0;
int m = min;
while (i < v.size() && v[i] < m) i++;
for ( ; i < v.size(); i++) {
for ( ; m < v[i]; m++) {
if (m > max) return true;
if (remValNotR(m)) if (!remVal(m, r, channel)) return false;
}
m = v[i]+1;
}
for ( ; m <= max; m++) {
if (remValNotR(m)) if (!remVal(m, r, channel)) return false;
}
return true;
}