Use more efficient colmatrix-based reduction
[libstick.git] / tests / persistence.h
index 27b01f2f18c73aa0dbfc3ef79e85bc8d583f1419..832457e505fd3d5a6ab81310e1913a741f0b12e2 100644 (file)
@@ -15,6 +15,7 @@ class persistence_TestSuite: public Test::Suite {
         typedef simplicial_complex<3, uint32_t, double> scomplex;
         typedef persistence<3, uint32_t, double> pers;
         typedef pers::boundary_matrix bm;
+        typedef pers::lowestones_matrix lom;
         typedef pers::transformation_matrix tm;
 
         bool setupcalled;
@@ -31,6 +32,7 @@ class persistence_TestSuite: public Test::Suite {
             TEST_ADD(persistence_TestSuite::test_matrix_reduction);
             TEST_ADD(persistence_TestSuite::test_betti_numbers);
             TEST_ADD(persistence_TestSuite::test_lowestones);
+            TEST_ADD(persistence_TestSuite::test_diagram);
         }
 
     protected:
@@ -170,48 +172,37 @@ class persistence_TestSuite: public Test::Suite {
             const bm &ringbm = ringp.get_boundary_matrix();
             const bm &ringrbm = ringp.get_reduced_boundary_matrix();
             const tm &ringtm = ringp.get_transformation_matrix();
-            TEST_ASSERT(ringrbm == ringbm * ringtm);
+            TEST_ASSERT(ringrbm == boolean_colrowmatrix<uint32_t>(ringbm) * ringtm);
+
 
             pers torusp(otorus);
             torusp.compute_matrices();
             const bm &torusbm = torusp.get_boundary_matrix();
             const bm &torusrbm = torusp.get_reduced_boundary_matrix();
             const tm &torustm = torusp.get_transformation_matrix();
-            TEST_ASSERT(torusrbm == torusbm * torustm);
-
-            uint32_t torusrbme_coords[][2] = {
-                    {0, 1}, {2, 7}, {3, 7}, {1, 8}, {2, 8}, {4, 10}, {5, 10}, {4, 11}, {6, 11}, {1, 13},
-                    {4, 13}, {10, 15}, {13, 15}, {14, 15}, {9, 19}, {10, 19}, {13, 19}, {17, 19}, {7, 20},
-                    {16, 20}, {18, 20}, {7, 21}, {9, 21}, {10, 21}, {11, 21}, {13, 21}, {16, 21}, {12, 23},
-                    {13, 23}, {22, 23}, {7, 24}, {8, 24}, {9, 24}, {10, 24}, {11, 24}, {12, 24}, {25, 28},
-                    {26, 28}, {25, 29}, {27, 29}, {1, 31}, {25, 31}, {28, 33}, {31, 33}, {32, 33}, {30, 35},
-                    {31, 35}, {34, 35}, {8, 37}, {30, 37}, {31, 37}, {36, 37}, {9, 40}, {28, 40}, {31, 40},
-                    {39, 40}, {9, 41}, {28, 41}, {29, 41}, {31, 41}, {38, 41}, {7, 42}, {8, 42}, {9, 42},
-                    {28, 42}, {29, 42}, {30, 42}, {7, 49}, {8, 49}, {9, 49}, {28, 49}, {29, 49}, {43, 49},
-                    {47, 49}, {10, 50}, {11, 50}, {28, 50}, {29, 50}, {43, 50}, {45, 50}, {29, 51}, {44, 51},
-                    {48, 51}, {10, 52}, {28, 52}, {43, 52}, {44, 52}, {28, 53}, {43, 53}, {46, 53 }
-            };
-            bm torusrbme(torusbm.size());
-            torusrbme.set_all(torusrbme_coords, sizeof(torusrbme_coords)/(2*sizeof(uint32_t)), true);
-            TEST_ASSERT(torusrbme == torusrbm);
+            TEST_ASSERT(torusrbm == boolean_colrowmatrix<uint32_t>(torusbm) * torustm);
+
+            const lom &toruslo = torusp.get_lowestones_matrix();
+            uint32_t torusloe_coords[] = {1, 0, 8, 7, 13, 10, 11, 0, 0, 0, 0,
+                0, 24, 0, 15, 0, 21, 19, 20, 0, 0, 0, 23, 0, 0, 31, 28, 29, 0,
+                0, 42, 0, 33, 0, 35, 0, 37, 0, 41, 40, 0, 0, 0, 0, 52, 50, 53,
+                49, 51, 0, 0, 0, 0, 0, 0};
+            lom torusloe(torusloe_coords, torusloe_coords + sizeof(torusloe_coords)/sizeof(uint32_t));
+            TEST_ASSERT(torusloe == toruslo);
+
 
             pers ballp(oball);
             ballp.compute_matrices();
             const bm &ballbm = ballp.get_boundary_matrix();
             const bm &ballrbm = ballp.get_reduced_boundary_matrix();
             const tm &balltm = ballp.get_transformation_matrix();
-            TEST_ASSERT(ballrbm == ballbm * balltm);
-
-            uint32_t ballrbme_coords[][2] = {
-                    {0, 1}, {1, 6}, {2, 6}, {1, 7}, {3, 7}, {1, 8}, {4, 8}, {1, 10}, {5, 10}, {6, 14},
-                    {10, 14}, {11, 14}, {6, 15}, {7, 15}, {10, 15}, {12, 15}, {6, 16}, {7, 16}, {8, 16},
-                    {10, 16}, {13, 16}, {6, 17}, {7, 17}, {8, 17}, {9, 17}, {6, 19}, {7, 19}, {18, 19},
-                    {14, 22}, {15, 22}, {19, 22}, {21, 22}, {14, 23}, {15, 23}, {16, 23}, {17, 23}, {19, 23},
-                    {20, 23 }
-            };
-            bm ballrbme(ballrbm.size());
-            ballrbme.set_all(ballrbme_coords, sizeof(ballrbme_coords)/(2*sizeof(uint32_t)), true);
-            TEST_ASSERT(ballrbme == ballrbm);
+            TEST_ASSERT(ballrbm == boolean_colrowmatrix<uint32_t>(ballbm) * balltm);
+
+            const lom &balllo = ballp.get_lowestones_matrix();
+            uint32_t ballloe_coords[] = {1, 0, 6, 7, 8, 10, 0, 0, 0, 17, 0, 14,
+                15, 16, 0, 0, 0, 0, 19, 0, 23, 22, 0, 0};
+            lom ballloe(ballloe_coords, ballloe_coords + sizeof(ballloe_coords)/sizeof(uint32_t));
+            TEST_ASSERT(ballloe == balllo);
 
             //std::cout << std::endl;
             //ballp.interpret_reduction(std::cout);
@@ -293,26 +284,56 @@ class persistence_TestSuite: public Test::Suite {
         void test_lowestones_impl(const scomplex::simplex_order& so) {
             pers p(so);
             p.compute_matrices();
-            const bm &lowestones = p.get_lowestones_matrix();
-
-            for (unsigned c=0; c < lowestones.size(); ++c)
-                assert(lowestones.get_column(c).size() <= 1);
-            for (unsigned r=0; r < lowestones.size(); ++r)
-                assert(lowestones.get_row(r).size() <= 1);
-            for (unsigned c=0; c < lowestones.size(); ++c) {
+            const lom &lowestones = p.get_lowestones_matrix();
+
+            // Check that lowestones contains every column-index only once,
+            // except the zeros.
+            lom cpy = lowestones;
+            sort(cpy.begin(), cpy.end());
+            lom::iterator it = cpy.begin();
+            // Skip the zeros
+            while (it != cpy.end() && *it == 0)
+                ++it;
+            // From now on, no column index appears twice
+            for (; it+1 != cpy.end(); ++it)
+                TEST_ASSERT(*it < *(it+1));
+
+            for (unsigned r=0; r < lowestones.size(); ++r) {
                 // If (r,c) is a one then
                 //   (i) a cycle dies with c --> row c is zero
                 //  (ii) a cycle is born with r --> column r is zero
                 //Hence
                 //   (i) the column r is a zero-column
                 //   (i) the row c is a zero-column
-                if (lowestones.get_column(c).size() > 0) {
-                    const uint32_t r = lowestones.get_column(c).back();
-                    assert(lowestones.get_column(r).size() == 0);
-                    assert(lowestones.get_row(c).size() == 0);
+
+                const uint32_t c = lowestones[r];
+                if (c > 0) {
+                    TEST_ASSERT(p.get_reduced_boundary_matrix().get(r,c) == true);
+                    TEST_ASSERT(p.get_reduced_boundary_matrix().get_column(r).size() == 0);
+                    TEST_ASSERT(lowestones[c] == 0);
                 }
             }
         }
+
+        void test_diagram() {
+            test_diagram_impl(oring);
+            test_diagram_impl(otorus);
+            test_diagram_impl(oball);
+        }
+
+        void test_diagram_impl(const scomplex::simplex_order& so) {
+            pers p(so);
+            p.compute_diagrams();
+
+            // Even Jesus died after his birth. Homology classes are nothing better.
+            for (int d=-1; d <= 3; ++d) {
+                const pers::diagram &dia = p.get_persistence_diagram(d);
+                for (unsigned i=0; i < dia.births.size(); ++i)
+                    if (dia.births[i].death > 0)
+                        assert(dia.births[i].birth < dia.births[i].death);
+            }
+
+        }
 };
 
 #endif