blob: ac83bc30f6259ad7b51e905e776c3079f214ed97 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001# Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -07002# Copyright 2023 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -08003# http://ceres-solver.org/
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 Google Inc. nor the names of its contributors may be
14# used to endorse or promote products derived from this software without
15# 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 OWNER 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# Author: keir@google.com (Keir Mierle)
30#
31# Generate bundle adjustment tests as separate binaries. Since the bundle
32# adjustment tests are fairly processing intensive, serializing them makes the
33# tests take forever to run. Splitting them into separate binaries makes it
34# easier to parallelize in continuous integration systems, and makes local
35# processing on multi-core workstations much faster.
36
37# Product of ORDERINGS, THREAD_CONFIGS, and SOLVER_CONFIGS is the full set of
38# tests to generate.
39ORDERINGS = ["kAutomaticOrdering", "kUserOrdering"]
40SINGLE_THREADED = "1"
41MULTI_THREADED = "4"
42THREAD_CONFIGS = [SINGLE_THREADED, MULTI_THREADED]
43
Austin Schuh3de38b02024-06-25 18:25:10 -070044DENSE_SOLVER_CONFIGS = [
45 # Linear solver Dense backend
46 ('DENSE_SCHUR', 'EIGEN'),
47 ('DENSE_SCHUR', 'LAPACK'),
48 ('DENSE_SCHUR', 'CUDA'),
49]
50
51SPARSE_SOLVER_CONFIGS = [
52 # Linear solver Sparse backend
53 ('SPARSE_NORMAL_CHOLESKY', 'SUITE_SPARSE'),
54 ('SPARSE_NORMAL_CHOLESKY', 'EIGEN_SPARSE'),
55 ('SPARSE_NORMAL_CHOLESKY', 'ACCELERATE_SPARSE'),
56 ('SPARSE_SCHUR', 'SUITE_SPARSE'),
57 ('SPARSE_SCHUR', 'EIGEN_SPARSE'),
58 ('SPARSE_SCHUR', 'ACCELERATE_SPARSE'),
59]
60
61ITERATIVE_SOLVER_CONFIGS = [
62 # Linear solver Sparse backend Preconditioner
63 ('ITERATIVE_SCHUR', 'NO_SPARSE', 'JACOBI'),
64 ('ITERATIVE_SCHUR', 'NO_SPARSE', 'SCHUR_JACOBI'),
65 ('ITERATIVE_SCHUR', 'NO_SPARSE', 'SCHUR_POWER_SERIES_EXPANSION'),
66 ('ITERATIVE_SCHUR', 'SUITE_SPARSE', 'CLUSTER_JACOBI'),
67 ('ITERATIVE_SCHUR', 'EIGEN_SPARSE', 'CLUSTER_JACOBI'),
68 ('ITERATIVE_SCHUR', 'ACCELERATE_SPARSE','CLUSTER_JACOBI'),
69 ('ITERATIVE_SCHUR', 'SUITE_SPARSE', 'CLUSTER_TRIDIAGONAL'),
70 ('ITERATIVE_SCHUR', 'EIGEN_SPARSE', 'CLUSTER_TRIDIAGONAL'),
71 ('ITERATIVE_SCHUR', 'ACCELERATE_SPARSE','CLUSTER_TRIDIAGONAL'),
Austin Schuh70cc9552019-01-21 19:46:48 -080072]
73
74FILENAME_SHORTENING_MAP = dict(
75 DENSE_SCHUR='denseschur',
76 ITERATIVE_SCHUR='iterschur',
77 SPARSE_NORMAL_CHOLESKY='sparsecholesky',
78 SPARSE_SCHUR='sparseschur',
Austin Schuh3de38b02024-06-25 18:25:10 -070079 EIGEN='eigen',
80 LAPACK='lapack',
81 CUDA='cuda',
Austin Schuh70cc9552019-01-21 19:46:48 -080082 NO_SPARSE='', # Omit sparse reference entirely for dense tests.
83 SUITE_SPARSE='suitesparse',
84 EIGEN_SPARSE='eigensparse',
Austin Schuh70cc9552019-01-21 19:46:48 -080085 ACCELERATE_SPARSE='acceleratesparse',
86 IDENTITY='identity',
87 JACOBI='jacobi',
88 SCHUR_JACOBI='schurjacobi',
89 CLUSTER_JACOBI='clustjacobi',
90 CLUSTER_TRIDIAGONAL='clusttri',
Austin Schuh3de38b02024-06-25 18:25:10 -070091 SCHUR_POWER_SERIES_EXPANSION='spse',
Austin Schuh70cc9552019-01-21 19:46:48 -080092 kAutomaticOrdering='auto',
93 kUserOrdering='user',
94)
95
96COPYRIGHT_HEADER = (
97"""// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -070098// Copyright 2023 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -080099// http://ceres-solver.org/
100//
101// Redistribution and use in source and binary forms, with or without
102// modification, are permitted provided that the following conditions are met:
103//
104// * Redistributions of source code must retain the above copyright notice,
105// this list of conditions and the following disclaimer.
106// * Redistributions in binary form must reproduce the above copyright notice,
107// this list of conditions and the following disclaimer in the documentation
108// and/or other materials provided with the distribution.
109// * Neither the name of Google Inc. nor the names of its contributors may be
110// used to endorse or promote products derived from this software without
111// specific prior written permission.
112//
113// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
114// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
115// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
116// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
117// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
118// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
119// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
120// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
121// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
122// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
123// POSSIBILITY OF SUCH DAMAGE.
124//
125// ========================================
126// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
127// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
128// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
129// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
130// ========================================
131//
132// This file is generated using generate_bundle_adjustment_tests.py.""")
133
134BUNDLE_ADJUSTMENT_TEST_TEMPLATE = (COPYRIGHT_HEADER + """
135
Austin Schuh3de38b02024-06-25 18:25:10 -0700136#include "ceres/bundle_adjustment_test_util.h"
137#include "ceres/internal/config.h"
138#include "gtest/gtest.h"
Austin Schuh70cc9552019-01-21 19:46:48 -0800139%(preprocessor_conditions_begin)s
Austin Schuh3de38b02024-06-25 18:25:10 -0700140namespace ceres::internal {
Austin Schuh70cc9552019-01-21 19:46:48 -0800141
142TEST_F(BundleAdjustmentTest,
143 %(test_class_name)s) { // NOLINT
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800144 BundleAdjustmentProblem bundle_adjustment_problem;
145 Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
Austin Schuh3de38b02024-06-25 18:25:10 -0700146 options->eta = 0.01;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800147 options->num_threads = %(num_threads)s;
148 options->linear_solver_type = %(linear_solver)s;
Austin Schuh3de38b02024-06-25 18:25:10 -0700149 options->dense_linear_algebra_library_type = %(dense_backend)s;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800150 options->sparse_linear_algebra_library_type = %(sparse_backend)s;
151 options->preconditioner_type = %(preconditioner)s;
152 if (%(ordering)s) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700153 options->linear_solver_ordering = nullptr;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800154 }
155 Problem* problem = bundle_adjustment_problem.mutable_problem();
156 RunSolverForConfigAndExpectResidualsMatch(*options, problem);
Austin Schuh70cc9552019-01-21 19:46:48 -0800157}
158
Austin Schuh3de38b02024-06-25 18:25:10 -0700159} // namespace ceres::internal
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800160%(preprocessor_conditions_end)s""")
Austin Schuh70cc9552019-01-21 19:46:48 -0800161
162def camelcasify(token):
163 """Convert capitalized underscore tokens to camel case"""
164 return ''.join([x.lower().capitalize() for x in token.split('_')])
165
166
167def generate_bundle_test(linear_solver,
Austin Schuh3de38b02024-06-25 18:25:10 -0700168 dense_backend,
Austin Schuh70cc9552019-01-21 19:46:48 -0800169 sparse_backend,
170 preconditioner,
171 ordering,
172 thread_config):
173 """Generate a bundle adjustment test executable configured appropriately"""
174
175 # Preconditioner only makes sense for iterative schur; drop it otherwise.
176 preconditioner_tag = preconditioner
177 if linear_solver != 'ITERATIVE_SCHUR':
178 preconditioner_tag = ''
179
Austin Schuh3de38b02024-06-25 18:25:10 -0700180 dense_backend_tag = dense_backend
181 if linear_solver != 'DENSE_SCHUR':
182 dense_backend_tag=''
183
Austin Schuh70cc9552019-01-21 19:46:48 -0800184 # Omit references to the sparse backend when one is not in use.
185 sparse_backend_tag = sparse_backend
186 if sparse_backend == 'NO_SPARSE':
187 sparse_backend_tag = ''
188
189 # Use a double underscore; otherwise the names are harder to understand.
190 test_class_name = '_'.join(filter(lambda x: x, [
191 camelcasify(linear_solver),
Austin Schuh3de38b02024-06-25 18:25:10 -0700192 camelcasify(dense_backend_tag),
Austin Schuh70cc9552019-01-21 19:46:48 -0800193 camelcasify(sparse_backend_tag),
194 camelcasify(preconditioner_tag),
195 ordering[1:], # Strip 'k'
196 'Threads' if thread_config == MULTI_THREADED else '']))
197
198 # Initial template parameters (augmented more below).
199 template_parameters = dict(
200 linear_solver=linear_solver,
Austin Schuh3de38b02024-06-25 18:25:10 -0700201 dense_backend=dense_backend,
Austin Schuh70cc9552019-01-21 19:46:48 -0800202 sparse_backend=sparse_backend,
203 preconditioner=preconditioner,
204 ordering=ordering,
205 num_threads=thread_config,
206 test_class_name=test_class_name)
207
208 # Accumulate appropriate #ifdef/#ifndefs for the solver's sparse backend.
209 preprocessor_conditions_begin = []
210 preprocessor_conditions_end = []
211 if sparse_backend == 'SUITE_SPARSE':
212 preprocessor_conditions_begin.append('#ifndef CERES_NO_SUITESPARSE')
213 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_SUITESPARSE')
Austin Schuh70cc9552019-01-21 19:46:48 -0800214 elif sparse_backend == 'ACCELERATE_SPARSE':
215 preprocessor_conditions_begin.append('#ifndef CERES_NO_ACCELERATE_SPARSE')
216 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_ACCELERATE_SPARSE')
217 elif sparse_backend == 'EIGEN_SPARSE':
218 preprocessor_conditions_begin.append('#ifdef CERES_USE_EIGEN_SPARSE')
219 preprocessor_conditions_end.insert(0, '#endif // CERES_USE_EIGEN_SPARSE')
220
Austin Schuh3de38b02024-06-25 18:25:10 -0700221 if dense_backend == "LAPACK":
222 preprocessor_conditions_begin.append('#ifndef CERES_NO_LAPACK')
223 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_LAPACK')
224 elif dense_backend == "CUDA":
225 preprocessor_conditions_begin.append('#ifndef CERES_NO_CUDA')
226 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_CUDA')
Austin Schuh70cc9552019-01-21 19:46:48 -0800227
228 # If there are #ifdefs, put newlines around them.
229 if preprocessor_conditions_begin:
230 preprocessor_conditions_begin.insert(0, '')
231 preprocessor_conditions_begin.append('')
232 preprocessor_conditions_end.insert(0, '')
233 preprocessor_conditions_end.append('')
234
235 # Put #ifdef/#ifndef stacks into the template parameters.
236 template_parameters['preprocessor_conditions_begin'] = '\n'.join(
237 preprocessor_conditions_begin)
238 template_parameters['preprocessor_conditions_end'] = '\n'.join(
239 preprocessor_conditions_end)
240
241 # Substitute variables into the test template, and write the result to a file.
242 filename_tag = '_'.join(FILENAME_SHORTENING_MAP.get(x) for x in [
243 linear_solver,
Austin Schuh3de38b02024-06-25 18:25:10 -0700244 dense_backend_tag,
Austin Schuh70cc9552019-01-21 19:46:48 -0800245 sparse_backend_tag,
246 preconditioner_tag,
247 ordering]
248 if FILENAME_SHORTENING_MAP.get(x))
Austin Schuh3de38b02024-06-25 18:25:10 -0700249
Austin Schuh70cc9552019-01-21 19:46:48 -0800250 if (thread_config == MULTI_THREADED):
251 filename_tag += '_threads'
252
253 filename = ('generated_bundle_adjustment_tests/ba_%s_test.cc' %
254 filename_tag.lower())
255 with open(filename, 'w') as fd:
256 fd.write(BUNDLE_ADJUSTMENT_TEST_TEMPLATE % template_parameters)
257
258 # All done.
Austin Schuh3de38b02024-06-25 18:25:10 -0700259 print('Generated', filename)
Austin Schuh70cc9552019-01-21 19:46:48 -0800260
261 return filename
262
263
264if __name__ == '__main__':
265 # Iterate over all the possible configurations and generate the tests.
266 generated_files = []
Austin Schuh3de38b02024-06-25 18:25:10 -0700267
268 for ordering in ORDERINGS:
269 for thread_config in THREAD_CONFIGS:
270 for linear_solver, dense_backend in DENSE_SOLVER_CONFIGS:
Austin Schuh70cc9552019-01-21 19:46:48 -0800271 generated_files.append(
272 generate_bundle_test(linear_solver,
Austin Schuh3de38b02024-06-25 18:25:10 -0700273 dense_backend,
274 'NO_SPARSE',
275 'IDENTITY',
276 ordering,
277 thread_config))
278
279 for linear_solver, sparse_backend, in SPARSE_SOLVER_CONFIGS:
280 generated_files.append(
281 generate_bundle_test(linear_solver,
282 'EIGEN',
283 sparse_backend,
284 'IDENTITY',
285 ordering,
286 thread_config))
287
288 for linear_solver, sparse_backend, preconditioner, in ITERATIVE_SOLVER_CONFIGS:
289 generated_files.append(
290 generate_bundle_test(linear_solver,
291 'EIGEN',
Austin Schuh70cc9552019-01-21 19:46:48 -0800292 sparse_backend,
293 preconditioner,
294 ordering,
295 thread_config))
296
Austin Schuh3de38b02024-06-25 18:25:10 -0700297
Austin Schuh70cc9552019-01-21 19:46:48 -0800298 # Generate the CMakeLists.txt as well.
299 with open('generated_bundle_adjustment_tests/CMakeLists.txt', 'w') as fd:
300 fd.write(COPYRIGHT_HEADER.replace('//', '#').replace('http:#', 'http://'))
301 fd.write('\n')
302 fd.write('\n')
303 for generated_file in generated_files:
304 fd.write('ceres_test(%s)\n' %
305 generated_file.split('/')[1].replace('_test.cc', ''))