Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/gdallinearsystem.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Linear system solver
5
 * Author:   VIZRT Development Team.
6
 *
7
 * This code was provided by Gilad Ronnen (gro at visrt dot com) with
8
 * permission to reuse under the following license.
9
 *
10
 ******************************************************************************
11
 * Copyright (c) 2004, VIZRT Inc.
12
 * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
13
 * Copyright (c) 2019, Martin Franzke <martin dot franzke at telekom dot de>
14
 *
15
 * SPDX-License-Identifier: MIT
16
 ****************************************************************************/
17
18
/*! @cond Doxygen_Suppress */
19
20
#include "cpl_port.h"
21
#include "cpl_conv.h"
22
#include "gdallinearsystem.h"
23
24
#ifdef HAVE_ARMADILLO
25
#include "armadillo_headers.h"
26
#endif
27
28
#include <cstdio>
29
#include <algorithm>
30
#include <cassert>
31
#include <cmath>
32
33
namespace
34
{
35
// LU decomposition of the quadratic matrix A
36
// see https://en.wikipedia.org/wiki/LU_decomposition#C_code_examples
37
bool solve(GDALMatrix &A, GDALMatrix &RHS, GDALMatrix &X, double eps)
38
24.5k
{
39
24.5k
    assert(A.getNumRows() == A.getNumCols());
40
24.5k
    if (eps < 0)
41
0
        return false;
42
24.5k
    int const m = A.getNumRows();
43
24.5k
    int const n = RHS.getNumCols();
44
    // row permutations
45
24.5k
    std::vector<int> perm(m);
46
157k
    for (int iRow = 0; iRow < m; ++iRow)
47
133k
        perm[iRow] = iRow;
48
49
    // Arbitrary threshold to trigger progress in debug mode
50
24.5k
    const bool bDebug = (m > 10000);
51
24.5k
    int nLastPct = -1;
52
53
127k
    for (int step = 0; step < m - 1; ++step)
54
104k
    {
55
104k
        if (bDebug)
56
0
        {
57
0
            const int nPct = (step * 100 * 10 / m) / 2;
58
0
            if (nPct != nLastPct)
59
0
            {
60
0
                CPLDebug("GDAL", "solve(): %d.%d %%", nPct / 10, nPct % 10);
61
0
                nLastPct = nPct;
62
0
            }
63
0
        }
64
65
        // determine pivot element
66
104k
        int iMax = step;
67
104k
        double dMax = std::abs(A(step, step));
68
819k
        for (int i = step + 1; i < m; ++i)
69
714k
        {
70
714k
            if (std::abs(A(i, step)) > dMax)
71
3.62k
            {
72
3.62k
                iMax = i;
73
3.62k
                dMax = std::abs(A(i, step));
74
3.62k
            }
75
714k
        }
76
104k
        if (dMax <= eps)
77
2.07k
        {
78
2.07k
            CPLError(CE_Failure, CPLE_AppDefined,
79
2.07k
                     "GDALLinearSystemSolve: matrix not invertible");
80
2.07k
            return false;
81
2.07k
        }
82
        // swap rows
83
102k
        if (iMax != step)
84
3.62k
        {
85
3.62k
            std::swap(perm[iMax], perm[step]);
86
79.7k
            for (int iCol = 0; iCol < m; ++iCol)
87
76.1k
            {
88
76.1k
                std::swap(A(iMax, iCol), A(step, iCol));
89
76.1k
            }
90
3.62k
        }
91
810k
        for (int iRow = step + 1; iRow < m; ++iRow)
92
708k
        {
93
708k
            A(iRow, step) /= A(step, step);
94
708k
        }
95
810k
        for (int iCol = step + 1; iCol < m; ++iCol)
96
708k
        {
97
24.9M
            for (int iRow = step + 1; iRow < m; ++iRow)
98
24.2M
            {
99
24.2M
                A(iRow, iCol) -= A(iRow, step) * A(step, iCol);
100
24.2M
            }
101
708k
        }
102
102k
    }
103
104
    // LUP solve;
105
89.7k
    for (int iCol = 0; iCol < n; ++iCol)
106
67.2k
    {
107
67.2k
        if (bDebug)
108
0
        {
109
0
            const int nPct = 500 + (iCol * 100 * 10 / n) / 2;
110
0
            if (nPct != nLastPct)
111
0
            {
112
0
                CPLDebug("GDAL", "solve(): %d.%d %%", nPct / 10, nPct % 10);
113
0
                nLastPct = nPct;
114
0
            }
115
0
        }
116
117
430k
        for (int iRow = 0; iRow < m; ++iRow)
118
362k
        {
119
362k
            X(iRow, iCol) = RHS(perm[iRow], iCol);
120
2.43M
            for (int k = 0; k < iRow; ++k)
121
2.06M
            {
122
2.06M
                X(iRow, iCol) -= A(iRow, k) * X(k, iCol);
123
2.06M
            }
124
362k
        }
125
430k
        for (int iRow = m - 1; iRow >= 0; --iRow)
126
362k
        {
127
2.43M
            for (int k = iRow + 1; k < m; ++k)
128
2.06M
            {
129
2.06M
                X(iRow, iCol) -= A(iRow, k) * X(k, iCol);
130
2.06M
            }
131
362k
            X(iRow, iCol) /= A(iRow, iRow);
132
362k
        }
133
67.2k
    }
134
135
22.4k
    if (bDebug)
136
0
    {
137
0
        CPLDebug("GDAL", "solve(): 100.0 %%");
138
0
    }
139
140
22.4k
    return true;
141
24.5k
}
142
}  // namespace
143
144
/************************************************************************/
145
/*                       GDALLinearSystemSolve()                        */
146
/*                                                                      */
147
/*   Solves the linear system A*X_i = RHS_i for each column i           */
148
/*   where A is a square matrix.                                        */
149
/************************************************************************/
150
bool GDALLinearSystemSolve(GDALMatrix &A, GDALMatrix &RHS, GDALMatrix &X,
151
                           [[maybe_unused]] bool bForceBuiltinMethod)
