You will also want to assign data to the edges or nodes of the graph, for example a length or capacity function defined on the edges. You can do this in LEMON using so called maps. You can define a map on the nodes or on the edges of the graph and the value of the map (the range of the function) can be practically almost of any type. Read more about maps here.
In this quick tour we want to show you some facilities LEMON library can provide through examples (simple demo programs). The examples will only show part of the functionality, but links will always be given to reach complete details. You will find links next to the code fragments that help to download full demo programs: save them on your computer and compile them according to the description in the page about How to start using LEMON.
Have fun!
First we show how to add nodes and edges to a graph manually. We will also define a map on the edges of the graph. After this we show the way one can read a graph (and perhaps maps on it) from a stream (e.g. a file). Of course we also have routines that write a graph (and perhaps maps) to a stream (file): this will also be shown. LEMON supports the DIMACS file formats to read network optimization problems, but more importantly we also have our own file format that gives a more flexible way to store data related to network optimization.
#include <iostream> #include <lemon/list_graph.h> int main() { typedef lemon::ListGraph Graph; typedef Graph::EdgeIt EdgeIt; typedef Graph::Edge Edge; typedef Graph::NodeIt NodeIt; typedef Graph::Node Node; typedef Graph::EdgeMap<int> LengthMap; using lemon::INVALID; Graph g; Node s=g.addNode(); Node v2=g.addNode(); Node v3=g.addNode(); Node v4=g.addNode(); Node v5=g.addNode(); Node t=g.addNode(); Edge s_v2=g.addEdge(s, v2); Edge s_v3=g.addEdge(s, v3); Edge v2_v4=g.addEdge(v2, v4); Edge v2_v5=g.addEdge(v2, v5); Edge v3_v5=g.addEdge(v3, v5); Edge v4_t=g.addEdge(v4, t); Edge v5_t=g.addEdge(v5, t); LengthMap length(g); length[s_v2]=10; length[s_v3]=10; length[v2_v4]=5; length[v2_v5]=8; length[v3_v5]=5; length[v4_t]=8; length[v5_t]=8; std::cout << "Hello World!" << std::endl; std::cout << std::endl; std::cout << "This is library LEMON here! We have a graph!" << std::endl; std::cout << std::endl; std::cout << "Nodes:"; for (NodeIt i(g); i!=INVALID; ++i) std::cout << " " << g.id(i); std::cout << std::endl; std::cout << "Edges:"; for (EdgeIt i(g); i!=INVALID; ++i) std::cout << " (" << g.id(g.source(i)) << "," << g.id(g.target(i)) << ")"; std::cout << std::endl; std::cout << std::endl; std::cout << "There is a map on the edges (length)!" << std::endl; std::cout << std::endl; for (EdgeIt i(g); i!=INVALID; ++i) std::cout << "length(" << g.id(g.source(i)) << "," << g.id(g.target(i)) << ")="<<length[i]<<std::endl; std::cout << std::endl; }
See the whole program in file hello_lemon.cc in the demo
subdir of LEMON package.
If you want to read more on the LEMON graph structures and concepts, read the page about graphs.
So here is a file describing a graph of 6 nodes (0 to 5), two nodemaps (called coordinates_x
and coordinates_y
), several edges, an edge map called capacity
and two designated nodes (called source
and target
).
@nodeset id coordinates_x coordinates_y 5 796.398 208.035 4 573.002 63.002 3 568.549 401.748 2 277.889 68.476 1 288.248 397.327 0 102.239 257.532 @edgeset id capacity 4 5 6 8 3 5 5 8 2 4 4 5 1 4 3 8 1 3 2 5 0 2 1 10 0 1 0 10 #This is a comment here @nodes source 0 target 5 @edges @attributes author "Attila BERNATH" @end
Finally let us give a simple example that reads a graph from a file and writes it to the standard output.
#include <iostream> #include <lemon/smart_graph.h> #include <lemon/graph_reader.h> #include <lemon/graph_writer.h> using namespace lemon; int main() { SmartGraph graph; try { std::string filename="sample.lgf"; std::string name; GraphReader<SmartGraph> reader(filename,graph); SmartGraph::EdgeMap<int> cap(graph); reader.readEdgeMap("capacity",cap); reader.readAttribute("name",name); reader.run(); std::cout << "Hello! We have read a graph from file " << filename<< " and some maps on it:\n now we write it to the standard output!" << std::endl; GraphWriter<SmartGraph> writer(std::cout, graph); writer.writeEdgeMap("multiplicity", cap); writer.writeAttribute("name",name); writer.run(); } catch (DataFormatError& error) { std::cerr << error.what() << std::endl; } return 0; }
See the whole program in file reader_writer_demo.cc.
Graph g; std::ifstream f("graph.dim"); readDimacs(f, g);
One can also store network (graph+capacity on the edges) instances and other things (minimum cost flow instances etc.) in DIMACS format and read these in LEMON: to see the details read the documentation of the Dimacs file format reader.
s
and t
in a graph g
. We omit the part reading the graph g
and the length map len
.
typedef ListGraph Graph; typedef Graph::Node Node; typedef Graph::Edge Edge; typedef Graph::EdgeMap<int> LengthMap; Graph g;
std::cout << "Dijkstra algorithm demo..." << std::endl; Dijkstra<Graph, LengthMap> dijkstra_test(g,len); dijkstra_test.run(s); std::cout << "The distance of node t from node s: " << dijkstra_test.dist(t) << std::endl; std::cout << "The shortest path from s to t goes through the following " << "nodes (the first one is t, the last one is s): " << std::endl; for (Node v=t;v != s; v=dijkstra_test.predNode(v)) { std::cout << g.id(v) << "<-"; } std::cout << g.id(s) << std::endl;
See the whole program in dijkstra_demo.cc.
Some explanation: after instantiating a member of the Dijkstra class we run the Dijkstra algorithm from node s
. After this we read some of the results. You can do much more with the Dijkstra class, for example you can run it step by step and gain full control of the execution. For a detailed description, see the documentation of the LEMON Dijkstra class.
First make a graph g
and a cost map edge_cost_map
, then make a bool edgemap tree_map
or a vector tree_edge_vec
for the algorithm output. After calling the function it gives back the weight of the minimum spanning tree and the tree_map
or the tree_edge_vec
contains the edges of the tree.
If you want to store the edges in a bool edgemap, then use the function as follows:
// Kruskal with boolmap; std::cout << "The weight of the minimum spanning tree is " << kruskal(g, edge_cost_map, tree_map)<<std::endl;
And if you rather use a vector instead of a bool map:
// Kruskal with vector; std::cout << "The weight of the minimum spanning tree again is " << kruskal(g, edge_cost_map, std::back_inserter(tree_edge_vec)) <<std::endl;
See the whole program in kruskal_demo.cc.
So far we have an interface for the commercial LP solver software CPLEX (developed by ILOG) and for the open source solver GLPK (a shorthand for Gnu Linear Programming Toolkit).
We will show two examples, the first one shows how simple it is to formalize and solve an LP problem in LEMON, while the second one shows how LEMON facilitates solving network optimization problems using LP solvers.
//A default solver is taken Lp lp; typedef Lp::Row Row; typedef Lp::Col Col; std::cout<<"A program demonstrating the LEMON LP solver interface"<<std::endl; std::cout<<"Solver used: "<<default_solver_name<<std::endl; //This will be a maximization lp.max(); //We add coloumns (variables) to our problem Col x1 = lp.addCol(); Col x2 = lp.addCol(); Col x3 = lp.addCol(); //Constraints lp.addRow(x1+x2+x3 <=100); lp.addRow(10*x1+4*x2+5*x3<=600); lp.addRow(2*x1+2*x2+6*x3<=300); //Nonnegativity of the variables lp.colLowerBound(x1, 0); lp.colLowerBound(x2, 0); lp.colLowerBound(x3, 0); //Objective function lp.obj(10*x1+6*x2+4*x3); //Call the routine of the underlying LP solver lp.solve(); //Print results if (lp.primalStatus()==LpSolverBase::OPTIMAL){ std::cout<<"Optimal solution found!"<<std::endl; printf("optimum value = %g; x1 = %g; x2 = %g; x3 = %g\n", lp.primalValue(), lp.primal(x1), lp.primal(x2), lp.primal(x3)); } else{ std::cout<<"Optimal solution not found!"<<std::endl; } //End of LEMON style code
See the whole code in lp_demo.cc.
In the following code we suppose that we already have the graph g
, the capacity map cap
, the source node s
and the target node t
in the memory. We will also omit the typedefs.
//Define a map on the edges for the variables of the LP problem typename G::template EdgeMap<Lp::Col> x(g); lp.addColSet(x); //Nonnegativity and capacity constraints for(EdgeIt e(g);e!=INVALID;++e) { lp.colUpperBound(x[e],cap[e]); lp.colLowerBound(x[e],0); } //Flow conservation constraints for the nodes (except for 's' and 't') for(NodeIt n(g);n!=INVALID;++n) if(n!=s&&n!=t) { Lp::Expr ex; for(InEdgeIt e(g,n);e!=INVALID;++e) ex+=x[e]; for(OutEdgeIt e(g,n);e!=INVALID;++e) ex-=x[e]; lp.addRow(ex==0); } //Objective function: the flow value entering 't' Lp::Expr obj; for(InEdgeIt e(g,t);e!=INVALID;++e) obj+=x[e]; for(OutEdgeIt e(g,t);e!=INVALID;++e) obj-=x[e]; lp.obj(obj); //Maximization lp.max(); //Solve with the underlying solver lp.solve();
The complete program can be found in file lp_maxflow_demo.cc. After compiling run it in the form:
./lp_maxflow_demo < sample.lgf
where sample.lgf is a file in the lemon format containing a maxflow instance (designated "source", "target" nodes and "capacity" map on the edges).