2
\$\begingroup\$

Not much formal education in programming. One year in school. We did C++ classes for most user defined objects. I've written a few different class variations for matrices but wonder about how useful that really is. Array pointers vs. STL containers, all this wrapper around an array or std::<vector> or std::vector<std::vector>>.

So I decided maybe a struct, but then found myself with the same thought. The struct just made accessing the point of the matrix (the underlying data) more annoying.

I wrote this namespace instead to have a bunch of vector operators and just use a typedef, but wonder if there are some downsides? Also, is it possible to not have to typedef and declare the using LMath::operator+ in the main .cpp? So if the main function #include "LMath.H" (crrently Matrix.h, found out can't typedef Matrix AND call the namespace Matrix::).

Matrix.h

#include <vector>

namespace LMath {

typedef std::vector<std::vector<float>> Matrix;
typedef std::vector<float> Vector;

void printMatrix(const Matrix&);
Matrix createMatrix(const Vector&, int rows, int cols);
Matrix createMatrix(const Matrix&);
Matrix transpose(const Matrix&);
Matrix dotMatrix(const Matrix&,const Matrix&);
Matrix addMatrix(const Matrix&,const Matrix&);
Matrix multMatrix(float,const Matrix&);
Matrix hadamard(const Matrix&,const Matrix&);
Matrix concat(const Matrix&,const Matrix&);
Matrix stack(const Matrix&,const Matrix&);
Matrix subtractMatrix(const Matrix&,const Matrix&);
bool isEqual(const Matrix&,const Matrix&);

bool operator==(const Matrix&,const Matrix&);
Matrix operator+(const Matrix&,const Matrix&);
//Matrix operator-(const Matrix&,const Matrix&);
//Matrix operator*(const Matrix&,const Matrix&);
//Matrix operator*(float,const Matrix&);

float dotVectors(const Vector&,const Vector&);
Vector subVectors(const Vector&,const Vector&);
}

Matrix.cpp

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

namespace LMath {

    typedef std::vector<std::vector<float>> Matrix;
    typedef std::vector<float> Vector;

    void printMatrix(const Matrix& matrix) {
        for(unsigned int i = 0; i < matrix.size(); i++) {
            for(unsigned int j = 0; j < matrix[i].size(); j++)
                std::cout << matrix[i][j] << ", ";
            std::cout << std::endl;
        }

        return;
    }


    //This function was a lot more useful when I was not just the 2d vector directly as a matrix...  
    //Might have some utility still..
    Matrix createMatrix(const Vector & data, int rows, int cols) {
        Matrix matrix;
        Vector temp;

        for(unsigned int i = 0; i < data.size(); i++) {

            temp.push_back(data[i]);    
            if((i+1) % cols == 0) {
                matrix.push_back(temp);
                temp.clear();
            }

        }
        return matrix;
    }

    //This function was a lot more useful when I was not using the 2d vector directly as a matrix...  
    //Now it's really just a copy function.
    Matrix createMatrix(const Matrix& formattedMatrix) {
        Matrix matrix;
        int Cols = formattedMatrix[0].size();

        Vector temp;
        for(unsigned int i = 0; i < formattedMatrix.size(); i++) {

            for(unsigned int j = 0; j < formattedMatrix[0].size(); j++) {
                if(Cols != formattedMatrix[i].size()) {
                    std::cout << "Irregular Matrix Detectd!" << std::endl << "Not allowed!" << std::endl;
                    matrix.clear();
                    temp.clear();
                    temp.push_back(0);
                    matrix.push_back(temp);
                    return matrix;
                }
                temp.push_back(formattedMatrix[i][j]);              
            }
            matrix.push_back(temp);
            temp.clear();
        }
        return matrix;
    }


    Matrix transpose(const Matrix & matrix) {
        Vector temp;
        Matrix transposeMatrix;
        for(unsigned int i = 0; i < matrix[0].size(); i++) {
            for(unsigned int j = 0; j < matrix.size(); j++) {
                temp.push_back(matrix[j][i]);
            }
            transposeMatrix.push_back(temp);
            temp.clear();
        }
        return transposeMatrix;
    }

