2
* \brief A block is a group of variables that must be moved together to improve
3
* the goal function without violating already active constraints.
4
* The variables in a block are spanned by a tree of active constraints.
7
* Tim Dwyer <tgdwyer@gmail.com>
9
* Copyright (C) 2005 Authors
11
* Released under GNU LGPL. Read the file 'COPYING' for more information.
14
#include "pairingheap/PairingHeap.h"
15
#include "constraint.h"
18
#ifdef RECTANGLE_OVERLAP_LOGGING
27
void Block::addVariable(Variable* const v) {
31
wposn += v->weight * (v->desiredPosition - v->offset);
34
Block::Block(Variable* const v) {
40
vars=new vector<Variable*>;
47
double Block::desiredWeightedPosition() {
49
for (Vit v=vars->begin();v!=vars->end();++v) {
50
wp += ((*v)->desiredPosition - (*v)->offset) * (*v)->weight;
60
void Block::setUpInConstraints() {
61
setUpConstraintHeap(in,true);
63
void Block::setUpOutConstraints() {
64
setUpConstraintHeap(out,false);
66
void Block::setUpConstraintHeap(PairingHeap<Constraint*>* &h,bool in) {
68
h = new PairingHeap<Constraint*>(&compareConstraints);
69
for (Vit i=vars->begin();i!=vars->end();++i) {
71
vector<Constraint*> *cs=in?&(v->in):&(v->out);
72
for (Cit j=cs->begin();j!=cs->end();++j) {
74
c->timeStamp=blockTimeCtr;
75
if (c->left->block != this && in || c->right->block != this && !in) {
81
void Block::merge(Block* b, Constraint* c) {
82
#ifdef RECTANGLE_OVERLAP_LOGGING
83
ofstream f(LOGFILE,ios::app);
84
f<<" merging on: "<<*c<<",c->left->offset="<<c->left->offset<<",c->right->offset="<<c->right->offset<<endl;
86
double dist = c->right->offset - c->left->offset - c->gap;
87
Block *l=c->left->block;
88
Block *r=c->right->block;
89
if (vars->size() < b->vars->size()) {
94
#ifdef RECTANGLE_OVERLAP_LOGGING
95
f<<" merged block="<<(b->deleted?*this:*b)<<endl;
99
* Merges b into this block across c. Can be either a
100
* right merge or a left merge
101
* @param b block to merge into this
102
* @param c constraint being merged
103
* @param distance separation required to satisfy c
105
void Block::merge(Block *b, Constraint *c, double dist) {
106
#ifdef RECTANGLE_OVERLAP_LOGGING
107
ofstream f(LOGFILE,ios::app);
108
f<<" merging: "<<*b<<"dist="<<dist<<endl;
111
wposn+=b->wposn-dist*b->weight;
114
for(Vit i=b->vars->begin();i!=b->vars->end();++i) {
123
void Block::mergeIn(Block *b) {
124
#ifdef RECTANGLE_OVERLAP_LOGGING
125
ofstream f(LOGFILE,ios::app);
126
f<<" merging constraint heaps... "<<endl;
128
// We check the top of the heaps to remove possible internal constraints
129
findMinInConstraint();
130
b->findMinInConstraint();
132
#ifdef RECTANGLE_OVERLAP_LOGGING
133
f<<" merged heap: "<<*in<<endl;
136
void Block::mergeOut(Block *b) {
137
findMinOutConstraint();
138
b->findMinOutConstraint();
141
Constraint *Block::findMinInConstraint() {
142
Constraint *v = NULL;
143
vector<Constraint*> outOfDate;
144
while (!in->isEmpty()) {
146
Block *lb=v->left->block;
147
Block *rb=v->right->block;
148
// rb may not be this if called between merge and mergeIn
149
#ifdef RECTANGLE_OVERLAP_LOGGING
150
ofstream f(LOGFILE,ios::app);
151
f<<" checking constraint ... "<<*v;
152
f<<" timestamps: left="<<lb->timeStamp<<" right="<<rb->timeStamp<<" constraint="<<v->timeStamp<<endl;
155
// constraint has been merged into the same block
156
#ifdef RECTANGLE_OVERLAP_LOGGING
158
f<<" violated internal constraint found! "<<*v<<endl;
159
f<<" lb="<<*lb<<endl;
160
f<<" rb="<<*rb<<endl;
164
#ifdef RECTANGLE_OVERLAP_LOGGING
165
f<<" ... skipping internal constraint"<<endl;
167
} else if(v->timeStamp < lb->timeStamp) {
168
// block at other end of constraint has been moved since this
170
outOfDate.push_back(v);
171
#ifdef RECTANGLE_OVERLAP_LOGGING
172
f<<" reinserting out of date (reinsert later)"<<endl;
178
for(Cit i=outOfDate.begin();i!=outOfDate.end();++i) {
180
v->timeStamp=blockTimeCtr;
190
Constraint *Block::findMinOutConstraint() {
191
if(out->isEmpty()) return NULL;
192
Constraint *v = out->findMin();
193
while (v->left->block == v->right->block) {
195
if(out->isEmpty()) return NULL;
200
void Block::deleteMinInConstraint() {
202
#ifdef RECTANGLE_OVERLAP_LOGGING
203
ofstream f(LOGFILE,ios::app);
204
f<<"deleteMinInConstraint... "<<endl;
205
f<<" result: "<<*in<<endl;
208
void Block::deleteMinOutConstraint() {
211
inline bool Block::canFollowLeft(Constraint *c, const Variable* const last) {
212
return c->left->block==this && c->active && last!=c->left;
214
inline bool Block::canFollowRight(Constraint *c, const Variable* const last) {
215
return c->right->block==this && c->active && last!=c->right;
218
// computes the derivative of v and the lagrange multipliers
219
// of v's out constraints (as the recursive sum of those below.
220
// Does not backtrack over u.
221
// also records the constraint with minimum lagrange multiplier
223
double Block::compute_dfdv(Variable* const v, Variable* const u,
224
Constraint *&min_lm) {
225
double dfdv=v->weight*(v->position() - v->desiredPosition);
226
for(Cit it=v->out.begin();it!=v->out.end();++it) {
228
if(canFollowRight(c,u)) {
229
dfdv+=c->lm=compute_dfdv(c->right,v,min_lm);
230
if(!c->equality&&(min_lm==NULL||c->lm<min_lm->lm)) min_lm=c;
233
for(Cit it=v->in.begin();it!=v->in.end();++it) {
235
if(canFollowLeft(c,u)) {
236
dfdv-=c->lm=-compute_dfdv(c->left,v,min_lm);
237
if(!c->equality&&(min_lm==NULL||c->lm<min_lm->lm)) min_lm=c;
244
// computes dfdv for each variable and uses the sum of dfdv on either side of
245
// the constraint c to compute the lagrangian multiplier lm_c.
246
// The top level v and r are variables between which we want to find the
247
// constraint with the smallest lm.
248
// When we find r we pass NULL to subsequent recursive calls,
249
// thus r=NULL indicates constraints are not on the shortest path.
250
// Similarly, m is initially NULL and is only assigned a value if the next
251
// variable to be visited is r or if a possible min constraint is returned from
252
// a nested call (rather than NULL).
253
// Then, the search for the m with minimum lm occurs as we return from
254
// the recursion (checking only constraints traversed left-to-right
255
// in order to avoid creating any new violations).
256
// We also do not consider equality constraints as potential split points
257
Block::Pair Block::compute_dfdv_between(
258
Variable* r, Variable* const v, Variable* const u,
259
const Direction dir = NONE, bool changedDirection = false) {
260
double dfdv=v->weight*(v->position() - v->desiredPosition);
262
for(Cit it(v->in.begin());it!=v->in.end();++it) {
264
if(canFollowLeft(c,u)) {
266
changedDirection = true;
270
if(!c->equality) m=c;
272
Pair p=compute_dfdv_between(r,c->left,v,
273
LEFT,changedDirection);
274
dfdv -= c->lm = -p.first;
279
for(Cit it(v->out.begin());it!=v->out.end();++it) {
281
if(canFollowRight(c,u)) {
283
changedDirection = true;
287
if(!c->equality) m=c;
289
Pair p=compute_dfdv_between(r,c->right,v,
290
RIGHT,changedDirection);
291
dfdv += c->lm = p.first;
293
m = changedDirection && !c->equality && c->lm < p.second->lm
301
// resets LMs for all active constraints to 0 by
302
// traversing active constraint tree starting from v,
303
// not back tracking over u
304
void Block::reset_active_lm(Variable* const v, Variable* const u) {
305
for(Cit it=v->out.begin();it!=v->out.end();++it) {
307
if(canFollowRight(c,u)) {
309
reset_active_lm(c->right,v);
312
for(Cit it=v->in.begin();it!=v->in.end();++it) {
314
if(canFollowLeft(c,u)) {
316
reset_active_lm(c->left,v);
321
* finds the constraint with the minimum lagrange multiplier, that is, the constraint
322
* that most wants to split
324
Constraint *Block::findMinLM() {
325
Constraint *min_lm=NULL;
326
reset_active_lm(vars->front(),NULL);
327
compute_dfdv(vars->front(),NULL,min_lm);
330
Constraint *Block::findMinLMBetween(Variable* const lv, Variable* const rv) {
331
Constraint *min_lm=NULL;
332
reset_active_lm(vars->front(),NULL);
333
min_lm=compute_dfdv_between(rv,lv,NULL).second;
337
// populates block b by traversing the active constraint tree adding variables as they're
338
// visited. Starts from variable v and does not backtrack over variable u.
339
void Block::populateSplitBlock(Block *b, Variable* const v, Variable* const u) {
341
for (Cit c=v->in.begin();c!=v->in.end();++c) {
342
if (canFollowLeft(*c,u))
343
populateSplitBlock(b, (*c)->left, v);
345
for (Cit c=v->out.begin();c!=v->out.end();++c) {
346
if (canFollowRight(*c,u))
347
populateSplitBlock(b, (*c)->right, v);
350
// Search active constraint tree from u to see if there is a directed path to v.
351
// Returns true if path is found with all constraints in path having their visited flag
353
bool Block::isActiveDirectedPathBetween(Variable* u, Variable *v) {
354
if(u==v) return true;
355
for (Cit c=u->out.begin();c!=u->out.end();++c) {
356
if(canFollowRight(*c,NULL)) {
357
if(isActiveDirectedPathBetween((*c)->right,v)) {
367
* Block needs to be split because of a violated constraint between vl and vr.
368
* We need to search the active constraint tree between l and r and find the constraint
369
* with min lagrangrian multiplier and split at that point.
370
* Returns the split constraint
372
Constraint* Block::splitBetween(Variable* const vl, Variable* const vr,
373
Block* &lb, Block* &rb) {
374
#ifdef RECTANGLE_OVERLAP_LOGGING
375
ofstream f(LOGFILE,ios::app);
376
f<<" need to split between: "<<*vl<<" and "<<*vr<<endl;
378
Constraint *c=findMinLMBetween(vl, vr);
379
#ifdef RECTANGLE_OVERLAP_LOGGING
380
f<<" going to split on: "<<*c<<endl;
387
* Creates two new blocks, l and r, and splits this block across constraint c,
388
* placing the left subtree of constraints (and associated variables) into l
389
* and the right into r.
391
void Block::split(Block* &l, Block* &r, Constraint* c) {
394
populateSplitBlock(l,c->left,c->right);
396
populateSplitBlock(r,c->right,c->left);
400
* Computes the cost (squared euclidean distance from desired positions) of the
401
* current positions for variables in this block
403
double Block::cost() {
405
for (Vit v=vars->begin();v!=vars->end();++v) {
406
double diff = (*v)->position() - (*v)->desiredPosition;
407
c += (*v)->weight * diff * diff;
411
ostream& operator <<(ostream &os, const Block& b)
414
for(Block::Vit v=b.vars->begin();v!=b.vars->end();++v) {