npstat is hosted by Hepforge, IPPP Durham
NPStat  5.10.0
gradientDescentForNeymanOSDE1D.hh
Go to the documentation of this file.
1 #ifndef NPSTAT_GRADIENTDESCENTFORNEYMANOSDE1D_HH_
2 #define NPSTAT_GRADIENTDESCENTFORNEYMANOSDE1D_HH_
3 
4 /*!
5 // \file gradientDescentForNeymanOSDE1D.hh
6 //
7 // \brief Gradient descent method for the Neyman OSDE. Not very well
8 // thought out, so it is very slow.
9 //
10 // Author: I. Volobouev
11 //
12 // March 2023
13 */
14 
15 #include <cmath>
16 #include <cassert>
17 #include <utility>
18 #include <algorithm>
19 
20 #include "npstat/nm/maxAbsValue.hh"
23 
26 
27 namespace npstat {
28  // The class "ConvergenceCalculator" must have a method
29  // "bool converged(...) const" with the same signature as the
30  // "converged" method of class "GradientNeymanOSDE1DConvergenceCalc".
31  template<class ConvergenceCalculator>
32  inline NeymanOSDE1DResult gradientDescentForNeymanOSDE1D(
33  const NeymanOSDE1D& nosde,
34  const double* initialCoeffs, const unsigned nCoeffs,
35  const double* shrinkages, const unsigned nShrinkages,
36  const ConvergenceCalculator& conv, const unsigned maxIterations)
37  {
38  // Some hardwired parameters
39  const double armijoC = 0.5;
40  const double armijoTau = 0.5;
41  const unsigned maxLineIterations = 100;
42 
43  std::vector<double> coeffs(initialCoeffs, initialCoeffs+nCoeffs);
44  std::vector<double> lineSearchCoeffs(nCoeffs);
45  std::vector<double> polyStats(nShrinkages);
46  std::vector<double> prevGradient(nCoeffs);
47  std::vector<double> currentGradient(nCoeffs);
48  std::vector<double> hessianEigenvalues(nCoeffs);
49  std::vector<double> step(nCoeffs);
50  std::vector<double> unitStep(nCoeffs);
51  std::vector<double> scales(nCoeffs);
52  Matrix<double> hessian(nCoeffs, nCoeffs);
53  SimpleScalarProduct<double> sp;
54 
55  double prevChisq = nosde.chiSquare(&coeffs[0], nCoeffs,
56  shrinkages, nShrinkages,
57  &polyStats[0], &prevGradient[0],
58  &hessian);
59  bool converged = false;
60  unsigned iter = 0;
61  for (; iter<maxIterations && !converged; ++iter)
62  {
63  // Try to get some idea about possible step sizes
64  scalesFromHessian(hessian, 1.0, &scales[0], nCoeffs);
65 
66  // Figure out the "unit step" in the direction
67  // opposing the gradient
68  const double gradientNorm = std::sqrt(sp(&prevGradient[0], &prevGradient[0], nCoeffs));
69  assert(gradientNorm > 0.0);
70  for (unsigned i=0; i<nCoeffs; ++i)
71  unitStep[i] = -prevGradient[i]*scales[i]/gradientNorm;
72  const double unitStepLen = std::sqrt(sp(&unitStep[0], &unitStep[0], nCoeffs));
73  assert(unitStepLen > 0.0);
74  for (unsigned i=0; i<nCoeffs; ++i)
75  unitStep[i] = -prevGradient[i]/gradientNorm;
76 
77  // Now perform backtracking line search
78  // const double m = sp(&unitStep[0], &prevGradient[0], nCoeffs);
79  // assert(m < 0.0);
80  const double m = -gradientNorm;
81  double alpha = unitStepLen/armijoTau;
82  bool lineIterConverged = false;
83  unsigned lit = 0;
84  double lineChisq = 0.0;
85  for (; lit<maxLineIterations && !lineIterConverged; ++lit)
86  {
87  alpha *= armijoTau;
88  for (unsigned i=0; i<nCoeffs; ++i)
89  {
90  step[i] = unitStep[i]*alpha;
91  lineSearchCoeffs[i] = coeffs[i] + step[i];
92  }
93  lineChisq = nosde.chiSquare(
94  &lineSearchCoeffs[0], nCoeffs, shrinkages, nShrinkages,
95  &polyStats[0], &currentGradient[0], nullptr);
96  lineIterConverged = lineChisq <= prevChisq + alpha*armijoC*m;
97  }
98  assert(lineIterConverged);
99  // std::cout << "Made " << lit << " line iterations" << std::endl;
100 
101  // Recalculate the Hessian
102  for (unsigned i=0; i<nCoeffs; ++i)
103  coeffs[i] = lineSearchCoeffs[i];
104  const double currentChisq = nosde.chiSquare(&coeffs[0], nCoeffs,
105  shrinkages, nShrinkages,
106  &polyStats[0], &currentGradient[0],
107  &hessian);
108  converged = conv.converged(iter, prevChisq, prevGradient, step,
109  currentChisq, currentGradient, hessian);
110 
111  prevChisq = currentChisq;
112  std::swap(prevGradient, currentGradient);
113  }
114 
115  return NeymanOSDE1DResult(coeffs, polyStats, prevChisq,
116  maxAbsValue(&prevGradient[0], prevGradient.size()),
117  NOSDE_GRADIENT_DESCENT, iter, converged, false);
118  }
119 }
120 
121 #endif // NPSTAT_GRADIENTDESCENTFORNEYMANOSDE1D_HH_
Class for representing results of the Neyman OSDE optimization algorithms.
OSDE based on minimizing the expected ISE of the empirical comparison density.
A simple code to calculate scalar products with unit weight function.
Maximum absolute value in an array.
Definition: AbsArrayProjector.hh:14
void scalesFromHessian(const Matrix< double > &hessian, double negativeScaleLimit, double *scales, unsigned nScales)
Make a guess about local function scales from the function Hessian.