0
votes

I am currently working on a linear algebra library (custom vector and matrix plus some algorithms) for educational purposes and personal use. I tried to implement a column iterator, an iterator that traverses a specified column of a Matrix.

Here is a code sample of the vector class (upon which the matrix class is built on):

template<class T>
class MVector
{
    std::vector<T> data;

public:

    explicit MVector(const std::size_t& n) :data(n) {}
    explicit MVector(std::size_t&& n) :data(n) {}

    typename std::vector<T>::iterator Begin(){
        return data.begin();
    }

    typename std::vector<T>::iterator End(){
        return data.end();
    }

    // many more functions and overloaded operators
    // end of class
};

The matrix class is based on this vector (or the std::vector for this matter), and looks like:

template<class T, std::size_t rowsize, std::size_t colsize>
class Matrix
{

private:

    // Data is stored in a MVector, a modified std::vector
    MVector<T> matrix;

    // size of row dimension of the matrix
    std::size_t row_dim;                  

    // size of row dimension of the matrix
    std::size_t column_dim;

public:

    Matrix(std::initializer_list<T> il) :matrix(il), 
                                     row_dim(rowsize), column_dim(colsize){}    
    //other constructors...

    // iterator
    typename std::vector<T>::iterator Begin(std::size_t row = 0){
        return matrix.Begin()+index(row,0);
    }

    typename std::vector<T>::iterator End(std::size_t row = rowsize){
        return matrix.Begin()+index(row,0);

    // index (convenience) function to access elements of the matrix via some_matrix(i,j)
    std::size_t index(std::size_t r, std::size_t c) const {
        return r*cols()+c; 
    }

    // this is exactly what I want the iterator to do: 
    // only without creating and returning an object. 

    // get c'th column
    // slicing is possible from both ends and by "jumping" over elements
    // @ param "begin" - starts at the n'th element 
    // @ param "end" - subtracts m from from the last element.
    // @ param "by" - selects every n'th column 
    MVector<T> get_column(std::size_t c, std::size_t begin = 0, 
                          std::size_t end = 0, std::size_t by = 1) const{
         assert(c < cols() && end < rows());
         MVector<T> columns;
         for (std::size_t i = index(begin, c); i < index(rows()-end,c); i+=by*cols()) {
             columns.addTo(matrix[i]);
         }
         return columns;                
    } 

// end of class  
};

So, iterating the rows works fine all I have to do is:

 int main{

 Matrix<int, 3, 2> a = {1,2,3,4,5,6}; 
 for (std::vector<int>::iterator iter = a.Begin(1); iter != a.End(2); iter++) {
     std::cout << *iter << " ";
 }

 std::cout << endl;
 return 0;
 }

 bash-3.2$ ./main
 3 4 

which is exactly what I want. However traversing the columns does not work with that approach. Hence I look for other solutions and found this Designing iterators for a Matrix class article which sounds very similar to my problem and situation, however I could not deduce a solution to the problem.

Other suggestions pointed to the boost libraries iterators: in particular to:

boost::adaptors::stride(rng, n) 
boost::adaptors::slice(rng, n, m)

which indeed provide very similar results as desired. But so does my get_column function. However I do not want to create a new object. Which is what the boost functions do. From the documentation "Returns: A new range based on rng where traversal is performed in steps of n."

So it seems that the iterator does not know when to stop.

So I am back at square one: how to return am iterator that iterates through the columns of a Matrix that is stored as a vector?

1
[OT]: no need to have const lvalue reference and r-value reference for size_t, just do MVector(std::size_t n)Jarod42
I'm afraid this won't be possible without creating an iterator object with overloaded increment and dereferencing operators. Unrelated: If your matrix size is reasonably small and a compile-time constant (as the template parameter is), you will likely gain performance by embedding a fixed-size array in your matrix object, rather than allocating memory from the free store via std::vector.5gon12eder
Are there anything that prevents you from using Boost.uBLAS?Grigorii Chudnov
I start to feel comfortable using stl. Every time i look into boost it seems to me like a totally different language and i simply can't get my head into it. I spend this entire day figuring out the boost iterators. And i understand that there are nice concepts but i can't figure out to implement them.Vincent
@GrigoriyChudnov, good point. Vincent, check out the Boost Ublas Matrix, it's flipping great.The Vivandiere

1 Answers

0
votes

I found a solution to the problem. It's a combination of:

this: Get each nth element of iterator range and this: http://www.codeproject.com/Questions/331444/How-to-use-boost-filter-iterator-as-a-class-member

In the end, there was no way around boost. The solution is very simple and looks like:

 template<class U>
 struct EveryNth {
     bool operator()(const U& ) { return m_count++ % N == 0; }
     EveryNth(std::size_t i) : m_count(0), N(i) {}
 private:
     int m_count;
     std::size_t N;
 };

class Matrix{
// code here

    typedef boost::filter_iterator<EveryNth<T>, 
                               typename std::vector<T>::iterator> FilterIter;

    FilterIter  begin_jump(std::size_t i){
        return boost::make_filter_iterator<EveryNth<T> >(EveryNth<T>(i), data.begin(),      data.end());
    }

    FilterIter end_jump(std::size_t i){
       return boost::make_filter_iterator<EveryNth<T> >(EveryNth<T>(i), data.end(), data.end());
    }

};

and the main:

int main(int argc, char *argv[]){

    std::vector<int> b = {1,2,3,4,5,6,7,8,9};
    MVector<int> a = MVector<int>(b);
    for_each(a.begin_jump(2), a.end_jump(2), 
             [](int i){std::cout << i << " " ;} 
        );

    std::cout << std::endl;
    return 0;
}

bash-3.2$ ./main
1 3 5 7 9 

or using a.begin_jump(3) instead of two:

bash-3.2$ ./main
1 4 7 

which is exactly the intended result.