Implement weighted graph - c++

Say I have the following adjacency matrix:
A B C D
A 0 9 0 5
B 9 0 0 0
C 0 0 0 2
D 5 0 2 0
How would this acutally be implemented? I realize I can use a 2D array to represent the weighted edges between vertices but I'm not sure how to represent the vertices.
int edges[4][4];
string vertices[4];
Is this the way to do it? The index in the vertices array corresponds to the row index in the edges array.

You can use a two dimensional std::map
Using this method allows for the matrix to grow and shrink when ever you want.
#include <map>
#include <string>
#include <iostream>
int main()
{
std::map<std::string, std::map<std::string, int>> vertices;
vertices["A"]["A"] = 0; vertices["A"]["B"] = 9; vertices["A"]["C"] = 0; vertices["A"]["D"] = 5;
vertices["B"]["A"] = 9; vertices["B"]["B"] = 0; vertices["B"]["C"] = 0; vertices["B"]["D"] = 0;
vertices["C"]["A"] = 0; vertices["C"]["B"] = 0; vertices["C"]["C"] = 0; vertices["C"]["D"] = 2;
vertices["D"]["A"] = 5; vertices["D"]["B"] = 0; vertices["D"]["C"] = 2; vertices["D"]["D"] = 0;
std::cout << vertices["A"]["A"] << std::endl;
std::cout << vertices["A"]["B"] << std::endl;
}

Going by the indexes of adjacency matrix as vertices is common practice.

If there are a fixed number of vertices in the graph. You could declare the vertices as an enum and index the array directly using the enumerated vertex names. I think it makes the mapping a little clearer.
enum VERTEX
{
A = 0,
B,
C,
D,
LAST = D
};
int edge[LAST+1][LAST+1];
edge[A][A] = 0;
edge[A][B] = edge[B][A] = 9;
edge[A][C] = edge[C][A] = 0;
// etc.
This keeps things simple and quick by allowing you to use an array without any look-up penalty while keeping things easy to understand.

For most intents and purposes it's usually more efficient to represent the graph as an adjentacy list:
std::vector< std::list<int> > graph;
So graph[i] is all neighboring vertexes of i. The advantage being that when dealing with graphs we usually want to traverse i's neighbors. Also, for large sparse graphs, space complexity is much lower. This of course could also be extended to include weights with something like:
std::vector< std::list<std::pair< int, int> > > graph;
or for more complex types, define a type Vertex...
EDIT: If you require indices as strings, this could easily be done by opting to std::map instead of std::vector:
std::map< std::string, std::list<std::pair< std::string /*vertex*/, int/*weight*/> > > graph;
But it appears from your original post that you just wish to map indexes to vertex names, in which case I'd go with the first soluion but also keep the vertex name mapped to the index:
std::vector< std::pair< std::string/*name*/, std::list<std::pair< int /*index*/, int/*weight*/> > > > graph;

Related

How to split set of vertices with Boost Graph?

