| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com> | |
| // | |
| // This Source Code Form is subject to the terms of the Mozilla | |
| // Public License v. 2.0. If a copy of the MPL was not distributed | |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| // SPDX-License-Identifier: MPL-2.0 | |
| #include "sparse.h" | |
| #include <Eigen/OrderingMethods> | |
| template <typename Idx> | |
| void verify_is_permutation(const PermutationMatrix<Dynamic, Dynamic, Idx>& perm) { | |
| typedef Matrix<Idx, Dynamic, 1> Vec; | |
| Vec sorted = perm.indices(); | |
| std::sort(sorted.data(), sorted.data() + sorted.size()); | |
| Vec expected = Vec::LinSpaced(perm.size(), 0, Idx(perm.size() - 1)); | |
| VERIFY_IS_EQUAL(sorted, expected); | |
| } | |
| template <typename Scalar> | |
| void test_colamd_uncompressed() { | |
| typedef int Idx; | |
| typedef SparseMatrix<Scalar, ColMajor, Idx> SpMat; | |
| const int n = 32; | |
| SpMat A(n, n); | |
| A.reserve(VectorXi::Constant(n, 5)); | |
| for (int j = 0; j < n; ++j) { | |
| A.insert(j, j) = Scalar(2) + Scalar(j); | |
| if (j > 0) A.insert(j - 1, j) = Scalar(-1); | |
| if (j + 1 < n) A.insert(j + 1, j) = Scalar(-1); | |
| } | |
| A.insert(0, n - 1) = Scalar(0.5); | |
| A.insert(n - 1, 0) = Scalar(0.25); | |
| VERIFY(!A.isCompressed()); | |
| // Regression for #649: COLAMD on uncompressed input used to silently corrupt | |
| // the permutation in release builds. | |
| PermutationMatrix<Dynamic, Dynamic, Idx> perm_u, perm_c; | |
| COLAMDOrdering<Idx> ord; | |
| ord(A, perm_u); | |
| verify_is_permutation(perm_u); | |
| SpMat B = A; | |
| B.makeCompressed(); | |
| ord(B, perm_c); | |
| verify_is_permutation(perm_c); | |
| VERIFY_IS_EQUAL(perm_u.indices(), perm_c.indices()); | |
| } | |
| void test_colamd_mixed_index_types() { | |
| typedef SparseMatrix<double, ColMajor, int> SpMat; | |
| const int n = 8; | |
| SpMat A(n, n); | |
| A.reserve(VectorXi::Constant(n, 3)); | |
| for (int j = 0; j < n; ++j) { | |
| A.insert(j, j) = 2.0 + j; | |
| if (j > 0) A.insert(j - 1, j) = -1.0; | |
| if (j + 1 < n) A.insert(j + 1, j) = -1.0; | |
| } | |
| A.makeCompressed(); | |
| // COLAMDOrdering<StorageIndex> is a public API choice independent of the | |
| // source matrix's StorageIndex. Keep accepting mixed index types. | |
| PermutationMatrix<Dynamic, Dynamic, long> perm; | |
| COLAMDOrdering<long> ord; | |
| ord(A, perm); | |
| verify_is_permutation(perm); | |
| } | |
| template <typename Scalar> | |
| void test_amd_uncompressed() { | |
| typedef int Idx; | |
| typedef SparseMatrix<Scalar, ColMajor, Idx> SpMat; | |
| const int n = 24; | |
| SpMat A(n, n); | |
| A.reserve(VectorXi::Constant(n, 4)); | |
| for (int j = 0; j < n; ++j) { | |
| A.insert(j, j) = Scalar(2) + Scalar(j); | |
| if (j > 0) { | |
| A.insert(j - 1, j) = Scalar(-1); | |
| A.insert(j, j - 1) = Scalar(-1); | |
| } | |
| } | |
| VERIFY(!A.isCompressed()); | |
| PermutationMatrix<Dynamic, Dynamic, Idx> perm; | |
| AMDOrdering<Idx> ord; | |
| ord(A, perm); | |
| verify_is_permutation(perm); | |
| } | |
| EIGEN_DECLARE_TEST(sparse_ordering) { | |
| for (int i = 0; i < g_repeat; ++i) { | |
| CALL_SUBTEST_1(test_colamd_uncompressed<double>()); | |
| CALL_SUBTEST_2(test_colamd_uncompressed<std::complex<double> >()); | |
| CALL_SUBTEST_3(test_amd_uncompressed<double>()); | |
| CALL_SUBTEST_4(test_colamd_mixed_index_types()); | |
| } | |
| } |