sfplanner 0.0.1 → 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.
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  SFP Planner for Ruby
2
2
  ====================
3
3
  - Author: Herry (herry13@gmail.com)
4
- - Version: 0.0.1
4
+ - Version: 0.1.0
5
5
  - License: [BSD License](https://github.com/herry13/sfp-ruby/blob/master/LICENSE)
6
6
 
7
- A Ruby gem that provides a Ruby interface to a planner that generates a plan as the solution of a planning problem specified in [SFP language](https://github.com/herry13/nuri/wiki/SFP-language).
7
+ A Ruby gem that provides a Ruby API to SFP planner that solves a planning task written in [SFP language](https://github.com/herry13/nuri/wiki/SFP-language).
8
8
 
9
9
  Click [here](https://github.com/herry13/nuri/wiki/SFP-language), for more details about SFP language.
10
10
 
@@ -25,64 +25,41 @@ Requirements
25
25
  - antlr3
26
26
  - json
27
27
 
28
+ Tested on:
29
+ - Ubuntu 12.04
30
+ - Debian Squeeze
31
+ - Scientific Linux 6.
32
+ - MacOS X 10.8
28
33
 
29
- Supporting Platforms
30
- --------------------
31
- - Linux (x86)
32
- - MacOS X
33
34
 
34
- Tested on: MacOS X 10.8, Ubuntu 12.04, and Scientific Linux 6.
35
+ To use as a command line
36
+ ------------------------
37
+ - solve a planning task, and then print the output in JSON
35
38
 
36
-
37
- To use as a command line to solve a planning task
38
- -------------------------------------------------
39
- - solve a planning task, and then print a sequential plan (if found) in JSON
40
-
41
- $ sfplanner <sfp-file>
42
-
43
- The planning task must be written in [SFP language](https://github.com/herry13/nuri/wiki/SFP-language).
44
-
45
-
46
- To generate a parallel (partial-order) plan
47
- -------------------------------------------
48
- - use option **--parallel** to generate a partial order plan
49
-
50
- $ sfplanner --parallel <sfp-file>
39
+ $ sfplanner <sfp-task-file>
51
40
 
52
41
 
53
42
  To use as Ruby library
54
43
  ----------------------
55
- - include file *sfplanner* library in your codes:
44
+ - parse an SFP file, and then generate the plan (if found) in Hash:
56
45
 
46
+ # include sfplanner library
57
47
  require 'sfplanner'
58
48
 
59
- - to parse an SFP file: create a Sfp::Parser object, and then pass the content of the file:
60
-
61
- # Determine the home directory of your SFP file.
62
- home_dir = File.expand_path(File.dirname("my_file.sfp"))
63
-
64
- # Create Sfp::Parser object
65
- parser = Sfp::Parser.new({:home_dir => "./"})
66
-
67
- # Parse the file.
68
- parser.parse(File.read("my_file.sfp"))
69
-
70
- # Get the result in Hash data structure
71
- result = parser.root
49
+ # solve and return the plan in Hash
50
+ planner.solve({:file => file_path})
72
51
 
73
- - to solve a planning task: create a Sfp::Planner object, and then pass the file's path:
52
+ - parse an SFP file, and then generate the plan in JSON:
74
53
 
75
- # Create Sfp::Planner object.
76
- planner = Sfp::Planner.new
77
-
78
- # Solve a planning task written in "my_file.sfp", then print
79
- # the result in JSON.
80
- puts planner.solve({:file => "my_file.sfp", :json => true})
54
+ # include sfplanner library
55
+ require 'sfplanner'
81
56
 
57
+ # solve and return the plan in JSON
58
+ planner.solve({:file => file_path, :json => true})
82
59
 
83
60
 
84
- Example of Planning Problem
85
- ---------------------------
61
+ Example of Planning Task
62
+ ------------------------
86
63
  - Create file **types.sfp** to hold required schemas:
87
64
 
88
65
  schema Service {
@@ -159,7 +136,7 @@ Example of Planning Problem
159
136
  - To generate the workflow, we invoke **sfp** command with argument
160
137
  the path of the task file:
161
138
 
162
- $ sfp task.sfp
139
+ $ sfplanner task.sfp
163
140
 
164
141
  Which will generate a workflow in JSON
165
142
 
@@ -207,23 +184,3 @@ Example of Planning Problem
207
184
  This workflow is sequential that has 3 procedures. If you executes
208
185
  the workflow in given order, it will achieves the goal state as well
209
186
  as perserves the global constraints during the execution.
210
-
211
- - To generate and execute the plan using Bash framework, we invoke **sfp**
212
- command with an option *--solve-execute* and with an argument the path of
213
- the task file:
214
-
215
- $ sfp --solve-execute task.sfp
216
-
217
- It will generate and execute the plan by invoking the Bash scripts in
218
- the current directory (or as specified in environment variable SFP_HOME)
219
- in the following sequence:
220
-
221
- ./modules/b/start
222
- ./modules/pc/redirect "$.b"
223
- ./modules/a/stop
224
-
225
- - If you save the plan in a file e.g. **plan.json**, you could execute it
226
- later using option *--execute*
227
-
228
- $ sfp --execute plan.json
229
-
data/bin/sfplanner CHANGED
@@ -4,7 +4,7 @@ libdir = File.expand_path(File.dirname(__FILE__))
4
4
  require "#{libdir}/../lib/sfplanner"
5
5
 
6
6
  opts = Trollop::options do
7
- version "sfplanner 0.0.1 (c) 2013 Herry"
7
+ version "sfplanner 0.0.2 (c) 2013 Herry"
8
8
  banner <<-EOS
9
9
  Solve a planning task specified in SFP language, and print the plan (if found) in JSON format.
10
10
 
@@ -15,6 +15,8 @@ where [options] are:
15
15
  EOS
16
16
 
17
17
  opt :parallel, "Generate a parallel (partial-order) plan, instead of sequential."
18
+ opt :json_input, "Input is in JSON format"
19
+ opt :pretty, "Print the plan in pretty JSON format"
18
20
  end
19
21
 
20
22
  def parse(filepath)
@@ -28,7 +30,11 @@ filepath = ARGV[0].to_s
28
30
 
29
31
  if filepath != ''
30
32
  planner = Sfp::Planner.new
31
- puts planner.solve({:file => ARGV[0], :pretty_json => true, :parallel => opts[:parallel]})
33
+ if opts[:json_input]
34
+ puts 'Option "--json-input" is not supported yet.'
35
+ else
36
+ puts planner.solve({:file => ARGV[0], :pretty_json => opts[:pretty], :parallel => opts[:parallel]})
37
+ end
32
38
  else
33
39
  Trollop::help
34
40
  end
Binary file
Binary file
@@ -4,8 +4,8 @@ module Sfp
4
4
  Debug = false
5
5
 
6
6
  class Config
7
- # The timeout for the solver in seconds (default 600s/5mins)
8
- @@timeout = 600
7
+ # The timeout for the solver in seconds (default 60s/1mins)
8
+ @@timeout = 60
9
9
 
10
10
  def self.timeout; @@timeout; end
11
11
 
@@ -24,8 +24,8 @@ module Sfp
24
24
  attr_reader :parser
25
25
 
26
26
  def initialize(params={})
27
- @debug = Debug
28
27
  @parser = Sfp::Parser.new(params)
28
+ @debug = Debug
29
29
  end
30
30
 
31
31
  # @param :string : SFP task in string
@@ -48,6 +48,10 @@ module Sfp
48
48
  @parser.parse(File.read(params[:file]))
49
49
  end
50
50
 
51
+ @debug = true if params[:debug]
52
+
53
+ save_sfp_task if @debug
54
+
51
55
  if not @parser.conformant
52
56
  return self.solve_classical_task(params)
53
57
  else
@@ -79,6 +83,12 @@ module Sfp
79
83
  end
80
84
 
81
85
  protected
86
+ def save_sfp_task
87
+ sfp_task = Sfp::Helper.deep_clone(@parser.root)
88
+ sfp_task.accept(Sfp::Visitor::ParentEliminator.new)
89
+ File.open('/tmp/planning.json', 'w') { |f| f.write(JSON.pretty_generate(sfp_task)) }
90
+ end
91
+
82
92
  def solve_conformant_task(params={})
83
93
  # TODO
84
94
  # 1) generate all possible initial states
@@ -140,7 +150,7 @@ module Sfp
140
150
  end
141
151
 
142
152
  def solve_classical_task(params={})
143
- @plan, @sas_task = self.solve_sas(@parser)
153
+ @plan, @sas_task = self.solve_sas(@parser, params)
144
154
 
145
155
  return @plan if params[:sas_plan]
146
156
 
@@ -241,11 +251,25 @@ module Sfp
241
251
  end
242
252
  end
243
253
 
244
- def solve_sas(parser)
245
- return nil if parser.nil?
254
+ def plan_preprocessing(plan)
255
+ return plan if plan.nil? or plan[0,2] != '1:'
256
+ plan1 = ''
257
+ plan.each_line { |line|
258
+ _, line = line.split(':', 2)
259
+ plan1 += "#{line.strip}\n"
260
+ }
261
+ plan1.strip
262
+ end
246
263
 
264
+ def solve_sas(parser, p={})
265
+ return nil if parser.nil?
266
+
247
267
  tmp_dir = '/tmp/nuri_' + (rand * 100000).to_i.abs.to_s
248
268
  begin
269
+ parser.compile_step_1
270
+ p[:sas_post_processor].sas_post_processor(parser) if p[:sas_post_processor]
271
+ parser.compile_step_2
272
+
249
273
  while File.exist?(tmp_dir)
250
274
  tmp_dir = '/tmp/nuri_' + (rand * 100000).to_i.abs.to_s
251
275
  end
@@ -253,7 +277,8 @@ module Sfp
253
277
  sas_file = tmp_dir + '/problem.sas'
254
278
  plan_file = tmp_dir + '/out.plan'
255
279
  File.open(sas_file, 'w') do |f|
256
- f.write(parser.to_sas)
280
+ #f.write(parser.to_sas)
281
+ f.write(parser.sas)
257
282
  f.flush
258
283
  end
259
284
 
@@ -265,6 +290,7 @@ module Sfp
265
290
  Kernel.system(command)
266
291
  end
267
292
  plan = (File.exist?(plan_file) ? File.read(plan_file) : nil)
293
+ plan = plan_preprocessing(plan)
268
294
 
269
295
  if plan != nil
270
296
  plan = extract_sas_plan(plan, parser)
@@ -336,6 +362,24 @@ module Sfp
336
362
  lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=2),
337
363
  lazy_wastar([hff2,hlm2],preferred=[hff2,hlm2],w=1)],
338
364
  repeat_last=true,continue_on_fail=true)"'
365
+ when 'autotune' then ' \
366
+ --heuristic "hCea=cea(cost_type=2)" \
367
+ --heuristic "hCg=cg(cost_type=1)" \
368
+ --heuristic "hGoalCount=goalcount(cost_type=2)" \
369
+ --heuristic "hFF=ff(cost_type=0)" \
370
+ --heuristic "hMad=mad()" \
371
+ --search "lazy(alt([single(sum([weight(g(), 2),weight(hFF, 3)])),
372
+ single(sum([weight(g(), 2),weight(hFF, 3)]),pref_only=true),
373
+ single(sum([weight(g(), 2),weight(hCg, 3)])),
374
+ single(sum([weight(g(), 2),weight(hCg, 3)]),pref_only=true),
375
+ single(sum([weight(g(), 2),weight(hCea, 3)])),
376
+ single(sum([weight(g(), 2),weight(hCea, 3)]),pref_only=true),
377
+ single(sum([weight(g(), 2),weight(hGoalCount, 3)])),
378
+ single(sum([weight(g(), 2),weight(hGoalCount, 3)]),pref_only=true),
379
+ single(sum([weight(g(), 2),weight(hMad, 3)])),
380
+ single(sum([weight(g(), 2),weight(hMad, 3)]),pref_only=true)],
381
+ boost=200),
382
+ preferred=[hCea,hGoalCount],reopen_closed=false,cost_type=1)"'
339
383
  else '--search "lazy_greedy(ff(cost_type=0))"'
