]> git.donarmstrong.com Git - lilypond.git/blob - lily/ineq-constrained-qp.cc
release: 1.0.1
[lilypond.git] / lily / ineq-constrained-qp.cc
1 /*
2   ineq-constrained-qp.cc -- implement Ineq_constrained_qp
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996, 1997--1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include "ineq-constrained-qp.hh"
9 #include "qlpsolve.hh"
10 #include "debug.hh"
11 #include "choleski.hh"
12 #include "main.hh"
13
14 /*
15   May be this also should go into a library
16  */
17
18 const int MAXITER=100;          // qlpsolve.hh
19
20 const int MAXDEGEN=5;
21
22 /*
23   assume x (idx) == value, and adjust constraints, lin and quad accordingly
24
25   TODO: add const_term
26   */
27 void
28 Ineq_constrained_qp::eliminate_var (int idx, Real value)
29 {
30   Vector row (quad_.row (idx));
31   row*= value;
32
33   quad_.delete_row (idx);
34
35   quad_.delete_column (idx);
36
37   lin_.del (idx);
38   row.del (idx);
39   lin_ +=row ;
40
41   for (int i=0; i < cons_.size(); i++)
42     {
43       consrhs_[i] -= cons_[i](idx) *value;
44       cons_[i].del (idx);
45     }
46 }
47
48 void
49 Ineq_constrained_qp::add_inequality_cons (Vector c, double r)
50 {
51   cons_.push (c);
52   consrhs_.push (r);
53 }
54
55 Ineq_constrained_qp::Ineq_constrained_qp (int novars):
56   quad_ (novars),
57   lin_ (novars),
58   const_term_ (0.0)
59 {
60 }
61
62 void
63 Ineq_constrained_qp::OK() const
64 {
65 #if !defined (NDEBUG) && defined (PARANOID)
66   assert (cons_.size() == consrhs_.size ());
67   Matrix Qdif= quad_ - quad_.transposed();
68   assert (Qdif.norm()/quad_.norm () < EPS);
69 #endif
70 }
71
72
73 Real
74 Ineq_constrained_qp::eval (Vector v)
75 {
76   return v * quad_ * v + lin_ * v + const_term_;
77 }
78
79
80 int
81 min_elt_index (Vector v)
82 {
83   Real m=infinity_f;
84   int idx=-1;
85   for (int i = 0; i < v.dim(); i++)
86     {
87       if (v (i) < m)
88         {
89           idx = i;
90           m = v (i);
91         }
92       assert (v (i) <= infinity_f);
93     }
94   return idx;
95 }
96
97
98 /**the numerical solving. Mordecai Avriel, Nonlinear Programming: analysis and methods (1976)
99   Prentice Hall.
100
101   Section 13.3
102
103   This is a "projected gradient" algorithm. Starting from a point x
104   the next point is found in a direction determined by projecting
105   the gradient onto the active constraints.  (well, not really the
106   gradient. The optimal solution obeying the active constraints is
107   tried. This is why H = Q^-1 in initialisation))
108
109
110   */
111 Vector
112 Ineq_constrained_qp::constraint_solve (Vector start) const
113 {
114   if (!dim())
115     return Vector (0);
116
117   Active_constraints act (this);
118   act.OK();
119
120
121   Vector x (start);
122   Vector gradient=quad_*x+lin_;
123
124   
125   //    Real fvalue = x*quad_*x/2 + lin*x + const_term;// it's no use.
126   Vector last_gradient (gradient);
127   int iterations=0;
128
129   while (iterations++ < MAXITER && act.degenerate_count_i_ < MAXDEGEN)
130     {
131       //#ifdef PARANOID
132       if (experimental_features_global_b)
133         assert_solution (x);
134       //#endif
135       
136       Vector direction= - act.find_active_optimum (gradient);
137
138       DOUT << "gradient "<< gradient<< "\ndirection " << direction<< '\n';
139
140       if (direction.norm() > EPS)
141         {
142           DOUT << act.status() << '\n';
143
144           Real unbounded_alfa = 1.0;
145           Real minalf = unbounded_alfa;
146
147           Inactive_iter minidx (act);
148
149
150           /*
151             we know the optimum on this "hyperplane". Check if we
152             bump into the edges of the simplex
153             */
154
155           for (Inactive_iter ia (act); ia.ok(); ia++)
156             {
157               Real dot = ia.vec() * direction;
158               if (dot >= 0)
159                 continue;
160
161               
162               Real numerator = ia.rhs () - ia.vec()*x;
163               if (numerator >= 0)
164                 {
165                   if (numerator > EPS)
166                     {
167                       warning (_f ("Ineq_constrained_qp::solve (): Constraint off by %f", numerator));
168                       act.degenerate_count_i_ ++;
169                     }
170                   minalf = -numerator;
171                   minidx = ia;
172                   break;
173                 }
174
175               Real alfa = numerator / dot;
176
177
178               if (minalf > alfa)
179                 {
180                   minidx = ia;
181                   minalf = alfa;
182                 }
183             }
184
185           Real optimal_step = minalf;
186
187           Vector deltax = direction * optimal_step;
188           x += deltax;
189           gradient += optimal_step * (quad_ * deltax);
190
191           DOUT << "step = " << optimal_step << " (|dx| = " <<
192             to_str (deltax.norm()) << ")\n";
193
194           if (minalf < unbounded_alfa)
195             {
196               /* bumped into an edge. try again, in smaller space. */
197               act.add_constraint (minidx.idx());
198               DOUT << "adding cons "<< minidx.idx () << '\n';
199               continue;
200             }
201           /*ASSERT: we are at the optimal solution for this "plane"*/
202         }
203
204       Vector lagrange_mult=act.get_lagrange (gradient);
205       int m= min_elt_index (lagrange_mult);
206
207       if (m>=0 && lagrange_mult (m) > 0)
208         {
209           break;                // optimal sol.
210         }
211       else if (m<0)
212         {
213           assert (gradient.norm() < EPS) ;
214
215           break;
216         }
217
218       DOUT << "dropping cons " << m << '\n';
219       act.drop_constraint (m);
220     }
221   if (iterations >= MAXITER)
222     WARN << _ ("didn't converge!") << '\n';
223   if (act.degenerate_count_i_ >= MAXDEGEN)
224     WARN << _ ("Too much degeneracy. ") << '\n';
225   DOUT <<  ": found " << x << " in " << iterations <<" iterations\n";
226   assert_solution (x);
227   return x;
228 }
229
230
231 Vector
232 Ineq_constrained_qp::solve (Vector start) const
233 {
234   /* no hassle if no constraints*/
235   if (! cons_.size())
236     {
237       Choleski_decomposition chol (quad_);
238       return - chol.solve (lin_);
239     }
240   else
241     {
242       return constraint_solve (start);
243     }
244 }
245
246 int
247 Ineq_constrained_qp::dim () const
248 {
249   return lin_.dim();
250 }
251
252
253
254
255 void
256 Ineq_constrained_qp::assert_solution (Vector sol) const
257 {
258   bool sol_b=true;
259
260   for (int i=0; sol_b && i < cons_.size(); i++) 
261     {
262       Real R=cons_[i] * sol- consrhs_[i];
263       if (R> -EPS)
264         sol_b = false;
265     }
266 }
267
268 void
269 Ineq_constrained_qp::print() const
270 {
271 #ifndef NPRINT
272   DOUT << "Quad " << quad_;
273   DOUT << "lin " << lin_ << '\n'
274        << "const " << const_term_<< '\n';
275   for (int i=0; i < cons_.size(); i++) 
276     {
277       DOUT << "constraint["<<i<<"]: " << cons_[i] << " >= " << consrhs_[i];
278       DOUT << '\n';
279     }
280 #endif
281 }