Files @ a79267af1717
Branch filter:

Location: AENC/switchchain/cpp/switchchain.cpp - annotation

Tom Bannink
Add Havel-Hakimi algorithm and mathematica output
7bef7b203f4e
a79267af1717
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
446bcd991614
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
446bcd991614
7bef7b203f4e
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
7bef7b203f4e
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
446bcd991614
446bcd991614
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
a79267af1717
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
7bef7b203f4e
a79267af1717
a79267af1717
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
446bcd991614
446bcd991614
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
a79267af1717
446bcd991614
446bcd991614
a79267af1717
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
446bcd991614
446bcd991614
7bef7b203f4e
446bcd991614
7bef7b203f4e
7bef7b203f4e
446bcd991614
446bcd991614
446bcd991614
7bef7b203f4e
a79267af1717
7bef7b203f4e
7bef7b203f4e
7bef7b203f4e
a79267af1717
446bcd991614
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
a79267af1717
a79267af1717
a79267af1717
7bef7b203f4e
446bcd991614
7bef7b203f4e
446bcd991614
446bcd991614
446bcd991614
446bcd991614
a79267af1717
a79267af1717
a79267af1717
446bcd991614
446bcd991614
a79267af1717
a79267af1717
446bcd991614
446bcd991614
a79267af1717
a79267af1717
a79267af1717
a79267af1717
446bcd991614
a79267af1717
a79267af1717
446bcd991614
446bcd991614
446bcd991614
#include <algorithm>
#include <fstream>
#include <iostream>
#include <numeric>
#include <random>
#include <vector>

class Edge {
  public:
    unsigned int u, v;

    bool operator==(const Edge &e) const { return u == e.u && v == e.v; }
};

// 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);
}

std::ostream &operator<<(std::ostream &s, const Edge &e) {
    s << '{' << e.u << ',' << e.v << '}';
    return s;
}

class DiDegree {
  public:
    unsigned int in;
    unsigned int out;
};

typedef std::vector<unsigned int> DegreeSequence;
typedef std::vector<DiDegree> DiDegreeSequence;

class Graph {
  public:
    Graph() {}

    Graph(unsigned int n) { adj.resize(n); }

    ~Graph() {}

    void resize(unsigned int n) {
        if (n < adj.size()) {
            edges.clear();
        }
        adj.resize(n);
    }

    unsigned int edgeCount() const { return edges.size(); }

    Edge &getEdge(unsigned int i) { return edges[i]; }
    const Edge &getEdge(unsigned int i) const { return edges[i]; }

