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