152
24.5k
{
153
24.5k
    assert(A.getNumRows() == RHS.getNumRows());
154
24.5k
    assert(A.getNumCols() == X.getNumRows());
155
24.5k
    assert(RHS.getNumCols() == X.getNumCols());
156
157
#ifdef HAVE_ARMADILLO
158
    if (!bForceBuiltinMethod)
159
    {
160
        try
161
        {
162
            arma::mat matA(A.data(), A.getNumRows(), A.getNumCols(), false,
163
                           true);
164
            arma::mat matRHS(RHS.data(), RHS.getNumRows(), RHS.getNumCols(),
165
                             false, true);
166
            arma::mat matOut(X.data(), X.getNumRows(), X.getNumCols(), false,
167
                             true);
168
#if ARMA_VERSION_MAJOR > 6 ||                                                  \
169
    (ARMA_VERSION_MAJOR == 6 && ARMA_VERSION_MINOR >= 500)
170
            // Perhaps available in earlier versions, but didn't check
171
            return arma::solve(matOut, matA, matRHS,
172
                               arma::solve_opts::equilibrate +
173
                                   arma::solve_opts::no_approx);
174
#else
175
            return arma::solve(matOut, matA, matRHS);
176
#endif
177
        }
178
        catch (std::exception const &e)
179
        {
180
            CPLError(CE_Failure, CPLE_AppDefined, "GDALLinearSystemSolve: %s",
181
                     e.what());
182
            return false;
183
        }
184
    }
185
#endif  // HAVE_ARMADILLO
186
187
24.5k
    return solve(A, RHS, X, 0);
188
24.5k
}
189
190
/*! @endcond */