Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
al_matrix_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
51#include <gtest/gtest.h>
52
53#include <cmath>
54#include <string>
55#include <vector>
56
57#include <al-domain.H>
58#include <al-vector.H>
59#include <al-matrix.H>
60
61using namespace Aleph;
62
63namespace
64{
65 using Domain = AlDomain<int>;
66 using DomainPtr = std::shared_ptr<Domain>;
69
70 // Helper to create a domain with elements 0..n-1
71 Domain make_domain(int n)
72 {
73 Domain d;
74 for (int i = 0; i < n; ++i)
75 d.insert(i);
76 return d;
77 }
78
79 // Helper to create a shared_ptr domain with elements 0..n-1
80 DomainPtr make_shared_domain(int n)
81 {
82 auto d = std::make_shared<Domain>();
83 for (int i = 0; i < n; ++i)
84 d->insert(i);
85 return d;
86 }
87
88 // String domain for testing generic types
90 using StrDomainPtr = std::shared_ptr<StrDomain>;
93
94} // namespace
95
96// =============================================================================
97// Construction Tests
98// =============================================================================
99
101{
102 Domain rows = make_domain(3);
103 Domain cols = make_domain(4);
104
105 Mat m(rows, cols);
106
107 // All entries should be zero
108 for (int r = 0; r < 3; ++r)
109 for (int c = 0; c < 4; ++c)
110 EXPECT_DOUBLE_EQ(m.get_entry(r, c), 0.0);
111}
112
114{
115 Domain rows = make_domain(2);
116 Domain cols = make_domain(3);
117
118 Mat m(rows, cols, {
119 {1.0, 2.0, 3.0},
120 {4.0, 5.0, 6.0}
121 });
122
123 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 1.0);
124 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 2.0);
125 EXPECT_DOUBLE_EQ(m.get_entry(0, 2), 3.0);
126 EXPECT_DOUBLE_EQ(m.get_entry(1, 0), 4.0);
127 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 5.0);
128 EXPECT_DOUBLE_EQ(m.get_entry(1, 2), 6.0);
129}
130
132{
133 Domain rows = make_domain(2);
134 Domain cols = make_domain(2);
135
136 Mat m(rows, cols, {
137 {1.0, 0.0},
138 {0.0, 2.0}
139 });
140
141 // Diagonal matrix - only 2 non-zero entries should be stored
142 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 1.0);
143 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 0.0);
144 EXPECT_DOUBLE_EQ(m.get_entry(1, 0), 0.0);
145 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 2.0);
146}
147
149{
150 Domain rows = make_domain(2);
151 Domain cols = make_domain(3);
152
154 Mat m(rows, cols, {
155 {1.0, 2.0, 3.0} // only 1 row, expected 2
156 }),
157 std::range_error
158 );
159}
160
162{
163 Domain rows = make_domain(2);
164 Domain cols = make_domain(3);
165
167 Mat m(rows, cols, {
168 {1.0, 2.0}, // 2 cols, expected 3
169 {4.0, 5.0, 6.0}
170 }),
171 std::range_error
172 );
173}
174
175// =============================================================================
176// Entry Access and Modification
177// =============================================================================
178
180{
181 Domain rows = make_domain(3);
182 Domain cols = make_domain(3);
183 Mat m(rows, cols);
184
185 m.set_entry(1, 2, 42.5);
186 EXPECT_DOUBLE_EQ(m.get_entry(1, 2), 42.5);
187
188 // Other entries remain zero
189 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 0.0);
190 EXPECT_DOUBLE_EQ(m.get_entry(2, 2), 0.0);
191}
192
194{
195 Domain rows = make_domain(2);
196 Domain cols = make_domain(2);
197 Mat m(rows, cols);
198
199 m.set_entry(0, 0, 5.0);
200 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 5.0);
201
202 m.set_entry(0, 0, 0.0);
203 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 0.0);
204}
205
207{
208 Domain rows = make_domain(2);
209 Domain cols = make_domain(2);
210 Mat m(rows, cols, 1e-7); // epsilon = 1e-7
211
212 m.set_entry(0, 0, 1e-8); // smaller than epsilon
213 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 0.0);
214}
215
217{
218 Domain rows = make_domain(2);
219 Domain cols = make_domain(2);
220 Mat m(rows, cols);
221
222 m.set_entry(0, 1, 10.0);
223 m.set_entry(0, 1, 20.0);
224 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 20.0);
225}
226
227// =============================================================================
228// Epsilon Handling
229// =============================================================================
230
232{
233 Domain rows = make_domain(2);
234 Domain cols = make_domain(2);
235 Mat m(rows, cols);
236
237 EXPECT_DOUBLE_EQ(m.get_epsilon(), 1e-7);
238
239 m.set_epsilon(1e-5);
240 EXPECT_DOUBLE_EQ(m.get_epsilon(), 1e-5);
241}
242
244{
245 Domain rows = make_domain(2);
246 Domain cols = make_domain(2);
247 Mat m(rows, cols);
248
249 EXPECT_THROW(m.set_epsilon(-1.0), std::domain_error);
250}
251
252// =============================================================================
253// Transpose
254// =============================================================================
255
257{
258 Domain rows = make_domain(2);
259 Domain cols = make_domain(3);
260
261 Mat m(rows, cols, {
262 {1.0, 2.0, 3.0},
263 {4.0, 5.0, 6.0}
264 });
265
266 Mat mt = m.transpose();
267
268 // Original: 2x3, Transpose: 3x2
269 EXPECT_DOUBLE_EQ(mt.get_entry(0, 0), 1.0);
270 EXPECT_DOUBLE_EQ(mt.get_entry(1, 0), 2.0);
271 EXPECT_DOUBLE_EQ(mt.get_entry(2, 0), 3.0);
272 EXPECT_DOUBLE_EQ(mt.get_entry(0, 1), 4.0);
273 EXPECT_DOUBLE_EQ(mt.get_entry(1, 1), 5.0);
274 EXPECT_DOUBLE_EQ(mt.get_entry(2, 1), 6.0);
275}
276
278{
279 Domain d = make_domain(3);
280
281 Mat m(d, d, {
282 {1.0, 2.0, 3.0},
283 {4.0, 5.0, 6.0},
284 {7.0, 8.0, 9.0}
285 });
286
287 Mat mtt = m.transpose().transpose();
288
289 EXPECT_TRUE(m == mtt);
290}
291
292// =============================================================================
293// Row and Column Extraction
294// =============================================================================
295
297{
298 Domain rows = make_domain(2);
299 Domain cols = make_domain(3);
300
301 Mat m(rows, cols, {
302 {1.0, 2.0, 3.0},
303 {4.0, 5.0, 6.0}
304 });
305
306 DynList<double> row0 = m.get_row_as_list(0);
307 std::vector<double> v0;
308 row0.for_each([&v0](double x) { v0.push_back(x); });
309
310 ASSERT_EQ(v0.size(), 3U);
311 EXPECT_DOUBLE_EQ(v0[0], 1.0);
312 EXPECT_DOUBLE_EQ(v0[1], 2.0);
313 EXPECT_DOUBLE_EQ(v0[2], 3.0);
314}
315
317{
318 Domain rows = make_domain(2);
319 Domain cols = make_domain(3);
320
321 Mat m(rows, cols, {
322 {1.0, 2.0, 3.0},
323 {4.0, 5.0, 6.0}
324 });
325
326 DynList<double> col1 = m.get_col_as_list(1);
327 std::vector<double> v1;
328 col1.for_each([&v1](double x) { v1.push_back(x); });
329
330 ASSERT_EQ(v1.size(), 2U);
331 EXPECT_DOUBLE_EQ(v1[0], 2.0);
332 EXPECT_DOUBLE_EQ(v1[1], 5.0);
333}
334
336{
337 Domain rows = make_domain(2);
338 Domain cols = make_domain(3);
339
340 Mat m(rows, cols, {
341 {1.0, 2.0, 3.0},
342 {4.0, 5.0, 6.0}
343 });
344
345 Vec row1 = m.get_row_vector(1);
346
347 EXPECT_DOUBLE_EQ(row1.get_entry(0), 4.0);
348 EXPECT_DOUBLE_EQ(row1.get_entry(1), 5.0);
349 EXPECT_DOUBLE_EQ(row1.get_entry(2), 6.0);
350}
351
353{
354 Domain rows = make_domain(2);
355 Domain cols = make_domain(3);
356
357 Mat m(rows, cols, {
358 {1.0, 2.0, 3.0},
359 {4.0, 5.0, 6.0}
360 });
361
362 Vec col2 = m.get_col_vector(2);
363
364 EXPECT_DOUBLE_EQ(col2.get_entry(0), 3.0);
365 EXPECT_DOUBLE_EQ(col2.get_entry(1), 6.0);
366}
367
369{
370 Domain rows = make_domain(2);
371 Domain cols = make_domain(3);
372 Mat m(rows, cols);
373
374 EXPECT_THROW(m.get_row_as_list(5), std::domain_error);
375 EXPECT_THROW(m.get_row_vector(5), std::domain_error);
376}
377
379{
380 Domain rows = make_domain(2);
381 Domain cols = make_domain(3);
382 Mat m(rows, cols);
383
384 EXPECT_THROW(m.get_col_as_list(5), std::domain_error);
385 EXPECT_THROW(m.get_col_vector(5), std::domain_error);
386}
387
388// =============================================================================
389// Matrix-Vector Multiplication
390// =============================================================================
391
393{
394 Domain d = make_domain(3);
395
396 // | 1 2 3 | | 1 | | 1*1 + 2*2 + 3*3 | | 14 |
397 // | 4 5 6 | * | 2 | = | 4*1 + 5*2 + 6*3 | = | 32 |
398 // | 7 8 9 | | 3 | | 7*1 + 8*2 + 9*3 | | 50 |
399
400 Mat m(d, d, {
401 {1.0, 2.0, 3.0},
402 {4.0, 5.0, 6.0},
403 {7.0, 8.0, 9.0}
404 });
405
406 Vec v(d);
407 v.set_entry(0, 1.0);
408 v.set_entry(1, 2.0);
409 v.set_entry(2, 3.0);
410
411 Vec result = m * v;
412
413 EXPECT_DOUBLE_EQ(result.get_entry(0), 14.0);
414 EXPECT_DOUBLE_EQ(result.get_entry(1), 32.0);
415 EXPECT_DOUBLE_EQ(result.get_entry(2), 50.0);
416}
417
419{
420 Domain d = make_domain(3);
421
422 // [1 2 3] * | 1 2 3 | = [1*1+2*4+3*7, 1*2+2*5+3*8, 1*3+2*6+3*9]
423 // | 4 5 6 | = [30, 36, 42]
424 // | 7 8 9 |
425
426 Mat m(d, d, {
427 {1.0, 2.0, 3.0},
428 {4.0, 5.0, 6.0},
429 {7.0, 8.0, 9.0}
430 });
431
432 Vec v(d);
433 v.set_entry(0, 1.0);
434 v.set_entry(1, 2.0);
435 v.set_entry(2, 3.0);
436
437 Vec result = v * m;
438
439 EXPECT_DOUBLE_EQ(result.get_entry(0), 30.0);
440 EXPECT_DOUBLE_EQ(result.get_entry(1), 36.0);
441 EXPECT_DOUBLE_EQ(result.get_entry(2), 42.0);
442}
443
445{
446 Domain d = make_domain(3);
447
448 // Sparse matrix with only diagonal
449 Mat m(d, d);
450 m.set_entry(0, 0, 2.0);
451 m.set_entry(1, 1, 3.0);
452 m.set_entry(2, 2, 4.0);
453
454 Vec v(d);
455 v.set_entry(0, 1.0);
456 v.set_entry(1, 2.0);
457 v.set_entry(2, 3.0);
458
459 Vec result = m.mult_matrix_vector_sparse(v);
460
461 EXPECT_DOUBLE_EQ(result.get_entry(0), 2.0);
462 EXPECT_DOUBLE_EQ(result.get_entry(1), 6.0);
463 EXPECT_DOUBLE_EQ(result.get_entry(2), 12.0);
464}
465
467{
468 Domain d = make_domain(2);
469
470 Mat m(d, d, {
471 {1.0, 2.0},
472 {3.0, 4.0}
473 });
474
475 Vec v(d);
476 v.set_entry(0, 5.0);
477 v.set_entry(1, 6.0);
478
479 Vec result = m.mult_matrix_vector_dot_product(v);
480
481 // [1*5 + 2*6, 3*5 + 4*6] = [17, 39]
482 EXPECT_DOUBLE_EQ(result.get_entry(0), 17.0);
483 EXPECT_DOUBLE_EQ(result.get_entry(1), 39.0);
484}
485
487{
488 Domain d2 = make_domain(2);
489 Domain d3 = make_domain(3);
490
491 Mat m(d2, d3); // 2x3 matrix
492 Vec v(d2); // vector of size 2, but should be 3 for multiplication
493
494 EXPECT_THROW(m * v, std::domain_error);
495}
496
497// =============================================================================
498// Matrix Addition
499// =============================================================================
500
502{
503 Domain d = make_domain(2);
504
505 Mat m1(d, d, {
506 {1.0, 2.0},
507 {3.0, 4.0}
508 });
509
510 Mat m2(d, d, {
511 {5.0, 6.0},
512 {7.0, 8.0}
513 });
514
515 Mat sum = m1 + m2;
516
517 EXPECT_DOUBLE_EQ(sum.get_entry(0, 0), 6.0);
518 EXPECT_DOUBLE_EQ(sum.get_entry(0, 1), 8.0);
519 EXPECT_DOUBLE_EQ(sum.get_entry(1, 0), 10.0);
520 EXPECT_DOUBLE_EQ(sum.get_entry(1, 1), 12.0);
521}
522
524{
525 Domain d = make_domain(2);
526
527 Mat m1(d, d, {
528 {1.0, 2.0},
529 {3.0, 4.0}
530 });
531
532 Mat m2(d, d, {
533 {1.0, 1.0},
534 {1.0, 1.0}
535 });
536
537 m1 += m2;
538
539 EXPECT_DOUBLE_EQ(m1.get_entry(0, 0), 2.0);
540 EXPECT_DOUBLE_EQ(m1.get_entry(0, 1), 3.0);
541 EXPECT_DOUBLE_EQ(m1.get_entry(1, 0), 4.0);
542 EXPECT_DOUBLE_EQ(m1.get_entry(1, 1), 5.0);
543}
544
546{
547 Domain d = make_domain(3);
548
549 Mat m1(d, d);
550 m1.set_entry(0, 0, 1.0);
551 m1.set_entry(2, 2, 2.0);
552
553 Mat m2(d, d);
554 m2.set_entry(1, 1, 3.0);
555 m2.set_entry(2, 2, 4.0);
556
557 Mat sum = m1 + m2;
558
559 EXPECT_DOUBLE_EQ(sum.get_entry(0, 0), 1.0);
560 EXPECT_DOUBLE_EQ(sum.get_entry(1, 1), 3.0);
561 EXPECT_DOUBLE_EQ(sum.get_entry(2, 2), 6.0);
562 EXPECT_DOUBLE_EQ(sum.get_entry(0, 1), 0.0);
563}
564
565// =============================================================================
566// Scalar Multiplication
567// =============================================================================
568
570{
571 Domain d = make_domain(2);
572
573 Mat m(d, d, {
574 {1.0, 2.0},
575 {3.0, 4.0}
576 });
577
578 m.mult_by_scalar(2.0);
579
580 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 2.0);
581 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 4.0);
582 EXPECT_DOUBLE_EQ(m.get_entry(1, 0), 6.0);
583 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 8.0);
584}
585
587{
588 Domain d = make_domain(2);
589
590 Mat m(d, d, {
591 {1.0, 2.0},
592 {3.0, 4.0}
593 });
594
595 Mat result = 3.0 * m;
596
597 EXPECT_DOUBLE_EQ(result.get_entry(0, 0), 3.0);
598 EXPECT_DOUBLE_EQ(result.get_entry(0, 1), 6.0);
599 EXPECT_DOUBLE_EQ(result.get_entry(1, 0), 9.0);
600 EXPECT_DOUBLE_EQ(result.get_entry(1, 1), 12.0);
601}
602
603// =============================================================================
604// Identity Matrix
605// =============================================================================
606
608{
609 Domain d = make_domain(3);
610 Mat m(d, d);
611
612 Mat id = m.identity();
613
614 for (int i = 0; i < 3; ++i)
615 for (int j = 0; j < 3; ++j)
616 EXPECT_DOUBLE_EQ(id.get_entry(i, j), (i == j) ? 1.0 : 0.0);
617}
618
620{
621 Domain d = make_domain(3);
622 Mat m(d, d);
623 Mat id = m.identity();
624
625 Vec v(d);
626 v.set_entry(0, 5.0);
627 v.set_entry(1, 10.0);
628 v.set_entry(2, 15.0);
629
630 Vec result = id * v;
631
632 EXPECT_DOUBLE_EQ(result.get_entry(0), 5.0);
633 EXPECT_DOUBLE_EQ(result.get_entry(1), 10.0);
634 EXPECT_DOUBLE_EQ(result.get_entry(2), 15.0);
635}
636
637// =============================================================================
638// Equality Comparison
639// =============================================================================
640
642{
643 Domain d = make_domain(2);
644
645 Mat m1(d, d, {
646 {1.0, 2.0},
647 {3.0, 4.0}
648 });
649
650 Mat m2(d, d, {
651 {1.0, 2.0},
652 {3.0, 4.0}
653 });
654
655 EXPECT_TRUE(m1 == m2);
656 EXPECT_FALSE(m1 != m2);
657}
658
660{
661 Domain d = make_domain(2);
662
663 Mat m1(d, d, {
664 {1.0, 2.0},
665 {3.0, 4.0}
666 });
667
668 Mat m2(d, d, {
669 {1.0, 2.0},
670 {3.0, 5.0} // different
671 });
672
673 EXPECT_FALSE(m1 == m2);
674 EXPECT_TRUE(m1 != m2);
675}
676
678{
679 Domain d = make_domain(2);
680
681 Mat m1(d, d, 1e-5);
682 m1.set_entry(0, 0, 1.0);
683
684 Mat m2(d, d, 1e-5);
685 m2.set_entry(0, 0, 1.0 + 1e-6); // within epsilon
686
687 EXPECT_TRUE(m1 == m2);
688}
689
690// =============================================================================
691// Static Factory Methods
692// =============================================================================
693
695{
696 Domain rows = make_domain(2);
697 Domain cols = make_domain(3);
698
699 Vec row0(cols);
700 row0.set_entry(0, 1.0);
701 row0.set_entry(1, 2.0);
702 row0.set_entry(2, 3.0);
703
704 Vec row1(cols);
705 row1.set_entry(0, 4.0);
706 row1.set_entry(1, 5.0);
707 row1.set_entry(2, 6.0);
708
712
713 Mat m = Mat::create_by_rows(rows, row_list);
714
715 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 1.0);
716 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 2.0);
717 EXPECT_DOUBLE_EQ(m.get_entry(0, 2), 3.0);
718 EXPECT_DOUBLE_EQ(m.get_entry(1, 0), 4.0);
719 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 5.0);
720 EXPECT_DOUBLE_EQ(m.get_entry(1, 2), 6.0);
721}
722
724{
725 Domain rows = make_domain(2);
726 Domain cols = make_domain(3);
727
728 Vec col0(rows);
729 col0.set_entry(0, 1.0);
730 col0.set_entry(1, 4.0);
731
732 Vec col1(rows);
733 col1.set_entry(0, 2.0);
734 col1.set_entry(1, 5.0);
735
736 Vec col2(rows);
737 col2.set_entry(0, 3.0);
738 col2.set_entry(1, 6.0);
739
744
745 Mat m = Mat::create_by_columns(cols, col_list);
746
747 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 1.0);
748 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 2.0);
749 EXPECT_DOUBLE_EQ(m.get_entry(0, 2), 3.0);
750 EXPECT_DOUBLE_EQ(m.get_entry(1, 0), 4.0);
751 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 5.0);
752 EXPECT_DOUBLE_EQ(m.get_entry(1, 2), 6.0);
753}
754
755// =============================================================================
756// Row/Column List Conversion
757// =============================================================================
758
760{
761 Domain d = make_domain(2);
762
763 Mat m(d, d, {
764 {1.0, 2.0},
765 {3.0, 4.0}
766 });
767
768 DynList<Vec> rows = m.to_rowlist();
769
770 std::vector<Vec> row_vec;
771 rows.for_each([&row_vec](const Vec & v) { row_vec.push_back(v); });
772
773 ASSERT_EQ(row_vec.size(), 2U);
774 EXPECT_DOUBLE_EQ(row_vec[0].get_entry(0), 1.0);
775 EXPECT_DOUBLE_EQ(row_vec[0].get_entry(1), 2.0);
776 EXPECT_DOUBLE_EQ(row_vec[1].get_entry(0), 3.0);
777 EXPECT_DOUBLE_EQ(row_vec[1].get_entry(1), 4.0);
778}
779
781{
782 Domain d = make_domain(2);
783
784 Mat m(d, d, {
785 {1.0, 2.0},
786 {3.0, 4.0}
787 });
788
789 DynList<Vec> cols = m.to_collist();
790
791 std::vector<Vec> col_vec;
792 cols.for_each([&col_vec](const Vec & v) { col_vec.push_back(v); });
793
794 ASSERT_EQ(col_vec.size(), 2U);
795 EXPECT_DOUBLE_EQ(col_vec[0].get_entry(0), 1.0);
796 EXPECT_DOUBLE_EQ(col_vec[0].get_entry(1), 3.0);
797 EXPECT_DOUBLE_EQ(col_vec[1].get_entry(0), 2.0);
798 EXPECT_DOUBLE_EQ(col_vec[1].get_entry(1), 4.0);
799}
800
801// =============================================================================
802// Set Vector as Row/Column
803// =============================================================================
804
806{
807 Domain d = make_domain(3);
808 Mat m(d, d);
809
810 Vec row(d);
811 row.set_entry(0, 10.0);
812 row.set_entry(1, 20.0);
813 row.set_entry(2, 30.0);
814
815 m.set_vector_as_row(1, row);
816
817 EXPECT_DOUBLE_EQ(m.get_entry(1, 0), 10.0);
818 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 20.0);
819 EXPECT_DOUBLE_EQ(m.get_entry(1, 2), 30.0);
820
821 // Other rows remain zero
822 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 0.0);
823 EXPECT_DOUBLE_EQ(m.get_entry(2, 0), 0.0);
824}
825
827{
828 Domain d = make_domain(3);
829 Mat m(d, d);
830
831 Vec col(d);
832 col.set_entry(0, 10.0);
833 col.set_entry(1, 20.0);
834 col.set_entry(2, 30.0);
835
836 m.set_vector_as_col(2, col);
837
838 EXPECT_DOUBLE_EQ(m.get_entry(0, 2), 10.0);
839 EXPECT_DOUBLE_EQ(m.get_entry(1, 2), 20.0);
840 EXPECT_DOUBLE_EQ(m.get_entry(2, 2), 30.0);
841
842 // Other columns remain zero
843 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 0.0);
844 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 0.0);
845}
846
847// =============================================================================
848// Outer Product
849// =============================================================================
850
852{
853 Domain d = make_domain(3);
854
855 Vec v1(d);
856 v1.set_entry(0, 1.0);
857 v1.set_entry(1, 2.0);
858 v1.set_entry(2, 3.0);
859
860 Vec v2(d);
861 v2.set_entry(0, 4.0);
862 v2.set_entry(1, 5.0);
863 v2.set_entry(2, 6.0);
864
865 Mat result = outer_product<int, int, double>(v1, v2);
866
867 // result[i,j] = v1[i] * v2[j]
868 EXPECT_DOUBLE_EQ(result.get_entry(0, 0), 4.0);
869 EXPECT_DOUBLE_EQ(result.get_entry(0, 1), 5.0);
870 EXPECT_DOUBLE_EQ(result.get_entry(0, 2), 6.0);
871 EXPECT_DOUBLE_EQ(result.get_entry(1, 0), 8.0);
872 EXPECT_DOUBLE_EQ(result.get_entry(1, 1), 10.0);
873 EXPECT_DOUBLE_EQ(result.get_entry(1, 2), 12.0);
874 EXPECT_DOUBLE_EQ(result.get_entry(2, 0), 12.0);
875 EXPECT_DOUBLE_EQ(result.get_entry(2, 1), 15.0);
876 EXPECT_DOUBLE_EQ(result.get_entry(2, 2), 18.0);
877}
878
879// =============================================================================
880// Generic Domain Types (String)
881// =============================================================================
882
884{
885 StrDomain rows;
886 rows.insert("A");
887 rows.insert("B");
888
889 StrDomain cols;
890 cols.insert("x");
891 cols.insert("y");
892 cols.insert("z");
893
894 StrMat m(rows, cols);
895 m.set_entry("A", "x", 1.0);
896 m.set_entry("A", "y", 2.0);
897 m.set_entry("B", "z", 3.0);
898
899 EXPECT_DOUBLE_EQ(m.get_entry("A", "x"), 1.0);
900 EXPECT_DOUBLE_EQ(m.get_entry("A", "y"), 2.0);
901 EXPECT_DOUBLE_EQ(m.get_entry("A", "z"), 0.0);
902 EXPECT_DOUBLE_EQ(m.get_entry("B", "x"), 0.0);
903 EXPECT_DOUBLE_EQ(m.get_entry("B", "y"), 0.0);
904 EXPECT_DOUBLE_EQ(m.get_entry("B", "z"), 3.0);
905}
906
908{
909 StrDomain rows;
910 rows.insert("A");
911 rows.insert("B");
912
913 StrDomain cols;
914 cols.insert("x");
915 cols.insert("y");
916
917 StrMat m(rows, cols);
918 m.set_entry("A", "x", 1.0);
919 m.set_entry("A", "y", 2.0);
920 m.set_entry("B", "x", 3.0);
921 m.set_entry("B", "y", 4.0);
922
923 StrMat mt = m.transpose();
924
925 EXPECT_DOUBLE_EQ(mt.get_entry("x", "A"), 1.0);
926 EXPECT_DOUBLE_EQ(mt.get_entry("y", "A"), 2.0);
927 EXPECT_DOUBLE_EQ(mt.get_entry("x", "B"), 3.0);
928 EXPECT_DOUBLE_EQ(mt.get_entry("y", "B"), 4.0);
929}
930
931// =============================================================================
932// String Representation
933// =============================================================================
934
936{
937 Domain d = make_domain(2);
938
939 Mat m(d, d, {
940 {1.0, 2.0},
941 {3.0, 4.0}
942 });
943
944 std::string s = m.to_str();
945 EXPECT_FALSE(s.empty());
946}
947
948// =============================================================================
949// Domain Accessors
950// =============================================================================
951
953{
954 Domain rows = make_domain(2);
955 Domain cols = make_domain(3);
956
957 Mat m(rows, cols);
958
959 EXPECT_EQ(&m.get_row_domain(), &rows);
960 EXPECT_EQ(&m.get_col_domain(), &cols);
961}
962
964{
965 Domain rows = make_domain(3);
966 Domain cols = make_domain(2);
967
968 Mat m(rows, cols);
969
970 DynList<int> row_list = m.row_domain_list();
971 DynList<int> col_list = m.col_domain_list();
972
973 std::vector<int> rv, cv;
974 row_list.for_each([&rv](int x) { rv.push_back(x); });
975 col_list.for_each([&cv](int x) { cv.push_back(x); });
976
977 ASSERT_EQ(rv.size(), 3U);
978 ASSERT_EQ(cv.size(), 2U);
979
980 // Should be sorted
981 EXPECT_EQ(rv[0], 0);
982 EXPECT_EQ(rv[1], 1);
983 EXPECT_EQ(rv[2], 2);
984 EXPECT_EQ(cv[0], 0);
985 EXPECT_EQ(cv[1], 1);
986}
987
988// =============================================================================
989// Matrix Subtraction
990// =============================================================================
991
993{
994 Domain d = make_domain(2);
995
996 Mat m1(d, d, {
997 {5.0, 6.0},
998 {7.0, 8.0}
999 });
1000
1001 Mat m2(d, d, {
1002 {1.0, 2.0},
1003 {3.0, 4.0}
1004 });
1005
1006 Mat diff = m1 - m2;
1007
1008 EXPECT_DOUBLE_EQ(diff.get_entry(0, 0), 4.0);
1009 EXPECT_DOUBLE_EQ(diff.get_entry(0, 1), 4.0);
1010 EXPECT_DOUBLE_EQ(diff.get_entry(1, 0), 4.0);
1011 EXPECT_DOUBLE_EQ(diff.get_entry(1, 1), 4.0);
1012}
1013
1015{
1016 Domain d = make_domain(2);
1017
1018 Mat m1(d, d, {
1019 {5.0, 6.0},
1020 {7.0, 8.0}
1021 });
1022
1023 Mat m2(d, d, {
1024 {1.0, 1.0},
1025 {1.0, 1.0}
1026 });
1027
1028 m1 -= m2;
1029
1030 EXPECT_DOUBLE_EQ(m1.get_entry(0, 0), 4.0);
1031 EXPECT_DOUBLE_EQ(m1.get_entry(0, 1), 5.0);
1032 EXPECT_DOUBLE_EQ(m1.get_entry(1, 0), 6.0);
1033 EXPECT_DOUBLE_EQ(m1.get_entry(1, 1), 7.0);
1034}
1035
1037{
1038 Domain d = make_domain(3);
1039
1040 Mat m1(d, d);
1041 m1.set_entry(0, 0, 5.0);
1042 m1.set_entry(2, 2, 10.0);
1043
1044 Mat m2(d, d);
1045 m2.set_entry(1, 1, 3.0);
1046 m2.set_entry(2, 2, 4.0);
1047
1048 Mat diff = m1 - m2;
1049
1050 EXPECT_DOUBLE_EQ(diff.get_entry(0, 0), 5.0);
1051 EXPECT_DOUBLE_EQ(diff.get_entry(1, 1), -3.0);
1052 EXPECT_DOUBLE_EQ(diff.get_entry(2, 2), 6.0);
1053 EXPECT_DOUBLE_EQ(diff.get_entry(0, 1), 0.0);
1054}
1055
1057{
1058 Domain d = make_domain(2);
1059
1060 Mat m(d, d, {
1061 {1.0, 2.0},
1062 {3.0, 4.0}
1063 });
1064
1065 Mat zero = m - m;
1066
1067 EXPECT_DOUBLE_EQ(zero.get_entry(0, 0), 0.0);
1068 EXPECT_DOUBLE_EQ(zero.get_entry(0, 1), 0.0);
1069 EXPECT_DOUBLE_EQ(zero.get_entry(1, 0), 0.0);
1070 EXPECT_DOUBLE_EQ(zero.get_entry(1, 1), 0.0);
1071}
1072
1074{
1075 Domain d1 = make_domain(2);
1076 Domain d2 = make_domain(2); // different domain object
1077
1078 Mat m1(d1, d1);
1079 Mat m2(d2, d2);
1080
1081 EXPECT_THROW(m1 - m2, std::domain_error);
1082 EXPECT_THROW(m1 -= m2, std::domain_error);
1083}
1084
1085// =============================================================================
1086// Copy and Move Semantics
1087// =============================================================================
1088
1090{
1091 Domain d = make_domain(2);
1092
1093 Mat m1(d, d, {
1094 {1.0, 2.0},
1095 {3.0, 4.0}
1096 });
1097
1098 Mat m2(m1); // copy construct
1099
1100 // Values should be equal
1101 EXPECT_TRUE(m1 == m2);
1102
1103 // Modifying m2 should not affect m1
1104 m2.set_entry(0, 0, 100.0);
1105 EXPECT_DOUBLE_EQ(m1.get_entry(0, 0), 1.0);
1106 EXPECT_DOUBLE_EQ(m2.get_entry(0, 0), 100.0);
1107}
1108
1110{
1111 Domain d = make_domain(2);
1112
1113 Mat m1(d, d, {
1114 {1.0, 2.0},
1115 {3.0, 4.0}
1116 });
1117
1118 Mat m2(d, d); // empty matrix
1119 m2 = m1; // copy assign
1120
1121 EXPECT_TRUE(m1 == m2);
1122
1123 // Modifying m2 should not affect m1
1124 m2.set_entry(1, 1, 200.0);
1125 EXPECT_DOUBLE_EQ(m1.get_entry(1, 1), 4.0);
1126 EXPECT_DOUBLE_EQ(m2.get_entry(1, 1), 200.0);
1127}
1128
1130{
1131 Domain d = make_domain(2);
1132
1133 Mat m(d, d, {
1134 {1.0, 2.0},
1135 {3.0, 4.0}
1136 });
1137
1138 m = m; // self-assignment
1139
1140 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 1.0);
1141 EXPECT_DOUBLE_EQ(m.get_entry(0, 1), 2.0);
1142 EXPECT_DOUBLE_EQ(m.get_entry(1, 0), 3.0);
1143 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 4.0);
1144}
1145
1147{
1148 Domain d = make_domain(2);
1149
1150 Mat m1(d, d, {
1151 {1.0, 2.0},
1152 {3.0, 4.0}
1153 });
1154
1155 Mat m2(std::move(m1)); // move construct
1156
1157 EXPECT_DOUBLE_EQ(m2.get_entry(0, 0), 1.0);
1158 EXPECT_DOUBLE_EQ(m2.get_entry(0, 1), 2.0);
1159 EXPECT_DOUBLE_EQ(m2.get_entry(1, 0), 3.0);
1160 EXPECT_DOUBLE_EQ(m2.get_entry(1, 1), 4.0);
1161}
1162
1164{
1165 Domain d = make_domain(2);
1166
1167 Mat m1(d, d, {
1168 {1.0, 2.0},
1169 {3.0, 4.0}
1170 });
1171
1172 Mat m2(d, d);
1173 m2 = std::move(m1); // move assign
1174
1175 EXPECT_DOUBLE_EQ(m2.get_entry(0, 0), 1.0);
1176 EXPECT_DOUBLE_EQ(m2.get_entry(0, 1), 2.0);
1177 EXPECT_DOUBLE_EQ(m2.get_entry(1, 0), 3.0);
1178 EXPECT_DOUBLE_EQ(m2.get_entry(1, 1), 4.0);
1179}
1180
1182{
1183 Domain d = make_domain(2);
1184 Mat m1(d, d, 1e-5); // custom epsilon
1185
1186 Mat m2(m1);
1187 EXPECT_DOUBLE_EQ(m2.get_epsilon(), 1e-5);
1188
1189 Mat m3(d, d);
1190 m3 = m1;
1191 EXPECT_DOUBLE_EQ(m3.get_epsilon(), 1e-5);
1192}
1193
1194// =============================================================================
1195// Shared Pointer Domain Tests
1196// =============================================================================
1197
1199{
1200 auto rows = make_shared_domain(2);
1201 auto cols = make_shared_domain(3);
1202
1203 Mat m(rows, cols, {
1204 {1.0, 2.0, 3.0},
1205 {4.0, 5.0, 6.0}
1206 });
1207
1208 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 1.0);
1209 EXPECT_DOUBLE_EQ(m.get_entry(1, 2), 6.0);
1210
1211 // Verify we can get the domain pointers back
1212 EXPECT_EQ(m.get_row_domain_ptr(), rows);
1213 EXPECT_EQ(m.get_col_domain_ptr(), cols);
1214}
1215
1217{
1219 m.set_entry(0, 0, 42.0);
1220 m.set_entry(1, 1, 99.0);
1221
1222 // Domain was created inline but matrix keeps it alive via shared_ptr
1223 EXPECT_DOUBLE_EQ(m.get_entry(0, 0), 42.0);
1224 EXPECT_DOUBLE_EQ(m.get_entry(1, 1), 99.0);
1225 EXPECT_EQ(m.get_row_domain().size(), 2U);
1226}
1227
1229{
1230 auto d = make_shared_domain(2);
1231 Mat m1(d, d);
1232 m1.set_entry(0, 0, 1.0);
1233
1234 Mat m2 = m1;
1235
1236 // Both matrices share the same domain pointer
1237 EXPECT_EQ(m1.get_row_domain_ptr(), m2.get_row_domain_ptr());
1238 EXPECT_EQ(m1.get_col_domain_ptr(), m2.get_col_domain_ptr());
1239}
1240
1242{
1243 auto d = make_shared_domain(3);
1244
1245 Vec v(d);
1246 v.set_entry(0, 1.0);
1247 v.set_entry(1, 2.0);
1248 v.set_entry(2, 3.0);
1249
1250 EXPECT_DOUBLE_EQ(v.get_entry(0), 1.0);
1251 EXPECT_DOUBLE_EQ(v.get_entry(1), 2.0);
1252 EXPECT_DOUBLE_EQ(v.get_entry(2), 3.0);
1253 EXPECT_EQ(v.get_domain_ptr(), d);
1254}
1255
1257{
1258 auto d = make_shared_domain(2);
1259
1260 Mat m(d, d, {
1261 {1.0, 2.0},
1262 {3.0, 4.0}
1263 });
1264
1265 Vec v(d);
1266 v.set_entry(0, 1.0);
1267 v.set_entry(1, 2.0);
1268
1269 Vec result = m * v;
1270
1271 // [1*1 + 2*2, 3*1 + 4*2] = [5, 11]
1272 EXPECT_DOUBLE_EQ(result.get_entry(0), 5.0);
1273 EXPECT_DOUBLE_EQ(result.get_entry(1), 11.0);
1274}
1275
1276// =============================================================================
1277// Domain Identity for Matrix Multiplication
1278// =============================================================================
1279
1281{
1282 auto d = make_shared_domain(2);
1283
1284 Mat A(d, d, {{1.0, 2.0}, {3.0, 4.0}});
1285 Mat B(d, d, {{5.0, 6.0}, {7.0, 8.0}});
1286
1287 // Same domain pointer - should work
1288 Mat C = A.vector_matrix_mult(B);
1289
1290 // A*B = [[1*5+2*7, 1*6+2*8], [3*5+4*7, 3*6+4*8]] = [[19, 22], [43, 50]]
1291 EXPECT_DOUBLE_EQ(C.get_entry(0, 0), 19.0);
1292 EXPECT_DOUBLE_EQ(C.get_entry(0, 1), 22.0);
1293 EXPECT_DOUBLE_EQ(C.get_entry(1, 0), 43.0);
1294 EXPECT_DOUBLE_EQ(C.get_entry(1, 1), 50.0);
1295}
1296
1298{
1299 auto d1 = make_shared_domain(2); // {0, 1}
1300 auto d2 = make_shared_domain(2); // {0, 1} - same content but different object
1301
1302 Mat A(d1, d1, {{1.0, 2.0}, {3.0, 4.0}});
1303 Mat B(d2, d2, {{5.0, 6.0}, {7.0, 8.0}});
1304
1305 // Different domain pointers - should throw even though sizes match
1306 EXPECT_THROW(A.vector_matrix_mult(B), std::domain_error);
1307 EXPECT_THROW(A.matrix_vector_mult(B), std::domain_error);
1308}
1309
1311{
1312 auto rows = make_shared_domain(2);
1313 auto middle = make_shared_domain(3);
1314 auto cols = make_shared_domain(2);
1315
1316 // A: 2x3, B: 3x2
1317 Mat A(rows, middle, {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}});
1318 Mat B(middle, cols, {{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}});
1319
1320 // A*B should work: column domain of A == row domain of B (both are 'middle')
1321 Mat C = A.vector_matrix_mult(B);
1322
1323 // Result is 2x2
1324 EXPECT_DOUBLE_EQ(C.get_entry(0, 0), 22.0); // 1*1 + 2*3 + 3*5
1325 EXPECT_DOUBLE_EQ(C.get_entry(0, 1), 28.0); // 1*2 + 2*4 + 3*6
1326 EXPECT_DOUBLE_EQ(C.get_entry(1, 0), 49.0); // 4*1 + 5*3 + 6*5
1327 EXPECT_DOUBLE_EQ(C.get_entry(1, 1), 64.0); // 4*2 + 5*4 + 6*6
1328}
Integer domain classes for sparse data structures.
Sparse matrix with generic domains.
Sparse vector with named elements.
Generic domain class based on hash set.
Definition al-domain.H:85
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Sparse matrix with generic row and column domains.
Definition al-matrix.H:66
Sparse vector implementation with domain-based indexing.
Definition al-vector.H:87
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:685
#define TEST(name)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
bool diff(const C1 &c1, const C2 &c2, Eq e=Eq())
Check if two containers differ.
DynList< T > maps(const C &c, Op op)
Classic map operation.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.