Commit 034718a3 authored by Szabolcs Horvát's avatar Szabolcs Horvát
Browse files

Add command line interface

parent ee08caf7
......@@ -6,4 +6,4 @@ See `Introduction.nb` in the `Mathematica` directory.
### Using the sampler from the command line
*Not implemented yet!*
See the [README](command_line/README.md) file in the `command_line` directory.
cmake_minimum_required(VERSION 3.2)
project(ConnectedGraphSampler)
set(CMAKE_CXX_STANDARD 14)
find_package( Boost 1.57 COMPONENTS program_options REQUIRED )
include_directories(
../src
${Boost_INCLUDE_DIR}
)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
add_executable( cdsample cdsample.cpp )
target_link_libraries( cdsample LINK_PUBLIC ${Boost_LIBRARIES} )
This directory contains a command line program `cdsample` that provides an interface to the graph sampler.
### Prerequisites
- A C++ compiler with C++14 support
- [CMake](https://cmake.org/)
- The [Boost](https://www.boost.org/) library for [Boost Program Options](https://www.boost.org/doc/libs/release/libs/program_options/).
### Compiling
Create a new directory, enter it, then run `cmake`:
```
mkdir build
cd build
cmake ..
```
If the configuration succeeded, run `make`:
```
make
```
An executable named `cdsample` will be created in the current directory.
### Example usage
Getting help:
```
$ ./cdsample -h
Usage:
./cdsample input_file
./cdsample --degrees d1 d2 d3
Allowed options:
-h [ --help ] produce help message
-f [ --file ] arg file containing degree sequence
-d [ --degrees ] arg degree sequence
-c [ --connected ] generate connected graphs
-m [ --multi ] generate loop-free multigraphs
-a [ --alpha ] arg (=1) set parameter for the heuristic
-n [ --count ] arg (=1) how many graphs to generate
-s [ --seed ] arg set random seed
```
Generate one graph with the degree sequence (1, 1, 2, 2, 3, 3):
```
$ ./cdsample -d 1 1 2 2 3 3
-7.3677085723743714
1 2
3 5
3 6
4 6
4 5
5 6
```
The first line is the natural logarithm of this graph's sampling weight. The following are tab-separated pairs of vertices representing the edges of the graph.
By default, not-necessarily-connected simple graphs are generated. The above graph is not connected: it contains the edge `(1,2)`. Vertices 1 and 2 are both of degree 1, thus in this graph they form a closed connected component.
To sample only connected graphs, use the `-c` option. To allow loop-free multigraphs, use the `-m` option.
To generate multiple graphs, use the `-n` option. Multiple outputs will be separated by a single empty line. The following example generates three loopless multigraphs:
```
$ ./cdsample -d 1 1 2 2 3 3 -mc -n 3
-8.6586927536899356
1 3
2 4
3 5
4 6
5 6
5 6
-8.5251613610654147
1 5
2 3
3 6
4 6
4 5
5 6
-8.9306264691735784
1 5
2 3
3 5
4 6
4 6
5 6
```
The degree sequence can be read from a file. Instead of using the `-d` argument, simply specify the file name, e.g. `cdsample degrees.txt`. An example degree sequence file, `degrees.txt`, is included.
#include "Sampler.h"
#include "ConnSampler.h"
#include "SamplerMulti.h"
#include "ConnSamplerMulti.h"
#include <boost/program_options.hpp>
#include <random>
#include <string>
#include <limits>
#include <iostream>
#include <fstream>
namespace po = boost::program_options;
using namespace CDS;
using namespace std;
int main(int argc, char *argv[]) {
try {
// Set up program options
po::options_description desc("Allowed options");
desc.add_options()
("help,h", "produce help message")
("file,f", po::value<string>(), "file containing degree sequence")
("degrees,d", po::value<vector<deg_t>>()->multitoken(), "degree sequence")
("connected,c", po::bool_switch(), "generate connected graphs")
("multi,m", po::bool_switch(), "generate loop-free multigraphs")
("alpha,a", po::value<double>()->default_value(1.0), "set parameter for the heuristic")
("count,n", po::value<long>()->default_value(1L), "how many graphs to generate")
("seed,s", po::value<long>(), "set random seed")
;
po::positional_options_description p;
p.add("file", 1);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
options(desc).positional(p).run(), vm);
po::notify(vm);
if (vm.count("help") || ! (vm.count("file") || vm.count("degrees"))) {
if (! vm.count("help"))
cerr << "Error: No degree sequence was given!\n";
cout << "Usage:\n"
<< argv[0] << " input_file\n"
<< argv[0] << " --degrees d1 d2 d3\n\n"
<< desc << "\n";
if (vm.count("help"))
return 0;
else
return 1;
}
if (vm.count("file") && vm.count("degrees")) {
cerr << "Error: On the command line, provide either an input file, or an explicit degree sequence, but not both!\n";
return 1;
}
// Read program options and degree sequence
double alpha = vm["alpha"].as<double>();
long n = vm["count"].as<long>();
vector<deg_t> degrees;
if (vm.count("file")) {
ifstream dsfile(vm["file"].as<string>());
if (! dsfile) {
cerr << "Error: Could not open " << vm["file"].as<string>() << "!\n";
return 1;
}
deg_t d;
while (dsfile >> d)
degrees.push_back(d);
if (! dsfile.eof()) {
cerr << "Error: Unexpected input in degree sequence file!\n";
return 1;
}
} else {
degrees = vm["degrees"].as<vector<deg_t>>();
}
// Set up random number generator
mt19937 rng(random_device{}());
if (vm.count("seed"))
rng.seed(vm["seed"].as<long>());
// Generate samples
// Ensure that no precision is lost when printing weight values
cout.precision(numeric_limits<double>::max_digits10);
for (; n > 0; --n) {
edgelist_t edges;
double logprob;
if (vm["multi"].as<bool>()) {
if (vm["connected"].as<bool>()) {
DegreeSequenceMulti ds(degrees.begin(), degrees.end());
tie(edges, logprob) = sample_conn_multi(ds, alpha, rng);
} else {
DegreeSequenceMulti ds(degrees.begin(), degrees.end());
tie(edges, logprob) = sample_multi(ds, alpha, rng);
}
} else {
if (vm["connected"].as<bool>()) {
DegreeSequence ds(degrees.begin(), degrees.end());
tie(edges, logprob) = sample_conn(ds, alpha, rng);
} else {
DegreeSequence ds(degrees.begin(), degrees.end());
tie(edges, logprob) = sample(ds, alpha, rng);
}
}
cout << logprob << '\n';
for (const auto &edge : edges) {
// 'edges' uses 0-based indexing. Increment vertex names to output with 1-based indexing.
cout << edge.first+1 << '\t' << edge.second+1 << '\n';
}
cout << "\n";
}
}
catch(exception& e) {
cerr << "Error: " << e.what() << "\n";
return 1;
}
catch(...) {
cerr << "Exception of unknown type!\n";
}
return 0;
}
14
12
5
5
4
4
3
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment