amb 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ ## 0.0.1
2
+
3
+ * Initial setup
@@ -0,0 +1,20 @@
1
+ Copyright 2010 Jean-Denis Vauguet
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,74 @@
1
+ This gem is a compilation of several implementations of the **ambiguous function/operator** pattern, useful for constraint programming. Each implementation comes with its own copyright notice and authors, so be sure to check the CREDITS file!
2
+
3
+ Ever wanted to:
4
+
5
+ * write straightforward parsers?
6
+ * solve crosswords and sudokus or the n-queens problem?
7
+ * design an equation-solver or a modelizer?
8
+ * understand how logic programming languages (Prolog…) work?
9
+
10
+ Them amb may be of interest to you.
11
+
12
+ ## Synopsis
13
+
14
+ *You may want to jump straight to the Examples section if formal stuff annoys you.*
15
+
16
+ Ambiguous functions were [defined in John McCarty's 1963 paper](http://www-formal.stanford.edu/jmc/basis1/node7.html) *A basis for a mathematical theory of computation*. They allow for writing nondeterministic programs which contain various alternatives for the program flow.
17
+
18
+ Typically, the programmer would specify a limited number of alternatives (eg. different values for a variable), so that the program must later choose between them at runtime to find which one(s) are valid predicates to solve the issue at stake. The "issue" is often formalized as a constraint or a set of constraints referencing the variables some alternatives have been provided for. The evaluation may result in the discovery of dead ends, in which case it must "switch" to a previous branching point and start over with a different alternative.
19
+
20
+ To discover which alternatives groups are valid, a check/discard/switch strategy must be enforced by the program. Most often it will make use of a ambiguous operator, `amb()` which implements this strategy. The most basic version of `amb` would be `amb(x,y)`, which returns, *in an unpredictible way*, either x or y when both are defined, or if only one is defined, whichever is defined. If none is defined, it will terminate the program and complain no solution can be found given these alternatives and constraint(s). Using some recursivity, `amb()` may be used to define arbitrary, complex ambiguous functions. It is quite difficult to implement a good `amb()` operator matching that formal definition. A simpler (yet functional) version has the operator return its first defined argument, then pass over the next defined one in case of a dead-end.
21
+
22
+ The most common strategy used for implementing `amb()`'s logic (check/discard) is backtracking, which is "a general algorithm for finding all (or some) solutions to some computational problem, that incrementally builds candidates to the solutions, and abandons each partial candidate *c* ("backtracks") as soon as it determines that *c* cannot possibly be completed to a valid solution". It almost always relies on some sort of continuations. It may be turned into backjumping for more efficiency, depending on the problem at stake. Another strategy is reinforcement learning or constraint learning, as used in some AI systems. This library implements simple backtracking only.
23
+
24
+ More details on all of this under the `doc/`` folder (*pending*).
25
+
26
+ ## Examples
27
+
28
+ ``` ruby
29
+ # Amb is a module
30
+ A = Class.new { include Amb }.new
31
+
32
+ x = A.choose(1,2,3,4) # register each alternative as a backtrack point
33
+ y = A.choose(1,2,3,4) # same for y, so we have 16 backtracking branches
34
+
35
+ puts "examining x = #{x}, y = #{y}"
36
+
37
+ # assertions will backtrack if a dead-end is discovered
38
+ A.assert x + y == 5
39
+ A.assert x - y == 1
40
+ #A.assert x == 2 # if this line is uncommented, then no solution can be found
41
+ # and the program raises a Amb::ExhaustedError
42
+
43
+ puts "> solution: x = #{x}, y = #{y}"
44
+ ```
45
+
46
+ will produce:
47
+
48
+ ``` ruby
49
+ examining x = 1, y = 1
50
+ examining x = 1, y = 2
51
+ examining x = 1, y = 3
52
+ examining x = 1, y = 4
53
+ examining x = 2, y = 1
54
+ examining x = 2, y = 2
55
+ examining x = 2, y = 3
56
+ examining x = 2, y = 4
57
+ examining x = 3, y = 1
58
+ examining x = 3, y = 2
59
+ solution: x = 3, y = 2
60
+ ```
61
+ This illustrates the incremental, backtracking pattern. Many more examples under the `examples/` directory.
62
+
63
+ ## Installation
64
+
65
+ gem install amb
66
+
67
+ ## Usage
68
+
69
+ ## TODO
70
+
71
+ ## See also
72
+
73
+ * the `doc/` and `examples/` directories.
74
+ * Continuations and fibers concepts.
@@ -0,0 +1,2 @@
1
+ require './lib/amb/amb'
2
+ require './lib/amb/amb_operator'
@@ -0,0 +1,160 @@
1
+ # Copyright 2006 by Jim Weirich <jim@weirichhouse.org>. All rights reserved.
2
+ # Permission is granted for use, modification and distribution as
3
+ # long as the above copyright notice is included.
4
+ # Modified by Jean-Denis Vauguet <jd@vauguet.fr> for Amb gem release.
5
+
6
+ # Amb is an ambiguous choice maker. You can ask an Amb object to
7
+ # select from a discrete list of choices, and then specify a set of
8
+ # constraints on those choices. After the constraints have been
9
+ # specified, you are guaranteed that the choices made earlier by amb
10
+ # will obey the constraints.
11
+ #
12
+ # For example, consider the following code:
13
+ #
14
+ # amb = Class.new { include Amb }.new
15
+ # x = amb.choose(1,2,3,4)
16
+ #
17
+ # At this point, amb may have chosen any of the four numbers (1
18
+ # through 4) to be assigned to x. But, now we can assert some
19
+ # conditions:
20
+ #
21
+ # amb.assert (x % 2) == 0
22
+ #
23
+ # This asserts that x must be even, so we know that the choice made by
24
+ # amb will be either 2 or 4. Next we assert:
25
+ #
26
+ # amb.assert x >= 3
27
+ #
28
+ # This further constrains our choice to 4.
29
+ #
30
+ # puts x # prints '4'
31
+ #
32
+ # Amb works by saving a contination at each choice point and
33
+ # backtracking to previousl choices if the contraints are not
34
+ # satisfied. In actual terms, the choice reconsidered and all the
35
+ # code following the choice is re-run after failed assertion.
36
+ #
37
+ # You can print out all the solutions by printing the solution and
38
+ # then explicitly failing to force another choice. For example:
39
+ #
40
+ # amb = Class.new { include Amb }.new
41
+ # x = Amb.choose(*(1..10))
42
+ # y = Amb.choose(*(1..10))
43
+ # amb.assert x + y == 15
44
+ #
45
+ # puts "x = #{x}, y = #{y}"
46
+ #
47
+ # amb.failure
48
+ #
49
+ # The above code will print all the solutions to the equation x + y ==
50
+ # 15 where x and y are integers between 1 and 10.
51
+ #
52
+ # The Amb class has two convience functions, solve and solve_all for
53
+ # encapsulating the use of Amb.
54
+ #
55
+ # This example finds the first solution to a set of constraints:
56
+ #
57
+ # Amb.solve do |amb|
58
+ # x = amb.choose(1,2,3,4)
59
+ # amb.assert (x % 2) == 0
60
+ # puts x
61
+ # end
62
+ #
63
+ # This example finds all the solutions to a set of constraints:
64
+ #
65
+ # Amb.solve_all do |amb|
66
+ # x = amb.choose(1,2,3,4)
67
+ # amb.assert (x % 2) == 0
68
+ # puts x
69
+ # end
70
+ #
71
+ module Amb
72
+ require 'continuation'
73
+
74
+ class ExhaustedError < RuntimeError; end
75
+
76
+ def self.included(base)
77
+ base.extend(ClassMethods)
78
+ end
79
+
80
+ # Memoize and return the alternatives continuations.
81
+ #
82
+ # @return [Array<Proc, Continuation>]
83
+ #
84
+ def back_amb
85
+ @__back_amb ||= [Proc.new { fail ExhaustedError, "amb tree exhausted" }]
86
+ end
87
+
88
+ # Make a choice amoung a set of discrete values.
89
+ #
90
+ # @param choices
91
+ #
92
+ def choose(*choices)
93
+ choices.each do |choice|
94
+ callcc do |fk|
95
+ back_amb << fk
96
+ return choice
97
+ end
98
+ end
99
+ failure
100
+ end
101
+
102
+ # Unconditional failure of a constraint, causing the last choice to be
103
+ # retried. This is equivalent to saying `assert(false)`.
104
+ #
105
+ def failure
106
+ back_amb.pop.call
107
+ end
108
+
109
+ # Assert the given condition is true. If the condition is false,
110
+ # cause a failure and retry the last choice. One may specify the condition
111
+ # either as an argument or as a block. If a block is provided, it will be
112
+ # passed the arguments, whereas without a block, the first argument will
113
+ # be used as the condition.
114
+ #
115
+ # @param cond
116
+ # @yield
117
+ #
118
+ def assert(*args)
119
+ cond = block_given? ? yield(*args) : args.first
120
+ failure unless cond
121
+ end
122
+
123
+ # Report the given failure message. This is called by solve in the event
124
+ # that no solutions are found, and by +solve_all+ when no more solutions
125
+ # are to be found. Report will simply display the message to standard
126
+ # output, but you may override this method in a derived class if you need
127
+ # different behavior.
128
+ #
129
+ # @param [#to_s] failure_message
130
+ #
131
+ def report(failure_message)
132
+ puts failure_message
133
+ end
134
+
135
+ module ClassMethods
136
+ # Class convenience method to search for the first solution to the
137
+ # constraints.
138
+ #
139
+ def solve(failure_message="No Solution")
140
+ amb = self.new
141
+ yield(amb)
142
+ rescue Amb::ExhaustedError => ex
143
+ amb.report(failure_message)
144
+ end
145
+
146
+ # Class convenience method to search for all the solutions to the
147
+ # constraints.
148
+ #
149
+ def solve_all(failure_message="No More Solutions")
150
+ amb = self.new
151
+ yield(amb)
152
+ amb.failure
153
+ rescue Amb::ExhaustedError => ex
154
+ amb.report(failure_message)
155
+ end
156
+ end
157
+
158
+ extend self
159
+ extend self::ClassMethods
160
+ end
@@ -0,0 +1,68 @@
1
+ # By Eric Kidd.
2
+ # See http://www.randomhacks.net/articles/2005/10/11/amb-operator.
3
+ module Amb
4
+ # Use the ambiguous computation pattern as a stand-alone operator.
5
+ module Operator
6
+ require 'continuation'
7
+
8
+ # A list of places we can "rewind" to if we encounter amb with no arguments.
9
+ $backtrack_points = []
10
+
11
+ # Rewind to our most recent backtrack point.
12
+ #
13
+ def backtrack
14
+ if $backtrack_points.empty?
15
+ raise "Can't backtrack"
16
+ else
17
+ $backtrack_points.pop.call
18
+ end
19
+ end
20
+
21
+ # Recursive implementation of the amb operator, as defined by McCarty[63].
22
+ #
23
+ # When choices are provided as arguments, it will save a backtracking save
24
+ # point for each and wait for being resumed. Resuming (starting the
25
+ # check/discard process) is triggered by calling amb without any arguments.
26
+ # It is expected you'll attach a conditionnal stating the constraint.
27
+ #
28
+ # @param choices a set of alternatives
29
+ # @example
30
+ #
31
+ # # amb will (appear to) choose values
32
+ # # for x and y that prevent future trouble.
33
+ # x = amb 1, 2, 3
34
+ # y = amb 4, 5, 6
35
+ #
36
+ # # Ooops! If x*y isn't 8, amb would
37
+ # # get angry. You wouldn't like
38
+ # # amb when it's angry.
39
+ # amb if x*y != 8
40
+ #
41
+ # # Sure enough, x is 2 and y is 4.
42
+ # puts "x = #{x}, y = #{y}"
43
+ #
44
+ def amb *choices
45
+ # Fail if we have no arguments.
46
+ backtrack if choices.empty?
47
+
48
+ callcc do |cc|
49
+ # cc contains the "current continuation". When called, it will make the
50
+ # program rewind to the end of this block.
51
+ $backtrack_points << cc
52
+
53
+ # Return our first argument.
54
+ return choices[0]
55
+ end
56
+
57
+ # We only get here if we backtrack by resuming the stored value of cc.
58
+ # We call amb recursively with the arguments we didn't use.
59
+ amb(*choices[1...choices.length])
60
+ end
61
+
62
+ # Backtracking beyond a call to cut is strictly forbidden.
63
+ #
64
+ def cut
65
+ $backtrack_points = []
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,6 @@
1
+ module Amb
2
+ MAJOR = 0
3
+ MINOR = 0
4
+ PATCH = 2
5
+ VERSION = [MAJOR, MINOR, PATCH].join('.')
6
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jean-Denis Vauguet <jd@vauguet.fr>
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-28 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: amb
16
+ requirement: &73260010 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *73260010
25
+ description: This gem is a compilation of several implementations of the ambiguous
26
+ function/operator, useful for constraint programming.
27
+ email: jd@vauguet.fr
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - lib/amb.rb
33
+ - lib/amb/amb_operator.rb
34
+ - lib/amb/amb.rb
35
+ - lib/amb/version.rb
36
+ - MIT-LICENSE
37
+ - README.md
38
+ - CHANGELOG.md
39
+ homepage: http://www.github.com/chikamichi/amb
40
+ licenses: []
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.6
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: McCarty's ambiguous function/operator implementations
63
+ test_files: []