finite_mdp 0.2.0 → 0.3.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 +4 -4
- data/README.rdoc +8 -12
- data/lib/finite_mdp/array_model.rb +226 -0
- data/lib/finite_mdp/hash_model.rb +10 -9
- data/lib/finite_mdp/model.rb +19 -18
- data/lib/finite_mdp/solver.rb +96 -83
- data/lib/finite_mdp/table_model.rb +28 -19
- data/lib/finite_mdp/vector_valued.rb +5 -5
- data/lib/finite_mdp/version.rb +2 -1
- data/lib/finite_mdp.rb +3 -2
- data/test/finite_mdp/finite_mdp_test.rb +151 -98
- metadata +33 -4
@@ -1,3 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
if ENV['COVERAGE']
|
3
|
+
require 'simplecov'
|
4
|
+
SimpleCov.start
|
5
|
+
end
|
6
|
+
|
1
7
|
require 'minitest/autorun'
|
2
8
|
require 'finite_mdp'
|
3
9
|
require 'set'
|
@@ -5,14 +11,14 @@ require 'set'
|
|
5
11
|
class TestFiniteMDP < MiniTest::Test
|
6
12
|
include FiniteMDP
|
7
13
|
|
8
|
-
def assert_close
|
14
|
+
def assert_close(expected, actual, tol = 1e-6)
|
9
15
|
assert (expected - actual).abs < tol,
|
10
16
|
"expected #{actual} to be within #{tol} of #{expected}"
|
11
17
|
end
|
12
18
|
|
13
19
|
# check that we get the same model back; model parameters must be set before
|
14
20
|
# calling; see test_recycling_robot
|
15
|
-
def check_recycling_robot_model
|
21
|
+
def check_recycling_robot_model(model, sparse)
|
16
22
|
model.check_transition_probabilities_sum
|
17
23
|
assert_equal Set[], model.terminal_states
|
18
24
|
|
@@ -32,26 +38,26 @@ class TestFiniteMDP < MiniTest::Test
|
|
32
38
|
assert_equal Set[:high, :low], Set[*model.next_states(:low, :search)]
|
33
39
|
assert_equal Set[:high, :low], Set[*model.next_states(:high, :search)]
|
34
40
|
|
35
|
-
assert_equal 1
|
36
|
-
assert_equal
|
37
|
-
assert_equal
|
38
|
-
assert_equal
|
39
|
-
assert_equal
|
40
|
-
assert_equal
|
41
|
+
assert_equal 1 - @beta, model.transition_probability(:low, :search, :high)
|
42
|
+
assert_equal @beta, model.transition_probability(:low, :search, :low)
|
43
|
+
assert_equal 0, model.transition_probability(:low, :wait, :high)
|
44
|
+
assert_equal 1, model.transition_probability(:low, :wait, :low)
|
45
|
+
assert_equal 1, model.transition_probability(:low, :recharge, :high)
|
46
|
+
assert_equal 0, model.transition_probability(:low, :recharge, :low)
|
41
47
|
|
42
|
-
assert_equal
|
43
|
-
assert_equal 1
|
44
|
-
assert_equal
|
45
|
-
assert_equal
|
48
|
+
assert_equal @alpha, model.transition_probability(:high, :search, :high)
|
49
|
+
assert_equal 1 - @alpha, model.transition_probability(:high, :search, :low)
|
50
|
+
assert_equal 1, model.transition_probability(:high, :wait, :high)
|
51
|
+
assert_equal 0, model.transition_probability(:high, :wait, :low)
|
46
52
|
|
47
53
|
assert_equal @r_rescue, model.reward(:low, :search, :high)
|
48
54
|
assert_equal @r_search, model.reward(:low, :search, :low)
|
49
|
-
assert_equal
|
50
|
-
assert_equal
|
55
|
+
assert_equal @r_wait, model.reward(:low, :wait, :low)
|
56
|
+
assert_equal 0, model.reward(:low, :recharge, :high)
|
51
57
|
|
52
58
|
assert_equal @r_search, model.reward(:high, :search, :high)
|
53
59
|
assert_equal @r_search, model.reward(:high, :search, :low)
|
54
|
-
assert_equal
|
60
|
+
assert_equal @r_wait, model.reward(:high, :wait, :high)
|
55
61
|
|
56
62
|
if sparse
|
57
63
|
assert_equal nil, model.reward(:low, :wait, :high)
|
@@ -59,7 +65,7 @@ class TestFiniteMDP < MiniTest::Test
|
|
59
65
|
assert_equal nil, model.reward(:high, :wait, :low)
|
60
66
|
else
|
61
67
|
assert_equal @r_wait, model.reward(:low, :wait, :high)
|
62
|
-
assert_equal
|
68
|
+
assert_equal 0, model.reward(:low, :recharge, :low)
|
63
69
|
assert_equal @r_wait, model.reward(:high, :wait, :low)
|
64
70
|
end
|
65
71
|
end
|
@@ -75,16 +81,17 @@ class TestFiniteMDP < MiniTest::Test
|
|
75
81
|
@r_rescue = -3
|
76
82
|
|
77
83
|
table_model = TableModel.new [
|
78
|
-
[:high, :search, :high, @alpha,
|
79
|
-
[:high, :search, :low, 1
|
80
|
-
[:low, :search, :high, 1
|
81
|
-
[:low, :search, :low, @beta,
|
84
|
+
[:high, :search, :high, @alpha, @r_search],
|
85
|
+
[:high, :search, :low, 1 - @alpha, @r_search],
|
86
|
+
[:low, :search, :high, 1 - @beta, @r_rescue],
|
87
|
+
[:low, :search, :low, @beta, @r_search],
|
82
88
|
[:high, :wait, :high, 1, @r_wait],
|
83
89
|
[:high, :wait, :low, 0, @r_wait],
|
84
90
|
[:low, :wait, :high, 0, @r_wait],
|
85
91
|
[:low, :wait, :low, 1, @r_wait],
|
86
92
|
[:low, :recharge, :high, 1, 0],
|
87
|
-
[:low, :recharge, :low, 0, 0]
|
93
|
+
[:low, :recharge, :low, 0, 0]
|
94
|
+
]
|
88
95
|
|
89
96
|
assert_equal 10, table_model.rows.size
|
90
97
|
|
@@ -96,6 +103,10 @@ class TestFiniteMDP < MiniTest::Test
|
|
96
103
|
check_recycling_robot_model hash_model, false
|
97
104
|
check_recycling_robot_model TableModel.from_model(hash_model, false), false
|
98
105
|
|
106
|
+
array_model = ArrayModel.from_model(table_model, false)
|
107
|
+
check_recycling_robot_model array_model, false
|
108
|
+
check_recycling_robot_model TableModel.from_model(array_model, false), false
|
109
|
+
|
99
110
|
# if we sparsify, we should lose some rows
|
100
111
|
sparse_table_model = TableModel.from_model(table_model)
|
101
112
|
assert_equal 7, sparse_table_model.rows.size
|
@@ -103,34 +114,56 @@ class TestFiniteMDP < MiniTest::Test
|
|
103
114
|
|
104
115
|
sparse_hash_model = HashModel.from_model(table_model)
|
105
116
|
check_recycling_robot_model sparse_hash_model, true
|
117
|
+
check_recycling_robot_model TableModel.from_model(sparse_hash_model), true
|
118
|
+
|
119
|
+
sparse_array_model = ArrayModel.from_model(table_model)
|
120
|
+
check_recycling_robot_model sparse_array_model, true
|
121
|
+
check_recycling_robot_model TableModel.from_model(sparse_array_model), true
|
106
122
|
|
107
123
|
# once they're gone, they don't come back
|
108
124
|
sparse_hash_model = HashModel.from_model(sparse_table_model, false)
|
109
125
|
check_recycling_robot_model sparse_hash_model, true
|
110
126
|
|
111
127
|
# try solving with value iteration
|
112
|
-
solver = Solver.new(table_model, 0.95, Hash.new {:wait})
|
113
|
-
|
114
|
-
|
128
|
+
solver = Solver.new(table_model, 0.95, Hash.new { :wait })
|
129
|
+
stable = solver.value_iteration(tolerance: 1e-4, max_iters: 200)
|
130
|
+
assert stable, 'did not converge'
|
131
|
+
assert_equal({ high: :search, low: :recharge }, solver.policy)
|
115
132
|
|
116
133
|
# try solving with policy iteration using iterative policy evaluation
|
117
|
-
solver = Solver.new(table_model, 0.95, Hash.new {:wait})
|
118
|
-
|
119
|
-
|
134
|
+
solver = Solver.new(table_model, 0.95, Hash.new { :wait })
|
135
|
+
stable = solver.policy_iteration(
|
136
|
+
value_tolerance: 1e-4, max_value_iters: 2, max_policy_iters: 50
|
137
|
+
) do |num_policy_iters, num_actions_changed, num_value_iters, value_delta|
|
138
|
+
assert num_policy_iters >= 0
|
139
|
+
assert num_actions_changed.nil? || num_actions_changed >= 0
|
140
|
+
assert num_value_iters > 0
|
141
|
+
assert value_delta >= 0
|
142
|
+
end
|
143
|
+
assert_equal true, stable, 'did not find stable policy'
|
144
|
+
assert_equal({ high: :search, low: :recharge }, solver.policy)
|
120
145
|
|
121
146
|
# try solving with policy iteration using exact policy evaluation
|
122
147
|
gamma = 0.95
|
123
|
-
solver = Solver.new(table_model, gamma, Hash.new {:wait})
|
124
|
-
|
125
|
-
|
148
|
+
solver = Solver.new(table_model, gamma, Hash.new { :wait })
|
149
|
+
stable = solver.policy_iteration_exact(max_iters: 20) do |*args|
|
150
|
+
assert_equal 2, args.size
|
151
|
+
num_iters, num_actions_changed = args
|
152
|
+
assert num_iters > 0
|
153
|
+
assert num_actions_changed >= 0
|
154
|
+
end
|
155
|
+
assert_equal true, stable, 'did not find stable policy'
|
156
|
+
assert_equal({ high: :search, low: :recharge }, solver.policy)
|
126
157
|
|
127
158
|
# check the corresponding state-action values (Q(s,a) values)
|
128
159
|
v = solver.value
|
129
|
-
q_high_search
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
160
|
+
q_high_search =
|
161
|
+
@alpha * (@r_search + gamma * v[:high]) +
|
162
|
+
(1 - @alpha) * (@r_search + gamma * v[:low])
|
163
|
+
q_high_wait = @r_wait + gamma * v[:high]
|
164
|
+
q_low_search =
|
165
|
+
(1 - @beta) * (@r_rescue + gamma * v[:high]) +
|
166
|
+
@beta * (@r_search + gamma * v[:low])
|
134
167
|
q_low_wait = @r_wait + gamma * v[:low]
|
135
168
|
q_low_recharge = 0 + gamma * v[:high]
|
136
169
|
|
@@ -157,8 +190,9 @@ class TestFiniteMDP < MiniTest::Test
|
|
157
190
|
#
|
158
191
|
# @param [Array<[i, j]>] terminii coordinates of the terminal states
|
159
192
|
#
|
160
|
-
def initialize
|
161
|
-
@grid
|
193
|
+
def initialize(grid, terminii)
|
194
|
+
@grid = grid
|
195
|
+
@terminii = terminii
|
162
196
|
end
|
163
197
|
|
164
198
|
attr_reader :grid, :terminii
|
@@ -166,21 +200,23 @@ class TestFiniteMDP < MiniTest::Test
|
|
166
200
|
# every position on the grid is a state, except for obstacles, which are
|
167
201
|
# indicated by a nil in the grid
|
168
202
|
def states
|
169
|
-
is
|
170
|
-
|
203
|
+
is = (0...grid.size).to_a
|
204
|
+
js = (0...grid.first.size).to_a
|
205
|
+
is.product(js).select { |i, j| grid[i][j] } + [:stop]
|
171
206
|
end
|
172
207
|
|
173
208
|
# can move north, east, south or west on the grid
|
174
209
|
MOVES = {
|
175
|
-
'^' => [-1,
|
176
|
-
'>' => [
|
177
|
-
'v' => [
|
178
|
-
'<' => [
|
210
|
+
'^' => [-1, 0],
|
211
|
+
'>' => [0, 1],
|
212
|
+
'v' => [1, 0],
|
213
|
+
'<' => [0, -1]
|
214
|
+
}.freeze
|
179
215
|
|
180
216
|
# agent can move north, south, east or west (unless it's in the :stop
|
181
217
|
# state); if it tries to move off the grid or into an obstacle, it stays
|
182
218
|
# where it is
|
183
|
-
def actions
|
219
|
+
def actions(state)
|
184
220
|
if state == :stop || terminii.member?(state)
|
185
221
|
[:stop]
|
186
222
|
else
|
@@ -189,9 +225,9 @@ class TestFiniteMDP < MiniTest::Test
|
|
189
225
|
end
|
190
226
|
|
191
227
|
# define the transition model
|
192
|
-
def transition_probability
|
228
|
+
def transition_probability(state, action, next_state)
|
193
229
|
if state == :stop || terminii.member?(state)
|
194
|
-
|
230
|
+
action == :stop && next_state == :stop ? 1 : 0
|
195
231
|
else
|
196
232
|
# agent usually succeeds in moving forward, but sometimes it ends up
|
197
233
|
# moving left or right
|
@@ -201,51 +237,62 @@ class TestFiniteMDP < MiniTest::Test
|
|
201
237
|
when 'v' then [['v', 0.8], ['<', 0.1], ['>', 0.1]]
|
202
238
|
when '<' then [['<', 0.8], ['^', 0.1], ['v', 0.1]]
|
203
239
|
end
|
204
|
-
move.map
|
240
|
+
move.map do |m, pr|
|
205
241
|
m_state = [state[0] + MOVES[m][0], state[1] + MOVES[m][1]]
|
206
242
|
m_state = state unless states.member?(m_state) # stay in bounds
|
207
243
|
pr if m_state == next_state
|
208
|
-
|
244
|
+
end.compact.inject(:+) || 0
|
209
245
|
end
|
210
246
|
end
|
211
247
|
|
212
248
|
# reward is given by the grid cells; zero reward for the :stop state
|
213
|
-
def reward
|
249
|
+
def reward(state, _action, _next_state)
|
214
250
|
state == :stop ? 0 : grid[state[0]][state[1]]
|
215
251
|
end
|
216
252
|
|
217
253
|
# helper for functions below
|
218
|
-
def hash_to_grid
|
219
|
-
0.upto(grid.size-1).map
|
254
|
+
def hash_to_grid(hash)
|
255
|
+
0.upto(grid.size - 1).map do |i|
|
256
|
+
0.upto(grid[i].size - 1).map do |j|
|
257
|
+
hash[[i, j]]
|
258
|
+
end
|
259
|
+
end
|
220
260
|
end
|
221
261
|
|
222
262
|
# print the values in a grid
|
223
|
-
def pretty_value
|
224
|
-
hash_to_grid(Hash[value.map {|s, v| [s,
|
225
|
-
row.map{|cell| cell || ' '}.join(' ')}
|
263
|
+
def pretty_value(value)
|
264
|
+
hash_to_grid(Hash[value.map { |s, v| [s, format('%+.3f', v)] }])
|
265
|
+
.map { |row| row.map { |cell| cell || ' ' }.join(' ') }
|
226
266
|
end
|
227
267
|
|
228
268
|
# print the policy using ASCII arrows
|
229
|
-
def pretty_policy
|
230
|
-
hash_to_grid(policy).map
|
231
|
-
|
269
|
+
def pretty_policy(policy)
|
270
|
+
hash_to_grid(policy).map do |row|
|
271
|
+
row.map do |cell|
|
272
|
+
cell.nil? || cell == :stop ? ' ' : cell
|
273
|
+
end.join(' ')
|
274
|
+
end
|
232
275
|
end
|
233
276
|
end
|
234
277
|
|
235
|
-
def check_grid_solutions
|
278
|
+
def check_grid_solutions(model, pretty_policy)
|
236
279
|
# solve with policy iteration (approximate policy evaluation)
|
237
280
|
solver = Solver.new(model, 1)
|
238
|
-
|
281
|
+
stable = solver.policy_iteration(
|
282
|
+
value_tolerance: 1e-5, max_value_iters: 10, max_policy_iters: 50
|
283
|
+
)
|
284
|
+
assert stable, 'did not converge'
|
239
285
|
assert_equal pretty_policy, model.pretty_policy(solver.policy)
|
240
286
|
|
241
287
|
# solve with policy (exact policy evaluation)
|
242
288
|
solver = Solver.new(model, 0.9999) # discount 1 gives singular matrix
|
243
|
-
assert solver.policy_iteration_exact(20),
|
289
|
+
assert solver.policy_iteration_exact(max_iters: 20), 'did not converge'
|
244
290
|
assert_equal pretty_policy, model.pretty_policy(solver.policy)
|
245
291
|
|
246
292
|
# solve with value iteration
|
247
293
|
solver = Solver.new(model, 1)
|
248
|
-
|
294
|
+
stable = solver.value_iteration(tolerance: 1e-5, max_iters: 100)
|
295
|
+
assert stable, 'did not converge'
|
249
296
|
assert_equal pretty_policy, model.pretty_policy(solver.policy)
|
250
297
|
|
251
298
|
solver
|
@@ -255,9 +302,10 @@ class TestFiniteMDP < MiniTest::Test
|
|
255
302
|
# the grid from Figures 17.1, 17.2(a) and 17.3
|
256
303
|
model = AIMAGridModel.new(
|
257
304
|
[[-0.04, -0.04, -0.04, +1],
|
258
|
-
[-0.04,
|
305
|
+
[-0.04, nil, -0.04, -1],
|
259
306
|
[-0.04, -0.04, -0.04, -0.04]],
|
260
|
-
|
307
|
+
[[0, 3], [1, 3]]
|
308
|
+
) # terminals (the +1 and -1 states)
|
261
309
|
model.check_transition_probabilities_sum
|
262
310
|
assert_equal Set[], model.terminal_states
|
263
311
|
|
@@ -266,86 +314,91 @@ class TestFiniteMDP < MiniTest::Test
|
|
266
314
|
[1, 0], [1, 2], [1, 3],
|
267
315
|
[2, 0], [2, 1], [2, 2], [2, 3], :stop], Set[*model.states]
|
268
316
|
|
269
|
-
assert_equal Set[
|
317
|
+
assert_equal Set[%w(^ > v <)], Set[model.actions([0, 0])]
|
270
318
|
assert_equal [:stop], model.actions([1, 3])
|
271
319
|
assert_equal [:stop], model.actions(:stop)
|
272
320
|
|
273
321
|
# check policy against Figure 17.2(a)
|
274
322
|
solver = check_grid_solutions model,
|
275
|
-
[
|
276
|
-
|
277
|
-
|
323
|
+
['> > > ',
|
324
|
+
'^ ^ ',
|
325
|
+
'^ < < <']
|
278
326
|
|
279
327
|
# check the actual (non-pretty) policy
|
280
328
|
assert_equal [
|
281
329
|
['>', '>', '>', :stop],
|
282
330
|
['^', nil, '^', :stop],
|
283
|
-
['^', '<', '<',
|
331
|
+
['^', '<', '<', '<']
|
332
|
+
], model.hash_to_grid(solver.policy)
|
284
333
|
|
285
334
|
# check values against Figure 17.3
|
286
|
-
assert [[0.812, 0.868, 0.918,
|
287
|
-
[0.762,
|
288
|
-
[0.705, 0.655, 0.611, 0.388]].flatten
|
289
|
-
|
290
|
-
|
335
|
+
assert [[0.812, 0.868, 0.918, 1],
|
336
|
+
[0.762, nil, 0.660, -1],
|
337
|
+
[0.705, 0.655, 0.611, 0.388]].flatten
|
338
|
+
.zip(model.hash_to_grid(solver.value).flatten)
|
339
|
+
.all? { |x, y| (x.nil? && y.nil?) || (x - y).abs < 5e-4 }
|
291
340
|
end
|
292
341
|
|
293
342
|
def test_aima_grid_2
|
294
343
|
# a grid from Figure 17.2(b)
|
295
344
|
r = -1.7
|
296
345
|
model = AIMAGridModel.new(
|
297
|
-
[[
|
298
|
-
[
|
299
|
-
[
|
300
|
-
|
346
|
+
[[r, r, r, +1],
|
347
|
+
[r, nil, r, -1],
|
348
|
+
[r, r, r, r]],
|
349
|
+
[[0, 3], [1, 3]]
|
350
|
+
) # terminals (the +1 and -1 states)
|
301
351
|
model.check_transition_probabilities_sum
|
302
352
|
assert_equal Set[], model.terminal_states # no actual terminals
|
303
353
|
|
304
354
|
check_grid_solutions model,
|
305
|
-
[
|
306
|
-
|
307
|
-
|
355
|
+
['> > > ',
|
356
|
+
'^ > ',
|
357
|
+
'> > > ^']
|
308
358
|
end
|
309
359
|
|
310
360
|
def test_aima_grid_3
|
311
361
|
# a grid from Figure 17.2(b)
|
312
362
|
r = -0.3
|
313
363
|
model = AIMAGridModel.new(
|
314
|
-
[[
|
315
|
-
[
|
316
|
-
[
|
317
|
-
|
364
|
+
[[r, r, r, +1],
|
365
|
+
[r, nil, r, -1],
|
366
|
+
[r, r, r, r]],
|
367
|
+
[[0, 3], [1, 3]]
|
368
|
+
) # terminals (the +1 and -1 states)
|
318
369
|
model.check_transition_probabilities_sum
|
319
370
|
assert_equal Set[], model.terminal_states # no actual terminals
|
320
371
|
|
321
372
|
check_grid_solutions model,
|
322
|
-
[
|
323
|
-
|
324
|
-
|
373
|
+
['> > > ',
|
374
|
+
'^ ^ ',
|
375
|
+
'^ > ^ <']
|
325
376
|
end
|
326
377
|
|
327
378
|
def test_aima_grid_4
|
328
379
|
# a grid from Figure 17.2(b)
|
329
380
|
r = -0.01
|
330
381
|
model = AIMAGridModel.new(
|
331
|
-
[[
|
332
|
-
[
|
333
|
-
[
|
334
|
-
|
382
|
+
[[r, r, r, +1],
|
383
|
+
[r, nil, r, -1],
|
384
|
+
[r, r, r, r]],
|
385
|
+
[[0, 3], [1, 3]]
|
386
|
+
) # terminals (the +1 and -1 states)
|
335
387
|
model.check_transition_probabilities_sum
|
336
388
|
assert_equal Set[], model.terminal_states # no actual terminals
|
337
389
|
|
338
390
|
check_grid_solutions model,
|
339
|
-
[
|
340
|
-
|
341
|
-
|
391
|
+
['> > > ',
|
392
|
+
'^ < ',
|
393
|
+
'^ < < v']
|
342
394
|
end
|
343
395
|
|
344
396
|
class MyPoint
|
345
397
|
include FiniteMDP::VectorValued
|
346
398
|
|
347
|
-
def initialize
|
348
|
-
@x
|
399
|
+
def initialize(x, y)
|
400
|
+
@x = x
|
401
|
+
@y = y
|
349
402
|
end
|
350
403
|
|
351
404
|
attr_accessor :x, :y
|
@@ -371,8 +424,8 @@ class TestFiniteMDP < MiniTest::Test
|
|
371
424
|
def test_incomplete_model
|
372
425
|
# model with a transition from a to b but no transitions from b
|
373
426
|
table_model = TableModel.new [
|
374
|
-
[:a, :a_a, :b, 1, 0]
|
427
|
+
[:a, :a_a, :b, 1, 0]
|
428
|
+
]
|
375
429
|
assert_equal Set[:b], table_model.terminal_states
|
376
430
|
end
|
377
431
|
end
|
378
|
-
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: finite_mdp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Lees-Miller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: narray
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ">"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.43.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.43.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.12.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.12.0
|
41
69
|
description: |-
|
42
70
|
This library provides several ways of describing a
|
43
71
|
finite Markov Decision Process (MDP) model (see FiniteMDP::Model) and some
|
@@ -52,6 +80,7 @@ extra_rdoc_files:
|
|
52
80
|
files:
|
53
81
|
- README.rdoc
|
54
82
|
- lib/finite_mdp.rb
|
83
|
+
- lib/finite_mdp/array_model.rb
|
55
84
|
- lib/finite_mdp/hash_model.rb
|
56
85
|
- lib/finite_mdp/model.rb
|
57
86
|
- lib/finite_mdp/solver.rb
|
@@ -67,7 +96,7 @@ rdoc_options:
|
|
67
96
|
- "--main"
|
68
97
|
- README.rdoc
|
69
98
|
- "--title"
|
70
|
-
- finite_mdp-0.
|
99
|
+
- finite_mdp-0.3.0 Documentation
|
71
100
|
require_paths:
|
72
101
|
- lib
|
73
102
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -82,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
111
|
version: '0'
|
83
112
|
requirements: []
|
84
113
|
rubyforge_project: finite_mdp
|
85
|
-
rubygems_version: 2.
|
114
|
+
rubygems_version: 2.5.1
|
86
115
|
signing_key:
|
87
116
|
specification_version: 4
|
88
117
|
summary: Solve small, finite Markov Decision Process models.
|