fixing shutdown
[oweals/gnunet.git] / src / ats / gnunet-service-ats-solver_ril.c
1 /*
2  This file is part of GNUnet.
3  (C) 2011 Christian Grothoff (and other contributing authors)
4
5  GNUnet is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published
7  by the Free Software Foundation; either version 3, or (at your
8  option) any later version.
9
10  GNUnet is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file ats/gnunet-service-ats-solver_ril.c
23  * @brief ATS reinforcement learning solver
24  * @author Fabian Oehlmann
25  * @author Matthias Wachs
26  */
27 #include "platform.h"
28 #include "float.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet-service-ats_addresses.h"
31 #include "gnunet_statistics_service.h"
32
33 #define RIL_DEFAULT_STEP_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 3000)
34 #define RIL_DEFAULT_ALGORITHM RIL_ALGO_Q
35 #define RIL_DEFAULT_DISCOUNT_FACTOR 0.5
36 #define RIL_DEFAULT_GRADIENT_STEP_SIZE 0.4
37 #define RIL_DEFAULT_TRACE_DECAY 0.6
38 #define RIL_EXPLORE_RATIO 0.1
39
40 /**
41  * ATS reinforcement learning solver
42  *
43  * General description
44  */
45
46 enum RIL_Action_Type
47 {
48   RIL_ACTION_NOTHING = 0,
49   RIL_ACTION_BW_IN_DBL = 1,
50   RIL_ACTION_BW_IN_HLV = 2,
51   RIL_ACTION_BW_IN_INC = 3,
52   RIL_ACTION_BW_IN_DEC = 4,
53   RIL_ACTION_BW_OUT_DBL = 5,
54   RIL_ACTION_BW_OUT_HLV = 6,
55   RIL_ACTION_BW_OUT_INC = 7,
56   RIL_ACTION_BW_OUT_DEC = 8,
57   RIL_ACTION_TYPE_NUM = 9
58 };
59 //TODO! add the rest of the actions
60
61 enum RIL_Algorithm
62 {
63   RIL_ALGO_SARSA = 0, RIL_ALGO_Q = 1
64 };
65
66 enum RIL_E_Modification
67 {
68   RIL_E_SET, RIL_E_ZERO, RIL_E_ACCUMULATE, RIL_E_REPLACE
69 };
70
71 /**
72  * Global learning parameters
73  */
74 struct RIL_Learning_Parameters
75 {
76   /**
77    * The TD-algorithm to use
78    */
79   enum RIL_Algorithm algorithm;
80
81   /**
82    * Learning discount factor in the TD-update
83    */
84   float gamma;
85
86   /**
87    * Gradient-descent step-size
88    */
89   float alpha;
90
91   /**
92    * Trace-decay factor for eligibility traces
93    */
94   float lambda;
95 };
96
97 struct RIL_Peer_Agent
98 {
99   /**
100    * Next agent in solver's linked list
101    */
102   struct RIL_Peer_Agent *next;
103
104   /**
105    * Previous agent in solver's linked list
106    */
107   struct RIL_Peer_Agent *prev;
108
109   /**
110    * Environment handle
111    */
112   struct GAS_RIL_Handle *envi;
113
114   /**
115    * Peer ID
116    */
117   struct GNUNET_PeerIdentity peer;
118
119   /**
120    * Whether the agent is active or not
121    */
122   int active;
123
124   /**
125    * Number of performed time-steps
126    */
127   unsigned long long step_count;
128
129   /**
130    * Experience matrix W
131    */
132   double ** W;
133
134   /**
135    * Number of rows of W / Number of state-vector features
136    */
137   int m;
138
139   /**
140    * Number of columns of W / Number of actions
141    */
142   int n;
143
144   /**
145    * Last perceived state feature vector
146    */
147   double * s_old;
148
149   /**
150    * Last chosen action
151    */
152   int a_old;
153
154   /**
155    * Eligibility trace vector
156    */
157   double * e;
158
159   /**
160    * Address in use
161    */
162   struct ATS_Address * address;
163
164   /**
165    * Inbound bandwidth assigned by the agent
166    */
167   unsigned long long bw_in;
168
169   /**
170    * Outbound bandwidth assigned by the agent
171    */
172   unsigned long long bw_out;
173 };
174
175 struct RIL_Network
176 {
177   /**
178    * ATS network type
179    */
180   enum GNUNET_ATS_Network_Type type;
181
182   /**
183    * Total available inbound bandwidth
184    */
185   unsigned long long bw_in_available;
186
187   /**
188    * Total assigned outbound bandwidth
189    */
190   unsigned long long bw_in_assigned;
191
192   /**
193    * Total available outbound bandwidth
194    */
195   unsigned long long bw_out_available;
196
197   /**
198    * Total assigned outbound bandwidth
199    */
200   unsigned long long bw_out_assigned;
201 };
202
203 struct RIL_Callbacks
204 {
205   /**
206    * Bandwidth changed callback
207    */
208   GAS_bandwidth_changed_cb bw_changed;
209
210   /**
211    * Bandwidth changed callback cls
212    */
213   void *bw_changed_cls;
214
215   /**
216    * ATS function to get preferences
217    */
218   GAS_get_preferences get_preferences;
219
220   /**
221    * Closure for ATS function to get preferences
222    */
223   void *get_preferences_cls;
224
225   /**
226    * ATS function to get properties
227    */
228   GAS_get_properties get_properties;
229
230   /**
231    * Closure for ATS function to get properties
232    */
233   void *get_properties_cls;
234 };
235
236 /**
237  * A handle for the reinforcement learning solver
238  */
239 struct GAS_RIL_Handle
240 {
241   /**
242    * Statistics handle
243    */
244   struct GNUNET_STATISTICS_Handle *stats;
245
246   /**
247    * Hashmap containing all valid addresses
248    */
249   const struct GNUNET_CONTAINER_MultiHashMap *addresses;
250
251   /**
252    * Callbacks for the solver
253    */
254   struct RIL_Callbacks *callbacks;
255
256   /**
257    * Bulk lock
258    */
259   int bulk_lock;
260
261   /**
262    * Number of changes while solver was locked
263    */
264   int bulk_requests;
265
266   /**
267    * Number of performed time-steps
268    */
269   unsigned long long step_count;
270
271   /**
272    * Interval time between steps in milliseconds //TODO? put in agent
273    */
274   struct GNUNET_TIME_Relative step_time;
275
276   /**
277    * Task identifier of the next time-step to be executed //TODO? put in agent
278    */
279   GNUNET_SCHEDULER_TaskIdentifier next_step;
280
281   /**
282    * Learning parameters
283    */
284   struct RIL_Learning_Parameters parameters;
285
286   /**
287    * Array of networks with global assignment state
288    */
289   struct RIL_Network * network_entries;
290
291   /**
292    * Networks count
293    */
294   unsigned int networks_count;
295
296   /**
297    * List of active peer-agents
298    */
299   struct RIL_Peer_Agent * agents_head;
300   struct RIL_Peer_Agent * agents_tail;
301 };
302
303 /**
304  *  Private functions
305  *  ---------------------------
306  */
307
308 /**
309  * Estimate the current action-value for state s and action a
310  * @param agent agent performing the estimation
311  * @param state s
312  * @param action a
313  * @return estimation value
314  */
315 static double
316 agent_estimate_q (struct RIL_Peer_Agent *agent, double *state, int action)
317 {
318   int i;
319   double result = 0;
320
321   for (i = 0; i < agent->m; i++)
322   {
323     result += state[i] * agent->W[action][i];
324   }
325
326   return result;
327 }
328
329 /**
330  * Decide whether to do exploration (i.e. taking a new action) or exploitation (i.e. taking the
331  * currently estimated best action) in the current step
332  * @param agent agent performing the step
333  * @return yes, if exploring
334  */
335 static int
336 agent_decide_exploration (struct RIL_Peer_Agent *agent)
337 {
338   double r = (double) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
339       UINT32_MAX) / (double) UINT32_MAX;
340
341   if (r < RIL_EXPLORE_RATIO)
342   {
343     return GNUNET_YES;
344   }
345   return GNUNET_NO;
346 }
347
348 /**
349  * Gets the action, with the maximal estimated Q-value (i.e. the one currently estimated to bring the
350  * most reward in the future)
351  * @param agent agent performing the calculation
352  * @param state the state from which to take the action
353  * @return the action promising most future reward
354  */
355 static int
356 agent_get_action_best (struct RIL_Peer_Agent *agent, double *state)
357 {
358   int i;
359   int max_i = -1;
360   double cur_q;
361   double max_q = -DBL_MAX;
362
363   for (i = 0; i < agent->n; i++)
364   {
365     cur_q = agent_estimate_q (agent, state, i);
366     if (cur_q > max_q)
367     {
368       max_q = cur_q;
369       max_i = i;
370     }
371   }
372
373   GNUNET_assert(-1 != max_i);
374
375   return max_i;
376 }
377
378 /**
379  * Gets any action, to explore the action space from that state
380  * @param agent agent performing the calculation
381  * @param state the state from which to take the action
382  * @return any action
383  */
384 static int
385 agent_get_action_explore (struct RIL_Peer_Agent *agent, double *state)
386 {
387   return GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, agent->n);
388 }
389
390 /**
391  * Updates the weights (i.e. coefficients) of the weight vector in matrix W for action a
392  * @param agent the agent performing the update
393  * @param reward the reward received for the last action
394  * @param s_next the new state, the last step got the agent into
395  * @param a_prime the new
396  */
397 static void
398 agent_update_weights (struct RIL_Peer_Agent *agent,
399     double reward,
400     double *s_next,
401     int a_prime)
402 {
403   int i;
404   double delta;
405   double *theta = agent->W[agent->a_old];
406
407   delta = reward + agent_estimate_q (agent, s_next, a_prime)
408       - agent_estimate_q (agent, agent->s_old, agent->a_old);
409   for (i = 0; i < agent->m; i++)
410   {
411     theta[i] += agent->envi->parameters.alpha * delta * (agent->e)[i];
412   }
413 }
414
415 /**
416  * Changes the eligibility trace vector e in various manners:
417  * RIL_E_ACCUMULATE - adds 1 to each component as in accumulating eligibility traces
418  * RIL_E_REPLACE - resets each component to 1 as in replacing traces
419  * RIL_E_SET - multiplies e with gamma and lambda as in the update rule
420  * RIL_E_ZERO - sets e to 0 as in Watkin's Q-learning algorithm when exploring and when initializing
421  * @param agent
422  * @param mod
423  */
424 static void
425 agent_modify_eligibility (struct RIL_Peer_Agent *agent,
426     enum RIL_E_Modification mod)
427 {
428   int i;
429   double *e = agent->e;
430   double gamma = agent->envi->parameters.gamma;
431   double lambda = agent->envi->parameters.lambda;
432
433   for (i = 0; i < agent->m; i++)
434   {
435     switch (mod)
436     {
437     case RIL_E_ACCUMULATE:
438       e[i] += 1;
439       break;
440     case RIL_E_REPLACE:
441       e[i] = 1;
442       break;
443     case RIL_E_SET:
444       e[i] = gamma * lambda;
445       break;
446     case RIL_E_ZERO:
447       e[i] = 0;
448       break;
449     }
450   }
451 }
452
453 static void
454 envi_change_active_address (struct GAS_RIL_Handle *solver,
455     struct RIL_Peer_Agent *agent,
456     struct ATS_Address *new_address,
457     unsigned long long new_bw_in,
458     unsigned long long new_bw_out)
459 {
460   int notify = GNUNET_NO;
461
462   if (agent->address != new_address)
463   {
464     agent->address->active = GNUNET_NO;
465     agent->address = new_address;
466     agent->address->active = GNUNET_YES;
467     agent->address->assigned_bw_in.value__ = htonl (agent->bw_in);
468     agent->address->assigned_bw_out.value__ = htonl (agent->bw_out);
469     notify |= GNUNET_YES;
470   }
471   if (agent->bw_in != new_bw_in)
472   {
473     agent->bw_in = new_bw_in;
474     agent->address->assigned_bw_in.value__ = htonl (new_bw_out);
475     notify |= GNUNET_YES;
476   }
477   if (agent->bw_out != new_bw_out)
478   {
479     agent->bw_out = new_bw_out;
480     agent->address->assigned_bw_out.value__ = htonl (new_bw_out);
481     notify |= GNUNET_YES;
482   }
483
484   if (notify)
485   {
486     solver->callbacks->bw_changed (solver->callbacks->bw_changed_cls,
487         agent->address);
488   }
489 }
490
491 /**
492  * Allocates a state vector and fills it with the features present
493  * @param solver the solver handle
494  * @return pointer to the state vector
495  */
496 static double *
497 envi_get_state (struct GAS_RIL_Handle *solver)
498 {
499   int i;
500   struct RIL_Network *net;
501   double *state = GNUNET_malloc (sizeof (double) * solver->networks_count * 4);
502
503   for (i = 0; i < solver->networks_count; i++)
504   {
505     net = &solver->network_entries[i];
506     state[i * 4 + 0] = (double) net->bw_in_assigned;
507     state[i * 4 + 1] = (double) net->bw_in_available;
508     state[i * 4 + 2] = (double) net->bw_out_assigned;
509     state[i * 4 + 3] = (double) net->bw_out_available;
510   }
511
512   return state;
513 }
514
515 /**
516  * Gets the reward of the last performed step
517  * @param solver solver handle
518  * @return the reward
519  */
520 static double
521 envi_get_reward (struct GAS_RIL_Handle *solver, struct RIL_Peer_Agent *agent)
522 {
523   //TODO! implement reward calculation
524
525   return (double) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
526       UINT32_MAX) / (double) UINT32_MAX;
527 }
528
529 static void
530 envi_action_bw_double (struct GAS_RIL_Handle *solver,
531     struct RIL_Peer_Agent *agent,
532     int direction_in)
533 {
534   if (direction_in)
535   {
536     envi_change_active_address (solver, agent, agent->address, agent->bw_in * 2,
537         agent->bw_out);
538   }
539   else
540   {
541     envi_change_active_address (solver, agent, agent->address, agent->bw_in,
542         agent->bw_out * 2);
543   }
544 }
545
546 static void
547 envi_action_bw_halven (struct GAS_RIL_Handle *solver,
548     struct RIL_Peer_Agent *agent,
549     int direction_in)
550 {
551   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
552   unsigned long long new_bw;
553
554   if (direction_in)
555   {
556     new_bw = agent->bw_in / 2;
557     if (new_bw < min_bw)
558       new_bw = min_bw;
559     envi_change_active_address (solver, agent, agent->address, new_bw,
560         agent->bw_out);
561   }
562   else
563   {
564     new_bw = agent->bw_out / 2;
565     if (new_bw < min_bw)
566       new_bw = min_bw;
567     envi_change_active_address (solver, agent, agent->address, agent->bw_in,
568         new_bw);
569   }
570 }
571
572 static void
573 envi_action_bw_inc (struct GAS_RIL_Handle *solver,
574     struct RIL_Peer_Agent *agent,
575     int direction_in)
576 {
577   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
578
579   if (direction_in)
580   {
581     envi_change_active_address (solver, agent, agent->address,
582         agent->bw_in + (5 * min_bw), agent->bw_out);
583   }
584   else
585   {
586     envi_change_active_address (solver, agent, agent->address, agent->bw_in,
587         agent->bw_out + (5 * min_bw));
588   }
589 }
590
591 static void
592 envi_action_bw_dec (struct GAS_RIL_Handle *solver,
593     struct RIL_Peer_Agent *agent,
594     int direction_in)
595 {
596   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
597   unsigned long long new_bw;
598
599   if (direction_in)
600   {
601     new_bw = agent->bw_in - (5 * min_bw);
602     if (new_bw < min_bw)
603       new_bw = min_bw;
604     envi_change_active_address (solver, agent, agent->address, new_bw,
605         agent->bw_out);
606   }
607   else
608   {
609     new_bw = agent->bw_out - (5 * min_bw);
610     if (new_bw < min_bw)
611       new_bw = min_bw;
612     envi_change_active_address (solver, agent, agent->address, agent->bw_in,
613         new_bw);
614   }
615 }
616
617 /**
618  * Puts the action into effect
619  * @param solver solver handle
620  * @param action action to perform by the solver
621  */
622 static void
623 envi_do_action (struct GAS_RIL_Handle *solver,
624     struct RIL_Peer_Agent *agent,
625     int action)
626 {
627   switch (action)
628   {
629   case RIL_ACTION_NOTHING:
630     break;
631   case RIL_ACTION_BW_IN_DBL:
632     envi_action_bw_double (solver, agent, GNUNET_YES);
633     break;
634   case RIL_ACTION_BW_IN_HLV:
635     envi_action_bw_halven (solver, agent, GNUNET_YES);
636     break;
637   case RIL_ACTION_BW_IN_INC:
638     envi_action_bw_inc (solver, agent, GNUNET_YES);
639     break;
640   case RIL_ACTION_BW_IN_DEC:
641     envi_action_bw_dec (solver, agent, GNUNET_YES);
642     break;
643   case RIL_ACTION_BW_OUT_DBL:
644     envi_action_bw_double (solver, agent, GNUNET_NO);
645     break;
646   case RIL_ACTION_BW_OUT_HLV:
647     envi_action_bw_halven (solver, agent, GNUNET_NO);
648     break;
649   case RIL_ACTION_BW_OUT_INC:
650     envi_action_bw_inc (solver, agent, GNUNET_NO);
651     break;
652   case RIL_ACTION_BW_OUT_DEC:
653     envi_action_bw_dec (solver, agent, GNUNET_NO);
654     break;
655   default:
656     // error - action does not exist
657     GNUNET_assert(GNUNET_NO);
658   }
659 }
660
661 /**
662  * Performs one step of the Markov Decision Process. Other than in the literature the step starts
663  * after having done the last action a_old. It observes the new state s_next and the reward
664  * received. Then the coefficient update is done according to the SARSA or Q-learning method. The
665  * next action is put into effect.
666  * @param agent the agent performing the step
667  */
668 static void
669 agent_step (struct RIL_Peer_Agent *agent)
670 {
671   int a_next = -1;
672   double *s_next;
673   double reward;
674
675   s_next = envi_get_state (agent->envi);
676   reward = envi_get_reward (agent->envi, agent);
677
678   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "agent_step() with algorithm %s\n",
679       agent->envi->parameters.algorithm ? "Q" : "SARSA");
680
681   switch (agent->envi->parameters.algorithm)
682   {
683   case RIL_ALGO_SARSA:
684     agent_modify_eligibility (agent, RIL_E_SET);
685     if (agent_decide_exploration (agent))
686     {
687       a_next = agent_get_action_explore (agent, s_next);
688     }
689     else
690     {
691       a_next = agent_get_action_best (agent, s_next);
692     }
693     //updates weights with selected action (on-policy), if not first step
694     if (-1 != agent->a_old)
695       agent_update_weights (agent, reward, s_next, a_next);
696     break;
697
698   case RIL_ALGO_Q:
699     //updates weights with best action, disregarding actually selected action (off-policy), if not first step
700     a_next = agent_get_action_best (agent, s_next);
701     if (-1 != agent->a_old)
702       agent_update_weights (agent, reward, s_next, a_next);
703     if (agent_decide_exploration (agent))
704     {
705       a_next = agent_get_action_explore (agent, s_next);
706       agent_modify_eligibility (agent, RIL_E_ZERO);
707     }
708     else
709     {
710       a_next = agent_get_action_best (agent, s_next);
711       agent_modify_eligibility (agent, RIL_E_SET);
712     }
713     break;
714   }
715
716   GNUNET_assert(-1 != a_next);
717
718   agent_modify_eligibility (agent, RIL_E_ACCUMULATE);
719
720   envi_do_action (agent->envi, agent, a_next);
721
722   GNUNET_free(agent->s_old);
723   agent->s_old = s_next;
724   agent->a_old = a_next;
725
726   agent->step_count += 1;
727 }
728
729 /**
730  * Cycles through all agents and lets the active ones do a step. Schedules the next step.
731  * @param solver the solver handle
732  * @param tc task context for the scheduler
733  */
734 static void
735 ril_periodic_step (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
736 {
737   struct GAS_RIL_Handle *solver = cls;
738   struct RIL_Peer_Agent *cur;
739
740   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "RIL step number %d\n",
741       solver->step_count);
742
743   for (cur = solver->agents_head; NULL != cur; cur = cur->next)
744   {
745     if (cur->active)
746     {
747       agent_step (cur);
748     }
749   }
750
751   solver->step_count += 1;
752   solver->next_step = GNUNET_SCHEDULER_add_delayed (solver->step_time,
753       &ril_periodic_step, solver);
754 }
755
756 /**
757  * Initialize an agent without addresses and its knowledge base
758  * @param s ril solver
759  * @param peer the one in question
760  * @return handle to the new agent
761  */
762 static struct RIL_Peer_Agent *
763 agent_init (void *s, const struct GNUNET_PeerIdentity *peer)
764 {
765   int i;
766   struct GAS_RIL_Handle * solver = s;
767   struct RIL_Peer_Agent * agent = GNUNET_malloc (sizeof (struct RIL_Peer_Agent));
768
769   agent->envi = solver;
770   agent->peer = *peer;
771   agent->step_count = 0;
772   agent->active = GNUNET_NO;
773   agent->s_old = envi_get_state (solver);
774   agent->n = RIL_ACTION_TYPE_NUM;
775   agent->m = solver->networks_count * 4;
776   agent->W = (double **) GNUNET_malloc (sizeof (double) * agent->n);
777   for (i = 0; i < agent->n; i++)
778   {
779     agent->W[i] = (double *) GNUNET_malloc (sizeof (double) * agent->m);
780   }
781   agent->a_old = -1;
782   agent->e = (double *) GNUNET_malloc (sizeof (double) * agent->m);
783   agent_modify_eligibility (agent, RIL_E_ZERO);
784
785   GNUNET_CONTAINER_DLL_insert_tail(solver->agents_head, solver->agents_tail,
786       agent);
787
788   return agent;
789 }
790
791 /**
792  * Deallocate agent
793  * @param s solver handle
794  * @param agent the agent to retire
795  */
796 static void
797 agent_die (struct GAS_RIL_Handle *solver, struct RIL_Peer_Agent *agent)
798 {
799   int i;
800
801   for (i = 0; i < agent->n; i++)
802   {
803     GNUNET_free(agent->W[i]);
804   }
805   GNUNET_free(agent->W);
806   GNUNET_free(agent->e);
807   GNUNET_free(agent->s_old);
808 }
809
810 static void
811 ril_remove_agent (struct GAS_RIL_Handle *s, struct RIL_Peer_Agent *agent)
812 {
813   struct RIL_Peer_Agent *cur_agent;
814   struct RIL_Peer_Agent *next_agent;
815
816   cur_agent = s->agents_head;
817   while (NULL != cur_agent)
818   {
819     next_agent = cur_agent->next;
820
821     if (agent == cur_agent)
822     {
823       GNUNET_CONTAINER_DLL_remove(s->agents_head, s->agents_tail, cur_agent);
824       agent_die (s, cur_agent);
825     }
826
827     cur_agent = next_agent;
828   }
829 }
830
831 /**
832  * Counts the (active) agents
833  * @param solver solver handle
834  * @param active_only whether only active agents should be counted
835  * @return number of agents
836  */
837 //static int
838 //ril_count_agents (struct GAS_RIL_Handle *solver, int active_only)
839 //{
840 //  int c;
841 //  struct RIL_Peer_Agent *cur;
842 //
843 //  c = 0;
844 //  for (cur = solver->agents_head; NULL != cur; cur = cur->next)
845 //  {
846 //    if ((!active_only) || (active_only && cur->active))
847 //    {
848 //      c += 1;
849 //    }
850 //  }
851 //  return c;
852 //}
853
854 /**
855  * Returns the agent for a peer
856  * @param s solver handle
857  * @param peer identity of the peer
858  * @param create whether to create an agent if none is allocated yet
859  * @return agent
860  */
861 static struct RIL_Peer_Agent *
862 ril_get_agent (struct GAS_RIL_Handle *solver,
863     const struct GNUNET_PeerIdentity *peer,
864     int create)
865 {
866   struct RIL_Peer_Agent *cur;
867
868   for (cur = solver->agents_head; NULL != cur; cur = cur->next)
869   {
870     if (0 == GNUNET_CRYPTO_hash_cmp (&peer->hashPubKey, &cur->peer.hashPubKey))
871     {
872       return cur;
873     }
874   }
875
876   if (create)
877     return agent_init (solver, peer);
878   return NULL ;
879 }
880
881 static int
882 ril_network_is_active (struct RIL_Network *network)
883 {
884   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
885
886   if (network->bw_out_available < min_bw)
887     return GNUNET_NO;
888   return GNUNET_YES;
889 }
890
891 /**
892  * Iterator, which allocates one agent per peer
893  *
894  * @param cls solver
895  * @param key peer identity
896  * @param value address
897  * @return whether iterator should continue
898  */
899 static int
900 ril_init_agents_it (void *cls, const struct GNUNET_HashCode *key, void *value)
901 {
902   struct GAS_RIL_Handle *solver = cls;
903   struct ATS_Address *address = value;
904   struct RIL_Peer_Agent *agent = NULL;
905   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
906
907   if (ril_network_is_active (address->solver_information))
908   {
909     agent = ril_get_agent (solver, &address->peer, GNUNET_YES);
910
911     GNUNET_assert(NULL != agent);
912
913     if (NULL == agent->address)
914     {
915       agent->address = address;
916       agent->address->active = GNUNET_YES;
917       agent->bw_in = min_bw;
918       agent->address->assigned_bw_in.value__ = htonl (min_bw);
919       agent->bw_out = min_bw;
920       agent->address->assigned_bw_out.value__ = htonl (min_bw);
921     }
922   }
923   return GNUNET_YES;
924 }
925
926 static void
927 ril_get_new_address_or_delete (struct GAS_RIL_Handle *solver,
928     struct RIL_Peer_Agent *agent)
929 {
930   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
931   //get new address for agent or delete agent
932
933   agent->address = NULL; //forget current address
934   GNUNET_CONTAINER_multihashmap_iterate (solver->addresses, &ril_init_agents_it,
935       solver); //put another address
936
937   if (NULL == agent->address) //no other address available
938   {
939     agent->active = GNUNET_NO;
940     ril_remove_agent (solver, agent);
941   }
942   else
943   {
944     envi_change_active_address (solver, agent, agent->address, min_bw, min_bw);
945   }
946 }
947
948 /**
949  * Lookup network struct by type
950  *
951  * @param s the solver handle
952  * @param type the network type
953  * @return the network struct
954  */
955 static struct RIL_Network *
956 ril_get_network (struct GAS_RIL_Handle *s, uint32_t type)
957 {
958   int i;
959
960   for (i = 0; i < s->networks_count; i++)
961   {
962     if (s->network_entries[i].type == type)
963     {
964       return &s->network_entries[i];
965     }
966   }
967   return NULL ;
968 }
969
970 /**
971  *  Solver API functions
972  *  ---------------------------
973  */
974
975 /**
976  * Changes the preferences for a peer in the problem
977  *
978  * @param solver the solver handle
979  * @param peer the peer to change the preference for
980  * @param kind the kind to change the preference
981  * @param pref_rel the normalized preference value for this kind over all clients
982  */
983 void
984 GAS_ril_address_change_preference (void *s,
985     const struct GNUNET_PeerIdentity *peer,
986     enum GNUNET_ATS_PreferenceKind kind,
987     double pref_rel)
988 {
989   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
990       "API_address_change_preference() Preference '%s' for peer '%s' changed to %.2f \n",
991       GNUNET_ATS_print_preference_type (kind), GNUNET_i2s (peer), pref_rel);
992   /*
993    * Nothing to do here. Preferences are considered during reward calculation.
994    */
995 }
996
997 /**
998  * Init the reinforcement learning problem solver
999  *
1000  * Quotas:
1001  * network[i] contains the network type as type GNUNET_ATS_NetworkType[i]
1002  * out_quota[i] contains outbound quota for network type i
1003  * in_quota[i] contains inbound quota for network type i
1004  *
1005  * Example
1006  * network = {GNUNET_ATS_NET_UNSPECIFIED, GNUNET_ATS_NET_LOOPBACK, GNUNET_ATS_NET_LAN, GNUNET_ATS_NET_WAN, GNUNET_ATS_NET_WLAN}
1007  * network[2]   == GNUNET_ATS_NET_LAN
1008  * out_quota[2] == 65353
1009  * in_quota[2]  == 65353
1010  *
1011  * @param cfg configuration handle
1012  * @param stats the GNUNET_STATISTICS handle
1013  * @param network array of GNUNET_ATS_NetworkType with length dest_length
1014  * @param addresses hashmap containing all addresses
1015  * @param out_quota array of outbound quotas
1016  * @param in_quota array of outbound quota
1017  * @param dest_length array length for quota arrays
1018  * @param bw_changed_cb callback for changed bandwidth amounts
1019  * @param bw_changed_cb_cls cls for callback
1020  * @param get_preference callback to get relative preferences for a peer
1021  * @param get_preference_cls cls for callback to get relative preferences
1022  * @param get_properties_cls for callback to get relative properties
1023  * @param get_properties_cls cls for callback to get relative properties
1024  * @return handle for the solver on success, NULL on fail
1025  */
1026 void *
1027 GAS_ril_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1028     const struct GNUNET_STATISTICS_Handle *stats,
1029     const struct GNUNET_CONTAINER_MultiHashMap *addresses,
1030     int *network,
1031     unsigned long long *out_quota,
1032     unsigned long long *in_quota,
1033     int dest_length,
1034     GAS_bandwidth_changed_cb bw_changed_cb,
1035     void *bw_changed_cb_cls,
1036     GAS_get_preferences get_preference,
1037     void *get_preference_cls,
1038     GAS_get_properties get_properties,
1039     void *get_properties_cls)
1040 {
1041   int c;
1042   unsigned long long tmp;
1043   char *string;
1044   struct RIL_Network * cur;
1045   struct GAS_RIL_Handle *solver = GNUNET_malloc (sizeof (struct GAS_RIL_Handle));
1046
1047   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "API_init() Initializing RIL solver\n");
1048
1049   GNUNET_assert(NULL != cfg);
1050   GNUNET_assert(NULL != stats);
1051   GNUNET_assert(NULL != network);
1052   GNUNET_assert(NULL != bw_changed_cb);
1053   GNUNET_assert(NULL != get_preference);
1054   GNUNET_assert(NULL != get_properties);
1055
1056   if (GNUNET_OK
1057       != GNUNET_CONFIGURATION_get_value_time (cfg, "ats", "RIL_STEP_TIME",
1058           &solver->step_time))
1059   {
1060     solver->step_time = RIL_DEFAULT_STEP_TIME;
1061   }
1062   if (GNUNET_OK
1063       == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "RIL_ALGORITHM",
1064           &string) && NULL != string && 0 == strcmp (string, "SARSA"))
1065   {
1066     solver->parameters.algorithm = RIL_ALGO_SARSA;
1067   }
1068   else
1069   {
1070     solver->parameters.algorithm = RIL_DEFAULT_ALGORITHM;
1071   }
1072   if (GNUNET_OK
1073       == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", "RIL_DISCOUNT_FACTOR",
1074           &tmp))
1075   {
1076     solver->parameters.gamma = (double) tmp / 100;
1077   }
1078   else
1079   {
1080     solver->parameters.gamma = RIL_DEFAULT_DISCOUNT_FACTOR;
1081   }
1082   if (GNUNET_OK
1083       == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1084           "RIL_GRADIENT_STEP_SIZE", &tmp))
1085   {
1086     solver->parameters.alpha = (double) tmp / 100;
1087   }
1088   else
1089   {
1090     solver->parameters.alpha = RIL_DEFAULT_GRADIENT_STEP_SIZE;
1091   }
1092   if (GNUNET_OK
1093       == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", "RIL_TRACE_DECAY",
1094           &tmp))
1095   {
1096     solver->parameters.lambda = (double) tmp / 100;
1097   }
1098   else
1099   {
1100     solver->parameters.lambda = RIL_DEFAULT_TRACE_DECAY;
1101   }
1102
1103   solver->stats = (struct GNUNET_STATISTICS_Handle *) stats;
1104   solver->callbacks = GNUNET_malloc (sizeof (struct RIL_Callbacks));
1105   solver->callbacks->bw_changed = bw_changed_cb;
1106   solver->callbacks->bw_changed_cls = bw_changed_cb_cls;
1107   solver->callbacks->get_preferences = get_preference;
1108   solver->callbacks->get_preferences_cls = get_preference_cls;
1109   solver->callbacks->get_properties = get_properties;
1110   solver->callbacks->get_properties_cls = get_properties_cls;
1111   solver->networks_count = dest_length;
1112   solver->network_entries =
1113       GNUNET_malloc (dest_length * sizeof (struct RIL_Network));
1114   solver->bulk_lock = GNUNET_NO;
1115   solver->addresses = addresses;
1116   solver->step_count = 0;
1117
1118   for (c = 0; c < dest_length; c++)
1119   {
1120     cur = &solver->network_entries[c];
1121     cur->type = network[c];
1122     cur->bw_in_available = in_quota[c];
1123     cur->bw_in_assigned = 0;
1124     cur->bw_out_available = out_quota[c];
1125     cur->bw_out_assigned = 0;
1126   }
1127
1128   solver->next_step = GNUNET_SCHEDULER_add_delayed (
1129       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_millisecond_ (),
1130           1000), &ril_periodic_step, solver);
1131
1132   return solver;
1133 }
1134
1135 /**
1136  * Shutdown the reinforcement learning problem solver
1137  *
1138  * @param solver the respective handle to shutdown
1139  */
1140 void
1141 GAS_ril_done (void * solver)
1142 {
1143   struct GAS_RIL_Handle *s = solver;
1144   struct RIL_Peer_Agent *cur_agent;
1145   struct RIL_Peer_Agent *next_agent;
1146
1147   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "API_done() Shutting down RIL solver\n");
1148
1149   cur_agent = s->agents_head;
1150   while (NULL != cur_agent)
1151   {
1152     next_agent = cur_agent->next;
1153     GNUNET_CONTAINER_DLL_remove(s->agents_head, s->agents_tail, cur_agent);
1154     agent_die (s, cur_agent);
1155     cur_agent = next_agent;
1156   }
1157
1158   GNUNET_SCHEDULER_cancel (s->next_step);
1159   GNUNET_free(s->callbacks);
1160   GNUNET_free(s->network_entries);
1161   GNUNET_free(s);
1162 }
1163
1164 /**
1165  * Add a single address within a network to the solver
1166  *
1167  * @param solver the solver Handle
1168  * @param address the address to add
1169  * @param network network type of this address
1170  */
1171 void
1172 GAS_ril_address_add (void *solver,
1173     struct ATS_Address *address,
1174     uint32_t network)
1175 {
1176   struct GAS_RIL_Handle *s = solver;
1177   //TODO! implement solver address add
1178   /*
1179    * if (new peer)
1180    *     initialize new agent
1181    * Add address
1182    * increase state vector
1183    * knowledge matrix
1184    * and action vector
1185    */
1186
1187   address->solver_information = ril_get_network (s, network);
1188
1189   /*
1190    * reiterate all addresses, create new agent if necessary and give the agent the address
1191    */
1192   GNUNET_CONTAINER_multihashmap_iterate (s->addresses, &ril_init_agents_it,
1193       solver);
1194
1195   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1196       "API_address_add() Added %s address for peer '%s'\n", address->plugin,
1197       GNUNET_i2s (&address->peer));
1198 }
1199
1200 /**
1201  * Remove an address from the solver
1202  *
1203  * @param solver the solver handle
1204  * @param address the address to remove
1205  * @param session_only delete only session not whole address
1206  */
1207 void
1208 GAS_ril_address_delete (void *solver,
1209     struct ATS_Address *address,
1210     int session_only)
1211 {
1212   //TODO! implement solver address delete
1213   //TODO! delete session only
1214   /*
1215    * remove address
1216    * if (last address of peer)
1217    *     remove agent
1218    * else
1219    *     decrease state vector
1220    *     decrease knowledge matrix
1221    *     decrease action vector
1222    */
1223   struct GAS_RIL_Handle *s = solver;
1224   struct RIL_Peer_Agent *agent;
1225
1226   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1227       "API_address_delete() deleting %s address %p for peer '%s'\n",
1228       address->active ? "active" : "inactive", address,
1229       GNUNET_i2s (&address->peer));
1230
1231   agent = ril_get_agent (s, &address->peer, GNUNET_NO);
1232
1233   if (NULL == agent)
1234   {
1235     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1236         "API_address_delete() deleting address for unallocated agent\n");
1237     return;
1238   }
1239
1240   if (address == agent->address) //if used address deleted
1241   {
1242     address->active = GNUNET_NO;
1243     ril_get_new_address_or_delete (s, agent);
1244   }
1245
1246   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1247       "API_address_delete() Deleted %s%s address for peer '%s'\n",
1248       session_only ? "session for " : "", address->plugin,
1249       GNUNET_i2s (&address->peer));
1250 }
1251
1252 /**
1253  * Transport properties for this address have changed
1254  *
1255  * @param solver solver handle
1256  * @param address the address
1257  * @param type the ATSI type in HBO
1258  * @param abs_value the absolute value of the property
1259  * @param rel_value the normalized value
1260  */
1261 void
1262 GAS_ril_address_property_changed (void *solver,
1263     struct ATS_Address *address,
1264     uint32_t type,
1265     uint32_t abs_value,
1266     double rel_value)
1267 {
1268   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1269       "API_address_property_changed() Property '%s' for peer '%s' address %p changed "
1270           "to %.2f \n", GNUNET_ATS_print_property_type (type),
1271       GNUNET_i2s (&address->peer), address, rel_value);
1272   /*
1273    * Nothing to do here, properties are considered in every reward calculation
1274    */
1275 }
1276
1277 /**
1278  * Transport session for this address has changed
1279  *
1280  * NOTE: values in addresses are already updated
1281  *
1282  * @param solver solver handle
1283  * @param address the address
1284  * @param cur_session the current session
1285  * @param new_session the new session
1286  */
1287 void
1288 GAS_ril_address_session_changed (void *solver,
1289     struct ATS_Address *address,
1290     uint32_t cur_session,
1291     uint32_t new_session)
1292 {
1293   //TODO? consider session changed in solver behaviour
1294   /*
1295    * Potentially add session activity as a feature in state vector
1296    */
1297   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "API_address_session_changed()\n");
1298 }
1299
1300 /**
1301  * Usage for this address has changed
1302  *
1303  * NOTE: values in addresses are already updated
1304  *
1305  * @param solver solver handle
1306  * @param address the address
1307  * @param in_use usage state
1308  */
1309 void
1310 GAS_ril_address_inuse_changed (void *solver,
1311     struct ATS_Address *address,
1312     int in_use)
1313 {
1314   //TODO! consider address_inuse_changed according to matthias' email
1315   /**
1316    * See matthias' email
1317    */
1318   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1319       "API_address_inuse_changed() Usage for %s address of peer '%s' changed to %s\n",
1320       address->plugin, GNUNET_i2s (&address->peer),
1321       (GNUNET_YES == in_use) ? "USED" : "UNUSED");
1322 }
1323
1324 /**
1325  * Network scope for this address has changed
1326  *
1327  * NOTE: values in addresses are already updated
1328  *
1329  * @param solver solver handle
1330  * @param address the address
1331  * @param current_network the current network
1332  * @param new_network the new network
1333  */
1334 void
1335 GAS_ril_address_change_network (void *solver,
1336     struct ATS_Address *address,
1337     uint32_t current_network,
1338     uint32_t new_network)
1339 {
1340   struct GAS_RIL_Handle *s = solver;
1341   struct RIL_Peer_Agent *agent;
1342   struct RIL_Network *net;
1343
1344   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1345       "API_address_change_network() Network type changed, moving "
1346           "%s address of peer %s from '%s' to '%s'\n",
1347       (GNUNET_YES == address->active) ? "active" : "inactive",
1348       GNUNET_i2s (&address->peer),
1349       GNUNET_ATS_print_network_type (current_network),
1350       GNUNET_ATS_print_network_type (new_network));
1351
1352   address->solver_information = ril_get_network (solver, new_network);
1353
1354   if (address->active)
1355   {
1356     agent = ril_get_agent (solver, &address->peer, GNUNET_NO);
1357
1358     //remove from old network
1359     net = ril_get_network (s, current_network);
1360     net->bw_in_assigned -= agent->bw_in;
1361     net->bw_out_assigned -= agent->bw_out;
1362
1363     if (ril_network_is_active (ril_get_network (s, new_network)))
1364     {
1365       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "New network is active\n");
1366       //add to new network
1367       net = ril_get_network (s, new_network);
1368       net->bw_in_assigned += agent->bw_in;
1369       net->bw_out_assigned += agent->bw_out;
1370
1371       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1372           "API_address_change_network() Moved %d inbound and %d "
1373               "outbound\n", agent->bw_in, agent->bw_out);
1374     }
1375     else //new network for this address is not active => address must not be considered
1376     {
1377       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "New network is not active\n");
1378
1379       net = agent->address->solver_information;
1380       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Before: active address %p is %s\n",
1381           agent->address, GNUNET_ATS_print_network_type (net->type));
1382
1383       address->active = GNUNET_NO;
1384       ril_get_new_address_or_delete (s, agent);
1385
1386       net = agent->address->solver_information;
1387       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "After: active address %p is %s\n",
1388           agent->address, GNUNET_ATS_print_network_type (net->type));
1389     }
1390   }
1391 }
1392
1393 /**
1394  * Get application feedback for a peer
1395  *
1396  * @param solver the solver handle
1397  * @param application the application
1398  * @param peer the peer to change the preference for
1399  * @param scope the time interval for this feedback: [now - scope .. now]
1400  * @param kind the kind to change the preference
1401  * @param score the score
1402  */
1403 void
1404 GAS_ril_address_preference_feedback (void *solver,
1405     void *application,
1406     const struct GNUNET_PeerIdentity *peer,
1407     const struct GNUNET_TIME_Relative scope,
1408     enum GNUNET_ATS_PreferenceKind kind,
1409     double score)
1410 {
1411   //TODO! collect reward until next reward calculation
1412   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1413       "API_address_preference_feedback() Peer '%s' got a feedback of %+.3f from application %s for "
1414           "preference %s for %d seconds\n", GNUNET_i2s (peer), "UNKNOWN",
1415       GNUNET_ATS_print_preference_type (kind), scope.rel_value_us / 1000000);
1416 }
1417
1418 /**
1419  * Start a bulk operation
1420  *
1421  * @param solver the solver
1422  */
1423 void
1424 GAS_ril_bulk_start (void *solver)
1425 {
1426   //TODO? consideration: keep bulk counter and stop agents during bulk
1427   /*
1428    * bulk counter up, but not really relevant, because there is no complete calculation of the
1429    * bandwidth assignment triggered anyway. Therefore, changes to addresses can come and go as
1430    * they want. Consideration: Step-pause during bulk-start-stop period...
1431    */
1432
1433   //GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "API_bulk_start()\n");
1434 }
1435
1436 /**
1437  * Bulk operation done
1438  */
1439 void
1440 GAS_ril_bulk_stop (void *solver)
1441 {
1442   //TODO? consideration: keep bulk counter and stop agents during bulk
1443   /*
1444    * bulk counter down, see bulk_start()
1445    */
1446
1447   //GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "API_bulk_stop()\n");
1448 }
1449
1450 /**
1451  * Get the preferred address for a specific peer
1452  *
1453  * @param solver the solver handle
1454  * @param peer the identity of the peer
1455  */
1456 const struct ATS_Address *
1457 GAS_ril_get_preferred_address (void *solver,
1458     const struct GNUNET_PeerIdentity *peer)
1459 {
1460   /*
1461    * activate agent, return currently chosen address
1462    */
1463   struct GAS_RIL_Handle *s = solver;
1464   struct RIL_Peer_Agent *agent;
1465
1466   agent = ril_get_agent (s, peer, GNUNET_NO);
1467
1468   if (NULL == agent)
1469   {
1470     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1471         "API_get_preferred_address() No agent for peer '%s' do not suggest address\n",
1472         GNUNET_i2s (peer));
1473     return NULL ;
1474   }
1475
1476   agent->active = GNUNET_YES;
1477
1478   GNUNET_assert(NULL != agent->address);
1479
1480   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1481       "API_get_preferred_address() Activated agent for peer '%s' with %s address\n",
1482       GNUNET_i2s (peer), agent->address->plugin);
1483
1484   return agent->address;
1485 }
1486
1487 /**
1488  * Stop notifying about address and bandwidth changes for this peer
1489  *
1490  * @param solver the solver handle
1491  * @param peer the peer
1492  */
1493 void
1494 GAS_ril_stop_get_preferred_address (void *solver,
1495     const struct GNUNET_PeerIdentity *peer)
1496 {
1497   struct GAS_RIL_Handle *s = solver;
1498   struct RIL_Peer_Agent *agent;
1499
1500   agent = ril_get_agent (s, peer, GNUNET_NO);
1501   agent->active = GNUNET_NO;
1502
1503   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1504       "API_stop_get_preferred_address() Paused agent for peer '%s' with %s address\n",
1505       GNUNET_i2s (peer), agent->address->plugin);
1506 }
1507
1508 /* end of gnunet-service-ats-solver_ril.c */