lib/sfplanner/planner.rb in sfplanner-0.0.1 vs lib/sfplanner/planner.rb in sfplanner-0.1.0
- old
+ new
@@ -2,12 +2,12 @@
class Planner
Heuristic = 'mixed' # lmcut, cg, cea, ff, mixed ([cg|cea|ff]=>lmcut)
Debug = false
class Config
- # The timeout for the solver in seconds (default 600s/5mins)
- @@timeout = 600
+ # The timeout for the solver in seconds (default 60s/1mins)
+ @@timeout = 60
def self.timeout; @@timeout; end
def self.set_timeout(timeout); @@timeout = timeout; end
@@ -22,12 +22,12 @@
attr_accessor :debug
attr_reader :parser
def initialize(params={})
- @debug = Debug
@parser = Sfp::Parser.new(params)
+ @debug = Debug
end
# @param :string : SFP task in string
# @param :sfp : SFP task in Hash data structure
# @param :file : SFP task in file with specified path
@@ -46,10 +46,14 @@
raise Exception, "File not found: #{params[:file]}" if not File.exist?(params[:file])
@parser.home_dir = File.expand_path(File.dirname(params[:file]))
@parser.parse(File.read(params[:file]))
end
+ @debug = true if params[:debug]
+
+ save_sfp_task if @debug
+
if not @parser.conformant
return self.solve_classical_task(params)
else
return self.solve_conformant_task(params)
end
@@ -77,10 +81,16 @@
return (params[:json] ? JSON.generate(state) :
(params[:pretty_json] ? JSON.pretty_generate(state) : state))
end
protected
+ def save_sfp_task
+ sfp_task = Sfp::Helper.deep_clone(@parser.root)
+ sfp_task.accept(Sfp::Visitor::ParentEliminator.new)
+ File.open('/tmp/planning.json', 'w') { |f| f.write(JSON.pretty_generate(sfp_task)) }
+ end
+
def solve_conformant_task(params={})
# TODO
# 1) generate all possible initial states
# remove states that do not satisfy the global constraint
def get_possible_partial_initial_states(init)
@@ -138,11 +148,11 @@
solutions = get_possible_plans(partial_inits)
merged_plan = merge_plans(solutions)
end
def solve_classical_task(params={})
- @plan, @sas_task = self.solve_sas(@parser)
+ @plan, @sas_task = self.solve_sas(@parser, params)
return @plan if params[:sas_plan]
plan = (params[:parallel] ? self.get_parallel_plan : self.get_sequential_plan)
return (params[:json] ? JSON.generate(plan) :
@@ -239,23 +249,38 @@
op_name = sas_operator[1,sas_operator.length-2].split(' ')[0]
actions << Action.new(parser.operators[op_name])
end
end
- def solve_sas(parser)
- return nil if parser.nil?
+ def plan_preprocessing(plan)
+ return plan if plan.nil? or plan[0,2] != '1:'
+ plan1 = ''
+ plan.each_line { |line|
+ _, line = line.split(':', 2)
+ plan1 += "#{line.strip}\n"
+ }
+ plan1.strip
+ end
+ def solve_sas(parser, p={})
+ return nil if parser.nil?
+
tmp_dir = '/tmp/nuri_' + (rand * 100000).to_i.abs.to_s
begin
+ parser.compile_step_1
+ p[:sas_post_processor].sas_post_processor(parser) if p[:sas_post_processor]
+ parser.compile_step_2
+
while File.exist?(tmp_dir)
tmp_dir = '/tmp/nuri_' + (rand * 100000).to_i.abs.to_s
end
Dir.mkdir(tmp_dir)
sas_file = tmp_dir + '/problem.sas'
plan_file = tmp_dir + '/out.plan'
File.open(sas_file, 'w') do |f|
- f.write(parser.to_sas)
+ #f.write(parser.to_sas)
+ f.write(parser.sas)
f.flush
end
if Heuristic == 'mixed'
mixed = MixedHeuristic.new(tmp_dir, sas_file, plan_file)
@@ -263,10 +288,11 @@
else
command = Sfp::Planner.getcommand(tmp_dir, sas_file, plan_file, Heuristic)
Kernel.system(command)
end
plan = (File.exist?(plan_file) ? File.read(plan_file) : nil)
+ plan = plan_preprocessing(plan)
if plan != nil
plan = extract_sas_plan(plan, parser)
sas_task = Nuri::Sas::Task.new(sas_file)
sas_task.sas_plan = plan
@@ -334,44 +360,63 @@
lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=5),
lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=3),
lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=2),
lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=1)],
repeat_last=true,continue_on_fail=true)"'
+ when 'autotune' then ' \
+--heuristic "hCea=cea(cost_type=2)" \
+--heuristic "hCg=cg(cost_type=1)" \
+--heuristic "hGoalCount=goalcount(cost_type=2)" \
+--heuristic "hFF=ff(cost_type=0)" \
+--heuristic "hMad=mad()" \
+--search "lazy(alt([single(sum([weight(g(), 2),weight(hFF, 3)])),
+ single(sum([weight(g(), 2),weight(hFF, 3)]),pref_only=true),
+ single(sum([weight(g(), 2),weight(hCg, 3)])),
+ single(sum([weight(g(), 2),weight(hCg, 3)]),pref_only=true),
+ single(sum([weight(g(), 2),weight(hCea, 3)])),
+ single(sum([weight(g(), 2),weight(hCea, 3)]),pref_only=true),
+ single(sum([weight(g(), 2),weight(hGoalCount, 3)])),
+ single(sum([weight(g(), 2),weight(hGoalCount, 3)]),pref_only=true),
+ single(sum([weight(g(), 2),weight(hMad, 3)])),
+ single(sum([weight(g(), 2),weight(hMad, 3)]),pref_only=true)],
+ boost=200),
+ preferred=[hCea,hGoalCount],reopen_closed=false,cost_type=1)"'
else '--search "lazy_greedy(ff(cost_type=0))"'
end
end
# Return a command to run the planner:
# - within given working directory "dir"
# - problem in SAS+ format, available in"sas_file"
# - solution will be saved in "plan_file"
- def self.getcommand(dir, sas_file, plan_file, heuristic='ff', debug=false)
+ def self.getcommand(dir, sas_file, plan_file, heuristic='ff', debug=false, timeout=nil)
planner = Sfp::Planner.path
params = Sfp::Planner.parameters(heuristic)
- timeout = Sfp::Planner::Config.timeout
+ timeout = Sfp::Planner::Config.timeout if timeout.nil?
os = `uname -s`.downcase.strip
command = case os
when 'linux'
then "cd #{dir}; " +
"ulimit -Sv #{Sfp::Planner::Config.max_memory}; " +
"#{planner}/preprocess < #{sas_file} 2>/dev/null 1>/dev/null; " +
"if [ -f 'output' ]; then " +
"timeout #{timeout} nice #{planner}/downward #{params} " +
- "--plan-file #{plan_file} < output; fi"
+ "--plan-file #{plan_file} < output 1>>search.log 2>>search.log; fi"
when 'macos', 'darwin'
then "cd #{dir}; " +
"ulimit -Sv #{Sfp::Planner::Config.max_memory}; " +
- "#{planner}/preprocess < #{sas_file} 1> /dev/null; " +
- "#{planner}/downward #{params} " +
- "--plan-file #{plan_file} < output 1> /dev/null;"
+ "#{planner}/preprocess < #{sas_file} 1>/dev/null 2>/dev/null ; " +
+ "if [ -f 'output' ]; then " +
+ "nice #{planner}/downward #{params} " +
+ "--plan-file #{plan_file} < output 1>>search.log 2>>search.log; fi"
else nil
end
- if not command.nil? and (os == 'linux' or os == 'macos' or os == 'darwin')
- command = "#{command} 1> /dev/null 2>/dev/null"
- end
+ #if not command.nil? and (os == 'linux' or os == 'macos' or os == 'darwin')
+ # command = "#{command}" #1> /dev/null 2>/dev/null"
+ #end
command
end
# Combination between two heuristic to obtain a suboptimal plan.
@@ -384,38 +429,58 @@
@sas_file = sas_file
@plan_file = plan_file
end
def solve
- # 1) solve with FF
- planner1 = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'ff')
- Kernel.system(planner1)
- # 1b) if not found, try CEA
+ use_admissible = false
+
+ # 1a) solve with autotune (see fd-autotune-2)
+ planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'autotune')
+ Kernel.system(planner)
+
+ # 1b) if not found, try mad
if not File.exist?(@plan_file)
- planner2 = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'cea')
- Kernel.system(planner2)
+ planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'mad')
+ Kernel.system(planner)
end
- # 1c) if not found, try CG
+# if not File.exist?(@plan_file)
+# planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'ff')
+# Kernel.system(planner)
+# end
+ # 1c) if not found, try CEA
if not File.exists?(@plan_file)
- planner3 = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'cg')
- Kernel.system(planner3)
- return false if not File.exist?(@plan_file)
+ planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'cea')
+ Kernel.system(planner)
end
+ # final try: using an admissible heuristic
+ #if not File.exist?(@plan_file)
+ # use_admissible = true
+ # planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'lmcut', false, '20m')
+ # Kernel.system(planner)
+ #end
+
+ return false if not File.exist?(@plan_file)
+ optimise_plan if not use_admissible
+
+ true
+ end
+
+ def optimise_plan
# 2) remove unselected operators
new_sas = @sas_file + '.2'
new_plan = @plan_file + '.2'
self.filter_operators(@sas_file, @plan_file, new_sas)
# 3) generate the final plan with LMCUT
lmcut = Sfp::Planner.getcommand(@dir, new_sas, new_plan, 'lmcut')
Kernel.system(lmcut)
- # LMCUT cannot find the sub-optimized plan
- File.delete(@plan_file)
- File.rename(new_plan, @plan_file) if File.exist?(new_plan)
-
- true
+ # 4) LMCUT cannot find the sub-optimized plan
+ if File.exist?(new_plan)
+ File.delete(@plan_file)
+ File.rename(new_plan, @plan_file)
+ end
end
def filter_operators(sas, plan, new_sas)
# generate the selected actions
selected = []