Content

Ungeblockte LU-Zerlegung

Bei der ungeblockten LU-Zerlegung mit partieller Pivotisierung soll die \(m \times n\) Matrix \(A\) mit der Zerlegung kompakt überschrieben werden. Die Zeilenvertauschungen sollen in einem Pivot-Vektor festgehalten werden. Nur wenn die Matrix \(U\) bei der Faktorisierung exakt singulär ist, soll das Verfahren abbrechen. In diesem Fall soll die Funktion den kleinsten Index \(i\) mit \(U(i,i)\) zurückliefern. Andernfalls wird durch einen Rückgabewert \(-1\) signalisiert, dass die Zerlegung erfolgreich durchgeführt werden konnte.

Aufgabe

Implementiert die ungeblockte LU-Zerlegung hpc::ulmlapack::getf2 nach dem in der Vorlesung vorgestellten Algorithmus. Dabei seien die Matrizen aber bei Null beginnend indiziert:

  • \(mn \leftarrow \min\{m, n\}\)

  • \(\text{For}\; j=0,\dots,mn\)

    • \(i:\;|x_i| = \max_{k = j,\dots,m-1} |x_k|\)

    • \(\text{If}\; i \neq j\)

      • \(A_{i,k} \leftrightarrow A_{j,k} \quad (0\leq k < n)\)

    • \(p_j = i\)

    • \(\text{If}\; A_{j,j} = 0\)

      • \(\text{return:}\; j\)

    • \(A_{k,j} \leftarrow \frac{A_{k,j}}{A_{j,j}} \quad (j+1 \leq k < m)\)

    • \(A_{k, l} \leftarrow A_{k, l} - A_{k,j} A_{j,l} \quad (j+1 \leq k,l < m)\)

  • \(\text{return:}\; -1\)

Natürlich sollen dabei möglichst die auf der vorigen Seite entwickelten Bausteine verwendet werden. Als Signatur legen wir fest:

#ifndef HPC_ULMLAPACK_GETF2_H
#define HPC_ULMLAPACK_GETF2_H 1

namespace hpc { namespace ulmlapack {

template <typename Index, typename TA, typename TP>
Index
getf2(Index m, Index n, TA *A, Index incRowA, Index incColA, TP *p, Index incP)
{
    /* ... */
}

} } // namespace ulmblas, hpc

#endif // HPC_ULMLAPACK_GETF2_H

Zum Test kann folgendes Programm benutzt werden:

#include <cassert>
#include <random>
#include <hpc/aux/walltime.h>
#include <hpc/matvec/copy.h>
#include <hpc/matvec/gematrix.h>
#include <hpc/matvec/mm.h>
#include <hpc/matvec/print.h>
#include <hpc/ulmlapack/getf2.h>

using namespace hpc::matvec;

template <typename T, typename I>
double
checkLU(const GeMatrixView<T, I> &A,
        GeMatrixView<T, I> &A_,
        const GeMatrixView<I, I> &p)
{
    assert(A.numRows==A_.numRows);
    assert(A.numCols==A_.numCols);
    assert(A.numCols>=A.numRows);
    assert(A.numRows==p.numRows);

    typedef typename GeMatrix<T, I>::Index  Index;

    Index m = A.numRows;
    Index n = A.numRows;

    // Copy L form A_ and set A_ to U
    GeMatrix<T, I> L(m, m);
    for (Index j=0; j<m; ++j) {
        for (Index i=0; i<m; ++i) {
            if (i==j) {
                L(i,i) = 1;
            } else if (i>j) {
                L(i,j)  = A_(i,j);
                A_(i,j) = 0;
            } else if (j>i) {
                L(i,j)  = 0;
            }
        }
    }

    // Compute LU = L*U
    GeMatrix<T, I> LU(m, n);
    for (Index j=0; j<n; ++j) {
        for (Index i=0; i<m; ++i) {
            LU(i,j) = 0;
        }
    }
    hpc::matvec::mm(T(1), L, A_, T(0), LU);

    // Apply P
    for (Index i=m; i>0; --i) {
        if (i-1!=p(i-1,0)) {
            hpc::ulmblas::swap(n,
                               &LU(i-1,0), LU.incCol,
                               &LU(p(i-1,0),0), LU.incCol);
        }
    }

    double diff = 0;
    for (Index j=0; j<n; ++j) {
        for (Index i=0; i<m; ++i) {
            diff += std::abs(A(i,j)-LU(i,j));
        }
    }
    return diff;
}

//
//  Random initializer for general matrices: real and complex valued
//
template <typename Index, typename T>
void
randomInit(Index m, Index n, T *A, Index incRowA, Index incColA)
{
    std::random_device                  random;
    std::default_random_engine          mt(random());
    std::uniform_real_distribution<T>   uniform(-100,100);

    for (Index i=0; i<m; ++i) {
        for (Index j=0; j<n; ++j) {
            A[i*incRowA+j*incColA] = uniform(mt);
        }
    }
}

int
main()
{
    const std::ptrdiff_t   m_min = 10;
    const std::ptrdiff_t   n_min = 10;

    const std::ptrdiff_t   m_max = 6000;
    const std::ptrdiff_t   n_max = 8000;

    const std::ptrdiff_t   m_inc = 10;
    const std::ptrdiff_t   n_inc = 10;

    GeMatrix<double, std::ptrdiff_t>      A(m_max, n_max);
    GeMatrix<double, std::ptrdiff_t>      A_org(m_max, n_max);
    GeMatrix<std::ptrdiff_t, std::ptrdiff_t>        p(m_max, 1);
    hpc::aux::WallTime<double>  timer;

    for (std::ptrdiff_t m=m_min, n=n_min; m<=m_max && n<=n_max; m+=m_inc, n+=n_inc) {

        randomInit(m, n, A.data, A.incRow, A.incCol);
        hpc::ulmblas::gecopy(m, n,
                             A.data, A.incRow, A.incCol,
                             A_org.data, A_org.incRow, A_org.incCol);
        //print(A_org, "A_org");

        timer.tic();
        std::ptrdiff_t info = hpc::ulmlapack::getf2(m, n,
                                                    A.data, A.incRow, A.incCol,
                                                    p.data, p.incRow);
        double t = timer.toc();
        //print(A, "A");
        //print(p, "p");

        auto A_ = A_org(0, 0, m, n);
        auto LU = A(0, 0, m, n);
        auto p_ = p(0, 0, m, 1);

        double diff = checkLU(A_, LU, p_);
        //print(A, "LU");
        std::printf("%4ld %4ld %4ld %16.3e %16.5lf\n", m, n, info, diff, t);
    }
}