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,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