source: src/main/java/weka/core/Optimization.java @ 17

Last change on this file since 17 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 43.7 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    Optimization.java
19 *    Copyright (C) 2003 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.core;
24
25import weka.core.TechnicalInformation.Field;
26import weka.core.TechnicalInformation.Type;
27
28/**
29 * Implementation of Active-sets method with BFGS update to solve optimization
30 * problem with only bounds constraints in multi-dimensions.  In this
31 * implementation we consider both the lower and higher bound constraints. <p/>
32 *
33 * Here is the sketch of our searching strategy, and the detailed description
34 * of the algorithm can be found in the Appendix of Xin Xu's MSc thesis:<p/>
35 * Initialize everything, incl. initial value, direction, etc.<p/>
36 * LOOP (main algorithm):<br/>
37 *
38 * 1. Perform the line search using the directions for free variables<br/>
39 * 1.1  Check all the bounds that are not "active" (i.e. binding variables)
40 *      and compute the feasible step length to the bound for each of them<br/>
41 * 1.2  Pick up the least feasible step length, say \alpha, and set it as
42 *      the upper bound of the current step length, i.e.
43 *      0&lt;\lambda&lt;=\alpha<br/>
44 * 1.3  Search for any possible step length&lt;=\alpha that can result the
45 *      "sufficient function decrease" (\alpha condition) AND "positive
46 *      definite inverse Hessian" (\beta condition), if possible, using
47 *      SAFEGUARDED polynomial interpolation.  This step length is "safe" and
48 *      thus is used to compute the next value of the free variables .<br/>
49 * 1.4  Fix the variable(s) that are newly bound to its constraint(s).<p/>     
50 *
51 * 2. Check whether there is convergence of all variables or their gradients.
52 *    If there is, check the possibilities to release any current bindings of
53 *    the fixed variables to their bounds based on the "reliable" second-order
54 *    Lagarange multipliers if available.  If it's available and negative for
55 *    one variable, then release it.  If not available, use first-order
56 *    Lagarange multiplier to test release.  If there is any released
57 *    variables, STOP the loop.  Otherwise update the inverse of Hessian matrix
58 *    and gradient for the newly released variables and CONTINUE LOOP.<p/>
59 *
60 * 3. Use BFGS formula to update the inverse of Hessian matrix.  Note the
61 *    already-fixed variables must have zeros in the corresponding entries
62 *    in the inverse Hessian.<p/> 
63 *
64 * 4. Compute the new (newton) search direction d=H^{-1}*g, where H^{-1} is the
65 *    inverse Hessian and g is the Jacobian.  Note that again, the already-
66 *    fixed variables will have zero direction.<p/>
67 *
68 * ENDLOOP<p/>
69 *
70 * A typical usage of this class is to create your own subclass of this class
71 * and provide the objective function and gradients as follows:<p/>
72 * <pre>
73 * class MyOpt extends Optimization {
74 *   // Provide the objective function
75 *   protected double objectiveFunction(double[] x) {
76 *       // How to calculate your objective function...
77 *       // ...
78 *   }
79 *
80 *   // Provide the first derivatives
81 *   protected double[] evaluateGradient(double[] x) {
82 *       // How to calculate the gradient of the objective function...
83 *       // ...
84 *   }
85 *
86 *   // If possible, provide the index^{th} row of the Hessian matrix
87 *   protected double[] evaluateHessian(double[] x, int index) {
88 *      // How to calculate the index^th variable's second derivative
89 *      // ...
90 *   }
91 * }
92 * </pre>
93 *
94 * When it's the time to use it, in some routine(s) of other class...
95 * <pre>
96 * MyOpt opt = new MyOpt();
97 *
98 * // Set up initial variable values and bound constraints
99 * double[] x = new double[numVariables];
100 * // Lower and upper bounds: 1st row is lower bounds, 2nd is upper
101 * double[] constraints = new double[2][numVariables];
102 * ...
103 *
104 * // Find the minimum, 200 iterations as default
105 * x = opt.findArgmin(x, constraints);
106 * while(x == null){  // 200 iterations are not enough
107 *    x = opt.getVarbValues();  // Try another 200 iterations
108 *    x = opt.findArgmin(x, constraints);
109 * }
110 *
111 * // The minimal function value
112 * double minFunction = opt.getMinFunction();
113 * ...
114 * </pre>
115 *
116 * It is recommended that Hessian values be provided so that the second-order
117 * Lagrangian multiplier estimate can be calcluated.  However, if it is not
118 * provided, there is no need to override the <code>evaluateHessian()</code>
119 * function.<p/>
120 *
121 * REFERENCES (see also the <code>getTechnicalInformation()</code> method):<br/>
122 * The whole model algorithm is adapted from Chapter 5 and other related
123 * chapters in Gill, Murray and Wright(1981) "Practical Optimization", Academic
124 * Press.  and Gill and Murray(1976) "Minimization Subject to Bounds on the
125 * Variables", NPL Report NAC72, while Chong and Zak(1996) "An Introduction to
126 * Optimization", John Wiley &amp; Sons, Inc. provides us a brief but helpful
127 * introduction to the method. <p/>
128 *
129 * Dennis and Schnabel(1983) "Numerical Methods for Unconstrained Optimization
130 * and Nonlinear Equations", Prentice-Hall Inc. and Press et al.(1992) "Numeric
131 * Recipe in C", Second Edition, Cambridge University Press. are consulted for
132 * the polynomial interpolation used in the line search implementation.  <p/>
133 *
134 * The Hessian modification in BFGS update uses Cholesky factorization and two
135 * rank-one modifications:<br/>
136 * Bk+1 = Bk + (Gk*Gk')/(Gk'Dk) + (dGk*(dGk)'))/[alpha*(dGk)'*Dk]. <br/>
137 * where Gk is the gradient vector, Dk is the direction vector and alpha is the
138 * step rate. <br/>
139 * This method is due to Gill, Golub, Murray and Saunders(1974) ``Methods for
140 * Modifying Matrix Factorizations'', Mathematics of Computation, Vol.28,
141 * No.126, pp 505-535. <p/>
142 *
143 * @author Xin Xu (xx5@cs.waikato.ac.nz)
144 * @version $Revision: 5953 $
145 * @see #getTechnicalInformation()
146 */
147public abstract class Optimization
148    implements TechnicalInformationHandler, RevisionHandler {
149   
150    protected double m_ALF = 1.0e-4;
151
152    protected double m_BETA = 0.9;   
153
154    protected double m_TOLX = 1.0e-6;
155   
156    protected double m_STPMX = 100.0;
157   
158    protected int m_MAXITS = 200;
159   
160    protected static boolean m_Debug = false;
161   
162    /** function value */
163    protected double m_f;   
164 
165    /** G'*p */
166    private double m_Slope;
167   
168    /** Test if zero step in lnsrch */
169    private boolean m_IsZeroStep = false;
170   
171    /** Used when iteration overflow occurs */
172    private double[] m_X;
173   
174    /** Compute machine precision */
175    protected static double m_Epsilon, m_Zero; 
176    static {
177        m_Epsilon=1.0;
178        while(1.0+m_Epsilon > 1.0){
179            m_Epsilon /= 2.0;       
180        }
181        m_Epsilon *= 2.0;
182        m_Zero = Math.sqrt(m_Epsilon);
183        if (m_Debug)
184            System.err.print("Machine precision is "+m_Epsilon+
185                             " and zero set to "+m_Zero);
186    }
187   
188    /**
189     * Returns an instance of a TechnicalInformation object, containing
190     * detailed information about the technical background of this class,
191     * e.g., paper reference or book this class is based on.
192     *
193     * @return the technical information about this class
194     */
195    public TechnicalInformation getTechnicalInformation() {
196      TechnicalInformation      result;
197      TechnicalInformation      additional;
198     
199      result = new TechnicalInformation(Type.MASTERSTHESIS);
200      result.setValue(Field.AUTHOR, "Xin Xu");
201      result.setValue(Field.YEAR, "2003");
202      result.setValue(Field.TITLE, "Statistical learning in multiple instance problem");
203      result.setValue(Field.SCHOOL, "University of Waikato");
204      result.setValue(Field.ADDRESS, "Hamilton, NZ");
205      result.setValue(Field.NOTE, "0657.594");
206
207      additional = result.add(Type.BOOK);
208      additional.setValue(Field.AUTHOR, "P. E. Gill and W. Murray and M. H. Wright");
209      additional.setValue(Field.YEAR, "1981");
210      additional.setValue(Field.TITLE, "Practical Optimization");
211      additional.setValue(Field.PUBLISHER, "Academic Press");
212      additional.setValue(Field.ADDRESS, "London and New York");
213     
214      additional = result.add(Type.TECHREPORT);
215      additional.setValue(Field.AUTHOR, "P. E. Gill and W. Murray");
216      additional.setValue(Field.YEAR, "1976");
217      additional.setValue(Field.TITLE, "Minimization subject to bounds on the variables");
218      additional.setValue(Field.INSTITUTION, "National Physical Laboratory");
219      additional.setValue(Field.NUMBER, "NAC 72");
220     
221      additional = result.add(Type.BOOK);
222      additional.setValue(Field.AUTHOR, "E. K. P. Chong and S. H. Zak");
223      additional.setValue(Field.YEAR, "1996");
224      additional.setValue(Field.TITLE, "An Introduction to Optimization");
225      additional.setValue(Field.PUBLISHER, "John Wiley and Sons");
226      additional.setValue(Field.ADDRESS, "New York");
227     
228      additional = result.add(Type.BOOK);
229      additional.setValue(Field.AUTHOR, "J. E. Dennis and R. B. Schnabel");
230      additional.setValue(Field.YEAR, "1983");
231      additional.setValue(Field.TITLE, "Numerical Methods for Unconstrained Optimization and Nonlinear Equations");
232      additional.setValue(Field.PUBLISHER, "Prentice-Hall");
233     
234      additional = result.add(Type.BOOK);
235      additional.setValue(Field.AUTHOR, "W. H. Press and B. P. Flannery and S. A. Teukolsky and W. T. Vetterling");
236      additional.setValue(Field.YEAR, "1992");
237      additional.setValue(Field.TITLE, "Numerical Recipes in C");
238      additional.setValue(Field.PUBLISHER, "Cambridge University Press");
239      additional.setValue(Field.EDITION, "Second");
240     
241      additional = result.add(Type.ARTICLE);
242      additional.setValue(Field.AUTHOR, "P. E. Gill and G. H. Golub and W. Murray and M. A. Saunders");
243      additional.setValue(Field.YEAR, "1974");
244      additional.setValue(Field.TITLE, "Methods for modifying matrix factorizations");
245      additional.setValue(Field.JOURNAL, "Mathematics of Computation");
246      additional.setValue(Field.VOLUME, "28");
247      additional.setValue(Field.NUMBER, "126");
248      additional.setValue(Field.PAGES, "505-535");
249     
250      return result;
251    }
252   
253    /**
254     * Subclass should implement this procedure to evaluate objective
255     * function to be minimized
256     *
257     * @param x the variable values
258     * @return the objective function value
259     * @throws Exception if something goes wrong
260     */
261    protected abstract double objectiveFunction(double[] x) throws Exception;
262
263    /**
264     * Subclass should implement this procedure to evaluate gradient
265     * of the objective function
266     *
267     * @param x the variable values
268     * @return the gradient vector
269     * @throws Exception if something goes wrong
270     */
271    protected abstract double[] evaluateGradient(double[] x) throws Exception;
272
273    /**
274     * Subclass is recommended to override this procedure to evaluate second-order
275     * gradient of the objective function.  If it's not provided, it returns
276     * null.
277     *
278     * @param x the variables
279     * @param index the row index in the Hessian matrix
280     * @return one row (the row #index) of the Hessian matrix, null as default
281     * @throws Exception if something goes wrong
282     */
283    protected double[] evaluateHessian(double[] x, int index) throws Exception{
284        return null;
285    }
286
287    /**
288     * Get the minimal function value
289     *
290     * @return minimal function value found
291     */
292    public double getMinFunction() {
293      return m_f;
294    }
295
296    /**
297     * Set the maximal number of iterations in searching (Default 200)
298     *
299     * @param it the maximal number of iterations
300     */
301    public void setMaxIteration(int it) {
302      m_MAXITS=it;
303    }
304     
305    /**
306     * Set whether in debug mode
307     *
308     * @param db use debug or not
309     */
310    public void setDebug(boolean db) {
311      m_Debug = db;
312    }
313   
314    /**
315     * Get the variable values.  Only needed when iterations exceeds
316     * the max threshold.
317     *
318     * @return the current variable values
319     */
320    public double[] getVarbValues() {
321      return m_X;
322    }
323   
324    /**
325     * Find a new point x in the direction p from a point xold at which the
326     * value of the function has decreased sufficiently, the positive
327     * definiteness of B matrix (approximation of the inverse of the Hessian)
328     * is preserved and no bound constraints are violated.  Details see "Numerical
329     * Methods for Unconstrained Optimization and Nonlinear Equations".
330     * "Numeric Recipes in C" was also consulted.
331     *
332     * @param xold old x value
333     * @param gradient gradient at that point
334     * @param direct direction vector
335     * @param stpmax maximum step length
336     * @param isFixed indicating whether a variable has been fixed
337     * @param nwsBounds non-working set bounds.  Means these variables are free and
338     *                  subject to the bound constraints in this step
339     * @param wsBdsIndx index of variables that has working-set bounds.  Means
340     *                  these variables are already fixed and no longer subject to
341     *                  the constraints
342     * @return new value along direction p from xold, null if no step was taken
343     * @throws Exception if an error occurs
344     */
345    public double[] lnsrch(double[] xold, double[] gradient, 
346                           double[] direct, double stpmax,
347                           boolean[] isFixed, double[][] nwsBounds,
348                           DynamicIntArray wsBdsIndx)
349        throws Exception {
350       
351        int i, k,len=xold.length, 
352            fixedOne=-1; // idx of variable to be fixed
353        double alam, alamin; // lambda to be found, and its lower bound
354       
355        // For convergence and bound test
356        double temp,test,alpha=Double.POSITIVE_INFINITY,fold=m_f,sum; 
357       
358        // For cubic interpolation
359        double a,alam2=0,b,disc=0,maxalam=1.0,rhs1,rhs2,tmplam;
360       
361        double[] x = new double[len]; // New variable values
362       
363        // Scale the step
364        for (sum=0.0,i=0;i<len;i++){
365            if(!isFixed[i]) // For fixed variables, direction = 0
366                sum += direct[i]*direct[i];
367        }       
368        sum = Math.sqrt(sum);
369       
370        if (m_Debug)
371            System.err.println("fold:  "+Utils.doubleToString(fold,10,7)+"\n"+
372                               "sum:  "+Utils.doubleToString(sum,10,7)+"\n"+
373                               "stpmax:  "+Utils.doubleToString(stpmax,10,7));
374        if (sum > stpmax){
375            for (i=0;i<len;i++)
376                if(!isFixed[i])
377                    direct[i] *= stpmax/sum;           
378        }
379        else
380            maxalam = stpmax/sum;
381       
382        // Compute initial rate of decrease, g'*d
383        m_Slope=0.0;
384        for (i=0;i<len;i++){
385            x[i] = xold[i];
386            if(!isFixed[i])
387                m_Slope += gradient[i]*direct[i];
388        }
389       
390        if (m_Debug)
391            System.err.print("slope:  " + Utils.doubleToString(m_Slope,10,7)+ "\n");
392       
393        // Slope too small
394        if(Math.abs(m_Slope)<=m_Zero){
395            if (m_Debug)
396                System.err.println("Gradient and direction orthogonal -- "+
397                                   "Min. found with current fixed variables"+
398                                   " (or all variables fixed). Try to release"+
399                                   " some variables now.");
400            return x;
401        }
402       
403        // Err: slope > 0
404        if(m_Slope > m_Zero){
405            if(m_Debug)
406                for(int h=0; h<x.length; h++)
407                    System.err.println(h+": isFixed="+isFixed[h]+", x="+
408                                       x[h]+", grad="+gradient[h]+", direct="+
409                                       direct[h]);
410            throw new Exception("g'*p positive! -- Try to debug from here: line 327.");
411        }
412       
413        // Compute LAMBDAmin and upper bound of lambda--alpha
414        test=0.0;
415        for(i=0;i<len;i++){         
416            if(!isFixed[i]){// No need for fixed variables
417                temp=Math.abs(direct[i])/Math.max(Math.abs(x[i]),1.0);
418                if (temp > test) test=temp;
419            }
420        }
421       
422        if(test>m_Zero) // Not converge
423            alamin = m_TOLX/test;
424        else{
425            if (m_Debug)
426                System.err.println("Zero directions for all free variables -- "+
427                                   "Min. found with current fixed variables"+
428                                   " (or all variables fixed). Try to release"+
429                                   " some variables now.");
430            return x;
431        }
432               
433        // Check whether any non-working-set bounds are "binding"
434        for(i=0;i<len;i++){
435            if(!isFixed[i]){// No need for fixed variables
436                double alpi;
437                if((direct[i]<-m_Epsilon) && !Double.isNaN(nwsBounds[0][i])){//Not feasible
438                    alpi = (nwsBounds[0][i]-xold[i])/direct[i];
439                    if(alpi <= m_Zero){ // Zero
440                        if (m_Debug)
441                            System.err.println("Fix variable "+i+
442                                               " to lower bound "+ nwsBounds[0][i]+
443                                               " from value "+ xold[i]);
444                        x[i] = nwsBounds[0][i];
445                        isFixed[i]=true; // Fix this variable
446                        alpha = 0.0;
447                        nwsBounds[0][i]=Double.NaN; //Add cons. to working set
448                        wsBdsIndx.addElement(i);
449                    }
450                    else if(alpha > alpi){ // Fix one variable in one iteration
451                        alpha = alpi;
452                        fixedOne = i;
453                    }                   
454                }
455                else if((direct[i]>m_Epsilon) && !Double.isNaN(nwsBounds[1][i])){//Not feasible
456                    alpi = (nwsBounds[1][i]-xold[i])/direct[i];
457                    if(alpi <= m_Zero){ // Zero
458                        if (m_Debug)
459                            System.err.println("Fix variable "+i+
460                                               " to upper bound "+ nwsBounds[1][i]+
461                                               " from value "+ xold[i]);
462                        x[i] = nwsBounds[1][i];
463                        isFixed[i]=true; // Fix this variable
464                        alpha = 0.0;
465                        nwsBounds[1][i]=Double.NaN; //Add cons. to working set
466                        wsBdsIndx.addElement(i);
467                    }
468                    else if(alpha > alpi){
469                        alpha = alpi;
470                        fixedOne = i;
471                    }                   
472                }                               
473            }
474        }       
475       
476        if (m_Debug){
477            System.err.println("alamin: " + Utils.doubleToString(alamin,10,7));
478            System.err.println("alpha: " + Utils.doubleToString(alpha,10,7));
479        }
480       
481        if(alpha <= m_Zero){ // Zero       
482            m_IsZeroStep = true;
483            if (m_Debug)
484                System.err.println("Alpha too small, try again");
485            return x;
486        }
487       
488        alam = alpha; // Always try full feasible newton step
489        if(alam > 1.0)
490            alam = 1.0;
491       
492        // Iteration of one newton step, if necessary, backtracking is done
493        double initF=fold, // Initial function value
494            hi=alam, lo=alam, newSlope=0, fhi=m_f, flo=m_f;// Variables used for beta condition
495        double[] newGrad;  // Gradient on the new variable values
496       
497        kloop:
498        for (k=0;;k++) {
499            if(m_Debug)
500                System.err.println("\nLine search iteration: " + k);
501           
502            for (i=0;i<len;i++){
503                if(!isFixed[i]){
504                    x[i] = xold[i]+alam*direct[i];  // Compute xnew
505                    if(!Double.isNaN(nwsBounds[0][i]) && (x[i]<nwsBounds[0][i])){   
506                        x[i] = nwsBounds[0][i]; //Rounding error       
507                    }
508                    else if(!Double.isNaN(nwsBounds[1][i]) && (x[i]>nwsBounds[1][i])){         
509                        x[i] = nwsBounds[1][i]; //Rounding error       
510                    }
511                }
512            }
513           
514            m_f = objectiveFunction(x);    // Compute fnew
515            if(Double.isNaN(m_f))
516                throw new Exception("Objective function value is NaN!");
517       
518            while(Double.isInfinite(m_f)){ // Avoid infinity
519                if(m_Debug)
520                    System.err.println("Too large m_f.  Shrink step by half.");
521                alam *= 0.5; // Shrink by half
522                if(alam <= m_Epsilon){
523                    if(m_Debug)
524                        System.err.println("Wrong starting points, change them!");
525                    return x;
526                }
527               
528                for (i=0;i<len;i++)
529                    if(!isFixed[i])
530                        x[i] = xold[i]+alam*direct[i]; 
531               
532                m_f = objectiveFunction(x); 
533                if(Double.isNaN(m_f))
534                    throw new Exception("Objective function value is NaN!");
535       
536                initF = Double.POSITIVE_INFINITY;
537            }
538           
539            if(m_Debug) {
540                System.err.println("obj. function: " + 
541                                   Utils.doubleToString(m_f, 10, 7));
542                System.err.println("threshold: " + 
543                                   Utils.doubleToString(fold+m_ALF*alam*m_Slope,10,7));
544            }
545           
546            if(m_f<=fold+m_ALF*alam*m_Slope){// Alpha condition: sufficient function decrease
547                if(m_Debug)             
548                    System.err.println("Sufficient function decrease (alpha condition): "); 
549                newGrad = evaluateGradient(x);
550                for(newSlope=0.0,i=0; i<len; i++)
551                    if(!isFixed[i])
552                        newSlope += newGrad[i]*direct[i];
553
554                if(newSlope >= m_BETA*m_Slope){ // Beta condition: ensure pos. defnty. 
555                    if(m_Debug)         
556                        System.err.println("Increasing derivatives (beta condition): ");       
557
558                    if((fixedOne!=-1) && (alam>=alpha)){ // Has bounds and over
559                        if(direct[fixedOne] > 0){
560                            x[fixedOne] = nwsBounds[1][fixedOne]; // Avoid rounding error
561                            nwsBounds[1][fixedOne]=Double.NaN; //Add cons. to working set
562                        }
563                        else{
564                            x[fixedOne] = nwsBounds[0][fixedOne]; // Avoid rounding error
565                            nwsBounds[0][fixedOne]=Double.NaN; //Add cons. to working set
566                        }
567                       
568                        if(m_Debug)
569                            System.err.println("Fix variable "
570                                               +fixedOne+" to bound "+ x[fixedOne]+
571                                               " from value "+ xold[fixedOne]);
572                        isFixed[fixedOne]=true; // Fix the variable
573                        wsBdsIndx.addElement(fixedOne);
574                    }           
575                    return x;
576                }
577                else if(k==0){ // First time: increase alam
578                    // Search for the smallest value not complying with alpha condition
579                    double upper = Math.min(alpha,maxalam); 
580                    if(m_Debug)
581                        System.err.println("Alpha condition holds, increase alpha... ");
582                    while(!((alam>=upper) || (m_f>fold+m_ALF*alam*m_Slope))){
583                        lo = alam;
584                        flo = m_f;
585                        alam *= 2.0;
586                        if(alam>=upper)  // Avoid rounding errors
587                            alam=upper;
588
589                        for (i=0;i<len;i++)
590                            if(!isFixed[i])
591                                x[i] = xold[i]+alam*direct[i];
592                        m_f = objectiveFunction(x);
593                        if(Double.isNaN(m_f))
594                            throw new Exception("Objective function value is NaN!");
595                       
596                        newGrad = evaluateGradient(x);
597                        for(newSlope=0.0,i=0; i<len; i++)
598                            if(!isFixed[i])
599                                newSlope += newGrad[i]*direct[i];
600                       
601                        if(newSlope >= m_BETA*m_Slope){
602                            if (m_Debug)               
603                                System.err.println("Increasing derivatives (beta condition): \n"+
604                                                   "newSlope = "+Utils.doubleToString(newSlope,10,7));
605                           
606                            if((fixedOne!=-1) && (alam>=alpha)){ // Has bounds and over
607                                if(direct[fixedOne] > 0){
608                                    x[fixedOne] = nwsBounds[1][fixedOne]; // Avoid rounding error
609                                    nwsBounds[1][fixedOne]=Double.NaN; //Add cons. to working set
610                                }
611                                else{
612                                    x[fixedOne] = nwsBounds[0][fixedOne]; // Avoid rounding error
613                                    nwsBounds[0][fixedOne]=Double.NaN; //Add cons. to working set
614                                }
615                               
616                                if(m_Debug)
617                                    System.err.println("Fix variable "
618                                                       +fixedOne+" to bound "+ x[fixedOne]+
619                                                       " from value "+ xold[fixedOne]);
620                                isFixed[fixedOne]=true; // Fix the variable
621                                wsBdsIndx.addElement(fixedOne);
622                            }                                               
623                            return x;
624                        }
625                    }
626                    hi = alam;
627                    fhi = m_f;                 
628                    break kloop;
629                }
630                else{
631                    if(m_Debug)
632                        System.err.println("Alpha condition holds.");
633                    hi = alam2; lo = alam; flo = m_f;
634                    break kloop;
635                }                   
636            }       
637            else if (alam < alamin) { // No feasible lambda found       
638                if(initF<fold){ 
639                    alam = Math.min(1.0,alpha);
640                    for (i=0;i<len;i++)
641                        if(!isFixed[i])
642                            x[i] = xold[i]+alam*direct[i]; //Still take Alpha
643                   
644                    if (m_Debug)
645                        System.err.println("No feasible lambda: still take"+
646                                           " alpha="+alam);
647                   
648                    if((fixedOne!=-1) && (alam>=alpha)){ // Has bounds and over
649                        if(direct[fixedOne] > 0){
650                            x[fixedOne] = nwsBounds[1][fixedOne]; // Avoid rounding error
651                            nwsBounds[1][fixedOne]=Double.NaN; //Add cons. to working set
652                        }
653                        else{
654                            x[fixedOne] = nwsBounds[0][fixedOne]; // Avoid rounding error
655                            nwsBounds[0][fixedOne]=Double.NaN; //Add cons. to working set
656                        }
657                       
658                        if(m_Debug)
659                            System.err.println("Fix variable "
660                                               +fixedOne+" to bound "+ x[fixedOne]+
661                                               " from value "+ xold[fixedOne]);
662                        isFixed[fixedOne]=true; // Fix the variable
663                        wsBdsIndx.addElement(fixedOne);
664                    }                               
665                }
666                else{   // Convergence on delta(x)
667                    for(i=0;i<len;i++) 
668                        x[i]=xold[i];
669                    m_f=fold;
670                    if (m_Debug)
671                        System.err.println("Cannot find feasible lambda"); 
672                }
673               
674                return x; 
675            }
676            else { // Backtracking by polynomial interpolation
677                if(k==0){ // First time backtrack: quadratic interpolation
678                    if(!Double.isInfinite(initF))
679                        initF = m_f;               
680                    // lambda = -g'(0)/(2*g''(0))
681                    tmplam = -0.5*alam*m_Slope/((m_f-fold)/alam-m_Slope);
682                }
683                else {    // Subsequent backtrack: cubic interpolation
684                    rhs1 = m_f-fold-alam*m_Slope;
685                    rhs2 = fhi-fold-alam2*m_Slope;
686                    a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2);
687                    b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
688                    if (a == 0.0) tmplam = -m_Slope/(2.0*b);
689                    else {
690                        disc=b*b-3.0*a*m_Slope;
691                        if (disc < 0.0) disc = 0.0;
692                        double numerator = -b+Math.sqrt(disc);
693                        if(numerator >= Double.MAX_VALUE){
694                            numerator = Double.MAX_VALUE;
695                            if (m_Debug)
696                                System.err.print("-b+sqrt(disc) too large! Set it to MAX_VALUE.");
697                        }
698                        tmplam=numerator/(3.0*a);
699                    }
700                    if (m_Debug)
701                        System.err.print("Cubic interpolation: \n" + 
702                                         "a:   " + Utils.doubleToString(a,10,7)+ "\n" +
703                                         "b:   " + Utils.doubleToString(b,10,7)+ "\n" +
704                                         "disc:   " + Utils.doubleToString(disc,10,7)+ "\n" +
705                                         "tmplam:   " + tmplam + "\n" +
706                                         "alam:   " + Utils.doubleToString(alam,10,7)+ "\n");   
707                    if (tmplam>0.5*alam)
708                        tmplam=0.5*alam;             // lambda <= 0.5*lambda_old
709                }
710            }
711            alam2=alam;
712            fhi=m_f;
713            alam=Math.max(tmplam,0.1*alam);          // lambda >= 0.1*lambda_old
714           
715            if(alam>alpha){
716                throw new Exception("Sth. wrong in lnsrch:"+
717                                    "Lambda infeasible!(lambda="+alam+
718                                    ", alpha="+alpha+", upper="+tmplam+
719                                    "|"+(-alpha*m_Slope/(2.0*((m_f-fold)/alpha-m_Slope)))+
720                                    ", m_f="+m_f+", fold="+fold+
721                                    ", slope="+m_Slope);
722            }       
723        } // Endfor(k=0;;k++)
724       
725        // Quadratic interpolation between lamda values between lo and hi.
726        // If cannot find a value satisfying beta condition, use lo.
727        double ldiff = hi-lo, lincr;
728        if(m_Debug)
729            System.err.println("Last stage of searching for beta condition (alam between "
730                               +Utils.doubleToString(lo,10,7)+" and "
731                               +Utils.doubleToString(hi,10,7)+")...\n"+
732                               "Quadratic Interpolation(QI):\n"+
733                               "Last newSlope = "+Utils.doubleToString(newSlope, 10, 7));
734       
735        while((newSlope<m_BETA*m_Slope) && (ldiff>=alamin)){
736            lincr = -0.5*newSlope*ldiff*ldiff/(fhi-flo-newSlope*ldiff);
737
738            if(m_Debug)
739                System.err.println("fhi = "+fhi+"\n"+
740                                   "flo = "+flo+"\n"+
741                                   "ldiff = "+ldiff+"\n"+
742                                   "lincr (using QI) = "+lincr+"\n");
743           
744            if(lincr<0.2*ldiff) lincr=0.2*ldiff;
745            alam = lo+lincr;
746            if(alam >= hi){ // We cannot go beyond the bounds, so the best we can try is hi
747                alam=hi;
748                lincr=ldiff;
749            }
750            for (i=0;i<len;i++)
751                if(!isFixed[i])
752                    x[i] = xold[i]+alam*direct[i];
753            m_f = objectiveFunction(x);
754            if(Double.isNaN(m_f))
755                throw new Exception("Objective function value is NaN!");
756       
757            if(m_f>fold+m_ALF*alam*m_Slope){ 
758                // Alpha condition fails, shrink lambda_upper
759                ldiff = lincr;
760                fhi = m_f;
761            }       
762            else{ // Alpha condition holds         
763                newGrad = evaluateGradient(x);
764                for(newSlope=0.0,i=0; i<len; i++)
765                    if(!isFixed[i])
766                        newSlope += newGrad[i]*direct[i];
767               
768                if(newSlope < m_BETA*m_Slope){
769                    // Beta condition fails, shrink lambda_lower
770                    lo = alam;
771                    ldiff -= lincr;
772                    flo = m_f;
773                }
774            }
775        } 
776       
777        if(newSlope < m_BETA*m_Slope){ // Cannot satisfy beta condition, take lo
778            if(m_Debug)
779                System.err.println("Beta condition cannot be satisfied, take alpha condition");
780            alam=lo;
781            for (i=0;i<len;i++)
782                if(!isFixed[i])
783                    x[i] = xold[i]+alam*direct[i];
784            m_f = flo;
785        }
786        else if(m_Debug)
787            System.err.println("Both alpha and beta conditions are satisfied. alam="
788                               +Utils.doubleToString(alam,10,7));
789       
790        if((fixedOne!=-1) && (alam>=alpha)){ // Has bounds and over
791            if(direct[fixedOne] > 0){
792                x[fixedOne] = nwsBounds[1][fixedOne]; // Avoid rounding error
793                nwsBounds[1][fixedOne]=Double.NaN; //Add cons. to working set
794            }
795            else{
796                x[fixedOne] = nwsBounds[0][fixedOne]; // Avoid rounding error
797                nwsBounds[0][fixedOne]=Double.NaN; //Add cons. to working set
798            }
799           
800            if(m_Debug)
801                System.err.println("Fix variable "
802                                   +fixedOne+" to bound "+ x[fixedOne]+
803                                   " from value "+ xold[fixedOne]);
804            isFixed[fixedOne]=true; // Fix the variable
805            wsBdsIndx.addElement(fixedOne);
806        }
807       
808        return x;
809    }
810   
811    /**
812     * Main algorithm.  Descriptions see "Practical Optimization"
813     *
814     * @param initX initial point of x, assuming no value's on the bound!
815     * @param constraints the bound constraints of each variable
816     *                    constraints[0] is the lower bounds and
817     *                    constraints[1] is the upper bounds
818     * @return the solution of x, null if number of iterations not enough
819     * @throws Exception if an error occurs
820     */
821    public double[] findArgmin(double[] initX, double[][] constraints) 
822        throws Exception{
823        int l = initX.length;
824       
825        // Initially all variables are free, all bounds are constraints of
826        // non-working-set constraints
827        boolean[] isFixed = new boolean[l];
828        double[][] nwsBounds = new double[2][l];
829        // Record indice of fixed variables, simply for efficiency
830        DynamicIntArray wsBdsIndx = new DynamicIntArray(constraints.length); 
831        // Vectors used to record the variable indices to be freed     
832        DynamicIntArray toFree=null, oldToFree=null;   
833
834        // Initial value of obj. function, gradient and inverse of the Hessian
835        m_f = objectiveFunction(initX);
836        if(Double.isNaN(m_f))
837            throw new Exception("Objective function value is NaN!");
838       
839        double sum=0;
840        double[] grad=evaluateGradient(initX), oldGrad, oldX,
841            deltaGrad=new double[l], deltaX=new double[l],
842            direct = new double[l], x = new double[l];
843        Matrix L = new Matrix(l, l);  // Lower triangle of Cholesky factor
844        double[] D = new double[l];   // Diagonal of Cholesky factor
845        for(int i=0; i<l; i++){
846            L.setRow(i, new double[l]);
847            L.setElement(i,i,1.0);
848            D[i] = 1.0;
849            direct[i] = -grad[i];
850            sum += grad[i]*grad[i];
851            x[i] = initX[i];
852            nwsBounds[0][i] = constraints[0][i];
853            nwsBounds[1][i] = constraints[1][i];
854            isFixed[i] = false;
855        }       
856        double stpmax = m_STPMX*Math.max(Math.sqrt(sum), l);
857       
858        iterates:
859        for(int step=0; step < m_MAXITS; step++){
860            if (m_Debug)
861                System.err.println("\nIteration # " + step + ":");         
862           
863            // Try at most one feasible newton step, i.e. 0<lamda<=alpha
864            oldX = x;
865            oldGrad = grad;
866           
867            // Also update grad
868            if (m_Debug)
869                System.err.println("Line search ... ");
870            m_IsZeroStep = false;
871            x=lnsrch(x, grad, direct, stpmax, 
872                     isFixed, nwsBounds, wsBdsIndx);
873            if (m_Debug)
874                System.err.println("Line search finished.");
875           
876            if(m_IsZeroStep){ // Zero step, simply delete rows/cols of D and L
877                for(int f=0; f<wsBdsIndx.size(); f++){
878                    int idx=wsBdsIndx.elementAt(f);
879                    L.setRow(idx, new double[l]);
880                    L.setColumn(idx, new double[l]);
881                    D[idx] = 0.0;
882                }               
883                grad = evaluateGradient(x);
884                step--;
885            }
886            else{
887                // Check converge on x
888                boolean finish = false;
889                double test=0.0;
890                for(int h=0; h<l; h++){
891                    deltaX[h] = x[h]-oldX[h];
892                    double tmp=Math.abs(deltaX[h])/
893                        Math.max(Math.abs(x[h]), 1.0);
894                    if(tmp > test) test = tmp;                             
895                }
896                if(test < m_Zero){
897                    if (m_Debug)
898                        System.err.println("\nDeltaX converge: "+test);
899                    finish = true;
900                }
901               
902                // Check zero gradient     
903                grad = evaluateGradient(x);
904                test=0.0;
905                double denom=0.0, dxSq=0.0, dgSq=0.0, newlyBounded=0.0; 
906                for(int g=0; g<l; g++){
907                    if(!isFixed[g]){               
908                        deltaGrad[g] = grad[g] - oldGrad[g];             
909                        // Calculate the denominators                       
910                        denom += deltaX[g]*deltaGrad[g];
911                        dxSq += deltaX[g]*deltaX[g];
912                        dgSq += deltaGrad[g]*deltaGrad[g];
913                    }
914                    else // Only newly bounded variables will be non-zero
915                        newlyBounded +=  deltaX[g]*(grad[g]-oldGrad[g]);
916                   
917                    // Note: CANNOT use projected gradient for testing
918                    // convergence because of newly bounded variables
919                    double tmp = Math.abs(grad[g])*
920                        Math.max(Math.abs(direct[g]),1.0)/
921                        Math.max(Math.abs(m_f),1.0);
922                    if(tmp > test) test = tmp; 
923                }
924               
925                if(test < m_Zero){
926                    if (m_Debug)
927                        System.err.println("Gradient converge: "+test);
928                    finish = true;
929                }           
930               
931                // dg'*dx could be < 0 using inexact lnsrch
932                if(m_Debug)
933                    System.err.println("dg'*dx="+(denom+newlyBounded)); 
934                // dg'*dx = 0
935                if(Math.abs(denom+newlyBounded) < m_Zero)
936                    finish = true;
937               
938                int size = wsBdsIndx.size();
939                boolean isUpdate = true;  // Whether to update BFGS formula         
940                // Converge: check whether release any current constraints
941                if(finish){
942                    if (m_Debug)
943                        System.err.println("Test any release possible ...");
944                       
945                    if(toFree != null)
946                        oldToFree = (DynamicIntArray)toFree.copy();
947                    toFree = new DynamicIntArray(wsBdsIndx.size());
948                   
949                    for(int m=size-1; m>=0; m--){
950                        int index=wsBdsIndx.elementAt(m);
951                        double[] hessian = evaluateHessian(x, index);                   
952                        double deltaL=0.0;
953                        if(hessian != null){
954                            for(int mm=0; mm<hessian.length; mm++)
955                                if(!isFixed[mm]) // Free variable
956                                    deltaL += hessian[mm]*direct[mm];
957                        }
958                       
959                        // First and second order Lagrangian multiplier estimate
960                        // If user didn't provide Hessian, use first-order only
961                        double L1, L2;
962                        if(x[index] >= constraints[1][index]) // Upper bound
963                            L1 = -grad[index];
964                        else if(x[index] <= constraints[0][index])// Lower bound
965                            L1 = grad[index];
966                        else
967                            throw new Exception("x["+index+"] not fixed on the"+
968                                                " bounds where it should have been!");
969                       
970                        // L2 = L1 + deltaL
971                        L2 = L1 + deltaL;                       
972                        if (m_Debug)
973                            System.err.println("Variable "+index+
974                                               ": Lagrangian="+L1+"|"+L2);
975                       
976                        //Check validity of Lagrangian multiplier estimate
977                        boolean isConverge = 
978                            (2.0*Math.abs(deltaL)) < Math.min(Math.abs(L1),
979                                                              Math.abs(L2)); 
980                        if((L1*L2>0.0) && isConverge){ //Same sign and converge: valid
981                            if(L2 < 0.0){// Negative Lagrangian: feasible
982                                toFree.addElement(index);
983                                wsBdsIndx.removeElementAt(m);
984                                finish=false; // Not optimal, cannot finish
985                            }
986                        }
987                       
988                        // Although hardly happen, better check it
989                        // If the first-order Lagrangian multiplier estimate is wrong,
990                        // avoid zigzagging
991                        if((hessian==null) && (toFree != null) && toFree.equal(oldToFree)) 
992                            finish = true;           
993                    }
994                   
995                    if(finish){// Min. found
996                        if (m_Debug)
997                            System.err.println("Minimum found.");
998                        m_f = objectiveFunction(x);
999                        if(Double.isNaN(m_f))
1000                            throw new Exception("Objective function value is NaN!");   
1001                        return x;
1002                    }
1003                   
1004                    // Free some variables
1005                    for(int mmm=0; mmm<toFree.size(); mmm++){
1006                        int freeIndx=toFree.elementAt(mmm);
1007                        isFixed[freeIndx] = false; // Free this variable
1008                        if(x[freeIndx] <= constraints[0][freeIndx]){// Lower bound
1009                            nwsBounds[0][freeIndx] = constraints[0][freeIndx];
1010                            if (m_Debug)
1011                                System.err.println("Free variable "+freeIndx+
1012                                                   " from bound "+ 
1013                                                   nwsBounds[0][freeIndx]);
1014                        }
1015                        else{ // Upper bound
1016                            nwsBounds[1][freeIndx] = constraints[1][freeIndx];
1017                            if (m_Debug)
1018                                System.err.println("Free variable "+freeIndx+
1019                                                   " from bound "+ 
1020                                                   nwsBounds[1][freeIndx]);
1021                        }                       
1022                        L.setElement(freeIndx, freeIndx, 1.0);
1023                        D[freeIndx] = 1.0;
1024                        isUpdate = false;                       
1025                    }                   
1026                }
1027               
1028                if(denom<Math.max(m_Zero*Math.sqrt(dxSq)*Math.sqrt(dgSq), m_Zero)){
1029                    if (m_Debug) 
1030                        System.err.println("dg'*dx negative!");
1031                    isUpdate = false; // Do not update             
1032                }               
1033                // If Hessian will be positive definite, update it
1034                if(isUpdate){
1035                   
1036                    // modify once: dg*dg'/(dg'*dx)     
1037                    double coeff = 1.0/denom; // 1/(dg'*dx)     
1038                    updateCholeskyFactor(L,D,deltaGrad,coeff,isFixed);
1039                   
1040                    // modify twice: g*g'/(g'*p)       
1041                    coeff = 1.0/m_Slope; // 1/(g'*p)
1042                    updateCholeskyFactor(L,D,oldGrad,coeff,isFixed);               
1043                }
1044            }
1045           
1046            // Find new direction
1047            Matrix LD = new Matrix(l,l); // L*D
1048            double[] b = new double[l];
1049           
1050            for(int k=0; k<l; k++){
1051                if(!isFixed[k])  b[k] = -grad[k];
1052                else             b[k] = 0.0;
1053               
1054                for(int j=k; j<l; j++){ // Lower triangle       
1055                    if(!isFixed[j] && !isFixed[k])
1056                        LD.setElement(j, k, L.getElement(j,k)*D[k]);
1057                }               
1058            }           
1059           
1060            // Solve (LD)*y = -g, where y=L'*direct
1061            double[] LDIR = solveTriangle(LD, b, true, isFixed);           
1062            LD = null;
1063           
1064            for(int m=0; m<LDIR.length; m++){
1065                if(Double.isNaN(LDIR[m]))
1066                    throw new Exception("L*direct["+m+"] is NaN!"
1067                                        +"|-g="+b[m]+"|"+isFixed[m]
1068                                        +"|diag="+D[m]);
1069            }
1070           
1071            // Solve L'*direct = y
1072            direct = solveTriangle(L, LDIR, false, isFixed);
1073            for(int m=0; m<direct.length; m++){
1074                if(Double.isNaN(direct[m]))
1075                    throw new Exception("direct is NaN!");
1076            }
1077           
1078            //System.gc();
1079        }
1080       
1081        if(m_Debug)
1082            System.err.println("Cannot find minimum"+
1083                               " -- too many interations!");
1084        m_X = x;
1085        return null;
1086    }
1087   
1088    /**
1089     * Solve the linear equation of TX=B where T is a triangle matrix
1090     * It can be solved using back/forward substitution, with O(N^2)
1091     * complexity
1092     * @param t the matrix T
1093     * @param b the vector B
1094     * @param isLower whether T is a lower or higher triangle matrix
1095     * @param isZero which row(s) of T are not used when solving the equation.
1096     *               If it's null or all 'false', then every row is used.
1097     * @return the solution of X
1098     */     
1099    public static double[] solveTriangle(Matrix t, double[] b, 
1100                                         boolean isLower, boolean[] isZero){
1101        int n = b.length; 
1102        double[] result = new double[n];
1103        if(isZero == null)
1104            isZero = new boolean[n];
1105       
1106        if(isLower){ // lower triangle, forward-substitution
1107            int j = 0;
1108            while((j<n)&&isZero[j]){result[j]=0.0; j++;} // go to the first row
1109           
1110            if(j<n){
1111                result[j] = b[j]/t.getElement(j,j);
1112               
1113                for(; j<n; j++){
1114                    if(!isZero[j]){
1115                        double numerator=b[j];
1116                        for(int k=0; k<j; k++)
1117                            numerator -= t.getElement(j,k)*result[k];
1118                        result[j] = numerator/t.getElement(j,j);
1119                    }
1120                    else 
1121                        result[j] = 0.0;
1122                }
1123            }
1124        }
1125        else{ // Upper triangle, back-substitution
1126            int j=n-1;
1127            while((j>=0)&&isZero[j]){result[j]=0.0; j--;} // go to the last row
1128           
1129            if(j>=0){
1130                result[j] = b[j]/t.getElement(j,j);
1131               
1132                for(; j>=0; j--){
1133                    if(!isZero[j]){
1134                        double numerator=b[j];
1135                        for(int k=j+1; k<n; k++)
1136                            numerator -= t.getElement(k,j)*result[k];
1137                        result[j] = numerator/t.getElement(j,j);
1138                    }
1139                    else 
1140                        result[j] = 0.0;
1141                }
1142            }
1143        }
1144       
1145        return result;
1146    }
1147
1148    /**
1149     * One rank update of the Cholesky factorization of B matrix in BFGS updates,
1150     * i.e. B = LDL', and B_{new} = LDL' + coeff*(vv') where L is a unit lower triangle
1151     * matrix and D is a diagonal matrix, and v is a vector.<br/>
1152     * When coeff > 0, we use C1 algorithm, and otherwise we use C2 algorithm described
1153     * in ``Methods for Modifying Matrix Factorizations''
1154     *
1155     * @param L the unit triangle matrix L
1156     * @param D the diagonal matrix D
1157     * @param v the update vector v
1158     * @param coeff the coeffcient of update
1159     * @param isFixed which variables are not to be updated
1160     */   
1161    protected void updateCholeskyFactor(Matrix L, double[] D, 
1162                                        double[] v, double coeff,
1163                                        boolean[] isFixed)
1164        throws Exception{
1165        double t, p, b;
1166        int n = v.length;
1167        double[] vp =  new double[n];   
1168        for (int i=0; i<v.length; i++)
1169            if(!isFixed[i])
1170                vp[i]=v[i];
1171            else
1172                vp[i]=0.0;
1173       
1174        if(coeff>0.0){
1175            t = coeff;     
1176            for(int j=0; j<n; j++){             
1177                if(isFixed[j]) continue;               
1178               
1179                p = vp[j];
1180                double d=D[j], dbarj=d+t*p*p;
1181                D[j] = dbarj;
1182               
1183                b = p*t/dbarj;
1184                t *= d/dbarj;
1185                for(int r=j+1; r<n; r++){
1186                    if(!isFixed[r]){
1187                        double l=L.getElement(r, j);
1188                        vp[r] -= p*l;
1189                        L.setElement(r, j, l+b*vp[r]);
1190                    }
1191                    else
1192                        L.setElement(r, j, 0.0);
1193                }
1194            }
1195        }
1196        else{
1197            double[] P = solveTriangle(L, v, true, isFixed);       
1198            t = 0.0;
1199            for(int i=0; i<n; i++)
1200                if(!isFixed[i])
1201                    t += P[i]*P[i]/D[i];               
1202           
1203            double sqrt=1.0+coeff*t;
1204            sqrt = (sqrt<0.0)? 0.0 : Math.sqrt(sqrt);
1205           
1206            double alpha=coeff, sigma=coeff/(1.0+sqrt), rho, theta;
1207           
1208            for(int j=0; j<n; j++){
1209                if(isFixed[j]) continue;
1210               
1211                double d=D[j];
1212                p = P[j]*P[j]/d;
1213                theta = 1.0+sigma*p;
1214                t -= p; 
1215                if(t<0.0) t=0.0; // Rounding error
1216
1217                double plus = sigma*sigma*p*t;
1218                if((j<n-1) && (plus <= m_Zero)) 
1219                    plus=m_Zero; // Avoid rounding error
1220                rho = theta*theta + plus;               
1221                D[j] = rho*d;
1222               
1223                if(Double.isNaN(D[j])){
1224                    throw new Exception("d["+j+"] NaN! P="+P[j]+",d="+d+
1225                                        ",t="+t+",p="+p+",sigma="+sigma+
1226                                        ",sclar="+coeff);
1227                }
1228               
1229                b=alpha*P[j]/(rho*d);
1230                alpha /= rho;
1231                rho = Math.sqrt(rho);
1232                double sigmaOld = sigma;
1233                sigma *= (1.0+rho)/(rho*(theta+rho));   
1234                if((j<n-1) && 
1235                   (Double.isNaN(sigma) || Double.isInfinite(sigma)))
1236                    throw new Exception("sigma NaN/Inf! rho="+rho+
1237                                       ",theta="+theta+",P["+j+"]="+
1238                                       P[j]+",p="+p+",d="+d+",t="+t+
1239                                       ",oldsigma="+sigmaOld);
1240               
1241                for(int r=j+1; r<n; r++){
1242                    if(!isFixed[r]){
1243                        double l=L.getElement(r, j);
1244                        vp[r] -= P[j]*l;
1245                        L.setElement(r, j, l+b*vp[r]);
1246                    }
1247                    else
1248                        L.setElement(r, j, 0.0);
1249                }
1250            }
1251        }       
1252    }
1253
1254  /**
1255   * Implements a simple dynamic array for ints.
1256   */
1257  private class DynamicIntArray
1258    implements RevisionHandler {
1259
1260    /** The int array. */
1261    private int[] m_Objects;
1262
1263    /** The current size; */
1264    private int m_Size = 0;
1265
1266    /** The capacity increment */
1267    private int m_CapacityIncrement = 1;
1268 
1269    /** The capacity multiplier. */
1270    private int m_CapacityMultiplier = 2;
1271
1272    /**
1273     * Constructs a vector with the given capacity.
1274     *
1275     * @param capacity the vector's initial capacity
1276     */
1277    public DynamicIntArray(int capacity) {
1278     
1279      m_Objects = new int[capacity];
1280    }
1281
1282    /**
1283     * Adds an element to this vector. Increases its
1284     * capacity if its not large enough.
1285     *
1286     * @param element the element to add
1287     */
1288    public final void addElement(int element) {
1289     
1290      if (m_Size == m_Objects.length) {
1291        int[] newObjects;
1292        newObjects = new int[m_CapacityMultiplier *
1293                             (m_Objects.length +
1294                              m_CapacityIncrement)];
1295        System.arraycopy(m_Objects, 0, newObjects, 0, m_Size);
1296        m_Objects = newObjects;
1297      }
1298      m_Objects[m_Size] = element;
1299      m_Size++;
1300    }
1301
1302    /**
1303     * Produces a copy of this vector.
1304     *
1305     * @return the new vector
1306     */
1307    public final Object copy() {
1308     
1309     
1310      DynamicIntArray copy = new DynamicIntArray(m_Objects.length);
1311     
1312      copy.m_Size = m_Size;
1313      copy.m_CapacityIncrement = m_CapacityIncrement;
1314      copy.m_CapacityMultiplier = m_CapacityMultiplier;
1315      System.arraycopy(m_Objects, 0, copy.m_Objects, 0, m_Size);
1316      return copy;
1317    }
1318
1319    /**
1320     * Returns the element at the given position.
1321     *
1322     * @param index the element's index
1323     * @return the element with the given index
1324     */
1325    public final int elementAt(int index) {
1326     
1327      return m_Objects[index];
1328    }
1329   
1330    /**
1331     * Check whether the two integer vectors equal to each other
1332     * Two integer vectors are equal if all the elements are the
1333     * same, regardless of the order of the elements
1334     *
1335     * @param b another integer vector
1336     * @return whether they are equal
1337     */ 
1338    private boolean equal(DynamicIntArray b){
1339      if((b==null) || (size()!=b.size()))
1340          return false;
1341     
1342      int size=size();
1343     
1344      // Only values matter, order does not matter
1345      int[] sorta=Utils.sort(m_Objects), sortb=Utils.sort(b.m_Objects);
1346      for(int j=0; j<size;j++)
1347        if(m_Objects[sorta[j]] != b.m_Objects[sortb[j]])
1348          return false;
1349     
1350      return true;
1351    }
1352
1353    /**
1354     * Deletes an element from this vector.
1355     *
1356     * @param index the index of the element to be deleted
1357     */
1358    public final void removeElementAt(int index) {
1359     
1360      System.arraycopy(m_Objects, index + 1, m_Objects, index, 
1361                       m_Size - index - 1);
1362      m_Size--;
1363    }
1364
1365    /**
1366     * Removes all components from this vector and sets its
1367     * size to zero.
1368     */
1369    public final void removeAllElements() {
1370     
1371      m_Objects = new int[m_Objects.length];
1372      m_Size = 0;
1373    }
1374
1375    /**
1376     * Returns the vector's current size.
1377     *
1378     * @return the vector's current size
1379     */
1380    public final int size() {
1381     
1382      return m_Size;
1383    }
1384   
1385    /**
1386     * Returns the revision string.
1387     *
1388     * @return          the revision
1389     */
1390    public String getRevision() {
1391      return RevisionUtils.extract("$Revision: 5953 $");
1392    }
1393  }
1394}
Note: See TracBrowser for help on using the repository browser.