linear_least_squares_problems.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. // 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: sameeragarwal@google.com (Sameer Agarwal)
  30. #include "ceres/linear_least_squares_problems.h"
  31. #include <cstdio>
  32. #include <memory>
  33. #include <string>
  34. #include <vector>
  35. #include "ceres/block_sparse_matrix.h"
  36. #include "ceres/block_structure.h"
  37. #include "ceres/casts.h"
  38. #include "ceres/file.h"
  39. #include "ceres/stringprintf.h"
  40. #include "ceres/triplet_sparse_matrix.h"
  41. #include "ceres/types.h"
  42. #include "glog/logging.h"
  43. namespace ceres::internal {
  44. std::unique_ptr<LinearLeastSquaresProblem>
  45. CreateLinearLeastSquaresProblemFromId(int id) {
  46. switch (id) {
  47. case 0:
  48. return LinearLeastSquaresProblem0();
  49. case 1:
  50. return LinearLeastSquaresProblem1();
  51. case 2:
  52. return LinearLeastSquaresProblem2();
  53. case 3:
  54. return LinearLeastSquaresProblem3();
  55. case 4:
  56. return LinearLeastSquaresProblem4();
  57. case 5:
  58. return LinearLeastSquaresProblem5();
  59. case 6:
  60. return LinearLeastSquaresProblem6();
  61. default:
  62. LOG(FATAL) << "Unknown problem id requested " << id;
  63. }
  64. return nullptr;
  65. }
  66. /*
  67. A = [1 2]
  68. [3 4]
  69. [6 -10]
  70. b = [ 8
  71. 18
  72. -18]
  73. x = [2
  74. 3]
  75. D = [1
  76. 2]
  77. x_D = [1.78448275;
  78. 2.82327586;]
  79. */
  80. std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem0() {
  81. auto problem = std::make_unique<LinearLeastSquaresProblem>();
  82. auto A = std::make_unique<TripletSparseMatrix>(3, 2, 6);
  83. problem->b = std::make_unique<double[]>(3);
  84. problem->D = std::make_unique<double[]>(2);
  85. problem->x = std::make_unique<double[]>(2);
  86. problem->x_D = std::make_unique<double[]>(2);
  87. int* Ai = A->mutable_rows();
  88. int* Aj = A->mutable_cols();
  89. double* Ax = A->mutable_values();
  90. int counter = 0;
  91. for (int i = 0; i < 3; ++i) {
  92. for (int j = 0; j < 2; ++j) {
  93. Ai[counter] = i;
  94. Aj[counter] = j;
  95. ++counter;
  96. }
  97. }
  98. Ax[0] = 1.;
  99. Ax[1] = 2.;
  100. Ax[2] = 3.;
  101. Ax[3] = 4.;
  102. Ax[4] = 6;
  103. Ax[5] = -10;
  104. A->set_num_nonzeros(6);
  105. problem->A = std::move(A);
  106. problem->b[0] = 8;
  107. problem->b[1] = 18;
  108. problem->b[2] = -18;
  109. problem->x[0] = 2.0;
  110. problem->x[1] = 3.0;
  111. problem->D[0] = 1;
  112. problem->D[1] = 2;
  113. problem->x_D[0] = 1.78448275;
  114. problem->x_D[1] = 2.82327586;
  115. return problem;
  116. }
  117. /*
  118. A = [1 0 | 2 0 0
  119. 3 0 | 0 4 0
  120. 0 5 | 0 0 6
  121. 0 7 | 8 0 0
  122. 0 9 | 1 0 0
  123. 0 0 | 1 1 1]
  124. b = [0
  125. 1
  126. 2
  127. 3
  128. 4
  129. 5]
  130. c = A'* b = [ 3
  131. 67
  132. 33
  133. 9
  134. 17]
  135. A'A = [10 0 2 12 0
  136. 0 155 65 0 30
  137. 2 65 70 1 1
  138. 12 0 1 17 1
  139. 0 30 1 1 37]
  140. cond(A'A) = 200.36
  141. S = [ 42.3419 -1.4000 -11.5806
  142. -1.4000 2.6000 1.0000
  143. -11.5806 1.0000 31.1935]
  144. r = [ 4.3032
  145. 5.4000
  146. 4.0323]
  147. S\r = [ 0.2102
  148. 2.1367
  149. 0.1388]
  150. A\b = [-2.3061
  151. 0.3172
  152. 0.2102
  153. 2.1367
  154. 0.1388]
  155. */
  156. // The following two functions create a TripletSparseMatrix and a
  157. // BlockSparseMatrix version of this problem.
  158. // TripletSparseMatrix version.
  159. std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem1() {
  160. int num_rows = 6;
  161. int num_cols = 5;
  162. auto problem = std::make_unique<LinearLeastSquaresProblem>();
  163. auto A = std::make_unique<TripletSparseMatrix>(
  164. num_rows, num_cols, num_rows * num_cols);
  165. problem->b = std::make_unique<double[]>(num_rows);
  166. problem->D = std::make_unique<double[]>(num_cols);
  167. problem->num_eliminate_blocks = 2;
  168. problem->x = std::make_unique<double[]>(num_cols);
  169. problem->x[0] = -2.3061;
  170. problem->x[1] = 0.3172;
  171. problem->x[2] = 0.2102;
  172. problem->x[3] = 2.1367;
  173. problem->x[4] = 0.1388;
  174. int* rows = A->mutable_rows();
  175. int* cols = A->mutable_cols();
  176. double* values = A->mutable_values();
  177. int nnz = 0;
  178. // Row 1
  179. {
  180. rows[nnz] = 0;
  181. cols[nnz] = 0;
  182. values[nnz++] = 1;
  183. rows[nnz] = 0;
  184. cols[nnz] = 2;
  185. values[nnz++] = 2;
  186. }
  187. // Row 2
  188. {
  189. rows[nnz] = 1;
  190. cols[nnz] = 0;
  191. values[nnz++] = 3;
  192. rows[nnz] = 1;
  193. cols[nnz] = 3;
  194. values[nnz++] = 4;
  195. }
  196. // Row 3
  197. {
  198. rows[nnz] = 2;
  199. cols[nnz] = 1;
  200. values[nnz++] = 5;
  201. rows[nnz] = 2;
  202. cols[nnz] = 4;
  203. values[nnz++] = 6;
  204. }
  205. // Row 4
  206. {
  207. rows[nnz] = 3;
  208. cols[nnz] = 1;
  209. values[nnz++] = 7;
  210. rows[nnz] = 3;
  211. cols[nnz] = 2;
  212. values[nnz++] = 8;
  213. }
  214. // Row 5
  215. {
  216. rows[nnz] = 4;
  217. cols[nnz] = 1;
  218. values[nnz++] = 9;
  219. rows[nnz] = 4;
  220. cols[nnz] = 2;
  221. values[nnz++] = 1;
  222. }
  223. // Row 6
  224. {
  225. rows[nnz] = 5;
  226. cols[nnz] = 2;
  227. values[nnz++] = 1;
  228. rows[nnz] = 5;
  229. cols[nnz] = 3;
  230. values[nnz++] = 1;
  231. rows[nnz] = 5;
  232. cols[nnz] = 4;
  233. values[nnz++] = 1;
  234. }
  235. A->set_num_nonzeros(nnz);
  236. CHECK(A->IsValid());
  237. problem->A = std::move(A);
  238. for (int i = 0; i < num_cols; ++i) {
  239. problem->D.get()[i] = 1;
  240. }
  241. for (int i = 0; i < num_rows; ++i) {
  242. problem->b.get()[i] = i;
  243. }
  244. return problem;
  245. }
  246. // BlockSparseMatrix version
  247. std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem2() {
  248. int num_rows = 6;
  249. int num_cols = 5;
  250. auto problem = std::make_unique<LinearLeastSquaresProblem>();
  251. problem->b = std::make_unique<double[]>(num_rows);
  252. problem->D = std::make_unique<double[]>(num_cols);
  253. problem->num_eliminate_blocks = 2;
  254. problem->x = std::make_unique<double[]>(num_cols);
  255. problem->x[0] = -2.3061;
  256. problem->x[1] = 0.3172;
  257. problem->x[2] = 0.2102;
  258. problem->x[3] = 2.1367;
  259. problem->x[4] = 0.1388;
  260. auto* bs = new CompressedRowBlockStructure;
  261. auto values = std::make_unique<double[]>(num_rows * num_cols);
  262. for (int c = 0; c < num_cols; ++c) {
  263. bs->cols.emplace_back();
  264. bs->cols.back().size = 1;
  265. bs->cols.back().position = c;
  266. }
  267. int nnz = 0;
  268. // Row 1
  269. {
  270. values[nnz++] = 1;
  271. values[nnz++] = 2;
  272. bs->rows.emplace_back();
  273. CompressedRow& row = bs->rows.back();
  274. row.block.size = 1;
  275. row.block.position = 0;
  276. row.cells.emplace_back(0, 0);
  277. row.cells.emplace_back(2, 1);
  278. }
  279. // Row 2
  280. {
  281. values[nnz++] = 3;
  282. values[nnz++] = 4;
  283. bs->rows.emplace_back();
  284. CompressedRow& row = bs->rows.back();
  285. row.block.size = 1;
  286. row.block.position = 1;
  287. row.cells.emplace_back(0, 2);
  288. row.cells.emplace_back(3, 3);
  289. }
  290. // Row 3
  291. {
  292. values[nnz++] = 5;
  293. values[nnz++] = 6;
  294. bs->rows.emplace_back();
  295. CompressedRow& row = bs->rows.back();
  296. row.block.size = 1;
  297. row.block.position = 2;
  298. row.cells.emplace_back(1, 4);
  299. row.cells.emplace_back(4, 5);
  300. }
  301. // Row 4
  302. {
  303. values[nnz++] = 7;
  304. values[nnz++] = 8;
  305. bs->rows.emplace_back();
  306. CompressedRow& row = bs->rows.back();
  307. row.block.size = 1;
  308. row.block.position = 3;
  309. row.cells.emplace_back(1, 6);
  310. row.cells.emplace_back(2, 7);
  311. }
  312. // Row 5
  313. {
  314. values[nnz++] = 9;
  315. values[nnz++] = 1;
  316. bs->rows.emplace_back();
  317. CompressedRow& row = bs->rows.back();
  318. row.block.size = 1;
  319. row.block.position = 4;
  320. row.cells.emplace_back(1, 8);
  321. row.cells.emplace_back(2, 9);
  322. }
  323. // Row 6
  324. {
  325. values[nnz++] = 1;
  326. values[nnz++] = 1;
  327. values[nnz++] = 1;
  328. bs->rows.emplace_back();
  329. CompressedRow& row = bs->rows.back();
  330. row.block.size = 1;
  331. row.block.position = 5;
  332. row.cells.emplace_back(2, 10);
  333. row.cells.emplace_back(3, 11);
  334. row.cells.emplace_back(4, 12);
  335. }
  336. auto A = std::make_unique<BlockSparseMatrix>(bs);
  337. memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
  338. for (int i = 0; i < num_cols; ++i) {
  339. problem->D.get()[i] = 1;
  340. }
  341. for (int i = 0; i < num_rows; ++i) {
  342. problem->b.get()[i] = i;
  343. }
  344. problem->A = std::move(A);
  345. return problem;
  346. }
  347. /*
  348. A = [1 0
  349. 3 0
  350. 0 5
  351. 0 7
  352. 0 9
  353. 0 0]
  354. b = [0
  355. 1
  356. 2
  357. 3
  358. 4
  359. 5]
  360. */
  361. // BlockSparseMatrix version
  362. std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem3() {
  363. int num_rows = 5;
  364. int num_cols = 2;
  365. auto problem = std::make_unique<LinearLeastSquaresProblem>();
  366. problem->b = std::make_unique<double[]>(num_rows);
  367. problem->D = std::make_unique<double[]>(num_cols);
  368. problem->num_eliminate_blocks = 2;
  369. auto* bs = new CompressedRowBlockStructure;
  370. auto values = std::make_unique<double[]>(num_rows * num_cols);
  371. for (int c = 0; c < num_cols; ++c) {
  372. bs->cols.emplace_back();
  373. bs->cols.back().size = 1;
  374. bs->cols.back().position = c;
  375. }
  376. int nnz = 0;
  377. // Row 1
  378. {
  379. values[nnz++] = 1;
  380. bs->rows.emplace_back();
  381. CompressedRow& row = bs->rows.back();
  382. row.block.size = 1;
  383. row.block.position = 0;
  384. row.cells.emplace_back(0, 0);
  385. }
  386. // Row 2
  387. {
  388. values[nnz++] = 3;
  389. bs->rows.emplace_back();
  390. CompressedRow& row = bs->rows.back();
  391. row.block.size = 1;
  392. row.block.position = 1;
  393. row.cells.emplace_back(0, 1);
  394. }
  395. // Row 3
  396. {
  397. values[nnz++] = 5;
  398. bs->rows.emplace_back();
  399. CompressedRow& row = bs->rows.back();
  400. row.block.size = 1;
  401. row.block.position = 2;
  402. row.cells.emplace_back(1, 2);
  403. }
  404. // Row 4
  405. {
  406. values[nnz++] = 7;
  407. bs->rows.emplace_back();
  408. CompressedRow& row = bs->rows.back();
  409. row.block.size = 1;
  410. row.block.position = 3;
  411. row.cells.emplace_back(1, 3);
  412. }
  413. // Row 5
  414. {
  415. values[nnz++] = 9;
  416. bs->rows.emplace_back();
  417. CompressedRow& row = bs->rows.back();
  418. row.block.size = 1;
  419. row.block.position = 4;
  420. row.cells.emplace_back(1, 4);
  421. }
  422. auto A = std::make_unique<BlockSparseMatrix>(bs);
  423. memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
  424. for (int i = 0; i < num_cols; ++i) {
  425. problem->D.get()[i] = 1;
  426. }
  427. for (int i = 0; i < num_rows; ++i) {
  428. problem->b.get()[i] = i;
  429. }
  430. problem->A = std::move(A);
  431. return problem;
  432. }
  433. /*
  434. A = [1 2 0 0 0 1 1
  435. 1 4 0 0 0 5 6
  436. 0 0 9 0 0 3 1]
  437. b = [0
  438. 1
  439. 2]
  440. */
  441. // BlockSparseMatrix version
  442. //
  443. // This problem has the unique property that it has two different
  444. // sized f-blocks, but only one of them occurs in the rows involving
  445. // the one e-block. So performing Schur elimination on this problem
  446. // tests the Schur Eliminator's ability to handle non-e-block rows
  447. // correctly when their structure does not conform to the static
  448. // structure determined by DetectStructure.
  449. //
  450. // NOTE: This problem is too small and rank deficient to be solved without
  451. // the diagonal regularization.
  452. std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem4() {
  453. int num_rows = 3;
  454. int num_cols = 7;
  455. auto problem = std::make_unique<LinearLeastSquaresProblem>();
  456. problem->b = std::make_unique<double[]>(num_rows);
  457. problem->D = std::make_unique<double[]>(num_cols);
  458. problem->num_eliminate_blocks = 1;
  459. auto* bs = new CompressedRowBlockStructure;
  460. auto values = std::make_unique<double[]>(num_rows * num_cols);
  461. // Column block structure
  462. bs->cols.emplace_back();
  463. bs->cols.back().size = 2;
  464. bs->cols.back().position = 0;
  465. bs->cols.emplace_back();
  466. bs->cols.back().size = 3;
  467. bs->cols.back().position = 2;
  468. bs->cols.emplace_back();
  469. bs->cols.back().size = 2;
  470. bs->cols.back().position = 5;
  471. int nnz = 0;
  472. // Row 1 & 2
  473. {
  474. bs->rows.emplace_back();
  475. CompressedRow& row = bs->rows.back();
  476. row.block.size = 2;
  477. row.block.position = 0;
  478. row.cells.emplace_back(0, nnz);
  479. values[nnz++] = 1;
  480. values[nnz++] = 2;
  481. values[nnz++] = 1;
  482. values[nnz++] = 4;
  483. row.cells.emplace_back(2, nnz);
  484. values[nnz++] = 1;
  485. values[nnz++] = 1;
  486. values[nnz++] = 5;
  487. values[nnz++] = 6;
  488. }
  489. // Row 3
  490. {
  491. bs->rows.emplace_back();
  492. CompressedRow& row = bs->rows.back();
  493. row.block.size = 1;
  494. row.block.position = 2;
  495. row.cells.emplace_back(1, nnz);
  496. values[nnz++] = 9;
  497. values[nnz++] = 0;
  498. values[nnz++] = 0;
  499. row.cells.emplace_back(2, nnz);
  500. values[nnz++] = 3;
  501. values[nnz++] = 1;
  502. }
  503. auto A = std::make_unique<BlockSparseMatrix>(bs);
  504. memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
  505. for (int i = 0; i < num_cols; ++i) {
  506. problem->D.get()[i] = (i + 1) * 100;
  507. }
  508. for (int i = 0; i < num_rows; ++i) {
  509. problem->b.get()[i] = i;
  510. }
  511. problem->A = std::move(A);
  512. return problem;
  513. }
  514. /*
  515. A problem with block-diagonal F'F.
  516. A = [1 0 | 0 0 2
  517. 3 0 | 0 0 4
  518. 0 -1 | 0 1 0
  519. 0 -3 | 0 1 0
  520. 0 -1 | 3 0 0
  521. 0 -2 | 1 0 0]
  522. b = [0
  523. 1
  524. 2
  525. 3
  526. 4
  527. 5]
  528. c = A'* b = [ 22
  529. -25
  530. 17
  531. 7
  532. 4]
  533. A'A = [10 0 0 0 10
  534. 0 15 -5 -4 0
  535. 0 -5 10 0 0
  536. 0 -4 0 2 0
  537. 10 0 0 0 20]
  538. cond(A'A) = 41.402
  539. S = [ 8.3333 -1.3333 0
  540. -1.3333 0.9333 0
  541. 0 0 10.0000]
  542. r = [ 8.6667
  543. -1.6667
  544. 1.0000]
  545. S\r = [ 0.9778
  546. -0.3889
  547. 0.1000]
  548. A\b = [ 0.2
  549. -1.4444
  550. 0.9777
  551. -0.3888
  552. 0.1]
  553. */
  554. std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem5() {
  555. int num_rows = 6;
  556. int num_cols = 5;
  557. auto problem = std::make_unique<LinearLeastSquaresProblem>();
  558. problem->b = std::make_unique<double[]>(num_rows);
  559. problem->D = std::make_unique<double[]>(num_cols);
  560. problem->num_eliminate_blocks = 2;
  561. // TODO: add x
  562. problem->x = std::make_unique<double[]>(num_cols);
  563. problem->x[0] = 0.2;
  564. problem->x[1] = -1.4444;
  565. problem->x[2] = 0.9777;
  566. problem->x[3] = -0.3888;
  567. problem->x[4] = 0.1;
  568. auto* bs = new CompressedRowBlockStructure;
  569. auto values = std::make_unique<double[]>(num_rows * num_cols);
  570. for (int c = 0; c < num_cols; ++c) {
  571. bs->cols.emplace_back();
  572. bs->cols.back().size = 1;
  573. bs->cols.back().position = c;
  574. }
  575. int nnz = 0;
  576. // Row 1
  577. {
  578. values[nnz++] = -1;
  579. values[nnz++] = 2;
  580. bs->rows.emplace_back();
  581. CompressedRow& row = bs->rows.back();
  582. row.block.size = 1;
  583. row.block.position = 0;
  584. row.cells.emplace_back(0, 0);
  585. row.cells.emplace_back(4, 1);
  586. }
  587. // Row 2
  588. {
  589. values[nnz++] = 3;
  590. values[nnz++] = 4;
  591. bs->rows.emplace_back();
  592. CompressedRow& row = bs->rows.back();
  593. row.block.size = 1;
  594. row.block.position = 1;
  595. row.cells.emplace_back(0, 2);
  596. row.cells.emplace_back(4, 3);
  597. }
  598. // Row 3
  599. {
  600. values[nnz++] = -1;
  601. values[nnz++] = 1;
  602. bs->rows.emplace_back();
  603. CompressedRow& row = bs->rows.back();
  604. row.block.size = 1;
  605. row.block.position = 2;
  606. row.cells.emplace_back(1, 4);
  607. row.cells.emplace_back(3, 5);
  608. }
  609. // Row 4
  610. {
  611. values[nnz++] = -3;
  612. values[nnz++] = 1;
  613. bs->rows.emplace_back();
  614. CompressedRow& row = bs->rows.back();
  615. row.block.size = 1;
  616. row.block.position = 3;
  617. row.cells.emplace_back(1, 6);
  618. row.cells.emplace_back(3, 7);
  619. }
  620. // Row 5
  621. {
  622. values[nnz++] = -1;
  623. values[nnz++] = 3;
  624. bs->rows.emplace_back();
  625. CompressedRow& row = bs->rows.back();
  626. row.block.size = 1;
  627. row.block.position = 4;
  628. row.cells.emplace_back(1, 8);
  629. row.cells.emplace_back(2, 9);
  630. }
  631. // Row 6
  632. {
  633. // values[nnz++] = 2;
  634. values[nnz++] = -2;
  635. values[nnz++] = 1;
  636. bs->rows.emplace_back();
  637. CompressedRow& row = bs->rows.back();
  638. row.block.size = 1;
  639. row.block.position = 5;
  640. // row.cells.emplace_back(0, 10);
  641. row.cells.emplace_back(1, 10);
  642. row.cells.emplace_back(2, 11);
  643. }
  644. auto A = std::make_unique<BlockSparseMatrix>(bs);
  645. memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
  646. for (int i = 0; i < num_cols; ++i) {
  647. problem->D.get()[i] = 1;
  648. }
  649. for (int i = 0; i < num_rows; ++i) {
  650. problem->b.get()[i] = i;
  651. }
  652. problem->A = std::move(A);
  653. return problem;
  654. }
  655. /*
  656. A = [1 2 0 0 0 1 1
  657. 1 4 0 0 0 5 6
  658. 3 4 0 0 0 7 8
  659. 5 6 0 0 0 9 0
  660. 0 0 9 0 0 3 1]
  661. b = [0
  662. 1
  663. 2
  664. 3
  665. 4]
  666. */
  667. // BlockSparseMatrix version
  668. //
  669. // This problem has the unique property that it has two different
  670. // sized f-blocks, but only one of them occurs in the rows involving
  671. // the one e-block. So performing Schur elimination on this problem
  672. // tests the Schur Eliminator's ability to handle non-e-block rows
  673. // correctly when their structure does not conform to the static
  674. // structure determined by DetectStructure.
  675. //
  676. // Additionally, this problem has the first row of the last row block of E being
  677. // larger than number of row blocks in E
  678. //
  679. // NOTE: This problem is too small and rank deficient to be solved without
  680. // the diagonal regularization.
  681. std::unique_ptr<LinearLeastSquaresProblem> LinearLeastSquaresProblem6() {
  682. int num_rows = 5;
  683. int num_cols = 7;
  684. auto problem = std::make_unique<LinearLeastSquaresProblem>();
  685. problem->b = std::make_unique<double[]>(num_rows);
  686. problem->D = std::make_unique<double[]>(num_cols);
  687. problem->num_eliminate_blocks = 1;
  688. auto* bs = new CompressedRowBlockStructure;
  689. auto values = std::make_unique<double[]>(num_rows * num_cols);
  690. // Column block structure
  691. bs->cols.emplace_back();
  692. bs->cols.back().size = 2;
  693. bs->cols.back().position = 0;
  694. bs->cols.emplace_back();
  695. bs->cols.back().size = 3;
  696. bs->cols.back().position = 2;
  697. bs->cols.emplace_back();
  698. bs->cols.back().size = 2;
  699. bs->cols.back().position = 5;
  700. int nnz = 0;
  701. // Row 1 & 2
  702. {
  703. bs->rows.emplace_back();
  704. CompressedRow& row = bs->rows.back();
  705. row.block.size = 2;
  706. row.block.position = 0;
  707. row.cells.emplace_back(0, nnz);
  708. values[nnz++] = 1;
  709. values[nnz++] = 2;
  710. values[nnz++] = 1;
  711. values[nnz++] = 4;
  712. row.cells.emplace_back(2, nnz);
  713. values[nnz++] = 1;
  714. values[nnz++] = 1;
  715. values[nnz++] = 5;
  716. values[nnz++] = 6;
  717. }
  718. // Row 3 & 4
  719. {
  720. bs->rows.emplace_back();
  721. CompressedRow& row = bs->rows.back();
  722. row.block.size = 2;
  723. row.block.position = 2;
  724. row.cells.emplace_back(0, nnz);
  725. values[nnz++] = 3;
  726. values[nnz++] = 4;
  727. values[nnz++] = 5;
  728. values[nnz++] = 6;
  729. row.cells.emplace_back(2, nnz);
  730. values[nnz++] = 7;
  731. values[nnz++] = 8;
  732. values[nnz++] = 9;
  733. values[nnz++] = 0;
  734. }
  735. // Row 5
  736. {
  737. bs->rows.emplace_back();
  738. CompressedRow& row = bs->rows.back();
  739. row.block.size = 1;
  740. row.block.position = 4;
  741. row.cells.emplace_back(1, nnz);
  742. values[nnz++] = 9;
  743. values[nnz++] = 0;
  744. values[nnz++] = 0;
  745. row.cells.emplace_back(2, nnz);
  746. values[nnz++] = 3;
  747. values[nnz++] = 1;
  748. }
  749. auto A = std::make_unique<BlockSparseMatrix>(bs);
  750. memcpy(A->mutable_values(), values.get(), nnz * sizeof(*A->values()));
  751. for (int i = 0; i < num_cols; ++i) {
  752. problem->D.get()[i] = (i + 1) * 100;
  753. }
  754. for (int i = 0; i < num_rows; ++i) {
  755. problem->b.get()[i] = i;
  756. }
  757. problem->A = std::move(A);
  758. return problem;
  759. }
  760. namespace {
  761. bool DumpLinearLeastSquaresProblemToConsole(const SparseMatrix* A,
  762. const double* D,
  763. const double* b,
  764. const double* x,
  765. int /*num_eliminate_blocks*/) {
  766. CHECK(A != nullptr);
  767. Matrix AA;
  768. A->ToDenseMatrix(&AA);
  769. LOG(INFO) << "A^T: \n" << AA.transpose();
  770. if (D != nullptr) {
  771. LOG(INFO) << "A's appended diagonal:\n" << ConstVectorRef(D, A->num_cols());
  772. }
  773. if (b != nullptr) {
  774. LOG(INFO) << "b: \n" << ConstVectorRef(b, A->num_rows());
  775. }
  776. if (x != nullptr) {
  777. LOG(INFO) << "x: \n" << ConstVectorRef(x, A->num_cols());
  778. }
  779. return true;
  780. }
  781. void WriteArrayToFileOrDie(const std::string& filename,
  782. const double* x,
  783. const int size) {
  784. CHECK(x != nullptr);
  785. VLOG(2) << "Writing array to: " << filename;
  786. FILE* fptr = fopen(filename.c_str(), "w");
  787. CHECK(fptr != nullptr);
  788. for (int i = 0; i < size; ++i) {
  789. fprintf(fptr, "%17f\n", x[i]);
  790. }
  791. fclose(fptr);
  792. }
  793. bool DumpLinearLeastSquaresProblemToTextFile(const std::string& filename_base,
  794. const SparseMatrix* A,
  795. const double* D,
  796. const double* b,
  797. const double* x,
  798. int /*num_eliminate_blocks*/) {
  799. CHECK(A != nullptr);
  800. LOG(INFO) << "writing to: " << filename_base << "*";
  801. std::string matlab_script;
  802. StringAppendF(&matlab_script,
  803. "function lsqp = load_trust_region_problem()\n");
  804. StringAppendF(&matlab_script, "lsqp.num_rows = %d;\n", A->num_rows());
  805. StringAppendF(&matlab_script, "lsqp.num_cols = %d;\n", A->num_cols());
  806. {
  807. std::string filename = filename_base + "_A.txt";
  808. FILE* fptr = fopen(filename.c_str(), "w");
  809. CHECK(fptr != nullptr);
  810. A->ToTextFile(fptr);
  811. fclose(fptr);
  812. StringAppendF(
  813. &matlab_script, "tmp = load('%s', '-ascii');\n", filename.c_str());
  814. StringAppendF(
  815. &matlab_script,
  816. "lsqp.A = sparse(tmp(:, 1) + 1, tmp(:, 2) + 1, tmp(:, 3), %d, %d);\n",
  817. A->num_rows(),
  818. A->num_cols());
  819. }
  820. if (D != nullptr) {
  821. std::string filename = filename_base + "_D.txt";
  822. WriteArrayToFileOrDie(filename, D, A->num_cols());
  823. StringAppendF(
  824. &matlab_script, "lsqp.D = load('%s', '-ascii');\n", filename.c_str());
  825. }
  826. if (b != nullptr) {
  827. std::string filename = filename_base + "_b.txt";
  828. WriteArrayToFileOrDie(filename, b, A->num_rows());
  829. StringAppendF(
  830. &matlab_script, "lsqp.b = load('%s', '-ascii');\n", filename.c_str());
  831. }
  832. if (x != nullptr) {
  833. std::string filename = filename_base + "_x.txt";
  834. WriteArrayToFileOrDie(filename, x, A->num_cols());
  835. StringAppendF(
  836. &matlab_script, "lsqp.x = load('%s', '-ascii');\n", filename.c_str());
  837. }
  838. std::string matlab_filename = filename_base + ".m";
  839. WriteStringToFileOrDie(matlab_script, matlab_filename);
  840. return true;
  841. }
  842. } // namespace
  843. bool DumpLinearLeastSquaresProblem(const std::string& filename_base,
  844. DumpFormatType dump_format_type,
  845. const SparseMatrix* A,
  846. const double* D,
  847. const double* b,
  848. const double* x,
  849. int num_eliminate_blocks) {
  850. switch (dump_format_type) {
  851. case CONSOLE:
  852. return DumpLinearLeastSquaresProblemToConsole(
  853. A, D, b, x, num_eliminate_blocks);
  854. case TEXTFILE:
  855. return DumpLinearLeastSquaresProblemToTextFile(
  856. filename_base, A, D, b, x, num_eliminate_blocks);
  857. default:
  858. LOG(FATAL) << "Unknown DumpFormatType " << dump_format_type;
  859. }
  860. return true;
  861. }
  862. } // namespace ceres::internal