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.