001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.optimization.general;
019    
020    import java.awt.geom.Point2D;
021    import java.io.Serializable;
022    import java.util.ArrayList;
023    
024    import junit.framework.Test;
025    import junit.framework.TestCase;
026    import junit.framework.TestSuite;
027    
028    import org.apache.commons.math.FunctionEvaluationException;
029    import org.apache.commons.math.analysis.DifferentiableMultivariateRealFunction;
030    import org.apache.commons.math.analysis.MultivariateRealFunction;
031    import org.apache.commons.math.analysis.MultivariateVectorialFunction;
032    import org.apache.commons.math.analysis.solvers.BrentSolver;
033    import org.apache.commons.math.linear.BlockRealMatrix;
034    import org.apache.commons.math.linear.RealMatrix;
035    import org.apache.commons.math.optimization.GoalType;
036    import org.apache.commons.math.optimization.OptimizationException;
037    import org.apache.commons.math.optimization.RealPointValuePair;
038    import org.apache.commons.math.optimization.SimpleScalarValueChecker;
039    
040    /**
041     * <p>Some of the unit tests are re-implementations of the MINPACK <a
042     * href="http://www.netlib.org/minpack/ex/file17">file17</a> and <a
043     * href="http://www.netlib.org/minpack/ex/file22">file22</a> test files. 
044     * The redistribution policy for MINPACK is available <a
045     * href="http://www.netlib.org/minpack/disclaimer">here</a>, for
046     * convenience, it is reproduced below.</p>
047    
048     * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
049     * <tr><td>
050     *    Minpack Copyright Notice (1999) University of Chicago.
051     *    All rights reserved
052     * </td></tr>
053     * <tr><td>
054     * Redistribution and use in source and binary forms, with or without
055     * modification, are permitted provided that the following conditions
056     * are met:
057     * <ol>
058     *  <li>Redistributions of source code must retain the above copyright
059     *      notice, this list of conditions and the following disclaimer.</li>
060     * <li>Redistributions in binary form must reproduce the above
061     *     copyright notice, this list of conditions and the following
062     *     disclaimer in the documentation and/or other materials provided
063     *     with the distribution.</li>
064     * <li>The end-user documentation included with the redistribution, if any,
065     *     must include the following acknowledgment:
066     *     <code>This product includes software developed by the University of
067     *           Chicago, as Operator of Argonne National Laboratory.</code>
068     *     Alternately, this acknowledgment may appear in the software itself,
069     *     if and wherever such third-party acknowledgments normally appear.</li>
070     * <li><strong>WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
071     *     WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
072     *     UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
073     *     THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
074     *     IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
075     *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
076     *     OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
077     *     OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
078     *     USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
079     *     THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
080     *     DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
081     *     UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
082     *     BE CORRECTED.</strong></li>
083     * <li><strong>LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
084     *     HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
085     *     ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
086     *     INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
087     *     ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
088     *     PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
089     *     SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
090     *     (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
091     *     EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
092     *     POSSIBILITY OF SUCH LOSS OR DAMAGES.</strong></li>
093     * <ol></td></tr>
094     * </table>
095    
096     * @author Argonne National Laboratory. MINPACK project. March 1980 (original fortran minpack tests)
097     * @author Burton S. Garbow (original fortran minpack tests)
098     * @author Kenneth E. Hillstrom (original fortran minpack tests)
099     * @author Jorge J. More (original fortran minpack tests)
100     * @author Luc Maisonobe (non-minpack tests and minpack tests Java translation)
101     */
102    public class NonLinearConjugateGradientOptimizerTest
103    extends TestCase {
104    
105        public NonLinearConjugateGradientOptimizerTest(String name) {
106            super(name);
107        }
108    
109        public void testTrivial() throws FunctionEvaluationException, OptimizationException {
110            LinearProblem problem =
111                new LinearProblem(new double[][] { { 2 } }, new double[] { 3 });
112            NonLinearConjugateGradientOptimizer optimizer =
113                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
114            optimizer.setMaxIterations(100);
115            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
116            RealPointValuePair optimum =
117                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0 });
118            assertEquals(1.5, optimum.getPoint()[0], 1.0e-10);
119            assertEquals(0.0, optimum.getValue(), 1.0e-10);
120        }
121    
122        public void testColumnsPermutation() throws FunctionEvaluationException, OptimizationException {
123    
124            LinearProblem problem =
125                new LinearProblem(new double[][] { { 1.0, -1.0 }, { 0.0, 2.0 }, { 1.0, -2.0 } },
126                                  new double[] { 4.0, 6.0, 1.0 });
127    
128            NonLinearConjugateGradientOptimizer optimizer =
129                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
130            optimizer.setMaxIterations(100);
131            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
132            RealPointValuePair optimum =
133                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0 });
134            assertEquals(7.0, optimum.getPoint()[0], 1.0e-10);
135            assertEquals(3.0, optimum.getPoint()[1], 1.0e-10);
136            assertEquals(0.0, optimum.getValue(), 1.0e-10);
137    
138        }
139    
140        public void testNoDependency() throws FunctionEvaluationException, OptimizationException {
141            LinearProblem problem = new LinearProblem(new double[][] {
142                    { 2, 0, 0, 0, 0, 0 },
143                    { 0, 2, 0, 0, 0, 0 },
144                    { 0, 0, 2, 0, 0, 0 },
145                    { 0, 0, 0, 2, 0, 0 },
146                    { 0, 0, 0, 0, 2, 0 },
147                    { 0, 0, 0, 0, 0, 2 }
148            }, new double[] { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 });
149            NonLinearConjugateGradientOptimizer optimizer =
150                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
151            optimizer.setMaxIterations(100);
152            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
153            RealPointValuePair optimum =
154                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0, 0, 0, 0 });
155            for (int i = 0; i < problem.target.length; ++i) {
156                assertEquals(0.55 * i, optimum.getPoint()[i], 1.0e-10);
157            }
158        }
159    
160        public void testOneSet() throws FunctionEvaluationException, OptimizationException {
161    
162            LinearProblem problem = new LinearProblem(new double[][] {
163                    {  1,  0, 0 },
164                    { -1,  1, 0 },
165                    {  0, -1, 1 }
166            }, new double[] { 1, 1, 1});
167            NonLinearConjugateGradientOptimizer optimizer =
168                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
169            optimizer.setMaxIterations(100);
170            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
171            RealPointValuePair optimum =
172                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0 });
173            assertEquals(1.0, optimum.getPoint()[0], 1.0e-10);
174            assertEquals(2.0, optimum.getPoint()[1], 1.0e-10);
175            assertEquals(3.0, optimum.getPoint()[2], 1.0e-10);
176    
177        }
178    
179        public void testTwoSets() throws FunctionEvaluationException, OptimizationException {
180            final double epsilon = 1.0e-7;
181            LinearProblem problem = new LinearProblem(new double[][] {
182                    {  2,  1,   0,  4,       0, 0 },
183                    { -4, -2,   3, -7,       0, 0 },
184                    {  4,  1,  -2,  8,       0, 0 },
185                    {  0, -3, -12, -1,       0, 0 },
186                    {  0,  0,   0,  0, epsilon, 1 },
187                    {  0,  0,   0,  0,       1, 1 }
188            }, new double[] { 2, -9, 2, 2, 1 + epsilon * epsilon, 2});
189    
190            NonLinearConjugateGradientOptimizer optimizer =
191                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
192            optimizer.setMaxIterations(100);
193            optimizer.setPreconditioner(new Preconditioner() {
194                public double[] precondition(double[] point, double[] r) {
195                    double[] d = r.clone();
196                    d[0] /=  72.0;
197                    d[1] /=  30.0;
198                    d[2] /= 314.0;
199                    d[3] /= 260.0;
200                    d[4] /= 2 * (1 + epsilon * epsilon);
201                    d[5] /= 4.0;
202                    return d;
203                }
204            });
205            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-13, 1.0e-13));
206    
207            RealPointValuePair optimum =
208                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0, 0, 0, 0 });
209            assertEquals( 3.0, optimum.getPoint()[0], 1.0e-10);
210            assertEquals( 4.0, optimum.getPoint()[1], 1.0e-10);
211            assertEquals(-1.0, optimum.getPoint()[2], 1.0e-10);
212            assertEquals(-2.0, optimum.getPoint()[3], 1.0e-10);
213            assertEquals( 1.0 + epsilon, optimum.getPoint()[4], 1.0e-10);
214            assertEquals( 1.0 - epsilon, optimum.getPoint()[5], 1.0e-10);
215    
216        }
217    
218        public void testNonInversible() throws FunctionEvaluationException, OptimizationException {
219    
220            LinearProblem problem = new LinearProblem(new double[][] {
221                    {  1, 2, -3 },
222                    {  2, 1,  3 },
223                    { -3, 0, -9 }
224            }, new double[] { 1, 1, 1 });
225            NonLinearConjugateGradientOptimizer optimizer =
226                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
227            optimizer.setMaxIterations(100);
228            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
229            RealPointValuePair optimum =
230                    optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 0, 0, 0 });
231            assertTrue(optimum.getValue() > 0.5);
232        }
233    
234        public void testIllConditioned() throws FunctionEvaluationException, OptimizationException {
235            LinearProblem problem1 = new LinearProblem(new double[][] {
236                    { 10.0, 7.0,  8.0,  7.0 },
237                    {  7.0, 5.0,  6.0,  5.0 },
238                    {  8.0, 6.0, 10.0,  9.0 },
239                    {  7.0, 5.0,  9.0, 10.0 }
240            }, new double[] { 32, 23, 33, 31 });
241            NonLinearConjugateGradientOptimizer optimizer =
242                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
243            optimizer.setMaxIterations(100);
244            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-13, 1.0e-13));
245            BrentSolver solver = new BrentSolver();
246            solver.setAbsoluteAccuracy(1.0e-15);
247            solver.setRelativeAccuracy(1.0e-15);
248            optimizer.setLineSearchSolver(solver);
249            RealPointValuePair optimum1 =
250                optimizer.optimize(problem1, GoalType.MINIMIZE, new double[] { 0, 1, 2, 3 });
251            assertEquals(1.0, optimum1.getPoint()[0], 1.0e-5);
252            assertEquals(1.0, optimum1.getPoint()[1], 1.0e-5);
253            assertEquals(1.0, optimum1.getPoint()[2], 1.0e-5);
254            assertEquals(1.0, optimum1.getPoint()[3], 1.0e-5);
255    
256            LinearProblem problem2 = new LinearProblem(new double[][] {
257                    { 10.00, 7.00, 8.10, 7.20 },
258                    {  7.08, 5.04, 6.00, 5.00 },
259                    {  8.00, 5.98, 9.89, 9.00 },
260                    {  6.99, 4.99, 9.00, 9.98 }
261            }, new double[] { 32, 23, 33, 31 });
262            RealPointValuePair optimum2 =
263                optimizer.optimize(problem2, GoalType.MINIMIZE, new double[] { 0, 1, 2, 3 });
264            assertEquals(-81.0, optimum2.getPoint()[0], 1.0e-1);
265            assertEquals(137.0, optimum2.getPoint()[1], 1.0e-1);
266            assertEquals(-34.0, optimum2.getPoint()[2], 1.0e-1);
267            assertEquals( 22.0, optimum2.getPoint()[3], 1.0e-1);
268    
269        }
270    
271        public void testMoreEstimatedParametersSimple()
272            throws FunctionEvaluationException, OptimizationException {
273    
274            LinearProblem problem = new LinearProblem(new double[][] {
275                    { 3.0, 2.0,  0.0, 0.0 },
276                    { 0.0, 1.0, -1.0, 1.0 },
277                    { 2.0, 0.0,  1.0, 0.0 }
278            }, new double[] { 7.0, 3.0, 5.0 });
279    
280            NonLinearConjugateGradientOptimizer optimizer =
281                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
282            optimizer.setMaxIterations(100);
283            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
284            RealPointValuePair optimum =
285                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 7, 6, 5, 4 });
286            assertEquals(0, optimum.getValue(), 1.0e-10);
287    
288        }
289    
290        public void testMoreEstimatedParametersUnsorted()
291            throws FunctionEvaluationException, OptimizationException {
292            LinearProblem problem = new LinearProblem(new double[][] {
293                     { 1.0, 1.0,  0.0,  0.0, 0.0,  0.0 },
294                     { 0.0, 0.0,  1.0,  1.0, 1.0,  0.0 },
295                     { 0.0, 0.0,  0.0,  0.0, 1.0, -1.0 },
296                     { 0.0, 0.0, -1.0,  1.0, 0.0,  1.0 },
297                     { 0.0, 0.0,  0.0, -1.0, 1.0,  0.0 }
298            }, new double[] { 3.0, 12.0, -1.0, 7.0, 1.0 });
299            NonLinearConjugateGradientOptimizer optimizer =
300                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
301            optimizer.setMaxIterations(100);
302            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
303            RealPointValuePair optimum =
304                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 2, 2, 2, 2, 2, 2 });
305            assertEquals(0, optimum.getValue(), 1.0e-10);
306        }
307    
308        public void testRedundantEquations() throws FunctionEvaluationException, OptimizationException {
309            LinearProblem problem = new LinearProblem(new double[][] {
310                    { 1.0,  1.0 },
311                    { 1.0, -1.0 },
312                    { 1.0,  3.0 }
313            }, new double[] { 3.0, 1.0, 5.0 });
314    
315            NonLinearConjugateGradientOptimizer optimizer =
316                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
317            optimizer.setMaxIterations(100);
318            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
319            RealPointValuePair optimum =
320                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 1, 1 });
321            assertEquals(2.0, optimum.getPoint()[0], 1.0e-8);
322            assertEquals(1.0, optimum.getPoint()[1], 1.0e-8);
323    
324        }
325    
326        public void testInconsistentEquations() throws FunctionEvaluationException, OptimizationException {
327            LinearProblem problem = new LinearProblem(new double[][] {
328                    { 1.0,  1.0 },
329                    { 1.0, -1.0 },
330                    { 1.0,  3.0 }
331            }, new double[] { 3.0, 1.0, 4.0 });
332    
333            NonLinearConjugateGradientOptimizer optimizer =
334                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
335            optimizer.setMaxIterations(100);
336            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-6, 1.0e-6));
337            RealPointValuePair optimum =
338                optimizer.optimize(problem, GoalType.MINIMIZE, new double[] { 1, 1 });
339            assertTrue(optimum.getValue() > 0.1);
340    
341        }
342    
343        public void testCircleFitting() throws FunctionEvaluationException, OptimizationException {
344            Circle circle = new Circle();
345            circle.addPoint( 30.0,  68.0);
346            circle.addPoint( 50.0,  -6.0);
347            circle.addPoint(110.0, -20.0);
348            circle.addPoint( 35.0,  15.0);
349            circle.addPoint( 45.0,  97.0);
350            NonLinearConjugateGradientOptimizer optimizer =
351                new NonLinearConjugateGradientOptimizer(ConjugateGradientFormula.POLAK_RIBIERE);
352            optimizer.setMaxIterations(100);
353            optimizer.setConvergenceChecker(new SimpleScalarValueChecker(1.0e-30, 1.0e-30));
354            BrentSolver solver = new BrentSolver();
355            solver.setAbsoluteAccuracy(1.0e-13);
356            solver.setRelativeAccuracy(1.0e-15);
357            optimizer.setLineSearchSolver(solver);
358            RealPointValuePair optimum =
359                optimizer.optimize(circle, GoalType.MINIMIZE, new double[] { 98.680, 47.345 });
360            Point2D.Double center = new Point2D.Double(optimum.getPointRef()[0], optimum.getPointRef()[1]);
361            assertEquals(69.960161753, circle.getRadius(center), 1.0e-8);
362            assertEquals(96.075902096, center.x, 1.0e-8);
363            assertEquals(48.135167894, center.y, 1.0e-8);
364        }
365    
366        private static class LinearProblem implements DifferentiableMultivariateRealFunction, Serializable {
367    
368            private static final long serialVersionUID = 703247177355019415L;
369            final RealMatrix factors;
370            final double[] target;
371            public LinearProblem(double[][] factors, double[] target) {
372                this.factors = new BlockRealMatrix(factors);
373                this.target  = target;
374            }
375    
376            private double[] gradient(double[] point) {
377                double[] r = factors.operate(point);
378                for (int i = 0; i < r.length; ++i) {
379                    r[i] -= target[i];
380                }
381                double[] p = factors.transpose().operate(r);
382                for (int i = 0; i < p.length; ++i) {
383                    p[i] *= 2;
384                }
385                return p;
386            }
387    
388            public double value(double[] variables) throws FunctionEvaluationException {
389                double[] y = factors.operate(variables);
390                double sum = 0;
391                for (int i = 0; i < y.length; ++i) {
392                    double ri = y[i] - target[i];
393                    sum += ri * ri;
394                }
395                return sum;
396            }
397    
398            public MultivariateVectorialFunction gradient() {
399                return new MultivariateVectorialFunction() {
400                    private static final long serialVersionUID = 2621997811350805819L;
401                    public double[] value(double[] point) {
402                        return gradient(point);
403                    }
404                };
405            }
406    
407            public MultivariateRealFunction partialDerivative(final int k) {
408                return new MultivariateRealFunction() {
409                    private static final long serialVersionUID = -6186178619133562011L;
410                    public double value(double[] point) {
411                        return gradient(point)[k];
412                    }
413                };
414            }
415    
416        }
417    
418        private static class Circle implements DifferentiableMultivariateRealFunction, Serializable {
419    
420            private static final long serialVersionUID = -4711170319243817874L;
421    
422            private ArrayList<Point2D.Double> points;
423    
424            public Circle() {
425                points  = new ArrayList<Point2D.Double>();
426            }
427    
428            public void addPoint(double px, double py) {
429                points.add(new Point2D.Double(px, py));
430            }
431    
432            public double getRadius(Point2D.Double center) {
433                double r = 0;
434                for (Point2D.Double point : points) {
435                    r += point.distance(center);
436                }
437                return r / points.size();
438            }
439    
440            private double[] gradient(double[] point) {
441    
442                // optimal radius
443                Point2D.Double center = new Point2D.Double(point[0], point[1]);
444                double radius = getRadius(center);
445    
446                // gradient of the sum of squared residuals
447                double dJdX = 0;
448                double dJdY = 0;
449                for (Point2D.Double pk : points) {
450                    double dk = pk.distance(center);
451                    dJdX += (center.x - pk.x) * (dk - radius) / dk;
452                    dJdY += (center.y - pk.y) * (dk - radius) / dk;
453                }
454                dJdX *= 2;
455                dJdY *= 2;
456    
457                return new double[] { dJdX, dJdY };
458    
459            }
460    
461            public double value(double[] variables)
462                    throws IllegalArgumentException, FunctionEvaluationException {
463    
464                Point2D.Double center = new Point2D.Double(variables[0], variables[1]);
465                double radius = getRadius(center);
466    
467                double sum = 0;
468                for (Point2D.Double point : points) {
469                    double di = point.distance(center) - radius;
470                    sum += di * di;
471                }
472    
473                return sum;
474    
475            }
476    
477            public MultivariateVectorialFunction gradient() {
478                return new MultivariateVectorialFunction() {
479                    private static final long serialVersionUID = 3174909643301201710L;
480                    public double[] value(double[] point) {
481                        return gradient(point);
482                    }
483                };
484            }
485    
486            public MultivariateRealFunction partialDerivative(final int k) {
487                return new MultivariateRealFunction() {
488                    private static final long serialVersionUID = 3073956364104833888L;
489                    public double value(double[] point) {
490                        return gradient(point)[k];
491                    }
492                };
493            }
494    
495        }
496    
497        public static Test suite() {
498            return new TestSuite(NonLinearConjugateGradientOptimizerTest.class);
499        }
500    
501    }