1

As the title mentions my goal is to use std::nth_element on a Armadillo-Matrix which contains a set of N points in d dimensions, i.e. a Nxd matrix. The rows represent a d-dimensional point. The comparison of two rows should be done along a fixed dimension, i.e.

row(i) < row(j) iff A(i, s) < A(j,s) (comparing the s-entry of each row).

My idea was now to use the column-iterators provided by Armadillo and overload the std::swap function to be used with two column-iterators and my desired swap:

void swap(arma::mat::col_iterator& lhs, arma::mat::col_iterator& rhs)
{
    lhs->M->swap_rows(lhs->current_row, rhs->current_row);
}

Here I use the swap_rows methods build in arma::mat. The call to std::nth_element looks like this:

// Setting indices to indicate which submatrix should be sorted:
arma::uword first = nodes[currentNodeIndex].indexFirstElem;
arma::uword last  = nodes[currentNodeIndex].indexLastElem + 1;
arma::uword nth = (last - first) / 2;    
// Calling std::nth_element:
std::nth_element(points.begin_col(first), points.begin_col(nth), points.begin_col(last));

Now the problem is that I get compiler-errors when overloading swap in the above manner:

    kd_tree.cpp: In function ‘void swap(double*&, double*&)’:
kd_tree.cpp:25:7: error: request for member ‘M’ in ‘* lhs’, which is of non-class type ‘double’
   25 |  lhs->M->swap_rows(lhs->current_row, rhs->current_row);
      |       ^
kd_tree.cpp:25:25: error: request for member ‘current_row’ in ‘* lhs’, which is of non-class type ‘double’
   25 |  lhs->M->swap_rows(lhs->current_row, rhs->current_row);
      |                         ^~~~~~~~~~~
kd_tree.cpp:25:43: error: request for member ‘current_row’ in ‘* rhs’, which is of non-class type ‘double’
   25 |  lhs->M->swap_rows(lhs->current_row, rhs->current_row);

I guess the errors tell me that the column-iterator is not able to access the matrix M on which it operates? Also it seems like I cannot access the current_row where my iterator is at. I tried looking at the armadillo-documentation but there was no further information on the actual interface of said iterator apart from its existence and how to initialize it with begin(). Also looking through the actual code of armadillo did not help me as I haven't found the definition of arma:mat::col_iterator.

So my question is what do the above errors tell me and how can I fix this? Also if you happen to know a better approach to the described problem that would be highly appreciated as well. :)

Beforehand I tried writing a custom random-access-operator for the rows of arma::mat but this failed as I could not overload value_type& operator*() { return A.row(i); } as A.row(i) seems to return a temporary arma::vec object or something.

Edit: I should mention that I tried implementing my own iterator using this code snippet and adjusting it to my needs. The swap-function above is also inspired by this code-snippet.

plumbum452
  • 11
  • 3
  • I think you are misunderstanding how iterator are supposed to work https://www.amazon.co.uk/STL-Tutorial-Reference-Guide-Addison-Wesley/dp/0321702123 – Alessandro Teruzzi Feb 18 '21 at 12:55

1 Answers1

1

I don't know armadillo library, but your error message seems to suggest that arma::mat::col_iterator is a typedef of double*.

Your goal is to swap the value pointed by the iterator. Let's try to reproduce it with a simple stand alone test:

#include <iostream>
#include <algorithm>

int main() {
    double* a = new double[2];
    double* b = new double[2];

    a[0] = 1.0;
    a[1] = 2.0;
    b[0] = 10.0;
    b[1] = 20.0;

    std::swap(a,b);
    std::cout << a[0] << "," << a[1] << std::endl;
    std::cout << b[0] << "," << b[1] << std::endl;    
    delete[] a;
    delete[] b;
    return 0;
}

The above will produce this output:

10,20

1,2

The whole vector was swapped, in order to achieve what you want you should provide you custom swap function that just swap the values pointed by the iterator, this is easy enough in our tests:

void swap(double*& a, double*& b) 
{
    std::swap(*a, *b);
}

So, I would modify your swap function to be:

void swap(arma::mat::col_iterator& lhs, arma::mat::col_iterator& rhs)
{
    std::swap(*lhs,*rhs);
}

Update, re-reading the question I am not sure if you want to swap the elements or the entire row, in any case you can do both using the correct swap function.

Alessandro Teruzzi
  • 3,584
  • 1
  • 24
  • 35
  • Thanks for the answer! My goal is to swap the contents of the two entire rows (not just the i-th entry of those rows) after comparing only the i-th entry of the rows. So simply using ```swap(arma::mat::col_iterator& lhs, arma::mat::col_iterator& rhs)``` did not work in my case as it would only swap the i-th entry. My problem is that I don't know how to modify swap in the correct way here. – plumbum452 Feb 18 '21 at 14:28