longjing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ require 'longjing/parameters'
2
+ require 'longjing/problem'
3
+
4
+ module Longjing
5
+ module FF
6
+ class Preprocess
7
+ module NegGoal
8
+ def applicable?(set)
9
+ set.include?(self)
10
+ end
11
+ def apply(set)
12
+ set << self
13
+ end
14
+ end
15
+
16
+ def execute(problem)
17
+ ret = propositionalize(problem)
18
+ reverse_negative_goals(ret.goal)
19
+ ret
20
+ end
21
+
22
+ def propositionalize(problem)
23
+ actions = problem[:actions].map do |action|
24
+ params = Parameters.new(action)
25
+ params.propositionalize(problem[:objects])
26
+ end.flatten
27
+ Problem.new(actions, problem[:init], problem[:goal])
28
+ end
29
+
30
+ def reverse_negative_goals(goal)
31
+ case goal
32
+ when PDDL::And
33
+ goal.literals.each do |lit|
34
+ reverse_negative_goals(lit)
35
+ end
36
+ when PDDL::Not
37
+ goal.extend(NegGoal)
38
+ goal.ff_neg_goal = true
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,140 @@
1
+ require 'longjing/pddl/literal'
2
+
3
+ module Longjing
4
+ module PDDL
5
+ class Literal
6
+ attr_accessor :ff_layer, :ff_goal
7
+ end
8
+ end
9
+
10
+ module FF
11
+ class RelaxedGraphPlan
12
+ attr_reader :actions, :literals
13
+
14
+ def initialize(cg)
15
+ @actions = cg.actions
16
+ @add2actions = cg.add2actions
17
+ @pre2actions = cg.pre2actions
18
+ @literals = cg.literals
19
+ end
20
+
21
+ def layers(goal, state)
22
+ step = 0
23
+ scheduled_facts = state.raw.to_a
24
+ scheduled_actions = []
25
+ @literals.each do |lit|
26
+ lit.ff_layer = nil
27
+ lit.ff_goal = false
28
+ end
29
+ goal.each do |lit|
30
+ lit.ff_goal = true
31
+ end
32
+ @actions.each do |action|
33
+ action.counter = 0
34
+ action.layer = Float::INFINITY
35
+ if action.pre.empty?
36
+ action.difficulty = 0
37
+ scheduled_actions << action
38
+ else
39
+ action.difficulty = Float::INFINITY
40
+ end
41
+ end
42
+ goal_count = goal.size
43
+ loop do
44
+ scheduled_facts.each do |lit|
45
+ next unless lit.ff_layer.nil?
46
+ lit.ff_layer = step
47
+ if lit.ff_goal
48
+ goal_count -= 1
49
+ end
50
+ if actions = @pre2actions[lit]
51
+ actions.each do |action|
52
+ next if action.counter == action.count_target
53
+ action.counter += 1
54
+ if action.counter == action.count_target
55
+ action.difficulty = step
56
+ scheduled_actions << action
57
+ end
58
+ end
59
+ end
60
+ end
61
+ break if goal_count == 0
62
+ scheduled_facts = []
63
+ scheduled_actions.each do |action|
64
+ action.layer = step
65
+ action.add.each do |lit|
66
+ if lit.ff_layer.nil?
67
+ scheduled_facts << lit
68
+ end
69
+ end
70
+ end
71
+ scheduled_actions = []
72
+ break if scheduled_facts.empty?
73
+ step += 1
74
+ end
75
+ end
76
+
77
+ def extract(goal, state, added_goals=[])
78
+ layers(goal, state)
79
+ goal_layers = goal.map(&:ff_layer)
80
+ return nil if goal_layers.any?(&:nil?)
81
+ # m = first layer contains all goals
82
+ m = goal_layers.max
83
+
84
+ marks = Hash.new{|h,k| h[k]={}}
85
+ layer2facts = Array.new(m + 1) { [] }
86
+
87
+ goal.each do |lit|
88
+ layer2facts[lit.ff_layer] << lit
89
+ end
90
+
91
+ plan = []
92
+ (1..m).to_a.reverse.each do |i|
93
+ subplan = []
94
+ layer2facts[i].each do |g|
95
+ next if marks[g].include?(i)
96
+ next unless actions = @add2actions[g]
97
+ action = actions.select do |a|
98
+ a.layer == i - 1
99
+ end.min_by(&:difficulty)
100
+
101
+ action.pre.each do |lit|
102
+ if lit.ff_layer != 0 && !marks[lit].include?(i - 1)
103
+ layer2facts[lit.ff_layer] << lit
104
+ end
105
+ end
106
+ action.add.each do |lit|
107
+ marks[lit][i] = true
108
+ marks[lit][i-1] = true
109
+ end
110
+ unless added_goals.empty?
111
+ action.del.each do |lit|
112
+ if added_goals.include?(lit)
113
+ return nil
114
+ end
115
+ end
116
+ end
117
+
118
+ subplan << action
119
+ end
120
+ unless subplan.empty?
121
+ plan << subplan
122
+ end
123
+ end
124
+ if plan.empty?
125
+ [plan]
126
+ else
127
+ helpful_actions = {}
128
+ layer2facts[1].each do |lit|
129
+ @add2actions[lit].each do |action|
130
+ if action.layer == 0
131
+ helpful_actions[action.action] = true
132
+ end
133
+ end
134
+ end
135
+ [plan, helpful_actions.empty? ? nil : helpful_actions.keys]
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,65 @@
1
+ require 'logger'
2
+
3
+ module Longjing
4
+ module Logging
5
+ def logger
6
+ @@logger ||= Logger.new(STDOUT).tap do |l|
7
+ l.level = Logger::WARN
8
+ l.formatter = proc do |severity, datetime, progname, msg|
9
+ "#{severity[0]} #{datetime}: #{msg}\n"
10
+ end
11
+ end
12
+ end
13
+
14
+ def logger=(l)
15
+ @@logger = l
16
+ end
17
+
18
+ def log(event=nil, *args, &block)
19
+ case event
20
+ when :exploring
21
+ state = args[0]
22
+ logger.debug { "\n\nExploring: #{state}\n=======================" }
23
+ when :action
24
+ action, result = args
25
+ logger.debug { "\nAction: #{action.signature}\n-----------------------" }
26
+ logger.debug { "=> #{result}" }
27
+ when :heuristic
28
+ new_state, solution, dist, best = args
29
+ logger.debug {
30
+ buf = ""
31
+ solution[0].reverse.each_with_index do |a, i|
32
+ buf << " #{i}. [#{a.map(&:signature).join(", ")}]\n"
33
+ end
34
+ "Relaxed plan (cost: #{dist}):\n#{buf}\n helpful actions: #{solution[1] ? solution[1].map(&:signature).join(", ") : '[]'}"
35
+ }
36
+ if dist < best
37
+ logger.debug { "Add to plan #{new_state.path.last.signature}, cost: #{dist}" }
38
+ else
39
+ logger.debug { "Add to frontier" }
40
+ end
41
+ when :facts
42
+ args[0].each_slice(3) do |group|
43
+ logger.info { " #{group.map(&:to_s).join(' ')}"}
44
+ end
45
+ when :problem
46
+ prob = args[0]
47
+ logger.info {
48
+ "Problem: #{prob[:problem]}, domain: #{prob[:domain]}"
49
+ }
50
+ logger.info {
51
+ "Requirements: #{prob[:requirements].join(', ')}"
52
+ }
53
+ log(:problem_stats, prob)
54
+ when :problem_stats
55
+ prob = args[0]
56
+ logger.info { "# types: #{Array(prob[:types]).size}" }
57
+ logger.info { "# predicates: #{prob[:predicates].size}" }
58
+ logger.info { "# object: #{prob[:objects].size}" }
59
+ logger.info { "# actions: #{prob[:actions].size}" }
60
+ when NilClass
61
+ logger.info(&block)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ module Longjing
2
+ class Parameters
3
+ def initialize(action)
4
+ @action = action
5
+ @params = action.params
6
+ end
7
+
8
+ def propositionalize(objects)
9
+ return [@action] if objects.empty?
10
+ permutate(objects).map do |arguments|
11
+ @action.substitute(arguments)
12
+ end.compact
13
+ end
14
+
15
+ # return: [objs, objs...]
16
+ def permutate(objects)
17
+ return [] if @params.empty?
18
+ type_args = @params.map do |param|
19
+ args = objects.select { |obj| obj.is_a?(param.type) }
20
+ return [] if args.empty?
21
+ args
22
+ end
23
+ Longjing.logger.debug {
24
+ "type arguments: #{type_args.map {|v| v.size}.join(', ')}"
25
+ }
26
+
27
+ type_args[0].product(*type_args[1..-1]).reject do |array|
28
+ array.uniq.size < @params.size
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ require 'longjing/pddl/parser.tab.rb'
2
+
3
+ module Longjing
4
+ class Error < StandardError
5
+ end
6
+
7
+ class UnknownDomain < Error
8
+ end
9
+
10
+ class UnsupportedRequirements < Error
11
+ end
12
+
13
+ module PDDL
14
+ module_function
15
+
16
+ def domains
17
+ @domains ||= {}
18
+ end
19
+
20
+ def parse(pddl)
21
+ Parser.new.parse(pddl, self.domains)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ require 'longjing/pddl/literal'
2
+
3
+ module Longjing
4
+ module PDDL
5
+ class Action
6
+ attr_reader :name, :params, :precond, :effect
7
+
8
+ def initialize(name, params, precond, effect)
9
+ @name = name
10
+ @params = params
11
+ @precond = precond
12
+ @effect = effect
13
+ end
14
+
15
+ def substitute(arguments)
16
+ variables = Hash[@params.zip(arguments)]
17
+ return nil unless precond = @precond.substitute(variables)
18
+ return nil unless effect = @effect.substitute(variables)
19
+ Action.new(@name, arguments, precond, effect)
20
+ end
21
+
22
+ def signature
23
+ "#{@name}(#{@params.map(&:name).map(&:to_s).join(' ')})"
24
+ end
25
+
26
+ def to_s
27
+ "(action #{@name} :parameters #{@params.join(' ')} :precondition #{@precond} :effect #{@effect})"
28
+ end
29
+ alias :inspect :to_s
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,220 @@
1
+ module Longjing
2
+ module PDDL
3
+ class Literal
4
+ def applicable?(set)
5
+ raise "Unsupported operation"
6
+ end
7
+
8
+ def apply(set)
9
+ raise "Unsupported operation"
10
+ end
11
+
12
+ def substitute(variables)
13
+ raise "Unsupported operation"
14
+ end
15
+
16
+ def to_a
17
+ [self]
18
+ end
19
+ end
20
+
21
+ class Empty < Literal
22
+ def applicable?(set)
23
+ true
24
+ end
25
+
26
+ def apply(set)
27
+ end
28
+
29
+ def substitute(variables)
30
+ self
31
+ end
32
+
33
+ def to_s
34
+ "()"
35
+ end
36
+
37
+ def inspect
38
+ "<empty>"
39
+ end
40
+ end
41
+ EMPTY = Empty.new
42
+
43
+ class Fact < Literal
44
+ @@insts = {}
45
+ class << self
46
+ def [](*args)
47
+ @@insts[args] ||= new(*args)
48
+ end
49
+ end
50
+
51
+ attr_reader :hash
52
+
53
+ def initialize(pred, objs)
54
+ @pred = pred
55
+ @objs = objs
56
+ @hash = [pred, objs].hash
57
+ end
58
+
59
+ def applicable?(set)
60
+ set.include?(self)
61
+ end
62
+
63
+ def apply(set)
64
+ set << self
65
+ end
66
+
67
+ def substitute(variables)
68
+ self
69
+ end
70
+
71
+ def to_s
72
+ "(#{[@pred.name, *@objs.map(&:name)].join(' ')})"
73
+ end
74
+
75
+ def inspect
76
+ "(fact #{[@pred.name, *@objs].join(' ')})"
77
+ end
78
+ end
79
+
80
+ class Formula < Literal
81
+ attr_reader :pred, :vars, :hash
82
+ def initialize(pred, vars)
83
+ @pred = pred
84
+ @vars = vars
85
+ @hash = [pred, vars].hash
86
+ end
87
+
88
+ def substitute(variables)
89
+ Fact[@pred, @vars.map{|v| variables[v]}]
90
+ end
91
+
92
+ def to_s
93
+ "(#{[@pred.name, *@vars].join(' ')})"
94
+ end
95
+
96
+ def inspect
97
+ "(formula #{to_s})"
98
+ end
99
+ end
100
+
101
+ class Not < Literal
102
+ @@insts = {}
103
+ class << self
104
+ def [](lit)
105
+ @@insts[lit] ||= new(lit)
106
+ end
107
+ end
108
+
109
+ attr_reader :literal, :hash
110
+
111
+ def initialize(literal)
112
+ @literal = literal
113
+ @hash = literal.hash
114
+ end
115
+
116
+ def applicable?(set)
117
+ !set.include?(@literal)
118
+ end
119
+
120
+ def apply(set)
121
+ set.delete(@literal)
122
+ end
123
+
124
+ def substitute(variables)
125
+ ret = @literal.substitute(variables)
126
+ if ret.nil?
127
+ EMPTY
128
+ elsif ret == EMPTY
129
+ nil
130
+ else
131
+ Not[ret]
132
+ end
133
+ end
134
+
135
+ def to_s
136
+ "(not #{@literal})"
137
+ end
138
+
139
+ def inspect
140
+ "(not #{@literal.inspect})"
141
+ end
142
+ end
143
+
144
+ class Equal < Literal
145
+ attr_reader :left, :right
146
+ def initialize(left, right)
147
+ @left, @right = left, right
148
+ end
149
+
150
+ def substitute(variables)
151
+ variables[@left] == variables[@right] ? EMPTY : nil
152
+ end
153
+
154
+ def to_s
155
+ "(= #{@left} #{@right})"
156
+ end
157
+
158
+ def inspect
159
+ "(= #{@left.inspect} #{@right.inspect})"
160
+ end
161
+ end
162
+
163
+ class EqualFormula < Equal
164
+ def substitute(variables)
165
+ @left == @right ? EMPTY : nil
166
+ end
167
+ end
168
+
169
+ class And < Literal
170
+ attr_reader :literals
171
+
172
+ def initialize(literals)
173
+ @literals = literals
174
+ end
175
+
176
+ def applicable?(set)
177
+ @literals.all? do |lit|
178
+ lit.applicable?(set)
179
+ end
180
+ end
181
+
182
+ def apply(set)
183
+ ret = set.dup
184
+ @literals.each do |lit|
185
+ lit.apply(ret)
186
+ end
187
+ ret
188
+ end
189
+
190
+ def substitute(variables)
191
+ ret = []
192
+ @literals.each do |lit|
193
+ n = lit.substitute(variables)
194
+ return nil if n.nil?
195
+ next if n == EMPTY
196
+ ret << n
197
+ end
198
+ if ret.empty?
199
+ nil
200
+ elsif ret.size == 1
201
+ ret[0]
202
+ else
203
+ And.new(ret)
204
+ end
205
+ end
206
+
207
+ def to_a
208
+ @literals
209
+ end
210
+
211
+ def to_s
212
+ "(and #{@literals.map(&:to_s).join(" ")})"
213
+ end
214
+
215
+ def inspect
216
+ "(and #{@literals.map(&:inspect).join(" ")})"
217
+ end
218
+ end
219
+ end
220
+ end