2

SO. I am working with an igraph object and I want to iterate over vertices in a particular order. The order is determined by a vertex attribute called "value" and I'd like to operate highest-to-lowest. igraph can provide all of the values as an igraph_vector_t in vertex id order. If vertex 17 has the highest value, I want to operate on it first.

After searching SO, I starting looking into the C++ boost multi_index. Here is a supporting struct:

struct indexed_vertex {
    igraph_integer_t vid;
    igraph_real_t value;
    indexed_vertex(igraph_integer_t vid, igraph_real_t value):vid(vid),value(value){}

    bool operator<(const indexed_vertex &vertex) const {
        return value<vertex.value;
    }
};

I created the following index object:

typedef boost::multi_index::multi_index_container<
        indexed_vertex,
        boost::multi_index::indexed_by<
                boost::multi_index::hashed_unique<
                    boost::multi_index::member<indexed_vertex, igraph_integer_t, &indexed_vertex::vid>
                >,
                boost::multi_index::ordered_non_unique<
                        boost::multi_index::member<indexed_vertex, igraph_real_t, &indexed_vertex::value>
                >
        >
> indexed_vertex_set;

My next trick is to visit the vertices in descending order. I've attempted this (from the docs)but fail almost instantly (hey! fail fast, right?)

indexed_vertex_set ivs;
indexed_vertex_set::nth_index<1>::type::iterator it = ivs.get<1>();

with the error

 error: no viable conversion from 'typename nth_index<1>::type' (aka 'boost::multi_index::detail::ordered_index<boost::multi_index::member<indexed_vertex, double, &indexed_vertex::value>, std::__1::less<double>, boost::multi_index::detail::nth_layer<2, indexed_vertex, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::member<indexed_vertex, int, &indexed_vertex::vid>, mpl_::na, mpl_::na, mpl_::na>, boost::multi_index::ordered_non_unique<boost::multi_index::member<indexed_vertex, double, &indexed_vertex::value>, mpl_::na, mpl_::na>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, std::__1::allocator<indexed_vertex> >, boost::mpl::vector0<mpl_::na>, boost::multi_index::detail::ordered_non_unique_tag, boost::multi_index::detail::null_augment_policy>') to 'indexed_vertex_set::nth_index<1>::type::iterator' (aka 'bidir_node_iterator<node_type>')
    indexed_vertex_set::nth_index<1>::type::iterator it = ivs.get<1>();

I've tried a few other variants, but keep getting back to this error. I'd appreciate suggestions. I haven't used the multi_index before, so I'm expecting I am fundamentally misunderstanding the scope.

BONUS QUESTION

Since it's the holidays, I'll point out my next task will be to do something like

for (vertex in iterator) {
   get-vertex-id();
   get-vertex-value();
   look-up-vertex-and-modify();
}

So if you're feeling generous, I'd appreciate guidance there as well.

Brian Dolan
  • 3,086
  • 2
  • 24
  • 35

2 Answers2

3

ivs.get<1>() gives you index, not iterator. You need to call begin(), end() and other methods on that index to get iterator (like you do on containers). You better use typedef though:

indexed_vertex_set ivs;
typedef indexed_vertex_set::nth_index<1>::type sorted_index;
sorted_index &idx = ivs.get<1>();
for( sorted_index::iterator it = idx.begin(); it != idx.end(); ++it ) {
    it->vid = 123; // getting access to fields
}
Slava
  • 43,454
  • 1
  • 47
  • 90
  • You are my person of the day. If you have a moment, could you explain it a bit more. I'm pretty new to C. Should I put the typedef in my header file? – Brian Dolan Dec 16 '15 at 22:00
  • Yes definitely you can same way as you typedef `boost::multi_index::multi_index_container` itself. You may want to localize that names to avoid conflicts, but that separate topic. – Slava Dec 16 '15 at 22:04
  • and reversing it, should I do `it=idx.end()-1; it!=idx.begin()` ? – Brian Dolan Dec 16 '15 at 22:07
  • 1
    No, better use `rbegin()` `rend()` - as it!=idx.begin() will miss one element, and `idx.end()-1` will work only on some indexes (which provide random access) – Slava Dec 16 '15 at 22:15
  • Awesome tip, ended up with `for (ivs_sorted::reverse_iterator it = idx.rbegin(); it!=idx.rend() && ivid << " / " << it->value << "!" << std::endl; }` – Brian Dolan Dec 16 '15 at 22:19
3

With C++11 this can be simpler:

mic_structure mic;

// ...

for (auto & it : mic.get<0>()) {
    // do something with iterator
}
maxywb
  • 2,275
  • 1
  • 19
  • 25