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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a17ae65cb063c4303bd29aabd3398ea2f2a08265
4
+ data.tar.gz: 50a3fdd3658157a6c5794b94585352b4323505fe
5
+ SHA512:
6
+ metadata.gz: 29296cb079d586c4ba748179c6877a5773a50bb135730bf1f15ba71ad28861db3e7e84ec63597f88b48a481c900fc8df27fc63671b78c47c235916822f0c5f4c
7
+ data.tar.gz: 53d69b19419c40586f270dbeeee8639cde8e9d1f963e9956650bbbb58766d1d637ca35c6a85212c8b218171a97a1b13a3c08925f7fd52b97817f0ab740b0e3a1
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /profile.html
@@ -0,0 +1 @@
1
+ 2.2.1
@@ -0,0 +1,27 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ before_install: gem install bundler -v 1.10.2
5
+ env:
6
+ global:
7
+ - JRUBY_OPTS="$JRUBY_OPTS --debug"
8
+ gemfile:
9
+ - Gemfile
10
+ language: ruby
11
+ rvm:
12
+ - 1.9.3
13
+ - 2.0.0
14
+ - 2.1
15
+ - 2.2.1
16
+ - jruby-19mode
17
+ - jruby-head
18
+ - rbx-2
19
+ - ruby-head
20
+ matrix:
21
+ allow_failures:
22
+ - rvm: jruby-head
23
+ - rvm: rbx-2
24
+ - rvm: ruby-head
25
+ fast_finish: true
26
+ sudo: false
27
+ before_install: gem install bundler -v 1.10.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in longjing.gemspec
4
+ gemspec
@@ -0,0 +1,35 @@
1
+ # Longjing 龙井
2
+
3
+ Longjing is a variety of pan-roasted green tea from the area of Longjing Village near Hangzhou in Zhejiang Province, China.
4
+
5
+ Longjing is classical planner which resolves problems defined by a specific version of PDDL (Planning Domain Definition Language) described in book "Artificial Intelligence: a modern approach".
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'longjing'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install longjing
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/xli/longjing.
@@ -0,0 +1,64 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+ require 'longjing'
4
+
5
+ def pddl_problems
6
+ Dir['test/domains/*.pddl'].each do |f|
7
+ Longjing.pddl(f)
8
+ end
9
+
10
+ Dir['test/problems/*.pddl'].map do |f|
11
+ prob = Longjing.pddl(f)
12
+ {name: prob[:problem], prob: prob}
13
+ end
14
+ end
15
+
16
+ def problems
17
+ pddl_problems
18
+ end
19
+
20
+ Rake::TestTask.new do |t|
21
+ t.libs << "test"
22
+ t.test_files = FileList['test/*test.rb']
23
+ t.verbose = true
24
+ end
25
+
26
+ task :default => [:test, :benchmark]
27
+
28
+ task :profile do
29
+ require 'ruby-prof'
30
+ puts "Profile started"
31
+ result = RubyProf.profile do
32
+ problems.each do |prob|
33
+ Longjing.plan(prob[:prob])
34
+ end
35
+ end
36
+ puts "output: profile.html"
37
+ printer = RubyProf::CallStackPrinter.new(result)
38
+ File.open("profile.html", 'w') do |f|
39
+ printer.print(f, {})
40
+ end
41
+ end
42
+
43
+ task :b => :benchmark
44
+
45
+ task :benchmark do
46
+ require 'benchmark'
47
+ puts "Benchmark started, output: benchmark.log"
48
+ stdout = STDOUT
49
+ $stdout = File.new('benchmark.log', 'w')
50
+ $stdout.sync = true
51
+ label_len = problems.map{|p|p[:name].length}.max + 1
52
+ Benchmark.benchmark(Benchmark::CAPTION, label_len, Benchmark::FORMAT, ">total:", ">avg:") do |x|
53
+ bms = problems.map do |prob|
54
+ x.report(prob[:name].to_s.ljust(20)) do
55
+ 10.times do
56
+ Longjing.plan(prob[:prob])
57
+ end
58
+ end
59
+ end
60
+ [bms.reduce(:+), bms.reduce(:+)/bms.size]
61
+ end
62
+ $stdout = stdout
63
+ puts File.read('benchmark.log')
64
+ end
data/TODO ADDED
@@ -0,0 +1,27 @@
1
+ * planning graph
2
+ ** !support typing
3
+ *** Inheritance
4
+ ** !propositionalize action schema
5
+ ** !planning graph for heuristic estimation
6
+ *** ? A* search
7
+ *** !Enforced hill climbing search
8
+ * FF
9
+ ** !relaxed graph plan as heuristic
10
+ ** !hill climbing search
11
+ ** !handle negative goal
12
+ ** !helper actions
13
+ ** !added goal deletion
14
+ ** !fallback to greedy best-first search when hill climbing search failed
15
+ ** !goals agenda
16
+ * support pddl 3.1 strip pddl input and output
17
+ ** !pddl file parser
18
+ ** analysis support requirements, only do plan with supported problem
19
+ ** test parser with more complex domains and problems
20
+ ** typing support
21
+ * full support of pddl 3.1
22
+ ** support definition of constant objects
23
+
24
+ --------
25
+ * remove actions having conflict effects:
26
+ ** !contain positive & negative of a fact
27
+ ** use predicates to validate?
@@ -0,0 +1,10 @@
1
+ user system total real
2
+ BW-rand-8 0.130000 0.000000 0.130000 ( 0.126941)
3
+ BW-rand-12 0.540000 0.000000 0.540000 ( 0.543002)
4
+ BW-rand-20 14.630000 0.030000 14.660000 ( 14.657970)
5
+ BW-rand-4 0.030000 0.000000 0.030000 ( 0.033519)
6
+ cake-a 0.000000 0.000000 0.000000 ( 0.001633)
7
+ cargo-a 0.010000 0.000000 0.010000 ( 0.008611)
8
+ freecell-f2-c2-s2-i1-02-12 4.220000 0.020000 4.240000 ( 4.236310)
9
+ >total: 19.560000 0.050000 19.610000 ( 19.607987)
10
+ >avg: 2.794286 0.007143 2.801429 ( 2.801141)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "longjing"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path = File.expand_path('../../lib', __FILE__)
4
+ $:.unshift(lib_path)
5
+
6
+ require 'longjing'
7
+
8
+ input = ARGV.dup
9
+
10
+ Longjing.logger.level = if input.include?('-v')
11
+ input.delete('-v')
12
+ Logger::DEBUG
13
+ elsif input.include?('-s')
14
+ input.delete('-s')
15
+ Logger::WARN
16
+ else
17
+ Logger::INFO
18
+ end
19
+
20
+ prof = if input.include?('-p')
21
+ input.delete('-p')
22
+ require 'ruby-prof'
23
+ puts "Profile start"
24
+ RubyProf.start
25
+ end
26
+
27
+ Longjing.pddl_plan(*input)
28
+
29
+ if prof
30
+ result = RubyProf.stop
31
+ puts "output: profile.html"
32
+ # printer = RubyProf::CallStackPrinter.new(result)
33
+ printer = RubyProf::GraphHtmlPrinter.new(result)
34
+ File.open("profile.html", 'w') do |f|
35
+ printer.print(f, {})
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,54 @@
1
+ require "longjing/version"
2
+ require 'longjing/logging'
3
+ require 'longjing/pddl'
4
+ require 'longjing/search'
5
+
6
+ module Longjing
7
+ extend Logging
8
+
9
+ module_function
10
+ def pddl(file)
11
+ raise "File #{file.inspect} does not exist" unless File.exist?(file)
12
+ PDDL.parse(File.read(file))
13
+ end
14
+
15
+ def pddl_problem(domain_file, problem_file)
16
+ pddl(domain_file)
17
+ pddl(problem_file)
18
+ end
19
+
20
+ def pddl_plan(domain_file, problem_file, search=:ff)
21
+ prob = pddl_problem(domain_file, problem_file)
22
+ plan(prob, search).tap do |solution|
23
+ validate!(prob, solution)
24
+ end
25
+ end
26
+
27
+ def plan(problem, search=:ff)
28
+ log(:problem, problem)
29
+ Search.send(search).new.search(problem)
30
+ end
31
+
32
+ def validate!(prob, solution)
33
+ raise "No solution" if solution.nil?
34
+ goal = prob[:goal]
35
+ actions = Hash[prob[:actions].map{|o| [o.name, o]}]
36
+ objects = Hash[prob[:objects].map{|o| [o.name, o]}]
37
+ state = prob[:init].to_set
38
+
39
+ solution.each do |step|
40
+ name, *args = step.gsub(/[()]/, ' ').strip.split(' ').map(&:to_sym)
41
+ action = actions[name]
42
+ args = Array(args).map{|arg| objects[arg]}
43
+ action = action.substitute(args)
44
+ if action.precond.applicable?(state)
45
+ state = action.effect.apply(state)
46
+ else
47
+ raise "Invalid solution, failed at step: #{step.inspect}"
48
+ end
49
+ end
50
+ unless goal.applicable?(state)
51
+ raise "Invalid solution, end with #{state}"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ module Longjing
2
+ module FF
3
+ class Action
4
+ attr_accessor :counter, :difficulty, :layer
5
+ attr_reader :count_target, :action, :hash
6
+ attr_reader :pre, :add, :del
7
+
8
+ def initialize(action)
9
+ @action = action
10
+ @pre = []
11
+ @add = []
12
+ @del = []
13
+ @action.precond.to_a.each do |lit|
14
+ if lit.ff_neg_goal || !lit.is_a?(PDDL::Not)
15
+ @pre << lit
16
+ end
17
+ end
18
+
19
+ @action.effect.to_a.each do |lit|
20
+ if lit.ff_neg_goal || !lit.is_a?(PDDL::Not)
21
+ @add << lit
22
+ else
23
+ @del << lit.literal
24
+ end
25
+ end
26
+ @count_target = @pre.size
27
+ @hash = self.object_id
28
+ end
29
+
30
+ def signature
31
+ @action.signature
32
+ end
33
+
34
+ def to_s
35
+ "Action[#{signature}]"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require 'longjing/ff/action'
2
+
3
+ module Longjing
4
+ module FF
5
+ class ConnectivityGraph
6
+ attr_reader :actions, :add2actions, :pre2actions, :del2actions, :literals
7
+
8
+ def initialize(problem)
9
+ @actions = problem.all_actions.map do |action|
10
+ Action.new(action)
11
+ end
12
+ @add2actions = {}
13
+ @pre2actions = {}
14
+ @del2actions = {}
15
+ @actions.each do |action|
16
+ action.pre.each do |lit|
17
+ @pre2actions[lit] ||= []
18
+ @pre2actions[lit] << action
19
+ end
20
+ action.add.each do |lit|
21
+ @add2actions[lit] ||= []
22
+ @add2actions[lit] << action
23
+ end
24
+ action.del.each do |lit|
25
+ @del2actions[lit] ||= {}
26
+ @del2actions[lit][action] = true
27
+ end
28
+ end
29
+ @literals = (@pre2actions.keys +
30
+ @add2actions.keys +
31
+ @del2actions.keys).uniq
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,125 @@
1
+ module Longjing
2
+ module FF
3
+ class Ordering
4
+ def initialize(connectivity_graph)
5
+ @actions = connectivity_graph.actions
6
+ @add2actions = connectivity_graph.add2actions
7
+ @del2actions = connectivity_graph.del2actions
8
+ end
9
+
10
+ def da(f)
11
+ if actions = @add2actions[f]
12
+ set = Hash[actions[0].del.map{|l|[l, true]}]
13
+ actions[1..-1].inject(set) do |memo, action|
14
+ n = {}
15
+ action.del.each do |f|
16
+ if memo.include?(f)
17
+ n[f] = true
18
+ end
19
+ end
20
+ n
21
+ end
22
+ else
23
+ {}
24
+ end
25
+ end
26
+
27
+ def possibly_achievable_atoms(p, action_set)
28
+ if actions = @add2actions[p]
29
+ actions.any? do |action|
30
+ next unless action_set.include?(action)
31
+ action.pre.all? do |lit|
32
+ if acs = @add2actions[lit]
33
+ acs.any? do |a|
34
+ action_set.include?(a) && a != action
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def heuristic_fixpoint_reduction(a)
43
+ facts = da(a)
44
+ del_a_actions = @del2actions[a] || {}
45
+ actions = {}
46
+ @actions.each do |action|
47
+ next if del_a_actions.include?(action)
48
+ next if action.pre.any?{|l| facts.include?(l)}
49
+ actions[action] = true
50
+ end
51
+
52
+ fixpoint = false
53
+ while(!fixpoint) do
54
+ fixpoint = true
55
+ facts.keys.each do |f|
56
+ if possibly_achievable_atoms(f, actions)
57
+ facts.delete(f)
58
+ actions = {}
59
+ @actions.each do |action|
60
+ next if del_a_actions.include?(action)
61
+ next if action.pre.any?{|l| facts.include?(l)}
62
+ actions[action] = true
63
+ end
64
+ fixpoint = false
65
+ end
66
+ end
67
+ end
68
+ [facts, actions]
69
+ end
70
+
71
+ def heuristic_ordering(a, b)
72
+ facts, actions = heuristic_fixpoint_reduction(a)
73
+ possibly_achievable_atoms(b, actions)
74
+ end
75
+
76
+ def goal_agenda(prob)
77
+ g = Hash.new{|h,k|h[k]={}}
78
+ list = prob.goal.to_a
79
+ list.each do |a|
80
+ list.each do |b|
81
+ next if a == b
82
+ g[a][b] = !heuristic_ordering(b, a)
83
+ end
84
+ end
85
+
86
+ compute_transitive_closure(list, g)
87
+
88
+ # in&out edges
89
+ degree = Hash.new{|h,k| h[k]=0}
90
+ hits = Hash.new{|h,k| h[k]=0}
91
+ list.each do |a|
92
+ list.each do |b|
93
+ next if a == b
94
+ if g[a][b]
95
+ degree[a] -= 1
96
+ degree[b] += 1
97
+ hits[a] += 1
98
+ hits[b] += 1
99
+ end
100
+ end
101
+ end
102
+
103
+ # order by increasing degree
104
+ # disconnected at the end
105
+ list.sort_by do |a|
106
+ hits[a] == 0 ? Float::INFINITY : degree[a]
107
+ end
108
+ end
109
+
110
+ def compute_transitive_closure(nodes, graph)
111
+ nodes.each do |j|
112
+ nodes.each do |i|
113
+ if graph[i][j]
114
+ nodes.each do |k|
115
+ if graph[j][k]
116
+ graph[i][k] = true
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end