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 = []