VVE constructs

VVE relies on a data structure called VVGraph.

Most of the time a simulation will consists in editing one or more graphs. The graph and its related classes are defined in the graph namespace, which is made directly accessible by the vve.h header file (i.e. you don't need to use fully-qualified names to access the graph::VVGraph, graph::Vertex or graph::Edge classes). It also relies on a new loop construct: the forall loop, provided for the user's convenience.

The forall loop

First, VVE provides a macro to iterate on any STL container without dealing with the iterators. This macro is called forall and can be used like this:

std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
forall(int i, v)
{
  cout << i << " ";
}

This code will output "1 2 3 4 ".

Another way to iterate is by using references. This method allows you to modify the value you are iterating on:

std::vector<double> v;
for(int i = 0 ; i < 100 ; i++)
{
  v.push_back(i);
}
forall(double& d, v)
{
  d = sin(d*2*M_PI/100);
}

At the end of this code, v contains the values of sin for $2k\pi/100$ with $k$ an integer from 0 to 99.

The last useful construct with the forall loop is the iteration using constant references. This is useful to iterate without copying the objects but without modifying them either. Note that you can't iterate overs STL sets using non-constant references!

std::set<double> s;
s.insert(20);
s.insert(10);
s.insert(100);
forall(const double& d, s)
{
  cout << d << " ";
}

This code will output "10 20 100 ".

The last construct will be very useful as the VVE graphs behaves almost like STL sets. Thus it will be most efficient to iterate over a graph using constant references:

vvgraph M;
// Filling in the graph
forall(const vertex& v, M)
{
  // Do something with the vertex
}
Note:
As for any other loop, the forall loop do not keep the container iterated on alive. This means that, if a function returns a container by value, you have to store it in a local variable before iterating of it. Example:

If you have this function defined:

std::vector<int> build_vector();

And you try to iterate like this:

forall(int i, build_vector())
{
  // Do something
}

The result is undefined, as the vector created by build_vector do not exist after the evalution of the function. The correct way to iterate on it is:

std::vector<int> v = build_vector();
forall(int i, v)
{
  // Do whatever
}

Defining your own graph

Before editing and exploring the structure of a graph, you have to define your own. A graph::VVGraph is a class template parametrized by the structure holding the label of vertexes and the structure holding the label of the edges. The usual first step is then to define a new type for your specialized graph:

typedef graph::VVGraph<MyVertexContent,MyEdgeContent> vvgraph;

Once you defined your own graph, you will want to create new types for the smart pointers for vertex and edge access. You have two ways of defining them: either directly using the graph::Vertex and graph::Edge templates, or using the types withing the graph you created. The recommended way (easier and less error-prone) is the second:

typedef vvgraph::vertex_t vertex;
typedef vvgraph::edge_t edge;

Now you have defined all the types you need to fully interact with your home-made graph.

Note:
The types vertex and edge are smart pointers on the actual vertexes and edges. However they are very different smart pointers. First, a vertex can be created (and accessed) outside any graph. To reflect that property, the vertex type is a strong reference on the vertex data. This means that as long as you hold a vertex object, you can access it and the data it holds will be available, even if your removed that vertex from all existing graph. That means also you have to be careful not to create reference loops! The only way to have loop is to put a vertex object in the vertex data structure. If you avoid that (unusual) construct, you won't ever have references loops. Second, an edge do not exists outside a graph. Thus, the edge type is only a weak reference to the edge data. This means that, if you have an edge object and delete that edge from the graph, you must not try to access that edge ever again. Only the graph is responsible to allocating/deallocating edge data and you can't do anything to prevent the data from being destroyed if you delete an edge.

Lookup methods

Now, let's have a look on the methods provided to explore a graph. I will suppose in that section that all the types defined in the previous section (i.e. vvgraph, vertex and edge) are available.

Vertex lookup methods

First, the graph behaves exactly as a STL set of vertexes. So if you know to use a set, you know how to use graph (at least as far as vertexes are concerned). This means you can already iterate on it:

vvgraph M;
forall(const vertex& v, M) // Constant reference, because it behaves as a STL set!
{
  // use v here
}

Now, if you want to use the vertex you iterate on, remember this is a smart pointer, so just act as if it was a pointer on the structure you defined:

struct MyVertexContent
{
  int a;
  double b;
};

typedef graph::VVGraph<MyVertexContent> vvgraph; // If the edge is not specified an empty structure is used.
vvgraph M;

// ...

forall(const vertex& v, M)
{
  v->a = 1;
  v->b = 1.3;
}

Note that when iterating using constant references, the constant qualification applies to the smart pointer, not its content (this is due to the implementation of the graph::Vertex smart pointer, not a general C++ rule!). So you can freely modify the data of a vertex, even if holding a constant reference on a vertex. This is also true for the edges. Note, however, that there exists a constant edge smart pointer, that won't allow you to modify the edge. You will get one if you iterate over a constant graph. As a rule, remember that vertexes content are not considered part of the graph, so modifying them do not modify the graph itself. However, the edges are part of the graph, so modifying them modifies the graph.

If you just want one vertex, you can ask for one using the graph::VVGraph::any method:

vvgraph M;
if(!M.empty())
{
  vertex v = M.any();
  v->a = 0;
}

Neighbors lookup methods

The first basic lookup method for neighbors is the graph::VVGraph::neighbors method, that allows you to iterate over the neighbors of a vertex:

forall(const vertex& n, M.neighbors(v))
{
  n->a = v->a+1;
}

Once you get a neighbor of a vertex, you can also access the neighbors before and after it:

forall(const vertex& n, M.neighbors(v))
{
  vertex nn = M.nextTo(v, n);
  // nn is the neighbor of v after n
  vertex np = M.prevTo(v, n);
  // np is the neighbor of v before n
}

Two useful methods are graph::VVGraph::valence, that gets the number of neighbors and graph::VVGraph::anyIn that returns a neighbor:

if(M.valence(v) > 0)
{
  vertex n = M.anyIn(v);
  n->a = v->a;
}

At last, it is possible to access the incoming edges, using the graph::VVGraph::iNeighbors method:

forall(const vertex& n, M.iNeighbors(v))
{
  M.edge(n, v)->a = 3; // Note that it is the edge n->v that exists, and not v->n
}

Edge lookup methods

The last category of lookups is for the edges. It is pretty simple as there are only three methods: graph::VVGraph::edge, graph::VVGraph::source and graph::VVGraph::target. They will allow you to interact with the edge objects:

vvgraph M;
if(!M.empty())
{
  vertex v = M.any();
  if(M.valence(v) > 0)
  {
    vertex n = M.anyIn(v);
    edge e = M.edge(v, n);
    assert(v == M.source(e));
    assert(n == M.target(e));
  }
}

Edition methods

A graph is constructed entirely using vertexes. The edge being accessed only if you need to label them. Therefore, there are only two categories of edition methods: vertexes and neighbors.

Vertex edition methods

You can add a vertex is using either the graph::VVGraph::addVertex method, which create and add a vertex, or the graph::VVGraph::insert graph, that insert an existing vertex in the graph. The recommended way is the use on graph::VVGraph::insert, rather than graph::VVGraph::addVertex. So you can start populating a graph like that:

vvgraph M;
for(int i = 0 ; i < 100 ; ++i)
{
  vertex v;    // Creates a new vertex object
  v->a = i;    // Initialize the vertex
  M.insert(v); // Insert the vertex in M
}

You can also remove a vertex from the graph using the graph::VVGraph::erase method. As a weird way to clear a graph you can do:

vvgraph M;
// ...
while(!M.empty())
{
  M.erase(M.any());
}

Note that removing a vertex from a graph remove all edges involving that vertex, incoming or outgoing.

At last, you can remove all vertexes at once, using the graph::VVGraph::clear() method:

vvgraph M;
M.clear();

Neighbors edition methods

Once you have at least two vertexes in a graph, you can start adding neighbors to them. Note that, to be valid in any neighborhood-related method, a vertex has to be first inserted into the graph.

The first edition method for the neighborhood is the graph::VVGraph::insertIn method, that allows you to add a vertex into the neighborhood of another one without specifying where:

vvgraph M;
vertex v1, v2;
M.insert(v1);
M.insert(v2);
M.insertIn(v1, v2); // Insert v2 in the neighborhood of v1

Once the neighborhood of v1 is not empty anymore, you can use graph::VVGraph::spliceAfter and graph::VVGraph::spliceBefore to insert new neighbors at specific positions:

vertex v3, v4;
M.insert(v3);
M.insert(v4);
M.spliceBefore(v1, v2, v3); // Splice v3 in v1 before v2
M.spliceAfter(v1, v2, v4); // Splice v4 in v1 after v2

In the end, the neighbors of v1 are ordered as v3 < v2 < v4 < v3.

You can also remove an edge using the graph::VVGraph::eraseEdge method. For example, to remove all outgoing edges from a vertex you can do:

while(M.valence(v) != 0)
{
  M.eraseEdge(v, M.anyIn(v));
}

At last, you can clear all edges (incoming and outgoing) of a vertex using the graph::VVGraph::clear(const vertex_t&) method:

M.clear(v);
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Generated on Fri May 31 15:37:54 2013 for VVE by  doxygen 1.6.3