From: erg Date: Fri, 28 Apr 2006 19:03:46 +0000 (+0000) Subject: Add the vpsc library for IPSEPCOLA features X-Git-Tag: LAST_LIBGRAPH~32^2~6663 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=03c20a55c921ac07599be2ae19ae34209ec0d02e;p=graphviz Add the vpsc library for IPSEPCOLA features --- diff --git a/lib/vpsc/solve_VPSC.cpp b/lib/vpsc/solve_VPSC.cpp new file mode 100644 index 000000000..dccede63a --- /dev/null +++ b/lib/vpsc/solve_VPSC.cpp @@ -0,0 +1,416 @@ +/** + * \brief Solve an instance of the "Variable Placement with Separation + * Constraints" problem. + * + * Authors: + * Tim Dwyer + * + * 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 +#include "constraint.h" +#include "block.h" +#include "blocks.h" +#include "solve_VPSC.h" +#include +#include +#ifdef RECTANGLE_OVERLAP_LOGGING +#include +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::iterator i=bs->begin();i!=bs->end();i++) { + Block *b=*i; + f<<" "<<*b< *vs=bs->totalOrder(); + for(list::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;islack()<-0.0000001) { +#ifdef RECTANGLE_OVERLAP_LOGGING + ofstream f(LOGFILE,ios::app); + f<<"Error: Unsatisfied constraint: "<<*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::const_iterator i=bs->begin();i!=bs->end();i++) { + Block *b=*i; + b->setUpInConstraints(); + b->setUpOutConstraints(); + } + for(set::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<split(b,l,r,c); + bs->cleanup(); + // split alters the block set so we have to restart + solved=false; + break; + } + } + } + for(unsigned i=0;islack()<-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()..."<cost(); + do { + lastcost=cost; + satisfy(); + splitBlocks(); + cost = bs->cost(); +#ifdef RECTANGLE_OVERLAP_LOGGING + f<<" 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()..."<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."<cleanup(); + for(unsigned i=0;islack()<-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."<::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."<::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="<lm<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<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..."<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< +using std::map; +using std::vector; +struct node { + set in; + set out; +}; +// useful in debugging - cycles would be BAD +bool VPSC::constraintGraphIsCyclic(const unsigned n, Variable *vs[]) { + map varmap; + vector graph; + for(unsigned i=0;i::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::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::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::iterator j=u->out.begin();j!=u->out.end();j++) { + node *v=*j; + v->in.erase(u); + } + delete u; + } + } + for(unsigned i=0; i bmap; + vector graph; + for(set::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::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::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::iterator j=u->out.begin();j!=u->out.end();j++) { + node *v=*j; + v->in.erase(u); + } + delete u; + } + } + for(unsigned i=0; i