    Matrix dotMatrix(const Matrix& lhs,const Matrix& rhs) {

        if(lhs[0].size() != rhs.size()) {
            std::cout << "ERROR: Matrix incompatible for dot product" << std::endl;
            std::cout << "Operation requested: Mat[" << lhs.size() << "x" << lhs[0].size() << "] . Mat[" << rhs.size() << "x" << rhs[0].size() << "]" << std::endl;  
            Vector temp;
            temp.push_back(0.0f);
            return createMatrix(temp,1,1);
        }

        Matrix matrix;
        Vector temp;

        for(unsigned int k = 0; k < lhs.size(); k++) {

            for(unsigned int i = 0; i < rhs[0].size(); i++) {
                Vector tempRHS;
                for(unsigned int j = 0; j < rhs.size(); j++) {  
                    tempRHS.push_back(rhs[j][i]);
                }
                temp.push_back(dotVectors(lhs[k],tempRHS));
            }
            matrix.push_back(temp);
            temp.clear();
        }

        return matrix;
    }


    Matrix addMatrix(const Matrix& lhs,const Matrix& rhs) {

        Matrix matrix;
        Vector temp;
        if(lhs.size() != rhs.size() || lhs[0].size() != rhs[0].size()) {
            std::cout << "WARNING: matrices are not compatible for addition!" << std::endl;
            std::cout << "Operation requested: Mat[" << lhs.size() << "x" << lhs[0].size() << "] + Mat[" << rhs.size() << "x" << rhs[0].size() << "]" << std::endl;             
            temp.push_back(0.0f);
            return createMatrix(temp,1,1);
        }

        for(unsigned int i = 0; i < lhs.size(); i++) {
            for(unsigned int j = 0; j < lhs[i].size(); j++) {
                temp.push_back(lhs[i][j]+rhs[i][j]);                
            }
            matrix.push_back(temp);
            temp.clear();
        }

        return matrix;
    }

    Matrix multMatrix(float scalar,const Matrix& matrix) {
        Matrix scaledMatrix;
        Vector temp;
        for(unsigned int i = 0; i < matrix.size(); i++) {
            for(unsigned int j = 0; j < matrix[i].size(); j++) {
                temp.push_back(matrix[i][j]*scalar);
            }
            scaledMatrix.push_back(temp);
            temp.clear();
        }
        return scaledMatrix;
    }

    Matrix hadamard(const Matrix& lhs,const Matrix& rhs) {
        if(lhs.size() != rhs.size() || lhs[0].size() != rhs[0].size()) {
            std::cout << "WARNING: matrices are not compatible for Hadamard Multiplication!" << std::endl;
            std::cout << "Operation requested: Mat[" << lhs.size() << "x" << lhs[0].size() << "] * Mat[" << rhs.size() << "x" << rhs[0].size() << "]" << std::endl;  
            Vector temp;
            temp.push_back(0.0f);
            return createMatrix(temp,1,1);
        }

        Matrix matrix;
        Vector temp;
        for(unsigned int i = 0; i < lhs.size(); i++) {
            for(unsigned int j = 0; j < lhs[i].size(); j++) {
                temp.push_back(lhs[i][j]*rhs[i][j]);                
            }
            matrix.push_back(temp);
            temp.clear();
        }

        return matrix;
    }

    Matrix concat(const Matrix& lhs,const Matrix& rhs) {
        if(lhs.size() != rhs.size()) {
            std::cout << "WARNING: matrices are not compatible for concatenation" << std::endl;
            std::cout << "Operation requested: Mat[" << lhs.size() << "x" << lhs[0].size() << "] + Mat[" << rhs.size() << "x" << rhs[0].size() << "]" << std::endl;  
            Vector temp;
            temp.push_back(0.0f);
            return createMatrix(temp,1,1);
        }

        Matrix matrix;
        Vector temp;
        for(unsigned int i = 0; i < lhs.size(); i++) {
            for(unsigned int j = 0; j < lhs[i].size(); j++) {
                temp.push_back(lhs[i][j]);              
            }
            for(unsigned int j = 0; j < rhs[i].size(); j++) {
                temp.push_back(rhs[i][j]);
            }
            matrix.push_back(temp);
            temp.clear();
        }

        return matrix;

    }

