chriswarbo-net: 6618b6f00ae723898e3053db143b8b6833864718

     1: // Search combinators
     2: 
     3: // A search combinator is any function which takes
     4: // no arguments and returns a result. This simple
     5: // interface represents a computation that hasn't
     6: // been run yet (also known as a "thunk"). This
     7: // nicely captures search and optimisation, as:
     8: // 1) Search/optimisation algorithms never halt,
     9: //    they can always give another value (even if
    10: //    it stabilises to a constant), so we need to
    11: //    represent these computations without actually
    12: //    performing them (which would take forever).
    13: //    Thunks effectively give us a lazy list (or a
    14: //    generator, if you prefer) of the results.
    15: // 2) Different search algorithms use different
    16: //    numbers of parameters, and use variables at
    17: //    multiple scope levels. A thunk interface
    18: //    encapsulates all of this so that the
    19: //    combinators can be composed interchangably.
    20: //    We can still use parameters and state, but
    21: //    they must be "baked" (curried) into the
    22: //    combinators. This minimises shared state and
    23: //    other such nastiness.
    24: // 
    25: // As a consequence of this currying, we generally
    26: // don't ending up define combinators directly, but
    27: // rather curryable functions (or 'combinator
    28: // factories', if you prefer) which take any
    29: // required parameters as arguments, set up any
    30: // required state and return a combinator with
    31: // these built-in.
    32: 
    33: 
    34: // Utility functions
    35: 
    36: // Combines two arrays using the given function
    37: var zip_with = function(f) {
    38:     return function(a, b) {
    39:         return a.map(function(v, k) {
    40:             return f(v, b[k]);
    41:         });
    42:     };
    43: };
    44: 
    45: // Reifies + ("add" may get confused with
    46: // array.push)
    47: var plus = function(a, b) { return a+b; };
    48: 
    49: // Likewise for * ("times" may get confused
    50: // with iterate(f) or range(n))
    51: var multiply = function(a, b) { return a*b; };
    52: 
    53: // Turns 0/1 into -1/1
    54: var choose_direction = function(a) {
    55:     return 2*a - 1;
    56: };
    57: 
    58: 
    59: // Combinators and combinator factories follow
    60: 
    61: // A constant combinator always gives the same
    62: // value
    63: var make_constant = function(val) {
    64:     return function() {
    65:         return val;
    66:     };
    67: };
    68: 
    69: // A stream of 1s
    70: var one = constant(1);
    71: 
    72: // A reducer combines the results of a
    73: // combinator using a reduction function.
    74: // For function f, initial value init and
    75: // results a, b, c, ... a reducer returns:
    76: // f(init, a)
    77: // f(f(init, a), b)
    78: // f(f(f(init, a), b), c)
    79: // ...
    80: var make_reducer = function(init, f, comb) {
    81:     return function() {
    82:         init = f(init, comb());
    83:         return init;
    84:     };
    85: };
    86: 
    87: // A useful example of a reducer. Produces
    88: // a stream of the Natural numbers
    89: var counter = make_reducer(0, plus, one);
    90: 
    91: // A reducer factory which appends results to an
    92: // array
    93: var make_hoarder = function(comb) {
    94:     return make_reducer(
    95:         [],
    96:         function(arr, v) {
    97:             if (typeof v === typeof []) return arr.concat([v]);
    98:             return arr.concat(v);
    99: 	}
   100:     );
   101: };
   102: 
   103: // Enumerates the sentences of the given alphabet,
   104: // according to the given combinator.
   105: // The combinator should return numbers, and we
   106: // turn these into sentences by first writing down
   107: // the number in a base of the alphabet's length
   108: // (eg.for English letters, this would be base 26)
   109: // then we use each 'digit' of this representation
   110: // to index the alphabet array.
   111: // We combine these symbols by using the given
   112: // reduction function, initialised with init.
   113: // 
   114: // As a concrete example, we can enumerate every
   115: // second DNA strand in lexicographical order like
   116: // this:
   117: // make_enumerator(
   118: //     ['A', 'C', 'G', 'T'],
   119: //     plus,
   120: //     '',
   121: //     make_reducer(
   122: //         0,
   123: //         plus,
   124: //         make_constant(2)
   125: //     )
   126: // );
   127: var make_enumerator = function(alphabet, reduction, init, comb) {
   128:     return function() {
   129:         var val = comb();
   130:         var result = init;
   131:         var rem;
   132:         do {
   133:             rem = val % alphabet.length;
   134:             val -= rem;
   135:             val /= alphabet.length;
   136:             result = reduction(result, alphabet[rem]);
   137:         }
   138:         while (val);
   139:         return result;
   140:     };
   141: };
   142: 
   143: // Sends the given combinator's values through
   144: // the given function. 
   145: var make_applicator = function(f, comb) {
   146:     return function() {
   147:         return f(comb());
   148:     };
   149: };
   150: 
   151: // SIMPLE, as defined by Jurgen Schmidhuber.
   152: // Returns every binary sequence in ascending
   153: // order.
   154: // 
   155: // This version enumerates the sentences of the
   156: // boolean alphabet
   157: var simple = make_enumerator(
   158:     [false, true],
   159:     function(a, b) {
   160:         return a.push(b);
   161:     },
   162:     [],
   163:     counter
   164: );
   165: // This version applies a binary conversion to a
   166: // counter
   167: var simple = make_applicator(
   168:     function(a) {
   169:         return a.toString(2).split('').map(function(b) {
   170:             return b=='1';
   171:         });
   172:     },
   173:     counter
   174: );
   175: 
   176: // Generates random values between 0 and 1
   177: var uniform_random = function() {
   178:     return Math.random();
   179: };
   180: 
   181: // Generates random floats between 0 and n
   182: var make_uniform_random = function(n) {
   183:     return make_applicator(
   184:         function(a) {
   185:             return n*a;
   186: 	}
   187:     );
   188: };
   189: 
   190: // Generates random integers between 0 and n
   191: var make_random_int = function(n) {
   192:     return make_applicator(
   193:         function(a) {
   194: 	    return Math.floor(a*n);
   195: 	},
   196:         uniform_random
   197:     );
   198: };
   199: 
   200: // Generates random bits
   201: var random_bit = make_random_int(2);
   202: 
   203: var random_step = make_applicator(choose_direction, random_bit);
   204: 
   205: // Random walk (1D)
   206: var random_walk = make_reducer(0, plus, random_step);
   207: 
   208: // Returns samples from a Pareto distribution
   209: var make_pareto = function(scale) {
   210:     return function() {
   211: 	return Math.pow(1.0 - Math.random(), -1.0 / scale());
   212:     };
   213: };
   214: var pareto = make_pareto(one);
   215: 
   216: var levy_flight = make_reducer(0, plus, pareto);
   217: 
   218: // Combines two combinators with the given function
   219: var make_product = function(f, comb1, comb2) {
   220:     return function() {
   221:         return f(comb1(), comb2());
   222:     };
   223: };
   224: 
   225: // Takes 2 combinators a and b, gives a combinator
   226: // which returns the results of a and b
   227: var make_pair = function(a, b) {
   228:     return function() {
   229:         return [a(), b()];
   230:     };
   231: };
   232: 
   233: var make_scattered = function(n, nil, comb, choice) {
   234:     return function() {
   235:         var index = choice();
   236:         var result = [];
   237:         var length = n();
   238:         for (var i=0; i < length; i++) {
   239:             if (i == index) result.push(comb());
   240:             else result.push(nil());
   241:         }
   242:         return result;
   243:     };
   244: };
   245: 
   246: // Makes a 2D walk from a boolean stream and a 1D
   247: // walk. The booleans determine the axis to move
   248: // along, the walk determines the distance
   249: var make_mahattan_walk = function(horizontal, step) {
   250:     return make_reducer(
   251:         [0, 0],
   252:         vector_plus,
   253:         make_product(
   254:             function(a, b) {
   255:                 if (a) return [b, 0];
   256:                 return [0, b];
   257:             },
   258:             horizontal,
   259:             step
   260:         )
   261:     );
   262: };
   263: 
   264: // Makes a 2D walk from two 1D walks, treating them
   265: // as x and y step sizes
   266: var make_cartesian_walk = function(x_step, y_step) {
   267:     return make_reducer(
   268:         [0, 0],
   269:         vector_plus,
   270:         make_pair(x_step, y_step)
   271:         )
   272:     );
   273: };
   274: 
   275: // Makes a 2D walk from two 1D walks, treating them
   276: // as an angle and a step size
   277: var make_polar_walk = function(angle, distance) {
   278:     return make_reducer(
   279:         [0, 0],
   280:         vector_plus,
   281:         make_applicator(
   282:             function(a) {
   283:                 var step = distance();
   284:                 return [step*Math.cos(a), step*Math.sin(a)];
   285:             },
   286:             angle()
   287:         )
   288:     );
   289: };
   290: 

Generated by git2html.