Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
solve_VPSC.cpp
Go to the documentation of this file.
1/*
2 * vim: ts=4 sw=4 et tw=0 wm=0
3 *
4 * libvpsc - A solver for the problem of Variable Placement with
5 * Separation Constraints.
6 *
7 * Copyright (C) 2005-2008 Monash University
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 * See the file LICENSE.LGPL distributed with the library.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * Author(s): Tim Dwyer
20 * Michael Wybrow
21*/
22
23#include <cmath>
24#include <sstream>
25#include <map>
26#include <cfloat>
27#include <set>
28
29#include "libvpsc/constraint.h"
30#include "libvpsc/block.h"
31#include "libvpsc/blocks.h"
32#include "libvpsc/solve_VPSC.h"
33#include "libvpsc/cbuffer.h"
34#include "libvpsc/variable.h"
35#include "libvpsc/assertions.h"
36#include "libvpsc/exceptions.h"
37
38#ifdef LIBVPSC_LOGGING
39#include <fstream>
40#endif
41
42using namespace std;
43
44namespace vpsc {
45
46static const double ZERO_UPPERBOUND=-1e-10;
47static const double LAGRANGIAN_TOLERANCE=-1e-4;
48
50 : Solver(vs,cs)
51{
53 for(Constraints::iterator i=inactive.begin();i!=inactive.end();++i) {
54 (*i)->active=false;
55 }
56}
57Solver::Solver(Variables const &vs, Constraints const &cs)
58 : m(cs.size()),
59 cs(cs),
60 n(vs.size()),
61 vs(vs),
62 needsScaling(false)
63{
64 for(unsigned i=0;i<n;++i) {
65 vs[i]->in.clear();
66 vs[i]->out.clear();
67
68 // Set needsScaling if any variables have a scale other than 1.
69 needsScaling |= (vs[i]->scale != 1);
70 }
71 for(unsigned i=0;i<m;++i) {
72 Constraint *c=cs[i];
73 c->left->out.push_back(c);
74 c->right->in.push_back(c);
75 c->needsScaling = needsScaling;
76 }
77 bs=new Blocks(vs);
78#ifdef LIBVPSC_LOGGING
80 //COLA_ASSERT(!constraintGraphIsCyclic(n,vs));
81#endif
82}
84 delete bs;
85}
86
88{
89 ++m;
90 c->active = false;
91 inactive.push_back(c);
92 c->left->out.push_back(c);
93 c->right->in.push_back(c);
94 c->needsScaling = needsScaling;
95}
96
97// useful in debugging
99#ifdef LIBVPSC_LOGGING
100 ofstream f(LOGFILE,ios::app);
101 for(set<Block*>::iterator i=bs->begin();i!=bs->end();++i) {
102 Block *b=*i;
103 f<<" "<<*b<<endl;
104 }
105 for(unsigned i=0;i<m;i++) {
106 f<<" "<<*cs[i]<<endl;
107 }
108#endif
109}
110
116 for(Variables::const_iterator i=vs.begin();i!=vs.end();++i) {
117 Variable* v=*i;
118 v->finalPosition=v->position();
119 COLA_ASSERT(v->finalPosition==v->finalPosition);
120 }
121}
133 list<Variable*> *vList=bs->totalOrder();
134 for(list<Variable*>::iterator i=vList->begin();i!=vList->end();++i) {
135 Variable *v=*i;
136 if(!v->block->deleted) {
137 bs->mergeLeft(v->block);
138 }
139 }
140 bs->cleanup();
141 bool activeConstraints=false;
142 for(unsigned i=0;i<m;i++) {
143 if(cs[i]->active) activeConstraints=true;
144 if(cs[i]->slack() < ZERO_UPPERBOUND) {
145#ifdef LIBVPSC_LOGGING
146 ofstream f(LOGFILE,ios::app);
147 f<<"Error: Unsatisfied constraint: "<<*cs[i]<<endl;
148#endif
149 //COLA_ASSERT(cs[i]->slack()>-0.0000001);
150 throw UnsatisfiedConstraint(*cs[i]);
151 }
152 }
153 delete vList;
154 copyResult();
155 return activeConstraints;
156}
157
159 bool solved=false;
160 // Solve shouldn't loop indefinately
161 // ... but just to make sure we limit the number of iterations
162 unsigned maxtries=100;
163 while(!solved&&maxtries>0) {
164 solved=true;
165 maxtries--;
166 size_t length = bs->size();
167 for (size_t i = 0; i < length; ++i)
168 {
169 Block *b = bs->at(i);
172 }
173 for (size_t i = 0; i < length; ++i)
174 {
175 Block *b = bs->at(i);
176 Constraint *c=b->findMinLM();
177 if(c!=nullptr && c->lm<LAGRANGIAN_TOLERANCE) {
178#ifdef LIBVPSC_LOGGING
179 ofstream f(LOGFILE,ios::app);
180 f<<"Split on constraint: "<<*c<<endl;
181#endif
182 // Split on c
183 Block *l=nullptr, *r=nullptr;
184 bs->split(b,l,r,c);
185 bs->cleanup();
186 // split alters the block set so we have to restart
187 solved=false;
188 break;
189 }
190 }
191 }
192 for(unsigned i=0;i<m;i++) {
193 if(cs[i]->slack() < ZERO_UPPERBOUND) {
194 COLA_ASSERT(cs[i]->slack()>ZERO_UPPERBOUND);
195 throw UnsatisfiedConstraint(*cs[i]);
196 }
197 }
198}
206 satisfy();
207 refine();
208 copyResult();
209 return bs->size()!=n;
210}
211
213#ifdef LIBVPSC_LOGGING
214 ofstream f(LOGFILE,ios::app);
215 f<<"solve_inc()..."<<endl;
216#endif
217 satisfy();
218 double lastcost = DBL_MAX, cost = bs->cost();
219 while(fabs(lastcost-cost)>0.0001) {
220 satisfy();
221 lastcost=cost;
222 cost = bs->cost();
223#ifdef LIBVPSC_LOGGING
224 f<<" bs->size="<<bs->size()<<", cost="<<cost<<endl;
225#endif
226 }
227 copyResult();
228 return bs->size()!=n;
229}
244#ifdef LIBVPSC_LOGGING
245 ofstream f(LOGFILE,ios::app);
246 f<<"satisfy_inc()..."<<endl;
247#endif
248 splitBlocks();
249 //long splitCtr = 0;
250 Constraint* v = nullptr;
251 //CBuffer buffer(inactive);
252 while ( (v = mostViolated(inactive)) &&
253 (v->equality || ((v->slack() < ZERO_UPPERBOUND) && !v->active)) )
254 {
255 COLA_ASSERT(!v->active);
256 Block *lb = v->left->block, *rb = v->right->block;
257 if(lb != rb) {
258 lb->merge(rb,v);
259 } else {
260 if(lb->isActiveDirectedPathBetween(v->right,v->left)) {
261 // cycle found, relax the violated, cyclic constraint
262 v->unsatisfiable=true;
263 continue;
264 //UnsatisfiableException e;
265 //lb->getActiveDirectedPathBetween(e.path,v->right,v->left);
266 //e.path.push_back(v);
267 //throw e;
268 }
269 //if(splitCtr++>10000) {
270 //throw "Cycle Error!";
271 //}
272 // constraint is within block, need to split first
273 try {
274 Constraint* splitConstraint
275 =lb->splitBetween(v->left,v->right,lb,rb);
276 if(splitConstraint!=nullptr) {
277 COLA_ASSERT(!splitConstraint->active);
278 inactive.push_back(splitConstraint);
279 } else {
280 v->unsatisfiable=true;
281 continue;
282 }
283 } catch(UnsatisfiableException e) {
284 e.path.push_back(v);
285#ifdef LIBVPSC_DEBUG
286 std::cerr << "Unsatisfiable:" << std::endl;
287 for(std::vector<Constraint*>::iterator r=e.path.begin();
288 r!=e.path.end();++r)
289 {
290 std::cerr << **r <<std::endl;
291 }
292#endif
293 v->unsatisfiable=true;
294 continue;
295 }
296 if(v->slack()>=0) {
297 COLA_ASSERT(!v->active);
298 // v was satisfied by the above split!
299 inactive.push_back(v);
300 bs->insert(lb);
301 bs->insert(rb);
302 } else {
303 bs->insert(lb->merge(rb,v));
304 delete ((lb->deleted) ? lb : rb);
305 }
306 }
307#ifdef LIBVPSC_LOGGING
308 f<<"...remaining blocks="<<bs->size()<<", cost="<<bs->cost()<<endl;
309#endif
310 }
311#ifdef LIBVPSC_LOGGING
312 f<<" finished merges."<<endl;
313#endif
314 bs->cleanup();
315 bool activeConstraints=false;
316 for(unsigned i=0;i<m;i++) {
317 v=cs[i];
318 if(v->active) activeConstraints=true;
319 if(v->slack() < ZERO_UPPERBOUND) {
320 ostringstream s;
321 s<<"Unsatisfied constraint: "<<*v;
322#ifdef LIBVPSC_LOGGING
323 ofstream f(LOGFILE,ios::app);
324 f<<s.str()<<endl;
325#endif
326 throw (char *) s.str().c_str();
327 }
328 }
329#ifdef LIBVPSC_LOGGING
330 f<<" finished cleanup."<<endl;
331 printBlocks();
332#endif
333 copyResult();
334 return activeConstraints;
335}
337#ifdef LIBVPSC_LOGGING
338 ofstream f(LOGFILE,ios::app);
339 f<<"moveBlocks()..."<<endl;
340#endif
341 size_t length = bs->size();
342 for (size_t i = 0; i < length; ++i)
343 {
344 Block *b = bs->at(i);
346 //b->posn = b->wposn / b->weight;
347 }
348#ifdef LIBVPSC_LOGGING
349 f<<" moved blocks."<<endl;
350#endif
351}
353#ifdef LIBVPSC_LOGGING
354 ofstream f(LOGFILE,ios::app);
355#endif
356 moveBlocks();
357 splitCnt=0;
358 // Split each block if necessary on min LM
359 size_t length = bs->size();
360 for (size_t i = 0; i < length; ++i)
361 {
362 Block *b = bs->at(i);
363 Constraint* v=b->findMinLM();
364 if(v!=nullptr && v->lm < LAGRANGIAN_TOLERANCE) {
365 COLA_ASSERT(!v->equality);
366#ifdef LIBVPSC_LOGGING
367 f<<" found split point: "<<*v<<" lm="<<v->lm<<endl;
368#endif
369 splitCnt++;
370 Block *b = v->left->block, *l=nullptr, *r=nullptr;
371 COLA_ASSERT(v->left->block == v->right->block);
372 //double pos = b->posn;
373 b->split(l,r,v);
374 //l->posn=r->posn=pos;
375 //l->wposn = l->posn * l->weight;
376 //r->wposn = r->posn * r->weight;
377 l->updateWeightedPosition();
378 r->updateWeightedPosition();
379 bs->insert(l);
380 bs->insert(r);
381 b->deleted=true;
382 COLA_ASSERT(!v->active);
383 inactive.push_back(v);
384#ifdef LIBVPSC_LOGGING
385 f<<" new blocks: "<<*l<<" and "<<*r<<endl;
386#endif
387 }
388 }
389 //if(splitCnt>0) { std::cout<<" splits: "<<splitCnt<<endl; }
390#ifdef LIBVPSC_LOGGING
391 f<<" finished splits."<<endl;
392#endif
393 bs->cleanup();
394}
395
401{
402 double slackForMostViolated = DBL_MAX;
403 Constraint* mostViolated = nullptr;
404#ifdef LIBVPSC_LOGGING
405 ofstream f(LOGFILE,ios::app);
406 f << "Looking for most violated..." << endl;
407#endif
408 size_t lSize = l.size();
409 size_t deleteIndex = lSize;
410 Constraint *constraint = nullptr;
411 double slack = 0;
412 for (size_t index = 0; index < lSize; ++index)
413 {
414 constraint = l[index];
415 slack = constraint->slack();
416 if (constraint->equality || slack < slackForMostViolated)
417 {
418 slackForMostViolated = slack;
419 mostViolated = constraint;
420 deleteIndex = index;
421 if (constraint->equality)
422 {
423 break;
424 }
425 }
426 }
427 // Because the constraint list is not order dependent we just
428 // move the last element over the deletePoint and resize
429 // downwards. There is always at least 1 element in the
430 // vector because of search.
431 if ( (deleteIndex < lSize) &&
432 (((slackForMostViolated < ZERO_UPPERBOUND) && !mostViolated->active) ||
434 {
435 l[deleteIndex] = l[lSize-1];
436 l.resize(lSize-1);
437 }
438#ifdef LIBVPSC_LOGGING
439 if (mostViolated)
440 {
441 f << " most violated is: " << *mostViolated << endl;
442 }
443 else
444 {
445 f << " non found." << endl;
446 }
447#endif
448 return mostViolated;
449}
450
451struct node {
452 set<node*> in;
453 set<node*> out;
454};
455// useful in debugging - cycles would be BAD
456bool Solver::constraintGraphIsCyclic(const unsigned n, Variable* const vs[]) {
457 map<Variable*, node*> varmap;
458 vector<node*> graph;
459 for(unsigned i=0;i<n;i++) {
460 node *u=new node;
461 graph.push_back(u);
462 varmap[vs[i]]=u;
463 }
464 for(unsigned i=0;i<n;i++) {
465 for(vector<Constraint*>::iterator c=vs[i]->in.begin();c!=vs[i]->in.end();++c) {
466 Variable *l=(*c)->left;
467 varmap[vs[i]]->in.insert(varmap[l]);
468 }
469
470 for(vector<Constraint*>::iterator c=vs[i]->out.begin();c!=vs[i]->out.end();++c) {
471 Variable *r=(*c)->right;
472 varmap[vs[i]]->out.insert(varmap[r]);
473 }
474 }
475 while(graph.size()>0) {
476 node *u=nullptr;
477 vector<node*>::iterator i=graph.begin();
478 for(;i!=graph.end();++i) {
479 u=*i;
480 if(u->in.size()==0) {
481 break;
482 }
483 }
484 if(i==graph.end() && graph.size()>0) {
485 //cycle found!
486 return true;
487 } else {
488 graph.erase(i);
489 for(set<node*>::iterator j=u->out.begin();j!=u->out.end();++j) {
490 node *v=*j;
491 v->in.erase(u);
492 }
493 delete u;
494 }
495 }
496 for(unsigned i=0; i<graph.size(); ++i) {
497 delete graph[i];
498 }
499 return false;
500}
501
502// useful in debugging - cycles would be BAD
504 map<Block*, node*> bmap;
505 vector<node*> graph;
506 size_t length = bs->size();
507 for (size_t i = 0; i < length; ++i)
508 {
509 Block *b = bs->at(i);
510 node *u=new node;
511 graph.push_back(u);
512 bmap[b]=u;
513 }
514 for (size_t i = 0; i < length; ++i)
515 {
516 Block *b = bs->at(i);
519 while(c!=nullptr) {
520 Block *l=c->left->block;
521 bmap[b]->in.insert(bmap[l]);
524 }
525
528 while(c!=nullptr) {
529 Block *r=c->right->block;
530 bmap[b]->out.insert(bmap[r]);
533 }
534 }
535 while(graph.size()>0) {
536 node *u=nullptr;
537 vector<node*>::iterator i=graph.begin();
538 for(;i!=graph.end();++i) {
539 u=*i;
540 if(u->in.size()==0) {
541 break;
542 }
543 }
544 if(i==graph.end() && graph.size()>0) {
545 //cycle found!
546 return true;
547 } else {
548 graph.erase(i);
549 for(set<node*>::iterator j=u->out.begin();j!=u->out.end();++j) {
550 node *v=*j;
551 v->in.erase(u);
552 }
553 delete u;
554 }
555 }
556 for(unsigned i=0; i<graph.size(); i++) {
557 delete graph[i];
558 }
559 return false;
560}
561}
iterator begin()
Iterator over children.
Definition node.h:557
Constraint * findMinLM()
Definition block.cpp:486
PairingHeap< Constraint *, CompareConstraints > * in
Definition block.h:86
void merge(Block *b, Constraint *c, double dist)
Definition block.cpp:166
Constraint * splitBetween(Variable *vl, Variable *vr, Block *&lb, Block *&rb)
Definition block.cpp:590
void setUpOutConstraints()
Definition block.cpp:120
bool isActiveDirectedPathBetween(Variable const *u, Variable const *v) const
Definition block.cpp:560
bool deleted
Definition block.h:84
void deleteMinInConstraint()
Definition block.cpp:274
void split(Block *&l, Block *&r, Constraint *c)
Definition block.cpp:612
Constraint * findMinOutConstraint()
Definition block.cpp:264
void updateWeightedPosition()
Definition block.cpp:97
Constraint * findMinInConstraint()
Definition block.cpp:215
void deleteMinOutConstraint()
Definition block.cpp:282
void setUpInConstraints()
Definition block.cpp:117
PairingHeap< Constraint *, CompareConstraints > * out
Definition block.h:87
void split(Block *b, Block *&l, Block *&r, Constraint *c)
Definition blocks.cpp:212
void insert(Block *block)
Definition blocks.h:90
void mergeLeft(Block *r)
Definition blocks.cpp:107
double cost()
Definition blocks.cpp:239
std::list< Variable * > * totalOrder()
Definition blocks.cpp:74
void cleanup()
Definition blocks.cpp:175
Block * at(size_t index) const
Definition blocks.h:85
size_t size() const
Definition blocks.h:80
A constraint determines a minimum or exact spacing required between two Variable objects.
Definition constraint.h:45
const bool equality
Whether the separation is an exact distance or not.
Definition constraint.h:111
Variable * left
The left Variable.
Definition constraint.h:102
double slack(void) const
Definition constraint.h:85
Constraints inactive
Definition solve_VPSC.h:124
Constraint * mostViolated(Constraints &l)
Scan constraint list for the most violated constraint, or the first equality constraint.
void addConstraint(Constraint *constraint)
Adds a constraint to the existing VPSC solver.
unsigned splitCnt
Definition solve_VPSC.h:123
bool solve()
Results in an optimum solution subject to the constraints.
IncSolver(Variables const &vs, Constraints const &cs)
bool satisfy()
Results in an approximate solution subject to the constraints.
Static solver for Variable Placement with Separation Constraints problem instance.
Definition solve_VPSC.h:60
Blocks * bs
Definition solve_VPSC.h:77
void printBlocks()
Solver(Variables const &vs, Constraints const &cs)
virtual ~Solver()
bool needsScaling
Definition solve_VPSC.h:82
bool constraintGraphIsCyclic(const unsigned n, Variable *const vs[])
void copyResult()
Stores the relative positions of the variables in their finalPosition field.
virtual bool solve()
Results in an optimum solution subject to the constraints.
bool blockGraphIsCyclic()
virtual bool satisfy()
Results in an approximate solution subject to the constraints.
std::vector< Variable * > const & vs
Definition solve_VPSC.h:81
std::vector< Constraint * > const & cs
Definition solve_VPSC.h:79
A variable is comprised of an ideal position, final position and a weight.
Definition variable.h:45
Constraints out
Definition variable.h:62
Constraints in
Definition variable.h:61
Geom::IntPoint size
double c[8][4]
Inkscape::XML::Node * node
STL namespace.
libvpsc: Variable Placement with Separation Constraints quadratic program solver library.
std::vector< vpsc::Variable * > Variables
A vector of pointers to Variable objects.
static const double LAGRANGIAN_TOLERANCE
std::vector< vpsc::Constraint * > Constraints
A vector of pointers to Constraint objects.
static const double ZERO_UPPERBOUND
Definition cbuffer.cpp:30
std::vector< Constraint * > path
Definition exceptions.h:28
int index