Quick summary: it might work but I haven't figured out how to make it
work.

<html><head><title>Random bit-vector network addressing</title>
<!-- see end of file for explanation, where it's put so that it's readable in 
the browser -->
<script src="../MochiKit-1.3.1/lib/MochiKit/MochiKit.js"></script>
<script type="text/javascript">
var bits_per_cell, bits_per_row, cells_per_row, cells_per_col, table

var randbits = []
function replenish_bits() {
   var num = Math.random()
   for (;;) {
      num *= 2
      if (num > 1) {
         randbits.push(1)
         num -= 1
      } else if (num == 1) {
         if (randbits.length) return
         else num = Math.random()
      } else {
         randbits.push(0)
      }
   }
}
function random_bit() {
   if (!randbits.length) replenish_bits()
   return randbits.pop()
}
function random_bitvector(length) {
   return map(random_bit, range(length)).join('')
}

function how_many_bits_per_row(total_bits) {
   /* make roughly square */
   var max = Math.ceil(Math.sqrt(bits_per_cell * 4))
   var min = Math.ceil(max/2)
   var ii = max
   for (var ii = max; ; ii--) {
      if (ii == min || total_bits % ii == 0) return ii
   }
}

function reset() {
   bits_per_cell = $('bits_per_cell').value
   cells_per_row = $('cells_per_row').value
   cells_per_col = $('cells_per_col').value
   bits_per_row = how_many_bits_per_row(bits_per_cell)
   table = map(function() { 
      return map(function() { 
         return random_bitvector(bits_per_cell) 
      }, range(cells_per_row))
   }, range(cells_per_col))
   refresh()
}

function display_cell(cellvalue) {
   var rv = []
   while (cellvalue.length) {
      rv.push(cellvalue.substr(0, bits_per_row))
      rv.push(BR())
      cellvalue = cellvalue.substr(bits_per_row)
   }
   return rv
}

function refresh() {
   replaceChildNodes('dest', TABLE({cellPadding: 2, cellSpacing: 0},
      map(function(ii) {
            return TR(null, map(function(jj) {
               var tcc = function(ev) { turn_cells_colors(ii, jj) }
               var rv = TD(null, display_cell(table[ii][jj]))
               connect(rv, 'onclick', tcc)
               return rv
            }, range(table[0].length)))
      }, range(table.length))))
}

function similarity(r0, c0, r1, c1) {
   var b0 = table[r0][c0]
   var b1 = table[r1][c1]
   var count = 0
   forEach(range(b0.length), function(ii) {
      if (b0[ii] == b1[ii]) count++
   })
   return count / b0.length
}

function similarity_color(r0, c0, r1, c1) {
   var sim = similarity(r0, c0, r1, c1)
   if (sim == 1) return '#77ff77'
   /* Map similarities of one-third and up into colors ranging from
    * white, through pink, into red. Bizarrely fromHSV(1, 0, 1) is black,
    * though fromHSV(1, 0.001, 1) or fromHSV(1, -0.001, 1) are white */
   return Color.fromHSV(1, 1.5*sim - 0.499, 1).toHexString()
}

function turn_cells_colors(r0, c0) {
   set_cell_backgrounds(function(ii, jj) {
      return similarity_color(r0, c0, ii, jj)
   })
}

function colors_by_bit(n) {
   set_cell_backgrounds(function(ii, jj) {
      return table[ii][jj][n] == '1' ? 'white' : 'gray'
   })
}

function set_cell_backgrounds(cell_color_func) {
   var domtable = $('dest').firstChild
   var ii = 0
   var jj = 0
   function turn_a_cell() {
      domtable.childNodes[ii].childNodes[jj].style.background = 
         cell_color_func(ii, jj)
      jj++
      if (jj == table[0].length) {
         ii++
         jj = 0
      }
      if (ii < table.length) setTimeout(turn_a_cell, 0)
   }
   turn_a_cell()
}

function neighbors(ii, jj) {
   function in_bounds(arg) {
      var ii = arg[0], jj = arg[1]
      return ii >= 0 && jj >= 0 && ii < table.length && jj < table[0].length
   }
   function get_cell(arg) { return table[arg[0]][arg[1]] }
   return map(get_cell, filter(in_bounds, 
                               [[ii, jj], [ii-1, jj], [ii, jj-1], 
                                [ii+1, jj], [ii, jj+1]]))
}

function majority_rule_digit(digits) {
   var counts = [0, 0]
   forEach(digits, function(digit) { counts[digit] += 1 })
   if (counts[0] > counts[1]) return 0
   else if (counts[1] > counts[0]) return 1
   else return random_bit()
}

function random_rule_digit(digits) {
   var counts = [0, 0]
   forEach(digits, function(digit) { counts[digit]++ })
   return (Math.random() < counts[0] / (counts[0] + counts[1])) ? 0 : 1
}

function map_digits(digit_rule, neighbors) {
   var rv = map(digit_rule, map(function(ii) { 
      return map(function(neighbor) { return neighbor[ii] },
                 neighbors)
   }, range(neighbors[0].length)))
   return rv.join('')
}
current_digit_rule = random_rule_digit

function change_rule(select_object) {
   var o = select_object.options[select_object.selectedIndex].value
   if (o == 'majority_rule_digit') current_digit_rule = majority_rule_digit
   else current_digit_rule = random_rule_digit
}

function mix_cell(ii, jj) {
   return map_digits(current_digit_rule, neighbors(ii, jj))
}

function propagate(continuation) {
   var newtable = []
   var ii = 0
   var jj = 0
   var newrow = []
   /* explicit CPS loop to avoid "script has stopped responding" message */
   function do_cell() {
      newrow.push(mix_cell(ii, jj))
      jj++
      if (jj == table[0].length) {
         jj = 0
         newtable.push(newrow)
         newrow = []
         ii++
      }
      if (ii < table.length) setTimeout(do_cell, 0)
      else {
         table = newtable
         refresh()
         if (continuation) continuation()
      }
   }
   do_cell()
}

function propagate_n(n) {
   if (n > 0) propagate(function(){propagate_n(n-1)})
}

function init() {
   reset()
}

</script>
</head>
<body onload="init()">
<h1>Random bit-vector network addressing</h1>
<p style="font-size: 2em">
<button onclick="propagate_n($('n').value)">propagate</button> <input id="n" 
    value="1" size="3"> times using a
    <select onchange="change_rule(this)" id='current_digit_rule'>
        <option selected="selected" 
value="random_rule_digit">stochastic</option>
        <option value="majority_rule_digit">mostly deterministic</option>
    </select> rule, click on a cell to color other cells by similarity, 
    <button onclick="colors_by_bit($('bit').value)">color</button> by bit
    <input id="bit" value="0" size="3" /> (
      <button onclick="$('bit').value++; colors_by_bit($('bit').value)"
        >up</button>,
      <button onclick="$('bit').value--; colors_by_bit($('bit').value)"
        >down</button>),
    or <button onclick="reset()">reset</button> with 
    <input id="cells_per_col" value="15" size="3"> rows of 
    <input id="cells_per_row" value="15" size="3"> cells containing
    <input id="bits_per_cell" value="32" size="4"> bits each.
</p>
<div id="dest"></div>

<h2>Explanation</h2>

<p>So I had this very simple idea for doing geographical addressing
and routing in ad-hoc mesh networks.  Every node starts by generating
a random ID, and then for some number of generations, executes the
following procedure:</p>

<ol>
<li>Send all your neighbors your current ID.</li>
<li>Receive all your neighbors' IDs.</li>
<li>Given your own ID and your neighbors' IDs, calculate a new ID for
yourself that is somehow more similar to your neighbors' IDs than your
old ID was.</li>
</ol>

<p>The idea is that, after some number of generations, much of the
local variation will be smoothed out of the ID space, so that you can
route a message to a node by forwarding it to whichever one of your
neighbors has the ID most similar to the destination node's ID, or
possibly choose one of your neighbors at random weighted by that
function.</p>

<p>There's a bit of tension in this idea --- if the smoothing process
is totally effective, every node will end up with the same ID, making
them useless for addressing, and if it's not that effective but still
rather too effective, there won't be much local variation in ID, so it
will be hard to figure out which direction a message should go in.
But if it's not smooth enough, the message won't make progress towards
its destination.</p>

<p>So this is a little experiment to try this idea out.  Each node has
a 32-bit randomly-generated address to start out; it's topologically
connected to the cells above and below it and to its left and right;
"similarity" is Hamming distance, new IDs are calculated bit by bit,
and there are two algorithms available for making an ID bit "more
similar" to those of a node's neighbors':</p>

<dl>

<dt>stochastic</dt>

<dd>The node's and neighbors' 0 bits (in a particular position,
e.g. bit #11) are counted; the new bit is 0 with probability N/M,
where N is the number of 0 bits found, and M is the total number of
bits (5 for most nodes, ranging down to 3 in the corners).  So if all
the bits examined are the same, the new bit is guaranteed to have the
same value; but if e.g. a single bit out of 5 is 0, then the new bit
is 0 with 20% probability and 1 with 80% probability.</dd>

<dt>mostly deterministic</dt>

<dd>The same set of bits are examined, but the new bit is the value
taken by the majority of the old bits (or uniformly randomly selected
if there was a tie, which can happen at the edges)</dd>

</dl>

<h2>Results</h2>

<p>It doesn't work as well as I would like.  Neither of the algorithms
is very effective at eliminating "local optima" on 32-bit IDs in a
15x15 grid, even after a large number of generations.  The
mostly-deterministic algorithm does eliminate the majority of local
bumpiness in the closeness function, so messages started nearby would
propagate successfully.</p>

<p>Neither algorithm is "effective" enough to produce noticeable
numbers of identical IDs.</p>

<p>Unsurprisingly, both algorithms seem to work if the number of bits
is larger than the number of nodes, as long as you run them long
enough.  But maybe that's just because I haven't been able to wait
very long to run largish simulations with a lot of bits.</p>

<p>The deterministic algorithm seems to mostly settle down after a few
generations, maybe 10 or 20, which means that no new information is
being transmitted at that point.  This is bad news, since in 10 or 20
generations, information can only flow (optimistically) 10 or 20 hops.
It gets a little bit better with more bits, but not a lot.</p>

<h2>Other directions</h2>

<p>It might be possible to rescue this approach in one or more of the
following ways:</p>

<ul>
<li>Randomly flip bits from time to time as a modification to the
mostly-deterministic algorithm.</li>

<li>Rather than using vectors of individual bits, use vectors of small
integers, perhaps of 2 or 3 bits each; then "more similar" IDs might
be a median, or stochastically chosen from near the median.</li>

<li>Change some bits more slowly than others, or change some bits
according to the stochastic algorithm, and the others according to the
majority-rule algorithm.</li>

<li>Gradually "anneal" the IDs: start with the stochastic algorithm
(or an algorithm where the bits are just uniformly selected each
step), then gradually increase the probability weight from the local
majority.</li>

</ul>

</body>
</html>

Reply via email to