1 /* 2 * TdZdd: a Top-down/Breadth-first Decision Diagram Manipulation Framework 3 * by Hiroaki Iwashita <iwashita@erato.ist.hokudai.ac.jp> 4 * Copyright (c) 2014 ERATO MINATO Project 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 */ 24 25 #pragma once 26 27 #include <cassert> 28 #include <cmath> 29 #include <ostream> 30 #include <stdexcept> 31 32 #ifdef _OPENMP 33 #include <omp.h> 34 #endif 35 36 #include "Node.hpp" 37 #include "NodeTable.hpp" 38 #include "../util/MyHashTable.hpp" 39 #include "../util/MyList.hpp" 40 #include "../util/MyVector.hpp" 41 42 namespace tdzdd { 43 44 template<int ARITY, bool BDD, bool ZDD> 45 class DdReducer { 46 NodeTableEntity<ARITY>& input; 47 NodeTableHandler<ARITY> oldDiagram; 48 NodeTableHandler<ARITY> newDiagram; 49 NodeTableEntity<ARITY>& output; 50 MyVector<MyVector<NodeId> > newIdTable; 51 MyVector<MyVector<NodeId*> > rootPtr; 52 53 struct ReducNodeInfo { 54 Node<ARITY> children; 55 size_t column; 56 hashtdzdd::DdReducer::ReducNodeInfo57 size_t hash() const { 58 return children.hash(); 59 } 60 operator ==tdzdd::DdReducer::ReducNodeInfo61 bool operator==(ReducNodeInfo const& o) const { 62 return children == o.children; 63 } 64 operator <<(std::ostream & os,ReducNodeInfo const & o)65 friend std::ostream& operator<<(std::ostream& os, 66 ReducNodeInfo const& o) { 67 return os << "(" << o.children << " -> " << o.column << ")"; 68 } 69 }; 70 71 #ifdef _OPENMP 72 static int const TASKS_PER_THREAD = 10; 73 74 int const threads; 75 int const tasks; 76 MyVector<MyVector<MyList<ReducNodeInfo> > > taskMatrix; 77 MyVector<size_t> baseColumn; 78 79 #ifdef DEBUG 80 ElapsedTimeCounter etcP1, etcP2, etcP3, etcS0, etcS1, etcS2, etcS3, etcS4; 81 #endif 82 #endif 83 84 bool readyForSequentialReduction; 85 86 public: DdReducer(NodeTableHandler<ARITY> & diagram,bool useMP=false)87 DdReducer(NodeTableHandler<ARITY>& diagram, bool useMP = false) : 88 input(diagram.privateEntity()), 89 oldDiagram(diagram), 90 newDiagram(input.numRows()), 91 output(newDiagram.privateEntity()), 92 newIdTable(input.numRows()), 93 rootPtr(input.numRows()), 94 #ifdef _OPENMP 95 threads(omp_get_max_threads()), 96 tasks(MyHashConstant::primeSize(TASKS_PER_THREAD * threads)), 97 taskMatrix(threads), 98 baseColumn(tasks + 1), 99 #endif 100 readyForSequentialReduction(false) { 101 #ifdef _OPENMP 102 #ifdef DEBUG 103 if (useMP) { 104 MessageHandler mh; 105 mh << "#thread = " << threads << ", #task = " << tasks; 106 } 107 etcS0.start(); 108 #endif 109 #endif 110 diagram = newDiagram; 111 112 input.initTerminals(); 113 input.makeIndex(useMP); 114 115 newIdTable[0].resize(2); 116 newIdTable[0][0] = 0; 117 newIdTable[0][1] = 1; 118 119 #ifdef _OPENMP 120 for (int y = 0; y < threads; ++y) { 121 taskMatrix[y].resize(tasks); 122 } 123 #ifdef DEBUG 124 etcS0.stop(); 125 #endif 126 #endif 127 } 128 129 #ifdef _OPENMP 130 #ifdef DEBUG ~DdReducer()131 ~DdReducer() { 132 if (etcP1 != 0) { 133 MessageHandler mh; 134 mh << "P1: " << etcP1 << "\n"; 135 mh << "P2: " << etcP2 << "\n"; 136 mh << "P3: " << etcP3 << "\n"; 137 mh << "S0: " << etcS0 << "\n"; 138 mh << "S1: " << etcS1 << "\n"; 139 mh << "S2: " << etcS2 << "\n"; 140 mh << "S3: " << etcS3 << "\n"; 141 mh << "S4: " << etcS4 << "\n"; 142 } 143 } 144 #endif 145 #endif 146 147 private: 148 /** 149 * Applies the node deletion rules. 150 * It is required before serial reduction (Algorithm-R) 151 * in order to make lower-level index safe. 152 */ makeReadyForSequentialReduction()153 void makeReadyForSequentialReduction() { 154 if (readyForSequentialReduction) return; 155 #ifdef DEBUG 156 size_t dead = 0; 157 #endif 158 for (int i = 2; i < input.numRows(); ++i) { 159 size_t const m = input[i].size(); 160 Node<ARITY>* const tt = input[i].data(); 161 162 for (size_t j = 0; j < m; ++j) { 163 for (int b = 0; b < ARITY; ++b) { 164 NodeId& f = tt[j].branch[b]; 165 if (f.row() == 0) continue; 166 167 NodeId f0 = input.child(f, 0); 168 NodeId deletable = BDD ? f0 : 0; 169 bool del = true; 170 171 for (int bb = (BDD || ZDD) ? 1 : 0; bb < ARITY; ++bb) { 172 if (input.child(f, bb) != deletable) { 173 del = false; 174 } 175 } 176 177 if (del) { 178 f = f0; 179 #ifdef DEBUG 180 if (f == 0) ++dead; 181 #endif 182 } 183 } 184 } 185 } 186 #ifdef DEBUG 187 MessageHandler mh; 188 mh << "[#dead = " << dead << "]"; 189 #endif 190 input.makeIndex(); 191 readyForSequentialReduction = true; 192 } 193 194 public: 195 /** 196 * Sets a root node. 197 * @param root reference to a root node ID storage. 198 */ setRoot(NodeId & root)199 void setRoot(NodeId& root) { 200 rootPtr[root.row()].push_back(&root); 201 } 202 203 /** 204 * Reduces one level. 205 * @param i level. 206 * @param useMP use an algorithm for multiple processors. 207 */ reduce(int i,bool useMP=false)208 void reduce(int i, bool useMP = false) { 209 if (useMP) { 210 reduceMP_(i); 211 } 212 else if (ARITY == 2) { 213 algorithmR(i); 214 } 215 else { 216 reduce_(i); 217 } 218 } 219 220 private: 221 /** 222 * Reduces one level using Algorithm-R. 223 * @param i level. 224 */ algorithmR(int i)225 void algorithmR(int i) { 226 assert(ARITY == 2); 227 makeReadyForSequentialReduction(); 228 size_t const m = input[i].size(); 229 Node<ARITY>* const tt = input[i].data(); 230 NodeId const mark(i, m); 231 232 MyVector<NodeId>& newId = newIdTable[i]; 233 newId.resize(m); 234 235 for (size_t j = m - 1; j + 1 > 0; --j) { 236 NodeId& f0 = tt[j].branch[0]; 237 NodeId& f1 = tt[j].branch[1]; 238 239 if (f0.row() != 0) f0 = newIdTable[f0.row()][f0.col()]; 240 if (f1.row() != 0) f1 = newIdTable[f1.row()][f1.col()]; 241 242 if ((BDD && f1 == f0) || (ZDD && f1 == 0)) { 243 newId[j] = f0; 244 } 245 else { 246 NodeId& f00 = input.child(f0, 0); 247 NodeId& f01 = input.child(f0, 1); 248 249 if (f01 != mark) { // the first touch from this level 250 f01 = mark; // mark f0 as touched 251 newId[j] = NodeId(i + 1, m); // tail of f0-equivalent list 252 } 253 else { 254 newId[j] = f00; // next of f0-equivalent list 255 } 256 f00 = NodeId(i + 1, j); // new head of f0-equivalent list 257 } 258 } 259 260 { 261 MyVector<int> const& levels = input.lowerLevels(i); 262 for (int const* t = levels.begin(); t != levels.end(); ++t) { 263 newIdTable[*t].clear(); 264 } 265 } 266 size_t mm = 0; 267 268 for (size_t j = 0; j < m; ++j) { 269 NodeId const f(i, j); 270 assert(newId[j].row() <= i + 1); 271 if (newId[j].row() <= i) continue; 272 273 for (size_t k = j; k < m;) { // for each g in f0-equivalent list 274 assert(j <= k); 275 NodeId const g(i, k); 276 NodeId& g0 = tt[k].branch[0]; 277 NodeId& g1 = tt[k].branch[1]; 278 NodeId& g10 = input.child(g1, 0); 279 NodeId& g11 = input.child(g1, 1); 280 assert(g1 != mark); 281 assert(newId[k].row() == i + 1); 282 size_t next = newId[k].col(); 283 284 if (g11 != f) { // the first touch to g1 in f0-equivalent list 285 g11 = f; // mark g1 as touched 286 g10 = g; // record g as a canonical node for <f0,g1> 287 newId[k] = NodeId(i, mm++, g0.hasEmpty()); 288 } 289 else { 290 g0 = g10; // make a forward link 291 g1 = mark; // mark g as forwarded 292 newId[k] = 0; 293 } 294 295 k = next; 296 } 297 } 298 299 if (!BDD) { 300 MyVector<int> const& levels = input.lowerLevels(i); 301 for (int const* t = levels.begin(); t != levels.end(); ++t) { 302 input[*t].clear(); 303 } 304 } 305 306 output.initRow(i, mm); 307 Node<ARITY>* nt = output[i].data(); 308 309 for (size_t j = 0; j < m; ++j) { 310 NodeId const& f0 = tt[j].branch[0]; 311 NodeId const& f1 = tt[j].branch[1]; 312 313 if (f1 == mark) { // forwarded 314 assert(f0.row() == i); 315 assert(newId[j] == 0); 316 newId[j] = newId[f0.col()]; 317 } 318 else if ((BDD && f1 == f0) || (ZDD && f1 == 0)) { // forwarded 319 assert(newId[j].row() < i); 320 } 321 else { 322 assert(newId[j].row() == i); 323 size_t k = newId[j].col(); 324 nt[k] = tt[j]; 325 } 326 } 327 328 for (size_t k = 0; k < rootPtr[i].size(); ++k) { 329 NodeId& root = *rootPtr[i][k]; 330 root = newId[root.col()]; 331 } 332 } 333 334 /** 335 * Reduces one level. 336 * @param i level. 337 */ reduce_(int i)338 void reduce_(int i) { 339 size_t const m = input[i].size(); 340 newIdTable[i].resize(m); 341 size_t jj = 0; 342 343 { 344 //MyList<ReducNodeInfo> rni; 345 //MyHashTable<ReducNodeInfo const*> uniq(m * 2); 346 MyHashTable<Node<ARITY> const*> uniq(m * 2); 347 348 for (size_t j = 0; j < m; ++j) { 349 Node<ARITY>* const p0 = input[i].data(); 350 Node<ARITY>& f = input[i][j]; 351 352 // make f canonical 353 NodeId& f0 = f.branch[0]; 354 f0 = newIdTable[f0.row()][f0.col()]; 355 NodeId deletable = BDD ? f0 : 0; 356 bool del = BDD || ZDD || (f0 == 0); 357 for (int b = 1; b < ARITY; ++b) { 358 NodeId& ff = f.branch[b]; 359 ff = newIdTable[ff.row()][ff.col()]; 360 if (ff != deletable) del = false; 361 } 362 363 if (del) { // f is redundant 364 newIdTable[i][j] = f0; 365 } 366 else { 367 Node<ARITY> const* pp = uniq.add(&f); 368 369 if (pp == &f) { 370 newIdTable[i][j] = NodeId(i, jj++, f0.hasEmpty()); 371 } 372 else { 373 newIdTable[i][j] = newIdTable[i][pp - p0]; 374 } 375 } 376 } 377 } 378 379 MyVector<int> const& levels = input.lowerLevels(i); 380 for (int const* t = levels.begin(); t != levels.end(); ++t) { 381 newIdTable[*t].clear(); 382 } 383 384 output.initRow(i, jj); 385 386 for (size_t j = 0; j < m; ++j) { 387 NodeId const& ff = newIdTable[i][j]; 388 if (ff.row() == i) { 389 output[i][ff.col()] = input[i][j]; 390 } 391 } 392 393 input[i].clear(); 394 395 for (size_t k = 0; k < rootPtr[i].size(); ++k) { 396 NodeId& root = *rootPtr[i][k]; 397 root = newIdTable[i][root.col()]; 398 } 399 } 400 401 /** 402 * Reduces one level using OpenMP. 403 * @param i level. 404 */ reduceMP_(int i)405 void reduceMP_(int i) { 406 #ifndef _OPENMP 407 reduce_(i); 408 #else 409 #ifdef DEBUG 410 etcS1.start(); 411 #endif 412 size_t const m = input[i].size(); 413 newIdTable[i].resize(m); 414 #ifdef DEBUG 415 etcS1.stop(); 416 etcP1.start(); 417 #endif 418 419 #pragma omp parallel 420 { 421 int y = omp_get_thread_num(); 422 MyHashTable<ReducNodeInfo const*> uniq; 423 424 #pragma omp for schedule(static) 425 for (size_t j = 0; j < m; ++j) { 426 Node<ARITY>& f = input[i][j]; 427 428 // make f canonical 429 NodeId& f0 = f.branch[0]; 430 f0 = newIdTable[f0.row()][f0.col()]; 431 NodeId deletable = BDD ? f0 : 0; 432 bool del = BDD || ZDD || (f0 == 0); 433 for (int b = 1; b < ARITY; ++b) { 434 NodeId& ff = f.branch[b]; 435 ff = newIdTable[ff.row()][ff.col()]; 436 if (ff != deletable) del = false; 437 } 438 439 if (del) { // f is redundant 440 newIdTable[i][j] = f0; 441 continue; 442 } 443 444 // schedule a task 445 int x = f.hash() % tasks; 446 ReducNodeInfo* p = taskMatrix[y][x].alloc_front(); 447 p->children = f; 448 p->column = j; 449 } 450 451 #pragma omp single 452 { 453 #ifdef DEBUG 454 etcP1.stop(); 455 etcS2.start(); 456 #endif 457 MyVector<int> const& levels = input.lowerLevels(i); 458 for (int const* t = levels.begin(); t != levels.end(); ++t) { 459 newIdTable[*t].clear(); 460 } 461 #ifdef DEBUG 462 etcS2.stop(); 463 etcP2.start(); 464 #endif 465 } 466 467 #pragma omp for schedule(dynamic) 468 for (int x = 0; x < tasks; ++x) { 469 size_t mm = 0; 470 for (int yy = 0; yy < threads; ++yy) { 471 mm += taskMatrix[yy][x].size(); 472 } 473 if (mm == 0) { 474 baseColumn[x + 1] = 0; 475 continue; 476 } 477 478 uniq.initialize(mm * 2); 479 size_t j = 0; 480 481 for (int yy = 0; yy < threads; ++yy) { 482 MyList<ReducNodeInfo>& taskq = taskMatrix[yy][x]; 483 484 for (typename MyList<ReducNodeInfo>::iterator t = 485 taskq.begin(); t != taskq.end(); ++t) { 486 ReducNodeInfo const* p = *t; 487 ReducNodeInfo const* pp = uniq.add(p); 488 489 if (pp == p) { 490 newIdTable[i][p->column] = 491 NodeId(i + x, 492 j++, 493 p->children.branch[0].hasEmpty()); // row += task ID 494 } 495 else { 496 newIdTable[i][p->column] = 497 newIdTable[i][pp->column]; 498 } 499 } 500 } 501 502 baseColumn[x + 1] = j; 503 } 504 505 for (int x = 0; x < tasks; ++x) { 506 taskMatrix[y][x].clear(); 507 } 508 509 #pragma omp single 510 { 511 #ifdef DEBUG 512 etcP2.stop(); 513 etcS3.start(); 514 #endif 515 for (int x = 1; x < tasks; ++x) { 516 baseColumn[x + 1] += baseColumn[x]; 517 } 518 519 // for (int k = 1; k < tasks; k <<= 1) { 520 //#pragma omp for schedule(static) 521 // for (int x = k; x < tasks; ++x) { 522 // if (x & k) { 523 // int w = (x & ~k) | (k - 1); 524 // baseColumn[x + 1] += baseColumn[w + 1]; 525 // } 526 // } 527 // } 528 529 output.initRow(i, baseColumn[tasks]); 530 #ifdef DEBUG 531 etcS3.stop(); 532 etcP3.start(); 533 #endif 534 } 535 536 #pragma omp for schedule(static) 537 for (size_t j = 0; j < m; ++j) { 538 NodeId& ff = newIdTable[i][j]; 539 if (ff.row() >= i) { 540 ff = NodeId(i, 541 ff.col() + baseColumn[ff.row() - i], 542 ff.getAttr()); 543 output[i][ff.col()] = input[i][j]; 544 } 545 } 546 } 547 #ifdef DEBUG 548 etcP3.stop(); 549 etcS4.start(); 550 #endif 551 input[i].clear(); 552 553 for (size_t k = 0; k < rootPtr[i].size(); ++k) { 554 NodeId& root = *rootPtr[i][k]; 555 root = newIdTable[i][root.col()]; 556 } 557 #ifdef DEBUG 558 etcS4.stop(); 559 #endif 560 #endif // _OPENMP 561 } 562 563 public: garbageCollect()564 void garbageCollect() { 565 // Initialize marks 566 for (int i = input.numRows() - 1; i > 0; --i) { 567 size_t m = input[i].size(); 568 569 size_t r = rootPtr[i].size(); 570 MyVector<size_t> roots; 571 roots.reserve(r + 1); 572 for (size_t k = 0; k < r; ++k) { 573 roots.push_back(rootPtr[i][k]->col()); 574 } 575 if (r >= 2) { 576 std::sort(roots.begin(), roots.end()); 577 roots.resize(std::unique(roots.begin(), roots.end()) 578 - roots.begin()); 579 } 580 roots.push_back(m); 581 582 for (size_t j = 0, k = 0; j < m; ++j) { 583 if (j < roots[k]) { 584 input.child(i, j, 0).setAttr(true); 585 } 586 else { 587 assert(j == roots[k]); 588 input.child(i, j, 0).setAttr(false); 589 ++k; 590 } 591 } 592 } 593 594 // Delete unnecessary nodes 595 for (int i = input.numRows() - 1; i > 0; --i) { 596 size_t m = input[i].size(); 597 for (size_t j = 0; j < m; ++j) { 598 NodeId& f0 = input.child(i, j, 0); 599 if (f0.getAttr()) { 600 f0 = 0; 601 for (int b = 1; b < ARITY; ++b) { 602 NodeId& ff = input.child(i, j, b); 603 ff = 0; 604 } 605 } 606 else { 607 input.child(f0, 0).setAttr(false); 608 for (int b = 1; b < ARITY; ++b) { 609 NodeId& ff = input.child(i, j, b); 610 input.child(ff, 0).setAttr(false); 611 } 612 } 613 } 614 } 615 } 616 }; 617 618 } // namespace tdzdd 619