I'm writing some algorithm in C++ for parallel graph coloring using Boost Graph and adjacency_list.
I'm working with very big graph (the smallest has 32K vertices).
What I'm trying to do is to take the whole set of vertices, split them in parts and assign each part to a different thread and work in parallel, but I'm struggling with some passages.
The basic idea what this:
int step = g.m_vertices.size()/4;
int min = 0;
for(int i = 0; i < 4; i++){
// call the function
}
And the function I call inside is something like that
for (vp = vertices(g); vp.first != vp.second; ++vp.first) {
cout << *vp.first << endl;
}
So I have two questions:
g.m_vertices.size()/4; is the right solutions? If initially I have 10 vertices, then I remove some vertex in the middle (e.g. 4), only 6 vertices left (so this is the new size) but the index of the vertices go from 0 to 5 or from 0 to 9?
How can pass only a subset of vertices to vp instead of passing vertices(g)?
g.m_vertices.size()/4; is the right solutions?
That depends ONLY on your requirements.
If initially I have 10 vertices, then I remove some vertex in the middle (e.g. 4), only 6 vertices left (so this is the new size) but the index of the vertices go from 0 to 5 or from 0 to 9?
That depends on your graph model. You don't specify the type of your graph (I know, you do say which template, but not the template parameters). Assuming vecS for the Vertex container selector, then yes, after 4 removals, the vertex descriptors (and index) will be [0,6).
How can pass only a subset of vertices to vp instead of passing vertices(g)
Many ways.
you can std::for_each with a parallel execution policy
you can use openmp to create a parallel section from the plain loop
you can use filtered_graph adapter to create 4 "views" of the underlying graph and operate on those
you can use PBGL which is actually created for dealing with huge graphs. This has the added benefit that it works with threading/interprocess/inter-host communication, can coordinate algorithms across segments etc.
you can use sub_graphs; this is mainly (only) interesting if the way your graphs get built have a natural segmentation
None of the solutions are trivial. But, here's naive demo using filtered_graph:
Live On Compiler Explorer
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/random.hpp>
#include <iostream>
#include <random>
using G = boost::adjacency_list<>;
using V = G::vertex_descriptor;
G make_graph() {
G g;
std::mt19937 prng(std::random_device{}());
generate_random_graph(g, 32 * 1024 - (prng() % 37), 64 * 1024, prng);
return g;
}
template <int NSegments, int Segment> struct SegmentVertices {
std::hash<V> _h;
bool operator()(V vd) const { return (_h(vd) % NSegments) == Segment; }
};
template <int N>
using Quart = boost::filtered_graph<G, boost::keep_all, SegmentVertices<4, N>>;
template <typename Graph>
void the_function(Graph const& g, std::string_view name)
{
std::cout << name << " " << size(boost::make_iterator_range(vertices(g)))
<< " vertices\n";
}
int main()
{
G g = make_graph();
the_function(g, "full graph");
Quart<0> f0(g, {}, {});
Quart<1> f1(g, {}, {});
Quart<2> f2(g, {}, {});
Quart<3> f3(g, {}, {});
the_function(f0, "f0");
the_function(f1, "f1");
the_function(f2, "f2");
the_function(f3, "f3");
}
Printing e.g.
full graph 32766 vertices
f0 8192 vertices
f1 8192 vertices
f2 8191 vertices
f3 8191 vertices

Create 2D vector from 2 1D vectors

I am fairly new to vectors and I'm trying to populate a 2D vector from 2 1D vectors for coordinate points. I have 2 vectors like this where source_x and source_y contains values from a file:
std::vector<float,T<float>> pos_x(5);
std::vector<float,T<float>> pos_y(5);
for (int i = 0; i < 5 ; i++){
pos_x[i] = i+1; //{1,2,3,4,5}
}
for (int i = 0; i < num ; i++){
pos_y[i] = i+1 ; //{1,2,3,4,5}
}
I created my 2D vector like this:
std::vector<std::vector<float, T<float>>> position;
for (int i = 0; i < num ; i++){
for (int j = 0; j < num ; i++){
//Output expected: {{1,2,3,4,5},{1,2,3,4,5}}
position[i][j] = //I'm confuse here
}
}
I am not certain how to populate pos_x to position[i] and pos_y to position[j].
Thank you
So my guess is this
std::vector<std::vector<float>> position(num, std::vector<float>(2));
for (int i = 0; i < num ; i++){
position[i][0] = pos_x[i];
position[i][1] = pos_y[i];
}
But I could easily be wrong.
UPDATE based on the example in the question I now think this is the correct code
std::vector<std::vector<float>> position(2, std::vector<float>(num));
for (int i = 0; i < num ; i++){
position[0][i] = pos_x[i];
position[1][i] = pos_y[i];
}
There's no such thing as a 2D vector. The truth is that you can create a vector that contains vectors. The first vector is used as an index in the collected vectors.
Note that this concept is similar to a 2D array: arr[3][4] means 3 indices, each one points to 4 data.
To create such 2D vector:
std::vector< std::vector <float>> positions.
Notice I didn't use the second parameter (as in std::vector<float, SomeAllocator> because we don't need this custom memory allocator.
Also notice that, contrary to arrays, I did't tell anything about the sizes of each vector, because the std::vector will take care of it.
Let's populate it.
The "main" vector contains vectors. So these secondary vectors may be created before stored in the "main" one.
std::vector<float> v1; //secondary
positions.push_back(v1); //add it to main vector
Put some values in the secondary:
v1.push_back(7.5);
v1.push_back(-3.1);
Another way is to access through the main vector. If we new this main vector contains v1 in its first index:
positions[0].push_back(8.); // same as v1.push_back(8.) if positions[0] refers to v1
or better using "at": positions.at(0).push_back(8.);
Change some value:
v1.at(1) = 66.88;
or
positions[0].at(1) = 66.88;
You can also do v1[1] = 66.88 but prefer the at() methof because it will check that index "1" is allowed by the size of the vector v1.
You can create and add another secondary vector:
std::vector<float> v2; //secondary
positions.push_back(v2); //add it to main vector
and work with it the same as with previous v1. Now, positions[1] refers to v2
I leave the rest of pulling from other vectors to you.

