- more code
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses_mlp.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_addresses_mlp.c
23  * @brief ats mlp problem solver
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet-service-ats_addresses.h"
30 #include "gnunet-service-ats_addresses_mlp.h"
31 #include "gnunet_statistics_service.h"
32 #include "glpk.h"
33
34 #define WRITE_MLP GNUNET_NO
35 #define DEBUG_ATS GNUNET_NO
36 #define VERBOSE_GLPK GNUNET_NO
37
38 /**
39  * Translate glpk solver error codes to text
40  * @param retcode return code
41  * @return string with result
42  */
43 const char *
44 mlp_solve_to_string (int retcode)
45 {
46   switch (retcode) {
47     case 0:
48       return "ok";
49       break;
50     case GLP_EBADB:
51       return "invalid basis";
52       break;
53     case GLP_ESING:
54       return "singular matrix";
55       break;
56     case GLP_ECOND:
57       return "ill-conditioned matrix";
58       break;
59     case GLP_EBOUND:
60       return "invalid bounds";
61       break;
62     case GLP_EFAIL:
63       return "solver failed";
64       break;
65     case GLP_EOBJLL:
66       return "objective lower limit reached";
67       break;
68     case GLP_EOBJUL:
69       return "objective upper limit reached";
70       break;
71     case GLP_EITLIM:
72       return "iteration limit exceeded";
73       break;
74     case GLP_ETMLIM:
75       return "time limit exceeded";
76       break;
77     case GLP_ENOPFS:
78       return "no primal feasible solution";
79       break;
80     case GLP_EROOT:
81       return "root LP optimum not provided";
82       break;
83     case GLP_ESTOP:
84       return "search terminated by application";
85       break;
86     case GLP_EMIPGAP:
87       return "relative mip gap tolerance reached";
88       break;
89     case GLP_ENOFEAS:
90       return "no dual feasible solution";
91       break;
92     case GLP_ENOCVG:
93       return "no convergence";
94       break;
95     case GLP_EINSTAB:
96       return "numerical instability";
97       break;
98     case GLP_EDATA:
99       return "invalid data";
100       break;
101     case GLP_ERANGE:
102       return "result out of range";
103       break;
104     default:
105       GNUNET_break (0);
106       return "unknown error";
107       break;
108   }
109   GNUNET_break (0);
110   return "unknown error";
111 }
112
113
114 /**
115  * Translate glpk status error codes to text
116  * @param retcode return code
117  * @return string with result
118  */
119 const char *
120 mlp_status_to_string (int retcode)
121 {
122   switch (retcode) {
123     case GLP_UNDEF:
124       return "solution is undefined";
125       break;
126     case GLP_FEAS:
127       return "solution is feasible";
128       break;
129     case GLP_INFEAS:
130       return "solution is infeasible";
131       break;
132     case GLP_NOFEAS:
133       return "no feasible solution exists";
134       break;
135     case GLP_OPT:
136       return "solution is optimal";
137       break;
138     case GLP_UNBND:
139       return "solution is unbounded";
140       break;
141     default:
142       GNUNET_break (0);
143       return "unknown error";
144       break;
145   }
146   GNUNET_break (0);
147   return "unknown error";
148 }
149
150 /**
151  * Translate ATS properties to text
152  * Just intended for debugging
153  *
154  * @param retcode return code
155  * @return string with result
156  */
157 const char *
158 mlp_ats_to_string (int ats_index)
159 {
160   switch (ats_index) {
161     case GNUNET_ATS_ARRAY_TERMINATOR:
162       return "GNUNET_ATS_ARRAY_TERMINATOR";
163       break;
164     case GNUNET_ATS_UTILIZATION_UP:
165       return "GNUNET_ATS_UTILIZATION_UP";
166       break;
167     case GNUNET_ATS_UTILIZATION_DOWN:
168       return "GNUNET_ATS_UTILIZATION_DOWN";
169       break;
170     case GNUNET_ATS_COST_LAN:
171       return "GNUNET_ATS_COST_LAN";
172       break;
173     case GNUNET_ATS_COST_WAN:
174       return "GNUNET_ATS_COST_LAN";
175       break;
176     case GNUNET_ATS_COST_WLAN:
177       return "GNUNET_ATS_COST_WLAN";
178       break;
179     case GNUNET_ATS_NETWORK_TYPE:
180       return "GNUNET_ATS_NETWORK_TYPE";
181       break;
182     case GNUNET_ATS_QUALITY_NET_DELAY:
183       return "GNUNET_ATS_QUALITY_NET_DELAY";
184       break;
185     case GNUNET_ATS_QUALITY_NET_DISTANCE:
186       return "GNUNET_ATS_QUALITY_NET_DISTANCE";
187       break;
188     default:
189       return "unknown";
190       break;
191   }
192   GNUNET_break (0);
193   return "unknown error";
194 }
195
196 /**
197  * Find a peer in the DLL
198  * @param the peer to find
199  * @return the peer struct
200  */
201 static struct ATS_Peer *
202 mlp_find_peer (struct GAS_MLP_Handle *mlp, const struct GNUNET_PeerIdentity *peer)
203 {
204   struct ATS_Peer *res = mlp->peer_head;
205   while (res != NULL)
206   {
207     if (0 == memcmp (peer, &res->id, sizeof (struct GNUNET_PeerIdentity)))
208       break;
209     res = res->next;
210   }
211   return res;
212 }
213
214 /**
215  * Intercept GLPK terminal output
216  * @param info the mlp handle
217  * @param s the string to print
218  * @return 0: glpk prints output on terminal, 0 != surpress output
219  */
220 static int
221 mlp_term_hook (void *info, const char *s)
222 {
223   /* Not needed atm struct MLP_information *mlp = info; */
224   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", s);
225   return 1;
226 }
227
228 /**
229  * Delete the MLP problem and free the constrain matrix
230  *
231  * @param mlp the MLP handle
232  */
233 static void
234 mlp_delete_problem (struct GAS_MLP_Handle *mlp)
235 {
236   if (mlp != NULL)
237   {
238     if (mlp->prob != NULL)
239       glp_delete_prob(mlp->prob);
240
241     /* delete row index */
242     if (mlp->ia != NULL)
243     {
244       GNUNET_free (mlp->ia);
245       mlp->ia = NULL;
246     }
247
248     /* delete column index */
249     if (mlp->ja != NULL)
250     {
251       GNUNET_free (mlp->ja);
252       mlp->ja = NULL;
253     }
254
255     /* delete coefficients */
256     if (mlp->ar != NULL)
257     {
258       GNUNET_free (mlp->ar);
259       mlp->ar = NULL;
260     }
261     mlp->ci = 0;
262     mlp->prob = NULL;
263   }
264 }
265
266 /**
267  * Add constraints that are iterating over "forall addresses"
268  * and collects all existing peers for "forall peers" constraints
269  *
270  * @param cls GAS_MLP_Handle
271  * @param key Hashcode
272  * @param value ATS_Address
273  *
274  * @return GNUNET_OK to continue
275  */
276 static int
277 create_constraint_it (void *cls, const GNUNET_HashCode * key, void *value)
278 {
279   struct GAS_MLP_Handle *mlp = cls;
280   struct ATS_Address *address = value;
281   struct MLP_information *mlpi;
282   unsigned int row_index;
283   char *name;
284
285   GNUNET_assert (address->mlp_information != NULL);
286   mlpi = (struct MLP_information *) address->mlp_information;
287
288   /* c 1) bandwidth capping
289    * b_t  + (-M) * n_t <= 0
290    */
291   row_index = glp_add_rows (mlp->prob, 1);
292   mlpi->r_c1 = row_index;
293   /* set row name */
294   GNUNET_asprintf(&name, "c1_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
295   glp_set_row_name (mlp->prob, row_index, name);
296   GNUNET_free (name);
297   /* set row bounds: <= 0 */
298   glp_set_row_bnds (mlp->prob, row_index, GLP_UP, 0.0, 0.0);
299   mlp->ia[mlp->ci] = row_index;
300   mlp->ja[mlp->ci] = mlpi->c_b;
301   mlp->ar[mlp->ci] = 1;
302   mlp->ci++;
303
304   mlp->ia[mlp->ci] = row_index;
305   mlp->ja[mlp->ci] = mlpi->c_n;
306   mlp->ar[mlp->ci] = -mlp->BIG_M;
307   mlp->ci++;
308
309   /* c 3) minimum bandwidth
310    * b_t + (-n_t * b_min) >= 0
311    */
312
313   row_index = glp_add_rows (mlp->prob, 1);
314   /* set row name */
315   GNUNET_asprintf(&name, "c3_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
316   glp_set_row_name (mlp->prob, row_index, name);
317   GNUNET_free (name);
318   mlpi->r_c3 = row_index;
319   /* set row bounds: >= 0 */
320   glp_set_row_bnds (mlp->prob, row_index, GLP_LO, 0.0, 0.0);
321
322   mlp->ia[mlp->ci] = row_index;
323   mlp->ja[mlp->ci] = mlpi->c_b;
324   mlp->ar[mlp->ci] = 1;
325   mlp->ci++;
326
327   mlp->ia[mlp->ci] = row_index;
328   mlp->ja[mlp->ci] = mlpi->c_n;
329   mlp->ar[mlp->ci] = - (double) mlp->b_min;
330   mlp->ci++;
331
332   /* c 4) minimum connections
333    * (1)*n_1 + ... + (1)*n_m >= n_min
334    */
335   mlp->ia[mlp->ci] = mlp->r_c4;
336   mlp->ja[mlp->ci] = mlpi->c_n;
337   mlp->ar[mlp->ci] = 1;
338   mlp->ci++;
339
340   /* c 6) maximize diversity
341    * (1)*n_1 + ... + (1)*n_m - d == 0
342    */
343   mlp->ia[mlp->ci] = mlp->r_c6;
344   mlp->ja[mlp->ci] = mlpi->c_n;
345   mlp->ar[mlp->ci] = 1;
346   mlp->ci++;
347
348   return GNUNET_OK;
349 }
350
351 /**
352  * Find the required ATS information for an address
353  *
354  * @param addr the address
355  * @param ats_index the desired ATS index
356  *
357  * @return the index on success, otherwise GNUNET_SYSERR
358  */
359
360 static int
361 mlp_lookup_ats (struct ATS_Address *addr, int ats_index)
362 {
363   struct GNUNET_ATS_Information * ats = addr->ats;
364   int c;
365   int found = GNUNET_NO;
366   for (c = 0; c < addr->ats_count; c++)
367   {
368     if (ats[c].type == ats_index)
369     {
370       found = GNUNET_YES;
371       break;
372     }
373   }
374   if (found == GNUNET_YES)
375     return c;
376   else
377     return GNUNET_SYSERR;
378 }
379
380 /**
381  * Adds the problem constraints for all addresses
382  * Required for problem recreation after address deletion
383  *
384  * @param addresses all addresses
385  */
386
387 static void
388 mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
389 {
390   unsigned int n_addresses;
391   int c;
392   char *name;
393
394   /* Problem matrix*/
395   n_addresses = GNUNET_CONTAINER_multihashmap_size(addresses);
396
397   /* Required indices in the constrain matrix
398    *
399    * feasibility constraints:
400    *
401    * c 1) bandwidth capping
402    * #rows: |n_addresses|
403    * #indices: 2 * |n_addresses|
404    *
405    * c 2) one active address per peer
406    * #rows: |peers|
407    * #indices: |n_addresses|
408    *
409    * c 3) minium bandwidth assigned
410    * #rows: |n_addresses|
411    * #indices: 2 * |n_addresses|
412    *
413    * c 4) minimum number of active connections
414    * #rows: 1
415    * #indices: |n_addresses|
416    *
417    * c 5) maximum ressource consumption
418    * #rows: |ressources|
419    * #indices: |n_addresses|
420    *
421    * Sum for feasibility constraints:
422    * #rows: 3 * |n_addresses| +  |ressources| + |peers| + 1
423    * #indices: 7 * |n_addresses|
424    *
425    * optimality constraints:
426    *
427    * c 6) diversity
428    * #rows: 1
429    * #indices: |n_addresses| + 1
430    *
431    * c 7) quality
432    * #rows: |quality properties|
433    * #indices: |n_addresses| + |quality properties|
434    *
435    * c 8) utilization
436    * #rows: 1
437    * #indices: |n_addresses| + 1
438    *
439    * c 9) relativity
440    * #rows: |peers|
441    * #indices: |n_addresses| + |peers|
442    * */
443
444   /* last +1 caused by glpk index starting with one */
445   int pi = ((7 * n_addresses) + (4 * n_addresses +  mlp->m_q + mlp->c_p + 2) + 1);
446   mlp->cm_size = pi;
447   mlp->ci = 1;
448
449   /* row index */
450   int *ia = GNUNET_malloc (pi * sizeof (int));
451   mlp->ia = ia;
452
453   /* column index */
454   int *ja = GNUNET_malloc (pi * sizeof (int));
455   mlp->ja = ja;
456
457   /* coefficient */
458   double *ar= GNUNET_malloc (pi * sizeof (double));
459   mlp->ar = ar;
460
461   /* Adding constraint rows
462    * This constraints are kind of "for all addresses"
463    * Feasibility constraints:
464    *
465    * c 1) bandwidth capping
466    * c 3) minimum bandwidth
467    * c 4) minimum number of connections
468    * c 6) maximize diversity
469    */
470
471   int min = mlp->n_min;
472   if (mlp->n_min > mlp->c_p)
473     min = mlp->c_p;
474
475   mlp->r_c4 = glp_add_rows (mlp->prob, 1);
476   glp_set_row_name (mlp->prob, mlp->r_c4, "c4");
477   glp_set_row_bnds (mlp->prob, mlp->r_c4, GLP_LO, min, min);
478
479   /* Add row for c6) */
480
481   mlp->r_c6 = glp_add_rows (mlp->prob, 1);
482   /* Set type type to fix */
483   glp_set_row_bnds (mlp->prob, mlp->r_c6, GLP_FX, 0.0, 0.0);
484   /* Setting -D */
485   ia[mlp->ci] = mlp->r_c6 ;
486   ja[mlp->ci] = mlp->c_d;
487   ar[mlp->ci] = -1;
488   mlp->ci++;
489
490   GNUNET_CONTAINER_multihashmap_iterate (addresses, create_constraint_it, mlp);
491
492   /* Adding constraint rows
493    * This constraints are kind of "for all peers"
494    * Feasibility constraints:
495    *
496    * c 2) 1 address per peer
497    * sum (n_p1_1 + ... + n_p1_n) = 1
498    *
499    * c 8) utilization
500    * sum (f_p * b_p1_1 + ... + f_p * b_p1_n) - u = 0
501    *
502    * c 9) relativity
503    * V p : sum (bt_1 + ... +bt_n) - f_p * r = 0
504    * */
505
506   /* Adding rows for c 8) */
507   mlp->r_c8 = glp_add_rows (mlp->prob, mlp->c_p);
508   glp_set_row_name (mlp->prob, mlp->r_c8, "c8");
509   /* Set row bound == 0 */
510   glp_set_row_bnds (mlp->prob, mlp->r_c8, GLP_FX, 0.0, 0.0);
511   /* -u */
512   ia[mlp->ci] = mlp->r_c8;
513   ja[mlp->ci] = mlp->c_u;
514   ar[mlp->ci] = -1;
515   mlp->ci++;
516
517   struct ATS_Peer * peer = mlp->peer_head;
518   while (peer != NULL)
519   {
520     struct ATS_Address *addr = peer->head;
521     struct MLP_information *mlpi = NULL;
522
523     /* Adding rows for c 2) */
524     peer->r_c2 = glp_add_rows (mlp->prob, 1);
525     GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&peer->id));
526     glp_set_row_name (mlp->prob, peer->r_c2, name);
527     GNUNET_free (name);
528     /* Set row bound == 1 */
529     glp_set_row_bnds (mlp->prob, peer->r_c2, GLP_FX, 1.0, 1.0);
530
531     /* Adding rows for c 9) */
532     peer->r_c9 = glp_add_rows (mlp->prob, 1);
533     GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&peer->id));
534     glp_set_row_name (mlp->prob, peer->r_c9, name);
535     GNUNET_free (name);
536     /* Set row bound == 0 */
537     glp_set_row_bnds (mlp->prob, peer->r_c9, GLP_LO, 0.0, 0.0);
538
539     /* Set -r */
540     ia[mlp->ci] = peer->r_c9;
541     ja[mlp->ci] = mlp->c_r;
542     ar[mlp->ci] = -1;
543     mlp->ci++;
544
545     while (addr != NULL)
546     {
547       mlpi = (struct MLP_information *) addr->mlp_information;
548
549       ia[mlp->ci] = peer->r_c2;
550       ja[mlp->ci] = mlpi->c_n;
551       ar[mlp->ci] = 1;
552       mlp->ci++;
553
554       ia[mlp->ci] = mlp->r_c8;
555       ja[mlp->ci] = mlpi->c_b;
556       ar[mlp->ci] = peer->f;
557       mlp->ci++;
558
559       ia[mlp->ci] = peer->r_c9;
560       ja[mlp->ci] = mlpi->c_b;
561       ar[mlp->ci] = 1;
562       mlp->ci++;
563
564       addr = addr->next;
565     }
566     peer = peer->next;
567   }
568
569   /* c 7) For all quality metrics */
570
571   for (c = 0; c < mlp->m_q; c++)
572   {
573     struct ATS_Peer *p = mlp->peer_head;
574     struct ATS_Address *addr = p->head;
575     struct MLP_information * mlpi;
576     double value = 1.0;
577
578     while (p != NULL)
579     {
580       /* Adding rows for c 7) */
581       mlp->r_q[c] = glp_add_rows (mlp->prob, 1);
582       GNUNET_asprintf(&name, "c7_q%i_atsi_%i", c, mlp->q[c]);
583       glp_set_row_name (mlp->prob, mlp->r_q[c], name);
584       GNUNET_free (name);
585       /* Set row bound == 0 */
586       glp_set_row_bnds (mlp->prob, mlp->r_q[c], GLP_LO, 0.0, 0.0);
587
588       /* Set -q_m */
589       ia[mlp->ci] = mlp->r_q[c];
590       ja[mlp->ci] = mlp->c_q[c];
591       ar[mlp->ci] = -1;
592       mlp->ci++;
593
594       while (addr != NULL)
595       {
596         /* lookup ATS information */
597         int index = mlp_lookup_ats(addr, mlp->q[c]);
598
599         if (index != GNUNET_SYSERR)
600         {
601           value = (double) addr->ats[index].value;
602 #if DEBUG_ATS
603           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Quality %i with ATS property `%s' has index %i in addresses ats information has value %f\n", c,  mlp_ats_to_string(mlp->q[c]), index, (double) addr->ats[index].value);
604 #endif
605         }
606 #if DEBUG_ATS
607         else
608           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Quality %i with ATS property `%s' not existing\n", c,  mlp_ats_to_string(mlp->q[c]), index);
609 #endif
610
611         mlpi = addr->mlp_information;
612         ia[mlp->ci] = mlp->r_q[c];
613         ja[mlp->ci] = mlpi->c_b;
614         ar[mlp->ci] = p->f * value;
615         mlp->ci++;
616
617         addr = addr->next;
618       }
619       p = p->next;
620     }
621   }
622 }
623
624
625 /**
626  * Add columns for all addresses
627  *
628  * @param cls GAS_MLP_Handle
629  * @param key Hashcode
630  * @param value ATS_Address
631  *
632  * @return GNUNET_OK to continue
633  */
634 static int
635 create_columns_it (void *cls, const GNUNET_HashCode * key, void *value)
636 {
637   struct GAS_MLP_Handle *mlp = cls;
638   struct ATS_Address *address = value;
639   struct MLP_information *mlpi;
640   unsigned int col;
641   char *name;
642
643   GNUNET_assert (address->mlp_information != NULL);
644   mlpi = address->mlp_information;
645
646   /* Add bandwidth column */
647   col = glp_add_cols (mlp->prob, 2);
648   mlpi->c_b = col;
649   mlpi->c_n = col + 1;
650
651   GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
652   glp_set_col_name (mlp->prob, mlpi->c_b , name);
653   GNUNET_free (name);
654   /* Lower bound == 0 */
655   glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0);
656   /* Continuous value*/
657   glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
658   /* Objective function coefficient == 0 */
659   glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
660
661
662   /* Add usage column */
663   GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
664   glp_set_col_name (mlp->prob, mlpi->c_n, name);
665   GNUNET_free (name);
666   /* Limit value : 0 <= value <= 1 */
667   glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0);
668   /* Integer value*/
669   glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
670   /* Objective function coefficient == 0 */
671   glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
672
673   return GNUNET_OK;
674 }
675
676
677
678 /**
679  * Create the MLP problem
680  *
681  * @param mlp the MLP handle
682  * @return GNUNET_OK or GNUNET_SYSERR
683  */
684 static int
685 mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
686 {
687   int res = GNUNET_OK;
688   int col;
689   int c;
690   char *name;
691
692   GNUNET_assert (mlp->prob == NULL);
693
694   /* create the glpk problem */
695   mlp->prob = glp_create_prob ();
696
697   /* Set a problem name */
698   glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution");
699
700   /* Set optimization direction to maximize */
701   glp_set_obj_dir (mlp->prob, GLP_MAX);
702
703   /* Adding invariant columns */
704
705   /* Diversity d column  */
706
707   col = glp_add_cols (mlp->prob, 1);
708   mlp->c_d = col;
709   /* Column name */
710   glp_set_col_name (mlp->prob, col, "d");
711   /* Column objective function coefficient */
712   glp_set_obj_coef (mlp->prob, col, mlp->co_D);
713   /* Column lower bound = 0.0 */
714   glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
715
716   /* Utilization u column  */
717
718   col = glp_add_cols (mlp->prob, 1);
719   mlp->c_u = col;
720   /* Column name */
721   glp_set_col_name (mlp->prob, col, "u");
722   /* Column objective function coefficient */
723   glp_set_obj_coef (mlp->prob, col, mlp->co_U);
724   /* Column lower bound = 0.0 */
725   glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
726
727   /* Relativity r column  */
728   col = glp_add_cols (mlp->prob, 1);
729   mlp->c_r = col;
730   /* Column name */
731   glp_set_col_name (mlp->prob, col, "r");
732   /* Column objective function coefficient */
733   glp_set_obj_coef (mlp->prob, col, mlp->co_R);
734   /* Column lower bound = 0.0 */
735   glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
736
737   /* Quality metric columns */
738   col = glp_add_cols(mlp->prob, mlp->m_q);
739   for (c = 0; c < mlp->m_q; c++)
740   {
741     mlp->c_q[c] = col + c;
742     GNUNET_asprintf (&name, "q_%u", mlp->q[c]);
743     glp_set_col_name (mlp->prob, col + c, name);
744     /* Column lower bound = 0.0 */
745     glp_set_col_bnds (mlp->prob, col + c, GLP_LO, 0.0, 0.0);
746     GNUNET_free (name);
747     /* Coefficient == Qm */
748     glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
749   }
750
751   /* Add columns for addresses */
752   GNUNET_CONTAINER_multihashmap_iterate (addresses, create_columns_it, mlp);
753
754   /* Add constraints */
755   mlp_add_constraints_all_addresses (mlp, addresses);
756
757   /* Load the matrix */
758   glp_load_matrix(mlp->prob, (mlp->ci-1), mlp->ia, mlp->ja, mlp->ar);
759
760   return res;
761 }
762
763 /**
764  * Solves the LP problem
765  *
766  * @param mlp the MLP Handle
767  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
768  */
769 static int
770 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
771 {
772   int res;
773   struct GNUNET_TIME_Relative duration;
774   struct GNUNET_TIME_Absolute end;
775   struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
776
777   /* LP presolver?
778    * Presolver is required if the problem was modified and an existing
779    * valid basis is now invalid */
780   if (mlp->presolver_required == GNUNET_YES)
781     mlp->control_param_lp.presolve = GLP_ON;
782   else
783     mlp->control_param_lp.presolve = GLP_OFF;
784
785   /* Solve LP problem to have initial valid solution */
786 lp_solv:
787   res = glp_simplex(mlp->prob, &mlp->control_param_lp);
788   if (res == 0)
789   {
790     /* The LP problem instance has been successfully solved. */
791   }
792   else if (res == GLP_EITLIM)
793   {
794     /* simplex iteration limit has been exceeded. */
795     // TODO Increase iteration limit?
796   }
797   else if (res == GLP_ETMLIM)
798   {
799     /* Time limit has been exceeded.  */
800     // TODO Increase time limit?
801   }
802   else
803   {
804     /* Problem was ill-defined, retry with presolver */
805     if (mlp->presolver_required == GNUNET_NO)
806     {
807       mlp->presolver_required = GNUNET_YES;
808       goto lp_solv;
809     }
810     else
811     {
812       /* Problem was ill-defined, no way to handle that */
813       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
814           "ats-mlp",
815           "Solving LP problem failed: %i %s\n", res, mlp_solve_to_string(res));
816       return GNUNET_SYSERR;
817     }
818   }
819
820   end = GNUNET_TIME_absolute_get ();
821   duration = GNUNET_TIME_absolute_get_difference (start, end);
822   mlp->lp_solved++;
823   mlp->lp_total_duration =+ duration.rel_value;
824
825   GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
826   GNUNET_STATISTICS_set (mlp->stats,"# LP execution time", duration.rel_value, GNUNET_NO);
827   GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average",
828                          mlp->lp_total_duration / mlp->lp_solved,  GNUNET_NO);
829
830
831   /* Analyze problem status  */
832   res = glp_get_status (mlp->prob);
833   switch (res) {
834     /* solution is optimal */
835     case GLP_OPT:
836     /* solution is feasible */
837     case GLP_FEAS:
838       break;
839
840     /* Problem was ill-defined, no way to handle that */
841     default:
842       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
843           "ats-mlp",
844           "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res));
845       return GNUNET_SYSERR;
846       break;
847   }
848
849   /* solved sucessfully, no presolver required next time */
850   mlp->presolver_required = GNUNET_NO;
851
852   return GNUNET_OK;
853 }
854
855
856 /**
857  * Solves the MLP problem
858  *
859  * @param mlp the MLP Handle
860  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
861  */
862 int
863 mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
864 {
865   int res;
866   struct GNUNET_TIME_Relative duration;
867   struct GNUNET_TIME_Absolute end;
868   struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
869
870   /* solve MLP problem */
871   res = glp_intopt(mlp->prob, &mlp->control_param_mlp);
872
873   if (res == 0)
874   {
875     /* The MLP problem instance has been successfully solved. */
876   }
877   else if (res == GLP_EITLIM)
878   {
879     /* simplex iteration limit has been exceeded. */
880     // TODO Increase iteration limit?
881   }
882   else if (res == GLP_ETMLIM)
883   {
884     /* Time limit has been exceeded.  */
885     // TODO Increase time limit?
886   }
887   else
888   {
889     /* Problem was ill-defined, no way to handle that */
890     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
891         "ats-mlp",
892         "Solving MLP problem failed:  %s\n", mlp_solve_to_string(res));
893     return GNUNET_SYSERR;
894   }
895
896   end = GNUNET_TIME_absolute_get ();
897   duration = GNUNET_TIME_absolute_get_difference (start, end);
898   mlp->mlp_solved++;
899   mlp->mlp_total_duration =+ duration.rel_value;
900
901   GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
902   GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time", duration.rel_value, GNUNET_NO);
903   GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average",
904                          mlp->mlp_total_duration / mlp->mlp_solved,  GNUNET_NO);
905
906   /* Analyze problem status  */
907   res = glp_mip_status(mlp->prob);
908   switch (res) {
909     /* solution is optimal */
910     case GLP_OPT:
911     /* solution is feasible */
912     case GLP_FEAS:
913       break;
914
915     /* Problem was ill-defined, no way to handle that */
916     default:
917       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
918           "ats-mlp",
919           "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res));
920       return GNUNET_SYSERR;
921       break;
922   }
923
924   return GNUNET_OK;
925 }
926
927 int mlp_solve_problem (struct GAS_MLP_Handle *mlp);
928
929 static void
930 mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
931 {
932   struct GAS_MLP_Handle *mlp = cls;
933
934   mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
935
936   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
937     return;
938
939 #if DEBUG_ATS
940   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
941 #endif
942   if (mlp->addr_in_problem != 0)
943     mlp_solve_problem(mlp);
944 }
945
946
947 /**
948  * Solves the MLP problem
949  *
950  * @param mlp the MLP Handle
951  * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
952  */
953 int
954 mlp_solve_problem (struct GAS_MLP_Handle *mlp)
955 {
956   int res;
957   mlp->last_execution = GNUNET_TIME_absolute_get ();
958 #if DEBUG_ATS
959   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solving\n");
960 #endif
961
962 #if WRITE_MLP
963   char * name;
964   static int i;
965   i++;
966   GNUNET_asprintf(&name, "problem_%i", i);
967   glp_write_lp (mlp->prob, 0, name);
968   GNUNET_free (name);
969 # endif
970
971   res = mlp_solve_lp_problem (mlp);
972
973 #if WRITE_MLP
974   GNUNET_asprintf(&name, "problem_%i_lp_solution", i);
975   glp_print_sol (mlp->prob,  name);
976   GNUNET_free (name);
977 # endif
978
979   if (res != GNUNET_OK)
980   {
981 #if DEBUG_ATS
982   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "LP Problem solving failed\n");
983 #endif
984     return GNUNET_SYSERR;
985   }
986
987   res = mlp_solve_mlp_problem (mlp);
988
989 #if WRITE_MLP
990   GNUNET_asprintf(&name, "problem_%i_mlp_solution", i);
991   glp_print_mip (mlp->prob, name);
992   GNUNET_free (name);
993 # endif
994   if (res != GNUNET_OK)
995   {
996 #if DEBUG_ATS
997   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP Problem solving failed\n");
998 #endif
999     return GNUNET_SYSERR;
1000   }
1001
1002 #if DEBUG_ATS
1003   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved\n");
1004 #endif
1005
1006   /* Process result */
1007
1008   if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
1009   {
1010     GNUNET_SCHEDULER_cancel(mlp->mlp_task);
1011     mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
1012   }
1013   mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
1014   return res;
1015 }
1016
1017 /**
1018  * Init the MLP problem solving component
1019  *
1020  * @param stats the GNUNET_STATISTICS handle
1021  * @param max_duration maximum numbers of iterations for the LP/MLP Solver
1022  * @param max_iterations maximum time limit for the LP/MLP Solver
1023  * @return struct GAS_MLP_Handle * on success, NULL on fail
1024  */
1025 struct GAS_MLP_Handle *
1026 GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1027               const struct GNUNET_STATISTICS_Handle *stats,
1028               struct GNUNET_TIME_Relative max_duration,
1029               unsigned int max_iterations)
1030 {
1031   struct GAS_MLP_Handle * mlp = GNUNET_malloc (sizeof (struct GAS_MLP_Handle));
1032
1033   double D;
1034   double R;
1035   double U;
1036   long long unsigned int tmp;
1037   unsigned int b_min;
1038   unsigned int n_min;
1039   struct GNUNET_TIME_Relative i_exec;
1040
1041   /* Init GLPK environment */
1042   GNUNET_assert (glp_init_env() == 0);
1043
1044   /* Create initial MLP problem */
1045   mlp->prob = glp_create_prob();
1046   GNUNET_assert (mlp->prob != NULL);
1047
1048   /* Get diversity coefficient from configuration */
1049   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1050                                                       "COEFFICIENT_D",
1051                                                       &tmp))
1052     D = (double) tmp / 100;
1053   else
1054     D = 1.0;
1055
1056   /* Get proportionality coefficient from configuration */
1057   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1058                                                       "COEFFICIENT_R",
1059                                                       &tmp))
1060     R = (double) tmp / 100;
1061   else
1062     R = 1.0;
1063
1064   /* Get utilization coefficient from configuration */
1065   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1066                                                       "COEFFICIENT_U",
1067                                                       &tmp))
1068     U = (double) tmp / 100;
1069   else
1070     U = 1.0;
1071
1072   /* Get quality metric coefficients from configuration */
1073   int i_delay = -1;
1074   int i_distance = -1;
1075   int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
1076   int c;
1077   for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1078   {
1079     /* initialize quality coefficients with default value 1.0 */
1080     mlp->co_Q[c] = 1.0;
1081
1082     mlp->q[c] = q[c];
1083     if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
1084       i_delay = c;
1085     if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
1086       i_distance = c;
1087   }
1088
1089   if ((i_delay != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1090                                                       "COEFFICIENT_QUALITY_DELAY",
1091                                                       &tmp)))
1092
1093     mlp->co_Q[i_delay] = (double) tmp / 100;
1094   else
1095     mlp->co_Q[i_delay] = 1.0;
1096
1097   if ((i_distance != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1098                                                       "COEFFICIENT_QUALITY_DISTANCE",
1099                                                       &tmp)))
1100     mlp->co_Q[i_distance] = (double) tmp / 100;
1101   else
1102     mlp->co_Q[i_distance] = 1.0;
1103
1104   /* Get minimum bandwidth per used address from configuration */
1105   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1106                                                       "MIN_BANDWIDTH",
1107                                                       &tmp))
1108     b_min = tmp;
1109   else
1110     b_min = 64000;
1111
1112   /* Get minimum number of connections from configuration */
1113   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1114                                                       "MIN_CONNECTIONS",
1115                                                       &tmp))
1116     n_min = tmp;
1117   else
1118     n_min = 4;
1119
1120   /* Get minimum number of connections from configuration */
1121   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "ats",
1122                                                         "ATS_EXEC_INTERVAL",
1123                                                         &i_exec))
1124     mlp->exec_interval = i_exec;
1125   else
1126     mlp->exec_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30);
1127
1128   mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
1129   mlp->max_iterations = max_iterations;
1130   mlp->max_exec_duration = max_duration;
1131
1132   /* Redirect GLPK output to GNUnet logging */
1133   glp_error_hook((void *) mlp, &mlp_term_hook);
1134
1135   /* Init LP solving parameters */
1136   glp_init_smcp(&mlp->control_param_lp);
1137 #if VERBOSE_GLPK
1138   mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
1139 #else
1140   mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
1141 #endif
1142   mlp->control_param_lp.it_lim = max_iterations;
1143   mlp->control_param_lp.tm_lim = max_duration.rel_value;
1144
1145   /* Init MLP solving parameters */
1146   glp_init_iocp(&mlp->control_param_mlp);
1147 #if VERBOSE_GLPK
1148   mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
1149 #else
1150   mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
1151 #endif
1152   mlp->control_param_mlp.tm_lim = max_duration.rel_value;
1153
1154   mlp->last_execution = GNUNET_TIME_absolute_get_forever();
1155
1156
1157   mlp->BIG_M = (double) UINT32_MAX;
1158   mlp->co_D = D;
1159   mlp->co_R = R;
1160   mlp->co_U = U;
1161   mlp->b_min = b_min;
1162   mlp->n_min = n_min;
1163   mlp->m_q = GNUNET_ATS_QualityPropertiesCount;
1164
1165   return mlp;
1166 }
1167
1168 /**
1169  * Updates a single address in the MLP problem
1170  *
1171  * If the address did not exist before in the problem:
1172  * The MLP problem has to be recreated and the problem has to be resolved
1173  *
1174  * Otherwise the addresses' values can be updated and the existing base can
1175  * be reused
1176  *
1177  * @param mlp the MLP Handle
1178  * @param addresses the address hashmap
1179  *        the address has to be already removed from the hashmap
1180  * @param address the address to update
1181  */
1182 void
1183 GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1184 {
1185   int new;
1186   struct MLP_information *mlpi;
1187   int c;
1188
1189   GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
1190
1191   /* We add a new address */
1192   if (address->mlp_information == NULL)
1193     new = GNUNET_YES;
1194   else
1195     new = GNUNET_NO;
1196
1197   /* Do the update */
1198   if (new == GNUNET_YES)
1199   {
1200     mlpi = GNUNET_malloc (sizeof (struct MLP_information));
1201     address->mlp_information = mlpi;
1202     mlp->addr_in_problem ++;
1203
1204     /* Check for and add peer */
1205     struct ATS_Peer *peer = mlp_find_peer (mlp, &address->peer);
1206     if (peer == NULL)
1207     {
1208 #if DEBUG_ATS
1209       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding new peer `%s'\n", GNUNET_i2s (&address->peer));
1210 #endif
1211       peer = GNUNET_malloc (sizeof (struct ATS_Peer));
1212       peer->head = NULL;
1213       peer->tail = NULL;
1214
1215       for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1216       {
1217         peer->f_q[c] = 1.0;
1218       }
1219       peer->f = 1.0;
1220
1221       memcpy (&peer->id, &address->peer, sizeof (struct GNUNET_PeerIdentity));
1222       GNUNET_assert(address->prev == NULL);
1223       GNUNET_assert(address->next == NULL);
1224       GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1225       GNUNET_CONTAINER_DLL_insert (mlp->peer_head, mlp->peer_tail, peer);
1226       mlp->c_p ++;
1227     }
1228     else
1229     {
1230 #if DEBUG_ATS
1231       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding address to peer `%s'\n", GNUNET_i2s (&address->peer));
1232 #endif
1233       GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1234     }
1235   }
1236   else
1237   {
1238 #if DEBUG_ATS
1239     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Updating existing address to peer `%s'\n", GNUNET_i2s (&address->peer));
1240 #endif
1241     mlpi = address->mlp_information;
1242
1243   }
1244
1245   /* Recalculate */
1246   if (new == GNUNET_YES)
1247   {
1248     mlp_delete_problem (mlp);
1249     mlp_create_problem (mlp, addresses);
1250     mlp->presolver_required = GNUNET_YES;
1251   }
1252   mlp_solve_problem (mlp);
1253 }
1254
1255 /**
1256  * Deletes a single address in the MLP problem
1257  *
1258  * The MLP problem has to be recreated and the problem has to be resolved
1259  *
1260  * @param mlp the MLP Handle
1261  * @param addresses the address hashmap
1262  *        the address has to be already removed from the hashmap
1263  * @param address the address to delete
1264  */
1265 void
1266 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1267 {
1268   GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
1269
1270   /* Free resources */
1271   if (address->mlp_information != NULL)
1272   {
1273     GNUNET_free (address->mlp_information);
1274     address->mlp_information = NULL;
1275
1276     mlp->addr_in_problem --;
1277   }
1278
1279   /* Remove from peer list */
1280   struct ATS_Peer *head = mlp_find_peer (mlp, &address->peer);
1281   GNUNET_assert (head != NULL);
1282 #if DEBUG_ATS
1283   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deleting address for `%s'\n", GNUNET_i2s (&address->peer));
1284 #endif
1285   GNUNET_CONTAINER_DLL_remove (head->head, head->tail, address);
1286   if ((head->head == NULL) && (head->tail == NULL))
1287   {
1288     /* No address for peer left, remove peer */
1289 #if DEBUG_ATS
1290     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deleting peer `%s'\n", GNUNET_i2s (&address->peer));
1291 #endif
1292     GNUNET_CONTAINER_DLL_remove (mlp->peer_head, mlp->peer_tail, head);
1293     GNUNET_free (head);
1294     mlp->c_p --;
1295   }
1296
1297   /* Update problem */
1298   mlp_delete_problem (mlp);
1299   if ((GNUNET_CONTAINER_multihashmap_size (addresses) > 0) && (mlp->c_p > 0))
1300   {
1301     mlp_create_problem (mlp, addresses);
1302
1303     /* Recalculate */
1304     mlp->presolver_required = GNUNET_YES;
1305     mlp_solve_problem (mlp);
1306   }
1307 }
1308
1309 /**
1310  * Changes the preferences for a peer in the MLP problem
1311  *
1312  * @param mlp the MLP Handle
1313  * @param peer the peer
1314  * @param kind the kind to change the preference
1315  * @param float the score
1316  */
1317 void
1318 GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp,
1319                                    const struct GNUNET_PeerIdentity *peer,
1320                                    enum GNUNET_ATS_PreferenceKind kind,
1321                                    float score)
1322 {
1323   GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
1324
1325   struct ATS_Peer *p = mlp_find_peer (mlp, peer);
1326   p = p;
1327   /* Here we have to do the matching */
1328 }
1329
1330 /**
1331  * Shutdown the MLP problem solving component
1332  * @param mlp the MLP handle
1333  */
1334 void
1335 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
1336 {
1337   struct ATS_Peer * peer;
1338   struct ATS_Peer * tmp;
1339
1340   if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
1341   {
1342     GNUNET_SCHEDULER_cancel(mlp->mlp_task);
1343     mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
1344   }
1345
1346   /* clean up peer list */
1347   if (mlp != NULL)
1348   {
1349     peer = mlp->peer_head;
1350     while (peer != NULL)
1351     {
1352       GNUNET_CONTAINER_DLL_remove(mlp->peer_head, mlp->peer_tail, peer);
1353       tmp = peer->next;
1354       GNUNET_free (peer);
1355       peer = tmp;
1356     }
1357   }
1358   mlp_delete_problem (mlp);
1359
1360   /* Clean up GLPK environment */
1361   glp_free_env();
1362
1363   GNUNET_free (mlp);
1364 }
1365
1366
1367 /* end of gnunet-service-ats_addresses_mlp.c */