blob: 291424468d55a85eddf25bbfb73d5221c7fd29c9 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 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#include "ceres/small_blas.h"
32
33#include <limits>
34#include "gtest/gtest.h"
35#include "ceres/internal/eigen.h"
36
37namespace ceres {
38namespace internal {
39
40const double kTolerance = 3.0 * std::numeric_limits<double>::epsilon();
41
42TEST(BLAS, MatrixMatrixMultiply) {
43 const int kRowA = 3;
44 const int kColA = 5;
45 Matrix A(kRowA, kColA);
46 A.setOnes();
47
48 const int kRowB = 5;
49 const int kColB = 7;
50 Matrix B(kRowB, kColB);
51 B.setOnes();
52
53 for (int row_stride_c = kRowA; row_stride_c < 3 * kRowA; ++row_stride_c) {
54 for (int col_stride_c = kColB; col_stride_c < 3 * kColB; ++col_stride_c) {
55 Matrix C(row_stride_c, col_stride_c);
56 C.setOnes();
57
58 Matrix C_plus = C;
59 Matrix C_minus = C;
60 Matrix C_assign = C;
61
62 Matrix C_plus_ref = C;
63 Matrix C_minus_ref = C;
64 Matrix C_assign_ref = C;
65 for (int start_row_c = 0; start_row_c + kRowA < row_stride_c; ++start_row_c) {
66 for (int start_col_c = 0; start_col_c + kColB < col_stride_c; ++start_col_c) {
67 C_plus_ref.block(start_row_c, start_col_c, kRowA, kColB) +=
68 A * B;
69
70 MatrixMatrixMultiply<kRowA, kColA, kRowB, kColB, 1>(
71 A.data(), kRowA, kColA,
72 B.data(), kRowB, kColB,
73 C_plus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
74
75 EXPECT_NEAR((C_plus_ref - C_plus).norm(), 0.0, kTolerance)
76 << "C += A * B \n"
77 << "row_stride_c : " << row_stride_c << "\n"
78 << "col_stride_c : " << col_stride_c << "\n"
79 << "start_row_c : " << start_row_c << "\n"
80 << "start_col_c : " << start_col_c << "\n"
81 << "Cref : \n" << C_plus_ref << "\n"
82 << "C: \n" << C_plus;
83
84
85 C_minus_ref.block(start_row_c, start_col_c, kRowA, kColB) -=
86 A * B;
87
88 MatrixMatrixMultiply<kRowA, kColA, kRowB, kColB, -1>(
89 A.data(), kRowA, kColA,
90 B.data(), kRowB, kColB,
91 C_minus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
92
93 EXPECT_NEAR((C_minus_ref - C_minus).norm(), 0.0, kTolerance)
94 << "C -= A * B \n"
95 << "row_stride_c : " << row_stride_c << "\n"
96 << "col_stride_c : " << col_stride_c << "\n"
97 << "start_row_c : " << start_row_c << "\n"
98 << "start_col_c : " << start_col_c << "\n"
99 << "Cref : \n" << C_minus_ref << "\n"
100 << "C: \n" << C_minus;
101
102 C_assign_ref.block(start_row_c, start_col_c, kRowA, kColB) =
103 A * B;
104
105 MatrixMatrixMultiply<kRowA, kColA, kRowB, kColB, 0>(
106 A.data(), kRowA, kColA,
107 B.data(), kRowB, kColB,
108 C_assign.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
109
110 EXPECT_NEAR((C_assign_ref - C_assign).norm(), 0.0, kTolerance)
111 << "C = A * B \n"
112 << "row_stride_c : " << row_stride_c << "\n"
113 << "col_stride_c : " << col_stride_c << "\n"
114 << "start_row_c : " << start_row_c << "\n"
115 << "start_col_c : " << start_col_c << "\n"
116 << "Cref : \n" << C_assign_ref << "\n"
117 << "C: \n" << C_assign;
118 }
119 }
120 }
121 }
122}
123
124TEST(BLAS, MatrixTransposeMatrixMultiply) {
125 const int kRowA = 5;
126 const int kColA = 3;
127 Matrix A(kRowA, kColA);
128 A.setOnes();
129
130 const int kRowB = 5;
131 const int kColB = 7;
132 Matrix B(kRowB, kColB);
133 B.setOnes();
134
135 for (int row_stride_c = kColA; row_stride_c < 3 * kColA; ++row_stride_c) {
136 for (int col_stride_c = kColB; col_stride_c < 3 * kColB; ++col_stride_c) {
137 Matrix C(row_stride_c, col_stride_c);
138 C.setOnes();
139
140 Matrix C_plus = C;
141 Matrix C_minus = C;
142 Matrix C_assign = C;
143
144 Matrix C_plus_ref = C;
145 Matrix C_minus_ref = C;
146 Matrix C_assign_ref = C;
147 for (int start_row_c = 0; start_row_c + kColA < row_stride_c; ++start_row_c) {
148 for (int start_col_c = 0; start_col_c + kColB < col_stride_c; ++start_col_c) {
149 C_plus_ref.block(start_row_c, start_col_c, kColA, kColB) +=
150 A.transpose() * B;
151
152 MatrixTransposeMatrixMultiply<kRowA, kColA, kRowB, kColB, 1>(
153 A.data(), kRowA, kColA,
154 B.data(), kRowB, kColB,
155 C_plus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
156
157 EXPECT_NEAR((C_plus_ref - C_plus).norm(), 0.0, kTolerance)
158 << "C += A' * B \n"
159 << "row_stride_c : " << row_stride_c << "\n"
160 << "col_stride_c : " << col_stride_c << "\n"
161 << "start_row_c : " << start_row_c << "\n"
162 << "start_col_c : " << start_col_c << "\n"
163 << "Cref : \n" << C_plus_ref << "\n"
164 << "C: \n" << C_plus;
165
166 C_minus_ref.block(start_row_c, start_col_c, kColA, kColB) -=
167 A.transpose() * B;
168
169 MatrixTransposeMatrixMultiply<kRowA, kColA, kRowB, kColB, -1>(
170 A.data(), kRowA, kColA,
171 B.data(), kRowB, kColB,
172 C_minus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
173
174 EXPECT_NEAR((C_minus_ref - C_minus).norm(), 0.0, kTolerance)
175 << "C -= A' * B \n"
176 << "row_stride_c : " << row_stride_c << "\n"
177 << "col_stride_c : " << col_stride_c << "\n"
178 << "start_row_c : " << start_row_c << "\n"
179 << "start_col_c : " << start_col_c << "\n"
180 << "Cref : \n" << C_minus_ref << "\n"
181 << "C: \n" << C_minus;
182
183 C_assign_ref.block(start_row_c, start_col_c, kColA, kColB) =
184 A.transpose() * B;
185
186 MatrixTransposeMatrixMultiply<kRowA, kColA, kRowB, kColB, 0>(
187 A.data(), kRowA, kColA,
188 B.data(), kRowB, kColB,
189 C_assign.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
190
191 EXPECT_NEAR((C_assign_ref - C_assign).norm(), 0.0, kTolerance)
192 << "C = A' * B \n"
193 << "row_stride_c : " << row_stride_c << "\n"
194 << "col_stride_c : " << col_stride_c << "\n"
195 << "start_row_c : " << start_row_c << "\n"
196 << "start_col_c : " << start_col_c << "\n"
197 << "Cref : \n" << C_assign_ref << "\n"
198 << "C: \n" << C_assign;
199 }
200 }
201 }
202 }
203}
204
205// TODO(sameeragarwal): Dedup and reduce the amount of duplication of
206// test code in this file.
207
208TEST(BLAS, MatrixMatrixMultiplyNaive) {
209 const int kRowA = 3;
210 const int kColA = 5;
211 Matrix A(kRowA, kColA);
212 A.setOnes();
213
214 const int kRowB = 5;
215 const int kColB = 7;
216 Matrix B(kRowB, kColB);
217 B.setOnes();
218
219 for (int row_stride_c = kRowA; row_stride_c < 3 * kRowA; ++row_stride_c) {
220 for (int col_stride_c = kColB; col_stride_c < 3 * kColB; ++col_stride_c) {
221 Matrix C(row_stride_c, col_stride_c);
222 C.setOnes();
223
224 Matrix C_plus = C;
225 Matrix C_minus = C;
226 Matrix C_assign = C;
227
228 Matrix C_plus_ref = C;
229 Matrix C_minus_ref = C;
230 Matrix C_assign_ref = C;
231 for (int start_row_c = 0; start_row_c + kRowA < row_stride_c; ++start_row_c) {
232 for (int start_col_c = 0; start_col_c + kColB < col_stride_c; ++start_col_c) {
233 C_plus_ref.block(start_row_c, start_col_c, kRowA, kColB) +=
234 A * B;
235
236 MatrixMatrixMultiplyNaive<kRowA, kColA, kRowB, kColB, 1>(
237 A.data(), kRowA, kColA,
238 B.data(), kRowB, kColB,
239 C_plus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
240
241 EXPECT_NEAR((C_plus_ref - C_plus).norm(), 0.0, kTolerance)
242 << "C += A * B \n"
243 << "row_stride_c : " << row_stride_c << "\n"
244 << "col_stride_c : " << col_stride_c << "\n"
245 << "start_row_c : " << start_row_c << "\n"
246 << "start_col_c : " << start_col_c << "\n"
247 << "Cref : \n" << C_plus_ref << "\n"
248 << "C: \n" << C_plus;
249
250
251 C_minus_ref.block(start_row_c, start_col_c, kRowA, kColB) -=
252 A * B;
253
254 MatrixMatrixMultiplyNaive<kRowA, kColA, kRowB, kColB, -1>(
255 A.data(), kRowA, kColA,
256 B.data(), kRowB, kColB,
257 C_minus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
258
259 EXPECT_NEAR((C_minus_ref - C_minus).norm(), 0.0, kTolerance)
260 << "C -= A * B \n"
261 << "row_stride_c : " << row_stride_c << "\n"
262 << "col_stride_c : " << col_stride_c << "\n"
263 << "start_row_c : " << start_row_c << "\n"
264 << "start_col_c : " << start_col_c << "\n"
265 << "Cref : \n" << C_minus_ref << "\n"
266 << "C: \n" << C_minus;
267
268 C_assign_ref.block(start_row_c, start_col_c, kRowA, kColB) =
269 A * B;
270
271 MatrixMatrixMultiplyNaive<kRowA, kColA, kRowB, kColB, 0>(
272 A.data(), kRowA, kColA,
273 B.data(), kRowB, kColB,
274 C_assign.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
275
276 EXPECT_NEAR((C_assign_ref - C_assign).norm(), 0.0, kTolerance)
277 << "C = A * B \n"
278 << "row_stride_c : " << row_stride_c << "\n"
279 << "col_stride_c : " << col_stride_c << "\n"
280 << "start_row_c : " << start_row_c << "\n"
281 << "start_col_c : " << start_col_c << "\n"
282 << "Cref : \n" << C_assign_ref << "\n"
283 << "C: \n" << C_assign;
284 }
285 }
286 }
287 }
288}
289
290TEST(BLAS, MatrixTransposeMatrixMultiplyNaive) {
291 const int kRowA = 5;
292 const int kColA = 3;
293 Matrix A(kRowA, kColA);
294 A.setOnes();
295
296 const int kRowB = 5;
297 const int kColB = 7;
298 Matrix B(kRowB, kColB);
299 B.setOnes();
300
301 for (int row_stride_c = kColA; row_stride_c < 3 * kColA; ++row_stride_c) {
302 for (int col_stride_c = kColB; col_stride_c < 3 * kColB; ++col_stride_c) {
303 Matrix C(row_stride_c, col_stride_c);
304 C.setOnes();
305
306 Matrix C_plus = C;
307 Matrix C_minus = C;
308 Matrix C_assign = C;
309
310 Matrix C_plus_ref = C;
311 Matrix C_minus_ref = C;
312 Matrix C_assign_ref = C;
313 for (int start_row_c = 0; start_row_c + kColA < row_stride_c; ++start_row_c) {
314 for (int start_col_c = 0; start_col_c + kColB < col_stride_c; ++start_col_c) {
315 C_plus_ref.block(start_row_c, start_col_c, kColA, kColB) +=
316 A.transpose() * B;
317
318 MatrixTransposeMatrixMultiplyNaive<kRowA, kColA, kRowB, kColB, 1>(
319 A.data(), kRowA, kColA,
320 B.data(), kRowB, kColB,
321 C_plus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
322
323 EXPECT_NEAR((C_plus_ref - C_plus).norm(), 0.0, kTolerance)
324 << "C += A' * B \n"
325 << "row_stride_c : " << row_stride_c << "\n"
326 << "col_stride_c : " << col_stride_c << "\n"
327 << "start_row_c : " << start_row_c << "\n"
328 << "start_col_c : " << start_col_c << "\n"
329 << "Cref : \n" << C_plus_ref << "\n"
330 << "C: \n" << C_plus;
331
332 C_minus_ref.block(start_row_c, start_col_c, kColA, kColB) -=
333 A.transpose() * B;
334
335 MatrixTransposeMatrixMultiplyNaive<kRowA, kColA, kRowB, kColB, -1>(
336 A.data(), kRowA, kColA,
337 B.data(), kRowB, kColB,
338 C_minus.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
339
340 EXPECT_NEAR((C_minus_ref - C_minus).norm(), 0.0, kTolerance)
341 << "C -= A' * B \n"
342 << "row_stride_c : " << row_stride_c << "\n"
343 << "col_stride_c : " << col_stride_c << "\n"
344 << "start_row_c : " << start_row_c << "\n"
345 << "start_col_c : " << start_col_c << "\n"
346 << "Cref : \n" << C_minus_ref << "\n"
347 << "C: \n" << C_minus;
348
349 C_assign_ref.block(start_row_c, start_col_c, kColA, kColB) =
350 A.transpose() * B;
351
352 MatrixTransposeMatrixMultiplyNaive<kRowA, kColA, kRowB, kColB, 0>(
353 A.data(), kRowA, kColA,
354 B.data(), kRowB, kColB,
355 C_assign.data(), start_row_c, start_col_c, row_stride_c, col_stride_c);
356
357 EXPECT_NEAR((C_assign_ref - C_assign).norm(), 0.0, kTolerance)
358 << "C = A' * B \n"
359 << "row_stride_c : " << row_stride_c << "\n"
360 << "col_stride_c : " << col_stride_c << "\n"
361 << "start_row_c : " << start_row_c << "\n"
362 << "start_col_c : " << start_col_c << "\n"
363 << "Cref : \n" << C_assign_ref << "\n"
364 << "C: \n" << C_assign;
365 }
366 }
367 }
368 }
369}
370
371TEST(BLAS, MatrixVectorMultiply) {
372 for (int num_rows_a = 1; num_rows_a < 10; ++num_rows_a) {
373 for (int num_cols_a = 1; num_cols_a < 10; ++num_cols_a) {
374 Matrix A(num_rows_a, num_cols_a);
375 A.setOnes();
376
377 Vector b(num_cols_a);
378 b.setOnes();
379
380 Vector c(num_rows_a);
381 c.setOnes();
382
383 Vector c_plus = c;
384 Vector c_minus = c;
385 Vector c_assign = c;
386
387 Vector c_plus_ref = c;
388 Vector c_minus_ref = c;
389 Vector c_assign_ref = c;
390
391 c_plus_ref += A * b;
392 MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
393 A.data(), num_rows_a, num_cols_a,
394 b.data(),
395 c_plus.data());
396 EXPECT_NEAR((c_plus_ref - c_plus).norm(), 0.0, kTolerance)
397 << "c += A * b \n"
398 << "c_ref : \n" << c_plus_ref << "\n"
399 << "c: \n" << c_plus;
400
401 c_minus_ref -= A * b;
402 MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, -1>(
403 A.data(), num_rows_a, num_cols_a,
404 b.data(),
405 c_minus.data());
406 EXPECT_NEAR((c_minus_ref - c_minus).norm(), 0.0, kTolerance)
407 << "c += A * b \n"
408 << "c_ref : \n" << c_minus_ref << "\n"
409 << "c: \n" << c_minus;
410
411 c_assign_ref = A * b;
412 MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 0>(
413 A.data(), num_rows_a, num_cols_a,
414 b.data(),
415 c_assign.data());
416 EXPECT_NEAR((c_assign_ref - c_assign).norm(), 0.0, kTolerance)
417 << "c += A * b \n"
418 << "c_ref : \n" << c_assign_ref << "\n"
419 << "c: \n" << c_assign;
420 }
421 }
422}
423
424TEST(BLAS, MatrixTransposeVectorMultiply) {
425 for (int num_rows_a = 1; num_rows_a < 10; ++num_rows_a) {
426 for (int num_cols_a = 1; num_cols_a < 10; ++num_cols_a) {
427 Matrix A(num_rows_a, num_cols_a);
428 A.setRandom();
429
430 Vector b(num_rows_a);
431 b.setRandom();
432
433 Vector c(num_cols_a);
434 c.setOnes();
435
436 Vector c_plus = c;
437 Vector c_minus = c;
438 Vector c_assign = c;
439
440 Vector c_plus_ref = c;
441 Vector c_minus_ref = c;
442 Vector c_assign_ref = c;
443
444 c_plus_ref += A.transpose() * b;
445 MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
446 A.data(), num_rows_a, num_cols_a,
447 b.data(),
448 c_plus.data());
449 EXPECT_NEAR((c_plus_ref - c_plus).norm(), 0.0, kTolerance)
450 << "c += A' * b \n"
451 << "c_ref : \n" << c_plus_ref << "\n"
452 << "c: \n" << c_plus;
453
454 c_minus_ref -= A.transpose() * b;
455 MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, -1>(
456 A.data(), num_rows_a, num_cols_a,
457 b.data(),
458 c_minus.data());
459 EXPECT_NEAR((c_minus_ref - c_minus).norm(), 0.0, kTolerance)
460 << "c += A' * b \n"
461 << "c_ref : \n" << c_minus_ref << "\n"
462 << "c: \n" << c_minus;
463
464 c_assign_ref = A.transpose() * b;
465 MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 0>(
466 A.data(), num_rows_a, num_cols_a,
467 b.data(),
468 c_assign.data());
469 EXPECT_NEAR((c_assign_ref - c_assign).norm(), 0.0, kTolerance)
470 << "c += A' * b \n"
471 << "c_ref : \n" << c_assign_ref << "\n"
472 << "c: \n" << c_assign;
473 }
474 }
475}
476
477} // namespace internal
478} // namespace ceres