summaryrefslogtreecommitdiffstats
path: root/src/libcola/gradient_projection.h
blob: 4ef68fc2e2428a6ffa330f576c991912be2912fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#ifndef _GRADIENT_PROJECTION_H
#define _GRADIENT_PROJECTION_H

#include <libvpsc/solve_VPSC.h>
#include <libvpsc/variable.h>
#include <libvpsc/constraint.h>
#include <libvpsc/generate-constraints.h>
#include <vector>
#include <iostream>
#include <math.h>

using namespace std;

typedef vector<vpsc::Constraint*> Constraints;
typedef vector<vpsc::Variable*> Variables;
typedef vector<pair<unsigned,double> > OffsetList;

class SimpleConstraint {
public:
    SimpleConstraint(unsigned l, unsigned r, double g) 
        : left(l), right(r), gap(g)  {}
    unsigned left;
    unsigned right;
    double gap;
};
typedef vector<SimpleConstraint*> SimpleConstraints;
class AlignmentConstraint {
friend class GradientProjection;
public:
    AlignmentConstraint(double pos) : position(pos), variable(NULL) {}
    void updatePosition() {
        position = variable->position();
    }
    OffsetList offsets;
    void* guide;
    double position;
private:
    vpsc::Variable* variable;
};
typedef vector<AlignmentConstraint*> AlignmentConstraints;

class PageBoundaryConstraints {
public:
    PageBoundaryConstraints(double lm, double rm, double w)
        : leftMargin(lm), rightMargin(rm), weight(w) { }
    void createVarsAndConstraints(Variables &vs, Constraints &cs) {
        vpsc::Variable* vl, * vr;
        // create 2 dummy vars, based on the dimension we are in
        vs.push_back(vl=new vpsc::Variable(vs.size(), leftMargin, weight));
        vs.push_back(vr=new vpsc::Variable(vs.size(), rightMargin, weight));

        // for each of the "real" variables, create a constraint that puts that var
        // between our two new dummy vars, depending on the dimension.
        for(OffsetList::iterator o=offsets.begin(); o!=offsets.end(); ++o)  {
            cs.push_back(new vpsc::Constraint(vl, vs[o->first], o->second));
            cs.push_back(new vpsc::Constraint(vs[o->first], vr, o->second));
        }
    }
    OffsetList offsets;
private:
    double leftMargin;
    double rightMargin;
    double weight;
};

typedef vector<pair<unsigned,double> > CList;
/**
 * A DummyVarPair is a pair of variables with an ideal distance between them and which have no
 * other interaction with other variables apart from through constraints.  This means that
 * the entries in the laplacian matrix for dummy vars and other vars would be 0 - thus, sparse
 * matrix techniques can be used in laplacian operations.
 * The constraints are specified by a two lists of pairs of variable indexes and required separation.
 * The two lists are:
 *   leftof: variables to which left must be to the left of, 
 *   rightof: variables to which right must be to the right of. 
 */
class DummyVarPair {
public:
    DummyVarPair(double desiredDist) : dist(desiredDist), lap2(1.0/(desiredDist*desiredDist)) { }
    CList leftof; // variables to which left dummy var must be to the left of
    CList rightof; // variables to which right dummy var must be to the right of
    double place_l;
    double place_r;
    void computeLinearTerm(double euclideanDistance) {   
        if(euclideanDistance > 1e-30) {
            b = place_r - place_l;
            b /= euclideanDistance * dist;
        } else { b=0; }
    }
    double stress(double euclideanDistance) {
        double diff = dist - euclideanDistance;
        return diff*diff / (dist*dist);
    }
private:
friend class GradientProjection; 
    /**
     * Setup vars and constraints for an instance of the VPSC problem.
     * Adds generated vars and constraints to the argument vectors.
     */
    void setupVPSC(Variables &vars, Constraints &cs) {
        double weight=1;
        left = new vpsc::Variable(vars.size(),place_l,weight);
        vars.push_back(left);
        right = new vpsc::Variable(vars.size(),place_r,weight);
        vars.push_back(right);
        for(CList::iterator cit=leftof.begin();
                cit!=leftof.end(); ++cit) {
            vpsc::Variable* v = vars[(*cit).first];
            cs.push_back(new vpsc::Constraint(left,v,(*cit).second)); 
        }
        for(CList::iterator cit=rightof.begin();
                cit!=rightof.end(); ++cit) {
            vpsc::Variable* v = vars[(*cit).first];
            cs.push_back(new vpsc::Constraint(v,right,(*cit).second)); 
        }
    }
    /**
     * Extract the result of a VPSC solution to the variable positions
     */
    void updatePosition() {
        place_l=left->position();
        place_r=right->position();
    }
    /**
     * Compute the descent vector, also copying the current position to old_place for
     * future reference
     */
    void computeDescentVector() {
        old_place_l=place_l;
        old_place_r=place_r;
        g = 2.0 * ( b + lap2 * ( place_l - place_r ) );
    }
    /** 
     * move in the direction of steepest descent (based on g computed by computeGradient)
     * alpha is the step size.
     */
    void steepestDescent(double alpha) {
        place_l -= alpha*g;
        place_r += alpha*g;
        left->desiredPosition=place_l;
        right->desiredPosition=place_r;
    }
    /**
     * add dummy vars' contribution to numerator and denominator for 
     * beta step size calculation
     */
    void betaCalc(double &numerator, double &denominator) {
        double dl = place_l-old_place_l,
               dr = place_r-old_place_r,
               r = 2.0 * lap2 * ( dr - dl );
        numerator += g * ( dl - dr );
        denominator += r*dl - r * dr;
    }
    /**
     * move by stepsize beta from old_place to place
     */
    void feasibleDescent(double beta) {
        left->desiredPosition = place_l = old_place_l + beta*(place_l - old_place_l);
        right->desiredPosition = place_r = old_place_r + beta*(place_r - old_place_r);
    }
    double absoluteDisplacement() {
        return fabs(place_l - old_place_l) + fabs(place_r - old_place_r);
    }
    double dist; // ideal distance between vars
    double b; // linear coefficient in quad form for left (b_right = -b)
    vpsc::Variable* left; // Variables used in constraints
    vpsc::Variable* right;
    double lap2; // laplacian entry
    double g; // descent vec for quad form for left (g_right = -g)
    double old_place_l; // old_place is where the descent vec g was computed
    double old_place_r;
};
typedef vector<DummyVarPair*> DummyVars;