How can I check if a vector is pointing to null vector in a 2d-vector in c++?

So, I have the following case:
I declared a vector of vector of integers as vector < vector<int> > edges. Basically, I am trying to implement a graph using the above where graph is defined as follows:
class graph
{
public:
int vertices;
vector < vector<int> > edges;
};
Now, during the insertion of an edge, I take input as the starting vertex and ending vertex. Now, I want to do something like this:
void insert(int a, int b, graph *mygraph) // a is starting vertex and b is ending vertex
{
auto it = mygraph->edges.begin();
//int v = 1;
vector<int> foo;
foo.push_back(b);
if (mygraph->edges[a].size() != 0) // Question here?
mygraph->edges[a].push_back(b);
else
mygraph->edges.push_back(foo);
return;
}
Now, in the line marked with Question here, basically, I want to check if the vector for that particular entry exists or not? size is actually wrong because I am trying to call size operation on a vector which doesn't exists. In other words, I want to check, if there is a vector which exists at a particular location in vector of vectors. How can I do it? Something like, mygraph->edges[a] != NULL?
Simply check that a does not exceed size of the vector. If it does, then resize the outer vector.
void insert(int a, int b, graph &mygraph) { // a is starting vertex and b is ending vertex
if (a >= mygraph.edges.size())
mygraph.edges.resize(a+1);
mygraph.edges[a].push_back(b);
}
You can approach your problem in two different ways:
Initialize edges to the number of vertices, and don't allow other vertices to be inserted after that. Why is that?
std::vector< std::vector<int> > v = { {1}, {2} };
// now you need to add an edge between vertex 4 and vertex 5
std::vector<int> edges3;
v.push_back(edges3); // v = { {1}, {2}, {} }
std::vector<int> edges4 = {5};
v.push_back(edges4); // v = { {1}, {2}, {}, {5} }
If you don't want to do it like that, you'd have to do something like this first:
std::vector< std::vector<int> > v;
for (int i = 0; i < maxVertices; i++)
{
std::vector<int> w;
v.push_back(w);
}
// now you need to add an edge between vertex 4 and vertex 5
v[4].push_back(5);
Change the structure used for edges, probably to something better suited for sparse matrices (which looks like your case here, since probably not every vertex is connected to every other vertex). Try:
std::map< int, std::vector<int> > edges;
That way you can match a single vertex with a list of other vertices without the need to initialize edges to the maximum possible number of vertices.
std::vector<int> vertices = {5};
edges[4] = vertices;
Edges is a vector of vectors. Vectors are stored contiguously in memory. You insert elements into a vector from the end. If the size of vector is 10, all 10 members are contiguous and their indexes are going to range from 0-9. If you delete a middle vector, say 5th, all vectors from index 6-9 get shifted up by 1.
The point of saying all this is that you can't have a situation where edges would have an index that doesn't hold a vector. To answer your question, a vector for index a would exist if
a < mygraph->edges.size ();

passing several boost multi_array from function