340
384
  end
341
385
  end
@@ -344,10 +388,10 @@ module Sfp
344
388
  # - within given working directory "dir"
345
389
  # - problem in SAS+ format, available in"sas_file"
346
390
  # - solution will be saved in "plan_file"
347
- def self.getcommand(dir, sas_file, plan_file, heuristic='ff', debug=false)
391
+ def self.getcommand(dir, sas_file, plan_file, heuristic='ff', debug=false, timeout=nil)
348
392
  planner = Sfp::Planner.path
349
393
  params = Sfp::Planner.parameters(heuristic)
350
- timeout = Sfp::Planner::Config.timeout
394
+ timeout = Sfp::Planner::Config.timeout if timeout.nil?
351
395
 
352
396
  os = `uname -s`.downcase.strip
353
397
  command = case os
@@ -357,19 +401,20 @@ module Sfp
357
401
  "#{planner}/preprocess < #{sas_file} 2>/dev/null 1>/dev/null; " +
358
402
  "if [ -f 'output' ]; then " +
359
403
  "timeout #{timeout} nice #{planner}/downward #{params} " +
360
- "--plan-file #{plan_file} < output; fi"
404
+ "--plan-file #{plan_file} < output 1>>search.log 2>>search.log; fi"
361
405
  when 'macos', 'darwin'