    Matrix stack(const Matrix& lhs,const Matrix& rhs) {
        if(lhs[0].size() != rhs[0].size()) {
            std::cout << "WARNING: matrices are not compatible for stacking" << std::endl;
            std::cout << "Operation requested: Mat[" << lhs.size() << "x" << lhs[0].size() << "] on Mat[" << rhs.size() << "x" << rhs[0].size() << "]" << std::endl;  
            Vector temp;
            temp.push_back(0.0f);
            return createMatrix(temp,1,1);
        }

        Matrix temp;
        for(unsigned int i = 0; i < lhs.size(); i++) {
            temp.push_back(lhs[i]);
        }
        for(unsigned int i = 0; i < rhs.size(); i++) {
            temp.push_back(rhs[i]);
        }
        return temp;
    }

    Matrix subtractMatrix(const Matrix& lhs,const Matrix& rhs) {
        if(lhs.size() != rhs.size() || lhs[0].size() != rhs[0].size()) {
            std::cout << "WARNING: matrices are not compatible for subtraction!" << std::endl;
            std::cout << "Operation requested: Mat[" << lhs.size() << "x" << lhs[0].size() << "] - Mat[" << rhs.size() << "x" << rhs[0].size() << "]" << std::endl;  
            Vector temp;
            temp.push_back(0.0f);
            return createMatrix(temp,1,1);
        }       

        Matrix matrix;
        Vector temp;
        for(unsigned int i = 0; i < lhs.size(); i++) {
            for(unsigned int j = 0; j < lhs[i].size(); j++) {
                temp.push_back(lhs[i][j] - rhs[i][j]);              
            }
            matrix.push_back(temp);
            temp.clear();
        }

        return matrix;

    }


    bool isEqual(const Matrix& lhs, const Matrix& rhs) {
        if(lhs.size() != rhs.size() || lhs[0].size() != rhs[0].size()) 
            return false;

        for(unsigned int i = 0; i < lhs.size(); i++) {
            for(unsigned int j = 0; j < lhs[0].size(); j++) {
                if(lhs[i][j] != rhs[i][j])
                    return false;
            }
        }
        return true;
    }

    float dotVectors(const Vector& vec1,const Vector& vec2) {

        if(vec1.size() != vec2.size()) {
            std::cout << "ERROR: Vectors of unequal size!" << std::endl;
            return 0.0f;
        }

        float temp = 0.0f;
        for(unsigned int i = 0; i < vec1.size(); i++) {
            temp += vec1[i] * vec2[i];
        }
        return temp;

    }

    Vector subVectors(const Vector& lhs,const Vector& rhs) {
        Vector temp;
        if(lhs.size() != rhs.size()) {
            std::cout << "ERROR: Vectors of unequal size!" << std::endl;
            return temp;
        }

        for(unsigned int i = 0; i < lhs.size(); i++) {
            temp.push_back(lhs[i] - rhs[i]);
        }
        return temp;
    }


    Matrix operator+(const Matrix& lhs,const Matrix& rhs) {
        return addMatrix(lhs,rhs);
    }   

    bool operator==(const Matrix& lhs, const Matrix& rhs) {
        return isEqual(lhs,rhs);
    }


}

Souce.cpp

#include <iostream>
#include <vector>
#include "Matrix.h"
#include <string>
#include <fstream>
#include <sstream>


//TODO: Find alternatives to these...
typedef std::vector<std::vector<float>> Matrix;
typedef std::vector<float> Vector;
using LMath::operator+;
using LMath::operator==;



void testMatrix(); //testing function.
Matrix loadData(std::string); //Not implemented yet
bool saveData(Matrix, std::string); //Not implemented yet





int main() {

    testMatrix();
    return 0;
}




Matrix loadData(std::string) {
    //TODO: Implement file loading and data parsing

    Matrix matrix;
    return matrix;
}
bool saveData(Matrix, std::string) {

    return true;
}


