From 7c418d0c3dc7d3b2f12fe0b0e9284450f8c4b89a Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 6 Oct 2017 13:46:58 +1100 Subject: [PATCH 01/34] Fix lookup tables for variables in output model. Flat versions of variables used to be recorded in the same lookup table (in order to find the output variable when two flat variables are unified), but that could interfere with creating output variables of the same names. --- include/minizinc/flatten_internal.hh | 1 + include/minizinc/optimize.hh | 4 ++-- lib/flatten.cpp | 2 +- lib/optimize.cpp | 14 +++++++------- lib/output.cpp | 16 ++++++++-------- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/minizinc/flatten_internal.hh b/include/minizinc/flatten_internal.hh index 5fdfc7a2c..212cc8555 100644 --- a/include/minizinc/flatten_internal.hh +++ b/include/minizinc/flatten_internal.hh @@ -69,6 +69,7 @@ namespace MiniZinc { Model* output; VarOccurrences vo; VarOccurrences output_vo; + VarOccurrences output_vo_flat; CopyMap cmap; IdMap reverseMappers; struct WW { diff --git a/include/minizinc/optimize.hh b/include/minizinc/optimize.hh index f2771808e..c7fab1459 100644 --- a/include/minizinc/optimize.hh +++ b/include/minizinc/optimize.hh @@ -25,9 +25,9 @@ namespace MiniZinc { IdMap idx; /// Add \a to the index - void add(VarDeclI* i, int idx_i); + void add_idx(VarDeclI* i, int idx_i); /// Add \a to the index - void add(VarDecl* e, int idx_i); + void add_idx(VarDecl* e, int idx_i); /// Find index of \a vd int find(VarDecl* vd); /// Remove index of \a vd diff --git a/lib/flatten.cpp b/lib/flatten.cpp index efba1a9fb..32c529dd4 100644 --- a/lib/flatten.cpp +++ b/lib/flatten.cpp @@ -262,7 +262,7 @@ namespace MiniZinc { { VarDeclI* vd = i->cast(); toAnnotate = vd->e()->e(); - vo.add(vd, _flat->size()-1); + vo.add_idx(vd, _flat->size()-1); toAdd = vd->e(); break; } diff --git a/lib/optimize.cpp b/lib/optimize.cpp index 2543ed4f8..9d70632d0 100644 --- a/lib/optimize.cpp +++ b/lib/optimize.cpp @@ -22,11 +22,11 @@ namespace MiniZinc { - void VarOccurrences::add(VarDeclI *i, int idx_i) + void VarOccurrences::add_idx(VarDeclI *i, int idx_i) { idx.insert(i->e()->id(), idx_i); } - void VarOccurrences::add(VarDecl *e, int idx_i) + void VarOccurrences::add_idx(VarDecl *e, int idx_i) { assert(find(e) == -1); idx.insert(e->id(), idx_i); @@ -208,10 +208,10 @@ namespace MiniZinc { // If both variables are output variables, unify them in the output model if (isOutput(id0->decl())) { - assert(env.output_vo.find(id0->decl()) != -1); - VarDecl* id0_output = (*env.output)[env.output_vo.find(id0->decl())]->cast()->e(); - assert(env.output_vo.find(id1->decl()) != -1); - VarDecl* id1_output = (*env.output)[env.output_vo.find(id1->decl())]->cast()->e(); + assert(env.output_vo_flat.find(id0->decl()) != -1); + VarDecl* id0_output = (*env.output)[env.output_vo_flat.find(id0->decl())]->cast()->e(); + assert(env.output_vo_flat.find(id1->decl()) != -1); + VarDecl* id1_output = (*env.output)[env.output_vo_flat.find(id1->decl())]->cast()->e(); if (id0_output->e() == NULL) { id0_output->e(id1_output->id()); } @@ -760,7 +760,7 @@ namespace MiniZinc { } } if (val) { - VarDecl* vd_out = (*envi.output)[envi.output_vo.find(cur)]->cast()->e(); + VarDecl* vd_out = (*envi.output)[envi.output_vo_flat.find(cur)]->cast()->e(); vd_out->e(val); CollectDecls cd(envi.vo,deletedVarDecls,m[cur_idx->second]->cast()); topDown(cd,cur->e()); diff --git a/lib/output.cpp b/lib/output.cpp index 019c26aaa..c52ecfc5e 100644 --- a/lib/output.cpp +++ b/lib/output.cpp @@ -323,9 +323,9 @@ namespace MiniZinc { VarDecl* reallyFlat = vd->flat(); while (reallyFlat != NULL && reallyFlat != reallyFlat->flat()) reallyFlat = reallyFlat->flat(); - IdMap::iterator idx = reallyFlat ? env.output_vo.idx.find(reallyFlat->id()) : env.output_vo.idx.end(); + IdMap::iterator idx = reallyFlat ? env.output_vo_flat.idx.find(reallyFlat->id()) : env.output_vo_flat.idx.end(); IdMap::iterator idx2 = env.output_vo.idx.find(vd->id()); - if (idx==env.output_vo.idx.end() && idx2==env.output_vo.idx.end()) { + 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()); Type t = nvi->e()->ti()->type(); if (t.ti() != Type::TI_PAR) { @@ -337,8 +337,8 @@ namespace MiniZinc { ClearAnnotations::run(nvi->e()); nvi->e()->introduced(false); if (reallyFlat) - env.output_vo.add(reallyFlat, env.output->size()); - env.output_vo.add(nvi, env.output->size()); + 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); @@ -778,12 +778,12 @@ namespace MiniZinc { } } } - if (env.output_vo.find(reallyFlat) == -1) - env.output_vo.add(reallyFlat, env.output->size()); + if (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(vdi_copy, env.output->size()); + 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); @@ -913,7 +913,7 @@ namespace MiniZinc { } vd->flat(NULL); } - e.output_vo.add(item->cast(), i); + e.output_vo.add_idx(item->cast(), i); CollectOccurrencesE ce(e.output_vo,item); topDown(ce, vd); } From 4c38fa27e26737fead584126a32508c77451621c Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 6 Oct 2017 15:01:35 +1100 Subject: [PATCH 02/34] Fix enum type inference for array literals with empty sets as their first arguments. Fixes #180. --- lib/typecheck.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/typecheck.cpp b/lib/typecheck.cpp index d885af894..4d9cfc0a6 100644 --- a/lib/typecheck.cpp +++ b/lib/typecheck.cpp @@ -760,16 +760,13 @@ namespace MiniZinc { if (ty.st() != vi->type().st()) { throw TypeError(_env,al.loc(),"non-uniform array literal"); } - if (ty.enumId() != vi->type().enumId()) { - ty.enumId(0); - } } else { haveInferredType = true; ty.st(vi->type().st()); - ty.enumId(vi->type().enumId()); } if (vi->type().bt() != Type::BT_BOT) { ty.bt(vi->type().bt()); + ty.enumId(vi->type().enumId()); } } } else { From 9aa1173caad0a6fc6c28b0401842d776c6dd16ea Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 6 Oct 2017 17:04:11 +1100 Subject: [PATCH 03/34] Fix incorrect simplification of float domain constraints. Fixes #159. --- include/minizinc/flatten_internal.hh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/minizinc/flatten_internal.hh b/include/minizinc/flatten_internal.hh index 212cc8555..73cf07a19 100644 --- a/include/minizinc/flatten_internal.hh +++ b/include/minizinc/flatten_internal.hh @@ -341,8 +341,7 @@ namespace MiniZinc { FloatSetVal* ndomain; switch (bot) { case BOT_LE: - v -= 1; - // fall through + return NULL; case BOT_LQ: { Ranges::Bounded b = Ranges::Bounded::maxiter(dr,v); @@ -350,8 +349,7 @@ namespace MiniZinc { } break; case BOT_GR: - v += 1; - // fall through + return NULL; case BOT_GQ: { Ranges::Bounded b = Ranges::Bounded::miniter(dr,v); From f2998f8cf02ae31810449ff41448a1492f386596 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 5 Oct 2017 15:09:12 +1100 Subject: [PATCH 04/34] values.h should include cmath, otherwise FloatVal::ceil calls itself --- include/minizinc/values.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/include/minizinc/values.hh b/include/minizinc/values.hh index aba2185ad..8464ff759 100644 --- a/include/minizinc/values.hh +++ b/include/minizinc/values.hh @@ -21,6 +21,7 @@ #include #include #include +#include namespace MiniZinc { class IntVal; From 5ed56088be2e719b57d614f4f5440f25f3b6e3fd Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Wed, 11 Oct 2017 17:31:33 +1100 Subject: [PATCH 05/34] nosets.mzn: standard decompositions for set operations: added superset --- share/minizinc/std/nosets.mzn | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/minizinc/std/nosets.mzn b/share/minizinc/std/nosets.mzn index 8e8e61535..77c52db69 100644 --- a/share/minizinc/std/nosets.mzn +++ b/share/minizinc/std/nosets.mzn @@ -176,6 +176,11 @@ predicate set_subset_reif(var set of int: x, var set of int: y, var bool: b) = else bx[i] -> by[i] endif ); +%%% Map the subset operation to superset +predicate set_superset(var set of int: x, var set of int: y) = set_subset( y, x ); +predicate set_superset_reif(var set of int: x, var set of int: y, var bool: b) = + set_subset_reif( y, x, b ); + function var set of int: set_intersect(var set of int: x, var set of int: y) ::promise_total = let { array[int] of var bool: bx = set2bools(x); From 0d8ff07ba364b821886d9bad881d80bed267055a Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Fri, 13 Oct 2017 17:58:45 +1100 Subject: [PATCH 06/34] MIP: a simplification of var_element for boolean index, much faster on CBC --- share/minizinc/linear/redefinitions.mzn | 3 +++ 1 file changed, 3 insertions(+) diff --git a/share/minizinc/linear/redefinitions.mzn b/share/minizinc/linear/redefinitions.mzn index 02c4111df..d06d45401 100644 --- a/share/minizinc/linear/redefinitions.mzn +++ b/share/minizinc/linear/redefinitions.mzn @@ -353,6 +353,9 @@ predicate array_var_float_element(var int: i00, array[int] of var float: a, z <= maxUB /\ if is_fixed(i00) then z==a[fix(i00)] elseif minLB==maxUB then true + elseif ub(i00)-lb(i00)==1 /*2==card( dom( i00 ) )*/ then + aux_float_eq_if_1(z, a[lb(i00)], (ub(i00)-i00)) /\ + aux_float_eq_if_1(z, a[ub(i00)], (i00-lb(i00))) else let { array[int] of var int: p = eq_encode(i00), From 993abd5dc2cf4a7b5f907dcdd173f9ca16892bcf Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 12 Oct 2017 13:43:47 +1100 Subject: [PATCH 07/34] MIP: Gurobi plugin: --dll specifies Gurobi dll name, -h shows default list --- solvers/MIP/MIP_gurobi_wrap.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/solvers/MIP/MIP_gurobi_wrap.cpp b/solvers/MIP/MIP_gurobi_wrap.cpp index 9356ea35b..10d688072 100644 --- a/solvers/MIP/MIP_gurobi_wrap.cpp +++ b/solvers/MIP/MIP_gurobi_wrap.cpp @@ -54,6 +54,8 @@ string MIP_WrapperFactory::getVersion( ) { return oss.str(); } +static const vector sGurobiDLLs = { "gurobi75", "gurobi70", "gurobi65" }; + void MIP_WrapperFactory::printHelp(ostream& os) { os << "GUROBI MIP wrapper options:" << std::endl @@ -71,11 +73,13 @@ void MIP_WrapperFactory::printHelp(ostream& os) { << "--writeParam write GUROBI parameters to file" << std::endl // << "--tuneParam instruct GUROBI to tune parameters instead of solving NOT IMPL" - << "--absGap absolute gap |primal-dual| to stop" << std::endl + << "\n--absGap absolute gap |primal-dual| to stop" << std::endl << "--relGap relative gap |primal-dual|/ to stop. Default 1e-8, set <0 to use backend's default" << std::endl << "--intTol integrality tolerance for a variable. Default 1e-6" << std::endl // << "--objDiff objective function discretization. Default 1.0" << std::endl + << "\n--dll Gurobi DLL base name, such as gurobi75, when using plugin. Default range tried: " + << sGurobiDLLs.front() << " .. " << sGurobiDLLs.back() << std::endl << std::endl; } @@ -97,6 +101,8 @@ void MIP_WrapperFactory::printHelp(ostream& os) { static double intTol=1e-6; static double objDiff=1.0; + static string sGurobiDLL; + bool MIP_WrapperFactory::processOption(int& i, int argc, const char** argv) { MiniZinc::CLOParser cop( i, argc, argv ); if ( string(argv[i])=="-a" @@ -114,6 +120,7 @@ bool MIP_WrapperFactory::processOption(int& i, int argc, const char** argv) { } else if ( cop.get( "--absGap", &absGap ) ) { } else if ( cop.get( "--relGap", &relGap ) ) { } else if ( cop.get( "--intTol", &intTol ) ) { + } else if ( cop.get( "--dll", &sGurobiDLL ) ) { // } else if ( cop.get( "--objDiff", &objDiff ) ) { } else return false; @@ -169,14 +176,19 @@ namespace { void MIP_gurobi_wrapper::openGUROBI() { #ifdef HAS_GUROBI_PLUGIN - - gurobi_dll = dll_open("gurobi70"); - if (gurobi_dll==NULL) { - gurobi_dll = dll_open("gurobi65"); + + if ( sGurobiDLL.size() ) { + gurobi_dll = dll_open( sGurobiDLL.c_str() ); + } else { + for( const auto& s: sGurobiDLLs ) { + gurobi_dll = dll_open( s.c_str() ); + if ( NULL!=gurobi_dll ) + break; + } } if (gurobi_dll==NULL) { - throw MiniZinc::InternalError("cannot load gurobi dll"); + throw MiniZinc::InternalError("cannot load gurobi dll, specify --dll"); } *(void**)(&dll_GRBaddconstr) = dll_sym(gurobi_dll, "GRBaddconstr"); From 42bcc7c52606608dc5d482cc4d7356967906da81 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 19 Oct 2017 13:24:57 +1100 Subject: [PATCH 08/34] Modif of 0a79886: simplify element only for true boolean indices. Best for Gurobi at least --- share/minizinc/linear/redefinitions.mzn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/minizinc/linear/redefinitions.mzn b/share/minizinc/linear/redefinitions.mzn index d06d45401..7b3f18c30 100644 --- a/share/minizinc/linear/redefinitions.mzn +++ b/share/minizinc/linear/redefinitions.mzn @@ -353,7 +353,7 @@ predicate array_var_float_element(var int: i00, array[int] of var float: a, z <= maxUB /\ if is_fixed(i00) then z==a[fix(i00)] elseif minLB==maxUB then true - elseif ub(i00)-lb(i00)==1 /*2==card( dom( i00 ) )*/ then + elseif {0,1}==dom(i00) /*ub(i00)-lb(i00)==1*/ /*2==card( dom( i00 ) )*/ then aux_float_eq_if_1(z, a[lb(i00)], (ub(i00)-i00)) /\ aux_float_eq_if_1(z, a[ub(i00)], (i00-lb(i00))) else From b7fbea9bfe4ba46fe8d4a70cbafb974944d623ee Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 20 Oct 2017 12:00:39 +1100 Subject: [PATCH 09/34] Fix garbage collection of WeakRefs (could sometimes contain stale pointers). --- lib/gc.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/gc.cpp b/lib/gc.cpp index ed8afda36..50c4e93d9 100644 --- a/lib/gc.cpp +++ b/lib/gc.cpp @@ -494,8 +494,8 @@ namespace MiniZinc { fixPrev = false; WeakRef* p = wr->_p; removeWeakRef(p); - p->_n = p; - p->_p = p; + p->_n = NULL; + p->_p = NULL; } if ((*wr)() && (*wr)()->_gc_mark==0) { wr->_e = NULL; @@ -768,7 +768,7 @@ namespace MiniZinc { GC::gc()->addWeakRef(this); } WeakRef::~WeakRef(void) { - if ((_e && !_e->isUnboxedInt()) || !_valid) + if (_e && !_e->isUnboxedInt()) GC::gc()->removeWeakRef(this); } WeakRef::WeakRef(const WeakRef& e) : _e(e()), _p(NULL), _n(NULL), _valid(true) { @@ -777,17 +777,22 @@ namespace MiniZinc { } WeakRef& WeakRef::operator =(const WeakRef& e) { - if ((_e && !_e->isUnboxedInt()) || !_valid) { + // Test if this WeakRef is currently active in the GC + bool isActive = (_e && !_e->isUnboxedInt()); + if (isActive) { + // Yes, active WeakRef. + // If after assigning WeakRef should be inactive, remove it. if (e()==NULL || e()->isUnboxedInt()) { GC::gc()->removeWeakRef(this); _n = _p = NULL; } - } else { - if (e()!=NULL && !e()->isUnboxedInt()) - GC::gc()->addWeakRef(this); } _e = e(); _valid = true; + + // If this WeakRef was not active but now should be, add it + if (!isActive && _e!=NULL && !_e->isUnboxedInt()) + GC::gc()->addWeakRef(this); return *this; } From 2f51c85e04c3dae8c84e4e088a5fe2c177c4160d Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Thu, 26 Oct 2017 18:10:23 +1100 Subject: [PATCH 10/34] Perform array bounds computation inside the main flattening loop (declarations are topologically sorted anyway). Also fixes a bug where copies of variable declarations may have been introduced when a later VarDecl referred to an earlier VarDecl (e.g. using index_set) that had not been flattened yet. --- lib/flatten.cpp | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/flatten.cpp b/lib/flatten.cpp index 32c529dd4..2f49ec9ae 100644 --- a/lib/flatten.cpp +++ b/lib/flatten.cpp @@ -5308,14 +5308,17 @@ namespace MiniZinc { onlyRangeDomains = eval_bool(e.envi(), check_only_range); } - class ExpandArrayDecls : public ItemVisitor { + bool hadSolveItem = false; + // Flatten main model + class FV : public ItemVisitor { public: EnvI& env; - ExpandArrayDecls(EnvI& env0) : env(env0) {} + bool& hadSolveItem; + FV(EnvI& env0, bool& hadSolveItem0) : env(env0), hadSolveItem(hadSolveItem0) {} + bool enter(Item* i) { + return !(i->isa() && env.failed()); + } void vVarDeclI(VarDeclI* v) { - if (v->e()->type().isvar() && v->e()->type().dim() > 0 && v->e()->e() == NULL) { - (void) flat_exp(env,Ctx(),v->e()->id(),NULL,constants().var_true); - } if (v->e()->type().ispar() && v->e()->type().dim() > 0 && v->e()->ti()->domain()==NULL && (v->e()->type().bt()==Type::BT_INT || v->e()->type().bt()==Type::BT_FLOAT)) { // Compute bounds for array literals @@ -5345,21 +5348,6 @@ namespace MiniZinc { v->e()->ti()->setComputedDomain(true); } } - } - } _ead(env); - iterItems(_ead,e.model());; - - bool hadSolveItem = false; - // Flatten main model - class FV : public ItemVisitor { - public: - EnvI& env; - bool& hadSolveItem; - FV(EnvI& env0, bool& hadSolveItem0) : env(env0), hadSolveItem(hadSolveItem0) {} - bool enter(Item* i) { - return !(i->isa() && env.failed()); - } - void vVarDeclI(VarDeclI* v) { if (v->e()->type().isvar() || v->e()->type().isann()) { (void) flat_exp(env,Ctx(),v->e()->id(),NULL,constants().var_true); } else { From 50765355b2724a34748d9ed460a560ddecadeda8 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 27 Oct 2017 16:14:04 +1100 Subject: [PATCH 11/34] Use non-reified redefinition for array_bool_clause (avoids introducing ::defines_var annotations that were keeping bool vars alive in the linearised FlatZinc). --- lib/flatten.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/flatten.cpp b/lib/flatten.cpp index 2f49ec9ae..cc72b4153 100644 --- a/lib/flatten.cpp +++ b/lib/flatten.cpp @@ -5680,6 +5680,13 @@ namespace MiniZinc { nc->type(Type::varbool()); nc->decl(array_bool_and); } + } else if (isTrueVar && c->id() == constants().ids.clause && array_bool_clause) { + std::vector args(2); + args[0] = c->args()[0]; + args[1] = c->args()[1]; + nc = new Call(c->loc().introduce(),array_bool_clause->id(),args); + nc->type(Type::varbool()); + nc->decl(array_bool_clause); } else if (c->id() == constants().ids.clause && array_bool_clause_reif) { std::vector args(3); args[0] = c->args()[0]; @@ -5722,6 +5729,10 @@ namespace MiniZinc { CollectDecls cd(env.vo,deletedVarDecls,vdi); topDown(cd,c); vd->e(NULL); + /// TODO: check if removing variables here makes sense: +// if (!isOutput(vd) && env.vo.occurrences(vd)==0) { +// removedItems.push_back(vdi); +// } if (nc != c) { vd->addAnnotation(constants().ann.is_defined_var); nc->addAnnotation(definesVarAnn(vd->id())); From 99d82711b25f8fb250b3ce438566a64f1a1dcadc Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Mon, 30 Oct 2017 16:59:15 +1100 Subject: [PATCH 12/34] Use queue instead of stack for constant propagation (this is so obvious!). --- lib/optimize.cpp | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/optimize.cpp b/lib/optimize.cpp index 9d70632d0..297fd09b3 100644 --- a/lib/optimize.cpp +++ b/lib/optimize.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace MiniZinc { @@ -223,27 +224,27 @@ namespace MiniZinc { void substituteFixedVars(EnvI& env, Item* ii, std::vector& deletedVarDecls); void simplifyBoolConstraint(EnvI& env, Item* ii, VarDecl* vd, bool& remove, - std::vector& vardeclQueue, - std::vector& constraintQueue, + std::deque& vardeclQueue, + std::deque& constraintQueue, std::vector& toRemove, UNORDERED_NAMESPACE::unordered_map& nonFixedLiteralCount); bool simplifyConstraint(EnvI& env, Item* ii, std::vector& deletedVarDecls, - std::vector& constraintQueue, - std::vector& vardeclQueue); + std::deque& constraintQueue, + std::deque& vardeclQueue); - void pushVarDecl(EnvI& env, VarDeclI* vdi, int vd_idx, std::vector& q) { + void pushVarDecl(EnvI& env, VarDeclI* vdi, int vd_idx, std::deque& q) { if (!vdi->removed() && !vdi->flag()) { vdi->flag(true); q.push_back(vd_idx); } } - void pushVarDecl(EnvI& env, int vd_idx, std::vector& q) { + void pushVarDecl(EnvI& env, int vd_idx, std::deque& q) { pushVarDecl(env, (*env.flat())[vd_idx]->cast(), vd_idx, q); } - void pushDependentConstraints(EnvI& env, Id* id, std::vector& q) { + void pushDependentConstraints(EnvI& env, Id* id, std::deque& q) { IdMap::iterator it = env.vo._m.find(id->decl()->id()); if (it != env.vo._m.end()) { for (VarOccurrences::Items::iterator item = it->second.begin(); item != it->second.end(); ++item) { @@ -276,8 +277,8 @@ namespace MiniZinc { std::vector toRemoveConstraints; std::vector deletedVarDecls; - std::vector constraintQueue; - std::vector vardeclQueue; + std::deque constraintQueue; + std::deque vardeclQueue; std::vector boolConstraints; @@ -516,8 +517,8 @@ namespace MiniZinc { UNORDERED_NAMESPACE::unordered_map nonFixedLiteralCount; while (!vardeclQueue.empty() || !constraintQueue.empty()) { while (!vardeclQueue.empty()) { - int var_idx = vardeclQueue.back(); - vardeclQueue.pop_back(); + int var_idx = vardeclQueue.front(); + vardeclQueue.pop_front(); m[var_idx]->cast()->flag(false); VarDecl* vd = m[var_idx]->cast()->e(); @@ -618,8 +619,8 @@ namespace MiniZinc { } bool handledConstraint = false; while (!handledConstraint && !constraintQueue.empty()) { - Item* item = constraintQueue.back(); - constraintQueue.pop_back(); + Item* item = constraintQueue.front(); + constraintQueue.pop_front(); Call* c; ArrayLit* al = NULL; if (ConstraintI* ci = item->dyn_cast()) { @@ -857,8 +858,9 @@ namespace MiniZinc { bool simplifyConstraint(EnvI& env, Item* ii, std::vector& deletedVarDecls, - std::vector& constraintQueue, - std::vector& vardeclQueue) { + std::deque& constraintQueue, + std::deque& vardeclQueue) { + Expression* con_e; bool is_true; bool is_false; @@ -1217,8 +1219,8 @@ namespace MiniZinc { } void simplifyBoolConstraint(EnvI& env, Item* ii, VarDecl* vd, bool& remove, - std::vector& vardeclQueue, - std::vector& constraintQueue, + std::deque& vardeclQueue, + std::deque& constraintQueue, std::vector& toRemove, UNORDERED_NAMESPACE::unordered_map& nonFixedLiteralCount) { if (ii->isa()) { From 46b302814b8dc666a4f69bdef111d0a7323e5201 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Thu, 2 Nov 2017 09:00:18 +1100 Subject: [PATCH 13/34] Use std::string instead of ASTString for symbol table in solns2out (could otherwise lead to GC problems) --- include/minizinc/solns2out.hh | 2 +- lib/solns2out_class.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/minizinc/solns2out.hh b/include/minizinc/solns2out.hh index b074794bf..8f1bd7689 100644 --- a/include/minizinc/solns2out.hh +++ b/include/minizinc/solns2out.hh @@ -44,7 +44,7 @@ namespace MiniZinc { Model* pOutput=0; typedef std::pair DE; - ASTStringMap::t declmap; + std::unordered_map declmap; Expression* outputExpr = NULL; bool fNewSol2Print = false; // should be set for evalOutput to work diff --git a/lib/solns2out_class.cpp b/lib/solns2out_class.cpp index 33a18b353..c258eafed 100644 --- a/lib/solns2out_class.cpp +++ b/lib/solns2out_class.cpp @@ -104,7 +104,8 @@ bool Solns2Out::initFromEnv(Env* pE) { void Solns2Out::createOutputMap() { for (unsigned int i=0; isize(); i++) { if (VarDeclI* vdi = (*getModel())[i]->dyn_cast()) { - declmap.insert(pair(vdi->e()->id()->str(),DE(vdi->e(),vdi->e()->e()))); + GCLock lock; + declmap.insert(pair(vdi->e()->id()->str().str(),DE(vdi->e(),vdi->e()->e()))); } else if (OutputI* oi = (*getModel())[i]->dyn_cast()) { MZN_ASSERT_HARD_MSG( outputExpr == oi->e(), "solns2out_base: <=1 output items allowed currently TODO?" ); @@ -116,7 +117,7 @@ Solns2Out::DE& Solns2Out::findOutputVar( ASTString id ) { declNewOutput(); if ( declmap.empty() ) createOutputMap(); - auto it = declmap.find( id ); + auto it = declmap.find( id.str() ); MZN_ASSERT_HARD_MSG( declmap.end()!=it, "solns2out_base: unexpected id in output: " << id ); return it->second; @@ -125,6 +126,7 @@ Solns2Out::DE& Solns2Out::findOutputVar( ASTString id ) { void Solns2Out::restoreDefaults() { for (unsigned int i=0; isize(); i++) { if (VarDeclI* vdi = (*getModel())[i]->dyn_cast()) { + GCLock lock; auto& de = findOutputVar(vdi->e()->id()->str()); vdi->e()->e(de.second()); vdi->e()->evaluated(false); From 42518217f70aa742dc3a0059117ae31723f50862 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 9 Nov 2017 13:24:58 +1100 Subject: [PATCH 14/34] #include for g++ 6.3.0 --- include/minizinc/solns2out.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/include/minizinc/solns2out.hh b/include/minizinc/solns2out.hh index 8f1bd7689..ba977f9b9 100644 --- a/include/minizinc/solns2out.hh +++ b/include/minizinc/solns2out.hh @@ -19,6 +19,7 @@ #include #include #include +#include #include #include From fa5bce999b0ebbc4350b722318b41349bc32bd7f Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 9 Nov 2017 16:32:41 +1100 Subject: [PATCH 15/34] MIP: corrected array_(var_)set_element --- share/minizinc/linear/redefinitions.mzn | 63 ++++++++++++++++++------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/share/minizinc/linear/redefinitions.mzn b/share/minizinc/linear/redefinitions.mzn index 7b3f18c30..dcb96bdae 100644 --- a/share/minizinc/linear/redefinitions.mzn +++ b/share/minizinc/linear/redefinitions.mzn @@ -354,8 +354,8 @@ predicate array_var_float_element(var int: i00, array[int] of var float: a, if is_fixed(i00) then z==a[fix(i00)] elseif minLB==maxUB then true elseif {0,1}==dom(i00) /*ub(i00)-lb(i00)==1*/ /*2==card( dom( i00 ) )*/ then - aux_float_eq_if_1(z, a[lb(i00)], (ub(i00)-i00)) /\ - aux_float_eq_if_1(z, a[ub(i00)], (i00-lb(i00))) + aux_float_eq_if_1(z, a[lb(i00)], (ub(i00)-i00)) /\ + aux_float_eq_if_1(z, a[ub(i00)], (i00-lb(i00))) else let { array[int] of var int: p = eq_encode(i00), @@ -432,28 +432,59 @@ predicate array_var_float_element__XBZ_lb(array[int] of var float: x, array[int] % Set constraints predicate array_var_set_element(var int: x, array[int] of var set of int: y, var set of int: z) = -% let { -% int: yLB = min([min(ub[i]]), -% } in + let { + constraint x in index_set( y ); + set of int: sUB = array_union( [ ub(y[i]) | i in dom(x) ] ); + set of int: sLB = array_intersect( [ lb(y[i]) | i in dom(x) ] ); + constraint trace( "set[]: \(x), \(y), \(z)\n" ); + } in forall (k in ub(z)) ( - if k in array_union(y) then - set2bools(z)[k] == - [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in index_set(y) ][x] + set2bools(z)[k] == + if k in sUB then + if k in sLB then + true + else + array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + else + false + endif + ) /\ + forall (k in sUB diff ub(z))( + if k in sLB then + false %% fail the constraint else - set2bools(z)[k] == false + not array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] endif ); predicate array_set_element(var int: x, array[int] of set of int: y, var set of int: z) = -% let { -% int: yLB = min([min(ub[i]]), -% } in + let { + constraint x in index_set( y ); + set of int: sUB = array_union( [ y[i] | i in dom(x) ] ); + set of int: sLB = array_intersect( [ y[i] | i in dom(x) ] ); + } in forall (k in ub(z)) ( - if k in array_union(y) then - set2bools(z)[k] == - [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in index_set(y) ][x] + set2bools(z)[k] == + if k in sUB then + if k in sLB then + true + else + array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + else + false + endif + ) /\ + forall (k in sUB diff ub(z))( + if k in sLB then + false %% fail the constraint else - set2bools(z)[k] == false + not array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] endif ); From 0497fd2014618b39b321aba3a04339f8466f805d Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 9 Nov 2017 16:37:52 +1100 Subject: [PATCH 16/34] array_(var_)set_element for no-sets decompositon: moved from MIP into std/nosets.mzn --- share/minizinc/linear/redefinitions.mzn | 58 ------------------------- share/minizinc/std/nosets.mzn | 58 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/share/minizinc/linear/redefinitions.mzn b/share/minizinc/linear/redefinitions.mzn index dcb96bdae..6db069ed9 100644 --- a/share/minizinc/linear/redefinitions.mzn +++ b/share/minizinc/linear/redefinitions.mzn @@ -430,64 +430,6 @@ predicate array_var_float_element__XBZ_lb(array[int] of var float: x, array[int] %-----------------------------------------------------------------------------% % Set constraints - -predicate array_var_set_element(var int: x, array[int] of var set of int: y, var set of int: z) = - let { - constraint x in index_set( y ); - set of int: sUB = array_union( [ ub(y[i]) | i in dom(x) ] ); - set of int: sLB = array_intersect( [ lb(y[i]) | i in dom(x) ] ); - constraint trace( "set[]: \(x), \(y), \(z)\n" ); - } in - forall (k in ub(z)) ( - set2bools(z)[k] == - if k in sUB then - if k in sLB then - true - else - array1d( lb(x)..ub(x), - [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] - endif - else - false - endif - ) /\ - forall (k in sUB diff ub(z))( - if k in sLB then - false %% fail the constraint - else - not array1d( lb(x)..ub(x), - [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] - endif - ); - -predicate array_set_element(var int: x, array[int] of set of int: y, var set of int: z) = - let { - constraint x in index_set( y ); - set of int: sUB = array_union( [ y[i] | i in dom(x) ] ); - set of int: sLB = array_intersect( [ y[i] | i in dom(x) ] ); - } in - forall (k in ub(z)) ( - set2bools(z)[k] == - if k in sUB then - if k in sLB then - true - else - array1d( lb(x)..ub(x), - [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] - endif - else - false - endif - ) /\ - forall (k in sUB diff ub(z))( - if k in sLB then - false %% fail the constraint - else - not array1d( lb(x)..ub(x), - [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] - endif - ); - %% ----------------------------------------------- (NO) SETS ---------------------------------------------- % XXX only for a fixed set here, general see below. % Normally not called because all plugged into the domain. diff --git a/share/minizinc/std/nosets.mzn b/share/minizinc/std/nosets.mzn index 77c52db69..c445f4ff7 100644 --- a/share/minizinc/std/nosets.mzn +++ b/share/minizinc/std/nosets.mzn @@ -256,5 +256,63 @@ function array[int] of var bool: setarray2bools(array[int] of var set of int: x) endif; %% Par version no sense + +predicate array_var_set_element(var int: x, array[int] of var set of int: y, var set of int: z) = + let { + constraint x in index_set( y ); + set of int: sUB = array_union( [ ub(y[i]) | i in dom(x) ] ); + set of int: sLB = array_intersect( [ lb(y[i]) | i in dom(x) ] ); + constraint trace( "set[]: \(x), \(y), \(z)\n" ); + } in + forall (k in ub(z)) ( + set2bools(z)[k] == + if k in sUB then + if k in sLB then + true + else + array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + else + false + endif + ) /\ + forall (k in sUB diff ub(z))( + if k in sLB then + false %% fail the constraint + else + not array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + ); + +predicate array_set_element(var int: x, array[int] of set of int: y, var set of int: z) = + let { + constraint x in index_set( y ); + set of int: sUB = array_union( [ y[i] | i in dom(x) ] ); + set of int: sLB = array_intersect( [ y[i] | i in dom(x) ] ); + } in + forall (k in ub(z)) ( + set2bools(z)[k] == + if k in sUB then + if k in sLB then + true + else + array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + else + false + endif + ) /\ + forall (k in sUB diff ub(z))( + if k in sLB then + false %% fail the constraint + else + not array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + ); + annotation set_search(array[int] of var set of int: x, ann: a1, ann: a2, ann: a3) = bool_search(setarray2bools(x),a1,a2,a3); From 05a842b97ebb4eb85e96c92f6e9f20bcdb848dec Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Fri, 10 Nov 2017 09:43:59 +1100 Subject: [PATCH 17/34] Removed output from nosets::array_var_set_element --- share/minizinc/std/nosets.mzn | 1 - 1 file changed, 1 deletion(-) diff --git a/share/minizinc/std/nosets.mzn b/share/minizinc/std/nosets.mzn index c445f4ff7..88a9bcb36 100644 --- a/share/minizinc/std/nosets.mzn +++ b/share/minizinc/std/nosets.mzn @@ -262,7 +262,6 @@ predicate array_var_set_element(var int: x, array[int] of var set of int: y, var constraint x in index_set( y ); set of int: sUB = array_union( [ ub(y[i]) | i in dom(x) ] ); set of int: sLB = array_intersect( [ lb(y[i]) | i in dom(x) ] ); - constraint trace( "set[]: \(x), \(y), \(z)\n" ); } in forall (k in ub(z)) ( set2bools(z)[k] == From 60fca6189d0e8489ee9678cef561232ac2318f13 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Fri, 17 Nov 2017 13:05:33 +1100 Subject: [PATCH 18/34] MIP: CBC: obj value offset for intermediate solutions --- solvers/MIP/MIP_osicbc_wrap.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/solvers/MIP/MIP_osicbc_wrap.cpp b/solvers/MIP/MIP_osicbc_wrap.cpp index a3a39a9d8..97369dc04 100644 --- a/solvers/MIP/MIP_osicbc_wrap.cpp +++ b/solvers/MIP/MIP_osicbc_wrap.cpp @@ -420,10 +420,10 @@ MyEventHandler3::event(CbcEvent whichEvent) // Trying to obtain solution for the original model: assert( model_ && model_->solver() ); - double objOffset=0; - model_->solver()->getDblParam(OsiObjOffset, objOffset); - double objVal = (model_->getObjValue() - objOffset); - double bestBnd = (model_->getBestPossibleObjValue() - objOffset); + // double objOffset=0; + // model_->solver()->getDblParam(OsiObjOffset, objOffset); + double objVal = (model_->getObjValue() ); //- objOffset); John Forrest suggested to remove, 17.11.17 + double bestBnd = (model_->getBestPossibleObjValue() ); //- objOffset); if ( 0!=cbcPreProcessPointer ) { if ( OsiSolverInterface* cbcPreOrig = cbcPreProcessPointer->originalModel() ) { objVal *= cbcPreOrig->getObjSense(); @@ -1078,4 +1078,4 @@ could easily be made available) John Forrest - */ \ No newline at end of file + */ From 19083bad34aed29f959550754ebeb20ccd937ce7 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Thu, 23 Nov 2017 09:47:23 +1100 Subject: [PATCH 19/34] Fix for WeakRef garbage collection (final WeakRef wasn't cleaned up) --- lib/gc.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/gc.cpp b/lib/gc.cpp index 50c4e93d9..9a33c8716 100644 --- a/lib/gc.cpp +++ b/lib/gc.cpp @@ -489,20 +489,26 @@ namespace MiniZinc { } bool fixPrev = false; + WeakRef* prevWr = NULL; for (WeakRef* wr = _weakRefs; wr != NULL; wr = wr->next()) { if (fixPrev) { fixPrev = false; - WeakRef* p = wr->_p; - removeWeakRef(p); - p->_n = NULL; - p->_p = NULL; + removeWeakRef(prevWr); + prevWr->_n = NULL; + prevWr->_p = NULL; } if ((*wr)() && (*wr)()->_gc_mark==0) { wr->_e = NULL; wr->_valid = false; fixPrev = true; + prevWr = wr; } } + if (fixPrev) { + removeWeakRef(prevWr); + prevWr->_n = NULL; + prevWr->_p = NULL; + } for (ASTNodeWeakMap* wr = _nodeWeakMaps; wr != NULL; wr = wr->next()) { std::vector toRemove; From 0984946a432f5a0daed41d3b580da4c55ac0fd6b Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Thu, 23 Nov 2017 09:48:10 +1100 Subject: [PATCH 20/34] Delete the model when destroying the Flattener --- lib/flattener.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/flattener.cpp b/lib/flattener.cpp index 1de072237..18364343f 100644 --- a/lib/flattener.cpp +++ b/lib/flattener.cpp @@ -190,6 +190,7 @@ Flattener::~Flattener() if(is_flatzinc) { pEnv->swap(); } + delete pEnv->model(); } From eff362cfba5956e348611dab5649d6a86eb49278 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 24 Nov 2017 08:52:15 +1100 Subject: [PATCH 21/34] Add more defines_var annotations to standard library decompositions --- share/minizinc/std/builtins.mzn | 116 ++++++++++++++++---------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/share/minizinc/std/builtins.mzn b/share/minizinc/std/builtins.mzn index 825ae1b45..15ba3f5f8 100644 --- a/share/minizinc/std/builtins.mzn +++ b/share/minizinc/std/builtins.mzn @@ -240,8 +240,8 @@ function $$E: max(set of $$E: x); /** @group builtins.arithmetic Return maximum of \a x and \a y */ function var int: max(var int: x, var int: y) :: promise_total = - let { var max(lb(x),lb(y))..max(ub(x),ub(y)): m; - constraint int_max(x,y,m); + let { var max(lb(x),lb(y))..max(ub(x),ub(y)): m ::is_defined_var; + constraint int_max(x,y,m) ::defines_var(m); } in m; /** @group builtins.arithmetic Return maximum of elements in array \a x */ @@ -253,8 +253,8 @@ function var int: max(array[$U] of var int: x) = /** @group builtins.arithmetic Return minimum of \a x and \a y */ function var int: min(var int: x, var int: y) :: promise_total = - let { var min(lb(x),lb(y))..min(ub(x),ub(y)): m; - constraint int_min(x,y,m); + let { var min(lb(x),lb(y))..min(ub(x),ub(y)): m ::is_defined_var; + constraint int_min(x,y,m) ::defines_var(m); } in m; /** @group builtins.arithmetic Return minimum of elements in array \a x */ @@ -269,8 +269,8 @@ function var int: min(array[$U] of var int: x) = /** @group builtins.arithmetic Return maximum of \a x and \a y */ function var float: max(var float: x, var float: y) :: promise_total = - let { var float: m; - constraint float_max(x,y,m); + let { var float: m ::is_defined_var; + constraint float_max(x,y,m) ::defines_var(m); } in m; /** @group builtins.arithmetic Return maximum of elements in array \a x */ @@ -282,8 +282,8 @@ function var float: max(array[$U] of var float: x) = /** @group builtins.arithmetic Return minimum of \a x and \a y */ function var float: min(var float: x, var float: y) :: promise_total = - let { var float: m; - constraint float_min(x,y,m); + let { var float: m ::is_defined_var; + constraint float_min(x,y,m) ::defines_var(m); } in m; /** @group builtins.arithmetic Return minimum of elements in array \a x */ @@ -309,8 +309,8 @@ function int: abs(int: x); /** @group builtins.arithmetic Return absolute value of \a x */ function var int: abs(var int: x) :: promise_total = if has_bounds(x) /\ lb(x) >= 0 then x else - let { var 0..max(-lb(x),ub(x)): m; - constraint int_abs(x,m); + let { var 0..max(-lb(x),ub(x)): m ::is_defined_var; + constraint int_abs(x,m) ::defines_var(m); } in m endif; @@ -320,14 +320,14 @@ function float: abs(float: x); function var float: abs(var float: x) :: promise_total = if has_bounds(x) then if lb(x) >= 0.0 then x else - let { var 0.0..max(-lb(x),ub(x)): m; - constraint float_abs(x,m); + let { var 0.0..max(-lb(x),ub(x)): m ::is_defined_var; + constraint float_abs(x,m) ::defines_var(m); } in m endif else - let { var float: m; + let { var float: m ::is_defined_var; constraint m >= 0.0; - constraint float_abs(x,m); + constraint float_abs(x,m) ::defines_var(m); } in m endif; @@ -358,8 +358,8 @@ function var int: pow(var int: x, var int: y) = } in if yy = 0 then 1 elseif yy = 1 then x else - let { var int: r; - constraint int_pow(x,y,r); + let { var int: r ::is_defined_var; + constraint int_pow(x,y,r) ::defines_var(r); } in r endif; @@ -374,8 +374,8 @@ function var float: pow(var float: x, var float: y) = } in if yy = 0.0 then 1.0 elseif yy = 1.0 then x else - let { var float: r; - constraint float_pow(x,y,r); + let { var float: r ::is_defined_var; + constraint float_pow(x,y,r) ::defines_var(r); } in r endif; @@ -390,8 +390,8 @@ function float: exp(float: x); /** @group builtins.explog Return \(e ^ {\a x}\) */ function var float: exp(var float: x) ::promise_total = let { - var float: r; - constraint float_exp(x,r); + var float: r ::is_defined_var; + constraint float_exp(x,r) ::defines_var(r); } in r; /** @group builtins.explog Return \(\ln \a x\) */ @@ -399,8 +399,8 @@ function float: ln(float: x); /** @group builtins.explog Return \(\ln \a x\) */ function var float: ln(var float: x) ::promise_total = let { - var float: r; - constraint float_ln(x,r); + var float: r ::is_defined_var; + constraint float_ln(x,r) ::defines_var(r); } in r; /** @group builtins.explog Return \(\log_{10} \a x\) */ @@ -408,8 +408,8 @@ function float: log10(float: x); /** @group builtins.explog Return \(\log_{10} \a x\) */ function var float: log10(var float: x) ::promise_total = let { - var float: r; - constraint float_log10(x,r); + var float: r ::is_defined_var; + constraint float_log10(x,r) ::defines_var(r); } in r; /** @group builtins.explog Return \(\log_{2} \a x\) */ @@ -417,8 +417,8 @@ function float: log2(float: x); /** @group builtins.explog Return \(\log_{2} \a x\) */ function var float: log2(var float: x) ::promise_total = let { - var float: r; - constraint float_log2(x,r); + var float: r ::is_defined_var; + constraint float_log2(x,r) ::defines_var(r); } in r; /** @group builtins.explog Return \(\log_{\a x} \a y\) */ @@ -435,8 +435,8 @@ function float: sin(float: x); /** @group builtins.trigonometric Return \(\sin \a x\) */ function var float: sin(var float: x) ::promise_total = let { - var -1.0..1.0: r; - constraint float_sin(x,r); + var -1.0..1.0: r ::is_defined_var; + constraint float_sin(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\cos \a x\) */ @@ -444,8 +444,8 @@ function float: cos(float: x); /** @group builtins.trigonometric Return \(\cos \a x\) */ function var float: cos(var float: x) ::promise_total = let { - var -1.0..1.0: r; - constraint float_cos(x,r); + var -1.0..1.0: r ::is_defined_var; + constraint float_cos(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\tan \a x\) */ @@ -453,8 +453,8 @@ function float: tan(float: x); /** @group builtins.trigonometric Return \(\tan \a x\) */ function var float: tan(var float: x) ::promise_total = let { - var float: r; - constraint float_tan(x,r); + var float: r ::is_defined_var; + constraint float_tan(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\mbox{asin}\ \a x\) */ @@ -462,8 +462,8 @@ function float: asin(float: x); /** @group builtins.trigonometric Return \(\mbox{asin}\ \a x\) */ function var float: asin(var float: x) ::promise_total = let { - var float: r; - constraint float_asin(x,r); + var float: r ::is_defined_var; + constraint float_asin(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\mbox{acos}\ \a x\) */ @@ -471,8 +471,8 @@ function float: acos(float: x); /** @group builtins.trigonometric Return \(\mbox{acos}\ \a x\) */ function var float: acos(var float: x) ::promise_total = let { - var float: r; - constraint float_acos(x,r); + var float: r ::is_defined_var; + constraint float_acos(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\mbox{atan}\ \a x\) */ @@ -480,8 +480,8 @@ function float: atan(float: x); /** @group builtins.trigonometric Return \(\mbox{atan}\ \a x\) */ function var float: atan(var float: x) ::promise_total = let { - var float: r; - constraint float_atan(x,r); + var float: r ::is_defined_var; + constraint float_atan(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\sinh \a x\) */ @@ -489,8 +489,8 @@ function float: sinh(float: x); /** @group builtins.trigonometric Return \(\sinh \a x\) */ function var float: sinh(var float: x) ::promise_total = let { - var float: r; - constraint float_sinh(x,r); + var float: r ::is_defined_var; + constraint float_sinh(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\cosh \a x\) */ @@ -498,8 +498,8 @@ function float: cosh(float: x); /** @group builtins.trigonometric Return \(\cosh \a x\) */ function var float: cosh(var float: x) ::promise_total = let { - var float: r; - constraint float_cosh(x,r); + var float: r ::is_defined_var; + constraint float_cosh(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\tanh \a x\) */ @@ -507,8 +507,8 @@ function float: tanh(float: x); /** @group builtins.trigonometric Return \(\tanh \a x\) */ function var float: tanh(var float: x) ::promise_total = let { - var float: r; - constraint float_tanh(x,r); + var float: r ::is_defined_var; + constraint float_tanh(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\mbox{asinh}\ \a x\) */ @@ -516,8 +516,8 @@ function float: asinh(float: x); /** @group builtins.trigonometric Return \(\mbox{asinh}\ \a x\) */ function var float: asinh(var float: x) ::promise_total = let { - var float: r; - constraint float_asinh(x,r); + var float: r ::is_defined_var; + constraint float_asinh(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\mbox{acosh}\ \a x\) */ @@ -525,8 +525,8 @@ function float: acosh(float: x); /** @group builtins.trigonometric Return \(\mbox{acosh}\ \a x\) */ function var float: acosh(var float: x) ::promise_total = let { - var float: r; - constraint float_acosh(x,r); + var float: r ::is_defined_var; + constraint float_acosh(x,r) ::defines_var(r); } in r; /** @group builtins.trigonometric Return \(\mbox{atanh}\ \a x\) */ @@ -534,8 +534,8 @@ function float: atanh(float: x); /** @group builtins.trigonometric Return \(\mbox{atanh}\ \a x\) */ function var float: atanh(var float: x) ::promise_total = let { - var float: r; - constraint float_atanh(x,r); + var float: r ::is_defined_var; + constraint float_atanh(x,r) ::defines_var(r); } in r; @@ -2001,8 +2001,8 @@ function var int: max_t(array[int] of var int: x) :: promise_total = elseif length(x)=1 then x[min(index_set(x))] elseif length(x)=2 then max(x[1],x[2]) else let { - var lb_array(x)..ub_array(x): m; - constraint array_int_maximum(m,x); + var lb_array(x)..ub_array(x): m ::is_defined_var; + constraint array_int_maximum(m,x) ::defines_var(m); } in m endif; @@ -2011,8 +2011,8 @@ function var int: min_t(array[int] of var int: x) :: promise_total = elseif length(x)=1 then x[1] elseif length(x)=2 then min(x[1],x[2]) else let { - var lb_array(x)..ub_array(x): m; - constraint array_int_minimum(m,x); + var lb_array(x)..ub_array(x): m ::is_defined_var; + constraint array_int_minimum(m,x) ::defines_var(m); } in m endif; @@ -2021,8 +2021,8 @@ function var float: max_t(array[int] of var float: x) :: promise_total = elseif length(x)=1 then x[min(index_set(x))] elseif length(x)=2 then min(x[1],x[2]) else let { - var float: m; - constraint array_float_maximum(m,x); + var float: m ::is_defined_var; + constraint array_float_maximum(m,x) ::defines_var(m); } in m endif; @@ -2031,8 +2031,8 @@ function var float: min_t(array[int] of var float: x) :: promise_total = elseif length(x)=1 then x[1] elseif length(x)=2 then min(x[1],x[2]) else let { - var float: m; - constraint array_float_minimum(m,x); + var float: m ::is_defined_var; + constraint array_float_minimum(m,x) ::defines_var(m); } in m endif; From a5901049e3d1cffae74ce918988f78b274535853 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 24 Nov 2017 10:28:31 +1100 Subject: [PATCH 22/34] Create dummy return values for functions that have an undefined result (previously flattening would crash on NULL return values). Fixes #187. --- lib/flatten.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/flatten.cpp b/lib/flatten.cpp index cc72b4153..0d597dffd 100644 --- a/lib/flatten.cpp +++ b/lib/flatten.cpp @@ -905,6 +905,37 @@ namespace MiniZinc { return true; } + Expression* createDummyValue(EnvI& env, const Type& t) { + if (t.dim()>0) { + Expression* ret = new ArrayLit(Location().introduce(), std::vector()); + Type ret_t = t; + ret_t.ti(Type::TI_PAR); + ret->type(ret_t); + return ret; + } + if (t.st()==Type::ST_SET) { + Expression* ret = new SetLit(Location().introduce(), std::vector()); + Type ret_t = t; + ret_t.ti(Type::TI_PAR); + ret->type(ret_t); + return ret; + } + switch (t.bt()) { + case Type::BT_INT: + return IntLit::a(0); + case Type::BT_BOOL: + return constants().boollit(false); + case Type::BT_FLOAT: + return FloatLit::a(0); + case Type::BT_STRING: + return new StringLit(Location().introduce(), ""); + case Type::BT_ANN: + return constants().ann.promise_total; + default: + return NULL; + } + } + KeepAlive bind(EnvI& env, Ctx ctx, VarDecl* vd, Expression* e) { assert(e==NULL || !e->isa()); if (vd==constants().var_ignore) @@ -3032,6 +3063,7 @@ namespace MiniZinc { ret.r = bind(env,ctx,r,eval_par(env,e)); ret.b = bind(env,Ctx(),b,constants().lit_true); } catch (ResultUndefinedError&) { + ret.r = createDummyValue(env, e->type()); ret.b = bind(env,Ctx(),b,constants().lit_false); } return ret; @@ -4980,6 +5012,7 @@ namespace MiniZinc { ret.r = bind(env,ctx,r,eval_par(env,cr_c)); ret.b = conj(env,b,Ctx(),args_ee); } catch (ResultUndefinedError&) { + ret.r = createDummyValue(env, cr_c->type()); ret.b = bind(env,Ctx(),b,constants().lit_false); return ret; } From 0e1a3b3cb89580edbf6e0053b1223e4ec8c92dc1 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Tue, 28 Nov 2017 15:52:51 +1100 Subject: [PATCH 23/34] #include in typecheck.cpp --- lib/typecheck.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/typecheck.cpp b/lib/typecheck.cpp index 4d9cfc0a6..0cff20ee2 100644 --- a/lib/typecheck.cpp +++ b/lib/typecheck.cpp @@ -18,6 +18,7 @@ #include #include +#include #include From 094b4fd411c3e84531f17342cb017724fb2b1326 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Thu, 30 Nov 2017 13:43:23 +1100 Subject: [PATCH 24/34] Add missing deopt builtins for any par type --- lib/builtins.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/builtins.cpp b/lib/builtins.cpp index b62b98b1a..2021ede87 100644 --- a/lib/builtins.cpp +++ b/lib/builtins.cpp @@ -264,6 +264,42 @@ namespace MiniZinc { throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); return eval_bool(env,e); } + + FloatVal b_deopt_float(EnvI& env, Call* call) { + ASTExprVec args = call->args(); + GCLock lock; + Expression* e = eval_par(env,args[0]); + if (e==constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_float(env,e); + } + + IntSetVal* b_deopt_intset(EnvI& env, Call* call) { + ASTExprVec args = call->args(); + GCLock lock; + Expression* e = eval_par(env,args[0]); + if (e==constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_intset(env,e); + } + + std::string b_deopt_string(EnvI& env, Call* call) { + ASTExprVec args = call->args(); + GCLock lock; + Expression* e = eval_par(env,args[0]); + if (e==constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_string(env,e); + } + + Expression* b_deopt_expr(EnvI& env, Call* call) { + ASTExprVec args = call->args(); + GCLock lock; + Expression* e = eval_par(env,args[0]); + if (e==constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return e; + }; IntVal b_array_lb_int(EnvI& env, Call* call) { ASTExprVec args = call->args(); @@ -2635,11 +2671,20 @@ namespace MiniZinc { std::vector t(1); t[0] = Type::parint(); t[0].ot(Type::OT_OPTIONAL); + t[0].bt(Type::BT_TOP); rb(env, m, ASTString("occurs"), t, b_occurs); + rb(env, m, ASTString("deopt"), t, b_deopt_expr); + t[0].bt(Type::BT_INT); rb(env, m, ASTString("deopt"), t, b_deopt_int); t[0].bt(Type::BT_BOOL); - rb(env, m, ASTString("occurs"), t, b_occurs); rb(env, m, ASTString("deopt"), t, b_deopt_bool); + t[0].bt(Type::BT_FLOAT); + rb(env, m, ASTString("deopt"), t, b_deopt_float); + t[0].bt(Type::BT_STRING); + rb(env, m, ASTString("deopt"), t, b_deopt_string); + t[0].bt(Type::BT_INT); + t[0].st(Type::ST_SET); + rb(env, m, ASTString("deopt"), t, b_deopt_intset); } { std::vector t(2); From 043c2dad524afd6966023940c556c0adf2b4d848 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 1 Dec 2017 17:22:59 +1100 Subject: [PATCH 25/34] Generate function _toString_ENUM(array[$U] of set of ENUM: x, bool: b) (the version for sets was previously missing) --- lib/typecheck.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/lib/typecheck.cpp b/lib/typecheck.cpp index 0cff20ee2..0e08f35bf 100644 --- a/lib/typecheck.cpp +++ b/lib/typecheck.cpp @@ -381,6 +381,91 @@ namespace MiniZinc { ti_fi,fi_params,bopp1); enumItems->addItem(fi); } + + { + /* + + function _toString_ENUM(array[$U] of set of ENUM: x, bool: b) = + let { + array[int] of set of ENUM: xx = array1d(x) + } in "[" ++ join(", ", [ _toString_ENUM(xx[i],b) | i in index_set(xx) ]) ++ "]"; + + */ + + TIId* tiid = new TIId(Location().introduce(),"U"); + TypeInst* ti_range = new TypeInst(Location().introduce(),Type::parint(),tiid); + std::vector ranges(1); + ranges[0] = ti_range; + + TypeInst* x_ti = new TypeInst(Location().introduce(),Type::parsetint(-1),ranges,ident); + VarDecl* vd_x = new VarDecl(Location().introduce(),x_ti,"x"); + vd_x->toplevel(false); + + TypeInst* b_ti = new TypeInst(Location().introduce(),Type::parbool()); + VarDecl* vd_b = new VarDecl(Location().introduce(),b_ti,"b"); + vd_b->toplevel(false); + + TypeInst* xx_range = new TypeInst(Location().introduce(),Type::parint(),NULL); + std::vector xx_ranges(1); + xx_ranges[0] = xx_range; + TypeInst* xx_ti = new TypeInst(Location().introduce(),Type::parsetint(1),xx_ranges); + + std::vector array1dArgs(1); + array1dArgs[0] = vd_x->id(); + Call* array1dCall = new Call(Location().introduce(),"array1d",array1dArgs); + + VarDecl* vd_xx = new VarDecl(Location().introduce(),xx_ti,"xx",array1dCall); + vd_xx->toplevel(false); + + TypeInst* idx_i_ti = new TypeInst(Location().introduce(),Type::parint()); + VarDecl* idx_i = new VarDecl(Location().introduce(),idx_i_ti,"i"); + idx_i->toplevel(false); + + std::vector aa_xxi_idx(1); + aa_xxi_idx[0] = idx_i->id(); + ArrayAccess* aa_xxi = new ArrayAccess(Location().introduce(),vd_xx->id(),aa_xxi_idx); + + std::vector _toString_ENUMArgs(2); + _toString_ENUMArgs[0] = aa_xxi; + _toString_ENUMArgs[1] = vd_b->id(); + Call* _toString_ENUM = new Call(Location().introduce(), + createEnumToStringName(ident, "_toString_"), + _toString_ENUMArgs); + + std::vector index_set_xx_args(1); + index_set_xx_args[0] = vd_xx->id(); + Call* index_set_xx = new Call(Location().introduce(),"index_set",index_set_xx_args); + std::vector gen_exps(1); + gen_exps[0] = idx_i; + Generator gen(gen_exps,index_set_xx,NULL); + + Generators generators; + generators._g.push_back(gen); + Comprehension* comp = new Comprehension(Location().introduce(),_toString_ENUM,generators,false); + + std::vector join_args(2); + join_args[0] = new StringLit(Location().introduce(),", "); + join_args[1] = comp; + Call* join = new Call(Location().introduce(),"join",join_args); + + StringLit* sl_open = new StringLit(Location().introduce(),"["); + BinOp* bopp0 = new BinOp(Location().introduce(),sl_open,BOT_PLUSPLUS,join); + StringLit* sl_close = new StringLit(Location().introduce(),"]"); + BinOp* bopp1 = new BinOp(Location().introduce(),bopp0,BOT_PLUSPLUS,sl_close); + + std::vector let_args(1); + let_args[0] = vd_xx; + Let* let = new Let(Location().introduce(),let_args,bopp1); + + TypeInst* ti_fi = new TypeInst(Location().introduce(),Type::parstring()); + std::vector fi_params(2); + fi_params[0] = vd_x; + fi_params[1] = vd_b; + FunctionI* fi = new FunctionI(Location().introduce(), + createEnumToStringName(ident, "_toString_"), + ti_fi,fi_params,let); + enumItems->addItem(fi); + } return ret; } From 20e96982e757ab020a4417bbaa4dc30b98ecbe40 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Mon, 4 Dec 2017 11:33:26 +1100 Subject: [PATCH 26/34] Don't try to evaluate par opt arrays to deduce bounds --- lib/flatten.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/flatten.cpp b/lib/flatten.cpp index 0d597dffd..d016aaab4 100644 --- a/lib/flatten.cpp +++ b/lib/flatten.cpp @@ -5352,7 +5352,7 @@ namespace MiniZinc { return !(i->isa() && env.failed()); } void vVarDeclI(VarDeclI* v) { - if (v->e()->type().ispar() && v->e()->type().dim() > 0 && v->e()->ti()->domain()==NULL + if (v->e()->type().ispar() && !v->e()->type().isopt() && v->e()->type().dim() > 0 && v->e()->ti()->domain()==NULL && (v->e()->type().bt()==Type::BT_INT || v->e()->type().bt()==Type::BT_FLOAT)) { // Compute bounds for array literals GCLock lock; From 0a1c9a7ce934933d2954d8224d50c6b9955b5008 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Mon, 4 Dec 2017 11:34:13 +1100 Subject: [PATCH 27/34] Define more functions on par opt types. Fixes #188. --- lib/builtins.cpp | 18 +++++++++- share/minizinc/std/builtins.mzn | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/lib/builtins.cpp b/lib/builtins.cpp index 2021ede87..82bc41e1c 100644 --- a/lib/builtins.cpp +++ b/lib/builtins.cpp @@ -2247,6 +2247,8 @@ namespace MiniZinc { std::vector t_arrayXd(1); t_arrayXd[0] = Type::top(-1); rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d_list); + t_arrayXd[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d_list); t_arrayXd[0] = Type::vartop(-1); rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d_list); t_arrayXd[0] = Type::optvartop(-1); @@ -2257,6 +2259,8 @@ namespace MiniZinc { t_arrayXd[0] = Type::parsetint(); t_arrayXd[1] = Type::top(-1); rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d); + t_arrayXd[1].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d); t_arrayXd[1] = Type::vartop(-1); rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d); t_arrayXd[1] = Type::optvartop(-1); @@ -2264,9 +2268,11 @@ namespace MiniZinc { } { std::vector t_arrayXd(2); - t_arrayXd[0] = Type::top(-1); + t_arrayXd[0] = Type::optvartop(-1); t_arrayXd[1] = Type::top(-1); rb(env, m, ASTString("arrayXd"), t_arrayXd, b_arrayXd); + t_arrayXd[1].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("arrayXd"), t_arrayXd, b_arrayXd); t_arrayXd[1] = Type::vartop(-1); rb(env, m, ASTString("arrayXd"), t_arrayXd, b_arrayXd); t_arrayXd[1] = Type::optvartop(-1); @@ -2278,6 +2284,8 @@ namespace MiniZinc { t_arrayXd[1] = Type::parsetint(); t_arrayXd[2] = Type::top(-1); rb(env, m, ASTString("array2d"), t_arrayXd, b_array2d); + t_arrayXd[2].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array2d"), t_arrayXd, b_array2d); t_arrayXd[2] = Type::vartop(-1); rb(env, m, ASTString("array2d"), t_arrayXd, b_array2d); t_arrayXd[2] = Type::optvartop(-1); @@ -2290,6 +2298,8 @@ namespace MiniZinc { t_arrayXd[2] = Type::parsetint(); t_arrayXd[3] = Type::top(-1); rb(env, m, ASTString("array3d"), t_arrayXd, b_array3d); + t_arrayXd[3].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array3d"), t_arrayXd, b_array3d); t_arrayXd[3] = Type::vartop(-1); rb(env, m, ASTString("array3d"), t_arrayXd, b_array3d); t_arrayXd[3] = Type::optvartop(-1); @@ -2303,6 +2313,8 @@ namespace MiniZinc { t_arrayXd[3] = Type::parsetint(); t_arrayXd[4] = Type::top(-1); rb(env, m, ASTString("array4d"), t_arrayXd, b_array4d); + t_arrayXd[4].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array4d"), t_arrayXd, b_array4d); t_arrayXd[4] = Type::vartop(-1); rb(env, m, ASTString("array4d"), t_arrayXd, b_array4d); t_arrayXd[4] = Type::optvartop(-1); @@ -2317,6 +2329,8 @@ namespace MiniZinc { t_arrayXd[4] = Type::parsetint(); t_arrayXd[5] = Type::top(-1); rb(env, m, ASTString("array5d"), t_arrayXd, b_array5d); + t_arrayXd[5].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array5d"), t_arrayXd, b_array5d); t_arrayXd[5] = Type::vartop(-1); rb(env, m, ASTString("array5d"), t_arrayXd, b_array5d); t_arrayXd[5] = Type::optvartop(-1); @@ -2332,6 +2346,8 @@ namespace MiniZinc { t_arrayXd[5] = Type::parsetint(); t_arrayXd[6] = Type::top(-1); rb(env, m, ASTString("array6d"), t_arrayXd, b_array6d); + t_arrayXd[6].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array6d"), t_arrayXd, b_array6d); t_arrayXd[6] = Type::vartop(-1); rb(env, m, ASTString("array6d"), t_arrayXd, b_array6d); t_arrayXd[6] = Type::optvartop(-1); diff --git a/share/minizinc/std/builtins.mzn b/share/minizinc/std/builtins.mzn index 15ba3f5f8..e1f9473a0 100644 --- a/share/minizinc/std/builtins.mzn +++ b/share/minizinc/std/builtins.mzn @@ -704,6 +704,8 @@ function var $$E: max(var set of $$E: s); /** @group builtins.array Return the concatenation of arrays \a x and \a y */ function array[int] of $T: '++'(array[int] of $T: x, array[int] of $T: y); /** @group builtins.array Return the concatenation of arrays \a x and \a y */ +function array[int] of opt $T: '++'(array[int] of opt $T: x, array[int] of opt $T: y); +/** @group builtins.array Return the concatenation of arrays \a x and \a y */ function array[int] of var $T: '++'(array[int] of var $T: x, array[int] of var $T: y); /** @group builtins.array Return the concatenation of arrays \a x and \a y */ function array[int] of var opt $T: '++'(array[int] of var opt $T: x, array[int] of var opt $T: y); @@ -723,6 +725,14 @@ function array[$$E] of $T: reverse(array[$$E] of $T: x) = let { int: l = max(index_set(x))+min(index_set(x)) } in array1d(index_set(x),[x[l-i] | i in index_set(x)]); +/** @group builtins.array Return the array \a x in reverse order + + The resulting array has the same index set as \a x. +*/ +function array[$$E] of opt $T: reverse(array[$$E] of opt $T: x) = + let { int: l = max(index_set(x))+min(index_set(x)) } in + array1d(index_set(x),[x[l-i] | i in index_set(x)]); + /** @group builtins.array Return the array \a x in reverse order The resulting array has the same index set as \a x. @@ -788,6 +798,8 @@ function set of $$E: index_set_6of6(array[int,int,int,int,int,$$E] of var opt $U /** @group builtins.array Return array \a x coerced to index set 1..length(\a x) */ function array[int] of $V: array1d(array[$U] of $V: x); /** @group builtins.array Return array \a x coerced to index set 1..length(\a x) */ +function array[int] of opt $V: array1d(array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to index set 1..length(\a x) */ function array[int] of var $V: array1d(array[$U] of var $V: x); /** @group builtins.array Return array \a x coerced to index set 1..length(\a x) */ function array[int] of var opt $V: array1d(array[$U] of var opt $V: x); @@ -796,6 +808,9 @@ function array[int] of var opt $V: array1d(array[$U] of var opt $V: x); function array[$$E] of $V: array1d(set of $$E: S, array[$U] of $V: x); /** @group builtins.array Return array \a x coerced to one-dimensional array with index set \a S */ +function array[$$E] of opt $V: array1d(set of $$E: S, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to one-dimensional array with index set \a S */ function array[$$E] of var $V: array1d(set of $$E: S, array[$U] of var $V: x); /** @group builtins.array Return array \a x coerced to one-dimensional array with index set \a S */ @@ -806,6 +821,9 @@ function array[$$E] of var opt $V: array1d(set of $$E: S, function array[$$E,$$F] of $V: array2d(set of $$E: S1, set of $$F: S2, array[$U] of $V: x); /** @group builtins.array Return array \a x coerced to two-dimensional array with index sets \a S1 and \a S2 */ +function array[$$E,$$F] of opt $V: array2d(set of $$E: S1, set of $$F: S2, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to two-dimensional array with index sets \a S1 and \a S2 */ function array[$$E,$$F] of var $V: array2d(set of $$E: S1, set of $$F: S2, array[$U] of var $V: x); /** @group builtins.array Return array \a x coerced to two-dimensional array with index sets \a S1 and \a S2 */ @@ -822,6 +840,13 @@ function array[$$E,$$F,$$G] of $V: array3d(set of $$E: S1, /** @group builtins.array Return array \a x coerced to three-dimensional array with index sets \a S1, \a S2 and \a S3 */ +function array[$$E,$$F,$$G] of opt $V: array3d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to three-dimensional array with + index sets \a S1, \a S2 and \a S3 +*/ function array[$$E,$$F,$$G] of var $V: array3d(set of $$E: S1, set of $$F: S2, set of $$G: S3, @@ -845,6 +870,14 @@ function array[$$E,$$F,$$G,$$H] of $V: array4d(set of $$E: S1, /** @group builtins.array Return array \a x coerced to 4-dimensional array with index sets \a S1, \a S2, \a S3 and \a S4 */ +function array[$$E,$$F,$$G,$$H] of opt $V: array4d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to 4-dimensional array with + index sets \a S1, \a S2, \a S3 and \a S4 +*/ function array[$$E,$$F,$$G,$$H] of var $V: array4d(set of $$E: S1, set of $$F: S2, set of $$G: S3, @@ -871,6 +904,15 @@ function array[$$E,$$F,$$G,$$H,$$I] of $V: array5d(set of $$E: S1, /** @group builtins.array Return array \a x coerced to 5-dimensional array with index sets \a S1, \a S2, \a S3, \a S4 and \a S5 */ +function array[$$E,$$F,$$G,$$H,$$I] of opt $V: array5d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to 5-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4 and \a S5 +*/ function array[$$E,$$F,$$G,$$H,$$I] of var $V: array5d(set of $$E: S1, set of $$F: S2, set of $$G: S3, @@ -900,6 +942,16 @@ function array[$$E,$$F,$$G,$$H,$$I,$$J] of $V: array6d(set of $$E: S1, /** @group builtins.array Return array \a x coerced to 6-dimensional array with index sets \a S1, \a S2, \a S3, \a S4, \a S5 and \a S6 */ +function array[$$E,$$F,$$G,$$H,$$I,$$J] of opt $V: array6d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + set of $$J: S6, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to 6-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4, \a S5 and \a S6 +*/ function array[$$E,$$F,$$G,$$H,$$I,$$J] of var $V: array6d(set of $$E: S1, set of $$F: S2, set of $$G: S3, @@ -925,6 +977,10 @@ function array[$T] of $V: arrayXd(array[$T] of var opt $X: x, array[$U] of $V: y /** @group builtins.array Return array \a y coerced to array with same number of dimensions and same index sets as array \a x */ +function array[$T] of opt $V: arrayXd(array[$T] of var opt $X: x, array[$U] of opt $V: y); +/** @group builtins.array Return array \a y coerced to array with same number of + dimensions and same index sets as array \a x +*/ function array[$T] of var $V: arrayXd(array[$T] of var opt $X: x, array[$U] of var $V: y); /** @group builtins.array Return array \a y coerced to array with same number of dimensions and same index sets as array \a x @@ -935,6 +991,9 @@ function array[$T] of var opt $V: arrayXd(array[$T] of var opt $X: x, array[$U] function array[$$E] of $T: row(array[int, $$E] of $T: x, int: r) = array1d(index_set_2of2(x), [x[r,i] | i in index_set_2of2(x)]); /** @group builtins.array Return row \a r of array \a x */ +function array[$$E] of opt $T: row(array[int, $$E] of opt $T: x, int: r) = + array1d(index_set_2of2(x), [x[r,i] | i in index_set_2of2(x)]); +/** @group builtins.array Return row \a r of array \a x */ function array[$$E] of var $T: row(array[int, $$E] of var $T: x, int: r) = array1d(index_set_2of2(x), [x[r,i] | i in index_set_2of2(x)]); /** @group builtins.array Return row \a r of array \a x */ @@ -945,6 +1004,9 @@ function array[$$E] of var opt $T: row(array[int, $$E] of var opt $T: x, int: r) function array[$$E] of $T: col(array[$$E,int] of $T: x, int: c) = array1d(index_set_1of2(x), [x[i,c] | i in index_set_1of2(x)]); /** @group builtins.array Return column \a c of array \a x */ +function array[$$E] of opt $T: col(array[$$E,int] of opt $T: x, int: c) = + array1d(index_set_1of2(x), [x[i,c] | i in index_set_1of2(x)]); +/** @group builtins.array Return column \a c of array \a x */ function array[$$E] of var $T: col(array[$$E,int] of var $T: x, int: c) = array1d(index_set_1of2(x), [x[i,c] | i in index_set_1of2(x)]); /** @group builtins.array Return column \a c of array \a x */ @@ -956,6 +1018,8 @@ test has_index(int: i, array[int] of var opt $T: x) = i in index_set(x); /** @group builtins.array Test if \a e is an element of array \a x */ test has_element($T: e, array[int] of $T: x) = exists (i in index_set(x)) (x[i]=e); /** @group builtins.array Test if \a e is an element of array \a x */ +test has_element($T: e, array[int] of opt $T: x) = exists (i in index_set(x)) (x[i]=e); +/** @group builtins.array Test if \a e is an element of array \a x */ predicate has_element($T: e, array[$$E] of var opt $T: x) = exists (i in index_set(x)) (x[i]=e); /*** From 252bc3c1bd9a57c194ea58321c945db713d5e867 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Mon, 4 Dec 2017 11:42:28 +1100 Subject: [PATCH 28/34] Fix type checker to accept arrays of opt set values --- lib/typecheck.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/typecheck.cpp b/lib/typecheck.cpp index 0e08f35bf..98b247a1e 100644 --- a/lib/typecheck.cpp +++ b/lib/typecheck.cpp @@ -778,6 +778,8 @@ namespace MiniZinc { throw TypeError(_env,sl.v()[i]->loc(),"set literals cannot contain arrays"); if (sl.v()[i]->type().isvar()) ty.ti(Type::TI_VAR); + if (sl.v()[i]->type().isopt()) + throw TypeError(_env,sl.v()[i]->loc(),"set literals cannot contain option type values"); if (sl.v()[i]->type().cv()) ty.cv(true); if (enumId != sl.v()[i]->type().enumId()) @@ -843,7 +845,7 @@ namespace MiniZinc { if (ty.bt()==Type::BT_UNKNOWN) { if (av == NULL) { if (haveInferredType) { - if (ty.st() != vi->type().st()) { + if (ty.st() != vi->type().st() && vi->type().ot()!=Type::OT_OPTIONAL) { throw TypeError(_env,al.loc(),"non-uniform array literal"); } } else { @@ -858,7 +860,7 @@ namespace MiniZinc { } else { if (av == NULL) { if (vi->type().bt() == Type::BT_BOT) { - if (vi->type().st() != ty.st()) { + if (vi->type().st() != ty.st() && vi->type().ot()!=Type::OT_OPTIONAL) { throw TypeError(_env,al.loc(),"non-uniform array literal"); } if (vi->type().enumId() != 0 && ty.enumId() != vi->type().enumId()) { From 3bea2f26f002c91f75e8b922cbc5331f59779c96 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Tue, 5 Dec 2017 09:47:44 +1100 Subject: [PATCH 29/34] Support printing of opt enum types. Fixes #189 --- lib/eval_par.cpp | 28 ++++++++------ lib/typecheck.cpp | 96 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 86 insertions(+), 38 deletions(-) diff --git a/lib/eval_par.cpp b/lib/eval_par.cpp index cc6e148ad..29272160a 100644 --- a/lib/eval_par.cpp +++ b/lib/eval_par.cpp @@ -313,13 +313,15 @@ namespace MiniZinc { void checkDom(EnvI& env, Id* arg, IntSetVal* dom, Expression* e) { bool oob = false; - if (e->type().isintset()) { - IntSetVal* ev = eval_intset(env, e); - IntSetRanges ev_r(ev); - IntSetRanges dom_r(dom); - oob = !Ranges::subset(ev_r, dom_r); - } else { - oob = !dom->contains(eval_int(env,e)); + if (!e->type().isopt()) { + if (e->type().isintset()) { + IntSetVal* ev = eval_intset(env, e); + IntSetRanges ev_r(ev); + IntSetRanges dom_r(dom); + oob = !Ranges::subset(ev_r, dom_r); + } else { + oob = !dom->contains(eval_int(env,e)); + } } if (oob) { std::ostringstream oss; @@ -329,11 +331,13 @@ namespace MiniZinc { } void checkDom(EnvI& env, Id* arg, FloatVal dom_min, FloatVal dom_max, Expression* e) { - FloatVal ev = eval_float(env, e); - if (ev < dom_min || ev > dom_max) { - std::ostringstream oss; - oss << "value for argument `" << *arg << "' out of bounds"; - throw EvalError(env, e->loc(), oss.str()); + if (!e->type().isopt()) { + FloatVal ev = eval_float(env, e); + if (ev < dom_min || ev > dom_max) { + std::ostringstream oss; + oss << "value for argument `" << *arg << "' out of bounds"; + throw EvalError(env, e->loc(), oss.str()); + } } } diff --git a/lib/typecheck.cpp b/lib/typecheck.cpp index 98b247a1e..eb1e6ec42 100644 --- a/lib/typecheck.cpp +++ b/lib/typecheck.cpp @@ -178,22 +178,36 @@ namespace MiniZinc { enumItems->addItem(new VarDeclI(Location().introduce(),vd_enumToString)); } - TypeInst* ti_aa = new TypeInst(Location().introduce(),Type::parint()); + Type tx = Type::parint(); + tx.ot(Type::OT_OPTIONAL); + TypeInst* ti_aa = new TypeInst(Location().introduce(),tx); VarDecl* vd_aa = new VarDecl(Location().introduce(),ti_aa,"x"); vd_aa->toplevel(false); TypeInst* ti_ab = new TypeInst(Location().introduce(),Type::parbool()); VarDecl* vd_ab = new VarDecl(Location().introduce(),ti_ab,"b"); vd_ab->toplevel(false); - std::vector aa_args(1); - aa_args[0] = vd_aa->id(); - ArrayAccess* aa = new ArrayAccess(Location().introduce(),vd_enumToString->id(),aa_args); TypeInst* ti_fi = new TypeInst(Location().introduce(),Type::parstring()); std::vector fi_params(2); fi_params[0] = vd_aa; fi_params[1] = vd_ab; + + std::vector deopt_args(1); + deopt_args[0] = vd_aa->id(); + Call* deopt = new Call(Location().introduce(), "deopt", deopt_args); + Call* occurs = new Call(Location().introduce(), "occurs", deopt_args); + std::vector aa_args(1); + aa_args[0] = deopt; + ArrayAccess* aa = new ArrayAccess(Location().introduce(),vd_enumToString->id(),aa_args); + + StringLit* sl_absent = new StringLit(Location().introduce(),"<>"); + std::vector ite_ifelse(2); + ite_ifelse[0] = occurs; + ite_ifelse[1] = aa; + ITE* ite = new ITE(Location().introduce(),ite_ifelse,sl_absent); + FunctionI* fi = new FunctionI(Location().introduce(), createEnumToStringName(ident, "_toString_"), - ti_fi,fi_params,aa); + ti_fi,fi_params,ite); enumItems->addItem(fi); } else { if (vd_enumToString) { @@ -202,7 +216,9 @@ namespace MiniZinc { vd_enumToString->e(new ArrayLit(Location().introduce(), std::vector())); } { - TypeInst* ti_aa = new TypeInst(Location().introduce(),Type::parint()); + Type tx = Type::parint(); + tx.ot(Type::OT_OPTIONAL); + TypeInst* ti_aa = new TypeInst(Location().introduce(),tx); VarDecl* vd_aa = new VarDecl(Location().introduce(),ti_aa,"x"); vd_aa->toplevel(false); @@ -210,23 +226,30 @@ namespace MiniZinc { VarDecl* vd_ab = new VarDecl(Location().introduce(),ti_ab,"b"); vd_ab->toplevel(false); + std::vector deopt_args(1); + deopt_args[0] = vd_aa->id(); + Call* deopt = new Call(Location().introduce(), "deopt", deopt_args); + Call* if_absent = new Call(Location().introduce(), "absent", deopt_args); + StringLit* sl_absent = new StringLit(Location().introduce(),"<>"); + StringLit* sl_dzn = new StringLit(Location().introduce(), ASTString("to_enum("+ident->str().str()+",")); std::vector showIntArgs(1); - showIntArgs[0] = vd_aa->id(); - Call* showInt_dzn = new Call(Location().introduce(), constants().ids.show, showIntArgs); - BinOp* construct_string_dzn = new BinOp(Location().introduce(), sl_dzn, BOT_PLUSPLUS, showInt_dzn); + showIntArgs[0] = deopt; + Call* showInt = new Call(Location().introduce(), constants().ids.show, showIntArgs); + BinOp* construct_string_dzn = new BinOp(Location().introduce(), sl_dzn, BOT_PLUSPLUS, showInt); StringLit* closing_bracket = new StringLit(Location().introduce(), ASTString(")")); BinOp* construct_string_dzn_2 = new BinOp(Location().introduce(), construct_string_dzn, BOT_PLUSPLUS, closing_bracket); StringLit* sl = new StringLit(Location().introduce(), ASTString(ident->str().str()+"_")); - Call* showInt = new Call(Location().introduce(), constants().ids.show, showIntArgs); BinOp* construct_string = new BinOp(Location().introduce(), sl, BOT_PLUSPLUS, showInt); - std::vector if_then(2); - if_then[0] = vd_ab->id(); - if_then[1] = construct_string_dzn_2; + std::vector if_then(4); + if_then[0] = if_absent; + if_then[1] = sl_absent; + if_then[2] = vd_ab->id(); + if_then[3] = construct_string_dzn_2; ITE* ite = new ITE(Location().introduce(), if_then, construct_string); @@ -244,9 +267,9 @@ namespace MiniZinc { { /* - function _toString_ENUM(array[$U] of ENUM: x, bool: b) = + function _toString_ENUM(array[$U] of opt Foo: x, bool: b) = let { - array[int] of ENUM: xx = array1d(x) + array[int] of opt ENUM: xx = array1d(x) } in "[" ++ join(", ", [ _toString_ENUM(xx[i],b) | i in index_set(xx) ]) ++ "]"; */ @@ -256,7 +279,9 @@ namespace MiniZinc { std::vector ranges(1); ranges[0] = ti_range; - TypeInst* x_ti = new TypeInst(Location().introduce(),Type::parint(-1),ranges,ident); + Type tx = Type::parint(-1); + tx.ot(Type::OT_OPTIONAL); + TypeInst* x_ti = new TypeInst(Location().introduce(),tx,ranges,ident); VarDecl* vd_x = new VarDecl(Location().introduce(),x_ti,"x"); vd_x->toplevel(false); @@ -267,7 +292,7 @@ namespace MiniZinc { TypeInst* xx_range = new TypeInst(Location().introduce(),Type::parint(),NULL); std::vector xx_ranges(1); xx_ranges[0] = xx_range; - TypeInst* xx_ti = new TypeInst(Location().introduce(),Type::parint(1),xx_ranges); + TypeInst* xx_ti = new TypeInst(Location().introduce(),tx,xx_ranges,ident); std::vector array1dArgs(1); array1dArgs[0] = vd_x->id(); @@ -329,12 +354,13 @@ namespace MiniZinc { { /* - function _toString_ENUM(set of ENUM: x, bool: b) = - "{" ++ join(", ", [ _toString_ENUM(i,b) | i in x ]) ++ "}"; + function _toString_ENUM(opt set of ENUM: x, bool: b) = + if absent(x) then <> else "{" ++ join(", ", [ _toString_ENUM(i,b) | i in x ]) ++ "}" endif; */ Type argType = Type::parsetenum(ident->type().enumId()); + argType.ot(Type::OT_OPTIONAL); TypeInst* x_ti = new TypeInst(Location().introduce(),argType,ident); VarDecl* vd_x = new VarDecl(Location().introduce(),x_ti,"x"); vd_x->toplevel(false); @@ -354,9 +380,16 @@ namespace MiniZinc { createEnumToStringName(ident, "_toString_"), _toString_ENUMArgs); + + std::vector deopt_args(1); + deopt_args[0] = vd_x->id(); + Call* deopt = new Call(Location().introduce(), "deopt", deopt_args); + Call* if_absent = new Call(Location().introduce(), "absent", deopt_args); + StringLit* sl_absent = new StringLit(Location().introduce(),"<>"); + std::vector gen_exps(1); gen_exps[0] = idx_i; - Generator gen(gen_exps,vd_x->id()); + Generator gen(gen_exps,deopt); Generators generators; generators._g.push_back(gen); @@ -372,22 +405,28 @@ namespace MiniZinc { StringLit* sl_close = new StringLit(Location().introduce(),"}"); BinOp* bopp1 = new BinOp(Location().introduce(),bopp0,BOT_PLUSPLUS,sl_close); + + std::vector if_then(2); + if_then[0] = if_absent; + if_then[1] = sl_absent; + ITE* ite = new ITE(Location().introduce(), if_then, bopp1); + TypeInst* ti_fi = new TypeInst(Location().introduce(),Type::parstring()); std::vector fi_params(2); fi_params[0] = vd_x; fi_params[1] = vd_b; FunctionI* fi = new FunctionI(Location().introduce(), createEnumToStringName(ident, "_toString_"), - ti_fi,fi_params,bopp1); + ti_fi,fi_params,ite); enumItems->addItem(fi); } { /* - function _toString_ENUM(array[$U] of set of ENUM: x, bool: b) = + function _toString_ENUM(array[$U] of opt set of ENUM: x, bool: b) = let { - array[int] of set of ENUM: xx = array1d(x) + array[int] of opt set of ENUM: xx = array1d(x) } in "[" ++ join(", ", [ _toString_ENUM(xx[i],b) | i in index_set(xx) ]) ++ "]"; */ @@ -397,7 +436,9 @@ namespace MiniZinc { std::vector ranges(1); ranges[0] = ti_range; - TypeInst* x_ti = new TypeInst(Location().introduce(),Type::parsetint(-1),ranges,ident); + Type tx = Type::parsetint(-1); + tx.ot(Type::OT_OPTIONAL); + TypeInst* x_ti = new TypeInst(Location().introduce(),tx,ranges,ident); VarDecl* vd_x = new VarDecl(Location().introduce(),x_ti,"x"); vd_x->toplevel(false); @@ -408,7 +449,7 @@ namespace MiniZinc { TypeInst* xx_range = new TypeInst(Location().introduce(),Type::parint(),NULL); std::vector xx_ranges(1); xx_ranges[0] = xx_range; - TypeInst* xx_ti = new TypeInst(Location().introduce(),Type::parsetint(1),xx_ranges); + TypeInst* xx_ti = new TypeInst(Location().introduce(),tx,xx_ranges,ident); std::vector array1dArgs(1); array1dArgs[0] = vd_x->id(); @@ -437,7 +478,7 @@ namespace MiniZinc { Call* index_set_xx = new Call(Location().introduce(),"index_set",index_set_xx_args); std::vector gen_exps(1); gen_exps[0] = idx_i; - Generator gen(gen_exps,index_set_xx,NULL); + Generator gen(gen_exps,index_set_xx); Generators generators; generators._g.push_back(gen); @@ -705,6 +746,9 @@ namespace MiniZinc { if (e->type().isvar()) { throw TypeError(env, e->loc(),"cannot coerce var set into array"); } + if (e->type().isopt()) { + throw TypeError(env, e->loc(),"cannot coerce opt set into array"); + } std::vector set2a_args(1); set2a_args[0] = e; Call* set2a = new Call(e->loc(), ASTString("set2array"), set2a_args); From a653ebe7f689f1a512c37948ab5e71f51d830651 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Fri, 8 Dec 2017 16:41:08 +1100 Subject: [PATCH 30/34] Fix evaluation of comprehensions in recursive functions (need to restore previous generator variable bindings when exiting comprehension) --- include/minizinc/ast.hh | 3 ++- include/minizinc/eval_par.hh | 10 ++++++++-- lib/ast.cpp | 13 +++++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/include/minizinc/ast.hh b/include/minizinc/ast.hh index 4737b9e8f..932301bf8 100644 --- a/include/minizinc/ast.hh +++ b/include/minizinc/ast.hh @@ -798,7 +798,6 @@ namespace MiniZinc { }; /// \brief A variable declaration expression class VarDecl : public Expression { - friend class Let; protected: /// Type-inst of the declared variable TypeInst* _ti; @@ -860,6 +859,8 @@ namespace MiniZinc { int payload(void) const { return _payload; } /// Set payload void payload(int i) { _payload = i; } + /// Put current value on trail + void trail(void); }; class EnvI; diff --git a/include/minizinc/eval_par.hh b/include/minizinc/eval_par.hh index b462a74b1..9f899b6bb 100644 --- a/include/minizinc/eval_par.hh +++ b/include/minizinc/eval_par.hh @@ -100,6 +100,8 @@ namespace MiniZinc { IntVal i, KeepAlive in, std::vector& a) { { GCLock lock; + GC::mark(); + e->decl(gen,id)->trail(); e->decl(gen,id)->e(IntLit::a(i)); } CallStackItem csi(env, e->decl(gen,id)->id(), i); @@ -133,14 +135,18 @@ namespace MiniZinc { } else { eval_comp_set(env, eval,e,gen,id+1,in,a); } + GC::untrail(); + e->decl(gen,id)->flat(NULL); } template void eval_comp_array(EnvI& env, Eval& eval, Comprehension* e, int gen, int id, IntVal i, KeepAlive in, std::vector& a) { - ArrayLit* al = in()->cast(); + GC::mark(); + e->decl(gen,id)->trail(); CallStackItem csi(env, e->decl(gen,id)->id(), i); + ArrayLit* al = in()->cast(); e->decl(gen,id)->e(al->v()[i.toInt()]); e->rehash(); if (id == e->n_decls(gen)-1) { @@ -173,7 +179,7 @@ namespace MiniZinc { } else { eval_comp_array(env, eval,e,gen,id+1,in,a); } - e->decl(gen,id)->e(NULL); + GC::untrail(); e->decl(gen,id)->flat(NULL); } diff --git a/lib/ast.cpp b/lib/ast.cpp index c5955132a..8110c5354 100644 --- a/lib/ast.cpp +++ b/lib/ast.cpp @@ -581,6 +581,14 @@ namespace MiniZinc { for (unsigned int i=_args.size(); i--;) cmb_hash(Expression::hash(_args[i])); } + + void + VarDecl::trail(void) { + GC::trail(&_e,e()); + if (_ti->ranges().size() > 0) { + GC::trail(reinterpret_cast(&_ti),_ti); + } + } void VarDecl::rehash(void) { @@ -624,10 +632,7 @@ namespace MiniZinc { GC::mark(); for (unsigned int i=_let.size(); i--;) { if (VarDecl* vd = _let[i]->dyn_cast()) { - GC::trail(&vd->_e,vd->e()); - if (vd->ti()->ranges().size() > 0) { - GC::trail(reinterpret_cast(&vd->_ti),vd->ti()); - } + vd->trail(); vd->e(_let_orig[i]); } } From 7bf3fd16e4c1ac314ed812821570a8fb7620b314 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Mon, 11 Dec 2017 14:08:05 +1100 Subject: [PATCH 31/34] MIP: print % before opening Gurobi env because of the 'Academic license' message. Necessary for solving FZN files + solns2out --- solvers/MIP/MIP_gurobi_wrap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/solvers/MIP/MIP_gurobi_wrap.cpp b/solvers/MIP/MIP_gurobi_wrap.cpp index 10d688072..44f1f1ff3 100644 --- a/solvers/MIP/MIP_gurobi_wrap.cpp +++ b/solvers/MIP/MIP_gurobi_wrap.cpp @@ -250,11 +250,12 @@ void MIP_gurobi_wrapper::openGUROBI() cbui.wrapper = this; /* Initialize the GUROBI environment */ + cout << "% " << flush; // Gurobi 7.5.2 prints "Academic License..." error = dll_GRBloadenv (&env, "mzn-gurobi.log"); wrap_assert ( !error, "Could not open GUROBI environment." ); error = dll_GRBsetintparam(env, "OutputFlag", 0); // Switch off output -// error = dll_GRBsetintparam(env, "LogToConsole", -// fVerbose ? 1 : 0); // also when flag_all_solutions? TODO +// error = dll_GRBsetintparam(env, "LogToConsole", +// fVerbose ? 1 : 0); // also when flag_all_solutions? TODO /* Create the problem. */ error = dll_GRBnewmodel(env, &model, "mzn_gurobi", 0, NULL, NULL, NULL, NULL, NULL); wrap_assert ( model, "Failed to create LP." ); From e9f329170d3fe60e63b5b78bdd0a6ebfe659adf8 Mon Sep 17 00:00:00 2001 From: "Jip J. Dekker" Date: Mon, 18 Dec 2017 20:08:27 +1100 Subject: [PATCH 32/34] Fix pthread linking error for CBC on ubuntu 16.04 with CMake 3.5.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 304a07b4c..60e1c98be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -556,7 +556,7 @@ if(HAS_OSICBC) # Tested version 2.9.8. Use svn to get the latest /stable snapsh # OSICBC_LINKEXTRAS: ArchLinux needs blas, lapack, bz2 target_link_libraries(minizinc_osicbc minizinc ${OSICBC_LIBS} ${OSICBC_LINKEXTRAS}) - target_link_libraries(mzn-cbc minizinc_osicbc ${OSICBC_LIBS} ${OSICBC_LINKEXTRAS}) + target_link_libraries(mzn-cbc minizinc_osicbc ${OSICBC_LIBS} ${OSICBC_LINKEXTRAS} ${CMAKE_THREAD_LIBS_INIT}) INSTALL(TARGETS minizinc_osicbc mzn-cbc RUNTIME DESTINATION bin From 34fbaf8091fca846c2298a061fa389dcae93f084 Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Mon, 8 Jan 2018 12:29:28 +1100 Subject: [PATCH 33/34] Change log and version bump --- CHANGES.txt | 31 +++++++++++++++++++++++++++++++ CMakeLists.txt | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 364cdadfb..121660811 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,37 @@ All bug numbers refer to the issue tracker at https://github.com/MiniZinc/libminizinc/issues +Version 2.1.7 +============= + +Changes: + - Improved linearisation for some element constraints. + - Improve performance of optimisation phase by using a queue instead of a + stack. + - Add --dll option for Gurobi backend to specify the Gurobi DLL to load. + - Add more defines_var annotations. + +Bug fixes: + - Fix generation of variable names in output model (sometimes could contain + duplicates). + - Fix enum type inference for array literals with empty sets as their first + arguments. Fixes #180. + - Fix incorrect simplification of float domain constraints. Fixes #159. + - Fix ceil builtin for float values. + - Add superset decomposition for solvers that do not support set variables. + - Fix three bugs in the garbage collector. + - Fix a bug in flattening that would create duplicate variables when a + variable declaration referred to another one in its type-inst. + - Fix a crash in flattening of partial functions. Fixes #187. + - Add missing deopt builtins for all par types. + - Fix output for arrays of sets of enums. + - Define more functions on par opt types. Fixes #188. + - Fix type checker to accept arrays of opt set values. + - Support printing of opt enum types. Fixes #189. + - Fix evaluation of comprehensions in recursive functions. + - Fix output of Gurobi backend when used in conjunction with solns2out. + - Fix pthread linking for mzn-cbc. + Version 2.1.6 ============= diff --git a/CMakeLists.txt b/CMakeLists.txt index 60e1c98be..e0af0691c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ endif() # The version number. set (libminizinc_VERSION_MAJOR 2) set (libminizinc_VERSION_MINOR 1) -set (libminizinc_VERSION_PATCH 6) +set (libminizinc_VERSION_PATCH 7) if (ADDITIONAL_DATE_STRING) set (libminizinc_VERSION_PATCH "${libminizinc_VERSION_PATCH}.${ADDITIONAL_DATE_STRING}") From 8bb12b909b68a3a65f8ab896eb71294d7ec746fa Mon Sep 17 00:00:00 2001 From: Guido Tack Date: Wed, 10 Jan 2018 14:18:59 +1100 Subject: [PATCH 34/34] Catch type error when set literal is declared that contains another set. --- CHANGES.txt | 1 + lib/typecheck.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 121660811..77643f4d4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -35,6 +35,7 @@ Bug fixes: - Fix evaluation of comprehensions in recursive functions. - Fix output of Gurobi backend when used in conjunction with solns2out. - Fix pthread linking for mzn-cbc. + - Catch type error when set literal is declared that contains another set. Version 2.1.6 ============= diff --git a/lib/typecheck.cpp b/lib/typecheck.cpp index eb1e6ec42..9f79fbdb5 100644 --- a/lib/typecheck.cpp +++ b/lib/typecheck.cpp @@ -820,6 +820,8 @@ namespace MiniZinc { for (unsigned int i=0; itype().dim() > 0) throw TypeError(_env,sl.v()[i]->loc(),"set literals cannot contain arrays"); + if (sl.v()[i]->type().is_set()) + throw TypeError(_env,sl.v()[i]->loc(),"set literals cannot contain sets"); if (sl.v()[i]->type().isvar()) ty.ti(Type::TI_VAR); if (sl.v()[i]->type().isopt())