amb 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +14 -0
- data/README.md +26 -6
- data/lib/amb.rb +2 -2
- data/lib/amb/amb.rb +16 -2
- data/lib/amb/version.rb +1 -1
- metadata +5 -5
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -19,7 +19,7 @@ Typically, the programmer would specify a limited number of alternatives (eg. di
|
|
19
19
|
|
20
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. [Quoting Dorai Sitaram](http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme-Z-H-16.html#node_chap_14), "`amb` takes zero or more expressions, and makes a nondeterministic (or "ambiguous") choice among them, preferring those choices that cause the program to converge meaningfully". 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; if only one is defined, whichever is defined; and terminate the program if none is defined. 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 though (for unpredictability is not what computers enjoys doing). 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, in a depth-first selection algorithm. It *is* dumb yet it works as expected.
|
21
21
|
|
22
|
-
Thus the most common strategy used for implementing `amb()`'s logic (check/discard) is chronological backtracking. It
|
22
|
+
Thus the most common strategy used for implementing `amb()`'s logic (check/discard) is chronological backtracking. It is kind of a brute-force, linear approach, which always relies on some sort of continuations (`call/cc`). A continuation is like a savepoint, representing "what's left to run" at a given time. Recording as much continuations as alternatives enables `amb` to test "the rest of the program" multiple times until a valid solution is found. This rather dumb algorithm may be tweaked into a (not so) smarter backjumping algorithm for more efficiency, depending on the nature of the problem at stake. Another strategy is reinforcement learning (aka. constraint learning), as used in some AI systems. Most of the time it's really hard. This library implements simple backtracking only.
|
23
23
|
|
24
24
|
More details on all of this under the `doc/` folder (*pending*).
|
25
25
|
|
@@ -58,13 +58,15 @@ examining x = 3, y = 1
|
|
58
58
|
examining x = 3, y = 2
|
59
59
|
solution: x = 3, y = 2
|
60
60
|
```
|
61
|
-
This illustrates the incremental, backtracking pattern leading to the first valid solution.
|
61
|
+
This illustrates the incremental, backtracking pattern leading to the first valid solution.
|
62
|
+
|
63
|
+
Many more examples under the `examples/` directory. You may run them with the `-d` flag to output information about the solving process.
|
62
64
|
|
63
65
|
## Installation
|
64
66
|
|
65
67
|
gem install amb
|
66
68
|
|
67
|
-
##
|
69
|
+
## Step by step
|
68
70
|
|
69
71
|
Here's a raw use-case, presenting the required *steps*. For concrete examples, see the `examples/` directory.
|
70
72
|
|
@@ -82,15 +84,33 @@ end
|
|
82
84
|
amb = Ambiguous.new
|
83
85
|
```
|
84
86
|
|
85
|
-
Then define your alternatives using `#choose` (aliased as `#choices` or `#alternatives`). It may take arbitrary code (values, proc…).
|
87
|
+
Then, define your alternatives using `#choose` (aliased as `#choices` or `#alternatives`). It may take arbitrary code (values, proc…). Don't forget to assign it to a variable, otherwise it is just useless alternatives. Most of the time, several alternatives set will be defined.
|
86
88
|
|
87
89
|
``` ruby
|
88
|
-
amb.choose()
|
90
|
+
x = amb.choose(1, 2, lambda { some code})
|
91
|
+
y = amb.choose(:some, :more, :alternatives)
|
89
92
|
```
|
90
93
|
|
94
|
+
Then, state at least one constraint which, hopefully, references your alternatives. If more than one constraint is expressed, all of them will be considered in the problem solving.
|
95
|
+
|
96
|
+
``` ruby
|
97
|
+
amb.assert(x != y)
|
98
|
+
amb.assert heavy_logical_computation_on(x, y)
|
99
|
+
```
|
100
|
+
|
101
|
+
And so on. Once again, for real use-cases, see the `examples/` directory.
|
102
|
+
|
91
103
|
## TODO
|
92
104
|
|
105
|
+
* more examples!
|
106
|
+
* more specs!
|
107
|
+
* spec out `choose` depending on a previous `choose`
|
108
|
+
* implements a version of amb matching the original definition (returns one of its argument at random) => keep track of backtracking paths
|
109
|
+
* implements a trampoline version (continuation-passing style)
|
110
|
+
* memoization/reset?
|
111
|
+
* a different selection algo? (BFS, smarter one)
|
112
|
+
|
93
113
|
## See also
|
94
114
|
|
95
|
-
*
|
115
|
+
* `doc/` and `examples/` directories.
|
96
116
|
* Continuations and fibers concepts.
|
data/lib/amb.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'amb/amb'
|
2
|
+
require 'amb/amb_operator'
|
data/lib/amb/amb.rb
CHANGED
@@ -98,6 +98,8 @@ module Amb
|
|
98
98
|
end
|
99
99
|
failure
|
100
100
|
end
|
101
|
+
alias :choices :choose
|
102
|
+
alias :alternatives :choose
|
101
103
|
|
102
104
|
# Unconditional failure of a constraint, causing the last choice to be
|
103
105
|
# retried. This is equivalent to saying `assert(false)`.
|
@@ -106,6 +108,10 @@ module Amb
|
|
106
108
|
# @TODO it'd be better not to have to
|
107
109
|
#
|
108
110
|
def failure
|
111
|
+
if $DEBUG
|
112
|
+
@__num_of_tries ||= 1
|
113
|
+
@__num_of_tries += 1
|
114
|
+
end
|
109
115
|
back_amb.pop.call
|
110
116
|
end
|
111
117
|
|
@@ -135,25 +141,33 @@ module Amb
|
|
135
141
|
puts failure_message
|
136
142
|
end
|
137
143
|
|
144
|
+
def branches_count
|
145
|
+
@__num_of_tries
|
146
|
+
end
|
147
|
+
|
138
148
|
module ClassMethods
|
139
149
|
# Class convenience method to search for the first solution to the
|
140
150
|
# constraints.
|
141
151
|
#
|
142
|
-
def solve(failure_message = "No
|
152
|
+
def solve(failure_message = "No solution.")
|
143
153
|
amb = self.new
|
144
154
|
yield(amb)
|
145
155
|
rescue Amb::ExhaustedError => ex
|
156
|
+
puts
|
157
|
+
puts "#{amb.branches_count} branches explored." if $DEBUG
|
146
158
|
amb.report(failure_message)
|
147
159
|
end
|
148
160
|
|
149
161
|
# Class convenience method to search for all the solutions to the
|
150
162
|
# constraints.
|
151
163
|
#
|
152
|
-
def solve_all(failure_message = "No
|
164
|
+
def solve_all(failure_message = "No more solutions.")
|
153
165
|
amb = self.new
|
154
166
|
yield(amb)
|
155
167
|
amb.failure
|
156
168
|
rescue Amb::ExhaustedError => ex
|
169
|
+
puts
|
170
|
+
puts "#{amb.branches_count} branches explored." if $DEBUG
|
157
171
|
amb.report(failure_message)
|
158
172
|
end
|
159
173
|
end
|
data/lib/amb/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-09-04 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: amb
|
16
|
-
requirement: &
|
16
|
+
requirement: &75631670 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *75631670
|
25
25
|
description: This gem is a compilation of several implementations of the ambiguous
|
26
26
|
function/operator, useful for constraint programming.
|
27
27
|
email: jd@vauguet.fr
|
@@ -56,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
56
|
version: '0'
|
57
57
|
requirements: []
|
58
58
|
rubyforge_project:
|
59
|
-
rubygems_version: 1.8.
|
59
|
+
rubygems_version: 1.8.10
|
60
60
|
signing_key:
|
61
61
|
specification_version: 3
|
62
62
|
summary: McCarty's ambiguous function/operator implementations
|