void testMatrix() {
    std::vector<float> temp;

    temp.push_back(1.0); temp.push_back(1.0); temp.push_back(1.0);
    temp.push_back(0.0); temp.push_back(1.0); temp.push_back(1.0);
    temp.push_back(1.0); temp.push_back(0.0); temp.push_back(1.0);

    std::cout << std::endl << "Matrix 1 (3 rows, 3 cols): " << std::endl;

    Matrix matrix1 = LMath::createMatrix(temp,3,3);
    LMath::printMatrix(matrix1);

    std::cout << std::endl << "Matrix 2 (3 rows, 3 cols): " << std::endl;
    Matrix matrix2 = LMath::transpose(matrix1);
    LMath::printMatrix(matrix2);

    temp.clear();
    temp.push_back(1.0); temp.push_back(1.0); temp.push_back(1.0);
    temp.push_back(0.0); temp.push_back(1.0); temp.push_back(1.0);

    std::cout << std::endl << "Matrix 3 (2 rows, 3 cols): " << std::endl;

    Matrix matrix3 = LMath::createMatrix(temp, 2, 3);
    LMath::printMatrix(matrix3);

    std::cout << std::endl << "Matrix 4 (3 rows, 2 cols): " << std::endl;
    Matrix matrix4 = LMath::transpose(matrix3);
    LMath::printMatrix(matrix4);

    std::cout << std::endl << "Matrix 5 (3 rows, 3 cols) = Matrix1 . Matrix2" << std::endl;
    Matrix matrix5 = LMath::dotMatrix(matrix1,matrix2);
    LMath::printMatrix(matrix5);

    std::cout << std::endl << "Matrix 6 (2 rows, 2 cols) = Matrix3 . Matrix4" << std::endl;
    Matrix matrix6 = LMath::dotMatrix(matrix3,matrix4);
    LMath::printMatrix(matrix6);

    std::cout << std::endl << "Matrix 7 (3 rows, 3 cols) = Matrix4 . Matrix3" << std::endl;
    Matrix matrix7 = LMath::dotMatrix(matrix4,matrix3);
    LMath::printMatrix(matrix7);

    std::cout << std::endl << "Matrix 8 (Error Test) = Matrix1 . Matrix3" << std::endl;
    Matrix matrix8 = LMath::dotMatrix(matrix1,matrix3);
    LMath::printMatrix(matrix8);

    std::cout << std::endl << "Matrix 9 (3 rows, 3 cols) = Matrix1 + Matrix2" << std::endl;
    Matrix matrix9 = LMath::addMatrix(matrix1, matrix2);
    LMath::printMatrix(matrix9);

    std::cout << std::endl << "Matrix 10 (3 rows, 3 cols) = -1 * Matrix9" << std::endl;
    Matrix matrix10 = LMath::multMatrix(-1, matrix9);
    LMath::printMatrix(matrix10);

    std::cout << std::endl << "Matrix 11 (3 rows, 3 cols) = Matrix9 + Matrix10" << std::endl;
    Matrix matrix11 = LMath::addMatrix(matrix9, matrix10);
    LMath::printMatrix(matrix11);

    std::cout << std::endl << "Matrix 12 (3 rows, 3 cols) = Hadamard(Matrix1 * Matrix9)" << std::endl;
    Matrix matrix12 = LMath::hadamard(matrix1, matrix9);
    LMath::printMatrix(matrix12);

    std::cout << std::endl << "Matrix 13 (3 rows, 3 cols) = concat(Matrix1 + Matrix2)" << std::endl;
    Matrix matrix13 = LMath::concat(matrix1, matrix2);
    LMath::printMatrix(matrix13);

    std::cout << std::endl << "Matrix 14 (5 rows, 3 cols) = stack(Matrix2 + Matrix3)" << std::endl;
    Matrix matrix14 = LMath::stack(matrix2, matrix3);
    LMath::printMatrix(matrix14);

    std::cout << std::endl << "Matrix 15 (Error Test) = stack(Matrix2 + Matrix8)" << std::endl;
    Matrix matrix15 = LMath::stack(matrix2, matrix8);
    LMath::printMatrix(matrix15);

    std::cout << std::endl << "Matrix 16 (3x3) = createMatrix(formatted vector matrix)" << std::endl;
    std::vector<std::vector<float>> formattedMat;
    temp.clear();
    for(int j = 0; j < 3; j++) {
        for(int i = 0; i < 3; i++) {
            temp.push_back((float)(i+2*j));
        }
        formattedMat.push_back(temp);
        temp.clear();
    }

    Matrix matrix16 = LMath::createMatrix(formattedMat);
    LMath::printMatrix(matrix16);

    std::cout << std::endl << "Matrix 17 (Error Test) = createMatrix(formatted vector matrix)" << std::endl;
    formattedMat[1].pop_back();
    Matrix matrix17 = LMath::createMatrix(formattedMat);
    LMath::printMatrix(matrix17);

    std::cout << std::endl << "Matrix16 again:" << std::endl;
    LMath::printMatrix(matrix16);

    std::cout << std::endl << "Matrix 18 (3x3) = Matrix16 - Matrix1" << std::endl;
    Matrix matrix18 = LMath::subtractMatrix(matrix16,matrix1);
    LMath::printMatrix(matrix18);

    //Todo: Implement Load/Save matrices
    //std::cout << std::endl << "Matrix 19 (3x3) - Load/Save data" << std::endl;
    //std::cout << saveData(matrix18,"Meh.data");
    //Matrix matrix19 = loadData("Meh.data");
    //LMath::printMatrix(matrix19);

    Matrix matrix20, matrix21, matrix22;

    Vector vec, vec2, vec3;

    for(unsigned int i = 0; i < 3; i++) {
        vec.clear();
        vec2.clear();
        vec3.clear();
        for(unsigned int j = 0; j < 3; j++) {
            float temp = float(i+j);
            vec.push_back(temp);
            vec2.push_back(2*temp);
            vec3.push_back(3*temp);
        }
        matrix20.push_back(vec);
        matrix21.push_back(vec2);
        matrix22.push_back(vec3);
    }

    Matrix matrix23 = matrix20 + matrix21;
    if(matrix22 == matrix23)
        std::cout << "Operators success" << std::endl;
    else
        std::cout << "Operators failure" << std::endl;


}
\$\endgroup\$
  • \$\begingroup\$ std::vector<std::vector<float>> is not the way I'd do it, what happens if the inner vectors contain a different number of elemens each? not just that, you're having multiple allocations there and your data ends up all over the place. do you really need a generic matrix object? if so, it would be better to have a class that stores the matrix dimensions and a single std::vector<float> of (n x m) elements that you access by using std::vector<float>::data, say MatrixVector.data()[x][y]. And implement the operators as function members of the class. \$\endgroup\$ – Rodrigo Hernandez Oct 26 '18 at 18:12
  • \$\begingroup\$ @RodrigoHernandez After thinking about it, I figured you're right. I added a template class idea now here. Is it possible to overload, let's say the [] operator, have it return a vector<T> appropriately for then the second []. Then can just access with Matrix[][] without the need to call a function? \$\endgroup\$ – Chemistpp Oct 28 '18 at 16:53
  • \$\begingroup\$ like a sub vector? If you return a vector it will be a new vector, so any changes made to it afterward will not be reflected in the main vector. It would work for read operations only but even then you'll be allocating memory you may not want to. You could overload operator [] and return a T* or const T* that points to the starting offset of your row/colum, and that will work as intended, but there is no safety net there, you could try to read or write past the end of the row/column and the compiler will let you do so, it may even seem to work during runtime. \$\endgroup\$ – Rodrigo Hernandez Oct 29 '18 at 17:48
  • \$\begingroup\$ @RodrigoHernandez ah man, good call on the read only access. Only needed to read them currently.. but I'm sure I would have found out here soon.. Anyway, bummer. And yeah, I don't really want to use a T*.. super easy to overwrite other elements in the linear array (plus bounds issues)... I think in a circuitous way, it could be done with a second data structure that has access to private data members.. don't quote me. but I'll probably opt for gets and sets. Thanks for your advice! I already like the class better again still. \$\endgroup\$ – Chemistpp Oct 29 '18 at 21:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.