362
406
  then "cd #{dir}; " +
363
407
  "ulimit -Sv #{Sfp::Planner::Config.max_memory}; " +
364
- "#{planner}/preprocess < #{sas_file} 1> /dev/null; " +
365
- "#{planner}/downward #{params} " +
366
- "--plan-file #{plan_file} < output 1> /dev/null;"
408
+ "#{planner}/preprocess < #{sas_file} 1>/dev/null 2>/dev/null ; " +
409
+ "if [ -f 'output' ]; then " +
410
+ "nice #{planner}/downward #{params} " +
411
+ "--plan-file #{plan_file} < output 1>>search.log 2>>search.log; fi"
367
412
  else nil
368
413
  end
369
414
 
370
- if not command.nil? and (os == 'linux' or os == 'macos' or os == 'darwin')
371
- command = "#{command} 1> /dev/null 2>/dev/null"
372
- end
415
+ #if not command.nil? and (os == 'linux' or os == 'macos' or os == 'darwin')
416
+ # command = "#{command}" #1> /dev/null 2>/dev/null"
417
+ #end
373
418
 
374
419
  command
375
420
  end
@@ -386,21 +431,41 @@ module Sfp
386
431
  end
387
432
 
388
433
  def solve
389
- # 1) solve with FF
390
- planner1 = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'ff')
391
- Kernel.system(planner1)
392
- # 1b) if not found, try CEA
434
+ use_admissible = false
435
+
436
+ # 1a) solve with autotune (see fd-autotune-2)
437
+ planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'autotune')
438
+ Kernel.system(planner)
439
+
440
+ # 1b) if not found, try mad
393
441
  if not File.exist?(@plan_file)
