blob: 7b0caa3075580fb14cc889c016d523b07797d33b [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001# Ceres Solver - A fast non-linear least squares minimizer
2# Copyright 2018 Google Inc. All rights reserved.
3# 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
44SOLVER_CONFIGS = [
45 # Linear solver Sparse backend Preconditioner
46 ('DENSE_SCHUR', 'NO_SPARSE', 'IDENTITY'),
47 ('ITERATIVE_SCHUR', 'NO_SPARSE', 'JACOBI'),
48 ('ITERATIVE_SCHUR', 'NO_SPARSE', 'SCHUR_JACOBI'),
49 ('ITERATIVE_SCHUR', 'SUITE_SPARSE', 'CLUSTER_JACOBI'),
50 ('ITERATIVE_SCHUR', 'EIGEN_SPARSE', 'CLUSTER_JACOBI'),
51 ('ITERATIVE_SCHUR', 'CX_SPARSE', 'CLUSTER_JACOBI'),
52 ('ITERATIVE_SCHUR', 'ACCELERATE_SPARSE','CLUSTER_JACOBI'),
53 ('ITERATIVE_SCHUR', 'SUITE_SPARSE', 'CLUSTER_TRIDIAGONAL'),
54 ('ITERATIVE_SCHUR', 'EIGEN_SPARSE', 'CLUSTER_TRIDIAGONAL'),
55 ('ITERATIVE_SCHUR', 'CX_SPARSE', 'CLUSTER_TRIDIAGONAL'),
56 ('ITERATIVE_SCHUR', 'ACCELERATE_SPARSE','CLUSTER_TRIDIAGONAL'),
57 ('SPARSE_NORMAL_CHOLESKY', 'SUITE_SPARSE', 'IDENTITY'),
58 ('SPARSE_NORMAL_CHOLESKY', 'EIGEN_SPARSE', 'IDENTITY'),
59 ('SPARSE_NORMAL_CHOLESKY', 'CX_SPARSE', 'IDENTITY'),
60 ('SPARSE_NORMAL_CHOLESKY', 'ACCELERATE_SPARSE','IDENTITY'),
61 ('SPARSE_SCHUR', 'SUITE_SPARSE', 'IDENTITY'),
62 ('SPARSE_SCHUR', 'EIGEN_SPARSE', 'IDENTITY'),
63 ('SPARSE_SCHUR', 'CX_SPARSE', 'IDENTITY'),
64 ('SPARSE_SCHUR', 'ACCELERATE_SPARSE','IDENTITY'),
65]
66
67FILENAME_SHORTENING_MAP = dict(
68 DENSE_SCHUR='denseschur',
69 ITERATIVE_SCHUR='iterschur',
70 SPARSE_NORMAL_CHOLESKY='sparsecholesky',
71 SPARSE_SCHUR='sparseschur',
72 NO_SPARSE='', # Omit sparse reference entirely for dense tests.
73 SUITE_SPARSE='suitesparse',
74 EIGEN_SPARSE='eigensparse',
75 CX_SPARSE='cxsparse',
76 ACCELERATE_SPARSE='acceleratesparse',
77 IDENTITY='identity',
78 JACOBI='jacobi',
79 SCHUR_JACOBI='schurjacobi',
80 CLUSTER_JACOBI='clustjacobi',
81 CLUSTER_TRIDIAGONAL='clusttri',
82 kAutomaticOrdering='auto',
83 kUserOrdering='user',
84)
85
86COPYRIGHT_HEADER = (
87"""// Ceres Solver - A fast non-linear least squares minimizer
88// Copyright 2018 Google Inc. All rights reserved.
89// http://ceres-solver.org/
90//
91// Redistribution and use in source and binary forms, with or without
92// modification, are permitted provided that the following conditions are met:
93//
94// * Redistributions of source code must retain the above copyright notice,
95// this list of conditions and the following disclaimer.
96// * Redistributions in binary form must reproduce the above copyright notice,
97// this list of conditions and the following disclaimer in the documentation
98// and/or other materials provided with the distribution.
99// * Neither the name of Google Inc. nor the names of its contributors may be
100// used to endorse or promote products derived from this software without
101// specific prior written permission.
102//
103// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
104// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
105// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
106// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
107// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
108// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
109// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
110// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
111// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
112// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
113// POSSIBILITY OF SUCH DAMAGE.
114//
115// ========================================
116// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
117// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
118// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
119// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
120// ========================================
121//
122// This file is generated using generate_bundle_adjustment_tests.py.""")
123
124BUNDLE_ADJUSTMENT_TEST_TEMPLATE = (COPYRIGHT_HEADER + """
125
126#include "bundle_adjustment_test_util.h"
127%(preprocessor_conditions_begin)s
128namespace ceres {
129namespace internal {
130
131TEST_F(BundleAdjustmentTest,
132 %(test_class_name)s) { // NOLINT
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800133 BundleAdjustmentProblem bundle_adjustment_problem;
134 Solver::Options* options = bundle_adjustment_problem.mutable_solver_options();
135 options->num_threads = %(num_threads)s;
136 options->linear_solver_type = %(linear_solver)s;
137 options->sparse_linear_algebra_library_type = %(sparse_backend)s;
138 options->preconditioner_type = %(preconditioner)s;
139 if (%(ordering)s) {
140 options->linear_solver_ordering.reset();
141 }
142 Problem* problem = bundle_adjustment_problem.mutable_problem();
143 RunSolverForConfigAndExpectResidualsMatch(*options, problem);
Austin Schuh70cc9552019-01-21 19:46:48 -0800144}
145
146} // namespace internal
147} // namespace ceres
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800148%(preprocessor_conditions_end)s""")
Austin Schuh70cc9552019-01-21 19:46:48 -0800149
150def camelcasify(token):
151 """Convert capitalized underscore tokens to camel case"""
152 return ''.join([x.lower().capitalize() for x in token.split('_')])
153
154
155def generate_bundle_test(linear_solver,
156 sparse_backend,
157 preconditioner,
158 ordering,
159 thread_config):
160 """Generate a bundle adjustment test executable configured appropriately"""
161
162 # Preconditioner only makes sense for iterative schur; drop it otherwise.
163 preconditioner_tag = preconditioner
164 if linear_solver != 'ITERATIVE_SCHUR':
165 preconditioner_tag = ''
166
167 # Omit references to the sparse backend when one is not in use.
168 sparse_backend_tag = sparse_backend
169 if sparse_backend == 'NO_SPARSE':
170 sparse_backend_tag = ''
171
172 # Use a double underscore; otherwise the names are harder to understand.
173 test_class_name = '_'.join(filter(lambda x: x, [
174 camelcasify(linear_solver),
175 camelcasify(sparse_backend_tag),
176 camelcasify(preconditioner_tag),
177 ordering[1:], # Strip 'k'
178 'Threads' if thread_config == MULTI_THREADED else '']))
179
180 # Initial template parameters (augmented more below).
181 template_parameters = dict(
182 linear_solver=linear_solver,
183 sparse_backend=sparse_backend,
184 preconditioner=preconditioner,
185 ordering=ordering,
186 num_threads=thread_config,
187 test_class_name=test_class_name)
188
189 # Accumulate appropriate #ifdef/#ifndefs for the solver's sparse backend.
190 preprocessor_conditions_begin = []
191 preprocessor_conditions_end = []
192 if sparse_backend == 'SUITE_SPARSE':
193 preprocessor_conditions_begin.append('#ifndef CERES_NO_SUITESPARSE')
194 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_SUITESPARSE')
195 elif sparse_backend == 'CX_SPARSE':
196 preprocessor_conditions_begin.append('#ifndef CERES_NO_CXSPARSE')
197 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_CXSPARSE')
198 elif sparse_backend == 'ACCELERATE_SPARSE':
199 preprocessor_conditions_begin.append('#ifndef CERES_NO_ACCELERATE_SPARSE')
200 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_ACCELERATE_SPARSE')
201 elif sparse_backend == 'EIGEN_SPARSE':
202 preprocessor_conditions_begin.append('#ifdef CERES_USE_EIGEN_SPARSE')
203 preprocessor_conditions_end.insert(0, '#endif // CERES_USE_EIGEN_SPARSE')
204
205 # Accumulate appropriate #ifdef/#ifndefs for threading conditions.
206 if thread_config == MULTI_THREADED:
207 preprocessor_conditions_begin.append('#ifndef CERES_NO_THREADS')
208 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_THREADS')
209
210 # If there are #ifdefs, put newlines around them.
211 if preprocessor_conditions_begin:
212 preprocessor_conditions_begin.insert(0, '')
213 preprocessor_conditions_begin.append('')
214 preprocessor_conditions_end.insert(0, '')
215 preprocessor_conditions_end.append('')
216
217 # Put #ifdef/#ifndef stacks into the template parameters.
218 template_parameters['preprocessor_conditions_begin'] = '\n'.join(
219 preprocessor_conditions_begin)
220 template_parameters['preprocessor_conditions_end'] = '\n'.join(
221 preprocessor_conditions_end)
222
223 # Substitute variables into the test template, and write the result to a file.
224 filename_tag = '_'.join(FILENAME_SHORTENING_MAP.get(x) for x in [
225 linear_solver,
226 sparse_backend_tag,
227 preconditioner_tag,
228 ordering]
229 if FILENAME_SHORTENING_MAP.get(x))
230 if (thread_config == MULTI_THREADED):
231 filename_tag += '_threads'
232
233 filename = ('generated_bundle_adjustment_tests/ba_%s_test.cc' %
234 filename_tag.lower())
235 with open(filename, 'w') as fd:
236 fd.write(BUNDLE_ADJUSTMENT_TEST_TEMPLATE % template_parameters)
237
238 # All done.
239 print 'Generated', filename
240
241 return filename
242
243
244if __name__ == '__main__':
245 # Iterate over all the possible configurations and generate the tests.
246 generated_files = []
247 for linear_solver, sparse_backend, preconditioner in SOLVER_CONFIGS:
248 for ordering in ORDERINGS:
249 for thread_config in THREAD_CONFIGS:
250 generated_files.append(
251 generate_bundle_test(linear_solver,
252 sparse_backend,
253 preconditioner,
254 ordering,
255 thread_config))
256
257 # Generate the CMakeLists.txt as well.
258 with open('generated_bundle_adjustment_tests/CMakeLists.txt', 'w') as fd:
259 fd.write(COPYRIGHT_HEADER.replace('//', '#').replace('http:#', 'http://'))
260 fd.write('\n')
261 fd.write('\n')
262 for generated_file in generated_files:
263 fd.write('ceres_test(%s)\n' %
264 generated_file.split('/')[1].replace('_test.cc', ''))