/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Guido Tack */ /* 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 #include #include #include #include #include #include #include #include #include #include namespace MiniZinc { void VarOccurrences::add_idx(VarDeclI* i, int idx_i) { idx.insert(i->e()->id(), idx_i); } void VarOccurrences::add_idx(VarDecl* e, int idx_i) { assert(find(e) == -1); idx.insert(e->id(), idx_i); } int VarOccurrences::find(VarDecl* vd) { IdMap::iterator it = idx.find(vd->id()); return it == idx.end() ? -1 : it->second; } void VarOccurrences::remove(VarDecl* vd) { idx.remove(vd->id()); } void VarOccurrences::add(VarDecl* v, Item* i) { IdMap::iterator vi = _m.find(v->id()->decl()->id()); if (vi == _m.end()) { Items items; items.insert(i); _m.insert(v->id()->decl()->id(), items); } else { vi->second.insert(i); } } int VarOccurrences::remove(VarDecl* v, Item* i) { IdMap::iterator vi = _m.find(v->id()->decl()->id()); assert(vi != _m.end()); vi->second.erase(i); return static_cast(vi->second.size()); } void VarOccurrences::removeAllOccurrences(VarDecl* v) { IdMap::iterator vi = _m.find(v->id()->decl()->id()); assert(vi != _m.end()); vi->second.clear(); } void VarOccurrences::unify(EnvI& env, Model* m, Id* id0_0, Id* id1_0) { Id* id0 = id0_0->decl()->id(); Id* id1 = id1_0->decl()->id(); VarDecl* v0 = id0->decl(); VarDecl* v1 = id1->decl(); if (v0 == v1) return; int v0idx = find(v0); assert(v0idx != -1); env.flat_removeItem(v0idx); IdMap::iterator vi0 = _m.find(v0->id()); if (vi0 != _m.end()) { IdMap::iterator vi1 = _m.find(v1->id()); if (vi1 == _m.end()) { _m.insert(v1->id(), vi0->second); } else { vi1->second.insert(vi0->second.begin(), vi0->second.end()); } _m.remove(v0->id()); } remove(v0); id0->redirect(id1); } void VarOccurrences::clear(void) { _m.clear(); idx.clear(); } int VarOccurrences::occurrences(VarDecl* v) { IdMap::iterator vi = _m.find(v->id()->decl()->id()); return (vi == _m.end() ? 0 : static_cast(vi->second.size())); } int VarOccurrences::usages(VarDecl* v) { auto vi = _m.find(v->id()->decl()->id()); if (vi == _m.end()) { return 0; } int count = 0; for (Item* i : vi->second) { auto vd = i->dyn_cast(); if (vd && vd->e() && vd->e() && vd->e()->e() && (vd->e()->e()->isa() || vd->e()->e()->isa())) { count += usages(vd->e()); } else { count++; } } return count; } void CollectOccurrencesI::vVarDeclI(VarDeclI* v) { CollectOccurrencesE ce(vo, v); topDown(ce, v->e()); } void CollectOccurrencesI::vConstraintI(ConstraintI* ci) { CollectOccurrencesE ce(vo, ci); topDown(ce, ci->e()); for (ExpressionSetIter it = ci->e()->ann().begin(); it != ci->e()->ann().end(); ++it) topDown(ce, *it); } void CollectOccurrencesI::vSolveI(SolveI* si) { CollectOccurrencesE ce(vo, si); topDown(ce, si->e()); for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++si) topDown(ce, *it); } bool isOutput(VarDecl* vd) { for (ExpressionSetIter it = vd->ann().begin(); it != vd->ann().end(); ++it) { if (*it) { if (*it == constants().ann.output_var) return true; if (Call* c = (*it)->dyn_cast()) { if (c->id() == constants().ann.output_array) return true; } } } return false; } void unify(EnvI& env, std::vector& deletedVarDecls, Id* id0, Id* id1) { if (id0->decl() != id1->decl()) { if (isOutput(id0->decl())) { std::swap(id0, id1); } if (id0->decl()->e() != NULL && !Expression::equal(id0->decl()->e(), id1->decl()->id())) { Expression* rhs = id0->decl()->e(); VarDeclI* vdi1 = (*env.flat())[env.vo.find(id1->decl())]->cast(); CollectOccurrencesE ce(env.vo, vdi1); topDown(ce, rhs); id1->decl()->e(rhs); id0->decl()->e(NULL); VarDeclI* vdi0 = (*env.flat())[env.vo.find(id0->decl())]->cast(); CollectDecls cd(env.vo, deletedVarDecls, vdi0); topDown(cd, rhs); } if (Expression::equal(id1->decl()->e(), id0->decl()->id())) { VarDeclI* vdi1 = (*env.flat())[env.vo.find(id1->decl())]->cast(); CollectDecls cd(env.vo, deletedVarDecls, vdi1); Expression* rhs = id1->decl()->e(); topDown(cd, rhs); id1->decl()->e(NULL); } // Compute intersection of domains if (id0->decl()->ti()->domain() != NULL) { if (id1->decl()->ti()->domain() != NULL) { if (id0->type().isint() || id0->type().isintset()) { IntSetVal* isv0 = eval_intset(env, id0->decl()->ti()->domain()); IntSetVal* isv1 = eval_intset(env, id1->decl()->ti()->domain()); IntSetRanges isv0r(isv0); IntSetRanges isv1r(isv1); Ranges::Inter inter(isv0r, isv1r); IntSetVal* nd = IntSetVal::ai(inter); if (nd->size() == 0) { env.fail(); } else if (nd->card() != isv1->card()) { id1->decl()->ti()->domain(new SetLit(Location(), nd)); if (nd->card() == isv0->card()) { id1->decl()->ti()->setComputedDomain(id0->decl()->ti()->computedDomain()); } else { id1->decl()->ti()->setComputedDomain(false); } } } else if (id0->type().isbool()) { if (eval_bool(env, id0->decl()->ti()->domain()) != eval_bool(env, id1->decl()->ti()->domain())) { env.fail(); } } else { // float FloatSetVal* isv0 = eval_floatset(env, id0->decl()->ti()->domain()); FloatSetVal* isv1 = eval_floatset(env, id1->decl()->ti()->domain()); FloatSetRanges isv0r(isv0); FloatSetRanges isv1r(isv1); Ranges::Inter inter(isv0r, isv1r); FloatSetVal* nd = FloatSetVal::ai(inter); FloatSetRanges nd_r(nd); FloatSetRanges isv1r_2(isv1); if (nd->size() == 0) { env.fail(); } else if (!Ranges::equal(nd_r, isv1r_2)) { id1->decl()->ti()->domain(new SetLit(Location(), nd)); FloatSetRanges nd_r_2(nd); FloatSetRanges isv0r_2(isv0); if (Ranges::equal(nd_r_2, isv0r_2)) { id1->decl()->ti()->setComputedDomain(id0->decl()->ti()->computedDomain()); } else { id1->decl()->ti()->setComputedDomain(false); } } } } else { id1->decl()->ti()->domain(id0->decl()->ti()->domain()); } } // If both variables are output variables, unify them in the output model if (isOutput(id0->decl())) { 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()); } } env.vo.unify(env, env.flat(), id0, id1); } } void substituteFixedVars(EnvI& env, Item* ii, std::vector& deletedVarDecls); void simplifyBoolConstraint(EnvI& env, Item* ii, VarDecl* vd, bool& remove, std::deque& vardeclQueue, std::deque& constraintQueue, std::vector& toRemove, std::unordered_map& nonFixedLiteralCount); bool simplifyConstraint(EnvI& env, Item* ii, std::vector& deletedVarDecls, std::deque& constraintQueue, std::deque& vardeclQueue); 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::deque& q) { pushVarDecl(env, (*env.flat())[vd_idx]->cast(), vd_idx, 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) { if (ConstraintI* ci = (*item)->dyn_cast()) { if (!ci->removed() && !ci->flag()) { ci->flag(true); q.push_back(ci); } } else if (VarDeclI* vdi = (*item)->dyn_cast()) { if (vdi->e()->id()->decl() != vdi->e()) { vdi = (*env.flat())[env.vo.find(vdi->e()->id()->decl())]->cast(); } if (!vdi->removed() && !vdi->flag() && vdi->e()->e()) { vdi->flag(true); q.push_back(vdi); } } } } } void optimize(Env& env, bool chain_compression) { if (env.envi().failed()) return; try { EnvI& envi = env.envi(); Model& m = *envi.flat(); std::vector toAssignBoolVars; std::vector toRemoveConstraints; std::vector deletedVarDecls; // Queue of constraint and variable items that still need to be optimised std::deque constraintQueue; // Queue of variable declarations (indexes into the model) that still need to be optimised std::deque vardeclQueue; std::vector boolConstraints; GCLock lock; // Phase 0: clean up // - clear flags for all constraint and variable declaration items // (flags are used to indicate whether an item is already queued or not) for (unsigned int i = 0; i < m.size(); i++) { if (!m[i]->removed()) { if (ConstraintI* ci = m[i]->dyn_cast()) { ci->flag(false); } else if (VarDeclI* vdi = m[i]->dyn_cast()) { vdi->flag(false); } } } // Phase 1: initialise queues // - remove equality constraints between identifiers // - remove toplevel forall constraints // - collect exists, clauses and reified foralls in boolConstraints // - remove "constraint x" where x is a bool var // - unify variables that are assigned to an identifier // - push bool vars that are fixed and have a RHS (to propagate the RHS constraint) // - push int vars that are fixed (either have a RHS or a singleton domain) for (unsigned int i = 0; i < m.size(); i++) { if (m[i]->removed()) continue; if (ConstraintI* ci = m[i]->dyn_cast()) { ci->flag(false); if (!ci->removed()) { if (Call* c = ci->e()->dyn_cast()) { if ((c->id() == constants().ids.int_.eq || c->id() == constants().ids.bool_eq || c->id() == constants().ids.float_.eq || c->id() == constants().ids.set_eq) && c->arg(0)->isa() && c->arg(1)->isa() && (c->arg(0)->cast()->decl()->e() == NULL || c->arg(1)->cast()->decl()->e() == NULL)) { // Equality constraint between two identifiers: unify unify(envi, deletedVarDecls, c->arg(0)->cast(), c->arg(1)->cast()); { VarDecl* vd = c->arg(0)->cast()->decl(); int v0idx = envi.vo.find(vd); pushVarDecl(envi, m[v0idx]->cast(), v0idx, vardeclQueue); } pushDependentConstraints(envi, c->arg(0)->cast(), constraintQueue); CollectDecls cd(envi.vo, deletedVarDecls, ci); topDown(cd, c); ci->e(constants().lit_true); envi.flat_removeItem(i); } else if (c->id() == constants().ids.int_.lin_eq && Expression::equal(c->arg(2), IntLit::a(0))) { ArrayLit* al_c = follow_id(c->arg(0))->cast(); if (al_c->size() == 2 && (*al_c)[0]->cast()->v() == -(*al_c)[1]->cast()->v()) { ArrayLit* al_x = follow_id(c->arg(1))->cast(); if ((*al_x)[0]->isa() && (*al_x)[1]->isa() && ((*al_x)[0]->cast()->decl()->e() == NULL || (*al_x)[1]->cast()->decl()->e() == NULL)) { // Equality constraint between two identifiers: unify unify(envi, deletedVarDecls, (*al_x)[0]->cast(), (*al_x)[1]->cast()); { VarDecl* vd = (*al_x)[0]->cast()->decl(); int v0idx = envi.vo.find(vd); pushVarDecl(envi, m[v0idx]->cast(), v0idx, vardeclQueue); } pushDependentConstraints(envi, (*al_x)[0]->cast(), constraintQueue); CollectDecls cd(envi.vo, deletedVarDecls, ci); topDown(cd, c); ci->e(constants().lit_true); envi.flat_removeItem(i); } } } else if (c->id() == constants().ids.forall) { // Remove forall constraints, assign variables inside the forall to true ArrayLit* al = follow_id(c->arg(0))->cast(); for (unsigned int j = al->size(); j--;) { if (Id* id = (*al)[j]->dyn_cast()) { if (id->decl()->ti()->domain() == NULL) { toAssignBoolVars.push_back(envi.vo.idx.find(id->decl()->id())->second); } else if (id->decl()->ti()->domain() == constants().lit_false) { env.envi().fail(); id->decl()->e(constants().lit_true); } } // todo: check else case (fixed bool inside a forall at this stage) } toRemoveConstraints.push_back(i); } else if (c->id() == constants().ids.exists || c->id() == constants().ids.clause) { // Add disjunctive constraints to the boolConstraints list boolConstraints.push_back(i); } } else if (Id* id = ci->e()->dyn_cast()) { if (id->decl()->ti()->domain() == constants().lit_false) { env.envi().fail(); ci->e(constants().lit_false); } else { if (id->decl()->ti()->domain() == NULL) { toAssignBoolVars.push_back(envi.vo.idx.find(id->decl()->id())->second); } toRemoveConstraints.push_back(i); } } } } else if (VarDeclI* vdi = m[i]->dyn_cast()) { vdi->flag(false); if (vdi->e()->e() && vdi->e()->e()->isa() && vdi->e()->type().dim() == 0) { // unify variable with the identifier it's assigned to Id* id1 = vdi->e()->e()->cast(); vdi->e()->e(NULL); unify(envi, deletedVarDecls, vdi->e()->id(), id1); pushDependentConstraints(envi, id1, constraintQueue); } if (vdi->e()->type().isbool() && vdi->e()->type().isvar() && vdi->e()->type().dim() == 0 && (vdi->e()->ti()->domain() == constants().lit_true || vdi->e()->ti()->domain() == constants().lit_false)) { // push RHS onto constraint queue since this bool var is fixed pushVarDecl(envi, vdi, i, vardeclQueue); pushDependentConstraints(envi, vdi->e()->id(), constraintQueue); } if (Call* c = Expression::dyn_cast(vdi->e()->e())) { if (c->id() == constants().ids.forall || c->id() == constants().ids.exists || c->id() == constants().ids.clause) { // push reified foralls, exists, clauses boolConstraints.push_back(i); } } if (vdi->e()->type().isint()) { if ((vdi->e()->e() && vdi->e()->e()->isa()) || (vdi->e()->ti()->domain() && vdi->e()->ti()->domain()->isa() && vdi->e()->ti()->domain()->cast()->isv()->size() == 1 && vdi->e()->ti()->domain()->cast()->isv()->min() == vdi->e()->ti()->domain()->cast()->isv()->max())) { // Variable is assigned an integer, or has a singleton domain pushVarDecl(envi, vdi, i, vardeclQueue); pushDependentConstraints(envi, vdi->e()->id(), constraintQueue); } } } } // Phase 2: handle boolean constraints // - check if any boolean constraint is subsumed (e.g. a fixed false in a forall, or a fixed // true in a disjunction) // - check if any boolean constraint has a single non-fixed literal left, then fix that literal for (unsigned int i = static_cast(boolConstraints.size()); i--;) { Item* bi = m[boolConstraints[i]]; if (bi->removed()) continue; Call* c; if (bi->isa()) { c = bi->cast()->e()->dyn_cast(); } else { c = bi->cast()->e()->e()->dyn_cast(); } if (c == NULL) continue; bool isConjunction = (c->id() == constants().ids.forall); bool subsumed = false; Id* finalId = NULL; bool finalIdNeg = false; int idCount = 0; std::vector pos; std::vector neg; for (unsigned int j = 0; j < c->n_args(); j++) { bool unit = (j == 0 ? isConjunction : !isConjunction); ArrayLit* al = follow_id(c->arg(j))->cast(); for (unsigned int k = 0; k < al->size(); k++) { if (Id* ident = (*al)[k]->dyn_cast()) { if (ident->decl()->ti()->domain() || (ident->decl()->e() && ident->decl()->e()->type().ispar())) { bool identValue = ident->decl()->ti()->domain() ? eval_bool(envi, ident->decl()->ti()->domain()) : eval_bool(envi, ident->decl()->e()); if (identValue != unit) { subsumed = true; goto subsumed_check_done; } } else { idCount++; finalId = ident; finalIdNeg = (j == 1); if (j == 0) pos.push_back(ident->decl()); else neg.push_back(ident->decl()); } } else { if ((*al)[k]->cast()->v() != unit) { subsumed = true; goto subsumed_check_done; } } } } if (pos.size() > 0 && neg.size() > 0) { std::sort(pos.begin(), pos.end()); std::sort(neg.begin(), neg.end()); unsigned int ix = 0; unsigned int iy = 0; for (;;) { if (pos[ix] == neg[iy]) { subsumed = true; break; } if (pos[ix] < neg[iy]) { ix++; } else { iy++; } if (ix == pos.size() || iy == neg.size()) break; } } subsumed_check_done: if (subsumed) { if (isConjunction) { if (bi->isa()) { env.envi().fail(); } else { if (bi->cast()->e()->ti()->domain()) { if (eval_bool(envi, bi->cast()->e()->ti()->domain())) { envi.fail(); } } else { CollectDecls cd(envi.vo, deletedVarDecls, bi); topDown(cd, bi->cast()->e()->e()); bi->cast()->e()->ti()->domain(constants().lit_false); bi->cast()->e()->ti()->setComputedDomain(true); bi->cast()->e()->e(constants().lit_false); pushVarDecl(envi, bi->cast(), boolConstraints[i], vardeclQueue); pushDependentConstraints(envi, bi->cast()->e()->id(), constraintQueue); } } } else { if (bi->isa()) { CollectDecls cd(envi.vo, deletedVarDecls, bi); topDown(cd, bi->cast()->e()); env.envi().flat_removeItem(bi); } else { if (bi->cast()->e()->ti()->domain()) { if (!eval_bool(envi, bi->cast()->e()->ti()->domain())) { envi.fail(); } } else { CollectDecls cd(envi.vo, deletedVarDecls, bi); topDown(cd, bi->cast()->e()->e()); bi->cast()->e()->ti()->domain(constants().lit_true); bi->cast()->e()->ti()->setComputedDomain(true); bi->cast()->e()->e(constants().lit_true); pushVarDecl(envi, bi->cast(), boolConstraints[i], vardeclQueue); pushDependentConstraints(envi, bi->cast()->e()->id(), constraintQueue); } } } } else if (idCount == 1 && bi->isa()) { assert(finalId->decl()->ti()->domain() == NULL); finalId->decl()->ti()->domain(constants().boollit(!finalIdNeg)); if (finalId->decl()->e() == NULL) finalId->decl()->e(constants().boollit(!finalIdNeg)); CollectDecls cd(envi.vo, deletedVarDecls, bi); topDown(cd, bi->cast()->e()); env.envi().flat_removeItem(bi); pushVarDecl(envi, envi.vo.idx.find(finalId->decl()->id())->second, vardeclQueue); pushDependentConstraints(envi, finalId, constraintQueue); } // todo: for var decls, we could unify the variable with the remaining finalId (the RHS) } // Fix all bool vars in toAssignBoolVars to true and push their declarations and constraints for (unsigned int i = static_cast(toAssignBoolVars.size()); i--;) { if (m[toAssignBoolVars[i]]->removed()) continue; VarDeclI* vdi = m[toAssignBoolVars[i]]->cast(); if (vdi->e()->ti()->domain() == NULL) { vdi->e()->ti()->domain(constants().lit_true); pushVarDecl(envi, vdi, toAssignBoolVars[i], vardeclQueue); pushDependentConstraints(envi, vdi->e()->id(), constraintQueue); } } // Phase 3: fixpoint of constraint and variable simplification std::unordered_map nonFixedLiteralCount; while (!vardeclQueue.empty() || !constraintQueue.empty()) { while (!vardeclQueue.empty()) { int var_idx = vardeclQueue.front(); vardeclQueue.pop_front(); m[var_idx]->cast()->flag(false); VarDecl* vd = m[var_idx]->cast()->e(); if (vd->type().isbool() && vd->ti()->domain()) { bool isTrue = vd->ti()->domain() == constants().lit_true; bool remove = false; if (vd->e()) { if (Id* id = vd->e()->dyn_cast()) { // Variable assigned to id, so fix id if (id->decl()->ti()->domain() == NULL) { id->decl()->ti()->domain(vd->ti()->domain()); pushVarDecl(envi, envi.vo.idx.find(id->decl()->id())->second, vardeclQueue); } else if (id->decl()->ti()->domain() != vd->ti()->domain()) { env.envi().fail(); } remove = true; } else if (Call* c = vd->e()->dyn_cast()) { if (isTrue && c->id() == constants().ids.forall) { // Reified forall is now fixed to true, so make all elements of the conjunction true remove = true; ArrayLit* al = follow_id(c->arg(0))->cast(); for (unsigned int i = 0; i < al->size(); i++) { if (Id* id = (*al)[i]->dyn_cast()) { if (id->decl()->ti()->domain() == NULL) { id->decl()->ti()->domain(constants().lit_true); pushVarDecl(envi, envi.vo.idx.find(id->decl()->id())->second, vardeclQueue); } else if (id->decl()->ti()->domain() == constants().lit_false) { env.envi().fail(); remove = true; } } } } else if (!isTrue && (c->id() == constants().ids.exists || c->id() == constants().ids.clause)) { // Reified disjunction is now fixed to false, so make all elements of the // disjunction false remove = true; for (unsigned int i = 0; i < c->n_args(); i++) { bool ispos = i == 0; ArrayLit* al = follow_id(c->arg(i))->cast(); for (unsigned int j = 0; j < al->size(); j++) { if (Id* id = (*al)[j]->dyn_cast()) { if (id->decl()->ti()->domain() == NULL) { id->decl()->ti()->domain(constants().boollit(!ispos)); pushVarDecl(envi, envi.vo.idx.find(id->decl()->id())->second, vardeclQueue); } else if (id->decl()->ti()->domain() == constants().boollit(ispos)) { env.envi().fail(); remove = true; } } } } } } } else { // If bool variable doesn't have a RHS, just remove it remove = true; } pushDependentConstraints(envi, vd->id(), constraintQueue); std::vector toRemove; IdMap::iterator it = envi.vo._m.find(vd->id()->decl()->id()); // Handle all boolean constraints that involve this variable if (it != envi.vo._m.end()) { for (VarOccurrences::Items::iterator item = it->second.begin(); item != it->second.end(); ++item) { if ((*item)->removed()) continue; if (VarDeclI* vdi = (*item)->dyn_cast()) { // The variable occurs in the RHS of another variable, so // if that is an array variable, simplify all constraints that // mention the array variable if (vdi->e()->e() && vdi->e()->e()->isa()) { IdMap::iterator ait = envi.vo._m.find(vdi->e()->id()->decl()->id()); if (ait != envi.vo._m.end()) { for (VarOccurrences::Items::iterator aitem = ait->second.begin(); aitem != ait->second.end(); ++aitem) { simplifyBoolConstraint(envi, *aitem, vd, remove, vardeclQueue, constraintQueue, toRemove, nonFixedLiteralCount); } } continue; } } // Simplify the constraint *item (which depends on this variable) simplifyBoolConstraint(envi, *item, vd, remove, vardeclQueue, constraintQueue, toRemove, nonFixedLiteralCount); } } // Actually remove all items that have become unnecessary in the step above for (unsigned int i = static_cast(toRemove.size()); i--;) { if (ConstraintI* ci = toRemove[i]->dyn_cast()) { CollectDecls cd(envi.vo, deletedVarDecls, ci); topDown(cd, ci->e()); envi.flat_removeItem(ci); } else { VarDeclI* vdi = toRemove[i]->cast(); CollectDecls cd(envi.vo, deletedVarDecls, vdi); topDown(cd, vdi->e()->e()); vdi->e()->e(NULL); } } if (remove) { deletedVarDecls.push_back(vd); } else { simplifyConstraint(envi, m[var_idx], deletedVarDecls, constraintQueue, vardeclQueue); } } else if (vd->type().isint() && vd->ti()->domain()) { IntSetVal* isv = eval_intset(envi, vd->ti()->domain()); if (isv->size() == 1 && isv->card() == 1) { simplifyConstraint(envi, m[var_idx], deletedVarDecls, constraintQueue, vardeclQueue); } } } // end of processing of variable queue // Now handle all non-boolean constraints (i.e. anything except forall, clause, exists) bool handledConstraint = false; while (!handledConstraint && !constraintQueue.empty()) { Item* item = constraintQueue.front(); constraintQueue.pop_front(); Call* c; ArrayLit* al = NULL; if (ConstraintI* ci = item->dyn_cast()) { ci->flag(false); c = Expression::dyn_cast(ci->e()); } else { if (item->removed()) { // This variable was removed because of unification, so we look up the // variable it was unified to item = m[envi.vo.find(item->cast()->e()->id()->decl())]->cast(); } item->cast()->flag(false); c = Expression::dyn_cast(item->cast()->e()->e()); al = Expression::dyn_cast(item->cast()->e()->e()); } if (!item->removed()) { if (al) { // Substitute all fixed variables by their values in array literals, then // push all constraints that depend on the array substituteFixedVars(envi, item, deletedVarDecls); pushDependentConstraints(envi, item->cast()->e()->id(), constraintQueue); } else if (!c || !(c->id() == constants().ids.forall || c->id() == constants().ids.exists || c->id() == constants().ids.clause)) { // For any constraint that is not forall, exists or clause, // substitute fixed arguments, then simplify it substituteFixedVars(envi, item, deletedVarDecls); handledConstraint = simplifyConstraint(envi, item, deletedVarDecls, constraintQueue, vardeclQueue); } } } } // Clean up constraints that have been removed in the previous phase for (unsigned int i = static_cast(toRemoveConstraints.size()); i--;) { ConstraintI* ci = m[toRemoveConstraints[i]]->cast(); CollectDecls cd(envi.vo, deletedVarDecls, ci); topDown(cd, ci->e()); envi.flat_removeItem(toRemoveConstraints[i]); } // Phase 4: Chain Breaking if (chain_compression) { ImpCompressor imp(envi, m, deletedVarDecls, boolConstraints); LECompressor le(envi, m, deletedVarDecls); for (auto& item : m) { imp.trackItem(item); le.trackItem(item); } imp.compress(); le.compress(); } // Phase 5: handle boolean constraints again (todo: check if we can // refactor this into a separate function) // // Difference to phase 2: constraint argument arrays are actually shortened here if possible for (unsigned int i = static_cast(boolConstraints.size()); i--;) { Item* bi = m[boolConstraints[i]]; if (bi->removed()) continue; Call* c; std::vector removedVarDecls; if (bi->isa()) { c = bi->cast()->e()->dyn_cast(); } else { c = Expression::dyn_cast(bi->cast()->e()->e()); } if (c == NULL) continue; bool isConjunction = (c->id() == constants().ids.forall); bool subsumed = false; for (unsigned int j = 0; j < c->n_args(); j++) { bool unit = (j == 0 ? isConjunction : !isConjunction); ArrayLit* al = follow_id(c->arg(j))->cast(); std::vector compactedAl; for (unsigned int k = 0; k < al->size(); k++) { if (Id* ident = (*al)[k]->dyn_cast()) { if (ident->decl()->ti()->domain()) { if (!(ident->decl()->ti()->domain() == constants().boollit(unit))) { subsumed = true; } removedVarDecls.push_back(ident->decl()); } else { compactedAl.push_back(ident); } } else { if ((*al)[k]->cast()->v() != unit) { subsumed = true; } } } if (compactedAl.size() < al->size()) { c->arg(j, new ArrayLit(al->loc(), compactedAl)); c->arg(j)->type(Type::varbool(1)); } } if (subsumed) { if (isConjunction) { if (bi->isa()) { env.envi().fail(); } else { ArrayLit* al = follow_id(c->arg(0))->cast(); for (unsigned int j = 0; j < al->size(); j++) { removedVarDecls.push_back((*al)[j]->cast()->decl()); } bi->cast()->e()->ti()->domain(constants().lit_false); bi->cast()->e()->ti()->setComputedDomain(true); bi->cast()->e()->e(constants().lit_false); } } else { if (bi->isa()) { CollectDecls cd(envi.vo, deletedVarDecls, bi); topDown(cd, bi->cast()->e()); env.envi().flat_removeItem(bi); } else { CollectDecls cd(envi.vo, deletedVarDecls, bi); topDown(cd, bi->cast()->e()->e()); bi->cast()->e()->ti()->domain(constants().lit_true); bi->cast()->e()->ti()->setComputedDomain(true); bi->cast()->e()->e(constants().lit_true); } } } for (unsigned int j = 0; j < removedVarDecls.size(); j++) { if (env.envi().vo.remove(removedVarDecls[j], bi) == 0) { if ((removedVarDecls[j]->e() == NULL || removedVarDecls[j]->ti()->domain() == NULL || removedVarDecls[j]->ti()->computedDomain()) && !isOutput(removedVarDecls[j])) { deletedVarDecls.push_back(removedVarDecls[j]); } } } if (VarDeclI* vdi = bi->dyn_cast()) { if (envi.vo.occurrences(vdi->e()) == 0) { if ((vdi->e()->e() == NULL || vdi->e()->ti()->domain() == NULL || vdi->e()->ti()->computedDomain()) && !isOutput(vdi->e())) { deletedVarDecls.push_back(vdi->e()); } } } } // Phase 6: remove deleted variables if possible while (!deletedVarDecls.empty()) { VarDecl* cur = deletedVarDecls.back(); deletedVarDecls.pop_back(); if (envi.vo.occurrences(cur) == 0) { IdMap::iterator cur_idx = envi.vo.idx.find(cur->id()); if (cur_idx != envi.vo.idx.end() && !m[cur_idx->second]->removed()) { if (isOutput(cur)) { // We have to change the output model if we remove this variable Expression* val = NULL; if (cur->type().isbool() && cur->ti()->domain()) { val = cur->ti()->domain(); } else if (cur->type().isint()) { if (cur->e() && cur->e()->isa()) { val = cur->e(); } else if (cur->ti()->domain() && cur->ti()->domain()->isa() && cur->ti()->domain()->cast()->isv()->size() == 1 && cur->ti()->domain()->cast()->isv()->min() == cur->ti()->domain()->cast()->isv()->max()) { val = IntLit::a(cur->ti()->domain()->cast()->isv()->min()); } } if (val) { // Find corresponding variable in output model and fix it 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()); envi.flat_removeItem(cur_idx->second); } } else { CollectDecls cd(envi.vo, deletedVarDecls, m[cur_idx->second]->cast()); topDown(cd, cur->e()); envi.flat_removeItem(cur_idx->second); } } } } } catch (ModelInconsistent&) { } } class SubstitutionVisitor : public EVisitor { protected: std::vector removed; Expression* subst(Expression* e) { if (VarDecl* vd = follow_id_to_decl(e)->dyn_cast()) { if (vd->type().isbool() && vd->ti()->domain()) { removed.push_back(vd); return vd->ti()->domain(); } if (vd->type().isint()) { if (vd->e() && vd->e()->isa()) { removed.push_back(vd); return vd->e(); } if (vd->ti()->domain() && vd->ti()->domain()->isa() && vd->ti()->domain()->cast()->isv()->size() == 1 && vd->ti()->domain()->cast()->isv()->min() == vd->ti()->domain()->cast()->isv()->max()) { removed.push_back(vd); return IntLit::a(vd->ti()->domain()->cast()->isv()->min()); } } } return e; } public: /// Visit array literal void vArrayLit(ArrayLit& al) { for (unsigned int i = 0; i < al.size(); i++) { al.set(i, subst(al[i])); } } /// Visit call void vCall(Call& c) { for (unsigned int i = 0; i < c.n_args(); i++) { c.arg(i, subst(c.arg(i))); } } /// Determine whether to enter node bool enter(Expression* e) { return !e->isa(); } void remove(EnvI& env, Item* item, std::vector& deletedVarDecls) { for (unsigned int i = 0; i < removed.size(); i++) { removed[i]->ann().remove(constants().ann.is_defined_var); if (env.vo.remove(removed[i], item) == 0) { if ((removed[i]->e() == NULL || removed[i]->ti()->domain() == NULL || removed[i]->ti()->computedDomain()) && !isOutput(removed[i])) { deletedVarDecls.push_back(removed[i]); } } } } }; void substituteFixedVars(EnvI& env, Item* ii, std::vector& deletedVarDecls) { SubstitutionVisitor sv; if (ConstraintI* ci = ii->dyn_cast()) { topDown(sv, ci->e()); for (ExpressionSetIter it = ci->e()->ann().begin(); it != ci->e()->ann().end(); ++it) { topDown(sv, *it); } } else if (VarDeclI* vdi = ii->dyn_cast()) { topDown(sv, vdi->e()); for (ExpressionSetIter it = vdi->e()->ann().begin(); it != vdi->e()->ann().end(); ++it) { topDown(sv, *it); } } else { SolveI* si = ii->cast(); topDown(sv, si->e()); for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) { topDown(sv, *it); } } sv.remove(env, ii, deletedVarDecls); } bool simplifyConstraint(EnvI& env, Item* ii, std::vector& deletedVarDecls, std::deque& constraintQueue, std::deque& vardeclQueue) { Expression* con_e; bool is_true; bool is_false; if (ConstraintI* ci = ii->dyn_cast()) { con_e = ci->e(); is_true = true; is_false = false; } else { VarDeclI* vdi = ii->cast(); con_e = vdi->e()->e(); is_true = (vdi->e()->type().isbool() && vdi->e()->ti()->domain() == constants().lit_true); is_false = (vdi->e()->type().isbool() && vdi->e()->ti()->domain() == constants().lit_false); assert(is_true || is_false || !vdi->e()->type().isbool() || vdi->e()->ti()->domain() == NULL); } if (Call* c = Expression::dyn_cast(con_e)) { if (c->id() == constants().ids.int_.eq || c->id() == constants().ids.bool_eq || c->id() == constants().ids.float_.eq) { if (is_true && c->arg(0)->isa() && c->arg(1)->isa() && (c->arg(0)->cast()->decl()->e() == NULL || c->arg(1)->cast()->decl()->e() == NULL)) { unify(env, deletedVarDecls, c->arg(0)->cast(), c->arg(1)->cast()); pushDependentConstraints(env, c->arg(0)->cast(), constraintQueue); CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); env.flat_removeItem(ii); } else if (c->arg(0)->type().ispar() && c->arg(1)->type().ispar()) { Expression* e0 = eval_par(env, c->arg(0)); Expression* e1 = eval_par(env, c->arg(1)); bool is_equal = Expression::equal(e0, e1); if ((is_true && is_equal) || (is_false && !is_equal)) { // do nothing } else if ((is_true && !is_equal) || (is_false && is_equal)) { env.fail(); } else { VarDeclI* vdi = ii->cast(); CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); vdi->e()->e(constants().boollit(is_equal)); vdi->e()->ti()->domain(constants().boollit(is_equal)); vdi->e()->ti()->setComputedDomain(true); pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); pushDependentConstraints(env, vdi->e()->id(), constraintQueue); } if (ii->isa()) { CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); env.flat_removeItem(ii); } } else if (is_true && ((c->arg(0)->isa() && c->arg(1)->type().ispar()) || (c->arg(1)->isa() && c->arg(0)->type().ispar()))) { Id* ident = c->arg(0)->isa() ? c->arg(0)->cast() : c->arg(1)->cast(); Expression* arg = c->arg(0)->isa() ? c->arg(1) : c->arg(0); bool canRemove = false; TypeInst* ti = ident->decl()->ti(); switch (ident->type().bt()) { case Type::BT_BOOL: if (ti->domain() == NULL) { ti->domain(constants().boollit(eval_bool(env, arg))); ti->setComputedDomain(false); canRemove = true; } else { if (eval_bool(env, ti->domain()) == eval_bool(env, arg)) { canRemove = true; } else { env.fail(); canRemove = true; } } break; case Type::BT_INT: { IntVal d = eval_int(env, arg); if (ti->domain() == NULL) { ti->domain(new SetLit(Location().introduce(), IntSetVal::a(d, d))); ti->setComputedDomain(false); canRemove = true; } else { IntSetVal* isv = eval_intset(env, ti->domain()); if (isv->contains(d)) { ident->decl()->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(d, d))); ident->decl()->ti()->setComputedDomain(false); canRemove = true; } else { env.fail(); canRemove = true; } } } break; case Type::BT_FLOAT: { if (ti->domain() == NULL) { ti->domain(new BinOp(Location().introduce(), arg, BOT_DOTDOT, arg)); ti->setComputedDomain(false); canRemove = true; } else { FloatVal value = eval_float(env, arg); if (LinearTraits::domain_contains(eval_floatset(env, ti->domain()), value)) { ti->domain(new BinOp(Location().introduce(), arg, BOT_DOTDOT, arg)); ti->setComputedDomain(false); canRemove = true; } else { env.fail(); canRemove = true; } } } break; default: break; } if (ident->decl()->e() == NULL) { ident->decl()->e(c->arg(0)->isa() ? c->arg(1) : c->arg(0)); ti->setComputedDomain(true); canRemove = true; } if (ident->decl()->e()->isa()) constraintQueue.push_back((*env.flat())[env.vo.find(ident->decl())]); pushDependentConstraints(env, ident, constraintQueue); if (canRemove) { CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); env.flat_removeItem(ii); } } } else if ((is_true || is_false) && c->id() == constants().ids.int_.le && ((c->arg(0)->isa() && c->arg(1)->type().ispar()) || (c->arg(1)->isa() && c->arg(0)->type().ispar()))) { // todo: does this every happen? int_le shouldn't be generated any more Id* ident = c->arg(0)->isa() ? c->arg(0)->cast() : c->arg(1)->cast(); Expression* arg = c->arg(0)->isa() ? c->arg(1) : c->arg(0); IntSetVal* domain = ident->decl()->ti()->domain() ? eval_intset(env, ident->decl()->ti()->domain()) : NULL; if (domain) { BinOpType bot = c->arg(0)->isa() ? (is_true ? BOT_LQ : BOT_GR) : (is_true ? BOT_GQ : BOT_LE); IntSetVal* newDomain = LinearTraits::limit_domain(bot, domain, eval_int(env, arg)); ident->decl()->ti()->domain(new SetLit(Location().introduce(), newDomain)); ident->decl()->ti()->setComputedDomain(false); if (newDomain->min() == newDomain->max()) { pushDependentConstraints(env, ident, constraintQueue); } CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); if (VarDeclI* vdi = ii->dyn_cast()) { vdi->e()->e(constants().boollit(is_true)); pushDependentConstraints(env, vdi->e()->id(), constraintQueue); if (env.vo.occurrences(vdi->e()) == 0) { if (isOutput(vdi->e())) { VarDecl* vd_out = (*env.output)[env.output_vo_flat.find(vdi->e())]->cast()->e(); vd_out->e(constants().boollit(is_true)); } env.flat_removeItem(vdi); } } else { env.flat_removeItem(ii); } } } else if (c->id() == constants().ids.bool2int) { VarDeclI* vdi = ii->dyn_cast(); VarDecl* vd; bool fixed = false; bool b_val = false; if (vdi) { vd = vdi->e(); } else if (Id* ident = c->arg(1)->dyn_cast()) { vd = ident->decl(); } else { vd = NULL; } IntSetVal* vd_dom = NULL; if (vd) { if (vd->ti()->domain()) { vd_dom = eval_intset(env, vd->ti()->domain()); if (vd_dom->max() < 0 || vd_dom->min() > 1) { env.fail(); return true; } fixed = vd_dom->min() == vd_dom->max(); b_val = (vd_dom->min() == 1); } } else { fixed = true; b_val = (eval_int(env, c->arg(1)) == 1); } if (fixed) { if (c->arg(0)->type().ispar()) { bool b2i_val = eval_bool(env, c->arg(0)); if (b2i_val != b_val) { env.fail(); } else { CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); env.flat_removeItem(ii); } } else { Id* ident = c->arg(0)->cast(); TypeInst* ti = ident->decl()->ti(); if (ti->domain() == NULL) { ti->domain(constants().boollit(b_val)); ti->setComputedDomain(false); } else if (eval_bool(env, ti->domain()) != b_val) { env.fail(); } CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); if (vd) { vd->e(IntLit::a(b_val)); vd->ti()->setComputedDomain(true); } pushDependentConstraints(env, ident, constraintQueue); if (vdi) { if (env.vo.occurrences(vd) == 0) { env.flat_removeItem(vdi); } } else { env.flat_removeItem(ii); } } } else { IntVal v = -1; if (BoolLit* bl = c->arg(0)->dyn_cast()) { v = bl->v() ? 1 : 0; } else if (Id* ident = c->arg(0)->dyn_cast()) { if (ident->decl()->ti()->domain()) { v = eval_bool(env, ident->decl()->ti()->domain()) ? 1 : 0; } } if (v != -1) { if (vd_dom && !vd_dom->contains(v)) { env.fail(); } else { CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); vd->e(IntLit::a(v)); vd->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(v, v))); vd->ti()->setComputedDomain(true); pushVarDecl(env, env.vo.find(vd), vardeclQueue); pushDependentConstraints(env, vd->id(), constraintQueue); } } } } else { // General propagation: call a propagator registered for this constraint type Expression* rewrite = NULL; GCLock lock; switch (OptimizeRegistry::registry().process(env, ii, c, rewrite)) { case OptimizeRegistry::CS_NONE: return false; case OptimizeRegistry::CS_OK: return true; case OptimizeRegistry::CS_FAILED: if (is_true) { env.fail(); return true; } else if (is_false) { if (ii->isa()) { CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); env.flat_removeItem(ii); } else { deletedVarDecls.push_back(ii->cast()->e()); } return true; } else { VarDeclI* vdi = ii->cast(); vdi->e()->ti()->domain(constants().lit_false); pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); return true; } case OptimizeRegistry::CS_ENTAILED: if (is_true) { if (ii->isa()) { CollectDecls cd(env.vo, deletedVarDecls, ii); topDown(cd, c); env.flat_removeItem(ii); } else { deletedVarDecls.push_back(ii->cast()->e()); } return true; } else if (is_false) { env.fail(); return true; } else { VarDeclI* vdi = ii->cast(); vdi->e()->ti()->domain(constants().lit_true); pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); return true; } case OptimizeRegistry::CS_REWRITE: { std::vector tdv; CollectDecls cd(env.vo, tdv, ii); topDown(cd, c); CollectOccurrencesE ce(env.vo, ii); topDown(ce, rewrite); for (unsigned int i = 0; i < tdv.size(); i++) { if (env.vo.occurrences(tdv[i]) == 0) deletedVarDecls.push_back(tdv[i]); } assert(rewrite != NULL); if (ConstraintI* ci = ii->dyn_cast()) { ci->e(rewrite); constraintQueue.push_back(ii); } else { VarDeclI* vdi = ii->cast(); vdi->e()->e(rewrite); if (vdi->e()->e() && vdi->e()->e()->isa() && vdi->e()->type().dim() == 0) { Id* id1 = vdi->e()->e()->cast(); vdi->e()->e(NULL); unify(env, deletedVarDecls, vdi->e()->id(), id1); pushDependentConstraints(env, id1, constraintQueue); } if (vdi->e()->e() && vdi->e()->e()->type().ispar() && vdi->e()->ti()->domain()) { if (vdi->e()->e()->type().isint()) { IntVal iv = eval_int(env, vdi->e()->e()); IntSetVal* dom = eval_intset(env, vdi->e()->ti()->domain()); if (!dom->contains(iv)) env.fail(); } else if (vdi->e()->e()->type().isintset()) { IntSetVal* isv = eval_intset(env, vdi->e()->e()); IntSetVal* dom = eval_intset(env, vdi->e()->ti()->domain()); IntSetRanges isv_r(isv); IntSetRanges dom_r(dom); if (!Ranges::subset(isv_r, dom_r)) env.fail(); } else if (vdi->e()->e()->type().isfloat()) { FloatVal fv = eval_float(env, vdi->e()->e()); FloatSetVal* dom = eval_floatset(env, vdi->e()->ti()->domain()); if (!dom->contains(fv)) env.fail(); } } if (vdi->e()->ti()->type() != Type::varbool() || vdi->e()->ti()->domain() == NULL) pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); if (is_true) { constraintQueue.push_back(ii); } } return true; } } } } return false; } int boolState(EnvI& env, Expression* e) { if (e->type().ispar()) { return eval_bool(env, e); } else { Id* id = e->cast(); if (id->decl()->ti()->domain() == NULL) return 2; return id->decl()->ti()->domain() == constants().lit_true; } } int decrementNonFixedVars(std::unordered_map& nonFixedLiteralCount, Call* c) { std::unordered_map::iterator it = nonFixedLiteralCount.find(c); if (it == nonFixedLiteralCount.end()) { int nonFixedVars = 0; for (unsigned int i = 0; i < c->n_args(); i++) { ArrayLit* al = follow_id(c->arg(i))->cast(); nonFixedVars += al->size(); for (unsigned int j = al->size(); j--;) { if ((*al)[j]->type().ispar()) nonFixedVars--; } } nonFixedVars--; // for the identifier we're currently processing nonFixedLiteralCount.insert(std::make_pair(c, nonFixedVars)); return nonFixedVars; } else { it->second--; return it->second; } } void simplifyBoolConstraint(EnvI& env, Item* ii, VarDecl* vd, bool& remove, std::deque& vardeclQueue, std::deque& constraintQueue, std::vector& toRemove, std::unordered_map& nonFixedLiteralCount) { if (ii->isa()) { remove = false; return; } bool isTrue = vd->ti()->domain() == constants().lit_true; Expression* e = NULL; ConstraintI* ci = ii->dyn_cast(); VarDeclI* vdi = ii->dyn_cast(); if (ci) { e = ci->e(); } else if (vdi) { e = vdi->e()->e(); if (e == NULL) return; if (Id* id = e->dyn_cast()) { assert(id->decl() == vd); if (vdi->e()->ti()->domain() == NULL) { vdi->e()->ti()->domain(constants().boollit(isTrue)); vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); } else if (id->decl()->ti()->domain() == constants().boollit(!isTrue)) { env.fail(); remove = false; } return; } } if (Id* ident = e->dyn_cast()) { assert(ident->decl() == vd); return; } if (e->isa()) { if (e == constants().lit_true && ci) { toRemove.push_back(ci); } return; } Call* c = e->cast(); if (c->id() == constants().ids.bool_eq) { Expression* b0 = c->arg(0); Expression* b1 = c->arg(1); int b0s = boolState(env, b0); int b1s = boolState(env, b1); if (b0s == 2) { std::swap(b0, b1); std::swap(b0s, b1s); } assert(b0s != 2); if (ci || vdi->e()->ti()->domain() == constants().lit_true) { if (b0s != b1s) { if (b1s == 2) { b1->cast()->decl()->ti()->domain(constants().boollit(isTrue)); vardeclQueue.push_back(env.vo.idx.find(b1->cast()->decl()->id())->second); if (ci) toRemove.push_back(ci); } else { env.fail(); remove = false; } } else { if (ci) toRemove.push_back(ci); } } else if (vdi && vdi->e()->ti()->domain() == constants().lit_false) { if (b0s != b1s) { if (b1s == 2) { b1->cast()->decl()->ti()->domain(constants().boollit(isTrue)); vardeclQueue.push_back(env.vo.idx.find(b1->cast()->decl()->id())->second); } } else { env.fail(); remove = false; } } else { remove = false; } } else if (c->id() == constants().ids.forall || c->id() == constants().ids.exists || c->id() == constants().ids.clause) { if (isTrue && c->id() == constants().ids.exists) { if (ci) { toRemove.push_back(ci); } else { if (vdi->e()->ti()->domain() == NULL) { vdi->e()->ti()->domain(constants().lit_true); vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); } else if (vdi->e()->ti()->domain() != constants().lit_true) { env.fail(); vdi->e()->e(constants().lit_true); } } } else if (!isTrue && c->id() == constants().ids.forall) { if (ci) { env.fail(); toRemove.push_back(ci); } else { if (vdi->e()->ti()->domain() == NULL) { vdi->e()->ti()->domain(constants().lit_false); vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); } else if (vdi->e()->ti()->domain() != constants().lit_false) { env.fail(); vdi->e()->e(constants().lit_false); } } } else { int nonfixed = decrementNonFixedVars(nonFixedLiteralCount, c); bool isConjunction = (c->id() == constants().ids.forall); assert(nonfixed >= 0); if (nonfixed <= 1) { bool subsumed = false; int nonfixed_i = -1; int nonfixed_j = -1; int realNonFixed = 0; for (unsigned int i = 0; i < c->n_args(); i++) { bool unit = (i == 0 ? isConjunction : !isConjunction); ArrayLit* al = follow_id(c->arg(i))->cast(); realNonFixed += al->size(); for (unsigned int j = al->size(); j--;) { if ((*al)[j]->type().ispar() || (*al)[j]->cast()->decl()->ti()->domain()) realNonFixed--; if ((*al)[j]->type().ispar() && eval_bool(env, (*al)[j]) != unit) { subsumed = true; i = 2; // break out of outer loop break; } else if (Id* id = (*al)[j]->dyn_cast()) { if (id->decl()->ti()->domain()) { bool idv = (id->decl()->ti()->domain() == constants().lit_true); if (unit != idv) { subsumed = true; i = 2; // break out of outer loop break; } } else { nonfixed_i = i; nonfixed_j = j; } } } } if (subsumed) { if (ci) { if (isConjunction) { env.fail(); ci->e(constants().lit_false); } else { toRemove.push_back(ci); } } else { if (vdi->e()->ti()->domain() == NULL) { vdi->e()->ti()->domain(constants().boollit(!isConjunction)); vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); } else if (vdi->e()->ti()->domain() != constants().boollit(!isConjunction)) { env.fail(); vdi->e()->e(constants().boollit(!isConjunction)); } } } else if (realNonFixed == 0) { if (ci) { if (isConjunction) { toRemove.push_back(ci); } else { env.fail(); ci->e(constants().lit_false); } } else { if (vdi->e()->ti()->domain() == NULL) { vdi->e()->ti()->domain(constants().boollit(isConjunction)); vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); } else if (vdi->e()->ti()->domain() != constants().boollit(isConjunction)) { env.fail(); vdi->e()->e(constants().boollit(isConjunction)); } toRemove.push_back(vdi); } } else { // not subsumed, nonfixed==1 assert(nonfixed_i != -1); ArrayLit* al = follow_id(c->arg(nonfixed_i))->cast(); Id* id = (*al)[nonfixed_j]->cast(); if (ci || vdi->e()->ti()->domain()) { bool result = nonfixed_i == 0; if (vdi && vdi->e()->ti()->domain() == constants().lit_false) result = !result; VarDecl* vd = id->decl(); if (vd->ti()->domain() == NULL) { vd->ti()->domain(constants().boollit(result)); vardeclQueue.push_back(env.vo.idx.find(vd->id())->second); } else if (vd->ti()->domain() != constants().boollit(result)) { env.fail(); vd->e(constants().lit_true); } } else { remove = false; } } } else if (c->id() == constants().ids.clause) { int posOrNeg = isTrue ? 0 : 1; ArrayLit* al = follow_id(c->arg(posOrNeg))->cast(); ArrayLit* al_other = follow_id(c->arg(1 - posOrNeg))->cast(); if (ci && al->size() == 1 && (*al)[0] != vd->id() && al_other->size() == 1) { // simple implication assert((*al_other)[0] == vd->id()); if (ci) { if ((*al)[0]->type().ispar()) { if (eval_bool(env, (*al)[0]) == isTrue) { toRemove.push_back(ci); } else { env.fail(); remove = false; } } else { Id* id = (*al)[0]->cast(); if (id->decl()->ti()->domain() == NULL) { id->decl()->ti()->domain(constants().boollit(isTrue)); vardeclQueue.push_back(env.vo.idx.find(id->decl()->id())->second); } else { if (id->decl()->ti()->domain() == constants().boollit(isTrue)) { toRemove.push_back(ci); } else { env.fail(); remove = false; } } } } } else { // proper clause for (unsigned int i = 0; i < al->size(); i++) { if ((*al)[i] == vd->id()) { if (ci) { toRemove.push_back(ci); } else { if (vdi->e()->ti()->domain() == NULL) { vdi->e()->ti()->domain(constants().lit_true); vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); } else if (vdi->e()->ti()->domain() != constants().lit_true) { env.fail(); vdi->e()->e(constants().lit_true); } } break; } } } } } } else { remove = false; } } } // namespace MiniZinc