longjing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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