2 This file is part of GNUnet.
3 (C) 2011 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file ats/gnunet-service-ats_addresses_mlp.c
23 * @brief ats mlp problem solver
24 * @author Matthias Wachs
25 * @author Christian Grothoff
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"
34 #define WRITE_MLP GNUNET_NO
35 #define DEBUG_ATS GNUNET_NO
36 #define VERBOSE_GLPK GNUNET_NO
39 * Translate glpk solver error codes to text
40 * @param retcode return code
41 * @return string with result
44 mlp_solve_to_string (int retcode)
51 return "invalid basis";
54 return "singular matrix";
57 return "ill-conditioned matrix";
60 return "invalid bounds";
63 return "solver failed";
66 return "objective lower limit reached";
69 return "objective upper limit reached";
72 return "iteration limit exceeded";
75 return "time limit exceeded";
78 return "no primal feasible solution";
81 return "root LP optimum not provided";
84 return "search terminated by application";
87 return "relative mip gap tolerance reached";
90 return "no dual feasible solution";
93 return "no convergence";
96 return "numerical instability";
99 return "invalid data";
102 return "result out of range";
106 return "unknown error";
110 return "unknown error";
115 * Translate glpk status error codes to text
116 * @param retcode return code
117 * @return string with result
120 mlp_status_to_string (int retcode)
124 return "solution is undefined";
127 return "solution is feasible";
130 return "solution is infeasible";
133 return "no feasible solution exists";
136 return "solution is optimal";
139 return "solution is unbounded";
143 return "unknown error";
147 return "unknown error";
151 * Translate ATS properties to text
152 * Just intended for debugging
154 * @param ats_index the ATS index
155 * @return string with result
158 mlp_ats_to_string (int ats_index)
161 case GNUNET_ATS_ARRAY_TERMINATOR:
162 return "GNUNET_ATS_ARRAY_TERMINATOR";
164 case GNUNET_ATS_UTILIZATION_UP:
165 return "GNUNET_ATS_UTILIZATION_UP";
167 case GNUNET_ATS_UTILIZATION_DOWN:
168 return "GNUNET_ATS_UTILIZATION_DOWN";
170 case GNUNET_ATS_COST_LAN:
171 return "GNUNET_ATS_COST_LAN";
173 case GNUNET_ATS_COST_WAN:
174 return "GNUNET_ATS_COST_LAN";
176 case GNUNET_ATS_COST_WLAN:
177 return "GNUNET_ATS_COST_WLAN";
179 case GNUNET_ATS_NETWORK_TYPE:
180 return "GNUNET_ATS_NETWORK_TYPE";
182 case GNUNET_ATS_QUALITY_NET_DELAY:
183 return "GNUNET_ATS_QUALITY_NET_DELAY";
185 case GNUNET_ATS_QUALITY_NET_DISTANCE:
186 return "GNUNET_ATS_QUALITY_NET_DISTANCE";
193 return "unknown error";
197 * Find a peer in the DLL
199 * @param mlp the mlp handle
200 * @param peer the peer to find
201 * @return the peer struct
203 static struct ATS_Peer *
204 mlp_find_peer (struct GAS_MLP_Handle *mlp, const struct GNUNET_PeerIdentity *peer)
206 struct ATS_Peer *res = mlp->peer_head;
209 if (0 == memcmp (peer, &res->id, sizeof (struct GNUNET_PeerIdentity)))
217 * Intercept GLPK terminal output
218 * @param info the mlp handle
219 * @param s the string to print
220 * @return 0: glpk prints output on terminal, 0 != surpress output
223 mlp_term_hook (void *info, const char *s)
225 /* Not needed atm struct MLP_information *mlp = info; */
226 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s", s);
231 * Delete the MLP problem and free the constrain matrix
233 * @param mlp the MLP handle
236 mlp_delete_problem (struct GAS_MLP_Handle *mlp)
240 if (mlp->prob != NULL)
241 glp_delete_prob(mlp->prob);
243 /* delete row index */
246 GNUNET_free (mlp->ia);
250 /* delete column index */
253 GNUNET_free (mlp->ja);
257 /* delete coefficients */
260 GNUNET_free (mlp->ar);
269 * Add constraints that are iterating over "forall addresses"
270 * and collects all existing peers for "forall peers" constraints
272 * @param cls GAS_MLP_Handle
273 * @param key Hashcode
274 * @param value ATS_Address
276 * @return GNUNET_OK to continue
279 create_constraint_it (void *cls, const GNUNET_HashCode * key, void *value)
281 struct GAS_MLP_Handle *mlp = cls;
282 struct ATS_Address *address = value;
283 struct MLP_information *mlpi;
284 unsigned int row_index;
287 GNUNET_assert (address->mlp_information != NULL);
288 mlpi = (struct MLP_information *) address->mlp_information;
290 /* c 1) bandwidth capping
291 * b_t + (-M) * n_t <= 0
293 row_index = glp_add_rows (mlp->prob, 1);
294 mlpi->r_c1 = row_index;
296 GNUNET_asprintf(&name, "c1_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
297 glp_set_row_name (mlp->prob, row_index, name);
299 /* set row bounds: <= 0 */
300 glp_set_row_bnds (mlp->prob, row_index, GLP_UP, 0.0, 0.0);
301 mlp->ia[mlp->ci] = row_index;
302 mlp->ja[mlp->ci] = mlpi->c_b;
303 mlp->ar[mlp->ci] = 1;
306 mlp->ia[mlp->ci] = row_index;
307 mlp->ja[mlp->ci] = mlpi->c_n;
308 mlp->ar[mlp->ci] = -mlp->BIG_M;
311 /* c 3) minimum bandwidth
312 * b_t + (-n_t * b_min) >= 0
315 row_index = glp_add_rows (mlp->prob, 1);
317 GNUNET_asprintf(&name, "c3_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
318 glp_set_row_name (mlp->prob, row_index, name);
320 mlpi->r_c3 = row_index;
321 /* set row bounds: >= 0 */
322 glp_set_row_bnds (mlp->prob, row_index, GLP_LO, 0.0, 0.0);
324 mlp->ia[mlp->ci] = row_index;
325 mlp->ja[mlp->ci] = mlpi->c_b;
326 mlp->ar[mlp->ci] = 1;
329 mlp->ia[mlp->ci] = row_index;
330 mlp->ja[mlp->ci] = mlpi->c_n;
331 mlp->ar[mlp->ci] = - (double) mlp->b_min;
334 /* c 4) minimum connections
335 * (1)*n_1 + ... + (1)*n_m >= n_min
337 mlp->ia[mlp->ci] = mlp->r_c4;
338 mlp->ja[mlp->ci] = mlpi->c_n;
339 mlp->ar[mlp->ci] = 1;
342 /* c 6) maximize diversity
343 * (1)*n_1 + ... + (1)*n_m - d == 0
345 mlp->ia[mlp->ci] = mlp->r_c6;
346 mlp->ja[mlp->ci] = mlpi->c_n;
347 mlp->ar[mlp->ci] = 1;
350 /* c 10) obey network specific quotas
351 * (1)*b_1 + ... + (1)*b_m <= quota_n
356 for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
358 if (mlp->quota_index[c] == address->atsp_network_type)
360 cur_row = mlp->r_quota[c];
367 mlp->ia[mlp->ci] = cur_row;
368 mlp->ja[mlp->ci] = mlpi->c_b;
369 mlp->ar[mlp->ci] = 1;
381 * Find the required ATS information for an address
383 * @param addr the address
384 * @param ats_index the desired ATS index
386 * @return the index on success, otherwise GNUNET_SYSERR
390 mlp_lookup_ats (struct ATS_Address *addr, int ats_index)
392 struct GNUNET_ATS_Information * ats = addr->ats;
394 int found = GNUNET_NO;
395 for (c = 0; c < addr->ats_count; c++)
397 if (ats[c].type == ats_index)
403 if (found == GNUNET_YES)
406 return GNUNET_SYSERR;
410 * Adds the problem constraints for all addresses
411 * Required for problem recreation after address deletion
413 * @param mlp the mlp handle
414 * @param addresses all addresses
418 mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
420 unsigned int n_addresses;
425 n_addresses = GNUNET_CONTAINER_multihashmap_size(addresses);
427 /* Required indices in the constrain matrix
429 * feasibility constraints:
431 * c 1) bandwidth capping
432 * #rows: |n_addresses|
433 * #indices: 2 * |n_addresses|
435 * c 2) one active address per peer
437 * #indices: |n_addresses|
439 * c 3) minium bandwidth assigned
440 * #rows: |n_addresses|
441 * #indices: 2 * |n_addresses|
443 * c 4) minimum number of active connections
445 * #indices: |n_addresses|
447 * c 5) maximum ressource consumption
448 * #rows: |ressources|
449 * #indices: |n_addresses|
451 * c 10) obey network specific quota
452 * #rows: |network types
453 * #indices: |n_addresses|
455 * Sum for feasibility constraints:
456 * #rows: 3 * |n_addresses| + |ressources| + |peers| + 1
457 * #indices: 7 * |n_addresses|
459 * optimality constraints:
463 * #indices: |n_addresses| + 1
466 * #rows: |quality properties|
467 * #indices: |n_addresses| + |quality properties|
471 * #indices: |n_addresses| + 1
475 * #indices: |n_addresses| + |peers|
478 /* last +1 caused by glpk index starting with one: [1..pi]*/
479 int pi = ((7 * n_addresses) + (5 * n_addresses + mlp->m_q + mlp->c_p + 2) + 1);
484 int *ia = GNUNET_malloc (pi * sizeof (int));
488 int *ja = GNUNET_malloc (pi * sizeof (int));
492 double *ar= GNUNET_malloc (pi * sizeof (double));
495 /* Adding constraint rows
496 * This constraints are kind of "for all addresses"
497 * Feasibility constraints:
499 * c 1) bandwidth capping
500 * c 3) minimum bandwidth
501 * c 4) minimum number of connections
502 * c 6) maximize diversity
503 * c 10) obey network specific quota
506 int min = mlp->n_min;
507 if (mlp->n_min > mlp->c_p)
510 mlp->r_c4 = glp_add_rows (mlp->prob, 1);
511 glp_set_row_name (mlp->prob, mlp->r_c4, "c4");
512 glp_set_row_bnds (mlp->prob, mlp->r_c4, GLP_LO, min, min);
514 /* Add row for c6) */
516 mlp->r_c6 = glp_add_rows (mlp->prob, 1);
517 /* Set type type to fix */
518 glp_set_row_bnds (mlp->prob, mlp->r_c6, GLP_FX, 0.0, 0.0);
520 ia[mlp->ci] = mlp->r_c6 ;
521 ja[mlp->ci] = mlp->c_d;
525 /* Add rows for c 10) */
526 for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
528 mlp->r_quota[c] = glp_add_rows (mlp->prob, 1);
530 GNUNET_asprintf(&text, "quota_ats_%i", mlp->quota_index[c]);
531 glp_set_row_name (mlp->prob, mlp->r_quota[c], text);
533 /* Set bounds to 0 <= x <= quota_out */
534 glp_set_row_bnds (mlp->prob, mlp->r_quota[c], GLP_UP, 0.0, mlp->quota_out[c]);
537 GNUNET_CONTAINER_multihashmap_iterate (addresses, create_constraint_it, mlp);
539 /* Adding constraint rows
540 * This constraints are kind of "for all peers"
541 * Feasibility constraints:
543 * c 2) 1 address per peer
544 * sum (n_p1_1 + ... + n_p1_n) = 1
547 * sum (f_p * b_p1_1 + ... + f_p * b_p1_n) - u = 0
550 * V p : sum (bt_1 + ... +bt_n) - f_p * r = 0
553 /* Adding rows for c 8) */
554 mlp->r_c8 = glp_add_rows (mlp->prob, mlp->c_p);
555 glp_set_row_name (mlp->prob, mlp->r_c8, "c8");
556 /* Set row bound == 0 */
557 glp_set_row_bnds (mlp->prob, mlp->r_c8, GLP_FX, 0.0, 0.0);
560 ia[mlp->ci] = mlp->r_c8;
561 ja[mlp->ci] = mlp->c_u;
565 struct ATS_Peer * peer = mlp->peer_head;
568 struct ATS_Address *addr = peer->head;
569 struct MLP_information *mlpi = NULL;
571 /* Adding rows for c 2) */
572 peer->r_c2 = glp_add_rows (mlp->prob, 1);
573 GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&peer->id));
574 glp_set_row_name (mlp->prob, peer->r_c2, name);
576 /* Set row bound == 1 */
577 glp_set_row_bnds (mlp->prob, peer->r_c2, GLP_FX, 1.0, 1.0);
579 /* Adding rows for c 9) */
580 peer->r_c9 = glp_add_rows (mlp->prob, 1);
581 GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&peer->id));
582 glp_set_row_name (mlp->prob, peer->r_c9, name);
584 /* Set row bound == 0 */
585 glp_set_row_bnds (mlp->prob, peer->r_c9, GLP_LO, 0.0, 0.0);
588 ia[mlp->ci] = peer->r_c9;
589 ja[mlp->ci] = mlp->c_r;
597 mlpi = (struct MLP_information *) addr->mlp_information;
599 /* coefficient for c 2) */
601 ia[mlp->ci] = peer->r_c2;
602 ja[mlp->ci] = mlpi->c_n;
606 /* coefficient for c 8) */
607 ia[mlp->ci] = mlp->r_c8;
608 ja[mlp->ci] = mlpi->c_b;
609 ar[mlp->ci] = peer->f;
612 /* coefficient for c 9) */
613 ia[mlp->ci] = peer->r_c9;
614 ja[mlp->ci] = mlpi->c_b;
623 /* c 7) For all quality metrics */
625 for (c = 0; c < mlp->m_q; c++)
627 struct ATS_Peer *p = mlp->peer_head;
628 struct ATS_Address *addr = p->head;
629 struct MLP_information * mlpi;
634 /* Adding rows for c 7) */
635 mlp->r_q[c] = glp_add_rows (mlp->prob, 1);
636 GNUNET_asprintf(&name, "c7_q%i_atsi_%i", c, mlp->q[c]);
637 glp_set_row_name (mlp->prob, mlp->r_q[c], name);
639 /* Set row bound == 0 */
640 glp_set_row_bnds (mlp->prob, mlp->r_q[c], GLP_LO, 0.0, 0.0);
642 ia[mlp->ci] = mlp->r_q[c];
643 ja[mlp->ci] = mlp->c_q[c];
649 mlpi = addr->mlp_information;
650 /* lookup ATS information */
651 int index = mlp_lookup_ats(addr, mlp->q[c]);
653 if (index != GNUNET_SYSERR)
655 value = (double) addr->ats[index].value;
657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "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);
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Quality %i with ATS property `%s' not existing\n", c, mlp_ats_to_string(mlp->q[c]), index);
662 mlpi = addr->mlp_information;
664 mlpi->r_q[c] = mlp->r_q[c];
665 mlpi->c_q[c] = mlpi->c_b;
668 ia[mlp->ci] = mlp->r_q[c];
669 ja[mlp->ci] = mlpi->c_b;
670 ar[mlp->ci] = p->f * value;
682 * Add columns for all addresses
684 * @param cls GAS_MLP_Handle
685 * @param key Hashcode
686 * @param value ATS_Address
688 * @return GNUNET_OK to continue
691 create_columns_it (void *cls, const GNUNET_HashCode * key, void *value)
693 struct GAS_MLP_Handle *mlp = cls;
694 struct ATS_Address *address = value;
695 struct MLP_information *mlpi;
699 GNUNET_assert (address->mlp_information != NULL);
700 mlpi = address->mlp_information;
702 /* Add bandwidth column */
703 col = glp_add_cols (mlp->prob, 2);
708 GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
709 glp_set_col_name (mlp->prob, mlpi->c_b , name);
711 /* Lower bound == 0 */
712 glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0);
713 /* Continuous value*/
714 glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
715 /* Objective function coefficient == 0 */
716 glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
719 /* Add usage column */
720 GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
721 glp_set_col_name (mlp->prob, mlpi->c_n, name);
723 /* Limit value : 0 <= value <= 1 */
724 glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0);
726 glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
727 /* Objective function coefficient == 0 */
728 glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
736 * Create the MLP problem
738 * @param mlp the MLP handle
739 * @param addresses the hashmap containing all adresses
740 * @return GNUNET_OK or GNUNET_SYSERR
743 mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
750 GNUNET_assert (mlp->prob == NULL);
752 /* create the glpk problem */
753 mlp->prob = glp_create_prob ();
755 /* Set a problem name */
756 glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution");
758 /* Set optimization direction to maximize */
759 glp_set_obj_dir (mlp->prob, GLP_MAX);
761 /* Adding invariant columns */
763 /* Diversity d column */
765 col = glp_add_cols (mlp->prob, 1);
768 glp_set_col_name (mlp->prob, col, "d");
769 /* Column objective function coefficient */
770 glp_set_obj_coef (mlp->prob, col, mlp->co_D);
771 /* Column lower bound = 0.0 */
772 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
774 /* Utilization u column */
776 col = glp_add_cols (mlp->prob, 1);
779 glp_set_col_name (mlp->prob, col, "u");
780 /* Column objective function coefficient */
781 glp_set_obj_coef (mlp->prob, col, mlp->co_U);
782 /* Column lower bound = 0.0 */
783 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
785 /* Relativity r column */
786 col = glp_add_cols (mlp->prob, 1);
789 glp_set_col_name (mlp->prob, col, "r");
790 /* Column objective function coefficient */
791 glp_set_obj_coef (mlp->prob, col, mlp->co_R);
792 /* Column lower bound = 0.0 */
793 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
795 /* Quality metric columns */
796 col = glp_add_cols(mlp->prob, mlp->m_q);
797 for (c = 0; c < mlp->m_q; c++)
799 mlp->c_q[c] = col + c;
800 GNUNET_asprintf (&name, "q_%u", mlp->q[c]);
801 glp_set_col_name (mlp->prob, col + c, name);
802 /* Column lower bound = 0.0 */
803 glp_set_col_bnds (mlp->prob, col + c, GLP_LO, 0.0, 0.0);
805 /* Coefficient == Qm */
806 glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
809 /* Add columns for addresses */
810 GNUNET_CONTAINER_multihashmap_iterate (addresses, create_columns_it, mlp);
812 /* Add constraints */
813 mlp_add_constraints_all_addresses (mlp, addresses);
815 /* Load the matrix */
816 glp_load_matrix(mlp->prob, (mlp->ci-1), mlp->ia, mlp->ja, mlp->ar);
822 * Solves the LP problem
824 * @param mlp the MLP Handle
825 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
828 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
831 struct GNUNET_TIME_Relative duration;
832 struct GNUNET_TIME_Absolute end;
833 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
836 * Presolver is required if the problem was modified and an existing
837 * valid basis is now invalid */
838 if (mlp->presolver_required == GNUNET_YES)
839 mlp->control_param_lp.presolve = GLP_ON;
841 mlp->control_param_lp.presolve = GLP_OFF;
843 /* Solve LP problem to have initial valid solution */
845 res = glp_simplex(mlp->prob, &mlp->control_param_lp);
848 /* The LP problem instance has been successfully solved. */
850 else if (res == GLP_EITLIM)
852 /* simplex iteration limit has been exceeded. */
853 // TODO Increase iteration limit?
855 else if (res == GLP_ETMLIM)
857 /* Time limit has been exceeded. */
858 // TODO Increase time limit?
862 /* Problem was ill-defined, retry with presolver */
863 if (mlp->presolver_required == GNUNET_NO)
865 mlp->presolver_required = GNUNET_YES;
870 /* Problem was ill-defined, no way to handle that */
871 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
873 "Solving LP problem failed: %i %s\n", res, mlp_solve_to_string(res));
874 return GNUNET_SYSERR;
878 end = GNUNET_TIME_absolute_get ();
879 duration = GNUNET_TIME_absolute_get_difference (start, end);
881 mlp->lp_total_duration =+ duration.rel_value;
883 GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
884 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time", duration.rel_value, GNUNET_NO);
885 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average",
886 mlp->lp_total_duration / mlp->lp_solved, GNUNET_NO);
889 /* Analyze problem status */
890 res = glp_get_status (mlp->prob);
892 /* solution is optimal */
894 /* solution is feasible */
898 /* Problem was ill-defined, no way to handle that */
900 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
902 "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res));
903 return GNUNET_SYSERR;
907 /* solved sucessfully, no presolver required next time */
908 mlp->presolver_required = GNUNET_NO;
915 * Solves the MLP problem
917 * @param mlp the MLP Handle
918 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
921 mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
924 struct GNUNET_TIME_Relative duration;
925 struct GNUNET_TIME_Absolute end;
926 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
928 /* solve MLP problem */
929 res = glp_intopt(mlp->prob, &mlp->control_param_mlp);
933 /* The MLP problem instance has been successfully solved. */
935 else if (res == GLP_EITLIM)
937 /* simplex iteration limit has been exceeded. */
938 // TODO Increase iteration limit?
940 else if (res == GLP_ETMLIM)
942 /* Time limit has been exceeded. */
943 // TODO Increase time limit?
947 /* Problem was ill-defined, no way to handle that */
948 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
950 "Solving MLP problem failed: %s\n", mlp_solve_to_string(res));
951 return GNUNET_SYSERR;
954 end = GNUNET_TIME_absolute_get ();
955 duration = GNUNET_TIME_absolute_get_difference (start, end);
957 mlp->mlp_total_duration =+ duration.rel_value;
959 GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
960 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time", duration.rel_value, GNUNET_NO);
961 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average",
962 mlp->mlp_total_duration / mlp->mlp_solved, GNUNET_NO);
964 /* Analyze problem status */
965 res = glp_mip_status(mlp->prob);
967 /* solution is optimal */
969 /* solution is feasible */
973 /* Problem was ill-defined, no way to handle that */
975 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
977 "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res));
978 return GNUNET_SYSERR;
985 int GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp);
988 mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
990 struct GAS_MLP_Handle *mlp = cls;
992 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
994 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
998 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
1000 if (mlp->addr_in_problem != 0)
1001 GAS_mlp_solve_problem(mlp);
1006 * Solves the MLP problem
1008 * @param mlp the MLP Handle
1009 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
1012 GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp)
1015 mlp->last_execution = GNUNET_TIME_absolute_get ();
1017 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solving\n");
1024 GNUNET_asprintf(&name, "problem_%i", i);
1025 glp_write_lp (mlp->prob, 0, name);
1029 res = mlp_solve_lp_problem (mlp);
1032 GNUNET_asprintf(&name, "problem_%i_lp_solution", i);
1033 glp_print_sol (mlp->prob, name);
1037 if (res != GNUNET_OK)
1040 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "LP Problem solving failed\n");
1042 return GNUNET_SYSERR;
1045 res = mlp_solve_mlp_problem (mlp);
1048 GNUNET_asprintf(&name, "problem_%i_mlp_solution", i);
1049 glp_print_mip (mlp->prob, name);
1052 if (res != GNUNET_OK)
1055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP Problem solving failed\n");
1057 return GNUNET_SYSERR;
1061 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved\n");
1062 /* Process result */
1063 struct ATS_Peer *p = NULL;
1064 struct ATS_Address *a = NULL;
1065 struct MLP_information *mlpi = NULL;
1067 for (p = mlp->peer_head; p != NULL; p = p->next)
1069 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s'\n", GNUNET_i2s (&p->id));
1070 for (a = p->head; a != NULL; a = a->next)
1075 mlpi = a->mlp_information;
1077 b = glp_mip_col_val(mlp->prob, mlpi->c_b);
1080 n = glp_mip_col_val(mlp->prob, mlpi->c_n);
1082 mlpi->n = GNUNET_YES;
1084 mlpi->n = GNUNET_NO;
1086 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\tAddress %s %f\n",
1087 (n == 1.0) ? "[x]" : "[ ]", b);
1092 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
1094 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
1095 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
1097 mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
1102 * Init the MLP problem solving component
1104 * @param cfg the GNUNET_CONFIGURATION_Handle handle
1105 * @param stats the GNUNET_STATISTICS handle
1106 * @param max_duration maximum numbers of iterations for the LP/MLP Solver
1107 * @param max_iterations maximum time limit for the LP/MLP Solver
1108 * @return struct GAS_MLP_Handle * on success, NULL on fail
1110 struct GAS_MLP_Handle *
1111 GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1112 const struct GNUNET_STATISTICS_Handle *stats,
1113 struct GNUNET_TIME_Relative max_duration,
1114 unsigned int max_iterations)
1116 struct GAS_MLP_Handle * mlp = GNUNET_malloc (sizeof (struct GAS_MLP_Handle));
1121 long long unsigned int tmp;
1124 struct GNUNET_TIME_Relative i_exec;
1127 /* Init GLPK environment */
1128 GNUNET_assert (glp_init_env() == 0);
1130 /* Create initial MLP problem */
1131 mlp->prob = glp_create_prob();
1132 GNUNET_assert (mlp->prob != NULL);
1134 /* Get diversity coefficient from configuration */
1135 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1138 D = (double) tmp / 100;
1142 /* Get proportionality coefficient from configuration */
1143 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1146 R = (double) tmp / 100;
1150 /* Get utilization coefficient from configuration */
1151 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1154 U = (double) tmp / 100;
1158 /* Get quality metric coefficients from configuration */
1160 int i_distance = -1;
1161 int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
1162 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1164 /* initialize quality coefficients with default value 1.0 */
1168 if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
1170 if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
1174 if ((i_delay != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1175 "COEFFICIENT_QUALITY_DELAY",
1178 mlp->co_Q[i_delay] = (double) tmp / 100;
1180 mlp->co_Q[i_delay] = 1.0;
1182 if ((i_distance != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1183 "COEFFICIENT_QUALITY_DISTANCE",
1185 mlp->co_Q[i_distance] = (double) tmp / 100;
1187 mlp->co_Q[i_distance] = 1.0;
1189 /* Get minimum bandwidth per used address from configuration */
1190 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1196 b_min = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
1199 /* Get minimum number of connections from configuration */
1200 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1207 /* Init network quotas */
1208 int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
1209 for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
1211 mlp->quota_index[c] = quotas[c];
1212 static char * entry_in = NULL;
1213 static char * entry_out = NULL;
1214 unsigned long long quota_in = 0;
1215 unsigned long long quota_out = 0;
1217 switch (quotas[c]) {
1218 case GNUNET_ATS_NET_UNSPECIFIED:
1219 entry_out = "UNSPECIFIED_QUOTA_OUT";
1220 entry_in = "UNSPECIFIED_QUOTA_IN";
1222 case GNUNET_ATS_NET_LOOPBACK:
1223 entry_out = "LOOPBACK_QUOTA_OUT";
1224 entry_in = "LOOPBACK_QUOTA_IN";
1226 case GNUNET_ATS_NET_LAN:
1227 entry_out = "LAN_QUOTA_OUT";
1228 entry_in = "LAN_QUOTA_IN";
1230 case GNUNET_ATS_NET_WAN:
1231 entry_out = "WAN_QUOTA_OUT";
1232 entry_in = "WAN_QUOTA_IN";
1234 case GNUNET_ATS_NET_WLAN:
1235 entry_out = "WLAN_QUOTA_OUT";
1236 entry_in = "WLAN_QUOTA_IN";
1242 if ((entry_in == NULL) || (entry_out == NULL))
1245 if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", entry_out, "a_out))
1247 quota_out = UINT32_MAX;
1249 if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_size (cfg, "ats", entry_in, "a_in))
1251 quota_in = UINT32_MAX;
1253 /* Check if defined quota could make problem unsolvable */
1254 if ((n_min * b_min) > quota_out)
1256 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Inconsistent quota configuration value `%s': " \
1257 "outbound quota (%u Bps) too small for combination of minimum connections and minimum bandwidth per peer (%u * %u Bps = %u)\n", entry_out, quota_out, n_min, b_min, n_min * b_min);
1258 unsigned int default_min = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
1259 if ((quota_out / n_min) > default_min)
1261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Reducing minimum bandwidth per peer to %u Bps\n",
1262 (quota_out / n_min));
1263 b_min = (quota_out / n_min);
1267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Reducing minimum bandwidth per peer to %u Bps and minimum connections to %u \n",
1268 default_min, (quota_out / default_min));
1269 b_min = default_min;
1270 n_min = (quota_out / default_min);
1274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found `%s' quota %llu and `%s' quota %llu\n",
1275 entry_out, quota_out, entry_in, quota_in);
1276 mlp->quota_out[c] = quota_out;
1277 mlp->quota_in[c] = quota_in;
1280 /* Get minimum number of connections from configuration */
1281 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "ats",
1282 "ATS_EXEC_INTERVAL",
1284 mlp->exec_interval = i_exec;
1286 mlp->exec_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30);
1288 mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
1289 mlp->max_iterations = max_iterations;
1290 mlp->max_exec_duration = max_duration;
1291 mlp->auto_solve = GNUNET_YES;
1293 /* Redirect GLPK output to GNUnet logging */
1294 glp_error_hook((void *) mlp, &mlp_term_hook);
1296 /* Init LP solving parameters */
1297 glp_init_smcp(&mlp->control_param_lp);
1299 mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
1301 mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
1304 mlp->control_param_lp.it_lim = max_iterations;
1305 mlp->control_param_lp.tm_lim = max_duration.rel_value;
1307 /* Init MLP solving parameters */
1308 glp_init_iocp(&mlp->control_param_mlp);
1310 mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
1312 mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
1314 mlp->control_param_mlp.tm_lim = max_duration.rel_value;
1316 mlp->last_execution = GNUNET_TIME_absolute_get_forever();
1319 mlp->BIG_M = (double) UINT32_MAX;
1325 mlp->m_q = GNUNET_ATS_QualityPropertiesCount;
1332 * Updates a single address in the MLP problem
1334 * If the address did not exist before in the problem:
1335 * The MLP problem has to be recreated and the problem has to be resolved
1337 * Otherwise the addresses' values can be updated and the existing base can
1340 * @param mlp the MLP Handle
1341 * @param addresses the address hashmap
1342 * the address has to be already removed from the hashmap
1343 * @param address the address to update
1346 GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1349 struct MLP_information *mlpi;
1351 GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
1353 /* We add a new address */
1354 if (address->mlp_information == NULL)
1360 if (new == GNUNET_YES)
1362 mlpi = GNUNET_malloc (sizeof (struct MLP_information));
1365 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1372 address->mlp_information = mlpi;
1373 mlp->addr_in_problem ++;
1375 /* Check for and add peer */
1376 struct ATS_Peer *peer = mlp_find_peer (mlp, &address->peer);
1380 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding new peer `%s'\n",
1381 GNUNET_i2s (&address->peer));
1383 peer = GNUNET_malloc (sizeof (struct ATS_Peer));
1388 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1394 memcpy (&peer->id, &address->peer, sizeof (struct GNUNET_PeerIdentity));
1395 GNUNET_assert(address->prev == NULL);
1396 GNUNET_assert(address->next == NULL);
1397 GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1398 GNUNET_CONTAINER_DLL_insert (mlp->peer_head, mlp->peer_tail, peer);
1404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding address to peer `%s'\n",
1405 GNUNET_i2s (&address->peer));
1407 GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1413 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating existing address to peer `%s'\n",
1414 GNUNET_i2s (&address->peer));
1416 mlpi = address->mlp_information;
1418 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1420 int index = mlp_lookup_ats(address, mlp->q[c]);
1421 if ((index != GNUNET_SYSERR) && (mlpi->c_q[c] != 0) && (mlpi->r_q[c] != 0))
1423 if (mlpi->q[c] == (double) address->ats[index].value)
1426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating address for peer `%s' value `%s'from %f to %f\n",
1427 GNUNET_i2s (&address->peer),
1428 mlp_ats_to_string(mlp->q[c]),
1430 (double) address->ats[index].value);
1434 case GNUNET_ATS_QUALITY_NET_DELAY:
1435 mlpi->q[c] = (double) address->ats[index].value;
1437 case GNUNET_ATS_QUALITY_NET_DISTANCE:
1438 mlpi->q[c] = (double) address->ats[index].value;
1444 /* Get current number of columns */
1445 int cols = glp_get_num_cols(mlp->prob);
1446 int *ind = GNUNET_malloc (cols * sizeof (int));
1447 double *val = GNUNET_malloc (cols * sizeof (double));
1449 /* Get the matrix row of quality */
1450 cols = glp_get_mat_row(mlp->prob, mlp->r_q[c], ind, val);
1453 /* Get the index if matrix row of quality */
1454 for (c2 = 1; c2 <= cols; c2++ )
1457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Existing element column %i : %f\n",
1460 if ((mlpi->c_b == ind[c2]) && (val[c2] != mlpi->q[c]))
1462 /* Update the value */
1463 val[c2] = mlpi->q[c];
1465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New element column %i : %f\n",
1471 /* Get the index if matrix row of quality */
1472 glp_set_mat_row (mlp->prob, mlpi->r_q[c], cols, ind, val);
1481 if (new == GNUNET_YES)
1483 mlp_delete_problem (mlp);
1484 mlp_create_problem (mlp, addresses);
1485 mlp->presolver_required = GNUNET_YES;
1487 if (mlp->auto_solve == GNUNET_YES)
1488 GAS_mlp_solve_problem (mlp);
1492 * Deletes a single address in the MLP problem
1494 * The MLP problem has to be recreated and the problem has to be resolved
1496 * @param mlp the MLP Handle
1497 * @param addresses the address hashmap
1498 * the address has to be already removed from the hashmap
1499 * @param address the address to delete
1502 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1504 GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
1506 /* Free resources */
1507 if (address->mlp_information != NULL)
1509 GNUNET_free (address->mlp_information);
1510 address->mlp_information = NULL;
1512 mlp->addr_in_problem --;
1515 /* Remove from peer list */
1516 struct ATS_Peer *head = mlp_find_peer (mlp, &address->peer);
1517 GNUNET_assert (head != NULL);
1519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting address for `%s'\n", GNUNET_i2s (&address->peer));
1521 GNUNET_CONTAINER_DLL_remove (head->head, head->tail, address);
1522 if ((head->head == NULL) && (head->tail == NULL))
1524 /* No address for peer left, remove peer */
1526 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting peer `%s'\n", GNUNET_i2s (&address->peer));
1528 GNUNET_CONTAINER_DLL_remove (mlp->peer_head, mlp->peer_tail, head);
1533 /* Update problem */
1534 mlp_delete_problem (mlp);
1535 if ((GNUNET_CONTAINER_multihashmap_size (addresses) > 0) && (mlp->c_p > 0))
1537 mlp_create_problem (mlp, addresses);
1540 mlp->presolver_required = GNUNET_YES;
1541 if (mlp->auto_solve == GNUNET_YES)
1542 GAS_mlp_solve_problem (mlp);
1547 mlp_get_preferred_address_it (void *cls, const GNUNET_HashCode * key, void *value)
1550 struct ATS_Address **aa = (struct ATS_Address **)cls;
1551 struct ATS_Address *addr = value;
1552 struct MLP_information *mlpi = addr->mlp_information;
1553 if (mlpi->n == GNUNET_YES)
1563 * Get the preferred address for a specific peer
1565 * @param mlp the MLP Handle
1566 * @param peer the peer
1567 * @return suggested address
1569 struct ATS_Address *
1570 GAS_mlp_get_preferred_address (struct GAS_MLP_Handle *mlp,
1571 struct GNUNET_CONTAINER_MultiHashMap * addresses,
1572 const struct GNUNET_PeerIdentity *peer)
1574 struct ATS_Address * aa = NULL;
1575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting preferred address for `%s'\n", GNUNET_i2s (peer));
1576 GNUNET_CONTAINER_multihashmap_get_multiple(addresses, &peer->hashPubKey, mlp_get_preferred_address_it, &aa);
1582 * Changes the preferences for a peer in the MLP problem
1584 * @param mlp the MLP Handle
1585 * @param peer the peer
1586 * @param kind the kind to change the preference
1587 * @param score the score
1590 GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp,
1591 const struct GNUNET_PeerIdentity *peer,
1592 enum GNUNET_ATS_PreferenceKind kind,
1595 GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
1597 struct ATS_Peer *p = mlp_find_peer (mlp, peer);
1599 /* Here we have to do the matching */
1603 * Shutdown the MLP problem solving component
1604 * @param mlp the MLP handle
1607 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
1609 struct ATS_Peer * peer;
1610 struct ATS_Peer * tmp;
1612 GNUNET_assert (mlp != NULL);
1614 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
1616 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
1617 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
1620 /* clean up peer list */
1621 peer = mlp->peer_head;
1622 while (peer != NULL)
1624 GNUNET_CONTAINER_DLL_remove(mlp->peer_head, mlp->peer_tail, peer);
1629 mlp_delete_problem (mlp);
1631 /* Clean up GLPK environment */
1638 /* end of gnunet-service-ats_addresses_mlp.c */