solve_VPSC.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /**
  2. * \file
  3. * \brief Solve an instance of the "Variable Placement with Separation
  4. * Constraints" problem.
  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 <cassert>
  20. #include <vpsc/constraint.h>
  21. #include <vpsc/block.h>
  22. #include <vpsc/blocks.h>
  23. #include <vpsc/solve_VPSC.h>
  24. #include <math.h>
  25. #include <memory>
  26. #include <sstream>
  27. #include <fstream>
  28. using std::ios;
  29. using std::ofstream;
  30. using std::ostringstream;
  31. using std::list;
  32. using std::set;
  33. #ifndef RECTANGLE_OVERLAP_LOGGING
  34. #define RECTANGLE_OVERLAP_LOGGING 0
  35. #endif
  36. IncVPSC::IncVPSC(const unsigned n, Variable *vs[], const unsigned m_,
  37. Constraint *cs_[])
  38. : VPSC(n, vs, m_, cs_) {
  39. inactive.assign(cs_, cs_ + m_);
  40. for(Constraint *c : inactive) {
  41. c->active=false;
  42. }
  43. }
  44. VPSC::VPSC(const unsigned n, Variable *vs[], const unsigned m_,
  45. Constraint *cs_[])
  46. : bs(n, vs), cs(cs_), m(m_) {
  47. if (RECTANGLE_OVERLAP_LOGGING) {
  48. printBlocks();
  49. assert(!constraintGraphIsCyclic(n,vs));
  50. }
  51. }
  52. // useful in debugging
  53. void VPSC::printBlocks() {
  54. if (RECTANGLE_OVERLAP_LOGGING) {
  55. ofstream f(LOGFILE,ios::app);
  56. for(Block *b : bs) {
  57. f<<" "<<*b<<"\n";
  58. }
  59. for(unsigned i=0;i<m;i++) {
  60. f<<" "<<*cs[i]<<"\n";
  61. }
  62. }
  63. }
  64. /**
  65. * Produces a feasible - though not necessarily optimal - solution by
  66. * examining blocks in the partial order defined by the directed acyclic
  67. * graph of constraints. For each block (when processing left to right) we
  68. * maintain the invariant that all constraints to the left of the block
  69. * (incoming constraints) are satisfied. This is done by repeatedly merging
  70. * blocks into bigger blocks across violated constraints (most violated
  71. * first) fixing the position of variables inside blocks relative to one
  72. * another so that constraints internal to the block are satisfied.
  73. */
  74. void VPSC::satisfy() {
  75. list<Variable*> vs=bs.totalOrder();
  76. for(Variable *v : vs) {
  77. if(!v->block->deleted) {
  78. bs.mergeLeft(v->block);
  79. }
  80. }
  81. bs.cleanup();
  82. for(unsigned i=0;i<m;i++) {
  83. if(cs[i]->slack()<-0.0000001) {
  84. if (RECTANGLE_OVERLAP_LOGGING) {
  85. ofstream f(LOGFILE,ios::app);
  86. f<<"Error: Unsatisfied constraint: "<<*cs[i]<<"\n";
  87. }
  88. //assert(cs[i]->slack()>-0.0000001);
  89. throw "Unsatisfied constraint";
  90. }
  91. }
  92. }
  93. void VPSC::refine() {
  94. for (bool solved = false; !solved;) {
  95. solved=true;
  96. for(Block *b : bs) {
  97. b->setUpInConstraints();
  98. b->setUpOutConstraints();
  99. }
  100. for(Block *b : bs) {
  101. Constraint *c=b->findMinLM();
  102. if(c!=nullptr && c->lm<0) {
  103. if (RECTANGLE_OVERLAP_LOGGING) {
  104. ofstream f(LOGFILE,ios::app);
  105. f<<"Split on constraint: "<<*c<<"\n";
  106. }
  107. // Split on c
  108. Block *l=nullptr, *r=nullptr;
  109. bs.split(b,l,r,c);
  110. bs.cleanup();
  111. // split alters the block set so we have to restart
  112. solved=false;
  113. break;
  114. }
  115. }
  116. }
  117. for(unsigned i=0;i<m;i++) {
  118. if(cs[i]->slack()<-0.0000001) {
  119. assert(cs[i]->slack()>-0.0000001);
  120. throw "Unsatisfied constraint";
  121. }
  122. }
  123. }
  124. /**
  125. * Calculate the optimal solution. After using satisfy() to produce a
  126. * feasible solution, refine() examines each block to see if further
  127. * refinement is possible by splitting the block. This is done repeatedly
  128. * until no further improvement is possible.
  129. */
  130. void VPSC::solve() {
  131. satisfy();
  132. refine();
  133. }
  134. void IncVPSC::solve() {
  135. if (RECTANGLE_OVERLAP_LOGGING) {
  136. ofstream f(LOGFILE,ios::app);
  137. f<<"solve_inc()...\n";
  138. }
  139. double lastcost,cost = bs.cost();
  140. do {
  141. lastcost=cost;
  142. satisfy();
  143. splitBlocks();
  144. cost = bs.cost();
  145. if (RECTANGLE_OVERLAP_LOGGING) {
  146. ofstream f(LOGFILE,ios::app);
  147. f<<" cost="<<cost<<"\n";
  148. }
  149. } while(fabs(lastcost-cost)>0.0001);
  150. }
  151. /**
  152. * incremental version of satisfy that allows refinement after blocks are
  153. * moved.
  154. *
  155. * - move blocks to new positions
  156. * - repeatedly merge across most violated constraint until no more
  157. * violated constraints exist
  158. *
  159. * Note: there is a special case to handle when the most violated constraint
  160. * is between two variables in the same block. Then, we must split the block
  161. * over an active constraint between the two variables. We choose the
  162. * constraint with the most negative lagrangian multiplier.
  163. */
  164. void IncVPSC::satisfy() {
  165. if (RECTANGLE_OVERLAP_LOGGING) {
  166. ofstream f(LOGFILE,ios::app);
  167. f<<"satisfy_inc()...\n";
  168. }
  169. splitBlocks();
  170. long splitCtr = 0;
  171. Constraint* v = nullptr;
  172. while(mostViolated(inactive,v)<-0.0000001) {
  173. assert(!v->active);
  174. Block *lb = v->left->block, *rb = v->right->block;
  175. if(lb != rb) {
  176. lb->merge(rb,v);
  177. } else {
  178. if(splitCtr++>10000) {
  179. throw "Cycle Error!";
  180. }
  181. // constraint is within block, need to split first
  182. inactive.push_back(lb->splitBetween(v->left,v->right,lb,rb));
  183. lb->merge(rb,v);
  184. bs.insert(lb);
  185. }
  186. }
  187. if (RECTANGLE_OVERLAP_LOGGING) {
  188. ofstream f(LOGFILE,ios::app);
  189. f<<" finished merges.\n";
  190. }
  191. bs.cleanup();
  192. for(unsigned i=0;i<m;i++) {
  193. v=cs[i];
  194. if(v->slack()<-0.0000001) {
  195. //assert(cs[i]->slack()>-0.0000001);
  196. ostringstream s;
  197. s<<"Unsatisfied constraint: "<<*v;
  198. throw s.str().c_str();
  199. }
  200. }
  201. if (RECTANGLE_OVERLAP_LOGGING) {
  202. ofstream f(LOGFILE,ios::app);
  203. f<<" finished cleanup.\n";
  204. printBlocks();
  205. }
  206. }
  207. void IncVPSC::moveBlocks() {
  208. if (RECTANGLE_OVERLAP_LOGGING) {
  209. ofstream f(LOGFILE,ios::app);
  210. f<<"moveBlocks()...\n";
  211. }
  212. for(Block *b : bs) {
  213. b->wposn = b->desiredWeightedPosition();
  214. b->posn = b->wposn / b->weight;
  215. }
  216. if (RECTANGLE_OVERLAP_LOGGING) {
  217. ofstream f(LOGFILE,ios::app);
  218. f<<" moved blocks.\n";
  219. }
  220. }
  221. void IncVPSC::splitBlocks() {
  222. moveBlocks();
  223. splitCnt=0;
  224. // Split each block if necessary on min LM
  225. for(Block *b : bs) {
  226. Constraint* v=b->findMinLM();
  227. if(v!=nullptr && v->lm < -0.0000001) {
  228. if (RECTANGLE_OVERLAP_LOGGING) {
  229. ofstream f(LOGFILE,ios::app);
  230. f<<" found split point: "<<*v<<" lm="<<v->lm<<"\n";
  231. }
  232. splitCnt++;
  233. Block *b2 = v->left->block, *l=nullptr, *r=nullptr;
  234. assert(v->left->block == v->right->block);
  235. double pos = b2->posn;
  236. b2->split(l,r,v);
  237. l->posn=r->posn=pos;
  238. l->wposn = l->posn * l->weight;
  239. r->wposn = r->posn * r->weight;
  240. bs.insert(l);
  241. bs.insert(r);
  242. b2->deleted=true;
  243. inactive.push_back(v);
  244. if (RECTANGLE_OVERLAP_LOGGING) {
  245. ofstream f(LOGFILE,ios::app);
  246. f<<" new blocks: "<<*l<<" and "<<*r<<"\n";
  247. }
  248. }
  249. }
  250. if (RECTANGLE_OVERLAP_LOGGING) {
  251. ofstream f(LOGFILE,ios::app);
  252. f<<" finished splits.\n";
  253. }
  254. bs.cleanup();
  255. }
  256. /**
  257. * Scan constraint list for the most violated constraint, or the first equality
  258. * constraint
  259. */
  260. double IncVPSC::mostViolated(ConstraintList &l, Constraint* &v) {
  261. double minSlack = DBL_MAX;
  262. if (RECTANGLE_OVERLAP_LOGGING) {
  263. ofstream f(LOGFILE,ios::app);
  264. f<<"Looking for most violated...\n";
  265. }
  266. ConstraintList::iterator end = l.end();
  267. ConstraintList::iterator deletePoint = end;
  268. for(ConstraintList::iterator i=l.begin();i!=end;i++) {
  269. Constraint *c=*i;
  270. double slack = c->slack();
  271. if (slack < minSlack) {
  272. minSlack=slack;
  273. v=c;
  274. deletePoint=i;
  275. }
  276. }
  277. // Because the constraint list is not order dependent we just
  278. // move the last element over the deletePoint and resize
  279. // downwards. There is always at least 1 element in the
  280. // vector because of search.
  281. if(deletePoint != end && minSlack<-0.0000001) {
  282. *deletePoint = l[l.size()-1];
  283. l.resize(l.size()-1);
  284. }
  285. if (RECTANGLE_OVERLAP_LOGGING) {
  286. ofstream f(LOGFILE,ios::app);
  287. f<<" most violated is: "<<*v<<"\n";
  288. }
  289. return minSlack;
  290. }
  291. #include <map>
  292. using std::map;
  293. using std::vector;
  294. struct node {
  295. set<node*> in;
  296. set<node*> out;
  297. };
  298. // useful in debugging - cycles would be BAD
  299. bool VPSC::constraintGraphIsCyclic(const unsigned n, Variable *vs[]) {
  300. map<Variable*, node*> varmap;
  301. vector<std::unique_ptr<node>> graph;
  302. for(unsigned i=0;i<n;i++) {
  303. graph.emplace_back(new node);
  304. varmap[vs[i]]=graph.back().get();
  305. }
  306. for(unsigned i=0;i<n;i++) {
  307. for(Constraint *c : vs[i]->in) {
  308. Variable *l=c->left;
  309. varmap[vs[i]]->in.insert(varmap[l]);
  310. }
  311. for(Constraint *c : vs[i]->out) {
  312. Variable *r=c->right;
  313. varmap[vs[i]]->out.insert(varmap[r]);
  314. }
  315. }
  316. while(!graph.empty()) {
  317. node *u=nullptr;
  318. vector<std::unique_ptr<node>>::iterator i=graph.begin();
  319. for(;i!=graph.end();i++) {
  320. u=(*i).get();
  321. if(u->in.empty()) {
  322. break;
  323. }
  324. }
  325. if(i==graph.end()) {
  326. //cycle found!
  327. return true;
  328. } else {
  329. graph.erase(i);
  330. for(node *v : u->out) {
  331. v->in.erase(u);
  332. }
  333. }
  334. }
  335. return false;
  336. }
  337. // useful in debugging - cycles would be BAD
  338. bool VPSC::blockGraphIsCyclic() {
  339. map<Block*, node*> bmap;
  340. vector<std::unique_ptr<node>> graph;
  341. for(Block *b : bs) {
  342. graph.emplace_back(new node);
  343. bmap[b]=graph.back().get();
  344. }
  345. for(Block *b : bs) {
  346. b->setUpInConstraints();
  347. Constraint *c=b->findMinInConstraint();
  348. while(c!=nullptr) {
  349. Block *l=c->left->block;
  350. bmap[b]->in.insert(bmap[l]);
  351. b->deleteMinInConstraint();
  352. c=b->findMinInConstraint();
  353. }
  354. b->setUpOutConstraints();
  355. c=b->findMinOutConstraint();
  356. while(c!=nullptr) {
  357. Block *r=c->right->block;
  358. bmap[b]->out.insert(bmap[r]);
  359. b->deleteMinOutConstraint();
  360. c=b->findMinOutConstraint();
  361. }
  362. }
  363. while(!graph.empty()) {
  364. node *u=nullptr;
  365. vector<std::unique_ptr<node>>::iterator i=graph.begin();
  366. for(;i!=graph.end();i++) {
  367. u=(*i).get();
  368. if(u->in.empty()) {
  369. break;
  370. }
  371. }
  372. if(i==graph.end()) {
  373. //cycle found!
  374. return true;
  375. }
  376. graph.erase(i);
  377. for(node *v : u->out) {
  378. v->in.erase(u);
  379. }
  380. }
  381. return false;
  382. }
  383. /**
  384. * @dir lib/vpsc
  385. * @brief library for solving the
  386. * %Variable Placement with Separation Constraints problem
  387. * for lib/neatogen
  388. *
  389. * This is a quadratic programming problem in which the squared differences
  390. * between a placement vector and some ideal placement are minimized subject
  391. * to a set of separation constraints. This is very useful in a number of layout problems.
  392. *
  393. * References:
  394. * - https://www.adaptagrams.org/documentation/libvpsc.html
  395. * - Tim Dwyer, Kim Marriott, and Peter J. Stuckey. Fast node overlap removal.
  396. * In Proceedings 13th International Symposium on Graph Drawing (GD '05),
  397. * volume 3843 of LNCS, pages 153-164. Springer, 2006.
  398. */