View Javadoc
1   /*
2    * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.numerical;
17  
18  /**
19   * Class to estimate the gradient of a multidimensional function.
20   * This class evaluates a function at very close locations of a given input
21   * point in order to determine the gradient at such point.
22   */
23  public class GradientEstimator {
24  
25      /**
26       * Constant considered as machine precision.
27       */
28      public static final double EPS = 1e-8;
29  
30      /**
31       * Listener to evaluate a multidimensional function.
32       */
33      protected final MultiDimensionFunctionEvaluatorListener listener;
34  
35      /**
36       * Internal array to hold input parameter's values.
37       */
38      private double[] xh;
39  
40      /**
41       * Constructor.
42       *
43       * @param listener Listener to evaluate a multidimensional function.
44       */
45      public GradientEstimator(final MultiDimensionFunctionEvaluatorListener listener) {
46          this.listener = listener;
47      }
48  
49      /**
50       * Returns the gradient of a multidimensional function at provided point.
51       *
52       * @param point Input point.
53       * @return Gradient.
54       * @throws EvaluationException Raised if function cannot be evaluated.
55       */
56      public double[] gradient(final double[] point) throws EvaluationException {
57          final var result = new double[point.length];
58          gradient(point, result);
59          return result;
60      }
61  
62      /**
63       * Sets estimated gradient in provided result array of a multidimensional
64       * function at provided point.
65       * This method is preferred respect to gradient(double[]) because result
66       * array can be reused and hence is more memory efficient.
67       *
68       * @param point  Input point.
69       * @param result Output parameter containing estimated array. This parameter
70       *               must be an array of length equal to point.
71       * @throws EvaluationException      Raised if function cannot be evaluated.
72       * @throws IllegalArgumentException Raised if length of result and point are
73       *                                  not equal.
74       */
75      public void gradient(final double[] point, final double[] result) throws EvaluationException {
76          final var length = point.length;
77          if (result.length != length) {
78              throw new IllegalArgumentException();
79          }
80  
81          if (xh == null || xh.length != length) {
82              xh = new double[length];
83          }
84          System.arraycopy(point, 0, xh, 0, length);
85  
86          double temp;
87          double h;
88          double fh;
89          final var fold = listener.evaluate(point);
90          for (var j = 0; j < length; j++) {
91              temp = point[j];
92              h = EPS * Math.abs(temp);
93              if (h == 0.0) {
94                  h = EPS;
95              }
96              xh[j] = temp + h;
97              h = xh[j] - temp;
98              fh = listener.evaluate(xh);
99              xh[j] = temp;
100             result[j] = (fh - fold) / h;
101         }
102     }
103 }