blob: a3469eb4a1285f0452a616244eb03dcb4e537582 [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
133 BundleAdjustmentProblem bundle_adjustment_problem;
134 Solver::Options* options =
135 bundle_adjustment_problem.mutable_solver_options();
136 options->num_threads = %(num_threads)s;
137 options->linear_solver_type = %(linear_solver)s;
138 options->sparse_linear_algebra_library_type = %(sparse_backend)s;
139 options->preconditioner_type = %(preconditioner)s;
140 if (%(ordering)s) {
141 options->linear_solver_ordering.reset();
142 }
143 Problem* problem = bundle_adjustment_problem.mutable_problem();
144 RunSolverForConfigAndExpectResidualsMatch(*options, problem);
145}
146
147} // namespace internal
148} // namespace ceres
149%(preprocessor_conditions_end)s
150""")
151
152def camelcasify(token):
153 """Convert capitalized underscore tokens to camel case"""
154 return ''.join([x.lower().capitalize() for x in token.split('_')])
155
156
157def generate_bundle_test(linear_solver,
158 sparse_backend,
159 preconditioner,
160 ordering,
161 thread_config):
162 """Generate a bundle adjustment test executable configured appropriately"""
163
164 # Preconditioner only makes sense for iterative schur; drop it otherwise.
165 preconditioner_tag = preconditioner
166 if linear_solver != 'ITERATIVE_SCHUR':
167 preconditioner_tag = ''
168
169 # Omit references to the sparse backend when one is not in use.
170 sparse_backend_tag = sparse_backend
171 if sparse_backend == 'NO_SPARSE':
172 sparse_backend_tag = ''
173
174 # Use a double underscore; otherwise the names are harder to understand.
175 test_class_name = '_'.join(filter(lambda x: x, [
176 camelcasify(linear_solver),
177 camelcasify(sparse_backend_tag),
178 camelcasify(preconditioner_tag),
179 ordering[1:], # Strip 'k'
180 'Threads' if thread_config == MULTI_THREADED else '']))
181
182 # Initial template parameters (augmented more below).
183 template_parameters = dict(
184 linear_solver=linear_solver,
185 sparse_backend=sparse_backend,
186 preconditioner=preconditioner,
187 ordering=ordering,
188 num_threads=thread_config,
189 test_class_name=test_class_name)
190
191 # Accumulate appropriate #ifdef/#ifndefs for the solver's sparse backend.
192 preprocessor_conditions_begin = []
193 preprocessor_conditions_end = []
194 if sparse_backend == 'SUITE_SPARSE':
195 preprocessor_conditions_begin.append('#ifndef CERES_NO_SUITESPARSE')
196 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_SUITESPARSE')
197 elif sparse_backend == 'CX_SPARSE':
198 preprocessor_conditions_begin.append('#ifndef CERES_NO_CXSPARSE')
199 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_CXSPARSE')
200 elif sparse_backend == 'ACCELERATE_SPARSE':
201 preprocessor_conditions_begin.append('#ifndef CERES_NO_ACCELERATE_SPARSE')
202 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_ACCELERATE_SPARSE')
203 elif sparse_backend == 'EIGEN_SPARSE':
204 preprocessor_conditions_begin.append('#ifdef CERES_USE_EIGEN_SPARSE')
205 preprocessor_conditions_end.insert(0, '#endif // CERES_USE_EIGEN_SPARSE')
206
207 # Accumulate appropriate #ifdef/#ifndefs for threading conditions.
208 if thread_config == MULTI_THREADED:
209 preprocessor_conditions_begin.append('#ifndef CERES_NO_THREADS')
210 preprocessor_conditions_end.insert(0, '#endif // CERES_NO_THREADS')
211
212 # If there are #ifdefs, put newlines around them.
213 if preprocessor_conditions_begin:
214 preprocessor_conditions_begin.insert(0, '')
215 preprocessor_conditions_begin.append('')
216 preprocessor_conditions_end.insert(0, '')
217 preprocessor_conditions_end.append('')
218
219 # Put #ifdef/#ifndef stacks into the template parameters.
220 template_parameters['preprocessor_conditions_begin'] = '\n'.join(
221 preprocessor_conditions_begin)
222 template_parameters['preprocessor_conditions_end'] = '\n'.join(
223 preprocessor_conditions_end)
224
225 # Substitute variables into the test template, and write the result to a file.
226 filename_tag = '_'.join(FILENAME_SHORTENING_MAP.get(x) for x in [
227 linear_solver,
228 sparse_backend_tag,
229 preconditioner_tag,
230 ordering]
231 if FILENAME_SHORTENING_MAP.get(x))
232 if (thread_config == MULTI_THREADED):
233 filename_tag += '_threads'
234
235 filename = ('generated_bundle_adjustment_tests/ba_%s_test.cc' %
236 filename_tag.lower())
237 with open(filename, 'w') as fd:
238 fd.write(BUNDLE_ADJUSTMENT_TEST_TEMPLATE % template_parameters)
239
240 # All done.
241 print 'Generated', filename
242
243 return filename
244
245
246if __name__ == '__main__':
247 # Iterate over all the possible configurations and generate the tests.
248 generated_files = []
249 for linear_solver, sparse_backend, preconditioner in SOLVER_CONFIGS:
250 for ordering in ORDERINGS:
251 for thread_config in THREAD_CONFIGS:
252 generated_files.append(
253 generate_bundle_test(linear_solver,
254 sparse_backend,
255 preconditioner,
256 ordering,
257 thread_config))
258
259 # Generate the CMakeLists.txt as well.
260 with open('generated_bundle_adjustment_tests/CMakeLists.txt', 'w') as fd:
261 fd.write(COPYRIGHT_HEADER.replace('//', '#').replace('http:#', 'http://'))
262 fd.write('\n')
263 fd.write('\n')
264 for generated_file in generated_files:
265 fd.write('ceres_test(%s)\n' %
266 generated_file.split('/')[1].replace('_test.cc', ''))