enum Dim { HORIZONTAL, VERTICAL };

class GradientProjection {
public:
	GradientProjection(
        const Dim k,
		unsigned n, 
		double** A,
		double* x,
		double tol,
		unsigned max_iterations,
        AlignmentConstraints* acs=NULL,
        bool nonOverlapConstraints=false,
        vpsc::Rectangle** rs=NULL,
        PageBoundaryConstraints *pbc = NULL,
        SimpleConstraints *sc = NULL)
            : k(k), n(n), A(A), place(x), rs(rs),
              nonOverlapConstraints(nonOverlapConstraints),
              tolerance(tol), acs(acs), max_iterations(max_iterations),
              g(new double[n]), d(new double[n]), old_place(new double[n]),
              constrained(false)
    {
        for(unsigned i=0;i<n;i++) {
            vars.push_back(new vpsc::Variable(i,1,1));
        }
        if(acs) {
            for(AlignmentConstraints::iterator iac=acs->begin();
                    iac!=acs->end();++iac) {
                AlignmentConstraint* ac=*iac;
                vpsc::Variable *v=ac->variable=new vpsc::Variable(vars.size(),ac->position,0.0001);
                vars.push_back(v);
                for(OffsetList::iterator o=ac->offsets.begin();
                        o!=ac->offsets.end();
                        o++) {
                    gcs.push_back(new vpsc::Constraint(v,vars[o->first],o->second,true));
                }
            }
        }
        if (pbc)  {          
            pbc->createVarsAndConstraints(vars,gcs);
        }
        if (sc) {
            for(SimpleConstraints::iterator c=sc->begin(); c!=sc->end();++c) {
                gcs.push_back(new vpsc::Constraint(
                        vars[(*c)->left],vars[(*c)->right],(*c)->gap));
            }
        }
        if(!gcs.empty() || nonOverlapConstraints) {
            constrained=true;
        }
	}
    virtual ~GradientProjection() {
        delete [] g;
        delete [] d;
        delete [] old_place;
        for(Constraints::iterator i(gcs.begin()); i!=gcs.end(); i++) {
            delete *i;
        }
        gcs.clear();
        for(unsigned i=0;i<vars.size();i++) {
            delete vars[i];
        }
    }
    void clearDummyVars();
	unsigned solve(double* b);
    DummyVars dummy_vars; // special vars that must be considered in Lapl.
private:
    vpsc::IncSolver* setupVPSC();
    void destroyVPSC(vpsc::IncSolver *vpsc);
    Dim k;
	unsigned n; // number of actual vars
	double** A; // Graph laplacian matrix
    double* place;
	Variables vars; // all variables
                          // computations
    Constraints gcs; /* global constraints - persist throughout all
                                iterations */
    Constraints lcs; /* local constraints - only for current iteration */
    vpsc::Rectangle** rs;
    bool nonOverlapConstraints;
    double tolerance;
    AlignmentConstraints* acs;
    unsigned max_iterations;
	double* g; /* gradient */
	double* d;
	double* old_place;
    bool constrained;
};

#endif /* _GRADIENT_PROJECTION_H */

// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=4 :