394
- planner2 = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'cea')
395
- Kernel.system(planner2)
442
+ planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'mad')
443
+ Kernel.system(planner)
396
444
  end
397
- # 1c) if not found, try CG
445
+ # if not File.exist?(@plan_file)
446
+ # planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'ff')
447
+ # Kernel.system(planner)
448
+ # end
449
+ # 1c) if not found, try CEA
398
450
  if not File.exists?(@plan_file)
399
- planner3 = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'cg')
400
- Kernel.system(planner3)
401
- return false if not File.exist?(@plan_file)
451
+ planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'cea')
452
+ Kernel.system(planner)
402
453
  end
403
454
 
455
+ # final try: using an admissible heuristic
456
+ #if not File.exist?(@plan_file)
457
+ # use_admissible = true
458
+ # planner = Sfp::Planner.getcommand(@dir, @sas_file, @plan_file, 'lmcut', false, '20m')
459
+ # Kernel.system(planner)
460
+ #end
461
+
462
+ return false if not File.exist?(@plan_file)
463
+ optimise_plan if not use_admissible
464
+
465
+ true
466
+ end
467
+
468
+ def optimise_plan
404
469
  # 2) remove unselected operators
405
470
  new_sas = @sas_file + '.2'
406
471
  new_plan = @plan_file + '.2'
@@ -410,11 +475,11 @@ module Sfp
410
475
  lmcut = Sfp::Planner.getcommand(@dir, new_sas, new_plan, 'lmcut')
411
476
  Kernel.system(lmcut)
412
477
 
413
- # LMCUT cannot find the sub-optimized plan
414
- File.delete(@plan_file)
415
- File.rename(new_plan, @plan_file) if File.exist?(new_plan)
416
-
417
- true
478
+ # 4) LMCUT cannot find the sub-optimized plan
479
+ if File.exist?(new_plan)
480
+ File.delete(@plan_file)
481
+ File.rename(new_plan, @plan_file)
482
+ end
418
483
  end
419
484
 
420
485
  def filter_operators(sas, plan, new_sas)
data/lib/sfplanner.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # external dependencies
2
2
  require 'rubygems'
3
3
  require 'json'
4
- require 'sfp'
4
+ #require 'sfp'
5
5
 
6
6
  # internal dependencies
7
7
  libdir = File.expand_path(File.dirname(__FILE__))
8
8
 
9
+ require libdir + '/../../sfp-ruby/lib/sfp.rb'
9
10
  require libdir + '/sfplanner/sas'
10
11
  require libdir + '/sfplanner/planner'
data/sfplanner.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'sfplanner'
3
- s.version = '0.0.1'
4
- s.date = '2013-07-03'
3
+ s.version = '0.1.0'
4
+ s.date = '2013-08-05'
5
5
  s.summary = 'SFPlanner'
6
6
  s.description = 'A Ruby gem that provides a Ruby API and a script to the SFP planner. This planner can automatically generate a plan that solves a planning problem written in SFP language.'
7
7
  s.authors = ['Herry']
@@ -16,5 +16,5 @@ Gem::Specification.new do |s|
16
16
  s.homepage = 'https://github.com/herry13/sfplanner'
17
17
  s.rubyforge_project = 'sfplanner'
18
18
 
19
- s.add_dependency 'sfp', '~> 0.3.0'
19
+ s.add_dependency 'sfp', '~> 0.3.6'
20
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sfplanner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-03 00:00:00.000000000 Z
12
+ date: 2013-08-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sfp
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.3.0
21
+ version: 0.3.6
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 0.3.0
29
+ version: 0.3.6
30
30
  description: A Ruby gem that provides a Ruby API and a script to the SFP planner.
31
31
  This planner can automatically generate a plan that solves a planning problem written
32
32
  in SFP language.