    bool createFromDegreeSequence(const DegreeSequence &d) {
        // Havel-Hakimi algorithm

        unsigned int n = d.size();

        // degree, vertex index
        std::vector<std::pair<unsigned int, unsigned int>> degrees(n);
        for (unsigned int i = 0; i < n; ++i) {
            degrees[i].first = d[i];
            degrees[i].second = i;
        }

        edges.clear();
        adj.resize(n);
        while (!degrees.empty()) {
            std::sort(degrees.begin(), degrees.end());
            // Highest degree is at back of the vector
            // Take it out
            unsigned int degree = degrees.back().first;
            unsigned int u = degrees.back().second;
            degrees.pop_back();
            if (degree > degrees.size()) {
                edges.clear();
                adj.clear();
                return false;
            }
            // Now loop over the last 'degree' entries of degrees
            auto rit = degrees.rbegin();
            for (unsigned int i = 0; i < degree; ++i) {
                if (rit->first == 0 || !addEdge({u, rit->second})) {
                    edges.clear();
                    adj.clear();
                    return false;
                }
                rit->first--;
                ++rit;
            }
        }
        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 {
        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;
        edges.push_back(e);
        adj[e.u].push_back(e.v);
        adj[e.v].push_back(e.u);
        return true;
    }

    // There are two possible edge exchanges
    // switchType indicates which one is desired
    // Returns false if the switch is not possible
    bool exchangeEdges(const Edge &e1, const Edge &e2, bool switchType) {
        // The new edges configuration is one of these two
        // A) e1.u - e2.u and e1.v - e2.v
        // B) e1.u - e2.v and e1.v - e2.u
        // First check if the move is possible
        if (switchType) {
            if (hasEdge({e1.u, e2.u}) || hasEdge({e1.v, e2.v}))
                return false; // conflicting edges
        } else {
            if (hasEdge({e1.u, e2.v}) || hasEdge({e1.v, e2.u}))
                return false; // conflicting edges
        }

        // Find the edges in the adjacency lists
        unsigned int i1, j1, i2, j2;
        for (i1 = 0; i1 < adj[e1.u].size(); ++i1) {
            if (adj[e1.u][i1] == e1.v)
                break;
        }
        for (j1 = 0; j1 < adj[e1.v].size(); ++j1) {
            if (adj[e1.v][j1] == e1.u)
                break;
        }
        for (i2 = 0; i2 < adj[e2.u].size(); ++i2) {
            if (adj[e2.u][i2] == e2.v)
                break;
        }
        for (j2 = 0; j2 < adj[e2.v].size(); ++j2) {
            if (adj[e2.v][j2] == e2.u)
                break;
        }

        // Remove the old edges
        bool removedOne = false;
        for (auto iter = edges.begin(); iter != edges.end();) {
            if (*iter == e1) {
                iter = edges.erase(iter);
                if (removedOne)
                    break;
                removedOne = true;
            } else if (*iter == e2) {
                iter = edges.erase(iter);
                if (removedOne)
                    break;
                removedOne = true;
            } else {
                ++iter;
            }
        }

        // Add the new edges
        if (switchType) {
            adj[e1.u][i1] = e2.u;
            adj[e1.v][j1] = e2.v;
            adj[e2.u][i2] = e1.u;
            adj[e2.v][j2] = e1.v;
            edges.push_back({e1.u, e2.u});
            edges.push_back({e1.v, e2.v});
        } else {
            adj[e1.u][i1] = e2.v;
            adj[e1.v][j1] = e2.u;
            adj[e2.u][i2] = e1.v;
            adj[e2.v][j2] = e1.u;
            edges.push_back({e1.u, e2.v});
            edges.push_back({e1.v, e2.u});
        }
        return true;
    }

  private:
    // Graph is saved in two formats for speed
    // The two should be kept consistent at all times
    std::vector<std::vector<unsigned int>> adj;
    std::vector<Edge> edges;
};

// Mathematica style export
std::ostream &operator<<(std::ostream &s, const Graph &g) {
    if (g.edgeCount() == 0) {
        s << '{' << '}';
        return s;
    }
    s << '{' << g.getEdge(0).u << '<' << '-' << '>' << g.getEdge(0).v;
    for (unsigned int i = 1; i < g.edgeCount(); ++i) {
        const Edge &e = g.getEdge(i);
        s << ',' << e.u << '<' << '-' << '>' << e.v;
    }
    s << '}';
    return s;
}

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() {
    Graph g;
    if (!g.createFromDegreeSequence({1, 2, 2, 2, 3, 3, 3})) {
        std::cerr << "Could not create graph from degree sequence.\n";
        return 1;
    }

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

    std::ofstream outfile("graphdata.m");
    outfile << '{' << g;

    std::cout << "Starting switch Markov chain" << std::endl;
    int movesDone = 0;
    int movesTotal = 100000;
    for (int i = 0; i < movesTotal; ++i) {
        if (chain.doMove())
            ++movesDone;
        if (i % (movesTotal / 100) == (movesTotal / 100 - 1))
            outfile << ',' << chain.g;
    }
    outfile << '}';

    std::cout << movesDone << '/' << movesTotal << " moves succeeded."
              << std::endl;

    return 0;
}