Loading...
Searching...
No Matches
compass.h
Go to the documentation of this file.
1#ifndef _BBM_COMPASS_H_
2#define _BBM_COMPASS_H_
3
4#include <limits>
5
9
10/************************************************************************/
11/*! \file compass.h
12
13 \brief Compass search (i.e., pattern search) optimization. Gradient free
14 optimization algorithm that probes each cardindal direction.
15
16 Follows the algorithm on page 402 from: "Optimization by Direct Search: New
17 Perspectives on Some Classical and Modern Methods" [Kolda et al. 2003]:
18 https://doi.org/10.1137/S003614450242889
19
20*************************************************************************/
21
22namespace bbm {
23
24 /**********************************************************************/
25 /*! \brief Compass Search
26
27 \tparam LOSSFUNC = loss function
28 \tparam PARAM = parameters to optimize
29 \tparam BOX = box constraint type (default=PARAM)
30
31 Performs a pattern search by probing each cardinal direction in the
32 parameter space.
33
34 When the Value type is a packet, this compass search will perform
35 N parallel searches (where N == size of the packet).
36
37 Satisfies: concepts::optimization algorithm
38 ***********************************************************************/
39 template<typename LOSSFUNC, typename PARAM, typename BOX=PARAM> requires concepts::lossfunction<LOSSFUNC> && concepts::parameter<PARAM> && concepts::parameter<BOX>
40 struct compass
41 {
42 BBM_IMPORT_CONFIG( LOSSFUNC );
43
44 /********************************************************************/
45 /*! \brief Constructor: compass search
46
47 \param lossfunc = loss function to minimize
48 \param param = set of parameters to optimize
49 \param lower = lower bound of box constraint
50 \param upper = upper bound of box constraint
51 \param tolerance = threshold on step size to determine convergence (default=Epsilon)
52 \param stepSize = initial step size (default=1.0)
53 \param contraction = reduction factor for step size if no probe in the cardinal directions improves the loss (default=0.5)
54 \param expansion = expansion factor for step size if a good candinal direction was found (default=1.0)
55 *********************************************************************/
56 compass(LOSSFUNC& lossfunc, PARAM& param, const BOX& lower=BOX(), const BOX& upper=BOX(),
57 Scalar tolerance=Constants::Epsilon(), Scalar stepSize=1.0, Scalar contraction=0.5, Scalar expansion=1.0, Mask mask=true) :
58 _lossfunc(lossfunc),
59 _param(param), _lower(lower), _upper(upper),
60 _initialStep(stepSize),
61 _tolerance(tolerance),
62 _contraction(contraction), _expansion(expansion),
63 _mask(mask)
64 {
65 // Initialize cardindal directions
66 for(Scalar i=1; i <= std::size(param); ++i)
67 {
68 _directions.push_back(+i);
69 _directions.push_back(-i);
70 }
71
72 // reset the internal state
73 reset();
74 }
75
76 /********************************************************************/
77 /*! \brief probe each cardinal direction, and update the parameters to
78 the one with the lowest loss.
79
80 \returns loss after update
81 *********************************************************************/
82 Value step(void)
83 {
84 Value best = 0;
85 Value loss = _lossValue;
86
87 // lambda to apply a candinal direction update of magnitude _step in _param
88 auto probe = [&](auto cardinal, Mask mask=true)
89 {
90 mask &= (cardinal != 0); // exclude invalid cardinal directions;
91 auto index = bbm::cast<index_t<Value>>( bbm::abs(cardinal) - 1 );
92 Value value = lookup<Value>(_param, index, bbm::cast<index_mask_t<Value>>(mask)) + bbm::select(cardinal < 0, -_step, _step);
93 set(_param, index, value, mask);
94 };
95
96 // only update nonconverged
97 Mask optimize = _mask && !is_converged();
98
99 // Quick bailout
100 if(bbm::none(optimize)) return 0;
101
102 // Update loss internal state
103 _lossfunc.update();
104
105 // for each cardinal direction
106 for(const auto& cardinal : _directions)
107 {
108 // move parameter in cardinal direction
109 probe(cardinal, optimize);
110
111 // check parameter falls inside box constraints (if any)
112 Mask in_box = optimize;
113 if(_lower != BOX() || _upper != BOX())
114 {
115 multirange_for([&](const auto& param, const auto& lower, const auto& upper)
116 {
117 in_box &= (param >= lower) && (param <= upper);
118 }, _param, _lower, _upper) ;
119 }
120
121 // compute loss
122 auto err = _lossfunc(in_box);
123
124 // remember if better
125 best = bbm::select(in_box && (err < loss), cardinal, best);
126 loss = bbm::select(in_box && (err < loss), err, loss);
127
128 // restore _param
129 probe(-cardinal, optimize);
130 }
131
132 // if loss decreases; make permanent change, otherwise decrease step
133 optimize &= (loss < _lossValue);
134 probe(best, optimize);
137
138 // Done.
139 return _lossValue;
140 }
141
142 /********************************************************************/
143 /*! \brief reset the step size
144 ********************************************************************/
145 void reset(void)
146 {
147 // reset cardinal directions
149
150 // compute current loss
151 _lossfunc.update();
153 }
154
155 /********************************************************************/
156 /*! \brief is_converged when _step < _tolerance
157 ********************************************************************/
158 Mask is_converged(void) const
159 {
160 return (_step < _tolerance) || (_mask == false);
161 }
162
163 /////////////////////
164 // Class Attributes
165 /////////////////////
166 private:
167 // parameters, constraint, and loss function
168 LOSSFUNC& _lossfunc;
169 PARAM& _param;
171
172 // optimization state
173 Value _step;
175
176 // optimization config
177 std::vector<Scalar> _directions;
182 Mask _mask;
183 };
184
186
187} // end bbm namespace
188
189#endif /* _BBM_COMPASS_H_ */
LOSS implementation of a loss function.
Definition: loss.h:29
optimization_algorithm concept
Definition: optimization_algorithm.h:25
loss function contract
#define BBM_CHECK_CONCEPT(CONCEPTNAME, CLASSNAME,...)
Check a class for a concept with bbm::concepts::archetypes in the namespace.
Definition: macro.h:35
Definition: aggregatebsdf.h:29
constexpr auto select(MASK &&mask, const A &a, const A &b)
Definition: backbone.h:255
void multirange_for(FUNC &&func, Ts &&... containers)
ranged for loop over multiple containers at once
Definition: multirange_for.h:43
void set(C &&container, const Index &idx, Value &&value, const index_mask_t< Index > &mask=true)
Generalization of backbone::set to include tuples/named/reflection-supported objects.
Definition: backbone.h:352
decltype(auto) value(T &&t)
return the value of an attribute, or if not an attribute the object
Definition: attribute_value.h:20
Optimization algorithm interface contract.
Concepts related to BSDF model parameters.
Compass Search.
Definition: compass.h:41
Scalar _initialStep
Definition: compass.h:178
Scalar _expansion
Definition: compass.h:181
Value _step
Definition: compass.h:173
void reset(void)
reset the step size
Definition: compass.h:145
Scalar _tolerance
Definition: compass.h:179
Scalar _contraction
Definition: compass.h:180
Value _lossValue
Definition: compass.h:174
BOX _lower
Definition: compass.h:170
Mask _mask
Definition: compass.h:182
std::vector< Scalar > _directions
Definition: compass.h:177
BBM_IMPORT_CONFIG(LOSSFUNC)
PARAM & _param
Definition: compass.h:169
LOSSFUNC & _lossfunc
Definition: compass.h:168
Value step(void)
probe each cardinal direction, and update the parameters to the one with the lowest loss.
Definition: compass.h:82
BOX _upper
Definition: compass.h:170
compass(LOSSFUNC &lossfunc, PARAM &param, const BOX &lower=BOX(), const BOX &upper=BOX(), Scalar tolerance=Constants::Epsilon(), Scalar stepSize=1.0, Scalar contraction=0.5, Scalar expansion=1.0, Mask mask=true)
Constructor: compass search.
Definition: compass.h:56
Mask is_converged(void) const
is_converged when _step < _tolerance
Definition: compass.h:158