Changeset - 0290e63ba024
[Not reviewed]
0 3 0
Tom Bannink - 8 years ago 2017-03-06 16:58:42
tombannink@gmail.com
Add start of faster cpp code
3 files changed with 50 insertions and 32 deletions:
0 comments (0 inline, 0 general)
cpp/graph.hpp
Show inline comments
 
@@ -79,25 +79,32 @@ class Graph {
 
        }
 
        return true;
 
    }
 

	
 
    DegreeSequence getDegreeSequence() const {
 
        DegreeSequence d(adj.size());
 
        std::transform(adj.begin(), adj.end(), d.begin(),
 
                       [](const auto &u) { return u.size(); });
 
        return d;
 
    }
 

	
 
    // Assumes valid vertex indices
 
    bool hasEdge(const Edge &e) const {
 
    bool hasEdge(const Edge& e_) const {
 
        Edge e;
 
        if (adj[e_.u].size() <= adj[e_.v].size()) {
 
            e = e_;
 
        } else {
 
            e.u = e_.v;
 
            e.v = e_.u;
 
        }
 
        for (unsigned int v : adj[e.u]) {
 
            if (v == e.v)
 
                return true;
 
        }
 
        return false;
 
    }
 

	
 
    bool addEdge(const Edge &e) {
 
        if (e.u >= adj.size() || e.v >= adj.size())
 
            return false;
 
        if (hasEdge(e))
 
            return false;
cpp/switchchain.cpp
Show inline comments
 
@@ -7,85 +7,103 @@
 
#include <numeric>
 
#include <random>
 
#include <vector>
 

	
 
// Its assumed that u,v are distinct.
 
// Check if all four vertices are distinct
 
bool edgeConflicts(const Edge& e1, const Edge& e2) {
 
    return (e1.u == e2.u || e1.u == e2.v || e1.v == e2.u || e1.v == e2.v);
 
}
 

	
 
class SwitchChain {
 
  public:
 
    SwitchChain() : mt(std::random_device{}()), permutationDistribution(0, 2) {
 
    SwitchChain()
 
        : mt(std::random_device{}()), permutationDistribution(0.5)
 
    // permutationDistribution(0, 2)
 
    {
 
        // random_device uses hardware entropy if available
 
        // std::random_device rd;
 
        // mt.seed(rd());
 
    }
 
    ~SwitchChain() {}
 

	
 
    bool initialize(const Graph& gstart) {
 
        if (gstart.edgeCount() == 0)
 
            return false;
 
        g = gstart;
 
        edgeDistribution.param(
 
            std::uniform_int_distribution<>::param_type(0, g.edgeCount() - 1));
 
        return true;
 
    }
 

	
 
    bool doMove() {
 
        Edge e1 = g.getEdge(edgeDistribution(mt));
 
        Edge e2 = g.getEdge(edgeDistribution(mt));
 
        // Keep regenerating while conflicting edges
 
        Edge e1, e2;
 
        int e1index, e2index;
 
        int timeout = 0;
 
        while (edgeConflicts(e1, e2)) {
 
            e1 = g.getEdge(edgeDistribution(mt));
 
            e2 = g.getEdge(edgeDistribution(mt));
 
        // Keep regenerating while conflicting edges
 
        do {
 
            e1index = edgeDistribution(mt);
 
            e2index = edgeDistribution(mt);
 
            e1 = g.getEdge(e1index);
 
            e2 = g.getEdge(e2index);
 
            ++timeout;
 
            if (timeout % 100 == 0) {
 
                std::cerr << "Warning: sampled " << timeout
 
                          << " random edges but they keep conflicting.\n";
 
            }
 
        }
 
        } while (edgeConflicts(e1, e2));
 

	
 
        // Consider one of the three possible permutations
 
        // 1) e1.u - e1.v and e2.u - e2.v (original)
 
        // 2) e1.u - e2.u and e1.v - e2.v
 
        // 3) e1.u - e2.v and e1.v - e2.u
 

	
 
        // Note that it might be that these new edges already exist
 
        // in which case we also reject the move
 
        // This is checked in exchangeEdges
 
        // in which case we reject the move
 
        bool switchType = permutationDistribution(mt);
 
        if (switchType) {
 
            if (g.hasEdge({e1.u, e2.u}) || g.hasEdge({e1.v, e2.v}))
 
                return false; // conflicting edges
 
        } else {
 
            if (g.hasEdge({e1.u, e2.v}) || g.hasEdge({e1.v, e2.u}))
 
                return false; // conflicting edges
 
        }
 

	
 
        int perm = permutationDistribution(mt);
 
        if (perm == 0) // Original permutation
 
            return false;
 
        return g.exchangeEdges(e1, e2, perm == 1);
 
        // TODO
 
        // rest of the switching process
 

	
 
        // int perm = permutationDistribution(mt);
 
        // if (perm == 0) // Original permutation
 
        //    return false;
 
        // return g.exchangeEdges(e1, e2, perm == 1);
 
        return g.exchangeEdges(e1, e2, switchType);
 
    }
 

	
 
    Graph g;
 
    std::mt19937 mt;
 
    std::uniform_int_distribution<> edgeDistribution;
 
    std::uniform_int_distribution<> permutationDistribution;
 
    //std::uniform_int_distribution<> permutationDistribution;
 
    std::bernoulli_distribution permutationDistribution;
 
};
 

	
 
int main() {
 
    // Generate a random degree sequence
 
    std::mt19937 rng(std::random_device{}());
 

	
 
    // Goal:
 
    // Degrees follow a power-law distribution with some parameter tau
 
    // Expect:  #tri = const * n^{ something }
 
    // The goal is to find the 'something' by finding the number of triangles
 
    // for different values of n and tau
 
    float tauValues[] = {2.2f, 2.35f, 2.5f, 2.65f, 2.8f};
 
    float tauValues[] = {2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f};
 

	
 
    Graph g;
 

	
 
    std::ofstream outfile("graphdata.m");
 
    outfile << '{';
 
    bool outputComma = false;
 

	
 
    for (int numVertices = 200; numVertices <= 1000; numVertices += 100) {
 
        for (float tau : tauValues) {
 

	
 
            DegreeSequence ds(numVertices);
 
            powerlaw_distribution degDist(tau, 1, numVertices);
 
@@ -105,29 +123,28 @@ int main() {
 
                    if (i % 10 == 0) {
 
                        std::cerr << "Warning: could not create graph from "
 
                                     "degree sequence. Trying again...\n";
 
                    }
 
                }
 

	
 
                SwitchChain chain;
 
                if (!chain.initialize(g)) {
 
                    std::cerr << "Could not initialize Markov chain.\n";
 
                    return 1;
 
                }
 

	
 
                std::cout << "Starting switch Markov chain with n = "
 
                          << numVertices << ", tau = " << tau << ". \t"
 
                          << std::flush;
 
                std::cout << "Running n = " << numVertices << ", tau = " << tau
 
                          << ". \t" << std::flush;
 

	
 
                constexpr int mixingTime = 30000;
 
                constexpr int mixingTime = 40000;
 
                constexpr int measureTime = 20000;
 
                constexpr int measureSkip =
 
                    200; // Take a sample every ... steps
 
                constexpr int measurements =
 
                    (measureTime - 1) / measureSkip + 1;
 
                int movesDone = 0;
 

	
 
                int triangles[measurements];
 

	
 
                for (int i = 0; i < mixingTime; ++i) {
 
                    if (chain.doMove())
 
                        ++movesDone;
showgraphs.m
Show inline comments
 
@@ -8,25 +8,25 @@ Needs["ErrorBarPlots`"]
 

	
 

	
 
(* ::Text:: *)
 
(*- Use different starting point for switch chain that is closer to uniform:*)
 
(*   Do configuration model, starting with the vertex with highest degree and keeping track of a "forbidden list" meaning dont pair something that is not allowed*)
 
(*   (a) How close is this to uniform ? At least w.r.t. the measure of #triangles*)
 
(*   (b) How often does this procedure work/fail. Might still be faster to do switchings from Erdos-Gallai.*)
 
(**)
 
(*- Improve runtime*)
 
(*   (a) Don't remove/add edges from the std::vector. Simply replace them*)
 
(*   (b) Better direct triangle counting? (I doubt it)*)
 
(*   (b') Better triangle counting by only keeping track of CHANGES in #triangles*)
 
(*   (c) Do not choose the three permutations with 1/3 probability: choose the "staying" one with a very low probability. Should still be a valid switch chain?*)
 
(*   (c) Do not choose the three permutations with 1/3 probability: choose the "staying" one with zero probability. Should still be a valid switch chain?*)
 
(**)
 
(*- Experimental mixing time as function of n. At (n,tau)=(1000,2.5) it seems to be between 10.000 and 20.000 steps.*)
 
(**)
 
(*- Count #moves that result in +-k triangles (one move could create many triangles at once!)*)
 

	
 

	
 
(* ::Subsection:: *)
 
(*Done*)
 

	
 

	
 
(* ::Text:: *)
 
(*- Do a single very long run: nothing weird seems to happen with the triangle counts. Tried 10 million steps.*)
 
@@ -53,35 +53,35 @@ gs2=Map[Graph[#,GraphLayout->Automatic]&,gsraw[[1]]];
 

	
 
Grid[Partition[gs,10],Frame->All]
 

	
 

	
 
(* ::Section:: *)
 
(*Plot triangle counts*)
 

	
 

	
 
(* ::Subsection:: *)
 
(*Data import and data merge*)
 

	
 

	
 
gsraw=Import[NotebookDirectory[]<>"data/min_deg_two/graphdata_merged.m"];
 
gsraw=Import[NotebookDirectory[]<>"data/graphdata_merged.m"];
 
gsraw=SortBy[gsraw,#[[1,1]]&]; (* Sort by n *)
 

	
 

	
 
gdata=GatherBy[gsraw,{#[[1,2]]&,#[[1,1]]&}];
 
(* gdata[[ tau index, n index, run index , {ntau, #tris, ds} ]] *)
 

	
 

	
 
newData=Import[NotebookDirectory[]<>"graphdata_tau_multi4.m"];
 
mergedData=Import[NotebookDirectory[]<>"graphdata_merged.m"];
 
Export[NotebookDirectory[]<>"graphdata_merged_new.m",Join[mergedData,newData]]
 
newData=Import[NotebookDirectory[]<>"data/graphdata_3.m"];
 
mergedData=Import[NotebookDirectory[]<>"data/graphdata_merged.m"];
 
Export[NotebookDirectory[]<>"data/graphdata_merged_new.m",Join[mergedData,newData]]
 

	
 

	
 
(* ::Subsection:: *)
 
(*Plot empirical distribution of maximum degree*)
 

	
 

	
 
maxDegrees=Map[{#[[1]],Max[#[[3]]]}&,gsraw];
 
maxDegrees=GatherBy[maxDegrees,{#[[1,2]]&,#[[1,1]]&}];
 
(* maxDegrees[[ tau index, n index, run index,  ntau or dmax ]] *)
 

	
 

	
 
Histogram[maxDegrees[[1,-1,All,2]],PlotRange->{{0,2000},{0,100}},AxesLabel->{"d_max","frequency"}]
 
@@ -142,30 +142,24 @@ taulabels=Map["tau = "<>ToString[#]&,averagesGrouped[[All,1,1,1,2]]];
 
averagesErrorBars=Map[
 
{{#[[1,1,1]],Mean[#[[All,2]]]},
 
ErrorBar[StandardDeviation[#[[All,2]]]/Sqrt[Length[#]]]
 
}&,averagesGrouped,{2}];
 

	
 

	
 
ErrorListPlot[averagesErrorBars,Joined->True,PlotMarkers->Automatic,AxesLabel->{"n","\[LeftAngleBracket]triangles\[RightAngleBracket]"},PlotLegends->taulabels]
 

	
 

	
 
ListLogLogPlot[averagesErrorBars[[All,All,1]],Joined->True,PlotMarkers->Automatic,AxesLabel->{"n","\[LeftAngleBracket]triangles\[RightAngleBracket]"},PlotLegends->taulabels]
 

	
 

	
 
averagesErrorBars=Map[
 
{{Log[#[[1,1,1]]],Log[Mean[#[[All,2]]]]},
 
ErrorBar[ (StandardDeviation[#[[All,2]]] )]
 
}&,averagesGrouped,{2}];
 

	
 

	
 
(* ::Subsection:: *)
 
(*Fitting the log-log-plot*)
 

	
 

	
 
loglogdata=Log[averagesErrorBars[[All,All,1]]];
 
fits=Map[Fit[#,{1,logn},logn]&,loglogdata];
 

	
 

	
 
Show[ListLogLogPlot[averagesErrorBars[[All,All,1]],PlotMarkers->Automatic,AxesLabel->{"n","\[LeftAngleBracket]triangles\[RightAngleBracket]"},PlotLegends->taulabels],Plot[fits,{logn,1,2000}]]
 

	
 

	
 
tauValues=averagesGrouped[[All,1,1,1,2]];
0 comments (0 inline, 0 general)