ambit 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
File without changes
@@ -0,0 +1,3 @@
1
+ === 0.9 / 2011-04-27
2
+
3
+ * First public release of ambit
@@ -0,0 +1,28 @@
1
+ == LICENSE:
2
+
3
+ (The BSD 2-clause License)
4
+
5
+ Copyright (c) 2011 Jim Wise
6
+ All rights reserved.
7
+
8
+ Redistribution and use in source and binary forms, with or without
9
+ modification, are permitted provided that the following conditions
10
+ are met:
11
+
12
+ 1. Redistributions of source code must retain the above copyright
13
+ notice, this list of conditions and the following disclaimer.
14
+ 2. Redistributions in binary form must reproduce the above copyright
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the distribution.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
22
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,11 @@
1
+ .autotest
2
+ History.txt
3
+ LICENSE.txt
4
+ Manifest.txt
5
+ README.rdoc
6
+ README.txt
7
+ Rakefile
8
+ examples/example.rb
9
+ examples/queens.rb
10
+ lib/ambit.rb
11
+ test/test_ambit.rb
@@ -0,0 +1,427 @@
1
+ = ambit
2
+
3
+ https://github.com/jimwise/ruby/tree/master/ambit
4
+
5
+ Author:: Jim Wise (mailto:jwise@draga.com)
6
+ Copyright:: Copyright (c) 2011 Jim Wise
7
+ License:: 2-clause BSD-Style (see LICENSE.txt)
8
+
9
+ == DESCRIPTION:
10
+
11
+ This is an all-ruby implementation of choose/fail nondeterministic
12
+ programming with branch cut, as described in Chapter 22 of Paul Graham's
13
+ <em>On Lisp</em>[1], Chapter, or Section 4.3 of <em>SICP</em>[2].
14
+
15
+ Due to Ruby containing a true call/cc, this is a much straighter port of
16
+ Paul Graham's scheme version of this code than his Common Lisp or my C
17
+ versions are. :-)
18
+
19
+ == REQUIREMENTS:
20
+
21
+ <b>This code will not work in JRuby or MacRuby (no callcc). It should work
22
+ in Ruby 1.9 with minor changes (callcc has moved to the 'continuation'
23
+ stdlib).</b>
24
+
25
+ == INSTALL:
26
+
27
+ To install:
28
+
29
+ $ gem install ambit
30
+
31
+ == DEVELOPERS:
32
+
33
+ After checking out the source, run:
34
+
35
+ $ rake newb
36
+
37
+ This task will install any missing dependencies, run the tests/specs,
38
+ and generate the RDoc.
39
+
40
+ == SYNOPSIS:
41
+
42
+ === What is Nondeterministic Programming?
43
+
44
+ Nondeterministic programming is a novel approach to problems where a
45
+ program must find a working solution out of many possible choices. It
46
+ greatly simplifies problems such graph searching, testing combinations of
47
+ values, and so on, where there are many possible values to consider, often
48
+ in some sort of hierarchical order, but the right combination is not known
49
+ in advance.
50
+
51
+ In such a situation, it can be useful to develop a program by pretending
52
+ our programming language includes knowledge of the future -- and is thus
53
+ able to _choose_ the right answer off the bat, and simply programming as
54
+ if this is the case.
55
+
56
+ A language with support for nondeterministic programming (such as Ruby
57
+ with this gem) helps us keep up this pretense by saving the state of
58
+ computation (with some limits) whenever we make an important choice. If
59
+ we later determine that we did _not_, in fact, make the correct choice
60
+ (lacking true language support for knowing the future), we can _fail_ the
61
+ current computation, which <em>causes computation to rewind to the last
62
+ choice made, and continue as if a different choice had been made</em>.
63
+
64
+ When all possible choices have been tried, the next time computation
65
+ _fails_, computation will be rewound to the previous choice point, and
66
+ will continue with the next possible choice from there.
67
+
68
+ Imagine, for instance, that we wish to test a combination lock with a
69
+ three-number combination, with each number between 1 and 10, inclusive:
70
+ Instead of writing code ourself to try every possible combination, we
71
+ simply proceed as if each choice was the correct one, failing if the lock
72
+ fails to open. In short:
73
+
74
+ first = Ambit.choose(1..10)
75
+ second = Ambit.choose(1..10)
76
+ third = Ambit.choose(1..10)
77
+
78
+ Ambit.fail! unless open_lock(first, second, third)
79
+
80
+ # when we get here, lock is open!
81
+
82
+ As our language does not actually implement knowledge of the future, this
83
+ will still try as many combinations as are needed to find the right one --
84
+ but we can program as if it has chosen the right one on the first try!
85
+
86
+ === How to Use This Gem
87
+
88
+ To get started, include this gem using
89
+
90
+ require 'rubygems'
91
+ require 'ambit
92
+
93
+ This gem provides the Ambit module. This module provides several methods
94
+ which implement nondeterministic programming.
95
+
96
+ ==== Choosing and Failing
97
+
98
+ The central method of Ambit is Ambit::choose.
99
+
100
+ Ambit::choose takes any enumerable (actually, any object which responds to
101
+ #each) as an argument, and begins a nondeterministic generate-and-test
102
+ process with the members of this object.
103
+
104
+ Ambit::choose immediately returns the first member of the enumerable, or
105
+ calls Ambit::fail! if the enumerable is empty:
106
+
107
+ a = Ambit::choose([1, 2, 3])
108
+ puts a
109
+ prints
110
+ 1
111
+
112
+ If, later, Ambit::fail! is called, <em>computation is rewound until the
113
+ point when Ambit::choose was last called</em>, and the next member of the
114
+ enumerable is returned <em>from the same call to Ambit::choose</em>:
115
+
116
+ a = Ambit::choose([1, 2, 3])
117
+ Ambit::fail! unless a.even?
118
+ puts a
119
+ prints
120
+ 2
121
+ (and only "2")
122
+
123
+ As an alternative, Ambit::assert can be used to fail if a condition holds;
124
+ Ambit::assert will rewind to the previous invocation of Ambit::choose if
125
+ and only if it's (single) argument is false:
126
+
127
+ a = Ambit::choose([1, 2, 3])
128
+ Ambit::assert a.even?
129
+ puts a
130
+ prints
131
+ 2
132
+ (and only "2")
133
+
134
+ Note that this call to Ambit::fail! (or Ambit::assert) can occur any amount
135
+ of time later, and works <em>even if the function which called choose has
136
+ since exited</em>. Execution is still rewound as needed to allow the next
137
+ value to be returned from the same call to Ambit::choose.
138
+
139
+ Calls to Ambit::choose can be nested to arbitrary depth -- each call to
140
+ Ambit::fail! will rewind to the <em>most recent</em> call to Ambit::choose.
141
+ If that set of choices has already returned every member of its enumerable,
142
+ execution is instead rewound to the previous invocation of Ambit::choose,
143
+ and execution continues with the next choice from that invocation's
144
+ enumerable:
145
+
146
+ a = Ambit::choose([1, 3, 5, 7, 9, 11, 13, 15])
147
+ b = Ambit::choose([0, 5, 10, 15])
148
+ Ambit::assert a == b
149
+ puts a
150
+ prints
151
+ 5
152
+ (and only "5")
153
+
154
+ If all choices from all past calls to Ambit::choose have been exhausted (or
155
+ if Ambit::fail! is called before any call to Ambit::choose), an exception of
156
+ type Ambit::ChoicesExhausted is raised instead.
157
+
158
+ ==== Side Effects
159
+
160
+ We've talked a lot above about "rewinding" computation to a previous choice
161
+ point. Not all computations can be rewound, however -- if the computation
162
+ we have performed since the choice point we are rewinding to has had side
163
+ effects (other than the choices made), those side effects will not
164
+ themselves be rewound. While some side effects (setting of variables) could
165
+ theoretically be tracked and undone, this would require very careful
166
+ semantics. And other side effects could not be undone by any level of
167
+ complexity added to our language -- if we have printed output to the user,
168
+ for instance, no amount of rewinding will make the user forget what he has
169
+ seen -- while we simulate the ability to see the future and to change the
170
+ past, we can, in fact, do neither.
171
+
172
+ This can sometimes cause confusion. This code, for instance:
173
+
174
+ a = Ambit::choose([1, 2, 3])
175
+ puts a
176
+ Ambit::fail! unless a.even?
177
+
178
+ prints
179
+
180
+ 1
181
+ 2
182
+
183
+ instead of only "2" -- the printing has already been done by the time we
184
+ call Ambit::fail!.
185
+
186
+ Such side effects can also be useful, however. This code:
187
+
188
+ i = 0
189
+ first = Ambit.choose(1..10)
190
+ second = Ambit.choose(1..10)
191
+ third = Ambit.choose(1..10)
192
+ i += 1
193
+ Ambit.fail! unless open_lock(first, second, third)
194
+ puts i
195
+
196
+ prints out the number of combinations which were tried in total (since +i+
197
+ remains incremented even when we rewind computation).
198
+
199
+ ==== More Than One Answer
200
+
201
+ Often, more than one combination of choices is interesting to consider -- it
202
+ may be useful, for instance, to see *all* combinations which do not fail,
203
+ instead of only the first.
204
+
205
+ Since Ambit::fail! will always rewind to the previous choice point, getting
206
+ more possible combinations is as easy as calling Ambit::fail! in order to
207
+ try the next combination, even though we have not, strictly, failed -- when
208
+ no more successful combinations are available, this call to Ambit::fail!
209
+ will instead raise an exception of type Ambit::ChoicesExhausted
210
+
211
+ begin
212
+ a = Ambit::choose([1, 3, 5, 7, 9, 11, 13, 15])
213
+ b = Ambit::choose([0, 5, 10, 15])
214
+ Ambit::assert a == b
215
+ puts a
216
+ Ambit::fail!
217
+ rescue Ambit::ChoicesExhausted
218
+ puts "Done."
219
+ end
220
+ prints
221
+ 5
222
+ 15
223
+ Done.
224
+
225
+ Note that this code, too depends on a side effect -- +a+ is output each time
226
+ we get a match, even though we then call Ambit::fail! to rewind computation
227
+ and try the next combination.
228
+
229
+ ==== Cleaning up
230
+
231
+ Ambit::clear! can be called at any time to eliminate all outstanding choices
232
+ on the default Generator, ending nondeterminism (and allowing any
233
+ outstanding alternate paths of execution to be garbage collected). This is
234
+ most useful when a given computation is finished, so that future invocations
235
+ of Ambit::fail! will not restart the now-finished computation with another
236
+ choice.
237
+
238
+ ==== Marking and Cutting
239
+
240
+ While Ambit::clear! can be used to abandon an entire set of nondeterministic
241
+ computations, sometimes it is useful to abandon only one branch of a
242
+ computation, while still keeping the ability to rewind to the choice which
243
+ first took us down that branch.
244
+
245
+ Suppose, for instance, that we are trying to guess a word with five letters:
246
+
247
+ a = Ambit::choose('a'..'z')
248
+ b = Ambit::choose('a'..'z')
249
+ c = Ambit::choose('a'..'z')
250
+ d = Ambit::choose('a'..'z')
251
+ Ambit::assert good_word(a, b, c, d)
252
+ print a, b, c, d
253
+
254
+ This works. But what if we were able to determine, once all four letters
255
+ were chosen, whether the first letter was correct? How would we proceed?
256
+
257
+ If we failed because the first letter was incorrect, we would continue trying
258
+ every possible value for the second, third and fourth letters -- even though none of
259
+ them could be correct. We need a way to rewind to an earlier choice point.
260
+
261
+ To work around this, Ambit provides a method, Ambit::cut! which "locks in" a
262
+ set of past choices, preventing them from being revisited later:
263
+
264
+ a = Ambit::choose('a'..'z')
265
+ Ambit::mark
266
+ b = Ambit::choose('a'..'z')
267
+ c = Ambit::choose('a'..'z')
268
+ d = Ambit::choose('a'..'z')
269
+ if !good_first_letter(a, b, c, d)
270
+ Ambit::cut!
271
+ Ambit::fail!
272
+ end
273
+ Ambit::assert good_word(a, b, c, d)
274
+ print a, b, c, d
275
+
276
+ When Ambit::cut! is called in the code above, all choices back to the
277
+ <em>most recent</em> call of Ambit::mark are wiped out -- the next call to
278
+ Ambit::fail! will rewind to the most recent Ambit::choose invocation
279
+ <em>before</em> the most recent call to Ambit::mark.
280
+
281
+ Ambit::cut! can also be used without Ambit::fail! to "commit" to all choices
282
+ since the last call to Ambit::mark -- in this case, we are saying that we
283
+ know these choices are good, so if we (later) fail, we want to rewind out of
284
+ the whole current branch of computation.
285
+
286
+ ==== Private Generators
287
+
288
+ In addition to using methods of the Ambit module directly, another option is
289
+ to allocate a Ambit::Generator object explicitly. All methods of the Ambit
290
+ module are also available as methods of Ambit::Generator (and in fact, the
291
+ module allocates a default Generator object to handle all calls made at the
292
+ module level).
293
+
294
+ Ambit::Generator::new can be used to allocate a new Generator:
295
+
296
+ nd = Ambit::Generator::new
297
+ nd.choose('a' .. 'e')
298
+
299
+ each object allocated in this fashion has its own set of choices, and
300
+ failing one will not directly affect others. Nesting choices from different
301
+ Generators is a good way to make code confusing, however, and should be
302
+ avoided -- this capability is mainly provided to allow multi-threaded
303
+ programs to safely use Ambit from more than one thread (see below).
304
+
305
+ Ambit::Generator#clear! is provided for the same reason as Ambit::clear!,
306
+ but it is often clearer to use a new Ambit::Generator object for each
307
+ unrelated set of nondeterministic computations.
308
+
309
+ ==== Compatibility
310
+
311
+ For historical reasons, Ambit::amb and Ambit::Generator#amb are provided as
312
+ aliases for Ambit::choose and Ambit::Generator#choose. Likewise, for
313
+ historical reasons, calling Ambit::amb (and Ambit::Generator#amb) with
314
+ no arguments is equivalent to calling Ambit::fail! (or
315
+ Ambit::Generator#fail!).
316
+
317
+ For the same reason, Ambit::require and Ambit::Generator#require are
318
+ provided as aliases for Ambit::assert and Ambit::Generator#assert.
319
+
320
+ These aliases allow for a more direct translation of programs written with
321
+ the *amb* operator discussed in *SICP* and elsewhere.
322
+
323
+ ==== Interaction with Threading
324
+
325
+ Given the strong modifications to flow of control which occur when a path of
326
+ computation is _failed_, care must be taken when using nondeterministic
327
+ programming in a multi-threaded program. The two main ways to do this are:
328
+
329
+ * perform all nondeterministic programming from a single thread of execution
330
+
331
+ * give each thread which will be using nondeterministic programming its own Ambit::Generator object. This can be done easily using thread local variables:
332
+
333
+ def nd_begin
334
+ Thread.current[:AMB] = Ambit::Generator.new
335
+ end
336
+
337
+ def nd_choose choices
338
+ Thread.current[:AMB].choose choices
339
+ end
340
+
341
+ def nd_fail!
342
+ Thread.current[:AMB].fail!
343
+ end
344
+
345
+ def nd_clear!
346
+ Thread.current[:AMB].clear!
347
+ end
348
+
349
+ === Longer example
350
+
351
+ This solution to the N queens problem is inspired by the prolog version in
352
+ <em>The Art of Prolog</em> by Leon Sterling and Ehud Shapiro[3], but is less
353
+ elegant, as this is not prolog (and I am not Sterling or Shapiro).
354
+
355
+ # we want to place N queens on an NxN chess board. Since we know no two queens
356
+ # can be in the same row, an array of N integers between 0 and N-1 will do to
357
+ # represent the placement. Since we know no two queens can be in the same column,
358
+ # each number from 1 .. N will appear once in this array; this means the solution
359
+ # is a permutation of 1 .. N
360
+
361
+ # Here is the complete board generator. Next is the test if a position is safe.
362
+
363
+ def queens n, board = []
364
+ if board.size == n
365
+ board
366
+ else
367
+ c = Ambit.choose(1..n)
368
+ Ambit.fail! unless safe board, c
369
+ queens n, board + [c]
370
+ end
371
+ end
372
+
373
+ # board is the first M columns of an NxN board, and is valid so far.
374
+ # piece is a proposed piece for the M+1th row of the board.
375
+ # returns true if piece is a valid placement, false otherwise
376
+
377
+ def safe board, piece
378
+ board.each_with_index do |c, r|
379
+ return false if c == piece # same column
380
+ # they're on the same diagonal if the distance in columns == the distance in rows
381
+ rdist = board.size - r
382
+ cdist = (piece - c).abs
383
+ return false if rdist == cdist
384
+ end
385
+ true
386
+ end
387
+
388
+ The file examples/queens.rb, installed with this gem, contains a version of
389
+ this with display code, and a command-line driver to print all solutions for
390
+ a given N.
391
+
392
+ === References
393
+
394
+ [1] Graham, Paul, <em>On Lisp</em>, Prentice Hall, 1993. Available online at http://www.paulgraham.com/onlisp.html
395
+
396
+ [2] Abelson, Harold and Gerald Jay Sussman, <em>Structure and Interpretation of Computer Programs, 2nd Edition</em>, MIT Press, 1996. Available online at http://mitpress.mit.edu/sicp/
397
+
398
+ [3] Sterling, Leon and Ehud Shapiro, <em>The Art of Prolog</em>, MIT Press, 1994
399
+
400
+ == LICENSE:
401
+
402
+ (The BSD 2-clause License)
403
+
404
+ Copyright (c) 2011 Jim Wise
405
+ All rights reserved.
406
+
407
+ Redistribution and use in source and binary forms, with or without
408
+ modification, are permitted provided that the following conditions
409
+ are met:
410
+
411
+ 1. Redistributions of source code must retain the above copyright
412
+ notice, this list of conditions and the following disclaimer.
413
+ 2. Redistributions in binary form must reproduce the above copyright
414
+ notice, this list of conditions and the following disclaimer in the
415
+ documentation and/or other materials provided with the distribution.
416
+
417
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
418
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
419
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
420
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
421
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
422
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
423
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
424
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
425
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
426
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
427
+ POSSIBILITY OF SUCH DAMAGE.