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