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

546 lines
19 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.
*
*/
#ifndef flatzinc_h
#define flatzinc_h
#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <ostream>
#include <sstream>
#include <algorithm>
#include <chuffed/support/vec.h>
#include <chuffed/core/engine.h>
#include <chuffed/core/propagator.h>
#include <chuffed/flatzinc/ast.h>
extern std::map<IntVar*, std::string> intVarString;
extern std::map<BoolView, std::string> boolVarString;
// Controls whether expressions like bool_sum_eq([x[i] = j | i in 1..n], 1)
// access the underlying literals x[i] = j or new ones via int_eq_reif(...)
// NOTE: Implemention isn't 100% ideal at this stage so kept it conditional
#define EXPOSE_INT_LITS 0
namespace FlatZinc {
/// Alias for a variable specification
class Alias {
public:
const int v;
Alias(int v0) : v(v0) {}
};
/// Optional value
template<class Val>
struct Option {
private:
bool _some;
Val _v;
public:
bool operator()(void) const { return _some; }
const Val& some(void) const { return _v; }
static Option<Val> none(void) {
Option<Val> o;
o._some = false;
new (&o._v) Val();
return o;
}
static Option<Val> some(const Val& v) {
Option<Val> o;
o._some = true;
o._v = v;
return o;
}
};
/// Base class for variable specifications
class VarSpec {
public:
/// Whether the variable has an "output" annotation
bool output;
/// Whether the variable was introduced in the mzn2fzn translation
bool introduced;
/// Whether the variable *looks* introduced by the mzn2fzn translation
bool looks_introduced;
/// Destructor
virtual ~VarSpec(void) {}
/// Variable index
int i;
/// Whether the variable aliases another variable
bool alias;
/// Whether the variable is assigned
bool assigned;
/// Constructor
VarSpec(bool output0, bool introduced0, bool looks0) : output(output0), introduced(introduced0), looks_introduced(looks0) {}
void set_looks_introduced(bool b) {
looks_introduced = b;
}
};
/// Specification for integer variables
class IntVarSpec : public VarSpec {
public:
Option<AST::SetLit* > domain;
IntVarSpec(const Option<AST::SetLit* >& d, bool output, bool introduced, bool looks = false)
: VarSpec(output, introduced, looks) {
alias = false;
assigned = false;
domain = d;
}
IntVarSpec(int i0, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = false; assigned = true; i = i0;
}
IntVarSpec(const Alias& eq, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = true; i = eq.v;
}
~IntVarSpec(void) {
if (!alias && !assigned && domain())
delete domain.some();
}
};
/// Specification for Boolean variables
class BoolVarSpec : public VarSpec {
public:
Option<AST::SetLit* > domain;
#if EXPOSE_INT_LITS
// whether it aliases an int literal such as [x <= 5]: (-1=no)
int alias_var;
IntRelType alias_irt;
int alias_val;
#endif
BoolVarSpec(Option<AST::SetLit* >& d, bool output, bool introduced, bool looks = false)
: VarSpec(output, introduced, looks) {
alias = false; assigned = false; domain = d;
#if EXPOSE_INT_LITS
alias_var = -1;
#endif
}
BoolVarSpec(bool b, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = false; assigned = true; i = b;
#if EXPOSE_INT_LITS
alias_var = -1;
#endif
}
BoolVarSpec(const Alias& eq, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = true; i = eq.v;
#if EXPOSE_INT_LITS
alias_var = -1;
#endif
}
~BoolVarSpec(void) {
if (!alias && !assigned && domain())
delete domain.some();
}
};
/// Specification for set variables
class SetVarSpec : public VarSpec {
public:
Option<AST::SetLit*> upperBound;
SetVarSpec(bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = false; assigned = false;
upperBound = Option<AST::SetLit* >::none();
}
SetVarSpec(const Option<AST::SetLit* >& v, bool output, bool introduced, bool looks = false)
: VarSpec(output, introduced, looks) {
alias = false; assigned = false; upperBound = v;
}
SetVarSpec(AST::SetLit* v, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = false; assigned = true;
upperBound = Option<AST::SetLit*>::some(v);
}
SetVarSpec(const Alias& eq, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = true; i = eq.v;
}
~SetVarSpec(void) {
if (!alias && upperBound())
delete upperBound.some();
}
};
/// Specification for floating point variables
class FloatVarSpec : public VarSpec {
public:
Option<std::vector<double>* > domain;
FloatVarSpec(Option<std::vector<double>* >& d, bool output, bool introduced, bool looks = false)
: VarSpec(output, introduced, looks) {
alias = false; assigned = false; domain = d;
}
FloatVarSpec(bool b, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = false; assigned = true; i = b;
}
FloatVarSpec(const Alias& eq, bool output, bool introduced, bool looks = false) : VarSpec(output, introduced, looks) {
alias = true; i = eq.v;
}
~FloatVarSpec(void) {
if (!alias && !assigned && domain())
delete domain.some();
}
};
/// Abstract representation of a constraint
class ConExpr {
public:
/// Identifier for the constraint
std::string id;
/// Constraint arguments
AST::Array* args;
/// Constructor
ConExpr(const std::string& id0, AST::Array* args0) :
id(id0), args(args0) {}
/// Return argument \a i
AST::Node* operator[](int i) const { return args->a[i]; }
/// Destructor
~ConExpr(void) { delete args; }
};
/// Map from constraint identifier to constraint posting functions
class Registry {
public:
/// Type of constraint posting function
typedef void (*poster) (const ConExpr&, AST::Node*);
/// Add posting function \a p with identifier \a id
void add(const std::string& id, poster p);
/// Post constraint specified by \a ce
void post(const ConExpr& ce, AST::Node* ann);
private:
/// The actual registry
std::map<std::string,poster> r;
};
/// Return global registry object
Registry& registry(void);
/// Symbol table mapping identifiers (strings) to values
template<class Val>
class SymbolTable {
private:
std::map<std::string,Val> m;
public:
/// Insert \a val with \a key
void put(const std::string& key, const Val& val) { m[key] = val; }
/// Return whether \a key exists, and set \a val if it does exist
bool get(const std::string& key, Val& val) const {
typename std::map<std::string,Val>::const_iterator i = m.find(key);
if (i == m.end()) return false;
val = i->second;
return true;
}
};
class FlatZincSpace : public Problem {
public:
/// Number of integer variables
int intVarCount;
/// Number of Boolean variables
int boolVarCount;
/// The integer variables
vec<IntVar*> iv;
/// Indicates whether an integer variable is introduced by mzn2fzn
std::vector<bool> iv_introduced;
/// The Boolean variables
vec<BoolView> bv;
/// Indicates whether a Boolean variable is introduced by mzn2fzn
std::vector<bool> bv_introduced;
AST::Array* output;
/// Construct problem with given number of variables
FlatZincSpace(int intVars, int boolVars, int setVars);
/// Create priority branch group
PriorityBranchGroup* priorityBranch(vec<Branching*> x, AST::Array* ann, VarBranch var_branch);
/// Parse the solve annotations and create corresponding branchings
void parseSolveAnn(AST::Array* ann);
/// Create final branching that fixes all variables
void fixAllSearch();
/// Create new integer variable from specification
void newIntVar(IntVarSpec* vs);
/// Create new Boolean variable from specification
void newBoolVar(BoolVarSpec* vs);
/// Create new set variable from specification
void newSetVar(SetVarSpec* vs);
/// Post a constraint specified by \a ce
void postConstraint(const ConExpr& ce, AST::Node* annotation);
/// Post the solve item
void solve(AST::Array* annotation);
/// Post that integer variable \a var should be minimized
void minimize(int var, AST::Array* annotation);
/// Post that integer variable \a var should be maximized
void maximize(int var, AST::Array* annotation);
/// Define output variables
void setOutputElem(AST::Node* ai) const;
void setOutput();
void printElem(AST::Node* ai, std::ostream& out = std::cout) const;
// Needed by the profiler
void print(std::ostream& out) {
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();
out << "[";
for (int j=0; j<size; j++) {
printElem(aia->a[j], out);
if (j<size-1) out << ", ";
}
out << "]";
}
else if (ai->isCall("ifthenelse")) {
AST::Array* aia = ai->getCall("ifthenelse")->getArgs(3);
if (aia->a[0]->isBool()) {
if (aia->a[0]->getBool()) printElem(aia->a[1], out);
else printElem(aia->a[2], out);
}
else if (aia->a[0]->isBoolVar()) {
BoolView b = bv[aia->a[0]->getBoolVar()];
if (b.isTrue()) printElem(aia->a[1], out);
else if (b.isFalse()) printElem(aia->a[2], out);
else std::cerr << "% Error: Condition not fixed." << std::endl;
}
else {
std::cerr << "% Error: Condition not Boolean." << std::endl;
}
}
else {
printElem(ai, out);
}
}
};
void printStream(std::ostream& out);
// Needed by the profiler
void printDomains(std::ostream& out = std::cout) {
out << "{";
bool outerFirst = true;
for (int i = 0 ; i < iv.size() ; i++) {
if (iv_introduced[i])
continue;
IntVar* var = iv[i];
std::string varName = intVarString[var];
if (varName.empty() || varName.find(so.filter_domains) == std::string::npos)
continue;
if (!outerFirst)
out << ",";
outerFirst = false;
out << '"' << varName << '"' << ":";
out << "[";
if (var->vals != NULL) {
bool first = true;
for (int val = var->getMin() ; val <= var->getMax() ; val++) {
if (var->vals[val]) {
if (!first)
out << ",";
first = false;
out << val;
}
}
}
else {
out << '[' << var->getMin() << "," << var->getMax() << ']';
}
out << "]";
}
for (int i = 0 ; i < bv.size() ; i++) {
if (bv_introduced[i])
continue;
BoolView bview = bv[i];
std::string bvstring = boolVarString[bview];
if (bvstring.find(so.filter_domains) == std::string::npos) {
continue;
}
// TODO: see if this is actually necessary
if (bvstring.empty()) {
// Try the other value
BoolView otherval = bview;
otherval.setSign(!otherval.getSign());
bvstring = boolVarString[otherval];
}
if (bvstring.compare("ASSIGNED_AT_ROOT") == 0)
continue;
if (!outerFirst)
out << ",";
outerFirst = false;
out << boolVarString[bview] << ":";
/* out << litString[toInt(bview.getLit(true))] << ":"; */
/* out << litString[toInt(bview.getLit(false))] << ":"; */
bool first = true;
if (!bview.isFixed())
out << "'undef'";
else if (bview.isTrue())
out << "'true'";
else if (bview.isFalse())
out << "'false'";
else
abort();
}
out << "}";
};
// Needed by the profiler
std::string getDomainsString(void) {
std::stringstream ss;
printDomains(ss);
return ss.str();
}
// Private membership functions
private:
/// Auxiliary functions for parsing the solve annotations and creating corresponding branchings
void parseSolveAnn(AST::Array* ann, BranchGroup* branching, int& nbNonEmptySearchAnnotations);
void parseSolveAnnAux(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations);
void parseSolveAnnIntSearch(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations);
void parseSolveAnnBoolSearch(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations);
void parseSolveAnnPrioritySearch(AST::Node* elemAnn, BranchGroup* branching, int& nbNonEmptySearchAnnotations);
};
extern FlatZincSpace *s;
typedef std::pair<std::string,Option<std::vector<int>* > > intvartype;
typedef std::pair<std::string, VarSpec*> varspec;
/// State of the FlatZinc parser
class ParserState {
public:
ParserState(const std::string& b, std::ostream& err0)
: buf(b.c_str()), pos(0), length(b.size()), fg(NULL),
hadError(false), err(err0) {}
ParserState(char* buf0, int length0, std::ostream& err0)
: buf(buf0), pos(0), length(length0), fg(NULL),
hadError(false), err(err0) {}
void* yyscanner;
const char* buf;
unsigned int pos, length;
FlatZinc::FlatZincSpace* fg;
std::vector<std::pair<std::string,AST::Node*> > _output;
SymbolTable<int> intvarTable;
SymbolTable<int> boolvarTable;
SymbolTable<int> floatvarTable;
SymbolTable<int> setvarTable;
SymbolTable<std::vector<int> > intvararrays;
SymbolTable<std::vector<int> > boolvararrays;
SymbolTable<std::vector<int> > floatvararrays;
SymbolTable<std::vector<int> > setvararrays;
SymbolTable<std::vector<int> > intvalarrays;
SymbolTable<std::vector<int> > boolvalarrays;
SymbolTable<int> intvals;
SymbolTable<bool> boolvals;
SymbolTable<AST::SetLit> setvals;
SymbolTable<std::vector<AST::SetLit> > setvalarrays;
std::vector<varspec> intvars;
std::vector<varspec> boolvars;
std::vector<varspec> setvars;
std::vector<ConExpr*> domainConstraints;
#if EXPOSE_INT_LITS
// for some reason the above list is posted in reverse order,
// don't want to disturb things so add the following (forward):
std::vector<std::pair<ConExpr*, AST::Node*> > domainConstraints2;
#endif
bool hadError;
std::ostream& err;
int fillBuffer(char* lexBuf, unsigned int lexBufSize) {
if (pos >= length) return 0;
int num = std::min(length - pos, lexBufSize);
memcpy(lexBuf,buf+pos,num);
pos += num;
return num;
}
void output(std::string x, AST::Node* n) {
_output.push_back(std::pair<std::string,AST::Node*>(x,n));
}
AST::Array* getOutput(void) {
std::sort(_output.begin(), _output.end());
AST::Array* a = new AST::Array();
for (unsigned int i=0; i<_output.size(); i++) {
a->a.push_back(new AST::String(_output[i].first+" = "));
if (_output[i].second->isArray()) {
AST::Array* oa = _output[i].second->getArray();
for (unsigned int j=0; j<oa->a.size(); j++) {
a->a.push_back(oa->a[j]);
oa->a[j] = NULL;
}
delete _output[i].second;
} else {
a->a.push_back(_output[i].second);
}
a->a.push_back(new AST::String(";\n"));
}
return a;
}
};
/// Exception class for FlatZinc errors
class Error {
private:
const std::string msg;
public:
Error(const std::string& where, const std::string& what)
: msg(where+": "+what) {}
const std::string& toString(void) const { return msg; }
};
void solve(const std::string& filename, std::ostream& err = std::cerr);
void solve(std::istream& is, std::ostream& err = std::cerr);
}
#endif