block.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /**
  2. * \brief A block is a group of variables that must be moved together to improve
  3. * the goal function without violating already active constraints.
  4. * The variables in a block are spanned by a tree of active constraints.
  5. *
  6. * Authors:
  7. * Tim Dwyer <[email protected]>
  8. *
  9. * Copyright (C) 2005 Authors
  10. *
  11. * This version is released under the CPL (Common Public License) with
  12. * the Graphviz distribution.
  13. * A version is also available under the LGPL as part of the Adaptagrams
  14. * project: https://github.com/mjwybrow/adaptagrams.
  15. * If you make improvements or bug fixes to this code it would be much
  16. * appreciated if you could also contribute those changes back to the
  17. * Adaptagrams repository.
  18. */
  19. #include <algorithm>
  20. #include <cassert>
  21. #include <ostream>
  22. #include <vector>
  23. #include <vpsc/constraint.h>
  24. #include <vpsc/block.h>
  25. #include <vpsc/blocks.h>
  26. #include <fstream>
  27. using std::ios;
  28. using std::ofstream;
  29. using std::vector;
  30. #ifndef RECTANGLE_OVERLAP_LOGGING
  31. #define RECTANGLE_OVERLAP_LOGGING 0
  32. #endif
  33. /// `>` comparator for constraints
  34. static bool gt(const Constraint *const lhs, const Constraint *const rhs) {
  35. return compareConstraints(rhs, lhs);
  36. }
  37. /// create a heap within a vector
  38. ///
  39. /// The standard library’s heap functionality is all structured around creating
  40. /// a max-heap but we want a min-heap. So we flip the comparator we give it.
  41. static void make_heap(std::vector<Constraint *> &heap) {
  42. std::make_heap(heap.begin(), heap.end(), gt);
  43. }
  44. /// add all elements from `heap2` into the heap `heap1`
  45. static void merge_heaps(std::vector<Constraint *> &heap1,
  46. const std::vector<Constraint *> &heap2) {
  47. heap1.insert(heap1.end(), heap2.begin(), heap2.end());
  48. make_heap(heap1);
  49. }
  50. /// get the minimum heap element
  51. static Constraint *findMin(std::vector<Constraint *> &heap) {
  52. assert(std::is_heap(heap.begin(), heap.end(), gt));
  53. return heap.front();
  54. }
  55. /// remove the minimum heap element
  56. static void deleteMin(std::vector<Constraint *> &heap) {
  57. assert(std::is_heap(heap.begin(), heap.end(), gt));
  58. std::pop_heap(heap.begin(), heap.end(), gt);
  59. heap.pop_back();
  60. }
  61. /// add an item to a heap
  62. static void insert(std::vector<Constraint *> &heap, Constraint *c) {
  63. assert(std::is_heap(heap.begin(), heap.end(), gt));
  64. heap.push_back(c);
  65. std::push_heap(heap.begin(), heap.end(), gt);
  66. }
  67. void Block::addVariable(Variable *v) {
  68. v->block=this;
  69. vars.push_back(v);
  70. weight+=v->weight;
  71. wposn += v->weight * (v->desiredPosition - v->offset);
  72. posn=wposn/weight;
  73. }
  74. Block::Block(Variable *v) {
  75. timeStamp=0;
  76. posn=weight=wposn=0;
  77. deleted=false;
  78. if(v!=nullptr) {
  79. v->offset=0;
  80. addVariable(v);
  81. }
  82. }
  83. double Block::desiredWeightedPosition() {
  84. double wp = 0;
  85. for (const Variable *v : vars) {
  86. wp += (v->desiredPosition - v->offset) * v->weight;
  87. }
  88. return wp;
  89. }
  90. void Block::setUpInConstraints() {
  91. in = setUpConstraintHeap(true);
  92. }
  93. void Block::setUpOutConstraints() {
  94. out = setUpConstraintHeap(false);
  95. }
  96. std::vector<Constraint *> Block::setUpConstraintHeap(bool use_in) {
  97. std::vector<Constraint *> h;
  98. for (Variable *v : vars) {
  99. vector<Constraint*> *cs= use_in ? &v->in : &v->out;
  100. for (Constraint *c : *cs) {
  101. c->timeStamp=blockTimeCtr;
  102. if ((c->left->block != this && use_in) || (c->right->block != this && !use_in)) {
  103. h.push_back(c);
  104. }
  105. }
  106. }
  107. make_heap(h);
  108. return h;
  109. }
  110. void Block::merge(Block* b, Constraint* c) {
  111. if (RECTANGLE_OVERLAP_LOGGING) {
  112. ofstream f(LOGFILE,ios::app);
  113. f<<" merging on: "<<*c<<",c->left->offset="<<c->left->offset<<",c->right->offset="<<c->right->offset<<"\n";
  114. }
  115. const double dist = c->right->offset - c->left->offset - c->gap;
  116. Block *l=c->left->block;
  117. Block *r=c->right->block;
  118. if (vars.size() < b->vars.size()) {
  119. r->merge(l,c,dist);
  120. } else {
  121. l->merge(r,c,-dist);
  122. }
  123. if (RECTANGLE_OVERLAP_LOGGING) {
  124. ofstream f(LOGFILE,ios::app);
  125. f<<" merged block="<<(b->deleted?*this:*b)<<"\n";
  126. }
  127. }
  128. /**
  129. * Merges b into this block across c. Can be either a
  130. * right merge or a left merge
  131. * @param b block to merge into this
  132. * @param c constraint being merged
  133. * @param distance separation required to satisfy c
  134. */
  135. void Block::merge(Block *b, Constraint *c, double dist) {
  136. if (RECTANGLE_OVERLAP_LOGGING) {
  137. ofstream f(LOGFILE,ios::app);
  138. f<<" merging: "<<*b<<"dist="<<dist<<"\n";
  139. }
  140. c->active=true;
  141. wposn+=b->wposn-dist*b->weight;
  142. weight+=b->weight;
  143. posn=wposn/weight;
  144. for (Variable *v : b->vars) {
  145. v->block=this;
  146. v->offset+=dist;
  147. vars.push_back(v);
  148. }
  149. b->deleted=true;
  150. }
  151. void Block::mergeIn(Block *b) {
  152. if (RECTANGLE_OVERLAP_LOGGING) {
  153. ofstream f(LOGFILE,ios::app);
  154. f<<" merging constraint heaps... \n";
  155. }
  156. // We check the top of the heaps to remove possible internal constraints
  157. findMinInConstraint();
  158. b->findMinInConstraint();
  159. merge_heaps(in, b->in);
  160. }
  161. void Block::mergeOut(Block *b) {
  162. findMinOutConstraint();
  163. b->findMinOutConstraint();
  164. merge_heaps(out, b->out);
  165. }
  166. Constraint *Block::findMinInConstraint() {
  167. Constraint *v = nullptr;
  168. vector<Constraint*> outOfDate;
  169. while (!in.empty()) {
  170. v = findMin(in);
  171. const Block *lb = v->left->block;
  172. const Block *rb = v->right->block;
  173. // rb may not be this if called between merge and mergeIn
  174. if (RECTANGLE_OVERLAP_LOGGING) {
  175. ofstream f(LOGFILE,ios::app);
  176. f<<" checking constraint ... "<<*v;
  177. f<<" timestamps: left="<<lb->timeStamp<<" right="<<rb->timeStamp<<" constraint="<<v->timeStamp<<"\n";
  178. }
  179. if(lb == rb) {
  180. // constraint has been merged into the same block
  181. if(RECTANGLE_OVERLAP_LOGGING && v->slack()<0) {
  182. ofstream f(LOGFILE,ios::app);
  183. f<<" violated internal constraint found! "<<*v<<"\n";
  184. f<<" lb="<<*lb<<"\n";
  185. f<<" rb="<<*rb<<"\n";
  186. }
  187. deleteMin(in);
  188. if (RECTANGLE_OVERLAP_LOGGING) {
  189. ofstream f(LOGFILE,ios::app);
  190. f<<" ... skipping internal constraint\n";
  191. }
  192. } else if(v->timeStamp < lb->timeStamp) {
  193. // block at other end of constraint has been moved since this
  194. deleteMin(in);
  195. outOfDate.push_back(v);
  196. if (RECTANGLE_OVERLAP_LOGGING) {
  197. ofstream f(LOGFILE,ios::app);
  198. f<<" reinserting out of date (reinsert later)\n";
  199. }
  200. } else {
  201. break;
  202. }
  203. }
  204. for (Constraint *c : outOfDate) {
  205. c->timeStamp=blockTimeCtr;
  206. insert(in, c);
  207. }
  208. if(in.empty()) {
  209. v=nullptr;
  210. } else {
  211. v = findMin(in);
  212. }
  213. return v;
  214. }
  215. Constraint *Block::findMinOutConstraint() {
  216. if(out.empty()) return nullptr;
  217. Constraint *v = findMin(out);
  218. while (v->left->block == v->right->block) {
  219. deleteMin(out);
  220. if(out.empty()) return nullptr;
  221. v = findMin(out);
  222. }
  223. return v;
  224. }
  225. void Block::deleteMinInConstraint() {
  226. deleteMin(in);
  227. }
  228. void Block::deleteMinOutConstraint() {
  229. deleteMin(out);
  230. }
  231. inline bool Block::canFollowLeft(const Constraint *c, const Variable *last) {
  232. return c->left->block==this && c->active && last!=c->left;
  233. }
  234. inline bool Block::canFollowRight(const Constraint *c, const Variable *last) {
  235. return c->right->block==this && c->active && last!=c->right;
  236. }
  237. // computes the derivative of v and the lagrange multipliers
  238. // of v's out constraints (as the recursive sum of those below.
  239. // Does not backtrack over u.
  240. // also records the constraint with minimum lagrange multiplier
  241. // in min_lm
  242. double Block::compute_dfdv(Variable *v, Variable *u, Constraint *&min_lm) {
  243. double dfdv=v->weight*(v->position() - v->desiredPosition);
  244. for (Constraint *c : v->out) {
  245. if(canFollowRight(c,u)) {
  246. dfdv+=c->lm=compute_dfdv(c->right,v,min_lm);
  247. if(min_lm==nullptr||c->lm<min_lm->lm) min_lm=c;
  248. }
  249. }
  250. for (Constraint *c : v->in) {
  251. if(canFollowLeft(c,u)) {
  252. dfdv-=c->lm=-compute_dfdv(c->left,v,min_lm);
  253. if(min_lm==nullptr||c->lm<min_lm->lm) min_lm=c;
  254. }
  255. }
  256. return dfdv;
  257. }
  258. // computes dfdv for each variable and uses the sum of dfdv on either side of
  259. // the constraint c to compute the lagrangian multiplier lm_c.
  260. // The top level v and r are variables between which we want to find the
  261. // constraint with the smallest lm.
  262. // When we find r we pass nullptr to subsequent recursive calls,
  263. // thus r=nullptr indicates constraints are not on the shortest path.
  264. // Similarly, m is initially nullptr and is only assigned a value if the next
  265. // variable to be visited is r or if a possible min constraint is returned from
  266. // a nested call (rather than nullptr).
  267. // Then, the search for the m with minimum lm occurs as we return from
  268. // the recursion (checking only constraints traversed left-to-right
  269. // in order to avoid creating any new violations).
  270. Block::Pair Block::compute_dfdv_between(Variable* r, Variable* v, Variable* u,
  271. Direction dir = NONE, bool changedDirection = false) {
  272. double dfdv=v->weight*(v->position() - v->desiredPosition);
  273. Constraint *m=nullptr;
  274. for (Constraint *c : v->in) {
  275. if(canFollowLeft(c,u)) {
  276. if(dir==RIGHT) {
  277. changedDirection = true;
  278. }
  279. if(c->left==r) {
  280. r=nullptr; m=c;
  281. }
  282. Pair p=compute_dfdv_between(r,c->left,v,
  283. LEFT,changedDirection);
  284. dfdv -= c->lm = -p.first;
  285. if(r && p.second)
  286. m = p.second;
  287. }
  288. }
  289. for (Constraint *c : v->out) {
  290. if(canFollowRight(c,u)) {
  291. if(dir==LEFT) {
  292. changedDirection = true;
  293. }
  294. if(c->right==r) {
  295. r=nullptr; m=c;
  296. }
  297. Pair p=compute_dfdv_between(r,c->right,v,
  298. RIGHT,changedDirection);
  299. dfdv += c->lm = p.first;
  300. if(r && p.second)
  301. m = changedDirection && c->lm < p.second->lm
  302. ? c
  303. : p.second;
  304. }
  305. }
  306. return Pair(dfdv,m);
  307. }
  308. // resets LMs for all active constraints to 0 by
  309. // traversing active constraint tree starting from v,
  310. // not back tracking over u
  311. void Block::reset_active_lm(Variable *v, Variable *u) {
  312. for (Constraint *c : v->out) {
  313. if(canFollowRight(c,u)) {
  314. c->lm=0;
  315. reset_active_lm(c->right,v);
  316. }
  317. }
  318. for (Constraint *c : v->in) {
  319. if(canFollowLeft(c,u)) {
  320. c->lm=0;
  321. reset_active_lm(c->left,v);
  322. }
  323. }
  324. }
  325. /**
  326. * finds the constraint with the minimum lagrange multiplier, that is, the constraint
  327. * that most wants to split
  328. */
  329. Constraint *Block::findMinLM() {
  330. Constraint *min_lm=nullptr;
  331. reset_active_lm(vars.front(),nullptr);
  332. compute_dfdv(vars.front(),nullptr,min_lm);
  333. return min_lm;
  334. }
  335. Constraint *Block::findMinLMBetween(Variable* lv, Variable* rv) {
  336. Constraint *min_lm=nullptr;
  337. reset_active_lm(vars.front(),nullptr);
  338. min_lm=compute_dfdv_between(rv,lv,nullptr).second;
  339. return min_lm;
  340. }
  341. // populates block b by traversing the active constraint tree adding variables as they're
  342. // visited. Starts from variable v and does not backtrack over variable u.
  343. void Block::populateSplitBlock(Block *b, Variable *v, Variable *u) {
  344. b->addVariable(v);
  345. for (Constraint *c : v->in) {
  346. if (canFollowLeft(c,u))
  347. populateSplitBlock(b, c->left, v);
  348. }
  349. for (Constraint *c : v->out) {
  350. if (canFollowRight(c,u))
  351. populateSplitBlock(b, c->right, v);
  352. }
  353. }
  354. /**
  355. * Block needs to be split because of a violated constraint between vl and vr.
  356. * We need to search the active constraint tree between l and r and find the constraint
  357. * with min lagrangrian multiplier and split at that point.
  358. * Returns the split constraint
  359. */
  360. Constraint* Block::splitBetween(Variable* vl, Variable* vr, Block* &lb, Block* &rb) {
  361. if (RECTANGLE_OVERLAP_LOGGING) {
  362. ofstream f(LOGFILE,ios::app);
  363. f<<" need to split between: "<<*vl<<" and "<<*vr<<"\n";
  364. }
  365. Constraint *c=findMinLMBetween(vl, vr);
  366. if (RECTANGLE_OVERLAP_LOGGING) {
  367. ofstream f(LOGFILE,ios::app);
  368. f<<" going to split on: "<<*c<<"\n";
  369. }
  370. split(lb,rb,c);
  371. deleted = true;
  372. return c;
  373. }
  374. /**
  375. * Creates two new blocks, l and r, and splits this block across constraint c,
  376. * placing the left subtree of constraints (and associated variables) into l
  377. * and the right into r.
  378. */
  379. void Block::split(Block* &l, Block* &r, Constraint* c) {
  380. c->active=false;
  381. l=new Block();
  382. populateSplitBlock(l,c->left,c->right);
  383. r=new Block();
  384. populateSplitBlock(r,c->right,c->left);
  385. }
  386. /**
  387. * Computes the cost (squared euclidean distance from desired positions) of the
  388. * current positions for variables in this block
  389. */
  390. double Block::cost() {
  391. double c = 0;
  392. for (const Variable *v : vars) {
  393. const double diff = v->position() - v->desiredPosition;
  394. c += v->weight * diff * diff;
  395. }
  396. return c;
  397. }
  398. std::ostream& operator <<(std::ostream &os, const Block &b) {
  399. os<<"Block:";
  400. for(const Variable *v : b.vars) {
  401. os<<" "<<*v;
  402. }
  403. if(b.deleted) {
  404. os<<" Deleted!";
  405. }
  406. return os;
  407. }