longjing 0.1.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,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