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 f2a1c4e389 Squashed 'software/mza/' content from commit f970a59b17
git-subtree-dir: software/mza
git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
2021-07-11 16:34:30 +10:00

1134 lines
42 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Guido Tack <guido.tack@monash.edu>
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <minizinc/astiterator.hh>
#include <minizinc/output.hh>
namespace MiniZinc {
void outputVarDecls(EnvI& env, Item* ci, Expression* e);
bool cannotUseRHSForOutput(EnvI& env, Expression* e,
std::unordered_set<FunctionI*>& seen_functions) {
if (e == NULL) return true;
class V : public EVisitor {
public:
EnvI& env;
std::unordered_set<FunctionI*>& seen_functions;
bool success;
V(EnvI& env0, std::unordered_set<FunctionI*>& seen_functions0)
: env(env0), seen_functions(seen_functions0), success(true) {}
/// Visit anonymous variable
void vAnonVar(const AnonVar&) { success = false; }
/// Visit array literal
void vArrayLit(const ArrayLit&) {}
/// Visit array access
void vArrayAccess(const ArrayAccess&) {}
/// Visit array comprehension
void vComprehension(const Comprehension&) {}
/// Visit if-then-else
void vITE(const ITE&) {}
/// Visit binary operator
void vBinOp(const BinOp&) {}
/// Visit unary operator
void vUnOp(const UnOp&) {}
/// Visit call
void vCall(Call& c) {
std::vector<Type> tv(c.n_args());
for (unsigned int i = c.n_args(); i--;) {
tv[i] = c.arg(i)->type();
tv[i].ti(Type::TI_PAR);
}
FunctionI* decl = env.output->matchFn(env, c.id(), tv, false);
Type t;
if (decl == NULL) {
FunctionI* origdecl = env.model->matchFn(env, c.id(), tv, false);
if (origdecl == NULL) {
throw FlatteningError(
env, c.loc(), "function " + c.id().str() + " is used in output, par version needed");
}
bool seen = (seen_functions.find(origdecl) != seen_functions.end());
if (seen) {
success = false;
} else {
seen_functions.insert(origdecl);
if (origdecl->e() && cannotUseRHSForOutput(env, origdecl->e(), seen_functions)) {
success = false;
} else {
if (!origdecl->from_stdlib()) {
decl = copy(env, env.cmap, origdecl)->cast<FunctionI>();
CollectOccurrencesE ce(env.output_vo, decl);
topDown(ce, decl->e());
topDown(ce, decl->ti());
for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]);
env.output->registerFn(env, decl);
env.output->addItem(decl);
outputVarDecls(env, origdecl, decl->e());
outputVarDecls(env, origdecl, decl->ti());
} else {
decl = origdecl;
}
c.decl(decl);
}
}
}
if (success) {
t = decl->rtype(env, tv, false);
if (!t.ispar()) success = false;
}
}
void vId(const Id& id) {}
/// Visit let
void vLet(const Let&) { success = false; }
/// Visit variable declaration
void vVarDecl(const VarDecl& vd) {}
/// Visit type inst
void vTypeInst(const TypeInst&) {}
/// Visit TIId
void vTIId(const TIId&) {}
/// Determine whether to enter node
bool enter(Expression* e) { return success; }
} _v(env, seen_functions);
topDown(_v, e);
return !_v.success;
}
bool cannotUseRHSForOutput(EnvI& env, Expression* e) {
std::unordered_set<FunctionI*> seen_functions;
return cannotUseRHSForOutput(env, e, seen_functions);
}
void removeIsOutput(VarDecl* vd) {
if (vd == NULL) return;
vd->ann().remove(constants().ann.output_var);
vd->ann().removeCall(constants().ann.output_array);
}
void copyOutput(EnvI& e) {
struct CopyOutput : public EVisitor {
EnvI& env;
CopyOutput(EnvI& env0) : env(env0) {}
void vId(Id& _id) { _id.decl(_id.decl()->flat()); }
void vCall(Call& c) {
std::vector<Type> tv(c.n_args());
for (unsigned int i = c.n_args(); i--;) {
tv[i] = c.arg(i)->type();
tv[i].ti(Type::TI_PAR);
}
FunctionI* decl = c.decl();
if (!decl->from_stdlib()) {
env.flat_addItem(decl);
}
}
};
if (OutputI* oi = e.model->outputItem()) {
GCLock lock;
OutputI* noi = copy(e, oi)->cast<OutputI>();
CopyOutput co(e);
topDown(co, noi->e());
e.flat_addItem(noi);
}
}
void cleanupOutput(EnvI& env) {
for (unsigned int i = 0; i < env.output->size(); i++) {
if (VarDeclI* vdi = (*env.output)[i]->dyn_cast<VarDeclI>()) {
vdi->e()->flat(NULL);
}
}
}
void makePar(EnvI& env, Expression* e) {
class OutputJSON : public EVisitor {
public:
EnvI& env;
OutputJSON(EnvI& env0) : env(env0) {}
void vCall(Call& c) {
if (c.id() == "outputJSON") {
bool outputObjective = (c.n_args() == 1 && eval_bool(env, c.arg(0)));
c.id(ASTString("array1d"));
Expression* json = copy(env, env.cmap, createJSONOutput(env, outputObjective, false));
std::vector<Expression*> new_args({json});
new_args[0]->type(Type::parstring(1));
c.args(new_args);
}
}
} _outputJSON(env);
topDown(_outputJSON, e);
class Par : public EVisitor {
public:
/// Visit variable declaration
void vVarDecl(VarDecl& vd) { vd.ti()->type(vd.type()); }
/// Determine whether to enter node
bool enter(Expression* e) {
Type t = e->type();
t.ti(Type::TI_PAR);
e->type(t);
return true;
}
} _par;
topDown(_par, e);
class Decls : public EVisitor {
protected:
static std::string createEnumToStringName(Id* ident, std::string prefix) {
std::string name = ident->str().str();
if (name[0] == '\'') {
name = "'" + prefix + name.substr(1);
} else {
name = prefix + name;
}
return name;
}
public:
EnvI& env;
Decls(EnvI& env0) : env(env0) {}
void vCall(Call& c) {
if (c.id() == "format" || c.id() == "show" || c.id() == "showDzn" || c.id() == "showJSON") {
int enumId = c.arg(c.n_args() - 1)->type().enumId();
if (enumId != 0 && c.arg(c.n_args() - 1)->type().dim() != 0) {
const std::vector<unsigned int>& enumIds = env.getArrayEnum(enumId);
enumId = enumIds[enumIds.size() - 1];
}
if (enumId > 0) {
Id* ti_id = env.getEnum(enumId)->e()->id();
GCLock lock;
std::vector<Expression*> args(3);
args[0] = c.arg(c.n_args() - 1);
if (args[0]->type().dim() > 1) {
std::vector<Expression*> a1dargs(1);
a1dargs[0] = args[0];
Call* array1d = new Call(Location().introduce(), ASTString("array1d"), a1dargs);
Type array1dt = args[0]->type();
array1dt.dim(1);
array1d->type(array1dt);
args[0] = array1d;
}
args[1] = constants().boollit(c.id() == "showDzn");
args[2] = constants().boollit(c.id() == "showJSON");
std::string enumName = createEnumToStringName(ti_id, "_toString_");
c.id(ASTString(enumName));
c.args(args);
}
if (c.id() == "showDzn" || (c.id() == "showJSON" && enumId > 0)) {
c.id(constants().ids.show);
}
}
c.decl(env.model->matchFn(env, &c, false));
}
} _decls(env);
topDown(_decls, e);
}
void checkRenameVar(EnvI& e, VarDecl* vd) {
if (vd->id()->idn() != vd->flat()->id()->idn()) {
TypeInst* vd_rename_ti = copy(e, e.cmap, vd->ti())->cast<TypeInst>();
VarDecl* vd_rename =
new VarDecl(Location().introduce(), vd_rename_ti, vd->flat()->id()->idn(), NULL);
vd_rename->flat(vd->flat());
makePar(e, vd_rename);
vd->e(vd_rename->id());
e.output->addItem(new VarDeclI(Location().introduce(), vd_rename));
}
}
class ClearAnnotations {
public:
/// Push all elements of \a v onto \a stack
template <class E>
static void pushVec(std::vector<Expression*>& stack, ASTExprVec<E> v) {
for (unsigned int i = 0; i < v.size(); i++) stack.push_back(v[i]);
}
static void run(Expression* root) {
std::vector<Expression*> stack;
stack.push_back(root);
while (!stack.empty()) {
Expression* e = stack.back();
stack.pop_back();
if (e == NULL) {
continue;
}
e->ann().clear();
switch (e->eid()) {
case Expression::E_INTLIT:
case Expression::E_FLOATLIT:
case Expression::E_BOOLLIT:
case Expression::E_STRINGLIT:
case Expression::E_ID:
case Expression::E_ANON:
case Expression::E_TIID:
break;
case Expression::E_SETLIT:
pushVec(stack, e->template cast<SetLit>()->v());
break;
case Expression::E_ARRAYLIT:
for (unsigned int i = 0; i < e->cast<ArrayLit>()->size(); i++) {
stack.push_back((*e->cast<ArrayLit>())[i]);
}
break;
case Expression::E_ARRAYACCESS:
pushVec(stack, e->template cast<ArrayAccess>()->idx());
stack.push_back(e->template cast<ArrayAccess>()->v());
break;
case Expression::E_COMP: {
Comprehension* comp = e->template cast<Comprehension>();
for (unsigned int i = comp->n_generators(); i--;) {
stack.push_back(comp->where(i));
stack.push_back(comp->in(i));
for (unsigned int j = comp->n_decls(i); j--;) {
stack.push_back(comp->decl(i, j));
}
}
stack.push_back(comp->e());
} break;
case Expression::E_ITE: {
ITE* ite = e->template cast<ITE>();
stack.push_back(ite->e_else());
for (int i = 0; i < ite->size(); i++) {
stack.push_back(ite->e_if(i));
stack.push_back(ite->e_then(i));
}
} break;
case Expression::E_BINOP:
stack.push_back(e->template cast<BinOp>()->rhs());
stack.push_back(e->template cast<BinOp>()->lhs());
break;
case Expression::E_UNOP:
stack.push_back(e->template cast<UnOp>()->e());
break;
case Expression::E_CALL:
for (unsigned int i = 0; i < e->template cast<Call>()->n_args(); i++)
stack.push_back(e->template cast<Call>()->arg(i));
break;
case Expression::E_VARDECL:
stack.push_back(e->template cast<VarDecl>()->e());
stack.push_back(e->template cast<VarDecl>()->ti());
break;
case Expression::E_LET:
stack.push_back(e->template cast<Let>()->in());
pushVec(stack, e->template cast<Let>()->let());
break;
case Expression::E_TI:
stack.push_back(e->template cast<TypeInst>()->domain());
pushVec(stack, e->template cast<TypeInst>()->ranges());
break;
}
}
}
};
void outputVarDecls(EnvI& env, Item* ci, Expression* e) {
class O : public EVisitor {
public:
EnvI& env;
Item* ci;
O(EnvI& env0, Item* ci0) : env(env0), ci(ci0) {}
void vId(Id& id) {
if (&id == constants().absent) return;
if (!id.decl()->toplevel()) return;
VarDecl* vd = id.decl();
VarDecl* reallyFlat = vd->flat();
while (reallyFlat != NULL && reallyFlat != reallyFlat->flat())
reallyFlat = reallyFlat->flat();
IdMap<int>::iterator idx =
reallyFlat ? env.output_vo_flat.idx.find(reallyFlat->id()) : env.output_vo_flat.idx.end();
IdMap<int>::iterator idx2 = env.output_vo.idx.find(vd->id());
if (idx == env.output_vo_flat.idx.end() && idx2 == env.output_vo.idx.end()) {
VarDeclI* nvi =
new VarDeclI(Location().introduce(), copy(env, env.cmap, vd)->cast<VarDecl>());
Type t = nvi->e()->ti()->type();
if (t.ti() != Type::TI_PAR) {
t.ti(Type::TI_PAR);
}
makePar(env, nvi->e());
nvi->e()->ti()->domain(NULL);
nvi->e()->flat(vd->flat());
ClearAnnotations::run(nvi->e());
nvi->e()->introduced(false);
if (reallyFlat) env.output_vo_flat.add_idx(reallyFlat, env.output->size());
env.output_vo.add_idx(nvi, env.output->size());
env.output_vo.add(nvi->e(), ci);
env.output->addItem(nvi);
IdMap<KeepAlive>::iterator it;
if ((it = env.reverseMappers.find(nvi->e()->id())) != env.reverseMappers.end()) {
Call* rhs = copy(env, env.cmap, it->second())->cast<Call>();
{
std::vector<Type> tv(rhs->n_args());
for (unsigned int i = rhs->n_args(); i--;) {
tv[i] = rhs->arg(i)->type();
tv[i].ti(Type::TI_PAR);
}
FunctionI* decl = env.output->matchFn(env, rhs->id(), tv, false);
Type t;
if (decl == NULL) {
FunctionI* origdecl = env.model->matchFn(env, rhs->id(), tv, false);
if (origdecl == NULL) {
throw FlatteningError(
env, rhs->loc(),
"function " + rhs->id().str() + " is used in output, par version needed");
}
if (!origdecl->from_stdlib()) {
decl = copy(env, env.cmap, origdecl)->cast<FunctionI>();
CollectOccurrencesE ce(env.output_vo, decl);
topDown(ce, decl->e());
topDown(ce, decl->ti());
for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]);
env.output->registerFn(env, decl);
env.output->addItem(decl);
} else {
decl = origdecl;
}
}
rhs->decl(decl);
}
outputVarDecls(env, nvi, it->second());
nvi->e()->e(rhs);
} else if (reallyFlat && cannotUseRHSForOutput(env, reallyFlat->e())) {
assert(nvi->e()->flat());
nvi->e()->e(NULL);
if (nvi->e()->type().dim() == 0) {
reallyFlat->addAnnotation(constants().ann.output_var);
} else {
std::vector<Expression*> args(reallyFlat->e()->type().dim());
for (unsigned int i = 0; i < args.size(); i++) {
if (nvi->e()->ti()->ranges()[i]->domain() == NULL) {
args[i] = new SetLit(Location().introduce(),
eval_intset(env, reallyFlat->ti()->ranges()[i]->domain()));
} else {
args[i] = new SetLit(Location().introduce(),
eval_intset(env, nvi->e()->ti()->ranges()[i]->domain()));
}
}
ArrayLit* al = new ArrayLit(Location().introduce(), args);
args.resize(1);
args[0] = al;
reallyFlat->addAnnotation(
new Call(Location().introduce(), constants().ann.output_array, args));
}
checkRenameVar(env, nvi->e());
} else {
outputVarDecls(env, nvi, nvi->e()->ti());
outputVarDecls(env, nvi, nvi->e()->e());
}
CollectOccurrencesE ce(env.output_vo, nvi);
topDown(ce, nvi->e());
}
}
} _o(env, ci);
topDown(_o, e);
}
void processDeletions(EnvI& e, std::vector<VarDecl*>& deletedFlatVarDecls) {
std::vector<VarDecl*> deletedVarDecls;
for (unsigned int i = 0; i < e.output->size(); i++) {
if (VarDeclI* vdi = (*e.output)[i]->dyn_cast<VarDeclI>()) {
if (!vdi->removed() && e.output_vo.occurrences(vdi->e()) == 0 &&
!vdi->e()->ann().contains(constants().ann.mzn_check_var) &&
!(vdi->e()->id()->idn() == -1 && vdi->e()->id()->v() == "_mzn_solution_checker")) {
CollectDecls cd(e.output_vo, deletedVarDecls, vdi);
topDown(cd, vdi->e()->e());
removeIsOutput(vdi->e()->flat());
if (e.output_vo.find(vdi->e()) != -1) e.output_vo.remove(vdi->e());
vdi->remove();
}
}
}
while (!deletedVarDecls.empty()) {
VarDecl* cur = deletedVarDecls.back();
deletedVarDecls.pop_back();
if (e.output_vo.occurrences(cur) == 0) {
IdMap<int>::iterator cur_idx = e.output_vo.idx.find(cur->id());
if (cur_idx != e.output_vo.idx.end()) {
VarDeclI* vdi = (*e.output)[cur_idx->second]->cast<VarDeclI>();
if (!vdi->removed()) {
CollectDecls cd(e.output_vo, deletedVarDecls, vdi);
topDown(cd, cur->e());
removeIsOutput(vdi->e()->flat());
if (e.output_vo.find(vdi->e()) != -1) e.output_vo.remove(vdi->e());
vdi->remove();
}
}
}
}
for (IdMap<VarOccurrences::Items>::iterator it = e.output_vo._m.begin();
it != e.output_vo._m.end(); ++it) {
std::vector<Item*> toRemove;
for (VarOccurrences::Items::iterator iit = it->second.begin(); iit != it->second.end(); ++iit) {
if ((*iit)->removed()) {
toRemove.push_back(*iit);
}
}
for (unsigned int i = 0; i < toRemove.size(); i++) {
it->second.erase(toRemove[i]);
}
}
}
void createDznOutputItem(EnvI& e, bool outputObjective, bool includeOutputItem) {
std::vector<Expression*> outputVars;
class DZNOVisitor : public ItemVisitor {
protected:
EnvI& e;
bool outputObjective;
bool includeOutputItem;
std::vector<Expression*>& outputVars;
bool had_add_to_output;
public:
DZNOVisitor(EnvI& e0, bool outputObjective0, bool includeOutputItem0,
std::vector<Expression*>& outputVars0)
: e(e0),
outputObjective(outputObjective0),
outputVars(outputVars0),
includeOutputItem(includeOutputItem0),
had_add_to_output(false) {}
void vVarDeclI(VarDeclI* vdi) {
VarDecl* vd = vdi->e();
bool process_var = false;
if (outputObjective && vd->id()->idn() == -1 && vd->id()->v() == "_objective") {
process_var = true;
} else {
if (vd->ann().contains(constants().ann.add_to_output)) {
if (!had_add_to_output) {
outputVars.clear();
}
had_add_to_output = true;
process_var = true;
} else {
if (!had_add_to_output) {
process_var = false;
if (vd->type().isvar()) {
if (vd->e()) {
if (ArrayLit* al = vd->e()->dyn_cast<ArrayLit>()) {
for (unsigned int i = 0; i < al->size(); i++) {
if ((*al)[i]->isa<AnonVar>()) {
process_var = true;
break;
}
}
} else if (vd->ann().contains(constants().ann.rhs_from_assignment)) {
process_var = true;
}
} else {
process_var = true;
}
}
}
}
}
if (process_var) {
std::ostringstream s;
s << vd->id()->str().str() << " = ";
if (vd->type().dim() > 0) {
ArrayLit* al = NULL;
if (vd->flat() && vd->flat()->e()) {
al = eval_array_lit(e, vd->flat()->e());
} else if (vd->e()) {
al = eval_array_lit(e, vd->e());
}
s << "array" << vd->type().dim() << "d(";
for (int i = 0; i < vd->type().dim(); i++) {
unsigned int enumId =
(vd->type().enumId() != 0 ? e.getArrayEnum(vd->type().enumId())[i] : 0);
if (enumId != 0) {
s << e.getEnum(enumId)->e()->id()->str() << ", ";
} else if (al != NULL) {
s << al->min(i) << ".." << al->max(i) << ", ";
} else {
IntSetVal* idxset = eval_intset(e, vd->ti()->ranges()[i]->domain());
s << *idxset << ", ";
}
}
}
StringLit* sl = new StringLit(Location().introduce(), s.str());
outputVars.push_back(sl);
std::vector<Expression*> showArgs(1);
showArgs[0] = vd->id();
Call* show = new Call(Location().introduce(), ASTString("showDzn"), showArgs);
show->type(Type::parstring());
FunctionI* fi = e.model->matchFn(e, show, false);
assert(fi);
show->decl(fi);
outputVars.push_back(show);
std::string ends = vd->type().dim() > 0 ? ")" : "";
ends += ";\n";
StringLit* eol = new StringLit(Location().introduce(), ends);
outputVars.push_back(eol);
}
}
void vOutputI(OutputI* oi) {
if (includeOutputItem) {
outputVars.push_back(new StringLit(Location().introduce(), "_output = "));
Call* concat = new Call(Location().introduce(), ASTString("concat"), {oi->e()});
concat->type(Type::parstring());
FunctionI* fi = e.model->matchFn(e, concat, false);
assert(fi);
concat->decl(fi);
Call* show = new Call(Location().introduce(), ASTString("showDzn"), {concat});
show->type(Type::parstring());
fi = e.model->matchFn(e, show, false);
assert(fi);
show->decl(fi);
outputVars.push_back(show);
outputVars.push_back(new StringLit(Location().introduce(), ";\n"));
}
oi->remove();
}
} dznov(e, outputObjective, includeOutputItem, outputVars);
iterItems(dznov, e.model);
OutputI* newOutputItem =
new OutputI(Location().introduce(), new ArrayLit(Location().introduce(), outputVars));
e.model->addItem(newOutputItem);
}
ArrayLit* createJSONOutput(EnvI& e, bool outputObjective, bool includeOutputItem) {
std::vector<Expression*> outputVars;
outputVars.push_back(new StringLit(Location().introduce(), "{\n"));
class JSONOVisitor : public ItemVisitor {
protected:
EnvI& e;
bool outputObjective;
bool includeOutputItem;
std::vector<Expression*>& outputVars;
bool had_add_to_output;
bool first_var;
public:
JSONOVisitor(EnvI& e0, bool outputObjective0, bool includeOutputItem0,
std::vector<Expression*>& outputVars0)
: e(e0),
outputObjective(outputObjective0),
outputVars(outputVars0),
includeOutputItem(includeOutputItem0),
had_add_to_output(false),
first_var(true) {}
void vVarDeclI(VarDeclI* vdi) {
VarDecl* vd = vdi->e();
bool process_var = false;
if (outputObjective && vd->id()->idn() == -1 && vd->id()->v() == "_objective") {
process_var = true;
} else {
if (vd->ann().contains(constants().ann.add_to_output)) {
if (!had_add_to_output) {
outputVars.clear();
outputVars.push_back(new StringLit(Location().introduce(), "{\n"));
first_var = true;
}
had_add_to_output = true;
process_var = true;
} else {
if (!had_add_to_output) {
process_var =
vd->type().isvar() &&
(vd->e() == NULL || vd->ann().contains(constants().ann.rhs_from_assignment));
}
}
}
if (process_var) {
std::ostringstream s;
if (first_var) {
first_var = false;
} else {
s << ",\n";
}
s << " \"" << vd->id()->str().str() << "\""
<< " : ";
StringLit* sl = new StringLit(Location().introduce(), s.str());
outputVars.push_back(sl);
std::vector<Expression*> showArgs(1);
showArgs[0] = vd->id();
Call* show = new Call(Location().introduce(), "showJSON", showArgs);
show->type(Type::parstring());
FunctionI* fi = e.model->matchFn(e, show, false);
assert(fi);
show->decl(fi);
outputVars.push_back(show);
}
}
void vOutputI(OutputI* oi) {
if (includeOutputItem) {
std::ostringstream s;
if (first_var) {
first_var = false;
} else {
s << ",\n";
}
s << " \"_output\""
<< " : ";
StringLit* sl = new StringLit(Location().introduce(), s.str());
outputVars.push_back(sl);
Call* concat = new Call(Location().introduce(), ASTString("concat"), {oi->e()});
concat->type(Type::parstring());
FunctionI* fi = e.model->matchFn(e, concat, false);
assert(fi);
concat->decl(fi);
Call* show = new Call(Location().introduce(), ASTString("showJSON"), {concat});
show->type(Type::parstring());
fi = e.model->matchFn(e, show, false);
assert(fi);
show->decl(fi);
outputVars.push_back(show);
}
oi->remove();
}
} jsonov(e, outputObjective, includeOutputItem, outputVars);
iterItems(jsonov, e.model);
outputVars.push_back(new StringLit(Location().introduce(), "\n}\n"));
return new ArrayLit(Location().introduce(), outputVars);
}
void createJSONOutputItem(EnvI& e, bool outputObjective, bool includeOutputItem) {
OutputI* newOutputItem =
new OutputI(Location().introduce(), createJSONOutput(e, outputObjective, includeOutputItem));
e.model->addItem(newOutputItem);
}
void createOutput(EnvI& e, std::vector<VarDecl*>& deletedFlatVarDecls,
FlatteningOptions::OutputMode outputMode, bool outputObjective,
bool includeOutputItem) {
// Create new output model
OutputI* outputItem = NULL;
GCLock lock;
switch (outputMode) {
case FlatteningOptions::OUTPUT_DZN:
createDznOutputItem(e, outputObjective, includeOutputItem);
break;
case FlatteningOptions::OUTPUT_JSON:
createJSONOutputItem(e, outputObjective, includeOutputItem);
default:
if (e.model->outputItem() == NULL) {
createDznOutputItem(e, outputObjective, false);
}
break;
}
// Copy output item from model into output model
outputItem = copy(e, e.cmap, e.model->outputItem())->cast<OutputI>();
makePar(e, outputItem->e());
e.output->addItem(outputItem);
// Copy all function definitions that are required for output into the output model
class CollectFunctions : public EVisitor {
public:
EnvI& env;
CollectFunctions(EnvI& env0) : env(env0) {}
bool enter(Expression* e) {
if (e->type().isvar()) {
Type t = e->type();
t.ti(Type::TI_PAR);
e->type(t);
}
return true;
}
void vCall(Call& c) {
std::vector<Type> tv(c.n_args());
for (unsigned int i = c.n_args(); i--;) {
tv[i] = c.arg(i)->type();
tv[i].ti(Type::TI_PAR);
}
FunctionI* decl = env.output->matchFn(env, c.id(), tv, false);
FunctionI* origdecl = env.model->matchFn(env, c.id(), tv, false);
bool canReuseDecl = (decl != nullptr);
if (canReuseDecl && origdecl) {
// Check if this is the exact same overloaded declaration as in the model
for (unsigned int i = 0; i < decl->params().size(); i++) {
if (decl->params()[i]->type() != origdecl->params()[i]->type()) {
// no, the types don't match, so we have to copy the original decl
canReuseDecl = false;
break;
}
}
}
Type t;
if (!canReuseDecl) {
if (origdecl == NULL || !origdecl->rtype(env, tv, false).ispar()) {
throw FlatteningError(
env, c.loc(), "function " + c.id().str() + " is used in output, par version needed");
}
if (!origdecl->from_stdlib()) {
decl = copy(env, env.cmap, origdecl)->cast<FunctionI>();
env.output->registerFn(env, decl);
env.output->addItem(decl);
if (decl->e()) {
makePar(env, decl->e());
topDown(*this, decl->e());
}
CollectOccurrencesE ce(env.output_vo, decl);
topDown(ce, decl->e());
topDown(ce, decl->ti());
for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]);
} else {
decl = origdecl;
}
}
c.decl(decl);
}
} _cf(e);
topDown(_cf, outputItem->e());
// If we are checking solutions using a checker model, all parameters of the checker model
// have to be made available in the output model
class OV1 : public ItemVisitor {
public:
EnvI& env;
CollectFunctions& _cf;
OV1(EnvI& env0, CollectFunctions& cf) : env(env0), _cf(cf) {}
void vVarDeclI(VarDeclI* vdi) {
if (vdi->e()->ann().contains(constants().ann.mzn_check_var)) {
VarDecl* output_vd = copy(env, env.cmap, vdi->e())->cast<VarDecl>();
topDown(_cf, output_vd);
}
}
} _ov1(e, _cf);
iterItems(_ov1, e.model);
// Copying the output item and the functions it depends on has created copies
// of all dependent VarDecls. However the output model does not contain VarDeclIs for
// these VarDecls yet. This iterator processes all variable declarations of the
// original model, and if they were copied (i.e., if the output model depends on them),
// the corresponding VarDeclI is created in the output model.
class OV2 : public ItemVisitor {
public:
EnvI& env;
OV2(EnvI& env0) : env(env0) {}
void vVarDeclI(VarDeclI* vdi) {
IdMap<int>::iterator idx = env.output_vo.idx.find(vdi->e()->id());
if (idx != env.output_vo.idx.end()) return;
if (Expression* vd_e = env.cmap.find(vdi->e())) {
// We found a copied VarDecl, now need to create a VarDeclI
VarDecl* vd = vd_e->cast<VarDecl>();
VarDeclI* vdi_copy = copy(env, env.cmap, vdi)->cast<VarDeclI>();
Type t = vdi_copy->e()->ti()->type();
t.ti(Type::TI_PAR);
vdi_copy->e()->ti()->domain(NULL);
vdi_copy->e()->flat(vdi->e()->flat());
bool isCheckVar = vdi_copy->e()->ann().contains(constants().ann.mzn_check_var);
Call* checkVarEnum = vdi_copy->e()->ann().getCall(constants().ann.mzn_check_enum_var);
vdi_copy->e()->ann().clear();
if (isCheckVar) {
vdi_copy->e()->ann().add(constants().ann.mzn_check_var);
}
if (checkVarEnum) {
vdi_copy->e()->ann().add(checkVarEnum);
}
vdi_copy->e()->introduced(false);
IdMap<KeepAlive>::iterator it;
if (!vdi->e()->type().ispar()) {
if (vd->flat() == NULL && vdi->e()->e() != NULL && vdi->e()->e()->type().ispar()) {
// Don't have a flat version of this variable, but the original has a right hand
// side that is par, so we can use that.
Expression* flate = eval_par(env, vdi->e()->e());
outputVarDecls(env, vdi_copy, flate);
vd->e(flate);
} else {
vd = follow_id_to_decl(vd->id())->cast<VarDecl>();
VarDecl* reallyFlat = vd->flat();
while (reallyFlat && reallyFlat != reallyFlat->flat()) reallyFlat = reallyFlat->flat();
if (reallyFlat == NULL) {
// The variable doesn't have a flat version. This can only happen if
// the original variable had type-inst var, but a right-hand-side that
// was par, so follow_id_to_decl lead to a par variable.
assert(vd->e() && vd->e()->type().ispar());
Expression* flate = eval_par(env, vd->e());
outputVarDecls(env, vdi_copy, flate);
vd->e(flate);
} else if (vd->flat()->e() && vd->flat()->e()->type().ispar()) {
// We can use the right hand side of the flat version of this variable
Expression* flate = copy(env, env.cmap, follow_id(reallyFlat->id()));
outputVarDecls(env, vdi_copy, flate);
vd->e(flate);
} else if ((it = env.reverseMappers.find(vd->id())) != env.reverseMappers.end()) {
// Found a reverse mapper, so we need to add the mapping function to the
// output model to map the FlatZinc value back to the model variable.
Call* rhs = copy(env, env.cmap, it->second())->cast<Call>();
{
std::vector<Type> tv(rhs->n_args());
for (unsigned int i = rhs->n_args(); i--;) {
tv[i] = rhs->arg(i)->type();
tv[i].ti(Type::TI_PAR);
}
FunctionI* decl = env.output->matchFn(env, rhs->id(), tv, false);
if (decl == NULL) {
FunctionI* origdecl = env.model->matchFn(env, rhs->id(), tv, false);
if (origdecl == NULL) {
throw FlatteningError(
env, rhs->loc(),
"function " + rhs->id().str() + " is used in output, par version needed");
}
if (!origdecl->from_stdlib()) {
decl = copy(env, env.cmap, origdecl)->cast<FunctionI>();
CollectOccurrencesE ce(env.output_vo, decl);
topDown(ce, decl->e());
topDown(ce, decl->ti());
for (unsigned int i = decl->params().size(); i--;)
topDown(ce, decl->params()[i]);
env.output->registerFn(env, decl);
env.output->addItem(decl);
} else {
decl = origdecl;
}
}
rhs->decl(decl);
}
outputVarDecls(env, vdi_copy, rhs);
vd->e(rhs);
} else if (cannotUseRHSForOutput(env, vd->e())) {
// If the VarDecl does not have a usable right hand side, it needs to be
// marked as output in the FlatZinc
vd->e(NULL);
assert(vd->flat());
if (vd->type().dim() == 0) {
vd->flat()->addAnnotation(constants().ann.output_var);
checkRenameVar(env, vd);
} else {
bool needOutputAnn = true;
if (reallyFlat->e() && reallyFlat->e()->isa<ArrayLit>()) {
ArrayLit* al = reallyFlat->e()->cast<ArrayLit>();
for (unsigned int i = 0; i < al->size(); i++) {
if (Id* id = (*al)[i]->dyn_cast<Id>()) {
if (env.reverseMappers.find(id) != env.reverseMappers.end()) {
needOutputAnn = false;
break;
}
}
}
if (!needOutputAnn) {
outputVarDecls(env, vdi_copy, al);
vd->e(copy(env, env.cmap, al));
}
}
if (needOutputAnn) {
std::vector<Expression*> args(vdi->e()->type().dim());
for (unsigned int i = 0; i < args.size(); i++) {
if (vdi->e()->ti()->ranges()[i]->domain() == NULL) {
args[i] =
new SetLit(Location().introduce(),
eval_intset(env, vd->flat()->ti()->ranges()[i]->domain()));
} else {
args[i] = new SetLit(Location().introduce(),
eval_intset(env, vd->ti()->ranges()[i]->domain()));
}
}
ArrayLit* al = new ArrayLit(Location().introduce(), args);
args.resize(1);
args[0] = al;
vd->flat()->addAnnotation(
new Call(Location().introduce(), constants().ann.output_array, args));
checkRenameVar(env, vd);
}
}
}
if (reallyFlat && env.output_vo_flat.find(reallyFlat) == -1)
env.output_vo_flat.add_idx(reallyFlat, env.output->size());
}
}
makePar(env, vdi_copy->e());
env.output_vo.add_idx(vdi_copy, env.output->size());
CollectOccurrencesE ce(env.output_vo, vdi_copy);
topDown(ce, vdi_copy->e());
env.output->addItem(vdi_copy);
}
}
} _ov2(e);
iterItems(_ov2, e.model);
CollectOccurrencesE ce(e.output_vo, outputItem);
topDown(ce, outputItem->e());
e.model->mergeStdLib(e, e.output);
processDeletions(e, deletedFlatVarDecls);
}
Expression* isFixedDomain(EnvI& env, VarDecl* vd) {
if (vd->type() != Type::varbool() && vd->type() != Type::varint() &&
vd->type() != Type::varfloat())
return NULL;
Expression* e = vd->ti()->domain();
if (e == constants().lit_true || e == constants().lit_false) return e;
if (SetLit* sl = Expression::dyn_cast<SetLit>(e)) {
if (sl->type().bt() == Type::BT_INT) {
IntSetVal* isv = eval_intset(env, sl);
return isv->min() == isv->max() ? IntLit::a(isv->min()) : NULL;
} else if (sl->type().bt() == Type::BT_FLOAT) {
FloatSetVal* fsv = eval_floatset(env, sl);
return fsv->min() == fsv->max() ? FloatLit::a(fsv->min()) : NULL;
}
}
return NULL;
}
void finaliseOutput(EnvI& e, std::vector<VarDecl*>& deletedFlatVarDecls) {
if (e.output->size() > 0) {
// Adapt existing output model
// (generated by repeated flattening)
e.output_vo.clear();
for (unsigned int i = 0; i < e.output->size(); i++) {
Item* item = (*e.output)[i];
if (item->removed()) continue;
switch (item->iid()) {
case Item::II_VD: {
VarDecl* vd = item->cast<VarDeclI>()->e();
IdMap<KeepAlive>::iterator it;
GCLock lock;
VarDecl* reallyFlat = vd->flat();
while (reallyFlat && reallyFlat != reallyFlat->flat()) reallyFlat = reallyFlat->flat();
if (vd->e() == NULL) {
if ((vd->flat()->e() && vd->flat()->e()->type().ispar()) ||
isFixedDomain(e, vd->flat())) {
VarDecl* reallyFlat = vd->flat();
while (reallyFlat != reallyFlat->flat()) reallyFlat = reallyFlat->flat();
removeIsOutput(reallyFlat);
Expression* flate;
if (Expression* fd = isFixedDomain(e, vd->flat())) {
flate = fd;
} else {
flate = copy(e, e.cmap, follow_id(reallyFlat->id()));
}
outputVarDecls(e, item, flate);
vd->e(flate);
} else if ((it = e.reverseMappers.find(vd->id())) != e.reverseMappers.end()) {
Call* rhs = copy(e, e.cmap, it->second())->cast<Call>();
std::vector<Type> tv(rhs->n_args());
for (unsigned int i = rhs->n_args(); i--;) {
tv[i] = rhs->arg(i)->type();
tv[i].ti(Type::TI_PAR);
}
FunctionI* decl = e.output->matchFn(e, rhs->id(), tv, false);
if (decl == NULL) {
FunctionI* origdecl = e.model->matchFn(e, rhs->id(), tv, false);
if (origdecl == NULL) {
throw FlatteningError(
e, rhs->loc(),
"function " + rhs->id().str() + " is used in output, par version needed");
}
if (!origdecl->from_stdlib()) {
decl = copy(e, e.cmap, origdecl)->cast<FunctionI>();
CollectOccurrencesE ce(e.output_vo, decl);
topDown(ce, decl->e());
topDown(ce, decl->ti());
for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]);
e.output->registerFn(e, decl);
e.output->addItem(decl);
} else {
decl = origdecl;
}
}
rhs->decl(decl);
removeIsOutput(reallyFlat);
if (e.vo.occurrences(reallyFlat) == 0 && reallyFlat->e() == NULL) {
deletedFlatVarDecls.push_back(reallyFlat);
}
outputVarDecls(e, item, it->second()->cast<Call>());
vd->e(rhs);
} else {
// If the VarDecl does not have a usable right hand side, it needs to be
// marked as output in the FlatZinc
assert(vd->flat());
bool needOutputAnn = true;
if (reallyFlat->e() && reallyFlat->e()->isa<ArrayLit>()) {
ArrayLit* al = reallyFlat->e()->cast<ArrayLit>();
for (unsigned int i = 0; i < al->size(); i++) {
if (Id* id = (*al)[i]->dyn_cast<Id>()) {
if (e.reverseMappers.find(id) != e.reverseMappers.end()) {
needOutputAnn = false;
break;
}
}
}
if (!needOutputAnn) {
removeIsOutput(vd);
removeIsOutput(reallyFlat);
if (e.vo.occurrences(reallyFlat) == 0) {
deletedFlatVarDecls.push_back(reallyFlat);
}
outputVarDecls(e, item, al);
vd->e(copy(e, e.cmap, al));
}
}
if (needOutputAnn) {
if (!isOutput(vd->flat())) {
GCLock lock;
if (vd->type().dim() == 0) {
vd->flat()->addAnnotation(constants().ann.output_var);
} else {
std::vector<Expression*> args(vd->type().dim());
for (unsigned int i = 0; i < args.size(); i++) {
if (vd->ti()->ranges()[i]->domain() == NULL) {
args[i] =
new SetLit(Location().introduce(),
eval_intset(e, vd->flat()->ti()->ranges()[i]->domain()));
} else {
args[i] = new SetLit(Location().introduce(),
eval_intset(e, vd->ti()->ranges()[i]->domain()));
}
}
ArrayLit* al = new ArrayLit(Location().introduce(), args);
args.resize(1);
args[0] = al;
vd->flat()->addAnnotation(
new Call(Location().introduce(), constants().ann.output_array, args));
}
checkRenameVar(e, vd);
}
}
}
vd->flat(NULL);
// Remove enum type
Type vdt = vd->type();
vdt.enumId(0);
vd->type(vdt);
vd->ti()->type(vdt);
}
e.output_vo.add_idx(item->cast<VarDeclI>(), i);
CollectOccurrencesE ce(e.output_vo, item);
topDown(ce, vd);
} break;
case Item::II_OUT: {
CollectOccurrencesE ce(e.output_vo, item);
topDown(ce, item->cast<OutputI>()->e());
} break;
case Item::II_FUN: {
CollectOccurrencesE ce(e.output_vo, item);
topDown(ce, item->cast<FunctionI>()->e());
topDown(ce, item->cast<FunctionI>()->ti());
for (unsigned int i = item->cast<FunctionI>()->params().size(); i--;)
topDown(ce, item->cast<FunctionI>()->params()[i]);
} break;
default:
throw FlatteningError(e, item->loc(), "invalid item in output model");
}
}
}
processDeletions(e, deletedFlatVarDecls);
}
} // namespace MiniZinc