longjing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,326 @@
1
+ class Longjing::PDDL::Parser
2
+ options no_result_var
3
+
4
+ token DEFINE DOMAIN REQUIREMENTS TYPES PREDICATES
5
+ ACTION PARAMETERS PRECONDITION EFFECT
6
+ PROBLEM OBJECTS GOAL INIT
7
+ NOT AND EQUAL
8
+ OPEN_BRACE CLOSE_BRACE
9
+ SYMBOL DASH ID VAR
10
+
11
+ rule
12
+
13
+ target
14
+ : OPEN_BRACE DEFINE domain_name requirements types predicates actions CLOSE_BRACE
15
+ { val[2].merge!({
16
+ requirements: val[3],
17
+ types: val[4],
18
+ predicates: val[5],
19
+ actions: val[6]
20
+ })}
21
+ | OPEN_BRACE DEFINE domain_name requirements predicates actions CLOSE_BRACE
22
+ { val[2].merge!({
23
+ requirements: val[3],
24
+ predicates: val[4],
25
+ actions: val[5]
26
+ })}
27
+ | OPEN_BRACE DEFINE domain_name types predicates actions CLOSE_BRACE
28
+ { val[2].merge!({
29
+ requirements: [:strips],
30
+ types: val[3],
31
+ predicates: val[4],
32
+ actions: val[5]
33
+ })}
34
+ | OPEN_BRACE DEFINE domain_name predicates actions CLOSE_BRACE
35
+ { val[2].merge!({
36
+ requirements: [:strips],
37
+ predicates: val[3],
38
+ actions: val[4]
39
+ })}
40
+ | OPEN_BRACE DEFINE domain_problem objects init goal CLOSE_BRACE
41
+ { val[2].merge({ objects: val[3], init: val[4], goal: val[5] })}
42
+ | OPEN_BRACE DEFINE domain_problem init goal CLOSE_BRACE
43
+ { val[2].merge({ objects: [], init: val[3], goal: val[4] })}
44
+ ;
45
+
46
+ domain_name
47
+ : OPEN_BRACE DOMAIN name CLOSE_BRACE { domain(val[2]) }
48
+ ;
49
+
50
+ domain_problem
51
+ : OPEN_BRACE PROBLEM name CLOSE_BRACE OPEN_BRACE DOMAIN name CLOSE_BRACE
52
+ { problem(val[2], val[6]) }
53
+ ;
54
+
55
+ requirements
56
+ : OPEN_BRACE REQUIREMENTS symbols CLOSE_BRACE { requirements(val[2]) }
57
+ ;
58
+
59
+ types
60
+ : OPEN_BRACE TYPES type_list CLOSE_BRACE { val[2] }
61
+ ;
62
+
63
+ predicates
64
+ : OPEN_BRACE PREDICATES predicate_list CLOSE_BRACE { val[2] }
65
+ ;
66
+
67
+ objects
68
+ : OPEN_BRACE OBJECTS object_list CLOSE_BRACE { val[2] }
69
+ | OPEN_BRACE OBJECTS CLOSE_BRACE { [] }
70
+ ;
71
+
72
+ init
73
+ : OPEN_BRACE INIT literals CLOSE_BRACE { val[2] }
74
+ | OPEN_BRACE INIT CLOSE_BRACE { [] }
75
+ ;
76
+
77
+ goal
78
+ : OPEN_BRACE GOAL literal CLOSE_BRACE { val[2] }
79
+ ;
80
+
81
+ actions
82
+ : action actions { [val[0]] + val[1] }
83
+ | action { [val[0]] }
84
+ ;
85
+
86
+ predicate_list
87
+ : predicate predicate_list { [val[0]] + val[1] }
88
+ | predicate { [val[0]] }
89
+ ;
90
+
91
+ action
92
+ : OPEN_BRACE ACTION name parameters precondition effect CLOSE_BRACE
93
+ { @params = nil; Action.new(val[2], val[3], val[4], val[5]) }
94
+ ;
95
+
96
+ parameters
97
+ : PARAMETERS OPEN_BRACE vars_list CLOSE_BRACE { parameters(val[2]) }
98
+ | PARAMETERS empty { [] }
99
+ ;
100
+
101
+ precondition
102
+ : PRECONDITION literal { val[1] }
103
+ | PRECONDITION empty { val[1] }
104
+ ;
105
+
106
+ effect
107
+ : EFFECT literal { val[1] }
108
+ | EFFECT empty { val[1] }
109
+ ;
110
+
111
+ literals
112
+ : literal literals { [val[0]] + val[1] }
113
+ | literal { [val[0]] }
114
+ ;
115
+
116
+ literal
117
+ : atom_literal
118
+ | OPEN_BRACE AND atom_literals CLOSE_BRACE { And.new(val[2]) }
119
+ ;
120
+
121
+ atom_literals
122
+ : atom_literal atom_literals { [val[0]] + val[1] }
123
+ | atom_literal { [val[0]] }
124
+ ;
125
+
126
+ atom_literal
127
+ : OPEN_BRACE name object_list CLOSE_BRACE { Fact[@predicates.fetch(val[1]), val[2]] }
128
+ | OPEN_BRACE name vars_list CLOSE_BRACE { Formula.new(@predicates.fetch(val[1]), val[2]) }
129
+ | OPEN_BRACE EQUAL object_list CLOSE_BRACE { Equal.new(*(val[2])) }
130
+ | OPEN_BRACE EQUAL vars_list CLOSE_BRACE { EqualFormula.new(*(val[2])) }
131
+ | OPEN_BRACE name CLOSE_BRACE { Fact[@predicates.fetch(val[1]), []] }
132
+ | OPEN_BRACE NOT atom_literal CLOSE_BRACE { Not[val[2]] }
133
+ ;
134
+
135
+ object_list
136
+ : objects_t object_list { val[0] + val[1] }
137
+ | objects_t { val[0] }
138
+ | names { val[0].map {|n| object(n)} }
139
+ ;
140
+
141
+ objects_t
142
+ : names type { val[0].map {|n| object(n, val[1])} }
143
+ ;
144
+
145
+ type_list
146
+ : types_t type_list { val[0] + val[1] }
147
+ | types_t { val[0] }
148
+ | names { val[0].map {|t| type(t)} }
149
+ ;
150
+
151
+ types_t
152
+ : names type { val[0].map {|t| type(t, val[1])} }
153
+ ;
154
+
155
+ predicate
156
+ : OPEN_BRACE name vars_list CLOSE_BRACE { predicate(val[1], val[2]) }
157
+ | OPEN_BRACE name CLOSE_BRACE { predicate(val[1]) }
158
+ ;
159
+
160
+ vars_list
161
+ : vars_t vars_list { val[0] + val[1] }
162
+ | vars_t { val[0] }
163
+ | var_names { val[0].map{|v| @params ? @params.fetch(v) : Var.new(v)} }
164
+ ;
165
+
166
+ vars_t
167
+ : var_names type { val[0].map{|v| Var.new(v, val[1])} }
168
+ ;
169
+
170
+ names
171
+ : name names { [val[0]] + val[1] }
172
+ | name { [val[0]] }
173
+ ;
174
+
175
+ type
176
+ : DASH name { type(val[1]) }
177
+ ;
178
+
179
+ var_names
180
+ : VAR var_names { [val[0]] + val[1] }
181
+ | VAR { [val[0]] }
182
+ ;
183
+
184
+ symbols
185
+ : SYMBOL symbols { [val[0]] + val[1] }
186
+ | SYMBOL { [val[0]] }
187
+ ;
188
+
189
+ name
190
+ : ID
191
+ | DEFINE
192
+ | DOMAIN
193
+ | PROBLEM
194
+ | NOT
195
+ | AND
196
+ ;
197
+
198
+ empty
199
+ : OPEN_BRACE CLOSE_BRACE { EMPTY }
200
+ ;
201
+ ---- header ----
202
+ require 'strscan'
203
+ require 'longjing/pddl/type'
204
+ require 'longjing/pddl/var'
205
+ require 'longjing/pddl/obj'
206
+ require 'longjing/pddl/predicate'
207
+ require 'longjing/pddl/literal'
208
+ require 'longjing/pddl/action'
209
+ ---- inner ----
210
+ SUPPORTED_REQUIREMENTS = [:strips, :typing,
211
+ :'negative-preconditions',
212
+ :equality]
213
+
214
+ def domain(name)
215
+ @predicates, @types = {}, {}
216
+ @domains[name] = { domain: name }
217
+ end
218
+
219
+ def problem(name, domain_name)
220
+ domain = @domains[domain_name]
221
+ raise UnknownDomain unless domain
222
+ @predicates = Hash[domain[:predicates].map{|pred| [pred.name, pred]}]
223
+ @types = if domain[:types]
224
+ Hash[domain[:types].map{|t| [t.name, t]}]
225
+ end
226
+ @objects = {}
227
+ { problem: name }.merge(domain)
228
+ end
229
+
230
+ def requirements(reqs)
231
+ unsupported = reqs - SUPPORTED_REQUIREMENTS
232
+ raise UnsupportedRequirements, unsupported unless unsupported.empty?
233
+ reqs
234
+ end
235
+
236
+ def predicate(name, vars=nil)
237
+ raise "Duplicated predicate name #{name}" if @predicates.has_key?(name)
238
+ @predicates[name] = Predicate.new(name, vars)
239
+ end
240
+
241
+ def type(name, parent=nil)
242
+ @types[name] ||= Type.new(name, parent)
243
+ end
244
+
245
+ def object(name, type=nil)
246
+ @objects[name] ||= Obj.new(name, type)
247
+ end
248
+
249
+ def parameters(params)
250
+ @params = Hash[params.map{|param| [param.name, param]}]
251
+ params
252
+ end
253
+
254
+ def parse(str, domains)
255
+ @domains = domains
256
+ @tokens = []
257
+ str = "" if str.nil?
258
+ scanner = StringScanner.new(str + ' ')
259
+
260
+ until scanner.eos?
261
+ case
262
+ when scanner.scan(/\s+/)
263
+ # ignore space
264
+ when scanner.scan(/;.*$/)
265
+ # ignore comments
266
+ when m = scanner.scan(/[\(]/)
267
+ @tokens.push [:OPEN_BRACE, m]
268
+ when m = scanner.scan(/[\)]/)
269
+ @tokens.push [:CLOSE_BRACE, m]
270
+ when m = scanner.scan(/-\s/)
271
+ @tokens.push [:DASH, m.strip.to_sym]
272
+ when m = scanner.scan(/=\s/)
273
+ @tokens.push [:EQUAL, m.strip.to_sym]
274
+ when m = scanner.scan(/define\b/i)
275
+ @tokens.push [:DEFINE, m.to_sym]
276
+ when m = scanner.scan(/\:?domain\b/i)
277
+ @tokens.push [:DOMAIN, m.to_sym]
278
+ when m = scanner.scan(/problem\b/i)
279
+ @tokens.push [:PROBLEM, m.to_sym]
280
+ when m = scanner.scan(/\:requirements\b/i)
281
+ @tokens.push [:REQUIREMENTS, m]
282
+ when m = scanner.scan(/\:types\b/i)
283
+ @tokens.push [:TYPES, m]
284
+ when m = scanner.scan(/\:predicates\b/i)
285
+ @tokens.push [:PREDICATES, m]
286
+ when m = scanner.scan(/\:action\b/i)
287
+ @tokens.push [:ACTION, m]
288
+ when m = scanner.scan(/\:parameters\b/i)
289
+ @tokens.push [:PARAMETERS, m]
290
+ when m = scanner.scan(/\:precondition\b/i)
291
+ @tokens.push [:PRECONDITION, m]
292
+ when m = scanner.scan(/\:effect\b/i)
293
+ @tokens.push [:EFFECT, m]
294
+ when m = scanner.scan(/\:objects\b/i)
295
+ @tokens.push [:OBJECTS, m]
296
+ when m = scanner.scan(/\:goal\b/i)
297
+ @tokens.push [:GOAL, m]
298
+ when m = scanner.scan(/\:init\b/i)
299
+ @tokens.push [:INIT, m]
300
+ when m = scanner.scan(/not\b/i)
301
+ @tokens.push [:NOT, m.to_sym]
302
+ when m = scanner.scan(/and\b/i)
303
+ @tokens.push [:AND, m.to_sym]
304
+ when m = scanner.scan(/\:[\w\-]+\b/i)
305
+ @tokens.push [:SYMBOL, m[1..-1].to_sym]
306
+ when m = scanner.scan(/\?[a-z][\w\-]*\b/i)
307
+ @tokens.push [:VAR, m.to_sym]
308
+ when m = scanner.scan(/[a-z][\w\-]*\b/i)
309
+ @tokens.push [:ID, m.to_sym]
310
+ else
311
+ raise "unexpected characters: #{scanner.peek(5).inspect}"
312
+ end
313
+ end
314
+ @tokens.push [false, false]
315
+ do_parse
316
+ end
317
+
318
+ def next_token
319
+ @tokens.shift
320
+ end
321
+
322
+ def on_error(t, val, vstack)
323
+ trace = vstack.each_with_index.map{|l, i| "#{' ' * i}#{l}"}
324
+ raise ParseError,
325
+ "\nparse error on value #{val.inspect}\n#{trace.join("\n")}"
326
+ end
@@ -0,0 +1,20 @@
1
+ module Longjing
2
+ module PDDL
3
+ class Predicate
4
+ attr_reader :name, :hash
5
+ def initialize(name, vars=nil)
6
+ @name = name
7
+ @vars = vars || []
8
+ @hash = name.hash
9
+ end
10
+
11
+ def to_s
12
+ "(#{[name, *@vars].join(' ')})"
13
+ end
14
+
15
+ def inspect
16
+ "(pred #{to_s})"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Longjing
2
+ module PDDL
3
+ class Type
4
+ attr_reader :name, :parent, :hash
5
+ def initialize(name, parent=nil)
6
+ @name = name
7
+ @parent = if name != :object
8
+ parent || Type::OBJECT
9
+ end
10
+ @hash = name.hash
11
+ end
12
+
13
+ def to_s
14
+ @parent ? "#{@name} - #{@parent}" : @name.to_s
15
+ end
16
+
17
+ def inspect
18
+ "(type #{to_s})"
19
+ end
20
+
21
+ OBJECT = Type.new(:object)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ module Longjing
2
+ module PDDL
3
+ class Var
4
+ attr_reader :name, :type, :hash
5
+ def initialize(name, type=nil)
6
+ @name = name
7
+ @type = type || Type::OBJECT
8
+ @hash = name.hash
9
+ end
10
+
11
+ def to_s
12
+ "#{@name} - #{@type}"
13
+ end
14
+
15
+ def inspect
16
+ "(var #{to_s})"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ require 'set'
2
+ require 'longjing/state'
3
+
4
+ module Longjing
5
+ class Problem
6
+ attr_reader :initial, :goal, :all_actions
7
+
8
+ def initialize(actions, init, goal)
9
+ @all_actions = actions
10
+ @initial = State.new(init.to_set)
11
+ @goal = goal
12
+ end
13
+
14
+ def goal?(state)
15
+ @goal.applicable?(state.raw)
16
+ end
17
+
18
+ def actions(state)
19
+ @all_actions.select do |action|
20
+ action.precond.applicable?(state.raw)
21
+ end
22
+ end
23
+
24
+ def result(action, state)
25
+ raw = action.effect.apply(state.raw)
26
+ State.new(raw, state.path + [action])
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ require 'longjing/search/base'
2
+ require "longjing/search/greedy"
3
+ require "longjing/search/ff"
4
+ require "longjing/search/ff_greedy"
@@ -0,0 +1,59 @@
1
+ require 'longjing/logging'
2
+ require 'longjing/search/statistics'
3
+
4
+ module Longjing
5
+ module Search
6
+ class Base
7
+ include Logging
8
+
9
+ attr_reader :statistics
10
+
11
+ def initialize
12
+ @t = Time.now
13
+ @statistics = Statistics.new
14
+ reset_best_heuristic
15
+ end
16
+
17
+ def reset_best_heuristic
18
+ @best = Float::INFINITY
19
+ end
20
+
21
+ def log_progress(state)
22
+ return if @best <= state.cost
23
+ @best = state.cost
24
+ log {
25
+ msg = [
26
+ "#{statistics.generated} generated",
27
+ "#{statistics.evaluated} evaluated",
28
+ "#{statistics.expanded} expanded",
29
+ "h=#{@best}",
30
+ "#{state.path.size} steps",
31
+ "t=#{Time.now - @t}"
32
+ ].join(', ')
33
+ }
34
+ end
35
+
36
+ def log_solution(steps)
37
+ log { "Solution found!" }
38
+ log { "Actual search time: #{Time.now - @t}" }
39
+ log { "Plan length: #{steps.size} step(s)." }
40
+ log { "Expanded #{statistics.expanded} state(s)." }
41
+ log { "Evaluated #{statistics.evaluated} state(s)." }
42
+ log { "Generated #{statistics.generated} state(s)." }
43
+ log { "Solution:" }
44
+ steps.each_with_index do |step, i|
45
+ log { "#{i}. #{step}" }
46
+ end
47
+ end
48
+
49
+ def solution(state)
50
+ state.path.map(&:signature).tap(&method(:log_solution))
51
+ end
52
+
53
+ def no_solution
54
+ log { "No solution found!" }
55
+ nil
56
+ end
57
+ end
58
+ end
59
+ end