-1

I would like the lapack package for some very useful functions which I would not like/being able to implement myself that well. The problem is that I cannot pass my custom Matrix class to the lapack function, I don't get the wanted behaviour. Here is my Matrix class

#ifndef MATRIX_T_H
#define MATRIX_T_H

#include <iostream>
#include <vector>
#include <functional>
#include <fstream>
#include "Complex.hpp"
#include "abs.hpp"

#define This (*this)
#define cout std::cout
#define endl std::endl
#define pa(a) std::pair<a>
#define O(a) std::optional<a>


template <class T>
class Column;

template <class T>
class Row;

template<class T>
struct pivot {
    uint index;
    T val;
};

template<class T>
class Matrix
{
public:
//**CONSTRUCTORS AND DESTRUCTORS*********************************************

    Matrix() : m_rows(0), m_cols(0) {}
    explicit Matrix(int nrows, int ncols=0);
    explicit Matrix (const T& val, uint nrows, uint ncols=0);
    explicit Matrix(T (&fun) (int,int), uint nrows, uint ncols=0);
    explicit Matrix(std::function<T(int,int)>& fun, uint nrows, uint ncols=0)
        : m_rows(nrows), m_cols(ncols==0 ? nrows : ncols)
    {
        createMatrix();
        fill(fun);
    }
    Matrix(const std::vector<std::vector<T>>& matrix);
    Matrix(const Matrix<T>& other); //Copy constructor
    Matrix(Matrix<T>&& other);      //Move constructor
    ~Matrix();
//****************************************************************************
//*****************  methods *************************************************
    void createMatrix(uint size) {
        m_rows = size;
        m_cols = size;
        createMatrix();
    }
    void createMatrix(uint n_rows, uint n_cols) {
        m_rows = n_rows;
        m_cols = n_cols;
        createMatrix();
    }
    T **getMatrix() {return m_matrix;}
    T *getLinearMatrix() {return m_line_matrix;}


//*****************  operators *******************************************
    operator T** () {return m_matrix;}
    operator T* () {return m_line_matrix;}

// MORE CODE

//********************************************************************


protected:
    T **m_matrix = nullptr;
    T *m_line_matrix = nullptr;
    bool m_created_matrix = false;
    uint m_rows;
    uint m_cols;

protected:
    void createMatrix();
    void deleteMatrix();
};


template<class T>
Matrix<T>::Matrix(int nrows, int ncols)
    : m_rows(nrows), m_cols(ncols==0 ? nrows : ncols) {
    createMatrix();
}

template<class T>
Matrix<T>::Matrix(const T &val, uint nrows, uint ncols)
    : m_rows(nrows), m_cols(ncols==0 ? nrows : ncols) {
    createMatrix();
    for (uint i=0; i<m_rows; i++)
        m_matrix[i][i] = val;
}

template<class T>
Matrix<T>::Matrix(T (&fun)(int, int), uint nrows, uint ncols)
    : m_rows(nrows), m_cols(ncols==0 ? nrows : ncols) {
    createMatrix();
    fill(fun);
}

template<class T>
Matrix<T>::Matrix(const std::vector<std::vector<T> > &matrix) : m_rows(matrix.size()), m_cols(matrix[0].size()) {
    createMatrix();
    for (int i=0; i<m_rows; i++)
        for (int j=0; j<m_cols; j++)
            m_matrix[i][j] = matrix[i][j];
}

template<class T>
Matrix<T>::Matrix(const Matrix<T> &other) : m_rows(other.rows()), m_cols(other.cols()) {
//    cout << "Matrix::copy_constructor" << endl;
    createMatrix();
    copyMatrix(other.m_matrix);
}

template<class T>
Matrix<T>::Matrix(Matrix<T> &&other) : m_created_matrix(true), m_rows(other.m_rows), m_cols(other.m_cols){
    delete[] m_matrix;
    delete[] m_line_matrix;
    m_matrix = other.m_matrix;
    m_line_matrix = other.m_line_matrix;
    other.m_line_matrix = nullptr;
    other.m_matrix = nullptr;
    other.m_created_matrix = false;
}

template<class T>
Matrix<T>::~Matrix() {
    deleteMatrix();
}


template<class T>
T *&Matrix<T>::operator [](int index) const {
    return m_matrix[index];
}


template<class T>
void Matrix<T>::createMatrix() {
    if (m_line_matrix!=nullptr || m_matrix != nullptr)
        deleteMatrix();

    m_line_matrix = new T[m_rows*m_cols]();
    if (m_line_matrix==0) {
        cout << "Could not allocate new memory. Needed " << m_rows*m_cols*sizeof(T) << " bytes" << endl;
        abort();
    }
    m_matrix = new T*[m_rows]();

    for (uint i=0; i<m_rows; i++)
        m_matrix[i] = m_line_matrix + m_cols*i;

    m_created_matrix = true;

}

template<class T>
void Matrix<T>::deleteMatrix() {
    delete [] m_line_matrix;
    delete[] m_matrix;
    m_line_matrix = nullptr;
    m_matrix = nullptr;
    m_created_matrix = false;
}

