During my PhD I always wanted to have an interactive way of plotting a nice looking Bethe Lattice and particular to my work a Dimer Bethe lattice. Because this visualization wasn’t an essential part of my research I never investigated into it. It is enough to draw them on paper for any practical purpose. I was then confronted with my PhD oral presentation, great opportunity to procrastinate and get the nice unessential plots done. They didn’t end in the final version of the presentation(again is not the fundamental part of my work), but I wanted to share them anyway.

I first found this project vis.js for browser based visualization, which has an entire section of examples dedicated to drawing networks. That is all I need. Doing things on the browser is great, it allows to publish many documents with user interactivity, which then can be openly shared as a webpage instead of a PDF document or other presentation files. With vis.js being able to plot networks the only task left is to find the good algorithm that solves the generation of the lattice problem.

Returning to JavaScript

The language of the web. Good that is a simple language, bad that is a simple language. I haven’t programmed in JavaScript in years(I was probably still in high school that time), other languages like PHP, C++, Python took over. But programming is one thing, the language syntax is another, so working again on JavaScript wasn’t to much of a problem.

The logic of vis.js to draw a network is that it takes a list of nodes and a list of edges, so I just need to generate them. A recursive algorithm was to me the most intuitive way to describe the Bethe Lattice, which is a connected cycle-free graph where each node is connected to a certain number of neighbors. In the end it is just a tree. I just need a function that creates a node, links that node to a head and then calls the function again for the amount of remaining neighbors the node has.

The Bethe Lattice

This algorithm lets itself be expressed by the following code:

function gen_Bethe(generations, neighbors) {
    var nodes = [];
    var edges = [];
    var counter = -1;

    // Recursive function to create a new node and call itself to generate
    // the branches(subtree) structure of the graph
    function tree(level, neighbors, head) {
        counter++;
        nodes.push({
            id: counter,
            label: String(counter),
            group:level
            });
        var current_node = counter;
        if (head != current_node) {
            edges.push({
                from: head,
                to: current_node
            });
        }

        if (level == 0) {
            return 0;
        }

        var childs = neighbors - 1;
        // Only the first/central node is special. For the recursive call
        // it has all neighbors
        if (current_node == 0) {
            childs = neighbors;
        }

        var low = level - 1;
        for (var i = 0; i < childs; i++) {
            tree(low, neighbors, current_node);
        }

        return 0;
    }

    tree(generations, neighbors, 0);

    return {
        nodes: nodes,
        edges: edges
    };
}

There are maybe better ways to solve this in JavaScript, I don’t know. The reduced amount of code makes me happy about it. The little amount of code does not reflect the amount of time I spent trying to make this work. I save it in a file /js/bethe_lattice_generator.js to be able to use it later.

In the head of the webpage I’ll import the vis.js JavaScript and CSS modules. Then I import the file with the previous algorithm to the bethe lattice and define a CSS #id to contain the plot.

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.css" rel="stylesheet" type="text/css">

<script type="text/javascript" src="/js/bethe_lattice_generator.js"></script>

<style type="text/css">
 #bethe-lattice {
    width: 500px;
    height: 500px;
    border: 1px solid lightgray;
    align: center;
  }
</style>

The next JavaScript section goes also in the webpage, it declares how vis.js is to be used to draw the lattice. I need to setup variables of nodes, edges, network (that is the lattice), and configuration options.

<script type="text/javascript">
  var nodes = null;
  var edges = null;
  var network = null;
  var setSmooth = true;

  // cleans the canvas where the lattice is plotted
  function destroy(net) {
    if (net !== null) {
      net.destroy();
      net = null;
    }
  }

  function draw_bethe(level, neighbors) {
    destroy(network);
    // create a network
    level = (typeof level == 'number') ?  level : 2;
    neighbors = (typeof neighbors == 'number') ?  neighbors : 3;
    var container = document.getElementById('bethe-lattice');
    var data = gen_Bethe(level, neighbors);
    var options = {
      physics: { stabilization: false }
    };
    network = new vis.Network(container, data, options);
  }
</script>

Finally, in the content of the website I embed the HTML where to draw the Bethe Lattice and propose some options of configurations.

<div id="bethe-lattice"></div>
<form onsubmit="draw_bethe(3,3); return false;">
  <input type="button" value="chain" onclick="draw_bethe(8, 2);">
  <input type="button" value="2,3" onclick="draw_bethe(2, 3);">
  <input type="button" value="3,3" onclick="draw_bethe(3, 3);">
  <input type="button" value="3,4" onclick="draw_bethe(3, 4);">
  <input type="button" value="4,4" onclick="draw_bethe(4, 4);">
</form>

The dimer Bethe lattice

The can be understood as the Bethe lattice but placing two closely connected nodes at each place where the original Bethe lattice only has one node. An alternative view of it is to think of two independent Bethe lattices that are placed side by side and coupled at each node.

When extending the previous algorithm to plot the dimer lattice I’ll join both views. I start generating the Bethe lattice, but for each node I generate I immediately create its neighbor and link them to compose the dimer. In that way I build the second lattice at the same time.

function gen_dimerBethe(generations, neighbors) {
    var nodes = [];
    var edges = [];
    var counter = -1;

    function tree(level, neighbors, head) {
        counter++;
        var current_node = counter;
        // first node
        nodes.push({
            id: counter,
            label: String(counter),
            group:20
        });
        // second node
        counter++;
        nodes.push({
            id: counter,
            label: String(counter),
            group:100
        });
        // link the two nodes to build the dimer
        edges.push({
            from: counter-1,
            to: counter,
            dashes: true
        });

        if (head != current_node) {
            edges.push({from: head, to: current_node, arrows:'to'});
            // The second lattice
            edges.push({from: head+1, to: current_node+1, arrows:'to'});
        }


        if (level == 0) {
            return 0;
        }

        var childs = neighbors - 1;
        if (current_node == 0) {
            childs = neighbors;
        }

        var low = level - 1;
        for (var i = 0; i < childs; i++) {
            tree(low, neighbors, current_node);
        }

        return 0;
    }

    tree(generations, neighbors, 0);

    return {
        nodes: nodes,
        edges: edges
    };
}

Setting up vis.js is the same as in the previous section,

<style type="text/css">
 #dimer-bethe-lattice {
    width: 500px;
    height: 500px;
    border: 1px solid lightgray;
    align: center;
}
</style>

<script type="text/javascript">
  var nodes = null;
  var edges = null;
  var setSmooth = true;
  var network_dimer = null;
  function draw_dimer_bethe(level, neighbors) {
    destroy(network_dimer);
    // create a network
    level = (typeof level == 'number') ?  level : 2;
    neighbors = (typeof neighbors == 'number') ?  neighbors : 3;
    var container = document.getElementById('dimer-bethe-lattice');
    var data = gen_dimerBethe(level, neighbors);
    var options = {
      physics: { stabilization: false }
    };
    network_dimer = new vis.Network(container, data, options);
  }

  // This draws the default lattices on window load
  window.onload=function(){draw_bethe();draw_dimer_bethe();};
</script>

Drawing the dimer Bethe lattice.

<div id="dimer-bethe-lattice"></div>
<form onsubmit="draw_dimer_bethe(3,3); return false;">
  <input type="button" value="ladder" onclick="draw_dimer_bethe(8, 2);">
  <input type="button" value="2,3" onclick="draw_dimer_bethe(2, 3);">
  <input type="button" value="3,3" onclick="draw_dimer_bethe(3, 3);">
  <input type="button" value="3,4" onclick="draw_dimer_bethe(3, 4);">
</form>