2

I'm working on a project which requires me to write my own matrix class implementation. I decided to implement the matrix class as a template to provide compile-time error-checking in the following way:

template<size_t N, size_t M> // N × M matrix
class Matrix
{
    // implementation...
};

I managed to implement basic operations such as addition/subtraction, transpose and multiplication. However, I'm having trouble implementing the determinant. I was thinking of implementing it recursively using the Laplace expansion, so I must first implement a way to calculate the i,j minor of a matrix. The problem is, the minor of an N × N matrix is an (N-1) × (N-1) matrix. The following does not compile: (error message is Error C2059 syntax error: '<', pointing to the first line in the function)

template<size_t N>
Matrix<N-1, N-1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
    Matrix<N-1, N-1> minor;
    // calculate i,j minor
    return minor
}

How could I go around this and calculate the minor, while keeping the templated form of the class?

EDIT: I was asked to provide a working example. Here is the relevant part of my code, I tried to keep it as minimal as possible. My Matrix class uses a Vector class, which I also wrote myself. I removed any unrelated code, and also changed any error-checks to asserts, as the actual code throws an exception class, which again was written by me.

Here is the Vector.h file:

#pragma once
#include <vector>
#include <cassert>

template<size_t S>
class Vector
{
public:
    Vector(double fInitialValue = 0.0);
    Vector(std::initializer_list<double> il);
    // indexing range is 0...S-1
    double operator[](size_t i) const;
    double& operator[](size_t i);

private:
    std::vector<double> m_vec;
};

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

template<size_t S>
Vector<S>::Vector(double fInitialValue) : m_vec(S, fInitialValue)
{
}

template<size_t S>
Vector<S>::Vector(std::initializer_list<double> il) : m_vec(il)
{
    assert(il.size() == S);
}

template<size_t S>
double Vector<S>::operator[](size_t i) const
{
    return m_vec[i];
}

template<size_t S>
double& Vector<S>::operator[](size_t i)
{
    return m_vec[i];
}

And here is the Matrix.h file:

#pragma once
#include "Vector.h"

template<size_t N, size_t M>
class Matrix
{
public:
    Matrix(double fInitialValue = 0.0);
    Matrix(std::initializer_list<Vector<M>> il);
    // indexing range is 0...N-1, 0...M-1
    Vector<M> operator[](int i) const;
    Vector<M>& operator[](int i);
    double Determinant() const;

private:
    std::vector<Vector<M>> m_mat; // a collection of row vectors
    template <size_t N>
    friend Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j);
};

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

template<size_t N, size_t M>
Matrix<N, M>::Matrix(double fInitialValue)
: m_mat(N, Vector<M>(fInitialValue)) {}

template<size_t N, size_t M>
Matrix<N, M>::Matrix(std::initializer_list<Vector<M>> il) : m_mat(il)
{
    assert(il.size() == N);
}

template<size_t N, size_t M>
Vector<M> Matrix<N, M>::operator[](int i) const
{
    return m_mat[i];
}

template<size_t N, size_t M>
Vector<M>& Matrix<N, M>::operator[](int i)
{
    return m_mat[i];
}

template<size_t N, size_t M>
double Matrix<N, M>::Determinant() const
{
    assert(N == M);
    if (N == 2) {
        return m_mat[0][0] * m_mat[1][1] - m_mat[0][1] * m_mat[1][0];
    }
    double det = 0;
    for (size_t j = 0; j < N; j++) {
        if (j % 2) {
            det += m_mat[0][j] * Minor((*this), 0, j).Determinant();
        }
        else {
            det -= m_mat[0][j] * Minor((*this), 0, j).Determinant();
        }
    }
    return det;
}

template <size_t N>
Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
    Matrix<N - 1, N - 1> minor;
    for (size_t n = 0; n < i; n++) {
        for (size_t m = 0; m < j; m++) {
            minor[n][m] = mat[n][m];
        }
    }
    for (size_t n = i + 1; n < N; n++) {
        for (size_t m = 0; m < j; m++) {
            minor[n - 1][m] = mat[n][m];
        }
    }
    for (size_t n = 0; n < i; n++) {
        for (size_t m = j + 1; m < N; m++) {
            minor[n][m - 1] = mat[n][m];
        }
    }
    for (size_t n = i + 1; n < N; n++) {
        for (size_t m = j + 1; m < N; m++) {
            minor[n - 1][m - 1] = mat[n][m];
        }
    }
    return minor;
}

Compiling these along with a simple main.cpp file:

#include "Matrix.h"
#include <iostream>

int main() {
    Matrix<3, 3> mat = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
    std::cout << mat.Determinant();
}

produces - Error C2760 syntax error: unexpected token '<', expected 'declaration' ...\matrix.h 67

EDIT2: Apparently I had written the template arguments as <N - 1><N - 1> instead of <N -1, N-1> in the implementation of the Minor function. Changing that fixed the error, but introduced a new one - compilation hangs, and after a minute or so I get Error C1060 compiler is out of heap space ...\matrix.h 65

nodivo
  • 29
  • 2
  • 3
    It doesn't answer your actual issue, but a tangential question: using Laplace expansion is a pretty bad way of calculating a determinant (https://en.wikipedia.org/wiki/Laplace_expansion#Computational_expense). Why not use a LU decomposition? – Rafael Lerm Nov 18 '18 at 21:58
  • *"The following does not compile*", what is the error message you see ? – Piotr Skotnicki Nov 18 '18 at 22:01
  • @RafaelLerm, to be honest, I haven't heard of LU decomposition before. I'll have to look a bit into that. But I would still like a solution using Laplace expansion. – nodivo Nov 18 '18 at 22:08
  • @PiotrSkotnicki This is the error message: Error C2059 syntax error: '<' pointing to the first line of code with the function `Minor` – nodivo Nov 18 '18 at 22:10
  • @nodivo have you included Matrix before Minor ? – Piotr Skotnicki Nov 18 '18 at 22:12
  • @PiotrSkotnicki All of my code is in a single `.h` file – nodivo Nov 18 '18 at 22:14
  • 1
    @nodivo then show it – Piotr Skotnicki Nov 18 '18 at 22:17
  • 1
    @nodivo Please show us a [mcve] that reproduces the error. Yet, you have a missing semicolon after `return minor`. – O'Neil Nov 18 '18 at 22:20
  • @PiotrSkotnicki I have added my code. I tried to keep it as minimal as possible. – nodivo Nov 19 '18 at 12:23
  • 3
    @nodivo `Matrix minor;` this is the problem, it should be spelled `Matrix minor;` – Piotr Skotnicki Nov 19 '18 at 12:24
  • `return matOut` ??? maybe `return minor` instead – Damien Nov 19 '18 at 13:31
  • Ugh. I'm so sorry. I did some quick renaming before posting the code and got no error (apart from the one in question). I changed the `M` to `N` and the `matOut` to `minor`, but I'm still getting the exact same error. – nodivo Nov 19 '18 at 16:18
  • @PiotrSkotnicki Oh wow, I didn't realize I didn't group the template parameters together! This fixed that compilation error. But now I'm facing a different one - `compiler is out of heap space`. Updating my post. – nodivo Nov 19 '18 at 16:46
  • @nodivo Think about what happens when `N` reaches 0. – O'Neil Nov 19 '18 at 23:32
  • @O'Neil I see it now! My Determinant function causes a recursion in compilation, creating the function Minor, Minor, Minor, etc.. but there's no stop condition. explicitly specializing the case where N==1, I stopped the recursion and the code compiled successfully. Thanks! – nodivo Nov 20 '18 at 19:28

0 Answers0