// MORE CODE
}

#undef This
#undef cout
#undef endl
#undef pa
#undef O

#endif // MATRIX_T_H

As you can see from createMatrix() I create an array dynamically and mimic the matrix structure by saving the addresses in a pointer to a pointer. So technically using the double operator [][] gives me the wanted element of the matrix.

The problem is when I try to pass it to the lapack function I am interested, to solve eigenvalue problems, which has signature

void LAPACK_dsygv(
    lapack_int const* itype, char const* jobz, char const* uplo,
    lapack_int const* n,
    double* A, lapack_int const* lda,
    double* B, lapack_int const* ldb,
    double* W,
    double* work, lapack_int const* lwork,
    lapack_int* info );

where lapack_int is simply an int value. I tried to pass my matrix in so many different ways, i.e.

  • dsygv_(&itype, &jobz , &uplo , &ndim, amm.getLinearMatrix(), &nnn , axx.getLinearMatrix(), &nnn, &wr[0], &aux[0], &lwork , &info);, where amm and axx are instances of my Matrix class and aux and wr are instances of std::vector;
  • dsygv_(&itype, &jobz , &uplo , &ndim, &(amm[0][0]), &nnn , &(axx[0][0]), &nnn, &wr[0], &aux[0], &lwork , &info);, same variables as before
  • dsygv_(&itype, &jobz , &uplo , &ndim, *amm, &nnn , *axx, &nnn, wr, aux, &lwork , &info);, where now I have double amm[NMAX][NMAX] and the same for axx, while I have int wr[NMAX], aux[4*NMAX]

In all cases the values of the matrices and arrays are the same, but only in the last case it works. I guess it has to do with the fact that the function does not recognize a matrix[][] as a double**.

How can I solve this?

As I said I tried changing the way I pass the arguments, but not having access to the lapack library code directly (and I don't want to recompile everything by hand) I cannot just use gdb or other tools to check what has been passed to the function.

Alessandro
  • 23
  • 7
  • why do you not use a `double[NMAX][NMAX]` to store the matrix? – 463035818_is_not_an_ai Aug 16 '23 at 08:33
  • @463035818_is_not_an_ai , I do use it, but I have to copy from my Matrix class instance, which is a waste of time and resources since these are very big matrices. My matrix class can automate many annoying operations on matrices so I prefer to base my project on that. Of course if I find no solution I stick to use double[NMAX][NMAX] which I know works. – Alessandro Aug 16 '23 at 08:38
  • for the interface and to "automate many annoying operations" the backing storage should not really matter, does it? Afaik its not uncommon to have the size of the matrix be part of its type. Actually that makes many operations more straightforward to implement. And I dont see anything in your code that wouldnt work if you store the matrix in a 2d array – 463035818_is_not_an_ai Aug 16 '23 at 08:54
  • the thing is, if the function expects you to pass a pointer to a 2d array, then you need to pass a pointer to a 2d array, not a pointer to an array of pointers. – 463035818_is_not_an_ai Aug 16 '23 at 08:56
  • @463035818_is_not_an_ai the backing storage does not matter, you are right, but in that case I would need to know the size of the matrix at compile time, don't I? I look everywhere how to create dynamically a matrix and I cannot find anything that works. – Alessandro Aug 16 '23 at 09:29
  • `template struct my_matrix { double data[M][N]; }` ? – 463035818_is_not_an_ai Aug 16 '23 at 09:34
  • @463035818_is_not_an_ai when I create an object I would need to know the size at compile time anyway. Otherwise I get the error: error: the value of ‘M’ is not usable in a constant expression – Alessandro Aug 16 '23 at 09:42
  • The idea is, for example, to provide the user the possibility to insert the matrix dimension and its values and to solve he eigensystem for the matrix. To do that I have to read the dimension at runtime and construct the matrix dynamically. I cannot just create an object of the type `my_matrix` if n is the dimension inserted. – Alessandro Aug 16 '23 at 09:46

1 Answers1

0
  • To fix the 1st: dsygv_(&itype, &jobz , &uplo , &ndim, amm.getLinearMatrix(), &nnn , axx.getLinearMatrix(), &nnn, &wr[0], &aux[0], &lwork , &info), because *amm.getLinearMatrix(), *axx.getLinearMatrix() have type double, not double *. Here should be compile error, shouldn't it?
  • To fix the 2nd dsygv_(&itype, &jobz , &uplo , &ndim, amm[0], &nnn , axx[0], &nnn, &wr[0], &aux[0], &lwork , &info); because &(amm[0][0]) is address of temporary double object amm[0][0]on the stack, so its value doesn't equal amm.getLinearMatrix()
Misha T
  • 56
  • 5
  • About the first fix, I actually did it that way already. There was a typo in my post (now fixed). So I knew it would have not worked. The second fix you proposed did not solve it, it still gives me the same error at the info output. – Alessandro Aug 16 '23 at 19:39