GCC Code Coverage Report


Directory: ./
File: numerics/lbfgs_optimizers.cpp
Date: 2026-09-27 03:24:14
Exec Total Coverage
Lines: 40 74 54.1%
Functions: 6 14 42.9%
Branches: 12 74 16.2%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifdef GEOGRAM_WITH_HLBFGS
41
42 #include <geogram/numerics/lbfgs_optimizers.h>
43 #include <geogram/basic/command_line.h>
44 #include <geogram/basic/argused.h>
45 #include <geogram/third_party/HLBFGS/HLBFGS.h>
46 #include <geogram/bibliography/bibliography.h>
47
48 #include <setjmp.h>
49 #include <iostream>
50
51 namespace GEO {
52
53 /**
54 * \brief Optimizer configuration
55 * \details Manages global variables and callbacks for the
56 * communication between the Optimizer class and the HLBFGS
57 * library.
58 * \internal
59 * The current implementation is actually a bottleneck: optimizer
60 * execution config is stored in static variables which prevents multiple
61 * optimizers to execute in parallel.
62 */
63 namespace OptimizerConfig {
64
65 static Optimizer::newiteration_callback newiteration_callback_ = nullptr;
66 static Optimizer::funcgrad_callback funcgrad_callback_ = nullptr;
67 static Optimizer::evalhessian_callback evalhessian_callback_ = nullptr;
68 static index_t N_ = 0;
69
70 /**
71 * \brief Initializes Optimizer configuration
72 * \details Sets the problem dimension and the various optimizer
73 * callbacks for the optimizer execution
74 * \param[in] N dimension of the problem
75 * \param[in] funcgrad_callback callback that evaluates
76 * the function to be minimized and its gradient
77 * \param[in] newiteration_callback callback that will be
78 * called at each iteration
79 * \param[in] evalhessian_callback callback that evaluates
80 * the Hessian of function to be minimized (second
81 * order derivatives)
82 */
83 12 static void init(
84 index_t N,
85 Optimizer::funcgrad_callback funcgrad_callback,
86 Optimizer::newiteration_callback newiteration_callback,
87 Optimizer::evalhessian_callback evalhessian_callback
88 ) {
89 12 N_ = N;
90 12 funcgrad_callback_ = funcgrad_callback;
91 12 newiteration_callback_ = newiteration_callback;
92 12 evalhessian_callback_ = evalhessian_callback;
93 12 }
94
95 /**
96 * \brief HLBFGS callback called at each iteration.
97 * \param[in] iter current iteration
98 * \param[in] call_iter total number of evaluations
99 * \param[in] x value of the parameters at current iteration
100 * \param[in] f value of the function
101 * \param[in] g gradient of the function
102 * \param[in] gnorm norm of the gradient
103 */
104 352 static void HLBFGS_newiteration_callback(
105 int iter, int call_iter, double* x, double* f, double* g,
106 double* gnorm
107 ) {
108 352 GEO::geo_argused(iter);
109 352 GEO::geo_argused(call_iter);
110 352 (* newiteration_callback_)(N_, x, * f, g, * gnorm);
111 352 }
112
113 /**
114 * \brief HLBFGS callback that evaluates the function and its
115 * gradient.
116 * \param[in] N dimension of the problem
117 * \param[in] x value of the parameters at current iteration
118 * \param[in] prev_x value of the parameters at previous iteration
119 * \param[out] f value of the function
120 * \param[out] g gradient of the function
121 */
122 383 static void HLBFGS_funcgrad_callback(
123 int N, double* x, double* prev_x, double* f, double* g
124 ) {
125 383 GEO::geo_argused(prev_x);
126 383 (* funcgrad_callback_)((index_t) N, x, * f, g);
127 383 }
128
129 /**
130 * \brief HLBFGS callback that evaluates the function, its gradient
131 * and Hessian.
132 * \param[in] N dimension of the problem
133 * \param[in] x value of the parameters at current iteration
134 * \param[in] prev_x value of the parameters at previous iteration
135 * \param[out] f value of the function
136 * \param[out] g gradient of the function
137 * \param[out] m_hessian Hessian of the function
138 */
139 ✗ static void HLBFGS_evalhessian_callback(
140 int N, double* x, double* prev_x, double* f, double* g,
141 HESSIAN_MATRIX& m_hessian
142 ) {
143 ✗ GEO::geo_argused(prev_x);
144 ✗ (* evalhessian_callback_)((index_t) N, x, * f, g, m_hessian);
145 ✗ }
146 }
147
148 /************************************************************************/
149
150 12 HLBFGSOptimizer::HLBFGSOptimizer() :
151 12 b_m1qn3_(false),
152 12 b_cg_(false) {
153
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 geo_cite("WEB:HLBFGS");
154 12 }
155
156 48 HLBFGSOptimizer::~HLBFGSOptimizer() {
157 48 }
158
159 12 void HLBFGSOptimizer::optimize(double* x) {
160
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 geo_assert(newiteration_callback_ != nullptr);
161
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 geo_assert(funcgrad_callback_ != nullptr);
162
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 geo_assert(n_ > 0);
163
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 geo_assert(x != nullptr);
164
165 12 OptimizerConfig::init(
166 n_,
167 funcgrad_callback_,
168 newiteration_callback_,
169 nullptr
170 );
171
172 double parameter[20];
173 int hlbfgs_info[20];
174
175 // initialize parameters and infos
176
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 INIT_HLBFGS(parameter, hlbfgs_info);
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 hlbfgs_info[3] = b_m1qn3_ ? 1 : 0; // determines whether we use m1qn3
178 12 hlbfgs_info[4] = (int) max_iter_; // max iterations
179 12 hlbfgs_info[5] =
180
3/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
12 GEO::CmdLine::get_arg_bool("debug") ? 1 : 0; // verbose
181
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 hlbfgs_info[10] = b_cg_ ? 1 : 0; // determines whether we use cg
182 12 parameter[5] = 0; // disabled
183 12 parameter[6] = epsg_;
184
185 12 HLBFGS(
186 12 (int) n_,
187
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 (int) m_,
188 x,
189 OptimizerConfig::HLBFGS_funcgrad_callback,
190 nullptr,
191 HLBFGS_UPDATE_Hessian,
192 OptimizerConfig::HLBFGS_newiteration_callback,
193 parameter,
194 hlbfgs_info
195 );
196 12 }
197
198 /************************************************************************/
199
200 ✗ HLBFGS_M1QN3Optimizer::HLBFGS_M1QN3Optimizer() {
201 ✗ set_m1qn3(true);
202 ✗ }
203
204 ✗ HLBFGS_M1QN3Optimizer::~HLBFGS_M1QN3Optimizer() {
205 ✗ }
206
207 /************************************************************************/
208
209 ✗ HLBFGS_CGOptimizer::HLBFGS_CGOptimizer() {
210 ✗ set_cg(true);
211 ✗ }
212
213 ✗ HLBFGS_CGOptimizer::~HLBFGS_CGOptimizer() {
214 ✗ }
215
216 /************************************************************************/
217
218 ✗ HLBFGS_HessOptimizer::HLBFGS_HessOptimizer() :
219 ✗ T_(0) {
220 ✗ }
221
222 ✗ HLBFGS_HessOptimizer::~HLBFGS_HessOptimizer() {
223 ✗ }
224
225 ✗ void HLBFGS_HessOptimizer::optimize(double* x) {
226 ✗ geo_assert(newiteration_callback_ != nullptr);
227 ✗ geo_assert(funcgrad_callback_ != nullptr);
228 ✗ geo_assert(evalhessian_callback_ != nullptr);
229 ✗ geo_assert(n_ > 0);
230 ✗ geo_assert(x != nullptr);
231
232 ✗ OptimizerConfig::init(
233 n_,
234 funcgrad_callback_,
235 newiteration_callback_,
236 evalhessian_callback_
237 );
238
239 double parameter[20];
240 int hlbfgs_info[20];
241
242 // initialize parameters and infos
243 ✗ INIT_HLBFGS(parameter, hlbfgs_info);
244 ✗ hlbfgs_info[4] = (int) max_iter_; // max iterations
245 ✗ hlbfgs_info[6] = (int) T_; // update interval of hessian
246 ✗ hlbfgs_info[7] = 1; // 0: without hessian, 1: with accurate hessian
247
248 ✗ HLBFGS(
249 ✗ (int) n_,
250 ✗ (int) m_,
251 x,
252 OptimizerConfig::HLBFGS_funcgrad_callback,
253 OptimizerConfig::HLBFGS_evalhessian_callback,
254 HLBFGS_UPDATE_Hessian,
255 OptimizerConfig::HLBFGS_newiteration_callback,
256 parameter,
257 hlbfgs_info
258 );
259 ✗ }
260 }
261
262 #endif
263