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 * Find a peer in the DLL
152 * @param the peer to find
153 * @return the peer struct
155 static struct ATS_Peer *
156 mlp_find_peer (struct GAS_MLP_Handle *mlp, const struct GNUNET_PeerIdentity *peer)
158 struct ATS_Peer *res = mlp->peer_head;
161 if (0 == memcmp (peer, &res->id, sizeof (struct GNUNET_PeerIdentity)))
169 * Intercept GLPK terminal output
170 * @param info the mlp handle
171 * @param s the string to print
172 * @return 0: glpk prints output on terminal, 0 != surpress output
175 mlp_term_hook (void *info, const char *s)
177 /* Not needed atm struct MLP_information *mlp = info; */
178 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", s);
183 * Delete the MLP problem and free the constrain matrix
185 * @param mlp the MLP handle
188 mlp_delete_problem (struct GAS_MLP_Handle *mlp)
192 if (mlp->prob != NULL)
193 glp_delete_prob(mlp->prob);
195 /* delete row index */
198 GNUNET_free (mlp->ia);
202 /* delete column index */
205 GNUNET_free (mlp->ja);
209 /* delete coefficients */
212 GNUNET_free (mlp->ar);
221 * Add constraints that are iterating over "forall addresses"
222 * and collects all existing peers for "forall peers" constraints
224 * @param cls GAS_MLP_Handle
225 * @param key Hashcode
226 * @param value ATS_Address
228 * @return GNUNET_OK to continue
231 create_constraint_it (void *cls, const GNUNET_HashCode * key, void *value)
233 struct GAS_MLP_Handle *mlp = cls;
234 struct ATS_Address *address = value;
235 struct MLP_information *mlpi;
236 unsigned int row_index;
239 GNUNET_assert (address->mlp_information != NULL);
240 mlpi = (struct MLP_information *) address->mlp_information;
242 /* c 1) bandwidth capping
243 * b_t + (-M) * n_t <= 0
245 row_index = glp_add_rows (mlp->prob, 1);
246 mlpi->r_c1 = row_index;
248 GNUNET_asprintf(&name, "c1_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
249 glp_set_row_name (mlp->prob, row_index, name);
251 /* set row bounds: <= 0 */
252 glp_set_row_bnds (mlp->prob, row_index, GLP_UP, 0.0, 0.0);
253 mlp->ia[mlp->ci] = row_index;
254 mlp->ja[mlp->ci] = mlpi->c_b;
255 mlp->ar[mlp->ci] = 1;
258 mlp->ia[mlp->ci] = row_index;
259 mlp->ja[mlp->ci] = mlpi->c_n;
260 mlp->ar[mlp->ci] = -mlp->BIG_M;
263 /* c 3) minimum bandwidth
264 * b_t + (-n_t * b_min) >= 0
267 row_index = glp_add_rows (mlp->prob, 1);
269 GNUNET_asprintf(&name, "c3_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
270 glp_set_row_name (mlp->prob, row_index, name);
272 mlpi->r_c3 = row_index;
273 /* set row bounds: >= 0 */
274 glp_set_row_bnds (mlp->prob, row_index, GLP_LO, 0.0, 0.0);
276 mlp->ia[mlp->ci] = row_index;
277 mlp->ja[mlp->ci] = mlpi->c_b;
278 mlp->ar[mlp->ci] = 1;
281 mlp->ia[mlp->ci] = row_index;
282 mlp->ja[mlp->ci] = mlpi->c_n;
283 mlp->ar[mlp->ci] = - (double) mlp->b_min;
286 /* c 4) minimum connections
287 * (1)*n_1 + ... + (1)*n_m >= n_min
289 mlp->ia[mlp->ci] = mlp->r_c4;
290 mlp->ja[mlp->ci] = mlpi->c_n;
291 mlp->ar[mlp->ci] = 1;
294 /* c 6) maximize diversity
295 * (1)*n_1 + ... + (1)*n_m - d == 0
297 mlp->ia[mlp->ci] = mlp->r_c6;
298 mlp->ja[mlp->ci] = mlpi->c_n;
299 mlp->ar[mlp->ci] = 1;
307 * Adds the problem constraints for all addresses
308 * Required for problem recreation after address deletion
310 * @param addresses all addresses
314 mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
316 unsigned int n_addresses;
321 n_addresses = GNUNET_CONTAINER_multihashmap_size(addresses);
323 /* Required indices in the constrain matrix
325 * feasibility constraints:
327 * c 1) bandwidth capping
328 * #rows: |n_addresses|
329 * #indices: 2 * |n_addresses|
331 * c 2) one active address per peer
333 * #indices: |n_addresses|
335 * c 3) minium bandwidth assigned
336 * #rows: |n_addresses|
337 * #indices: 2 * |n_addresses|
339 * c 4) minimum number of active connections
341 * #indices: |n_addresses|
343 * c 5) maximum ressource consumption
344 * #rows: |ressources|
345 * #indices: |n_addresses|
347 * Sum for feasibility constraints:
348 * #rows: 3 * |n_addresses| + |ressources| + |peers| + 1
349 * #indices: 7 * |n_addresses|
351 * optimality constraints:
355 * #indices: |n_addresses| + 1
358 * #rows: |quality properties|
359 * #indices: |n_addresses| + |quality properties|
363 * #indices: |n_addresses| + 1
367 * #indices: |n_addresses| + |peers|
370 /* last +1 caused by glpk index starting with one */
371 int pi = ((7 * n_addresses) + (4 * n_addresses + mlp->m_q + mlp->c_p + 2) + 1);
376 int *ia = GNUNET_malloc (pi * sizeof (int));
380 int *ja = GNUNET_malloc (pi * sizeof (int));
384 double *ar= GNUNET_malloc (pi * sizeof (double));
387 /* Adding constraint rows
388 * This constraints are kind of "for all addresses"
389 * Feasibility constraints:
391 * c 1) bandwidth capping
392 * c 3) minimum bandwidth
393 * c 4) minimum number of connections
394 * c 6) maximize diversity
397 int min = mlp->n_min;
398 if (mlp->n_min > mlp->c_p)
401 mlp->r_c4 = glp_add_rows (mlp->prob, 1);
402 glp_set_row_name (mlp->prob, mlp->r_c4, "c4");
403 glp_set_row_bnds (mlp->prob, mlp->r_c4, GLP_LO, min, min);
405 /* Add row for c6) */
407 mlp->r_c6 = glp_add_rows (mlp->prob, 1);
408 /* Set type type to fix */
409 glp_set_row_bnds (mlp->prob, mlp->r_c6, GLP_FX, 0.0, 0.0);
411 ia[mlp->ci] = mlp->r_c6 ;
412 ja[mlp->ci] = mlp->c_d;
416 GNUNET_CONTAINER_multihashmap_iterate (addresses, create_constraint_it, mlp);
418 /* Adding constraint rows
419 * This constraints are kind of "for all peers"
420 * Feasibility constraints:
422 * c 2) 1 address per peer
423 * sum (n_p1_1 + ... + n_p1_n) = 1
426 * sum (f_p * b_p1_1 + ... + f_p * b_p1_n) - u = 0
429 * V p : sum (bt_1 + ... +bt_n) - f_p * r = 0
432 /* Adding rows for c 8) */
433 mlp->r_c8 = glp_add_rows (mlp->prob, mlp->c_p);
434 glp_set_row_name (mlp->prob, mlp->r_c8, "c8");
435 /* Set row bound == 0 */
436 glp_set_row_bnds (mlp->prob, mlp->r_c8, GLP_FX, 0.0, 0.0);
438 ia[mlp->ci] = mlp->r_c8;
439 ja[mlp->ci] = mlp->c_u;
443 struct ATS_Peer * peer = mlp->peer_head;
446 struct ATS_Address *addr = peer->head;
447 struct MLP_information *mlpi = NULL;
449 /* Adding rows for c 2) */
450 peer->r_c2 = glp_add_rows (mlp->prob, 1);
451 GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&peer->id));
452 glp_set_row_name (mlp->prob, peer->r_c2, name);
454 /* Set row bound == 1 */
455 glp_set_row_bnds (mlp->prob, peer->r_c2, GLP_FX, 1.0, 1.0);
457 /* Adding rows for c 9) */
458 peer->r_c9 = glp_add_rows (mlp->prob, 1);
459 GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&peer->id));
460 glp_set_row_name (mlp->prob, peer->r_c9, name);
462 /* Set row bound == 0 */
463 glp_set_row_bnds (mlp->prob, peer->r_c9, GLP_LO, 0.0, 0.0);
466 ia[mlp->ci] = peer->r_c9;
467 ja[mlp->ci] = mlp->c_r;
473 mlpi = (struct MLP_information *) addr->mlp_information;
475 ia[mlp->ci] = peer->r_c2;
476 ja[mlp->ci] = mlpi->c_n;
480 ia[mlp->ci] = mlp->r_c8;
481 ja[mlp->ci] = mlpi->c_b;
482 ar[mlp->ci] = peer->f;
485 ia[mlp->ci] = peer->r_c9;
486 ja[mlp->ci] = mlpi->c_b;
495 /* c 7) For all quality metrics */
497 for (c = 0; c < mlp->m_q; c++)
499 struct ATS_Peer *p = mlp->peer_head;
500 struct ATS_Address *addr = p->head;
501 struct MLP_information * mlpi;
506 /* Adding rows for c 7) */
507 mlp->r_q[c] = glp_add_rows (mlp->prob, 1);
508 GNUNET_asprintf(&name, "c7_q%i_atsi_%i", c, mlp->q[c]);
509 glp_set_row_name (mlp->prob, mlp->r_q[c], name);
511 /* Set row bound == 0 */
512 glp_set_row_bnds (mlp->prob, mlp->r_q[c], GLP_LO, 0.0, 0.0);
515 ia[mlp->ci] = mlp->r_q[c];
516 ja[mlp->ci] = mlp->c_q[c];
522 mlpi = addr->mlp_information;
523 ia[mlp->ci] = mlp->r_q[c];
524 ja[mlp->ci] = mlpi->c_b;
525 ar[mlp->ci] = p->f * value;
537 * Add columns for all addresses
539 * @param cls GAS_MLP_Handle
540 * @param key Hashcode
541 * @param value ATS_Address
543 * @return GNUNET_OK to continue
546 create_columns_it (void *cls, const GNUNET_HashCode * key, void *value)
548 struct GAS_MLP_Handle *mlp = cls;
549 struct ATS_Address *address = value;
550 struct MLP_information *mlpi;
554 GNUNET_assert (address->mlp_information != NULL);
555 mlpi = address->mlp_information;
557 /* Add bandwidth column */
558 col = glp_add_cols (mlp->prob, 2);
562 GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
563 glp_set_col_name (mlp->prob, mlpi->c_b , name);
565 /* Lower bound == 0 */
566 glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0);
567 /* Continuous value*/
568 glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
569 /* Objective function coefficient == 0 */
570 glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
573 /* Add usage column */
574 GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
575 glp_set_col_name (mlp->prob, mlpi->c_n, name);
577 /* Limit value : 0 <= value <= 1 */
578 glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0);
580 glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
581 /* Objective function coefficient == 0 */
582 glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
590 * Create the MLP problem
592 * @param mlp the MLP handle
593 * @return GNUNET_OK or GNUNET_SYSERR
596 mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
603 GNUNET_assert (mlp->prob == NULL);
605 /* create the glpk problem */
606 mlp->prob = glp_create_prob ();
608 /* Set a problem name */
609 glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution");
611 /* Set optimization direction to maximize */
612 glp_set_obj_dir (mlp->prob, GLP_MAX);
614 /* Adding invariant columns */
616 /* Diversity d column */
618 col = glp_add_cols (mlp->prob, 1);
621 glp_set_col_name (mlp->prob, col, "d");
622 /* Column objective function coefficient */
623 glp_set_obj_coef (mlp->prob, col, mlp->co_D);
624 /* Column lower bound = 0.0 */
625 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
627 /* Utilization u column */
629 col = glp_add_cols (mlp->prob, 1);
632 glp_set_col_name (mlp->prob, col, "u");
633 /* Column objective function coefficient */
634 glp_set_obj_coef (mlp->prob, col, mlp->co_U);
635 /* Column lower bound = 0.0 */
636 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
638 /* Relativity r column */
639 col = glp_add_cols (mlp->prob, 1);
642 glp_set_col_name (mlp->prob, col, "r");
643 /* Column objective function coefficient */
644 glp_set_obj_coef (mlp->prob, col, mlp->co_R);
645 /* Column lower bound = 0.0 */
646 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
648 /* Quality metric columns */
649 col = glp_add_cols(mlp->prob, mlp->m_q);
650 for (c = 0; c < mlp->m_q; c++)
652 mlp->c_q[c] = col + c;
653 GNUNET_asprintf (&name, "q_%u", mlp->q[c]);
654 glp_set_col_name (mlp->prob, col + c, name);
655 /* Column lower bound = 0.0 */
656 glp_set_col_bnds (mlp->prob, col + c, GLP_LO, 0.0, 0.0);
658 /* Coefficient == Qm */
659 glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
662 /* Add columns for addresses */
663 GNUNET_CONTAINER_multihashmap_iterate (addresses, create_columns_it, mlp);
665 /* Add constraints */
666 mlp_add_constraints_all_addresses (mlp, addresses);
668 /* Load the matrix */
669 glp_load_matrix(mlp->prob, (mlp->ci-1), mlp->ia, mlp->ja, mlp->ar);
675 * Solves the LP problem
677 * @param mlp the MLP Handle
678 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
681 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
684 struct GNUNET_TIME_Relative duration;
685 struct GNUNET_TIME_Absolute end;
686 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
689 * Presolver is required if the problem was modified and an existing
690 * valid basis is now invalid */
691 if (mlp->presolver_required == GNUNET_YES)
692 mlp->control_param_lp.presolve = GLP_ON;
694 mlp->control_param_lp.presolve = GLP_OFF;
696 /* Solve LP problem to have initial valid solution */
698 res = glp_simplex(mlp->prob, &mlp->control_param_lp);
701 /* The LP problem instance has been successfully solved. */
703 else if (res == GLP_EITLIM)
705 /* simplex iteration limit has been exceeded. */
706 // TODO Increase iteration limit?
708 else if (res == GLP_ETMLIM)
710 /* Time limit has been exceeded. */
711 // TODO Increase time limit?
715 /* Problem was ill-defined, retry with presolver */
716 if (mlp->presolver_required == GNUNET_NO)
718 mlp->presolver_required = GNUNET_YES;
723 /* Problem was ill-defined, no way to handle that */
724 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
726 "Solving LP problem failed: %i %s\n", res, mlp_solve_to_string(res));
727 return GNUNET_SYSERR;
731 end = GNUNET_TIME_absolute_get ();
732 duration = GNUNET_TIME_absolute_get_difference (start, end);
734 mlp->lp_total_duration =+ duration.rel_value;
736 GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
737 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time", duration.rel_value, GNUNET_NO);
738 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average",
739 mlp->lp_total_duration / mlp->lp_solved, GNUNET_NO);
742 /* Analyze problem status */
743 res = glp_get_status (mlp->prob);
745 /* solution is optimal */
747 /* solution is feasible */
751 /* Problem was ill-defined, no way to handle that */
753 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
755 "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res));
756 return GNUNET_SYSERR;
760 /* solved sucessfully, no presolver required next time */
761 mlp->presolver_required = GNUNET_NO;
768 * Solves the MLP problem
770 * @param mlp the MLP Handle
771 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
774 mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
777 struct GNUNET_TIME_Relative duration;
778 struct GNUNET_TIME_Absolute end;
779 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
781 /* solve MLP problem */
782 res = glp_intopt(mlp->prob, &mlp->control_param_mlp);
786 /* The MLP problem instance has been successfully solved. */
788 else if (res == GLP_EITLIM)
790 /* simplex iteration limit has been exceeded. */
791 // TODO Increase iteration limit?
793 else if (res == GLP_ETMLIM)
795 /* Time limit has been exceeded. */
796 // TODO Increase time limit?
800 /* Problem was ill-defined, no way to handle that */
801 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
803 "Solving MLP problem failed: %s\n", mlp_solve_to_string(res));
804 return GNUNET_SYSERR;
807 end = GNUNET_TIME_absolute_get ();
808 duration = GNUNET_TIME_absolute_get_difference (start, end);
810 mlp->mlp_total_duration =+ duration.rel_value;
812 GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
813 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time", duration.rel_value, GNUNET_NO);
814 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average",
815 mlp->mlp_total_duration / mlp->mlp_solved, GNUNET_NO);
817 /* Analyze problem status */
818 res = glp_mip_status(mlp->prob);
820 /* solution is optimal */
822 /* solution is feasible */
826 /* Problem was ill-defined, no way to handle that */
828 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
830 "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res));
831 return GNUNET_SYSERR;
838 int mlp_solve_problem (struct GAS_MLP_Handle *mlp);
841 mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
843 struct GAS_MLP_Handle *mlp = cls;
845 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
847 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
851 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
853 if (mlp->addr_in_problem != 0)
854 mlp_solve_problem(mlp);
859 * Solves the MLP problem
861 * @param mlp the MLP Handle
862 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
865 mlp_solve_problem (struct GAS_MLP_Handle *mlp)
868 mlp->last_execution = GNUNET_TIME_absolute_get ();
870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solving\n");
877 GNUNET_asprintf(&name, "problem_%i", i);
878 glp_write_lp (mlp->prob, 0, name);
882 res = mlp_solve_lp_problem (mlp);
885 GNUNET_asprintf(&name, "problem_%i_lp_solution", i);
886 glp_print_sol (mlp->prob, name);
890 if (res != GNUNET_OK)
893 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "LP Problem solving failed\n");
895 return GNUNET_SYSERR;
898 res = mlp_solve_mlp_problem (mlp);
901 GNUNET_asprintf(&name, "problem_%i_mlp_solution", i);
902 glp_print_mip (mlp->prob, name);
905 if (res != GNUNET_OK)
908 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MLP Problem solving failed\n");
910 return GNUNET_SYSERR;
914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved\n");
919 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
921 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
922 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
924 mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
929 * Init the MLP problem solving component
931 * @param stats the GNUNET_STATISTICS handle
932 * @param max_duration maximum numbers of iterations for the LP/MLP Solver
933 * @param max_iterations maximum time limit for the LP/MLP Solver
934 * @return struct GAS_MLP_Handle * on success, NULL on fail
936 struct GAS_MLP_Handle *
937 GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
938 const struct GNUNET_STATISTICS_Handle *stats,
939 struct GNUNET_TIME_Relative max_duration,
940 unsigned int max_iterations)
942 struct GAS_MLP_Handle * mlp = GNUNET_malloc (sizeof (struct GAS_MLP_Handle));
947 long long unsigned int tmp;
950 struct GNUNET_TIME_Relative i_exec;
952 /* Init GLPK environment */
953 GNUNET_assert (glp_init_env() == 0);
955 /* Create initial MLP problem */
956 mlp->prob = glp_create_prob();
957 GNUNET_assert (mlp->prob != NULL);
959 /* Get diversity coefficient from configuration */
960 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
963 D = (double) tmp / 100;
967 /* Get proportionality coefficient from configuration */
968 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
971 R = (double) tmp / 100;
975 /* Get utilization coefficient from configuration */
976 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
979 U = (double) tmp / 100;
983 /* Get quality metric coefficients from configuration */
986 int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
988 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
990 /* initialize quality coefficients with default value 1.0 */
994 if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
996 if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
1000 if ((i_delay != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1001 "COEFFICIENT_QUALITY_DELAY",
1004 mlp->co_Q[i_delay] = (double) tmp / 100;
1006 mlp->co_Q[i_delay] = 1.0;
1008 if ((i_distance != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1009 "COEFFICIENT_QUALITY_DISTANCE",
1011 mlp->co_Q[i_distance] = (double) tmp / 100;
1013 mlp->co_Q[i_distance] = 1.0;
1015 /* Get minimum bandwidth per used address from configuration */
1016 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1023 /* Get minimum number of connections from configuration */
1024 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1031 /* Get minimum number of connections from configuration */
1032 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "ats",
1033 "ATS_EXEC_INTERVAL",
1035 mlp->exec_interval = i_exec;
1037 mlp->exec_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30);
1039 mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
1040 mlp->max_iterations = max_iterations;
1041 mlp->max_exec_duration = max_duration;
1043 /* Redirect GLPK output to GNUnet logging */
1044 glp_error_hook((void *) mlp, &mlp_term_hook);
1046 /* Init LP solving parameters */
1047 glp_init_smcp(&mlp->control_param_lp);
1049 mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
1051 mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
1053 mlp->control_param_lp.it_lim = max_iterations;
1054 mlp->control_param_lp.tm_lim = max_duration.rel_value;
1056 /* Init MLP solving parameters */
1057 glp_init_iocp(&mlp->control_param_mlp);
1059 mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
1061 mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
1063 mlp->control_param_mlp.tm_lim = max_duration.rel_value;
1065 mlp->last_execution = GNUNET_TIME_absolute_get_forever();
1068 mlp->BIG_M = (double) UINT32_MAX;
1074 mlp->m_q = GNUNET_ATS_QualityPropertiesCount;
1080 * Updates a single address in the MLP problem
1082 * If the address did not exist before in the problem:
1083 * The MLP problem has to be recreated and the problem has to be resolved
1085 * Otherwise the addresses' values can be updated and the existing base can
1088 * @param mlp the MLP Handle
1089 * @param addresses the address hashmap
1090 * the address has to be already removed from the hashmap
1091 * @param address the address to update
1094 GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1097 struct MLP_information *mlpi;
1100 GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
1102 /* We add a new address */
1103 if (address->mlp_information == NULL)
1109 if (new == GNUNET_YES)
1111 mlpi = GNUNET_malloc (sizeof (struct MLP_information));
1112 address->mlp_information = mlpi;
1113 mlp->addr_in_problem ++;
1115 /* Check for and add peer */
1116 struct ATS_Peer *peer = mlp_find_peer (mlp, &address->peer);
1120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding new peer `%s'\n", GNUNET_i2s (&address->peer));
1122 peer = GNUNET_malloc (sizeof (struct ATS_Peer));
1126 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1132 memcpy (&peer->id, &address->peer, sizeof (struct GNUNET_PeerIdentity));
1133 GNUNET_assert(address->prev == NULL);
1134 GNUNET_assert(address->next == NULL);
1135 GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1136 GNUNET_CONTAINER_DLL_insert (mlp->peer_head, mlp->peer_tail, peer);
1142 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding address to peer `%s'\n", GNUNET_i2s (&address->peer));
1144 GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1150 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Updating existing address to peer `%s'\n", GNUNET_i2s (&address->peer));
1152 mlpi = address->mlp_information;
1157 if (new == GNUNET_YES)
1159 mlp_delete_problem (mlp);
1160 mlp_create_problem (mlp, addresses);
1161 mlp->presolver_required = GNUNET_YES;
1163 mlp_solve_problem (mlp);
1167 * Deletes a single address in the MLP problem
1169 * The MLP problem has to be recreated and the problem has to be resolved
1171 * @param mlp the MLP Handle
1172 * @param addresses the address hashmap
1173 * the address has to be already removed from the hashmap
1174 * @param address the address to delete
1177 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1179 GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
1181 /* Free resources */
1182 if (address->mlp_information != NULL)
1184 GNUNET_free (address->mlp_information);
1185 address->mlp_information = NULL;
1187 mlp->addr_in_problem --;
1190 /* Remove from peer list */
1191 struct ATS_Peer *head = mlp_find_peer (mlp, &address->peer);
1192 GNUNET_assert (head != NULL);
1194 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deleting address for `%s'\n", GNUNET_i2s (&address->peer));
1196 GNUNET_CONTAINER_DLL_remove (head->head, head->tail, address);
1197 if ((head->head == NULL) && (head->tail == NULL))
1199 /* No address for peer left, remove peer */
1201 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deleting peer `%s'\n", GNUNET_i2s (&address->peer));
1203 GNUNET_CONTAINER_DLL_remove (mlp->peer_head, mlp->peer_tail, head);
1208 /* Update problem */
1209 mlp_delete_problem (mlp);
1210 if ((GNUNET_CONTAINER_multihashmap_size (addresses) > 0) && (mlp->c_p > 0))
1212 mlp_create_problem (mlp, addresses);
1215 mlp->presolver_required = GNUNET_YES;
1216 mlp_solve_problem (mlp);
1221 * Changes the preferences for a peer in the MLP problem
1223 * @param mlp the MLP Handle
1224 * @param peer the peer
1225 * @param kind the kind to change the preference
1226 * @param float the score
1229 GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp,
1230 const struct GNUNET_PeerIdentity *peer,
1231 enum GNUNET_ATS_PreferenceKind kind,
1234 GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
1236 struct ATS_Peer *p = mlp_find_peer (mlp, peer);
1238 /* Here we have to do the matching */
1242 * Shutdown the MLP problem solving component
1243 * @param mlp the MLP handle
1246 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
1248 struct ATS_Peer * peer;
1249 struct ATS_Peer * tmp;
1251 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
1253 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
1254 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
1257 /* clean up peer list */
1260 peer = mlp->peer_head;
1261 while (peer != NULL)
1263 GNUNET_CONTAINER_DLL_remove(mlp->peer_head, mlp->peer_tail, peer);
1269 mlp_delete_problem (mlp);
1271 /* Clean up GLPK environment */
1278 /* end of gnunet-service-ats_addresses_mlp.c */