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

524 lines
22 KiB
C++

/*
* 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 <chuffed/support/vec.h>
#include <chuffed/flatzinc/flatzinc.h>
#include <chuffed/core/engine.h>
#include <chuffed/branching/branching.h>
using namespace std;
void output_var(Branching *v);
namespace FlatZinc {
FlatZincSpace *s;
VarBranch ann2ivarsel(AST::Node* ann) {
if (AST::Atom* s = dynamic_cast<AST::Atom*>(ann)) {
if (s->id == "input_order") return VAR_INORDER;
if (s->id == "first_fail") return VAR_SIZE_MIN;
if (s->id == "anti_first_fail") return VAR_SIZE_MAX;
if (s->id == "smallest") return VAR_MIN_MIN;
if (s->id == "smallest_largest") return VAR_MAX_MIN;
if (s->id == "largest") return VAR_MAX_MAX;
if (s->id == "largest_smallest") return VAR_MIN_MAX;
if (s->id == "occurrence") return VAR_DEGREE_MAX;
if (s->id == "most_constrained") return VAR_SIZE_MIN;
if (s->id == "max_regret") return VAR_REGRET_MIN_MAX;
if (s->id == "random_order") return VAR_RANDOM;
}
cerr << "% Warning: Unknown or not support variable selection annotation '";
ann->print(cerr);
cerr << "'! Ignore variable selection annotation and replace it by 'input_order'." << endl;
return VAR_INORDER;
}
ValBranch ann2ivalsel(AST::Node* ann) {
if (AST::Atom* s = dynamic_cast<AST::Atom*>(ann)) {
if (s->id == "default") return VAL_DEFAULT;
if (s->id == "indomain") return VAL_MIN;
if (s->id == "indomain_min") return VAL_MIN;
if (s->id == "indomain_max") return VAL_MAX;
//if (s->id == "indomain_middle") return VAL_MIDDLE;
if (s->id == "indomain_middle") CHUFFED_ERROR("The value choice annotation 'indomain_middle' is not supported yet!\n");
if (s->id == "indomain_median") return VAL_MEDIAN;
if (s->id == "indomain_split") return VAL_SPLIT_MIN;
if (s->id == "indomain_reverse_split") return VAL_SPLIT_MAX;
//if (s->id == "indomain_random") return VAL_RANDOM;
if (s->id == "indomain_random") CHUFFED_ERROR("The value choice annotation 'indomain_random' is not supported yet!\n");
}
std::cerr << "% Warning, ignored search annotation: ";
ann->print(std::cerr);
std::cerr << std::endl;
return VAL_DEFAULT;
}
FlatZincSpace::FlatZincSpace(int intVars, int boolVars, int setVars)
: intVarCount(0), boolVarCount(0), iv(intVars), iv_introduced(intVars),
bv(boolVars), bv_introduced(boolVars), output(NULL) {
s = this;
}
void FlatZincSpace::newIntVar(IntVarSpec* vs) {
// Resizing of the vectors if required
if (intVarCount == iv.size()) {
const int newSize = intVarCount > 0 ? 2 * intVarCount : 1;
iv.growTo(newSize);
iv_introduced.resize(newSize);
}
bool considerIntroduced = false;
if (so.use_var_is_introduced) considerIntroduced = vs->introduced;
else considerIntroduced = !vs->output;
if (so.introduced_heuristic && vs->looks_introduced) considerIntroduced = true;
if (vs->alias) {
iv[intVarCount++] = iv[vs->i];
} else {
IntVar* v = NULL;
if (vs->assigned) v = getConstant(vs->i);
else if (vs->domain()) {
AST::SetLit* sl = vs->domain.some();
if (sl->interval) v = ::newIntVar(sl->min, sl->max);
else {
vec<int> d;
for (unsigned int i = 0; i < sl->s.size(); i++) d.push(sl->s[i]);
sort((int*) d, (int*) d + d.size());
v = ::newIntVar(d[0], d.last());
if ((d.last()-d[0] >= d.size() * mylog2(d.size())) ||
(d.size() <= so.eager_limit && (d.last() - d[0] + 1) > so.eager_limit)) {
new (v) IntVarSL(*v, d);
} else {
if (!v->allowSet(d)) TL_FAIL();
}
}
}
else {
v = ::newIntVar();
}
/* std::cerr << "int var: " << intVarCount << " " << v << "\n"; */
if (so.exclude_introduced && considerIntroduced)
v->should_be_learnable = false;
if (!so.decide_introduced && considerIntroduced)
v->should_be_decidable = false;
iv[intVarCount++] = v;
}
iv_introduced[intVarCount-1] = considerIntroduced;
}
void FlatZincSpace::newBoolVar(BoolVarSpec* vs) {
// Resizing of the vectors if required
if (boolVarCount == iv.size()) {
const int newSize = boolVarCount > 0 ? 2 * boolVarCount : 1;
bv.growTo(newSize);
bv_introduced.resize(newSize);
}
bool considerIntroduced = false;
if (so.use_var_is_introduced) considerIntroduced = vs->introduced;
else considerIntroduced = !vs->output;
if (so.introduced_heuristic && vs->looks_introduced) considerIntroduced = true;
if (vs->alias) {
bv[boolVarCount++] = bv[vs->i];
}
else {
BoolView v;
#if EXPOSE_INT_LITS
if (vs->alias_var >= 0) {
iv[vs->alias_var]->specialiseToEL();
switch (vs->alias_irt) {
case IRT_EQ:
v = BoolView(iv[vs->alias_var]->getLit(vs->alias_val, 1));
break;
case IRT_NE:
v = BoolView(iv[vs->alias_var]->getLit(vs->alias_val, 0));
break;
case IRT_GE:
v = BoolView(iv[vs->alias_var]->getLit(vs->alias_val, 2));
break;
case IRT_GT:
v = BoolView(iv[vs->alias_var]->getLit(vs->alias_val + 1, 2));
break;
case IRT_LE:
v = BoolView(iv[vs->alias_var]->getLit(vs->alias_val, 3));
break;
case IRT_LT:
v = BoolView(iv[vs->alias_var]->getLit(vs->alias_val - 1, 3));
break;
default:
NEVER;
}
}
else
#endif
v = ::newBoolVar();
if (vs->assigned) v.setVal(vs->i);
else if (vs->domain()) {
AST::SetLit* sl = vs->domain.some();
assert(sl->interval);
assert(sl->min <= 1);
assert(sl->max >= 0);
assert(sl->min <= sl->max);
if (sl->min == 1) v.setVal(true);
if (sl->max == 0) v.setVal(false);
}
if (so.exclude_introduced && considerIntroduced) {
v.setLearnable(false);
v.setDecidable(false);
v.setUIPable(false);
}
if (!so.decide_introduced && considerIntroduced) {
v.setDecidable(false);
}
bv[boolVarCount++] = v;
}
bv_introduced[boolVarCount-1] = considerIntroduced;
}
void
FlatZincSpace::newSetVar(SetVarSpec*) {
throw FlatZinc::Error("LazyGeoff", "set variables not supported");
}
void FlatZincSpace::postConstraint(const ConExpr& ce, AST::Node* ann) {
try {
registry().post(ce, ann);
} catch (AST::TypeError& e) {
throw FlatZinc::Error("Type error", e.what());
} catch (exception& e) {
throw FlatZinc::Error("LazyGeoff", e.what());
}
}
// Parsing the 'int_search' annotation and setting up the branching for it
void FlatZincSpace::parseSolveAnnIntSearch(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations) {
assert(elemAnn->isCall("int_search"));
try {
// Retrieval of the data
AST::Call *call = elemAnn->getCall("int_search");
AST::Array *args = call->getArgs(4);
AST::Array *vars = args->a[0]->getArray();
vec<Branching*> va;
for (unsigned int i = 0; i < vars->a.size(); i++) {
// Removal of constants
if (vars->a[i]->isInt()) continue;
IntVar* v = iv[vars->a[i]->getIntVar()];
// Removal of fixed variables
if (v->isFixed()) continue;
va.push(v);
}
branching->add(createBranch(va, ann2ivarsel(args->a[1]), ann2ivalsel(args->a[2])));
if (AST::String* s = dynamic_cast<AST::String*>(args->a[3])) {
if (s->s == "all") so.nof_solutions = 0;
}
nbNonEmptySearchAnnotations++;
}
catch (AST::TypeError& e) {
throw FlatZinc::Error("Type error in int_search annotation", e.what());
}
}
// Parsing the 'bool_search' annotation and setting up the branching for it
void FlatZincSpace::parseSolveAnnBoolSearch(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations) {
assert(elemAnn->isCall("bool_search"));
try {
// Retrieval of the data
AST::Call *call = elemAnn->getCall("bool_search");
AST::Array *args = call->getArgs(4);
AST::Array *vars = args->a[0]->getArray();
vec<Branching*> va(vars->a.size());
for (int i=vars->a.size(); i--; )
va[i] = new BoolView(bv[vars->a[i]->getBoolVar()]);
branching->add(createBranch(va, ann2ivarsel(args->a[1]), ann2ivalsel(args->a[2])));
if (AST::String* s = dynamic_cast<AST::String*>(args->a[3])) {
if (s->s == "all") so.nof_solutions = 0;
}
nbNonEmptySearchAnnotations++;
}
catch (AST::TypeError& e) {
throw FlatZinc::Error("Type error in bool_search annotation", e.what());
}
}
// Parsing the 'priority_search' annotation and setting up the branching for it
void FlatZincSpace::parseSolveAnnPrioritySearch(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations) {
assert(elemAnn->isCall("priority_search"));
try {
// Retrieval of the data
AST::Call *call = elemAnn->getCall("priority_search");
AST::Array *args = call->getArgs(4);
AST::Array *vars = args->a[0]->getArray();
AST::Array *annotations = args->a[1]->getArray();
// Retrieval of all variables
// NOTE that constants or fixed variables cannot be removed, because they act as
// delegates for choosing the branch group to search on next
vec<Branching*> va;
for (unsigned int i = 0; i < vars->a.size(); i++) {
// Removal of constants
IntVar* v = NULL;
if (vars->a[i]->isInt()) {
int value = vars->a[i]->getInt();
v = getConstant(value);
}
else {
v = iv[vars->a[i]->getIntVar()];
}
va.push(v);
}
// Create a new priority branch group and add to branching
PriorityBranchGroup * priorityBranching = new PriorityBranchGroup(va, ann2ivarsel(args->a[2]));
// Parse search annotations
int nbChildSearchAnnotations = 0;
parseSolveAnn(annotations, priorityBranching, nbChildSearchAnnotations);
if (vars->a.size() != nbChildSearchAnnotations)
throw FlatZinc::Error("Type error in priority_search annotation", "Variable and annotation array must have the same size");
if (AST::String* s = dynamic_cast<AST::String*>(args->a[3])) {
if (s->s == "all") so.nof_solutions = 0;
}
branching->add(priorityBranching);
nbNonEmptySearchAnnotations++;
}
catch (AST::TypeError& e) {
throw FlatZinc::Error("Type error in priority_search annotation", e.what());
}
}
void FlatZincSpace::parseSolveAnnAux(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations) {
if (elemAnn->isCall("int_search")) {
parseSolveAnnIntSearch(elemAnn, branching, nbNonEmptySearchAnnotations);
}
else if (elemAnn->isCall("bool_search")) {
parseSolveAnnBoolSearch(elemAnn, branching, nbNonEmptySearchAnnotations);
}
else if (elemAnn->isCall("priority_search")) {
parseSolveAnnPrioritySearch(elemAnn, branching, nbNonEmptySearchAnnotations);
}
else {
throw FlatZinc::Error("Error in search annotation", "Unknown search annotation");
}
}
// Users should add search annotation with (core vars, default, default) even if they know nothing
// Entry function for parsing the solve annotation
void FlatZincSpace::parseSolveAnn(AST::Array* ann) {
int nbNonEmptySearchAnnotations = 0;
try {
// Parse the search annotation
parseSolveAnn(ann, engine.branching, nbNonEmptySearchAnnotations);
}
catch (FlatZinc::Error& e) {
cerr << "% " << e.toString() << ". Ignore search annotation!" << endl;
// Removal of successful parsed parts of the search annotation
engine.branching = new BranchGroup();
// Reset counter
nbNonEmptySearchAnnotations = 0;
}
// Check whether a search was specified
if (nbNonEmptySearchAnnotations == 0) {
if (!so.vsids) {
so.vsids = true;
engine.branching->add(&sat);
}
}
}
void FlatZincSpace::parseSolveAnn(AST::Array* ann, BranchGroup* branching, int & nbNonEmptySearchAnnotations) {
if (ann) {
for (unsigned int i = 0; i < ann->a.size(); i++) {
if (ann->a[i]->isCall("restart_none") && so.restart_type_override) {
so.restart_type = NONE;
} else if (ann->a[i]->isCall("restart_constant")) {
AST::Call* call = ann->a[i]->getCall("restart_constant");
if (so.restart_type_override) { so.restart_type = CONSTANT; }
if (so.restart_scale_override) { so.restart_scale = static_cast<unsigned int>(call->args->getInt()); }
} else if (ann->a[i]->isCall("restart_linear")) {
AST::Call* call = ann->a[i]->getCall("restart_linear");
if (so.restart_type_override) { so.restart_type = LINEAR; }
if (so.restart_scale_override) { so.restart_scale = static_cast<unsigned int>(call->args->getInt()); }
} else if (ann->a[i]->isCall("restart_luby")) {
AST::Call* call = ann->a[i]->getCall("restart_luby");
if (so.restart_type_override) { so.restart_type = LUBY; }
if (so.restart_scale_override) { so.restart_scale = static_cast<unsigned int>(call->args->getInt()); }
} else if (ann->a[i]->isCall("restart_geometric")) {
AST::Call* call = ann->a[i]->getCall("restart_geometric");
if (so.restart_type_override) { so.restart_type = GEOMETRIC; }
AST::Array* args = call->getArgs(2);
if (so.restart_base_override) { so.restart_base = args->a[0]->getFloat(); }
if (so.restart_base < 1.0) {
CHUFFED_ERROR("Illegal restart base. Restart count will converge to zero.");
}
if (so.restart_scale_override) { so.restart_scale = static_cast<unsigned int>(args->a[1]->getInt()); }
} else if (ann->a[i]->isCall("seq_search")) {
// Get the call
AST::Call* c = ann->a[i]->getCall();
// Create a new branch group and add to the branching
BranchGroup* newBranching = new BranchGroup();
branching->add(newBranching);
if (c->args->isArray()) {
// Parse annotation and add to the newly created branching
int nbChildSearchAnnotations = 0;
parseSolveAnn(c->args->getArray(), newBranching, nbChildSearchAnnotations);
if (nbChildSearchAnnotations > 0)
nbNonEmptySearchAnnotations++;
}
else {
// Parse annotation and add to the newly created branching
parseSolveAnnAux(c->args, newBranching, nbNonEmptySearchAnnotations);
}
}
else {
// Parse annotation and add to the branching
parseSolveAnnAux(ann->a[i], branching, nbNonEmptySearchAnnotations);
}
}
}
}
void FlatZincSpace::fixAllSearch() {
vec<Branching*> va;
for (int i = 0; i < intVarCount; i++) {
if (iv_introduced[i]) continue;
IntVar* v = iv[i];
if (v->isFixed()) continue;
va.push(v);
}
for (int i = 0; i < boolVarCount; i++) {
if (bv_introduced[i]) continue;
va.push(new BoolView(bv[i]));
}
for (int i = intVarCount; i--; ) {
if (!iv_introduced[i]) continue;
IntVar* v = iv[i];
if (v->isFixed()) continue;
va.push(v);
}
for (int i = 0; i < boolVarCount; i++) {
if (!bv_introduced[i]) continue;
va.push(new BoolView(bv[i]));
}
if (va.size()) branch(va, VAR_INORDER, VAL_DEFAULT);
}
void FlatZincSpace::solve(AST::Array* ann) {
parseSolveAnn(ann);
fixAllSearch();
}
void FlatZincSpace::minimize(int var, AST::Array* ann) {
parseSolveAnn(ann);
optimize(iv[var], OPT_MIN);
fixAllSearch();
}
void FlatZincSpace::maximize(int var, AST::Array* ann) {
parseSolveAnn(ann);
optimize(iv[var], OPT_MAX);
fixAllSearch();
}
void FlatZincSpace::setOutputElem(AST::Node* ai) const {
if (ai->isIntVar()) {
output_var(iv[ai->getIntVar()]);
} else if (ai->isBoolVar()) {
output_var(new BoolView(bv[ai->getBoolVar()]));
}
}
void FlatZincSpace::setOutput() {
if (output == NULL) return;
for (unsigned int i=0; i< output->a.size(); i++) {
AST::Node* ai = output->a[i];
if (ai->isArray()) {
AST::Array* aia = ai->getArray();
int size = aia->a.size();
for (int j=0; j<size; j++) {
setOutputElem(aia->a[j]);
}
} else if (ai->isCall("ifthenelse")) {
AST::Array* aia = ai->getCall("ifthenelse")->getArgs(3);
setOutputElem(aia->a[1]);
setOutputElem(aia->a[2]);
} else {
setOutputElem(ai);
}
}
}
void FlatZincSpace::printElem(AST::Node* ai, ostream& out) const {
int k;
if (ai->isInt(k)) {
out << k;
}
else if (ai->isIntVar()) {
out << iv[ai->getIntVar()]->getVal();
}
else if (ai->isBoolVar()) {
if (bv[ai->getBoolVar()].isTrue()) {
out << "true";
}
else if (bv[ai->getBoolVar()].isFalse()) {
out << "false";
}
else {
out << "false..true";
}
}
else if (ai->isBool()) {
out << (ai->getBool() ? "true" : "false");
}
else if (ai->isSet()) {
AST::SetLit* s = ai->getSet();
if (s->interval) {
out << s->min << ".." << s->max;
}
else {
out << "{";
for (unsigned int i=0; i<s->s.size(); i++) {
out << s->s[i] << (i < s->s.size()-1 ? ", " : "}");
}
}
}
else if (ai->isString()) {
std::string s = ai->getString();
for (unsigned int i=0; i<s.size(); i++) {
if (s[i] == '\\' && i<s.size()-1) {
switch (s[i+1]) {
case 'n': out << "\n"; break;
case '\\': out << "\\"; break;
case 't': out << "\t"; break;
default: out << "\\" << s[i+1];
}
i++;
}
else {
out << s[i];
}
}
}
}
}