View Javadoc

1   package de.bwaldvogel.liblinear;
2   
3   import static de.bwaldvogel.liblinear.Linear.info;
4   
5   
6   class Tron {
7   
8       private final Function fun_obj;
9   
10      private final double   eps;
11  
12      private final int      max_iter;
13  
14      public Tron( final Function fun_obj ) {
15          this(fun_obj, 0.1);
16      }
17  
18      public Tron( final Function fun_obj, double eps ) {
19          this(fun_obj, eps, 1000);
20      }
21  
22      public Tron( final Function fun_obj, double eps, int max_iter ) {
23          this.fun_obj = fun_obj;
24          this.eps = eps;
25          this.max_iter = max_iter;
26      }
27  
28      void tron(double[] w) {
29          // Parameters for updating the iterates.
30          double eta0 = 1e-4, eta1 = 0.25, eta2 = 0.75;
31  
32          // Parameters for updating the trust region size delta.
33          double sigma1 = 0.25, sigma2 = 0.5, sigma3 = 4;
34  
35          int n = fun_obj.get_nr_variable();
36          int i, cg_iter;
37          double delta, snorm, one = 1.0;
38          double alpha, f, fnew, prered, actred, gs;
39          int search = 1, iter = 1;
40          double[] s = new double[n];
41          double[] r = new double[n];
42          double[] w_new = new double[n];
43          double[] g = new double[n];
44  
45          for (i = 0; i < n; i++)
46              w[i] = 0;
47  
48          f = fun_obj.fun(w);
49          fun_obj.grad(w, g);
50          delta = euclideanNorm(g);
51          double gnorm1 = delta;
52          double gnorm = gnorm1;
53  
54          if (gnorm <= eps * gnorm1) search = 0;
55  
56          iter = 1;
57  
58          while (iter <= max_iter && search != 0) {
59              cg_iter = trcg(delta, g, s, r);
60  
61              System.arraycopy(w, 0, w_new, 0, n);
62              daxpy(one, s, w_new);
63  
64              gs = dot(g, s);
65              prered = -0.5 * (gs - dot(s, r));
66              fnew = fun_obj.fun(w_new);
67  
68              // Compute the actual reduction.
69              actred = f - fnew;
70  
71              // On the first iteration, adjust the initial step bound.
72              snorm = euclideanNorm(s);
73              if (iter == 1) delta = Math.min(delta, snorm);
74  
75              // Compute prediction alpha*snorm of the step.
76              if (fnew - f - gs <= 0)
77                  alpha = sigma3;
78              else
79                  alpha = Math.max(sigma1, -0.5 * (gs / (fnew - f - gs)));
80  
81              // Update the trust region bound according to the ratio of actual to
82              // predicted reduction.
83              if (actred < eta0 * prered)
84                  delta = Math.min(Math.max(alpha, sigma1) * snorm, sigma2 * delta);
85              else if (actred < eta1 * prered)
86                  delta = Math.max(sigma1 * delta, Math.min(alpha * snorm, sigma2 * delta));
87              else if (actred < eta2 * prered)
88                  delta = Math.max(sigma1 * delta, Math.min(alpha * snorm, sigma3 * delta));
89              else
90                  delta = Math.max(delta, Math.min(alpha * snorm, sigma3 * delta));
91  
92              info("iter %2d act %5.3e pre %5.3e delta %5.3e f %5.3e |g| %5.3e CG %3d%n", iter, actred, prered, delta, f, gnorm, cg_iter);
93  
94              if (actred > eta0 * prered) {
95                  iter++;
96                  System.arraycopy(w_new, 0, w, 0, n);
97                  f = fnew;
98                  fun_obj.grad(w, g);
99  
100                 gnorm = euclideanNorm(g);
101                 if (gnorm <= eps * gnorm1) break;
102             }
103             if (f < -1.0e+32) {
104                 info("warning: f < -1.0e+32%n");
105                 break;
106             }
107             if (Math.abs(actred) <= 0 && prered <= 0) {
108                 info("warning: actred and prered <= 0%n");
109                 break;
110             }
111             if (Math.abs(actred) <= 1.0e-12 * Math.abs(f) && Math.abs(prered) <= 1.0e-12 * Math.abs(f)) {
112                 info("warning: actred and prered too small%n");
113                 break;
114             }
115         }
116     }
117 
118     private int trcg(double delta, double[] g, double[] s, double[] r) {
119         int n = fun_obj.get_nr_variable();
120         double one = 1;
121         double[] d = new double[n];
122         double[] Hd = new double[n];
123         double rTr, rnewTrnew, cgtol;
124 
125         for (int i = 0; i < n; i++) {
126             s[i] = 0;
127             r[i] = -g[i];
128             d[i] = r[i];
129         }
130         cgtol = 0.1 * euclideanNorm(g);
131 
132         int cg_iter = 0;
133         rTr = dot(r, r);
134 
135         while (true) {
136             if (euclideanNorm(r) <= cgtol) break;
137             cg_iter++;
138             fun_obj.Hv(d, Hd);
139 
140             double alpha = rTr / dot(d, Hd);
141             daxpy(alpha, d, s);
142             if (euclideanNorm(s) > delta) {
143                 info("cg reaches trust region boundary%n");
144                 alpha = -alpha;
145                 daxpy(alpha, d, s);
146 
147                 double std = dot(s, d);
148                 double sts = dot(s, s);
149                 double dtd = dot(d, d);
150                 double dsq = delta * delta;
151                 double rad = Math.sqrt(std * std + dtd * (dsq - sts));
152                 if (std >= 0)
153                     alpha = (dsq - sts) / (std + rad);
154                 else
155                     alpha = (rad - std) / dtd;
156                 daxpy(alpha, d, s);
157                 alpha = -alpha;
158                 daxpy(alpha, Hd, r);
159                 break;
160             }
161             alpha = -alpha;
162             daxpy(alpha, Hd, r);
163             rnewTrnew = dot(r, r);
164             double beta = rnewTrnew / rTr;
165             scale(beta, d);
166             daxpy(one, r, d);
167             rTr = rnewTrnew;
168         }
169 
170         return (cg_iter);
171     }
172 
173     /**
174      * constant times a vector plus a vector
175      *
176      * <pre>
177      * vector2 += constant * vector1
178      * </pre>
179      *
180      * @since 1.8
181      */
182     private static void daxpy(double constant, double vector1[], double vector2[]) {
183         if (constant == 0) return;
184 
185         assert vector1.length == vector2.length;
186         for (int i = 0; i < vector1.length; i++) {
187             vector2[i] += constant * vector1[i];
188         }
189     }
190 
191     /**
192      * returns the dot product of two vectors
193      *
194      * @since 1.8
195      */
196     private static double dot(double vector1[], double vector2[]) {
197 
198         double product = 0;
199         assert vector1.length == vector2.length;
200         for (int i = 0; i < vector1.length; i++) {
201             product += vector1[i] * vector2[i];
202         }
203         return product;
204 
205     }
206 
207     /**
208      * returns the euclidean norm of a vector
209      *
210      * @since 1.8
211      */
212     private static double euclideanNorm(double vector[]) {
213 
214         int n = vector.length;
215 
216         if (n < 1) {
217             return 0;
218         }
219 
220         if (n == 1) {
221             return Math.abs(vector[0]);
222         }
223 
224         // this algorithm is (often) more accurate than just summing up the squares and taking the square-root afterwards
225 
226         double scale = 0; // scaling factor that is factored out
227         double sum = 1; // basic sum of squares from which scale has been factored out
228         for (int i = 0; i < n; i++) {
229             if (vector[i] != 0) {
230                 double abs = Math.abs(vector[i]);
231                 // try to get the best scaling factor
232                 if (scale < abs) {
233                     double t = scale / abs;
234                     sum = 1 + sum * (t * t);
235                     scale = abs;
236                 } else {
237                     double t = abs / scale;
238                     sum += t * t;
239                 }
240             }
241         }
242 
243         return scale * Math.sqrt(sum);
244     }
245 
246     /**
247      * scales a vector by a constant
248      *
249      * @since 1.8
250      */
251     private static void scale(double constant, double vector[]) {
252         if (constant == 1.0) return;
253         for (int i = 0; i < vector.length; i++) {
254             vector[i] *= constant;
255         }
256 
257     }
258 }