]> granicus.if.org Git - graphviz/commitdiff
Add the vpsc library for IPSEPCOLA features
authorerg <devnull@localhost>
Fri, 28 Apr 2006 19:03:46 +0000 (19:03 +0000)
committererg <devnull@localhost>
Fri, 28 Apr 2006 19:03:46 +0000 (19:03 +0000)
lib/vpsc/solve_VPSC.cpp [new file with mode: 0644]

diff --git a/lib/vpsc/solve_VPSC.cpp b/lib/vpsc/solve_VPSC.cpp
new file mode 100644 (file)
index 0000000..dccede6
--- /dev/null
@@ -0,0 +1,416 @@
+/**
+ * \brief Solve an instance of the "Variable Placement with Separation
+ * Constraints" problem.
+ *
+ * Authors:
+ *   Tim Dwyer <tgdwyer@gmail.com>
+ *
+ * Copyright (C) 2005 Authors
+ * 
+ * This version is released under the CPL (Common Public License) with
+ * the Graphviz distribution.
+ * A version is also available under the LGPL as part of the Adaptagrams
+ * project: http://sourceforge.net/projects/adaptagrams.  
+ * If you make improvements or bug fixes to this code it would be much
+ * appreciated if you could also contribute those changes back to the
+ * Adaptagrams repository.
+ */
+
+#include <cassert>
+#include "constraint.h"
+#include "block.h"
+#include "blocks.h"
+#include "solve_VPSC.h"
+#include <math.h>
+#include <sstream>
+#ifdef RECTANGLE_OVERLAP_LOGGING
+#include <fstream>
+using std::ios;
+using std::ofstream;
+using std::endl;
+#endif
+
+using std::ostringstream;
+using std::list;
+using std::set;
+
+IncVPSC::IncVPSC(const unsigned n, Variable *vs[], const unsigned m, Constraint *cs[]) 
+       : VPSC(n,vs,m,cs) {
+       inactive.assign(cs,cs+m);
+       for(ConstraintList::iterator i=inactive.begin();i!=inactive.end();i++) {
+               (*i)->active=false;
+       }
+}
+VPSC::VPSC(const unsigned n, Variable *vs[], const unsigned m, Constraint *cs[]) : cs(cs), m(m) {
+       bs=new Blocks(n, vs);
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       printBlocks();
+       assert(!constraintGraphIsCyclic(n,vs));
+#endif
+}
+VPSC::~VPSC() {
+       delete bs;
+}
+
+// useful in debugging
+void VPSC::printBlocks() {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       ofstream f(LOGFILE,ios::app);
+       for(set<Block*>::iterator i=bs->begin();i!=bs->end();i++) {
+               Block *b=*i;
+               f<<"  "<<*b<<endl;
+       }
+       for(unsigned i=0;i<m;i++) {
+               f<<"  "<<*cs[i]<<endl;
+       }
+#endif
+}
+/**
+* Produces a feasible - though not necessarily optimal - solution by
+* examining blocks in the partial order defined by the directed acyclic
+* graph of constraints. For each block (when processing left to right) we
+* maintain the invariant that all constraints to the left of the block
+* (incoming constraints) are satisfied. This is done by repeatedly merging
+* blocks into bigger blocks across violated constraints (most violated
+* first) fixing the position of variables inside blocks relative to one
+* another so that constraints internal to the block are satisfied.
+*/
+void VPSC::satisfy() {
+       list<Variable*> *vs=bs->totalOrder();
+       for(list<Variable*>::iterator i=vs->begin();i!=vs->end();i++) {
+               Variable *v=*i;
+               if(!v->block->deleted) {
+                       bs->mergeLeft(v->block);
+               }
+       }
+       bs->cleanup();
+       for(unsigned i=0;i<m;i++) {
+               if(cs[i]->slack()<-0.0000001) {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+                       ofstream f(LOGFILE,ios::app);
+                       f<<"Error: Unsatisfied constraint: "<<*cs[i]<<endl;
+#endif
+                       //assert(cs[i]->slack()>-0.0000001);
+                       throw "Unsatisfied constraint";
+               }
+       }
+       delete vs;
+}
+
+void VPSC::refine() {
+       bool solved=false;
+       // Solve shouldn't loop indefinately
+       // ... but just to make sure we limit the number of iterations
+       unsigned maxtries=100;
+       while(!solved&&maxtries>=0) {
+               solved=true;
+               maxtries--;
+               for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
+                       Block *b=*i;
+                       b->setUpInConstraints();
+                       b->setUpOutConstraints();
+               }
+               for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
+                       Block *b=*i;
+                       Constraint *c=b->findMinLM();
+                       if(c!=NULL && c->lm<0) {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+                               ofstream f(LOGFILE,ios::app);
+                               f<<"Split on constraint: "<<*c<<endl;
+#endif
+                               // Split on c
+                               Block *l=NULL, *r=NULL;
+                               bs->split(b,l,r,c);
+                               bs->cleanup();
+                               // split alters the block set so we have to restart
+                               solved=false;
+                               break;
+                       }
+               }
+       }
+       for(unsigned i=0;i<m;i++) {
+               if(cs[i]->slack()<-0.0000001) {
+                       assert(cs[i]->slack()>-0.0000001);
+                       throw "Unsatisfied constraint";
+               }
+       }
+}
+/**
+ * Calculate the optimal solution. After using satisfy() to produce a
+ * feasible solution, refine() examines each block to see if further
+ * refinement is possible by splitting the block. This is done repeatedly
+ * until no further improvement is possible.
+ */
+void VPSC::solve() {
+       satisfy();
+       refine();
+}
+
+void IncVPSC::solve() {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       ofstream f(LOGFILE,ios::app);
+       f<<"solve_inc()..."<<endl;
+#endif
+       double lastcost,cost = bs->cost();
+       do {
+               lastcost=cost;
+               satisfy();
+               splitBlocks();
+               cost = bs->cost();
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       f<<"  cost="<<cost<<endl;
+#endif
+       } while(fabs(lastcost-cost)>0.0001);
+}
+/**
+ * incremental version of satisfy that allows refinement after blocks are
+ * moved.
+ *
+ *  - move blocks to new positions
+ *  - repeatedly merge across most violated constraint until no more
+ *    violated constraints exist
+ *
+ * Note: there is a special case to handle when the most violated constraint
+ * is between two variables in the same block.  Then, we must split the block
+ * over an active constraint between the two variables.  We choose the 
+ * constraint with the most negative lagrangian multiplier. 
+ */
+void IncVPSC::satisfy() {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       ofstream f(LOGFILE,ios::app);
+       f<<"satisfy_inc()..."<<endl;
+#endif
+       splitBlocks();
+       long splitCtr = 0;
+       Constraint* v = NULL;
+       while(mostViolated(inactive,v)<-0.0000001) {
+               assert(!v->active);
+               Block *lb = v->left->block, *rb = v->right->block;
+               if(lb != rb) {
+                       lb->merge(rb,v);
+               } else {
+                       if(splitCtr++>10000) {
+                               throw "Cycle Error!";
+                       }
+                       // constraint is within block, need to split first
+                       inactive.push_back(lb->splitBetween(v->left,v->right,lb,rb));
+                       lb->merge(rb,v);
+                       bs->insert(lb);
+               }
+       }
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       f<<"  finished merges."<<endl;
+#endif
+       bs->cleanup();
+       for(unsigned i=0;i<m;i++) {
+               v=cs[i];
+               if(v->slack()<-0.0000001) {
+                       //assert(cs[i]->slack()>-0.0000001);
+                       ostringstream s;
+                       s<<"Unsatisfied constraint: "<<*v;
+                       throw s.str().c_str();
+               }
+       }
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       f<<"  finished cleanup."<<endl;
+       printBlocks();
+#endif
+}
+void IncVPSC::moveBlocks() {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       ofstream f(LOGFILE,ios::app);
+       f<<"moveBlocks()..."<<endl;
+#endif
+       for(set<Block*>::const_iterator i(bs->begin());i!=bs->end();i++) {
+               Block *b = *i;
+               b->wposn = b->desiredWeightedPosition();
+               b->posn = b->wposn / b->weight;
+       }
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       f<<"  moved blocks."<<endl;
+#endif
+}
+void IncVPSC::splitBlocks() {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       ofstream f(LOGFILE,ios::app);
+#endif
+       moveBlocks();
+       splitCnt=0;
+       // Split each block if necessary on min LM
+       for(set<Block*>::const_iterator i(bs->begin());i!=bs->end();i++) {
+               Block* b = *i;
+               Constraint* v=b->findMinLM();
+               if(v!=NULL && v->lm < -0.0000001) {
+#ifdef RECTANGLE_OVERLAP_LOGGING
+                       f<<"    found split point: "<<*v<<" lm="<<v->lm<<endl;
+#endif
+                       splitCnt++;
+                       Block *b = v->left->block, *l=NULL, *r=NULL;
+                       assert(v->left->block == v->right->block);
+                       double pos = b->posn;
+                       b->split(l,r,v);
+                       l->posn=r->posn=pos;
+                       l->wposn = l->posn * l->weight;
+                       r->wposn = r->posn * r->weight;
+                       bs->insert(l);
+                       bs->insert(r);
+                       b->deleted=true;
+                       inactive.push_back(v);
+#ifdef RECTANGLE_OVERLAP_LOGGING
+                       f<<"  new blocks: "<<*l<<" and "<<*r<<endl;
+#endif
+               }
+       }
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       f<<"  finished splits."<<endl;
+#endif
+       bs->cleanup();
+}
+
+/**
+ * Scan constraint list for the most violated constraint, or the first equality
+ * constraint
+ */
+double IncVPSC::mostViolated(ConstraintList &l, Constraint* &v) {
+       double minSlack = DBL_MAX;
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       ofstream f(LOGFILE,ios::app);
+       f<<"Looking for most violated..."<<endl;
+#endif
+       ConstraintList::iterator end = l.end();
+       ConstraintList::iterator deletePoint = end;
+       for(ConstraintList::iterator i=l.begin();i!=end;i++) {
+               Constraint *c=*i;
+               double slack = c->slack();
+               if(c->equality || slack < minSlack) {
+                       minSlack=slack; 
+                       v=c;
+                       deletePoint=i;
+                       if(c->equality) break;
+               }
+       }
+       // Because the constraint list is not order dependent we just
+       // move the last element over the deletePoint and resize
+       // downwards.  There is always at least 1 element in the
+       // vector because of search.
+       if(deletePoint != end && minSlack<-0.0000001) {
+               *deletePoint = l[l.size()-1];
+               l.resize(l.size()-1);
+       }
+#ifdef RECTANGLE_OVERLAP_LOGGING
+       f<<"  most violated is: "<<*v<<endl;
+#endif
+       return minSlack;
+}
+
+#include <map>
+using std::map;
+using std::vector;
+struct node {
+       set<node*> in;
+       set<node*> out;
+};
+// useful in debugging - cycles would be BAD
+bool VPSC::constraintGraphIsCyclic(const unsigned n, Variable *vs[]) {
+       map<Variable*, node*> varmap;
+       vector<node*> graph;
+       for(unsigned i=0;i<n;i++) {
+               node *u=new node;
+               graph.push_back(u);
+               varmap[vs[i]]=u;
+       }
+       for(unsigned i=0;i<n;i++) {
+               for(vector<Constraint*>::iterator c=vs[i]->in.begin();c!=vs[i]->in.end();c++) {
+                       Variable *l=(*c)->left;
+                       varmap[vs[i]]->in.insert(varmap[l]);
+               }
+
+               for(vector<Constraint*>::iterator c=vs[i]->out.begin();c!=vs[i]->out.end();c++) {
+                       Variable *r=(*c)->right;
+                       varmap[vs[i]]->out.insert(varmap[r]);
+               }
+       }
+       while(graph.size()>0) {
+               node *u=NULL;
+               vector<node*>::iterator i=graph.begin();
+               for(;i!=graph.end();i++) {
+                       u=*i;
+                       if(u->in.size()==0) {
+                               break;
+                       }
+               }
+               if(i==graph.end() && graph.size()>0) {
+                       //cycle found!
+                       return true;
+               } else {
+                       graph.erase(i);
+                       for(set<node*>::iterator j=u->out.begin();j!=u->out.end();j++) {
+                               node *v=*j;
+                               v->in.erase(u);
+                       }
+                       delete u;
+               }
+       }
+       for(unsigned i=0; i<graph.size(); i++) {
+               delete graph[i];
+       }
+       return false;
+}
+
+// useful in debugging - cycles would be BAD
+bool VPSC::blockGraphIsCyclic() {
+       map<Block*, node*> bmap;
+       vector<node*> graph;
+       for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
+               Block *b=*i;
+               node *u=new node;
+               graph.push_back(u);
+               bmap[b]=u;
+       }
+       for(set<Block*>::const_iterator i=bs->begin();i!=bs->end();i++) {
+               Block *b=*i;
+               b->setUpInConstraints();
+               Constraint *c=b->findMinInConstraint();
+               while(c!=NULL) {
+                       Block *l=c->left->block;
+                       bmap[b]->in.insert(bmap[l]);
+                       b->deleteMinInConstraint();
+                       c=b->findMinInConstraint();
+               }
+
+               b->setUpOutConstraints();
+               c=b->findMinOutConstraint();
+               while(c!=NULL) {
+                       Block *r=c->right->block;
+                       bmap[b]->out.insert(bmap[r]);
+                       b->deleteMinOutConstraint();
+                       c=b->findMinOutConstraint();
+               }
+       }
+       while(graph.size()>0) {
+               node *u=NULL;
+               vector<node*>::iterator i=graph.begin();
+               for(;i!=graph.end();i++) {
+                       u=*i;
+                       if(u->in.size()==0) {
+                               break;
+                       }
+               }
+               if(i==graph.end() && graph.size()>0) {
+                       //cycle found!
+                       return true;
+               } else {
+                       graph.erase(i);
+                       for(set<node*>::iterator j=u->out.begin();j!=u->out.end();j++) {
+                               node *v=*j;
+                               v->in.erase(u);
+                       }
+                       delete u;
+               }
+       }
+       for(unsigned i=0; i<graph.size(); i++) {
+               delete graph[i];
+       }
+       return false;
+}
+