npstat is hosted by Hepforge, IPPP Durham
NPStat  5.10.0
LOrPE1DFixedDegreeCVPicker.hh
Go to the documentation of this file.
1 #ifndef NPSTAT_LORPE1DFIXEDDEGREECVPICKER_HH_
2 #define NPSTAT_LORPE1DFIXEDDEGREECVPICKER_HH_
3 
4 /*!
5 // \file LOrPE1DFixedDegreeCVPicker.hh
6 //
7 // \brief Optimization of LOrPE bandwidth choice by cross-validation
8 // with a hybrid algorithm combining golden section search and
9 // a grid scan
10 //
11 // Author: I. Volobouev
12 //
13 // August 2022
14 */
15 
16 #include <cmath>
17 #include <cassert>
18 #include <climits>
19 #include <stdexcept>
20 #include <map>
21 
23 #include "npstat/nm/MathUtils.hh"
26 #include "npstat/nm/DualAxis.hh"
28 
30 
31 namespace npstat {
32  namespace Private {
33  template<class Lorpe>
34  class LOrPE1DFixedDegreeCVPickerHelper : public Functor1<double,double>
35  {
36  public:
37  inline LOrPE1DFixedDegreeCVPickerHelper(Lorpe& lorpe, const double fixedDegree)
38  : lorpe_(lorpe),
39  fixedDegree_(fixedDegree)
40  {
41  assert(fixedDegree_ >= 0.0);
42  }
43 
44  inline virtual ~LOrPE1DFixedDegreeCVPickerHelper() {}
45 
46  inline virtual double operator()(const double& bwFactor) const
47  {
48  const double cv = lorpe_(fixedDegree_, bwFactor);
49  const auto insertResult = cvValues_.insert(std::make_pair(bwFactor,cv));
50  assert(insertResult.second);
51  return -cv;
52  }
53 
54  inline std::pair<double,bool> getMemoized(const double bwFactor) const
55  {
56  const auto it = cvValues_.find(bwFactor);
57  if (it == cvValues_.end())
58  return std::make_pair(0.0, false);
59  else
60  return std::make_pair(it->second, true);
61  }
62 
63  private:
64  Lorpe& lorpe_;
65  double fixedDegree_;
66  mutable std::map<double,double> cvValues_;
67  };
68  }
69 
71  {
72  public:
73  inline LOrPE1DFixedDegreeCVPicker(const double degree,
74  const double minBwFactor,
75  const double maxBwFactor,
76  const unsigned nBwFactors,
77  const double firstBwFactorToTry,
78  const unsigned initialStepSizeInFactorsGridCells)
79  : bandwidthFactors_(EquidistantInLogSpace(minBwFactor, maxBwFactor, nBwFactors)),
80  fixedDegree_(degree),
81  initialStep_(initialStepSizeInFactorsGridCells)
82  {
83  if (fixedDegree_ < 0.0) throw std::invalid_argument(
84  "In npstat::LOrPE1DFixedDegreeCVPicker constructor: "
85  "degree argument must be non-negative");
86  if (minBwFactor >= maxBwFactor) throw std::invalid_argument(
87  "In npstat::LOrPE1DFixedDegreeCVPicker constructor: "
88  "minimum factor must be less than maximum");
89  i0_ = bandwidthFactors_.getInterval(firstBwFactorToTry).first;
90  assert(i0_ < nBwFactors);
91  if (!initialStep_)
92  initialStep_ = 1U;
93  }
94 
95  inline unsigned nBwFactors() const {return bandwidthFactors_.nCoords();}
96  inline double getBwFactor(const unsigned i) const {return bandwidthFactors_.coordinate(i);}
97  inline double searchStart() const {return bandwidthFactors_.coordinate(i0_);}
98 
99  template<class Lorpe>
100  inline LOrPE1DCVResult crossValidate(Lorpe& lorpe) const
101  {
103 
104  const unsigned nscan = bandwidthFactors_.nCoords();
105  const Helper helper(lorpe, fixedDegree_);
106  unsigned imin = UINT_MAX;
107  double fMinusOne, fmin, fPlusOne;
108 
109  const MinSearchStatus1D status = goldenSectionSearchOnAGrid(
110  helper, bandwidthFactors_, i0_, initialStep_,
111  &imin, &fMinusOne, &fmin, &fPlusOne);
112 
113  if (status == MIN_SEARCH_FAILED)
114  {
115  // This most likely means we have found a local maximum
116  // instead of a minimum. Switch to a simple scan instead
117  // to find the global minimum.
118  std::vector<double> buffer(2*nscan);
119  double* bwLogs = &buffer[0];
120  double* cvValues = bwLogs + nscan;
121 
122  for (unsigned i=0; i<nscan; ++i)
123  {
124  const double bwFactor = bandwidthFactors_.coordinate(i);
125  bwLogs[i] = std::log(bwFactor);
126  const std::pair<double,bool>& searched = helper.getMemoized(bwFactor);
127  if (searched.second)
128  cvValues[i] = searched.first;
129  else
130  cvValues[i] = lorpe(fixedDegree_, bwFactor);
131  }
132 
133  const ScanExtremum1D& scanMax = findScanMaximum1D(bwLogs, nscan,
134  cvValues, nscan);
135  return LOrPE1DCVResult(
136  fixedDegree_, std::exp(scanMax.location()), scanMax.value(),
137  true, scanMax.isOnTheBoundary());
138  }
139  else if (status == MIN_SEARCH_OK)
140  {
141  assert(imin);
142  assert(imin + 1U < nscan);
143 
144  const double bwMin = bandwidthFactors_.coordinate(imin-1U);
145  const double bwFactor = bandwidthFactors_.coordinate(imin);
146  const double bwMax = bandwidthFactors_.coordinate(imin+1U);
147 
148  // Build parabolic approximation in the log space
149  double extremumCoordinate, extremumValue;
150  const bool isMinimum = parabolicExtremum(
151  std::log(bwMin), fMinusOne,
152  std::log(bwFactor), fmin,
153  std::log(bwMax), fPlusOne,
154  &extremumCoordinate, &extremumValue);
155  assert(isMinimum);
156 
157  return LOrPE1DCVResult(
158  fixedDegree_, std::exp(extremumCoordinate), -extremumValue,
159  true, false);
160  }
161  else
162  {
163  // The minimum is on the grid boundary
164  assert(imin < nscan);
165  const double bwFactor = bandwidthFactors_.coordinate(imin);
166  const std::pair<double,bool>& searched = helper.getMemoized(bwFactor);
167  assert(searched.second);
168  return LOrPE1DCVResult(
169  fixedDegree_, bwFactor, searched.first, true, true);
170  }
171  }
172 
173  private:
174  DualAxis bandwidthFactors_;
175  double fixedDegree_;
176  unsigned i0_;
177  unsigned initialStep_;
178  };
179 }
180 
181 #endif // NPSTAT_LORPE1DFIXEDDEGREECVPICKER_HH_
Represent both equidistant and non-uniform coordinate sets for rectangular grids.
Equidistant sequences of points in either linear or log space.
An object representing the result of the 1-d LOrPE cross-validation procedure.
Various simple mathematical utilities which did not end up inside dedicated headers.
Finding extrema of scanned 1-d curves.
Interface definitions and concrete simple functors for a variety of functor-based calculations.
Definition: DualAxis.hh:25
Definition: EquidistantSequence.hh:55
Definition: LOrPE1DCVResult.hh:19
Definition: LOrPE1DFixedDegreeCVPicker.hh:71
Definition: LOrPE1DFixedDegreeCVPicker.hh:35
Definition: ScanExtremum1D.hh:16
Search for 1-d function minimum in log space using the golden section method.
Definition: AbsArrayProjector.hh:14
MinSearchStatus1D goldenSectionSearchOnAGrid(const Functor1< double, double > &f, const DualAxis &axis, unsigned i0, unsigned initialStep, unsigned *imin, double *fMinusOne, double *fmin, double *fPlusOne)
bool parabolicExtremum(double x0, double y0, double x1, double y1, double x2, double y2, double *extremumCoordinate, double *extremumValue)
Definition: SimpleFunctors.hh:58