this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Jip J. Dekker <jip.dekker@monash.edu> 6 * Guido Tack <guido.tack@monash.edu> 7 */ 8 9/* This Source Code Form is subject to the terms of the Mozilla Public 10 * License, v. 2.0. If a copy of the MPL was not distributed with this 11 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 12 13#include <minizinc/interpreter.hh> 14#include <minizinc/interpreter/primitives.hh> 15#include <minizinc/interpreter/values.hh> 16 17namespace MiniZinc { 18 19std::tuple<std::vector<Val>, std::vector<Val>, Val> simplify_linexp(Val v) { 20 std::vector<Val> coeffs = {Val(1)}; 21 std::vector<Val> vars = {v}; 22 Val d = 0; 23 simplify_linexp(coeffs, vars, d); 24 return {coeffs, vars, d}; 25}; 26 27void simplify_linexp(std::vector<Val>& coeffs, std::vector<Val>& vars, Val& d) { 28 assert(coeffs.size() == vars.size()); 29 std::vector<std::pair<Val, Val>> defs; 30 defs.reserve(vars.size()); 31 for (int j = vars.size() - 1; j >= 0; --j) { 32 if (coeffs[j] != 0) { 33 defs.emplace_back(coeffs[j], vars[j]); 34 } 35 } 36 coeffs.clear(); 37 vars.clear(); 38 39 std::vector<int> idx; 40 while (!defs.empty()) { 41 Val coeff = defs.back().first; 42 Val stacktop = Val::follow_alias(defs.back().second); 43 defs.pop_back(); 44 if (coeff == 0) continue; 45 if (stacktop.isInt()) { 46 d += coeff * stacktop; 47 } else { 48 Variable* cur = stacktop.toVar(); 49 if (Constraint* defby = cur->defined_by()) { 50 switch (defby->pred()) { 51 case PrimitiveMap::INT_PLUS: { 52 assert(stacktop == Val::follow_alias(defby->arg(2))); 53 for (int i = 0; i < 2; ++i) { 54 Val arg = Val::follow_alias(defby->arg(i)); 55 if (arg.isInt()) { 56 d += coeff * arg; 57 } else { 58 defs.emplace_back(coeff, arg); 59 } 60 } 61 continue; 62 } 63 case PrimitiveMap::INT_MINUS: { 64 assert(stacktop == Val::follow_alias(defby->arg(2))); 65 Val lhs = Val::follow_alias(defby->arg(0)); 66 if (lhs.isInt()) { 67 d += coeff * lhs; 68 } else { 69 defs.emplace_back(coeff, lhs); 70 } 71 Val rhs = Val::follow_alias(defby->arg(1)); 72 if (rhs.isInt()) { 73 d += coeff * -rhs; 74 } else { 75 defs.emplace_back(-coeff, rhs); 76 } 77 continue; 78 } 79 case PrimitiveMap::INT_SUM: { 80 assert(stacktop == Val::follow_alias(defby->arg(1))); 81 Val arr = defby->arg(0).toVec()->raw_data(); 82 for (int i = 0; i < arr.size(); ++i) { 83 Val arg = Val::follow_alias(arr[i]); 84 if (arg.isInt()) { 85 d += coeff * arg; 86 } else { 87 defs.emplace_back(coeff, arg); 88 } 89 } 90 continue; 91 } 92 case PrimitiveMap::INT_TIMES: { 93 assert(stacktop == Val::follow_alias(defby->arg(2))); 94 Val lhs = Val::follow_alias(defby->arg(0)); 95 Val rhs = Val::follow_alias(defby->arg(1)); 96 if (lhs.isInt()) { 97 if (rhs.isInt()) { 98 // both constants, compute result 99 d += coeff * lhs * rhs; 100 } else { 101 defs.emplace_back(coeff * lhs, rhs); 102 } 103 continue; 104 } 105 if (rhs.isInt()) { 106 defs.emplace_back(coeff * rhs, lhs); 107 continue; 108 } 109 break; 110 } 111 default: { 112 } 113 } 114 } 115 if (coeff != 0) { 116 coeffs.emplace_back(coeff); 117 vars.emplace_back(cur); 118 idx.push_back(idx.size()); 119 } 120 } 121 } 122 123 if (coeffs.size() > 1) { 124 // Find and merge duplicate variables 125 class CmpValIdx { 126 public: 127 std::vector<Val>& x; 128 explicit CmpValIdx(std::vector<Val>& x0) : x(x0) {} 129 bool operator()(int i, int j) const { return x[i].timestamp() < x[j].timestamp(); } 130 }; 131 std::sort(idx.begin(), idx.end(), CmpValIdx(vars)); 132 std::vector<Val> coeffs_simple; 133 coeffs_simple.reserve(coeffs.size()); 134 std::vector<Val> vars_simple; 135 vars_simple.reserve(vars.size()); 136 137 int ci = 0; 138 coeffs_simple.push_back(coeffs[idx[0]]); 139 vars_simple.push_back(vars[idx[0]]); 140 bool foundDuplicates = false; 141 for (unsigned int i = 1; i < idx.size(); i++) { 142 if (vars[idx[i]].timestamp() == vars_simple[ci].timestamp()) { 143 coeffs_simple[ci] += coeffs[idx[i]]; 144 foundDuplicates = true; 145 } else { 146 coeffs_simple.push_back(coeffs[idx[i]]); 147 vars_simple.push_back(vars[idx[i]]); 148 ci++; 149 } 150 } 151 int j = 0; 152 for (unsigned int i = 0; i < coeffs_simple.size(); i++) { 153 if (coeffs_simple[i] != 0) { 154 coeffs[j] = coeffs_simple[i]; 155 vars[j++] = vars_simple[i]; 156 } 157 } 158 coeffs.resize(j); 159 vars.resize(j); 160 } 161} 162 163std::string Val::toString(bool trim) const { 164 std::ostringstream oss; 165 Val v = follow_alias(*this); 166 if (v.isInt()) { 167 oss << v.toIntVal(); 168 } else if (v.isVar()) { 169 if (v.timestamp() >= 0) { 170 oss << "X" << timestamp() << "("; 171 } 172 oss << v.lb().toString() << "," << v.ub().toString(); 173 if (v.toVar()->timestamp() >= 0) { 174 oss << ")"; 175 } 176 } else { 177 oss << "X" << v.toVec()->timestamp() << "["; 178 if (!trim || v.size() <= 4) { 179 for (size_t i = 0; i < v.size(); i++) { 180 oss << v[i].toString(trim); 181 if (i < v.size() - 1) oss << ","; 182 } 183 } else { 184 oss << "<->"; 185 } 186 oss << "]"; 187 } 188 return oss.str(); 189} 190 191// WARNING: Provide interpreter variable only if v is an uncopied reference 192// with a strong reference count. The reference (and the reference count of 193// the RCO) will be adjusted in case of an alias. 194Val Val::follow_alias(const Val& v, Interpreter* interpreter) { 195 if (v.isVar() && v.toVar()->aliased()) { 196 Val nval = v; 197 while (nval.isVar() && nval.toVar()->aliased()) { 198 nval = nval.toVar()->alias(); 199 } 200 if (interpreter && !interpreter->trail.is_trailed(v.toVar())) { 201 Val& mut_v = const_cast<Val&>(v); 202 nval.addRef(interpreter); 203 mut_v.rmRef(interpreter); 204 mut_v._v = nval._v; 205 } 206 return nval; 207 } else { 208 return v; 209 } 210} 211 212Val Val::lb() const { 213 if (isInt()) { 214 return *this; 215 } else if (isVec()) { 216 // Assume it is a set (sorted Vec of integer ranges) 217 assert(operator[](0).isInt()); 218 return operator[](0); 219 } else { 220 return toVar()->lb(); 221 } 222} 223 224Val Val::ub() const { 225 if (isInt()) { 226 return *this; 227 } else if (isVec()) { 228 // Assume it is a set (sorted Vec of integer ranges) 229 assert(operator[](size() - 1).isInt()); 230 return operator[](size() - 1); 231 } else { 232 return toVar()->ub(); 233 } 234} 235 236void Val::finalizeLin(Interpreter* interpreter) { 237 if (isInt()) { 238 return; 239 } 240 if (isVar()) { 241 Constraint* c = this->toVar()->defined_by(); 242 if (c && c->pred() <= PrimitiveMap::PARTIAL_LINEAR) { 243 // Create linear equation 244 std::vector<Val> coeffs = {Val(1)}; 245 std::vector<Val> vars = {*this}; 246 Val d = 0; 247 simplify_linexp(coeffs, vars, d); 248 249 Constraint* nc = nullptr; 250 if (vars.empty()) { 251 bool succes = this->toVar()->setVal(interpreter, d); 252 assert(succes); 253 } else if (c->pred() == PrimitiveMap::INT_TIMES && vars.size() == 1 && vars[0] == *this) { 254 // Times operation between two variables. Just leave it as it is. 255 return; 256 } else { 257 assert(std::none_of(vars.begin(), vars.end(), [&](Val v) { return v == *this; })); 258 coeffs.push_back(Val(-1)); 259 vars.push_back(*this); 260 261 Vec* ncoeffs = Vec::a(interpreter, interpreter->newIdent(), coeffs); 262 ncoeffs->addRef(interpreter); 263 Vec* nvars = Vec::a(interpreter, interpreter->newIdent(), vars); 264 nvars->addRef(interpreter); 265 266 std::vector<Val> args = {Val(ncoeffs), Val(nvars), Val(-d)}; 267 FixedKey<3> cse_key(*interpreter, args); 268 BytecodeProc::Mode mode = BytecodeProc::ROOT; 269 auto lookup = interpreter->cse_find(PrimitiveMap::INT_LIN_EQ, cse_key, mode); 270 if (lookup.second) { 271 // CSE Match found (perform cleanup) 272 assert(lookup.first.isInt()); 273 cse_key.destroy(*interpreter); 274 275 // This is the assumption that the linear expression doesn't exist 276 // in negated form in the CSE. If this would happen then the state 277 // should have been marked inconsistent. 278 assert(lookup.first.toInt() == 1); 279 } else { 280 bool b; 281 std::tie(nc, b) = Constraint::a(interpreter, PrimitiveMap::INT_LIN_EQ, mode, args); 282 assert(nc || b); 283 Val cse_val(1); 284 interpreter->cse_insert(PrimitiveMap::INT_LIN_EQ, cse_key, mode, cse_val); 285 } 286 287 RefCountedObject::rmRef(interpreter, ncoeffs); 288 RefCountedObject::rmRef(interpreter, nvars); 289 } 290 // Need to check whether variable still has a definition 291 // (may have been aliased during the construction of nc) 292 c = this->toVar()->defined_by(); 293 this->toVar()->_definitions.clear(); 294 if (nc) { 295 this->toVar()->addDefinition(interpreter, nc); 296 } 297 298 if (c) { 299 // still had a definition, so destroy it 300 // FIXME: c->destroy will remove a non-existing reference to this. 301 this->toVar()->addRef(interpreter); 302 c->destroy(interpreter); 303 ::free(c); 304 } 305 } 306 } else { 307 this->toVec()->finalizeLin(interpreter); 308 } 309} 310 311} // namespace MiniZinc