I have a bunch of 3d arrays generated using boost::multi_array in a function. I would not want to use all these arrays in another code of mine is there any way to do this?
When I had a 2d case what I did was
typedef boost::numeric::ublas::matrix<double> fils;
boost::array<fils,5> filter1(unsigned width, unsigned height)
{
matrix<double>l,m,n,o,p;
//perform other steps//
boost::array<fils,5> t={l,m,n,o,p};
return t;
}
main.cpp
int main()
{
boost::array<fils,5> z;
z= t(w,h);
}
for the 2d case this method works fine. I now want to do the same with a 3D case where
typedef boost::multi_array<double,3>x;
boost::array<x,12>x1(unsigned w,unsigned h,unsigned s)
{
typedef boost::multi_array<double,3>M;
typedef M::index Mi;
m l(boost::extents[w][h][s]),m(boost::extents[w][h][s]),n(boost::extents[w][h][s]),o(boost::extents[w][h][s]);
//perform steps//
}
how do I get the matrices l,m,n,o,p so that I can use them as source in other bits of code.
In my opinion the most elegant solution is to declare a 4-D multi_array like so :
typedef boost::multi_array<double,4> FloatArray4D;
typedef M::index Mi;
function create4dArray()
{
FloatArray4D returnValue(boost::extents[w][h][s][4]);
// Populate the array as you please here is an example.
for (int i = 0; i < 4; i++) {
for (int j = 0; j < w; j++) {
for (int k = 0; k < h; k++) {
for (int x = 0; x < s; x++) {
returnValue[j][k][x][i] = i+j*10+k*100+x*1000;
}
}
}
}
return returnValue;
}
Then you can access the subarray by indexing on the last coordinate. It might be more efficient to index them by the first coordinate (in terms of localization of the data) but I don't know the implementation details of boost::multi_array (can someone weight in on this in comments ?)
To extract a view (no-copy) of your 3-D data from the 4-D multi_array created you can use this :
typedef boost::multi_array_types::index_range range;
FloatArray4D::index_gen indices;
FloatArray4D my4DArray = create4dArray();
// Create a new view with 3 dimentions (corresponding to your l) fixing the 4th dimention to 0
FloatArray4D::array_view<3>::type l = [indices[range()][range()][range()][0];
then you can use l as if it was your 3-D array.
PS: NEVER name something x or M, especially not a type. Yes long names are a pain to type, but get a decent text editor with auto-completion and it won't be a problem.
Knowing what an object is by its name however, will always be great. It improves readability, for you and for anyone else who has to read your code.
Also do not typedef inside a function. If you want to define a custom type do it in a header file that is shared.
You don't want to have to declare that type everywhere.
And actually don't overuse typedef, only use it if it improves readability.

Creating random undirected graph in C++

The issue is I need to create a random undirected graph to test the benchmark of Dijkstra's algorithm using an array and heap to store vertices. AFAIK a heap implementation shall be faster than an array when running on sparse and average graphs, however when it comes to dense graphs, the heap should became less efficient than an array.
I tried to write code that will produce a graph based on the input - number of vertices and total number of edges (maximum number of edges in undirected graph is n(n-1)/2).
On the entrance I divide the total number of edges by the number of vertices so that I have a const number of edges coming out from every single vertex. The graph is represented by an adjacency list. Here is what I came up with:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <list>
#include <set>
#define MAX 1000
#define MIN 1
class Vertex
{
public:
int Number;
int Distance;
Vertex(void);
Vertex(int, int);
~Vertex(void);
};
Vertex::Vertex(void)
{
Number = 0;
Distance = 0;
}
Vertex::Vertex(int C, int D)
{
Number = C;
Distance = D;
}
Vertex::~Vertex(void)
{
}
int main()
{
int VertexNumber, EdgeNumber;
while(scanf("%d %d", &VertexNumber, &EdgeNumber) > 0)
{
int EdgesFromVertex = (EdgeNumber/VertexNumber);
std::list<Vertex>* Graph = new std::list<Vertex> [VertexNumber];
srand(time(NULL));
int Distance, Neighbour;
bool Exist, First;
std::set<std::pair<int, int>> Added;
for(int i = 0; i < VertexNumber; i++)
{
for(int j = 0; j < EdgesFromVertex; j++)
{
First = true;
Exist = true;
while(First || Exist)
{
Neighbour = rand() % (VertexNumber - 1) + 0;
if(!Added.count(std::pair<int, int>(i, Neighbour)))
{
Added.insert(std::pair<int, int>(i, Neighbour));
Exist = false;
}
First = false;
}
}
First = true;
std::set<std::pair<int, int>>::iterator next = Added.begin();
for(std::set<std::pair<int, int>>::iterator it = Added.begin(); it != Added.end();)
{
if(!First)
Added.erase(next);
Distance = rand() % MAX + MIN;
Graph[it->first].push_back(Vertex(it->second, Distance));
Graph[it->second].push_back(Vertex(it->first, Distance));
std::set<std::pair<int, int>>::iterator next = it;
First = false;
}
}
// Dijkstra's implementation
}
return 0;
}
I get an error:
set iterator not dereferencable" when trying to create graph from set data.
I know it has something to do with erasing set elements on the fly, however I need to erase them asap to diminish memory usage.
Maybe there's a better way to create some undirectioned graph? Mine is pretty raw, but that's the best I came up with. I was thinking about making a directed graph which is easier task, but it doesn't ensure that every two vertices will be connected.
I would be grateful for any tips and solutions!
Piotry had basically the same idea I did, but he left off a step.
Only read half the matrix, and ignore you diagonal for writing values to. If you always want a node to have an edge to itself, add a one at the diagonal. If you always do not want a node to have an edge to itself, leave it as a zero.
You can read the other half of your matrix for a second graph for testing your implementation.
Look at the description of std::set::erase :
Iterator validity
Iterators, pointers and references referring to elements removed by
the function are invalidated.
All other iterators, pointers and
references keep their validity.
In your code, if next is equal to it, and you erase element of std::set by next, you can't use it. In this case you must (at least) change it and only after this keep using of it.