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