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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.ruby-version +1 -0
- data/.travis.yml +27 -0
- data/Gemfile +4 -0
- data/README.md +35 -0
- data/Rakefile +64 -0
- data/TODO +27 -0
- data/benchmark.log +10 -0
- data/bin/console +14 -0
- data/bin/longjing +37 -0
- data/bin/setup +7 -0
- data/lib/longjing.rb +54 -0
- data/lib/longjing/ff/action.rb +39 -0
- data/lib/longjing/ff/connectivity_graph.rb +35 -0
- data/lib/longjing/ff/ordering.rb +125 -0
- data/lib/longjing/ff/preprocess.rb +43 -0
- data/lib/longjing/ff/relaxed_graph_plan.rb +140 -0
- data/lib/longjing/logging.rb +65 -0
- data/lib/longjing/parameters.rb +32 -0
- data/lib/longjing/pddl.rb +24 -0
- data/lib/longjing/pddl/action.rb +32 -0
- data/lib/longjing/pddl/literal.rb +220 -0
- data/lib/longjing/pddl/obj.rb +26 -0
- data/lib/longjing/pddl/parser.tab.rb +842 -0
- data/lib/longjing/pddl/parser.y +326 -0
- data/lib/longjing/pddl/predicate.rb +20 -0
- data/lib/longjing/pddl/type.rb +24 -0
- data/lib/longjing/pddl/var.rb +20 -0
- data/lib/longjing/problem.rb +29 -0
- data/lib/longjing/search.rb +4 -0
- data/lib/longjing/search/base.rb +59 -0
- data/lib/longjing/search/ff.rb +138 -0
- data/lib/longjing/search/ff_greedy.rb +28 -0
- data/lib/longjing/search/greedy.rb +43 -0
- data/lib/longjing/search/statistics.rb +10 -0
- data/lib/longjing/state.rb +27 -0
- data/lib/longjing/version.rb +3 -0
- data/longjing.gemspec +24 -0
- metadata +123 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'longjing/search/base'
|
3
|
+
require 'longjing/ff/preprocess'
|
4
|
+
require 'longjing/ff/connectivity_graph'
|
5
|
+
require 'longjing/ff/ordering'
|
6
|
+
require 'longjing/ff/relaxed_graph_plan'
|
7
|
+
|
8
|
+
module Longjing
|
9
|
+
module PDDL
|
10
|
+
class Literal
|
11
|
+
attr_accessor :ff_neg_goal
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Search
|
16
|
+
def self.ff
|
17
|
+
FF
|
18
|
+
end
|
19
|
+
|
20
|
+
class FF < Base
|
21
|
+
include Longjing::FF
|
22
|
+
|
23
|
+
def init(problem)
|
24
|
+
log { 'Preprocess' }
|
25
|
+
@problem = Preprocess.new.execute(problem)
|
26
|
+
log { 'Propositionalized problem:' }
|
27
|
+
log { "# actions: #{@problem.all_actions.size}" }
|
28
|
+
log { 'Initialize graphs' }
|
29
|
+
cg = ConnectivityGraph.new(@problem)
|
30
|
+
@h = RelaxedGraphPlan.new(cg)
|
31
|
+
@ordering = Ordering.new(cg)
|
32
|
+
end
|
33
|
+
|
34
|
+
def search(problem)
|
35
|
+
log { 'FF search starts' }
|
36
|
+
init(problem)
|
37
|
+
log { "Build goal agenda" }
|
38
|
+
agenda = @ordering.goal_agenda(@problem)
|
39
|
+
log { "Goal agenda:" }
|
40
|
+
log(:facts, agenda)
|
41
|
+
goal = []
|
42
|
+
state = @problem.initial
|
43
|
+
agenda.each do |g|
|
44
|
+
goal << g
|
45
|
+
unless state = hill_climbing(state, goal)
|
46
|
+
return greedy_search
|
47
|
+
end
|
48
|
+
end
|
49
|
+
solution(state)
|
50
|
+
end
|
51
|
+
|
52
|
+
def hill_climbing(state, goal_list)
|
53
|
+
log { "Hill climbing, goal:" }
|
54
|
+
log(:facts, goal_list)
|
55
|
+
reset_best_heuristic
|
56
|
+
|
57
|
+
best = if relaxed_solution = @h.extract(goal_list, state)
|
58
|
+
distance(relaxed_solution)
|
59
|
+
end
|
60
|
+
logger.debug { "Initial cost: #{best}" }
|
61
|
+
return unless best
|
62
|
+
state.cost = best
|
63
|
+
helpful_actions = relaxed_solution[1]
|
64
|
+
goal_set = goal_list.to_set
|
65
|
+
until best == 0 do
|
66
|
+
state, best, helpful_actions = breadth_first([state],
|
67
|
+
best,
|
68
|
+
helpful_actions,
|
69
|
+
goal_set,
|
70
|
+
goal_list)
|
71
|
+
return unless state
|
72
|
+
logger.debug { "----\nState: #{state}\nCost: #{best}\nPath: #{state.path.map(&:signature).join("\n")}" }
|
73
|
+
end
|
74
|
+
state
|
75
|
+
end
|
76
|
+
|
77
|
+
def breadth_first(frontier, best, helpful_actions,
|
78
|
+
goal_set, goal_list)
|
79
|
+
known = Hash[frontier.map{|f| [f, true]}]
|
80
|
+
until frontier.empty? do
|
81
|
+
state = frontier.shift
|
82
|
+
statistics.expanded += 1
|
83
|
+
|
84
|
+
log(:exploring, state)
|
85
|
+
log_progress(state)
|
86
|
+
|
87
|
+
actions = helpful_actions || @problem.actions(state)
|
88
|
+
helpful_actions = nil
|
89
|
+
actions.each do |action|
|
90
|
+
new_state = @problem.result(action, state)
|
91
|
+
statistics.generated += 1
|
92
|
+
log(:action, action, new_state)
|
93
|
+
if known.include?(new_state)
|
94
|
+
logger.debug { "Known state" }
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
added_goals = Hash[action.effect.to_a.select{|lit| goal_set.include?(lit)}.map{|k| [k, true]}]
|
99
|
+
if solution = @h.extract(goal_list, new_state, added_goals)
|
100
|
+
dist = distance(solution)
|
101
|
+
new_state.cost = dist
|
102
|
+
log(:heuristic, new_state, solution, dist, best)
|
103
|
+
if dist < best
|
104
|
+
return [new_state, dist, solution[1]]
|
105
|
+
else
|
106
|
+
known[new_state] = true
|
107
|
+
frontier << new_state
|
108
|
+
end
|
109
|
+
else
|
110
|
+
# ignore infinite heuristic state
|
111
|
+
logger.debug { "No relaxed solution" }
|
112
|
+
end
|
113
|
+
statistics.evaluated += 1
|
114
|
+
end
|
115
|
+
end
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
|
119
|
+
def greedy_search
|
120
|
+
reset_best_heuristic
|
121
|
+
goal_list = @problem.goal.to_a
|
122
|
+
greedy = Greedy.new
|
123
|
+
heuristic = lambda {|state| distance(@h.extract(goal_list, state))}
|
124
|
+
greedy.search(@problem, heuristic)
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def distance(relaxed)
|
129
|
+
return Float::INFINITY if relaxed.nil?
|
130
|
+
if relaxed[0].empty?
|
131
|
+
0
|
132
|
+
else
|
133
|
+
relaxed[0].map(&:size).reduce(:+)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'longjing/search/base'
|
3
|
+
require 'longjing/ff/preprocess'
|
4
|
+
require 'longjing/ff/connectivity_graph'
|
5
|
+
require 'longjing/ff/ordering'
|
6
|
+
require 'longjing/ff/relaxed_graph_plan'
|
7
|
+
|
8
|
+
module Longjing
|
9
|
+
module PDDL
|
10
|
+
class Literal
|
11
|
+
attr_accessor :ff_neg_goal
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Search
|
16
|
+
def self.ff_greedy
|
17
|
+
FFGreedy
|
18
|
+
end
|
19
|
+
|
20
|
+
class FFGreedy < FF
|
21
|
+
def search(problem)
|
22
|
+
log { 'FF greedy search starts' }
|
23
|
+
init(problem)
|
24
|
+
greedy_search
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'longjing/search/base'
|
3
|
+
|
4
|
+
module Longjing
|
5
|
+
module Search
|
6
|
+
class Greedy < Base
|
7
|
+
def search(problem, heuristic)
|
8
|
+
log { "Greedy search" }
|
9
|
+
initial = problem.initial
|
10
|
+
if problem.goal?(initial)
|
11
|
+
return solution(initial)
|
12
|
+
end
|
13
|
+
initial.cost = heuristic.call(initial)
|
14
|
+
frontier = SortedSet.new([initial])
|
15
|
+
known = {initial => true}
|
16
|
+
until frontier.empty? do
|
17
|
+
state = frontier.first
|
18
|
+
statistics.expanded += 1
|
19
|
+
frontier.delete(state)
|
20
|
+
log(:exploring, state)
|
21
|
+
log_progress(state)
|
22
|
+
problem.actions(state).each do |action|
|
23
|
+
new_state = problem.result(action, state)
|
24
|
+
statistics.generated += 1
|
25
|
+
log(:action, action, new_state)
|
26
|
+
if known.include?(new_state)
|
27
|
+
logger.debug { "Known state" }
|
28
|
+
next
|
29
|
+
end
|
30
|
+
if problem.goal?(new_state)
|
31
|
+
return solution(new_state)
|
32
|
+
end
|
33
|
+
new_state.cost = heuristic.call(new_state)
|
34
|
+
statistics.evaluated += 1
|
35
|
+
known[new_state] = true
|
36
|
+
frontier << new_state
|
37
|
+
end
|
38
|
+
end
|
39
|
+
no_solution
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Longjing
|
2
|
+
class State
|
3
|
+
attr_reader :raw, :path, :hash
|
4
|
+
attr_accessor :cost
|
5
|
+
|
6
|
+
def initialize(raw, path=[])
|
7
|
+
@raw = raw
|
8
|
+
@path = path
|
9
|
+
@hash = @raw.hash
|
10
|
+
@cost = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(state)
|
14
|
+
return true if state.object_id == self.object_id
|
15
|
+
@raw == state.raw
|
16
|
+
end
|
17
|
+
alias :eql? :==
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"{#{@raw.to_a.join(" ")}}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def <=>(s)
|
24
|
+
@cost <=> s.cost
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/longjing.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'longjing/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "longjing"
|
8
|
+
spec.version = Longjing::VERSION
|
9
|
+
spec.authors = ["Xiao Li"]
|
10
|
+
spec.email = ["swing1979@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Longjing is classical planner.}
|
13
|
+
spec.description = %q{Longjing is classical planner for learning classical planning algorithms.}
|
14
|
+
spec.homepage = "https://github.com/xli/longjing"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "ruby-prof"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: longjing
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Xiao Li
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ruby-prof
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Longjing is classical planner for learning classical planning algorithms.
|
56
|
+
email:
|
57
|
+
- swing1979@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".ruby-version"
|
64
|
+
- ".travis.yml"
|
65
|
+
- Gemfile
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- TODO
|
69
|
+
- benchmark.log
|
70
|
+
- bin/console
|
71
|
+
- bin/longjing
|
72
|
+
- bin/setup
|
73
|
+
- lib/longjing.rb
|
74
|
+
- lib/longjing/ff/action.rb
|
75
|
+
- lib/longjing/ff/connectivity_graph.rb
|
76
|
+
- lib/longjing/ff/ordering.rb
|
77
|
+
- lib/longjing/ff/preprocess.rb
|
78
|
+
- lib/longjing/ff/relaxed_graph_plan.rb
|
79
|
+
- lib/longjing/logging.rb
|
80
|
+
- lib/longjing/parameters.rb
|
81
|
+
- lib/longjing/pddl.rb
|
82
|
+
- lib/longjing/pddl/action.rb
|
83
|
+
- lib/longjing/pddl/literal.rb
|
84
|
+
- lib/longjing/pddl/obj.rb
|
85
|
+
- lib/longjing/pddl/parser.tab.rb
|
86
|
+
- lib/longjing/pddl/parser.y
|
87
|
+
- lib/longjing/pddl/predicate.rb
|
88
|
+
- lib/longjing/pddl/type.rb
|
89
|
+
- lib/longjing/pddl/var.rb
|
90
|
+
- lib/longjing/problem.rb
|
91
|
+
- lib/longjing/search.rb
|
92
|
+
- lib/longjing/search/base.rb
|
93
|
+
- lib/longjing/search/ff.rb
|
94
|
+
- lib/longjing/search/ff_greedy.rb
|
95
|
+
- lib/longjing/search/greedy.rb
|
96
|
+
- lib/longjing/search/statistics.rb
|
97
|
+
- lib/longjing/state.rb
|
98
|
+
- lib/longjing/version.rb
|
99
|
+
- longjing.gemspec
|
100
|
+
homepage: https://github.com/xli/longjing
|
101
|
+
licenses: []
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.4.5
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: Longjing is classical planner.
|
123
|
+
test_files: []
|