LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
rl_td_agent_general.m
1classdef rl_td_agent_general < handle % class for TD learning and TD control
2
3 properties
4 v; % value function
5 vSize; % size of value function
6 epsilon = 1; % explore-exploit rate
7 eps_decay = 0.9999; % explore-exploit rate decay
8 lr = 0.1; % learning rate
9 end
10
11 methods
12 function obj = rl_td_agent_general(lr, eps, epsDecay)
13 obj.lr = lr;
14 obj.epsilon = eps;
15 obj.eps_decay = epsDecay;
16 obj.v = 0;
17 obj.vSize = 0;
18 end
19
20 function reset(obj, env)
21 obj.v = 0;
22 obj.vSize = 0;
23 env.reset();
24 end
25
26 function v = getValueFunction(obj)
27 v = obj.v;
28 end
29
30
31
32 % TD learning for value function with heuristic routing strategy
33 function v = solve_for_fixed_policy(obj, env, num_episodes) % num_epsiodes = 10^4 ususally
34
35 obj.reset(env);
36
37 obj.v = zeros((zeros(1, env.nqueues)+env.stateSize + 1)); % value function
38 obj.vSize = size(obj.v);
39
40 t = 0; % time of current event
41 c = 0; % incurred costs between the visits
42 T = 0; % total discounted elapsed time
43 C = 0; % total discounted costs
44 x = zeros(1, env.nqueues); % initial state
45 n = zeros(1, env.nqueues); % initial previous state
46
47
48 j = 0;
49 while j < num_episodes
50 if mod(j, 1e3)==0
51 line_printf('running episode #%d \n',j);
52 end
53
54 [dt, depNode, arvNode, sample] = env.sample();
55 t = dt + t;
56 c = c + sum(x) * dt;
57
58 if ismember(depNode, env.idxOfQueueInNodes) % Event involves departure from server i
59 depServer = find(env.idxOfQueueInNodes == depNode);
60 x(depServer) = x(depServer) - 1;
61 end
62
63 if ismember(arvNode, env.idxOfQueueInNodes) % Event involves Arrival at server j
64 arvServer = find(env.idxOfQueueInNodes == arvNode);
65 x(arvServer) = x(arvServer) + 1;
66 end
67
68 env.update(sample);
69
70 if env.isInStateSpace(x)
71 j = j + 1;
72 T = env.gamma * T + t;
73 C = env.gamma * C + c;
74 mean_cost_rate = C/T;
75
76 prev_state = num2cell(n+1); % obj.get_state_from_loc(obj.vSize, n+1);
77 cur_state = num2cell(x+1); % obj.get_state_from_loc(obj.vSize, x+1);
78 obj.v(prev_state{:}) = (1-obj.lr)*obj.v(prev_state{:}) + obj.lr*(c - t*mean_cost_rate + obj.v(cur_state{:})); %(1-obj.lr)*obj.v(prev_state) + obj.lr*(c - t*mean_cost_rate + obj.v(cur_state));
79 obj.v = obj.v - obj.v(1);
80
81 t = 0;
82 c = 0;
83 n = x;
84 end
85
86 end
87
88 v = obj.v;
89 end
90
91
92
93 % TD Control with Tabular value function
94 function value_function = solve(obj, env, num_episodes) % num_epsiodes = 10^4 ususally
95 obj.reset(env);
96
97 obj.v = zeros((zeros(1, env.nqueues)+env.stateSize + 1)); % value function
98 obj.vSize = size(obj.v);
99
100 t = 0; % time of current event
101 c = 0; % incurred costs between the visits
102 T = 0; % total discounted elapsed time
103 C = 0; % total discounted costs
104 x = zeros(1, env.nqueues); % initial state
105 n = zeros(1, env.nqueues); % initial previous state
106
107 eps = obj.epsilon;
108
109 j = 0;
110 while j < num_episodes
111 if mod(j, 1e3)==0
112 line_printf('running episode #%d .\n',j);
113 end
114
115
116 eps = eps * obj.eps_decay;
117
118 [dt, depNode, arvNode, sample] = env.sample();
119 t = dt + t;
120 c = c + sum(x) * dt;
121
122 if ismember(depNode, env.idxOfQueueInNodes) % Event involves departure from server i
123 depServer = find(env.idxOfQueueInNodes == depNode);
124 x(depServer) = max(0, x(depServer) - 1);
125 end
126
127 if ismember(depNode, env.idxOfActionNodes) && env.isInActionSpace(x) % actions wanted at server i, and in action space
128
129 actions = env.actionSpace{depNode}; % dep at node i, possible actions are [k,l,m]
130
131
132 % create an exploit-explore policy
133 next_values = obj.gen_next_values(env, x, actions);
134 policy = obj.createGreedyPolicy(next_values, eps, length(actions));
135
136 arvNode = actions(sum(rand >= cumsum([0, policy])));
137
138 % update sample with new arvNode
139 for i = 1:length(sample.event)
140 if sample.event{i}.event == EventType.ARV
141 sample.event{i}.node = arvNode;
142 break;
143 end
144 end
145
146 end
147
148 % update current state and model
149 x(env.idxOfQueueInNodes == arvNode) = x(env.idxOfQueueInNodes == arvNode) + 1;
150 env.update(sample);
151
152
153 if env.isInStateSpace(x) % in State Space, update state value
154 j = j + 1;
155 T = env.gamma * T + t;
156 C = env.gamma * C + c;
157 mean_cost_rate = C/T;
158
159 prev_state = num2cell(n+1);
160 cur_state = num2cell(x+1);
161 obj.v(prev_state{:}) = (1-obj.lr)*obj.v(prev_state{:}) + obj.lr*(c - t*mean_cost_rate + obj.v(cur_state{:})); % here "obj.v(cur_state) * env.gamma" ?
162 obj.v = obj.v - obj.v(1);
163
164 t = 0;
165 c = 0;
166 n = x;
167 end
168
169 end
170 value_function = obj.v;
171 end
172
173
174
175
176 % TD Control with HashMap value fn
177 function [X, Y]=solve_by_hashmap(obj, env, num_episodes)
178 obj.reset(env);
179
180 % Value function stored as parallel arrays keyed by state string:
181 % pvKeys{i} is the state string, pvVals(i) its estimated value.
182 pvKeys = {}; % state-string keys
183 pvVals = []; % corresponding values
184 [pvKeys, pvVals] = pv_set(pvKeys, pvVals, num2str(zeros(1, env.nqueues)), 0);
185 [pvKeys, pvVals] = pv_set(pvKeys, pvVals, 'external', 0);
186
187 t = 0; % time of current event
188 c = 0; % incurred costs between the visits
189 T = 0; % total discounted elapsed time
190 C = 0; % total discounted costs
191 x = zeros(1, env.nqueues); % initial state
192 n = zeros(1, env.nqueues); % initial previous state
193
194 eps = obj.epsilon;
195
196 j = 0;
197 while j < num_episodes
198 if mod(j, 1e3)==0
199 line_printf('running episode #%d .\n',j);
200 end
201
202 % if mod(j, 100) == 0
203 eps = eps * obj.eps_decay;
204 % end
205
206 [dt, depNode, arvNode, sample] = env.sample();
207 t = dt + t;
208 c = c + sum(x) * dt;
209
210 if ismember(depNode, env.idxOfQueueInNodes) % Event involves departure from server i
211 depServer = find(env.idxOfQueueInNodes == depNode);
212 x(depServer) = max(0, x(depServer) - 1);
213 end
214
215
216 if ismember(depNode, env.idxOfActionNodes) && env.isInActionSpace(x) % actions wanted at server i, and in action space
217
218 actions = env.actionSpace{depNode}; % dep at node i, possible actions are [k,l,m]
219
220 % create an exploit-explore policy
221 nextPointValues = zeros(1, length(actions));
222 for act_i = 1 : length(actions)
223 q_idx = find(env.idxOfQueueInNodes == actions(act_i));
224 tmp_next_state = x;
225 tmp_next_state(q_idx) = tmp_next_state(q_idx) + 1;
226 if pv_iskey(pvKeys, num2str(tmp_next_state))
227 nextPointValues(act_i) = pv_get(pvKeys, pvVals, num2str(tmp_next_state));
228 else
229 nextPointValues(act_i) = pv_get(pvKeys, pvVals, 'external');
230 end
231 end
232 policy = obj.createGreedyPolicy(nextPointValues, eps, length(actions));
233
234 arvNode = actions(sum(rand >= cumsum([0, policy])));
235
236 % update sample
237 for i = 1:length(sample.event)
238 if sample.event{i}.event == EventType.ARV
239 sample.event{i}.node = arvNode;
240 break;
241 end
242 end
243
244 end
245
246 % update current state and model
247 x(env.idxOfQueueInNodes == arvNode) = x(env.idxOfQueueInNodes == arvNode) + 1;
248 env.update(sample);
249
250 if env.isInStateSpace(x) % in State Space, update state value
251 j = j + 1;
252 T = env.gamma * T + t;
253 C = env.gamma * C + c;
254 mean_cost_rate = C/T;
255
256 if ~pv_iskey(pvKeys, num2str(n))
257 [pvKeys, pvVals] = pv_set(pvKeys, pvVals, num2str(n), pv_get(pvKeys, pvVals, 'external'));
258 end
259
260 if pv_iskey(pvKeys, num2str(x))
261 [pvKeys, pvVals] = pv_set(pvKeys, pvVals, num2str(n), (1-obj.lr)*pv_get(pvKeys, pvVals, num2str(n)) + obj.lr*(c-t*mean_cost_rate + pv_get(pvKeys, pvVals, num2str(x))));
262 else
263 [pvKeys, pvVals] = pv_set(pvKeys, pvVals, num2str(n), (1-obj.lr)*pv_get(pvKeys, pvVals, num2str(n)) + obj.lr*(c-t*mean_cost_rate + pv_get(pvKeys, pvVals, 'external')));
264 end
265
266 if sum(n)==0
267 substractor = pv_get(pvKeys, pvVals, num2str(n));
268 pvVals = pvVals - substractor;
269 end
270
271 t = 0;
272 c = 0;
273 n = x;
274 end
275
276 end
277
278
279 [pvKeys, pvVals] = pv_remove(pvKeys, pvVals, 'external');
280 X = zeros(length(pvKeys), 1 + env.nqueues);
281 Y = zeros(length(pvKeys), 1);
282 for iterator = 1:length(pvKeys)
283 X(iterator, :) = [1 str2num(pvKeys{iterator})]; %#ok<ST2NM>
284 Y(iterator, :) = pvVals(iterator);
285 end
286
287 end
288
289
290
291 % TD control using linear value fn approximator:
292 % v(q1,q2,...,qn) = w1*q1 + w2*q2 + ... + wn*qn (linear fn)
293 function [X, Y, coeff]=solve_by_linear(obj, env, num_episodes)
294 [X, Y] = obj.solve_by_hashmap(env, num_episodes);
295
296 coeff = regress(Y, X);
297 end
298
299
300 % TD control using quadratic value fn approximator:
301 % v(q1,q2,...,qn) = sum_{i,j} w_{ij} * q_i * q_j (quadratic fn)
302 function [X, Y, coeff]=solve_by_quad(obj, env, num_episodes)
303 [X, Y] = obj.solve_by_hashmap(env, num_episodes);
304
305 sizeX = size(X);
306 for i = 2:sizeX(2)
307 for j = i:sizeX(2)
308 X(:,end+1) = X(:,i).* X(:,j);
309 end
310 end
311
312 coeff = regress(Y, X);
313 end
314
315
316 function values=gen_next_values(obj, env, cur_state, actions) % cur_state = x
317 values = zeros(1, length(actions));
318 for act_i = 1 : length(actions)
319 q_idx = find(env.idxOfQueueInNodes == actions(act_i));
320 tmp_loc = cur_state + 1;
321 tmp_loc(q_idx) = tmp_loc(q_idx) + 1;
322 tmp_idx = num2cell(tmp_loc);
323 values(act_i) = obj.v(tmp_idx{:});
324 end
325 end
326
327 end
328
329
330 methods(Static)
331 function policy = createGreedyPolicy(state_Q, epsilon, nA)
332 policy = ones(1, nA) * epsilon / nA;
333 argmin = find(state_Q == min(state_Q));
334 policy(argmin) = policy(argmin) + (1-epsilon)/length(argmin);
335 end
336
337 end
338end
339
340% Local helpers implementing a string-keyed value map as parallel arrays
341% (keys is a cell of char keys, vals a numeric vector of the same length).
342function tf = pv_iskey(keys, k)
343tf = any(strcmp(keys, k));
344end
345
346function v = pv_get(keys, vals, k)
347i = find(strcmp(keys, k), 1);
348v = vals(i);
349end
350
351function [keys, vals] = pv_set(keys, vals, k, v)
352i = find(strcmp(keys, k), 1);
353if isempty(i)
354 keys{end+1} = k;
355 vals(end+1) = v;
356else
357 vals(i) = v;
358end
359end
360
361function [keys, vals] = pv_remove(keys, vals, k)
362i = find(strcmp(keys, k), 1);
363if ~isempty(i)
364 keys(i) = [];
365 vals(i) = [];
366end
367end
368
Definition Station.m:287
Definition Station.m:245