1classdef rl_td_agent_general < handle %
class for TD learning and TD control
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
12 function obj = rl_td_agent_general(lr, eps, epsDecay)
15 obj.eps_decay = epsDecay;
20 function reset(obj, env)
26 function v = getValueFunction(obj)
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
37 obj.v = zeros((zeros(1, env.nqueues)+env.stateSize + 1)); % value function
38 obj.vSize = size(obj.v);
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
49 while j < num_episodes
51 line_printf(
'running episode #%d \n',j);
54 [dt, depNode, arvNode, sample] = env.sample();
58 if ismember(depNode, env.idxOfQueueInNodes) % Event involves departure from server i
59 depServer = find(env.idxOfQueueInNodes == depNode);
60 x(depServer) = x(depServer) - 1;
63 if ismember(arvNode, env.idxOfQueueInNodes) % Event involves Arrival at server j
64 arvServer = find(env.idxOfQueueInNodes == arvNode);
65 x(arvServer) = x(arvServer) + 1;
70 if env.isInStateSpace(x)
72 T = env.gamma * T + t;
73 C = env.gamma * C + c;
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);
93 % TD Control with Tabular value function
94 function value_function = solve(obj, env, num_episodes) % num_epsiodes = 10^4 ususally
97 obj.v = zeros((zeros(1, env.nqueues)+env.stateSize + 1)); % value function
98 obj.vSize = size(obj.v);
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
110 while j < num_episodes
112 line_printf(
'running episode #%d .\n',j);
116 eps = eps * obj.eps_decay;
118 [dt, depNode, arvNode, sample] = env.sample();
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);
127 if ismember(depNode, env.idxOfActionNodes) && env.isInActionSpace(x) % actions wanted at server i, and in action space
129 actions = env.actionSpace{depNode}; % dep at node i, possible actions are [k,l,m]
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));
136 arvNode = actions(sum(rand >= cumsum([0, policy])));
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;
148 % update current state and model
149 x(env.idxOfQueueInNodes == arvNode) = x(env.idxOfQueueInNodes == arvNode) + 1;
153 if env.isInStateSpace(x) % in State Space, update state value
155 T = env.gamma * T + t;
156 C = env.gamma * C + c;
157 mean_cost_rate = C/T;
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);
170 value_function = obj.v;
176 % TD Control with HashMap value fn
177 function [X, Y]=solve_by_hashmap(obj, env, num_episodes)
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);
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
197 while j < num_episodes
199 line_printf(
'running episode #%d .\n',j);
202 %
if mod(j, 100) == 0
203 eps = eps * obj.eps_decay;
206 [dt, depNode, arvNode, sample] = env.sample();
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);
216 if ismember(depNode, env.idxOfActionNodes) && env.isInActionSpace(x) % actions wanted at server i, and in action space
218 actions = env.actionSpace{depNode}; % dep at node i, possible actions are [k,l,m]
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));
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));
229 nextPointValues(act_i) = pv_get(pvKeys, pvVals,
'external');
232 policy = obj.createGreedyPolicy(nextPointValues, eps, length(actions));
234 arvNode = actions(sum(rand >= cumsum([0, policy])));
237 for i = 1:length(sample.event)
238 if sample.event{i}.event == EventType.ARV
239 sample.event{i}.node = arvNode;
246 % update current state and model
247 x(env.idxOfQueueInNodes == arvNode) = x(env.idxOfQueueInNodes == arvNode) + 1;
250 if env.isInStateSpace(x) % in State Space, update state value
252 T = env.gamma * T + t;
253 C = env.gamma * C + c;
254 mean_cost_rate = C/T;
256 if ~pv_iskey(pvKeys, num2str(n))
257 [pvKeys, pvVals] = pv_set(pvKeys, pvVals, num2str(n), pv_get(pvKeys, pvVals,
'external'));
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))));
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')));
267 substractor = pv_get(pvKeys, pvVals, num2str(n));
268 pvVals = pvVals - substractor;
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);
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);
296 coeff = regress(Y, X);
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);
308 X(:,end+1) = X(:,i).* X(:,j);
312 coeff = regress(Y, X);
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{:});
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);
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));
346function v = pv_get(keys, vals, k)
347i = find(strcmp(keys, k), 1);
351function [keys, vals] = pv_set(keys, vals, k, v)
352i = find(strcmp(keys, k), 1);
361function [keys, vals] = pv_remove(keys, vals, k)
362i = find(strcmp(keys, k), 1);