blob: 8dcb4b7b976d9ece67392eafb6fb4acf80e0e6f8 [file] [log] [blame]
Austin Schuh3de38b02024-06-25 18:25:10 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2023 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: joydeepb@cs.utexas.edu (Joydeep Biswas)
30
31#include "ceres/cuda_vector.h"
32
33#include <string>
34
35#include "ceres/internal/config.h"
36#include "ceres/internal/eigen.h"
37#include "glog/logging.h"
38#include "gtest/gtest.h"
39
40namespace ceres {
41namespace internal {
42
43#ifndef CERES_NO_CUDA
44
45TEST(CudaVector, Creation) {
46 ContextImpl context;
47 std::string message;
48 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
49 CudaVector x(&context, 1000);
50 EXPECT_EQ(x.num_rows(), 1000);
51 EXPECT_NE(x.data(), nullptr);
52}
53
54TEST(CudaVector, CopyVector) {
55 Vector x(3);
56 x << 1, 2, 3;
57 ContextImpl context;
58 std::string message;
59 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
60 CudaVector y(&context, 10);
61 y.CopyFromCpu(x);
62 EXPECT_EQ(y.num_rows(), 3);
63
64 Vector z(3);
65 z << 0, 0, 0;
66 y.CopyTo(&z);
67 EXPECT_EQ(x, z);
68}
69
70TEST(CudaVector, Move) {
71 ContextImpl context;
72 std::string message;
73 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
74 CudaVector y(&context, 10);
75 const auto y_data = y.data();
76 const auto y_descr = y.descr();
77 EXPECT_EQ(y.num_rows(), 10);
78 CudaVector z(std::move(y));
79 EXPECT_EQ(y.data(), nullptr);
80 EXPECT_EQ(y.descr(), nullptr);
81 EXPECT_EQ(y.num_rows(), 0);
82
83 EXPECT_EQ(z.data(), y_data);
84 EXPECT_EQ(z.descr(), y_descr);
85}
86
87TEST(CudaVector, DeepCopy) {
88 Vector x(3);
89 x << 1, 2, 3;
90 ContextImpl context;
91 std::string message;
92 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
93 CudaVector x_gpu(&context, 3);
94 x_gpu.CopyFromCpu(x);
95
96 CudaVector y_gpu(&context, 3);
97 y_gpu.SetZero();
98 EXPECT_EQ(y_gpu.Norm(), 0.0);
99
100 y_gpu = x_gpu;
101 Vector y(3);
102 y << 0, 0, 0;
103 y_gpu.CopyTo(&y);
104 EXPECT_EQ(x, y);
105}
106
107TEST(CudaVector, Dot) {
108 Vector x(3);
109 Vector y(3);
110 x << 1, 2, 3;
111 y << 100, 10, 1;
112 ContextImpl context;
113 std::string message;
114 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
115 CudaVector x_gpu(&context, 10);
116 CudaVector y_gpu(&context, 10);
117 x_gpu.CopyFromCpu(x);
118 y_gpu.CopyFromCpu(y);
119
120 EXPECT_EQ(x_gpu.Dot(y_gpu), 123.0);
121 EXPECT_EQ(Dot(x_gpu, y_gpu), 123.0);
122}
123
124TEST(CudaVector, Norm) {
125 Vector x(3);
126 x << 1, 2, 3;
127 ContextImpl context;
128 std::string message;
129 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
130 CudaVector x_gpu(&context, 10);
131 x_gpu.CopyFromCpu(x);
132
133 EXPECT_NEAR(x_gpu.Norm(),
134 sqrt(1.0 + 4.0 + 9.0),
135 std::numeric_limits<double>::epsilon());
136
137 EXPECT_NEAR(Norm(x_gpu),
138 sqrt(1.0 + 4.0 + 9.0),
139 std::numeric_limits<double>::epsilon());
140}
141
142TEST(CudaVector, SetZero) {
143 Vector x(4);
144 x << 1, 1, 1, 1;
145 ContextImpl context;
146 std::string message;
147 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
148 CudaVector x_gpu(&context, 10);
149 x_gpu.CopyFromCpu(x);
150
151 EXPECT_NEAR(x_gpu.Norm(), 2.0, std::numeric_limits<double>::epsilon());
152
153 x_gpu.SetZero();
154 EXPECT_NEAR(x_gpu.Norm(), 0.0, std::numeric_limits<double>::epsilon());
155
156 x_gpu.CopyFromCpu(x);
157 EXPECT_NEAR(x_gpu.Norm(), 2.0, std::numeric_limits<double>::epsilon());
158 SetZero(x_gpu);
159 EXPECT_NEAR(x_gpu.Norm(), 0.0, std::numeric_limits<double>::epsilon());
160}
161
162TEST(CudaVector, Resize) {
163 ContextImpl context;
164 std::string message;
165 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
166 CudaVector x_gpu(&context, 10);
167 EXPECT_EQ(x_gpu.num_rows(), 10);
168 x_gpu.Resize(4);
169 EXPECT_EQ(x_gpu.num_rows(), 4);
170}
171
172TEST(CudaVector, Axpy) {
173 Vector x(4);
174 Vector y(4);
175 x << 1, 1, 1, 1;
176 y << 100, 10, 1, 0;
177 ContextImpl context;
178 std::string message;
179 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
180 CudaVector x_gpu(&context, 4);
181 CudaVector y_gpu(&context, 4);
182 x_gpu.CopyFromCpu(x);
183 y_gpu.CopyFromCpu(y);
184
185 x_gpu.Axpby(2.0, y_gpu, 1.0);
186 Vector result;
187 Vector expected(4);
188 expected << 201, 21, 3, 1;
189 x_gpu.CopyTo(&result);
190 EXPECT_EQ(result, expected);
191}
192
193TEST(CudaVector, AxpbyBEquals1) {
194 Vector x(4);
195 Vector y(4);
196 x << 1, 1, 1, 1;
197 y << 100, 10, 1, 0;
198 ContextImpl context;
199 std::string message;
200 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
201 CudaVector x_gpu(&context, 4);
202 CudaVector y_gpu(&context, 4);
203 x_gpu.CopyFromCpu(x);
204 y_gpu.CopyFromCpu(y);
205
206 x_gpu.Axpby(2.0, y_gpu, 1.0);
207 Vector result;
208 Vector expected(4);
209 expected << 201, 21, 3, 1;
210 x_gpu.CopyTo(&result);
211 EXPECT_EQ(result, expected);
212}
213
214TEST(CudaVector, AxpbyMemberFunctionBNotEqual1) {
215 Vector x(4);
216 Vector y(4);
217 x << 1, 1, 1, 1;
218 y << 100, 10, 1, 0;
219 ContextImpl context;
220 std::string message;
221 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
222 CudaVector x_gpu(&context, 4);
223 CudaVector y_gpu(&context, 4);
224 x_gpu.CopyFromCpu(x);
225 y_gpu.CopyFromCpu(y);
226
227 x_gpu.Axpby(2.0, y_gpu, 3.0);
228 Vector result;
229 Vector expected(4);
230 expected << 203, 23, 5, 3;
231 x_gpu.CopyTo(&result);
232 EXPECT_EQ(result, expected);
233}
234
235TEST(CudaVector, AxpbyMemberFunctionBEqual1) {
236 Vector x(4);
237 Vector y(4);
238 x << 1, 1, 1, 1;
239 y << 100, 10, 1, 0;
240 ContextImpl context;
241 std::string message;
242 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
243 CudaVector x_gpu(&context, 4);
244 CudaVector y_gpu(&context, 4);
245 x_gpu.CopyFromCpu(x);
246 y_gpu.CopyFromCpu(y);
247
248 x_gpu.Axpby(2.0, y_gpu, 1.0);
249 Vector result;
250 Vector expected(4);
251 expected << 201, 21, 3, 1;
252 x_gpu.CopyTo(&result);
253 EXPECT_EQ(result, expected);
254}
255
256TEST(CudaVector, AxpbyMemberXAliasesY) {
257 Vector x(4);
258 x << 100, 10, 1, 0;
259 ContextImpl context;
260 std::string message;
261 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
262 CudaVector x_gpu(&context, 4);
263 CudaVector y_gpu(&context, 4);
264 x_gpu.CopyFromCpu(x);
265 y_gpu.SetZero();
266
267 x_gpu.Axpby(2.0, x_gpu, 1.0);
268 Vector result;
269 Vector expected(4);
270 expected << 300, 30, 3, 0;
271 x_gpu.CopyTo(&result);
272 EXPECT_EQ(result, expected);
273}
274
275TEST(CudaVector, AxpbyNonMemberMethodNoAliases) {
276 Vector x(4);
277 Vector y(4);
278 x << 1, 1, 1, 1;
279 y << 100, 10, 1, 0;
280 ContextImpl context;
281 std::string message;
282 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
283 CudaVector x_gpu(&context, 4);
284 CudaVector y_gpu(&context, 4);
285 CudaVector z_gpu(&context, 4);
286 x_gpu.CopyFromCpu(x);
287 y_gpu.CopyFromCpu(y);
288 z_gpu.Resize(4);
289 z_gpu.SetZero();
290
291 Axpby(2.0, x_gpu, 3.0, y_gpu, z_gpu);
292 Vector result;
293 Vector expected(4);
294 expected << 302, 32, 5, 2;
295 z_gpu.CopyTo(&result);
296 EXPECT_EQ(result, expected);
297}
298
299TEST(CudaVector, AxpbyNonMemberMethodXAliasesY) {
300 Vector x(4);
301 x << 100, 10, 1, 0;
302 ContextImpl context;
303 std::string message;
304 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
305 CudaVector x_gpu(&context, 4);
306 CudaVector z_gpu(&context, 4);
307 x_gpu.CopyFromCpu(x);
308 z_gpu.SetZero();
309
310 Axpby(2.0, x_gpu, 3.0, x_gpu, z_gpu);
311 Vector result;
312 Vector expected(4);
313 expected << 500, 50, 5, 0;
314 z_gpu.CopyTo(&result);
315 EXPECT_EQ(result, expected);
316}
317
318TEST(CudaVector, AxpbyNonMemberMethodXAliasesZ) {
319 Vector x(4);
320 Vector y(4);
321 x << 1, 1, 1, 1;
322 y << 100, 10, 1, 0;
323 ContextImpl context;
324 std::string message;
325 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
326 CudaVector x_gpu(&context, 10);
327 CudaVector y_gpu(&context, 10);
328 x_gpu.CopyFromCpu(x);
329 y_gpu.CopyFromCpu(y);
330
331 Axpby(2.0, x_gpu, 3.0, y_gpu, x_gpu);
332 Vector result;
333 Vector expected(4);
334 expected << 302, 32, 5, 2;
335 x_gpu.CopyTo(&result);
336 EXPECT_EQ(result, expected);
337}
338
339TEST(CudaVector, AxpbyNonMemberMethodYAliasesZ) {
340 Vector x(4);
341 Vector y(4);
342 x << 1, 1, 1, 1;
343 y << 100, 10, 1, 0;
344 ContextImpl context;
345 std::string message;
346 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
347 CudaVector x_gpu(&context, 4);
348 CudaVector y_gpu(&context, 4);
349 x_gpu.CopyFromCpu(x);
350 y_gpu.CopyFromCpu(y);
351
352 Axpby(2.0, x_gpu, 3.0, y_gpu, y_gpu);
353 Vector result;
354 Vector expected(4);
355 expected << 302, 32, 5, 2;
356 y_gpu.CopyTo(&result);
357 EXPECT_EQ(result, expected);
358}
359
360TEST(CudaVector, AxpbyNonMemberMethodXAliasesYAliasesZ) {
361 Vector x(4);
362 x << 100, 10, 1, 0;
363 ContextImpl context;
364 std::string message;
365 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
366 CudaVector x_gpu(&context, 10);
367 x_gpu.CopyFromCpu(x);
368
369 Axpby(2.0, x_gpu, 3.0, x_gpu, x_gpu);
370 Vector result;
371 Vector expected(4);
372 expected << 500, 50, 5, 0;
373 x_gpu.CopyTo(&result);
374 EXPECT_EQ(result, expected);
375}
376
377TEST(CudaVector, DtDxpy) {
378 Vector x(4);
379 Vector y(4);
380 Vector D(4);
381 x << 1, 2, 3, 4;
382 y << 100, 10, 1, 0;
383 D << 4, 3, 2, 1;
384 ContextImpl context;
385 std::string message;
386 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
387 CudaVector x_gpu(&context, 4);
388 CudaVector y_gpu(&context, 4);
389 CudaVector D_gpu(&context, 4);
390 x_gpu.CopyFromCpu(x);
391 y_gpu.CopyFromCpu(y);
392 D_gpu.CopyFromCpu(D);
393
394 y_gpu.DtDxpy(D_gpu, x_gpu);
395 Vector result;
396 Vector expected(4);
397 expected << 116, 28, 13, 4;
398 y_gpu.CopyTo(&result);
399 EXPECT_EQ(result, expected);
400}
401
402TEST(CudaVector, Scale) {
403 Vector x(4);
404 x << 1, 2, 3, 4;
405 ContextImpl context;
406 std::string message;
407 CHECK(context.InitCuda(&message)) << "InitCuda() failed because: " << message;
408 CudaVector x_gpu(&context, 4);
409 x_gpu.CopyFromCpu(x);
410
411 x_gpu.Scale(-3.0);
412
413 Vector result;
414 Vector expected(4);
415 expected << -3.0, -6.0, -9.0, -12.0;
416 x_gpu.CopyTo(&result);
417 EXPECT_EQ(result, expected);
418}
419
420#endif // CERES_NO_CUDA
421
422} // namespace internal
423} // namespace ceres