Files @ c039c549918d
Branch filter:

Location: AENC/switchchain/cpp/switchchain.cpp

Tom Bannink
Add start of triangle counting for powerlaws
#include "exports.hpp"
#include "graph.hpp"
#include <algorithm>
#include <fstream>
#include <iostream>
#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) {
        // 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
        int timeout = 0;
        while (edgeConflicts(e1, e2)) {
            e1 = g.getEdge(edgeDistribution(mt));
            e2 = g.getEdge(edgeDistribution(mt));
            ++timeout;
            if (timeout % 100 == 0) {
                std::cerr << "Warning: sampled " << timeout
                          << " random edges but they keep conflicting.\n";
            }
        }
        // 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

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

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

int main() {
    // 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

    Graph g;

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

    for (int numVertices = 500; numVertices <= 1000; numVertices += 100) {
        // Generate a random degree sequence
        std::mt19937 gen(std::random_device{}());

        // average degree 12
        DegreeSequence ds(numVertices);
        std::poisson_distribution<> degDist(12);

        // For a single n, take samples over several instances of
        // the degree distribution
        for (int degreeSample = 0; degreeSample < 10; ++degreeSample) {

            // Try at most 10 times to generate a valid sequence
            bool validGraph = false;
            for (int i = 0; i < 10; ++i) {
                std::generate(ds.begin(), ds.end(),
                              [&degDist, &gen] { return degDist(gen); });
                if (g.createFromDegreeSequence(ds)) {
                    validGraph = true;
                    break;
                }
            }
            if (!validGraph) {
                std::cerr << "Could not create graph from degree sequence.\n";
                return 1;
            }
            std::sort(ds.begin(), ds.end());

            //std::cout << "Degree sequence:";
            //for (auto i : ds)
            //    std::cout << ' ' << i;
            //std::cout << std::endl;

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

            std::cout << "Starting switch Markov chain" << std::endl;

            constexpr int mixingTime = 15000;
            constexpr int measureTime = 5000;
            int movesDone = 0;

            int triangles[measureTime];
            for (int i = 0; i < mixingTime; ++i) {
                if (chain.doMove())
                    ++movesDone;
            }
            for (int i = 0; i < measureTime; ++i) {
                if (chain.doMove())
                    ++movesDone;
                triangles[i] = chain.g.countTriangles();
            }

            if (outputComma)
                outfile << ',';
            outputComma = true;

            outfile << '{' << numVertices << ',' << '{';
            outfile << triangles[0];
            for (int i = 1; i < measureTime; ++i)
                outfile << ',' << triangles[i];
            outfile << '}' << '}';

            std::cout << movesDone << '/' << mixingTime + measureTime
                      << " moves succeeded." << std::endl;
        }
    }
    outfile << '}';
    return 0;
}