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
38 #define ENABLE_C8 GNUNET_YES
39 #define ENABLE_C9 GNUNET_YES
41 * Translate glpk solver error codes to text
42 * @param retcode return code
43 * @return string with result
46 mlp_solve_to_string (int retcode)
52 return "invalid basis";
54 return "singular matrix";
56 return "ill-conditioned matrix";
58 return "invalid bounds";
60 return "solver failed";
62 return "objective lower limit reached";
64 return "objective upper limit reached";
66 return "iteration limit exceeded";
68 return "time limit exceeded";
70 return "no primal feasible solution";
72 return "root LP optimum not provided";
74 return "search terminated by application";
76 return "relative mip gap tolerance reached";
78 return "no dual feasible solution";
80 return "no convergence";
82 return "numerical instability";
84 return "invalid data";
86 return "result out of range";
89 return "unknown error";
95 * Translate glpk status error codes to text
96 * @param retcode return code
97 * @return string with result
100 mlp_status_to_string (int retcode)
104 return "solution is undefined";
106 return "solution is feasible";
108 return "solution is infeasible";
110 return "no feasible solution exists";
112 return "solution is optimal";
114 return "solution is unbounded";
117 return "unknown error";
122 * Translate ATS properties to text
123 * Just intended for debugging
125 * @param ats_index the ATS index
126 * @return string with result
129 mlp_ats_to_string (int ats_index)
132 case GNUNET_ATS_ARRAY_TERMINATOR:
133 return "GNUNET_ATS_ARRAY_TERMINATOR";
134 case GNUNET_ATS_UTILIZATION_UP:
135 return "GNUNET_ATS_UTILIZATION_UP";
136 case GNUNET_ATS_UTILIZATION_DOWN:
137 return "GNUNET_ATS_UTILIZATION_DOWN";
138 case GNUNET_ATS_COST_LAN:
139 return "GNUNET_ATS_COST_LAN";
140 case GNUNET_ATS_COST_WAN:
141 return "GNUNET_ATS_COST_LAN";
142 case GNUNET_ATS_COST_WLAN:
143 return "GNUNET_ATS_COST_WLAN";
144 case GNUNET_ATS_NETWORK_TYPE:
145 return "GNUNET_ATS_NETWORK_TYPE";
146 case GNUNET_ATS_QUALITY_NET_DELAY:
147 return "GNUNET_ATS_QUALITY_NET_DELAY";
148 case GNUNET_ATS_QUALITY_NET_DISTANCE:
149 return "GNUNET_ATS_QUALITY_NET_DISTANCE";
157 * Find a peer in the DLL
159 * @param mlp the mlp handle
160 * @param peer the peer to find
161 * @return the peer struct
163 static struct ATS_Peer *
164 mlp_find_peer (struct GAS_MLP_Handle *mlp, const struct GNUNET_PeerIdentity *peer)
166 struct ATS_Peer *res = mlp->peer_head;
169 if (0 == memcmp (peer, &res->id, sizeof (struct GNUNET_PeerIdentity)))
177 * Intercept GLPK terminal output
178 * @param info the mlp handle
179 * @param s the string to print
180 * @return 0: glpk prints output on terminal, 0 != surpress output
183 mlp_term_hook (void *info, const char *s)
185 /* Not needed atm struct MLP_information *mlp = info; */
186 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s", s);
191 * Delete the MLP problem and free the constrain matrix
193 * @param mlp the MLP handle
196 mlp_delete_problem (struct GAS_MLP_Handle *mlp)
200 if (mlp->prob != NULL)
201 glp_delete_prob(mlp->prob);
203 /* delete row index */
206 GNUNET_free (mlp->ia);
210 /* delete column index */
213 GNUNET_free (mlp->ja);
217 /* delete coefficients */
220 GNUNET_free (mlp->ar);
229 * Add constraints that are iterating over "forall addresses"
230 * and collects all existing peers for "forall peers" constraints
232 * @param cls GAS_MLP_Handle
233 * @param key Hashcode
234 * @param value ATS_Address
236 * @return GNUNET_OK to continue
239 create_constraint_it (void *cls, const struct GNUNET_HashCode * key, void *value)
241 struct GAS_MLP_Handle *mlp = cls;
242 struct ATS_Address *address = value;
243 struct MLP_information *mlpi;
244 unsigned int row_index;
247 GNUNET_assert (address->mlp_information != NULL);
248 mlpi = (struct MLP_information *) address->mlp_information;
250 /* c 1) bandwidth capping
251 * b_t + (-M) * n_t <= 0
253 row_index = glp_add_rows (mlp->prob, 1);
254 mlpi->r_c1 = row_index;
256 GNUNET_asprintf(&name, "c1_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
257 glp_set_row_name (mlp->prob, row_index, name);
259 /* set row bounds: <= 0 */
260 glp_set_row_bnds (mlp->prob, row_index, GLP_UP, 0.0, 0.0);
261 mlp->ia[mlp->ci] = row_index;
262 mlp->ja[mlp->ci] = mlpi->c_b;
263 mlp->ar[mlp->ci] = 1;
266 mlp->ia[mlp->ci] = row_index;
267 mlp->ja[mlp->ci] = mlpi->c_n;
268 mlp->ar[mlp->ci] = -mlp->BIG_M;
271 /* c 3) minimum bandwidth
272 * b_t + (-n_t * b_min) >= 0
275 row_index = glp_add_rows (mlp->prob, 1);
277 GNUNET_asprintf(&name, "c3_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
278 glp_set_row_name (mlp->prob, row_index, name);
280 mlpi->r_c3 = row_index;
281 /* set row bounds: >= 0 */
282 glp_set_row_bnds (mlp->prob, row_index, GLP_LO, 0.0, 0.0);
284 mlp->ia[mlp->ci] = row_index;
285 mlp->ja[mlp->ci] = mlpi->c_b;
286 mlp->ar[mlp->ci] = 1;
289 mlp->ia[mlp->ci] = row_index;
290 mlp->ja[mlp->ci] = mlpi->c_n;
291 mlp->ar[mlp->ci] = - (double) mlp->b_min;
294 /* c 4) minimum connections
295 * (1)*n_1 + ... + (1)*n_m >= n_min
297 mlp->ia[mlp->ci] = mlp->r_c4;
298 mlp->ja[mlp->ci] = mlpi->c_n;
299 mlp->ar[mlp->ci] = 1;
302 /* c 6) maximize diversity
303 * (1)*n_1 + ... + (1)*n_m - d == 0
305 mlp->ia[mlp->ci] = mlp->r_c6;
306 mlp->ja[mlp->ci] = mlpi->c_n;
307 mlp->ar[mlp->ci] = 1;
310 /* c 10) obey network specific quotas
311 * (1)*b_1 + ... + (1)*b_m <= quota_n
316 for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
318 if (mlp->quota_index[c] == address->atsp_network_type)
320 cur_row = mlp->r_quota[c];
327 mlp->ia[mlp->ci] = cur_row;
328 mlp->ja[mlp->ci] = mlpi->c_b;
329 mlp->ar[mlp->ci] = 1;
341 * Find the required ATS information for an address
343 * @param addr the address
344 * @param ats_index the desired ATS index
346 * @return the index on success, otherwise GNUNET_SYSERR
350 mlp_lookup_ats (struct ATS_Address *addr, int ats_index)
352 struct GNUNET_ATS_Information * ats = addr->ats;
354 int found = GNUNET_NO;
355 for (c = 0; c < addr->ats_count; c++)
357 if (ats[c].type == ats_index)
363 if (found == GNUNET_YES)
366 return GNUNET_SYSERR;
370 * Adds the problem constraints for all addresses
371 * Required for problem recreation after address deletion
373 * @param mlp the mlp handle
374 * @param addresses all addresses
378 mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
380 unsigned int n_addresses;
385 n_addresses = GNUNET_CONTAINER_multihashmap_size(addresses);
387 /* Required indices in the constrain matrix
389 * feasibility constraints:
391 * c 1) bandwidth capping
392 * #rows: |n_addresses|
393 * #indices: 2 * |n_addresses|
395 * c 2) one active address per peer
397 * #indices: |n_addresses|
399 * c 3) minium bandwidth assigned
400 * #rows: |n_addresses|
401 * #indices: 2 * |n_addresses|
403 * c 4) minimum number of active connections
405 * #indices: |n_addresses|
407 * c 5) maximum ressource consumption
408 * #rows: |ressources|
409 * #indices: |n_addresses|
411 * c 10) obey network specific quota
412 * #rows: |network types
413 * #indices: |n_addresses|
415 * Sum for feasibility constraints:
416 * #rows: 3 * |n_addresses| + |ressources| + |peers| + 1
417 * #indices: 7 * |n_addresses|
419 * optimality constraints:
423 * #indices: |n_addresses| + 1
426 * #rows: |quality properties|
427 * #indices: |n_addresses| + |quality properties|
431 * #indices: |n_addresses| + 1
435 * #indices: |n_addresses| + |peers|
438 /* last +1 caused by glpk index starting with one: [1..pi]*/
439 int pi = ((7 * n_addresses) + (5 * n_addresses + mlp->m_q + mlp->c_p + 2) + 1);
444 int *ia = GNUNET_malloc (pi * sizeof (int));
448 int *ja = GNUNET_malloc (pi * sizeof (int));
452 double *ar= GNUNET_malloc (pi * sizeof (double));
455 /* Adding constraint rows
456 * This constraints are kind of "for all addresses"
457 * Feasibility constraints:
459 * c 1) bandwidth capping
460 * c 3) minimum bandwidth
461 * c 4) minimum number of connections
462 * c 6) maximize diversity
463 * c 10) obey network specific quota
466 /* Row for c4) minimum connection */
467 int min = mlp->n_min;
468 /* Number of minimum connections is min(|Peers|, n_min) */
469 if (mlp->n_min > mlp->c_p)
472 mlp->r_c4 = glp_add_rows (mlp->prob, 1);
473 glp_set_row_name (mlp->prob, mlp->r_c4, "c4");
474 glp_set_row_bnds (mlp->prob, mlp->r_c4, GLP_LO, min, min);
476 /* Add row for c6) */
478 mlp->r_c6 = glp_add_rows (mlp->prob, 1);
479 /* Set type type to fix */
480 glp_set_row_bnds (mlp->prob, mlp->r_c6, GLP_FX, 0.0, 0.0);
482 ia[mlp->ci] = mlp->r_c6 ;
483 ja[mlp->ci] = mlp->c_d;
487 /* Add rows for c 10) */
488 for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
490 mlp->r_quota[c] = glp_add_rows (mlp->prob, 1);
492 GNUNET_asprintf(&text, "quota_ats_%i", mlp->quota_index[c]);
493 glp_set_row_name (mlp->prob, mlp->r_quota[c], text);
495 /* Set bounds to 0 <= x <= quota_out */
496 glp_set_row_bnds (mlp->prob, mlp->r_quota[c], GLP_UP, 0.0, mlp->quota_out[c]);
499 GNUNET_CONTAINER_multihashmap_iterate (addresses, create_constraint_it, mlp);
501 /* Adding constraint rows
502 * This constraints are kind of "for all peers"
503 * Feasibility constraints:
505 * c 2) 1 address per peer
506 * sum (n_p1_1 + ... + n_p1_n) = 1
509 * sum (f_p * b_p1_1 + ... + f_p * b_p1_n) - u = 0
512 * V p : sum (bt_1 + ... +bt_n) - f_p * r = 0
515 /* Adding rows for c 8) */
516 mlp->r_c8 = glp_add_rows (mlp->prob, mlp->c_p);
517 glp_set_row_name (mlp->prob, mlp->r_c8, "c8");
518 /* Set row bound == 0 */
519 glp_set_row_bnds (mlp->prob, mlp->r_c8, GLP_FX, 0.0, 0.0);
522 ia[mlp->ci] = mlp->r_c8;
523 ja[mlp->ci] = mlp->c_u;
527 struct ATS_Peer * peer = mlp->peer_head;
531 struct ATS_Address *addr = peer->head;
532 struct MLP_information *mlpi = NULL;
534 /* Adding rows for c 2) */
535 peer->r_c2 = glp_add_rows (mlp->prob, 1);
536 GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&peer->id));
537 glp_set_row_name (mlp->prob, peer->r_c2, name);
539 /* Set row bound == 1 */
540 glp_set_row_bnds (mlp->prob, peer->r_c2, GLP_FX, 1.0, 1.0);
542 /* Adding rows for c 9) */
544 peer->r_c9 = glp_add_rows (mlp->prob, 1);
545 GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&peer->id));
546 glp_set_row_name (mlp->prob, peer->r_c9, name);
548 /* Set row bound == 0 */
549 glp_set_row_bnds (mlp->prob, peer->r_c9, GLP_LO, 0.0, 0.0);
552 ia[mlp->ci] = peer->r_c9;
553 ja[mlp->ci] = mlp->c_r;
554 ar[mlp->ci] = -peer->f;
557 /* For all addresses of this peer */
560 mlpi = (struct MLP_information *) addr->mlp_information;
562 /* coefficient for c 2) */
563 ia[mlp->ci] = peer->r_c2;
564 ja[mlp->ci] = mlpi->c_n;
568 /* coefficient for c 8) */
569 ia[mlp->ci] = mlp->r_c8;
570 ja[mlp->ci] = mlpi->c_b;
571 ar[mlp->ci] = peer->f;
575 /* coefficient for c 9) */
576 ia[mlp->ci] = peer->r_c9;
577 ja[mlp->ci] = mlpi->c_b;
587 /* c 7) For all quality metrics */
588 for (c = 0; c < mlp->m_q; c++)
591 struct ATS_Address *ta;
592 struct MLP_information * mlpi;
595 /* Adding rows for c 7) */
596 mlp->r_q[c] = glp_add_rows (mlp->prob, 1);
597 GNUNET_asprintf(&name, "c7_q%i_%s", c, mlp_ats_to_string(mlp->q[c]));
598 glp_set_row_name (mlp->prob, mlp->r_q[c], name);
600 /* Set row bound == 0 */
601 glp_set_row_bnds (mlp->prob, mlp->r_q[c], GLP_FX, 0.0, 0.0);
603 ia[mlp->ci] = mlp->r_q[c];
604 ja[mlp->ci] = mlp->c_q[c];
608 for (tp = mlp->peer_head; tp != NULL; tp = tp->next)
609 for (ta = tp->head; ta != NULL; ta = ta->next)
611 mlpi = ta->mlp_information;
612 value = mlpi->q_averaged[c];
614 mlpi->r_q[c] = mlp->r_q[c];
616 ia[mlp->ci] = mlp->r_q[c];
617 ja[mlp->ci] = mlpi->c_b;
618 ar[mlp->ci] = tp->f_q[c] * value;
626 * Add columns for all addresses
628 * @param cls GAS_MLP_Handle
629 * @param key Hashcode
630 * @param value ATS_Address
632 * @return GNUNET_OK to continue
635 create_columns_it (void *cls, const struct GNUNET_HashCode * key, void *value)
637 struct GAS_MLP_Handle *mlp = cls;
638 struct ATS_Address *address = value;
639 struct MLP_information *mlpi;
643 GNUNET_assert (address->mlp_information != NULL);
644 mlpi = address->mlp_information;
646 /* Add bandwidth column */
647 col = glp_add_cols (mlp->prob, 2);
652 GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
653 glp_set_col_name (mlp->prob, mlpi->c_b , name);
655 /* Lower bound == 0 */
656 glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0);
657 /* Continuous value*/
658 glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
659 /* Objective function coefficient == 0 */
660 glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
663 /* Add usage column */
664 GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
665 glp_set_col_name (mlp->prob, mlpi->c_n, name);
667 /* Limit value : 0 <= value <= 1 */
668 glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0);
670 glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
671 /* Objective function coefficient == 0 */
672 glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
680 * Create the MLP problem
682 * @param mlp the MLP handle
683 * @param addresses the hashmap containing all adresses
684 * @return GNUNET_OK or GNUNET_SYSERR
687 mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
694 GNUNET_assert (mlp->prob == NULL);
696 /* create the glpk problem */
697 mlp->prob = glp_create_prob ();
699 /* Set a problem name */
700 glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution");
702 /* Set optimization direction to maximize */
703 glp_set_obj_dir (mlp->prob, GLP_MAX);
705 /* Adding invariant columns */
707 /* Diversity d column */
708 col = glp_add_cols (mlp->prob, 1);
711 glp_set_col_name (mlp->prob, col, "d");
712 /* Column objective function coefficient */
713 glp_set_obj_coef (mlp->prob, col, mlp->co_D);
714 /* Column lower bound = 0.0 */
715 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
717 /* Utilization u column */
718 col = glp_add_cols (mlp->prob, 1);
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);
728 /* Relativity r column */
729 col = glp_add_cols (mlp->prob, 1);
732 glp_set_col_name (mlp->prob, col, "r");
733 /* Column objective function coefficient */
734 glp_set_obj_coef (mlp->prob, col, mlp->co_R);
735 /* Column lower bound = 0.0 */
736 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
739 /* Quality metric columns */
740 col = glp_add_cols(mlp->prob, mlp->m_q);
741 for (c = 0; c < mlp->m_q; c++)
743 mlp->c_q[c] = col + c;
744 GNUNET_asprintf (&name, "q_%u", mlp->q[c]);
745 glp_set_col_name (mlp->prob, col + c, name);
746 /* Column lower bound = 0.0 */
747 glp_set_col_bnds (mlp->prob, col + c, GLP_LO, 0.0, 0.0);
749 /* Coefficient == Qm */
750 glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
753 /* Add columns for addresses */
754 GNUNET_CONTAINER_multihashmap_iterate (addresses, create_columns_it, mlp);
756 /* Add constraints */
757 mlp_add_constraints_all_addresses (mlp, addresses);
759 /* Load the matrix */
760 glp_load_matrix(mlp->prob, (mlp->ci-1), mlp->ia, mlp->ja, mlp->ar);
767 * Solves the LP problem
769 * @param mlp the MLP Handle
770 * @param s_ctx context to return results
771 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
774 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *s_ctx)
777 struct GNUNET_TIME_Relative duration;
778 struct GNUNET_TIME_Absolute end;
779 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
782 * Presolver is required if the problem was modified and an existing
783 * valid basis is now invalid */
784 if (mlp->presolver_required == GNUNET_YES)
785 mlp->control_param_lp.presolve = GLP_ON;
787 mlp->control_param_lp.presolve = GLP_OFF;
789 /* Solve LP problem to have initial valid solution */
791 res = glp_simplex(mlp->prob, &mlp->control_param_lp);
794 /* The LP problem instance has been successfully solved. */
796 else if (res == GLP_EITLIM)
798 /* simplex iteration limit has been exceeded. */
799 // TODO Increase iteration limit?
801 else if (res == GLP_ETMLIM)
803 /* Time limit has been exceeded. */
804 // TODO Increase time limit?
808 /* Problem was ill-defined, retry with presolver */
809 if (mlp->presolver_required == GNUNET_NO)
811 mlp->presolver_required = GNUNET_YES;
816 /* Problem was ill-defined, no way to handle that */
817 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
819 "Solving LP problem failed: %i %s\n", res, mlp_solve_to_string(res));
820 return GNUNET_SYSERR;
824 end = GNUNET_TIME_absolute_get ();
825 duration = GNUNET_TIME_absolute_get_difference (start, end);
827 mlp->lp_total_duration =+ duration.rel_value;
828 s_ctx->lp_duration = duration;
830 GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
831 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time (ms)", duration.rel_value, GNUNET_NO);
832 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average (ms)",
833 mlp->lp_total_duration / mlp->lp_solved, GNUNET_NO);
835 /* Analyze problem status */
836 res = glp_get_status (mlp->prob);
838 /* solution is optimal */
840 /* solution is feasible */
844 /* Problem was ill-defined, no way to handle that */
846 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
848 "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res));
849 return GNUNET_SYSERR;
853 /* solved sucessfully, no presolver required next time */
854 mlp->presolver_required = GNUNET_NO;
861 * Solves the MLP problem
863 * @param mlp the MLP Handle
864 * @param s_ctx context to return results
865 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
868 mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *s_ctx)
871 struct GNUNET_TIME_Relative duration;
872 struct GNUNET_TIME_Absolute end;
873 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
875 /* solve MLP problem */
876 res = glp_intopt(mlp->prob, &mlp->control_param_mlp);
880 /* The MLP problem instance has been successfully solved. */
882 else if (res == GLP_EITLIM)
884 /* simplex iteration limit has been exceeded. */
885 // TODO Increase iteration limit?
887 else if (res == GLP_ETMLIM)
889 /* Time limit has been exceeded. */
890 // TODO Increase time limit?
894 /* Problem was ill-defined, no way to handle that */
895 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
897 "Solving MLP problem failed: %s\n", mlp_solve_to_string(res));
898 return GNUNET_SYSERR;
901 end = GNUNET_TIME_absolute_get ();
902 duration = GNUNET_TIME_absolute_get_difference (start, end);
904 mlp->mlp_total_duration =+ duration.rel_value;
905 s_ctx->mlp_duration = duration;
907 GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
908 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time (ms)", duration.rel_value, GNUNET_NO);
909 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average (ms)",
910 mlp->mlp_total_duration / mlp->mlp_solved, GNUNET_NO);
912 /* Analyze problem status */
913 res = glp_mip_status(mlp->prob);
915 /* solution is optimal */
917 /* solution is feasible */
921 /* Problem was ill-defined, no way to handle that */
923 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
925 "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res));
926 return GNUNET_SYSERR;
933 int GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *ctx);
937 mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
939 struct GAS_MLP_Handle *mlp = cls;
940 struct GAS_MLP_SolutionContext ctx;
942 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
944 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
949 if (mlp->addr_in_problem != 0)
950 GAS_mlp_solve_problem(mlp, &ctx);
955 * Solves the MLP problem
957 * @param mlp the MLP Handle
958 * @param ctx solution context
959 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
962 GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp, struct GAS_MLP_SolutionContext *ctx)
965 /* Check if solving is already running */
966 if (GNUNET_YES == mlp->semaphore)
968 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
970 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
971 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
973 mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
974 return GNUNET_SYSERR;
976 mlp->semaphore = GNUNET_YES;
978 mlp->last_execution = GNUNET_TIME_absolute_get ();
980 ctx->lp_result = GNUNET_SYSERR;
981 ctx->mlp_result = GNUNET_SYSERR;
982 ctx->lp_duration = GNUNET_TIME_UNIT_FOREVER_REL;
983 ctx->mlp_duration = GNUNET_TIME_UNIT_FOREVER_REL;
985 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solve LP problem\n");
990 GNUNET_asprintf(&name, "problem_%i", i);
991 glp_write_lp (mlp->prob, 0, name);
995 res = mlp_solve_lp_problem (mlp, ctx);
996 ctx->lp_result = res;
997 if (res != GNUNET_OK)
999 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "LP Problem solving failed\n");
1000 mlp->semaphore = GNUNET_NO;
1001 return GNUNET_SYSERR;
1005 GNUNET_asprintf(&name, "problem_%i_lp_solution", i);
1006 glp_print_sol (mlp->prob, name);
1011 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Solve MLP problem\n");
1012 res = mlp_solve_mlp_problem (mlp, ctx);
1013 ctx->mlp_result = res;
1014 if (res != GNUNET_OK)
1016 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP Problem solving failed\n");
1017 mlp->semaphore = GNUNET_NO;
1018 return GNUNET_SYSERR;
1021 GNUNET_asprintf(&name, "problem_%i_mlp_solution", i);
1022 glp_print_mip (mlp->prob, name);
1026 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved %s (LP duration %llu / MLP duration %llu)\n",
1027 (GNUNET_OK == res) ? "successfully" : "failed", ctx->lp_duration.rel_value, ctx->mlp_duration.rel_value);
1028 /* Process result */
1029 struct ATS_Peer *p = NULL;
1030 struct ATS_Address *a = NULL;
1031 struct MLP_information *mlpi = NULL;
1033 for (p = mlp->peer_head; p != NULL; p = p->next)
1035 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s'\n", GNUNET_i2s (&p->id));
1036 for (a = p->head; a != NULL; a = a->next)
1041 mlpi = a->mlp_information;
1043 b = glp_mip_col_val(mlp->prob, mlpi->c_b);
1046 n = glp_mip_col_val(mlp->prob, mlpi->c_n);
1048 mlpi->n = GNUNET_YES;
1050 mlpi->n = GNUNET_NO;
1052 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\tAddress %s %f\n",
1053 (n == 1.0) ? "[x]" : "[ ]", b);
1057 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
1059 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
1060 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
1062 mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
1063 mlp->semaphore = GNUNET_NO;
1068 * Init the MLP problem solving component
1070 * @param cfg the GNUNET_CONFIGURATION_Handle handle
1071 * @param stats the GNUNET_STATISTICS handle
1072 * @param max_duration maximum numbers of iterations for the LP/MLP Solver
1073 * @param max_iterations maximum time limit for the LP/MLP Solver
1074 * @return struct GAS_MLP_Handle * on success, NULL on fail
1076 struct GAS_MLP_Handle *
1077 GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1078 const struct GNUNET_STATISTICS_Handle *stats,
1079 struct GNUNET_TIME_Relative max_duration,
1080 unsigned int max_iterations)
1082 struct GAS_MLP_Handle * mlp = GNUNET_malloc (sizeof (struct GAS_MLP_Handle));
1087 unsigned long long tmp;
1090 struct GNUNET_TIME_Relative i_exec;
1092 char * quota_out_str;
1093 char * quota_in_str;
1095 /* Init GLPK environment */
1096 int res = glp_init_env();
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
1100 "initialization successful");
1103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
1104 "environment is already initialized");
1107 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
1108 "initialization failed (insufficient memory)");
1113 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
1114 "initialization failed (unsupported programming model)");
1122 /* Create initial MLP problem */
1123 mlp->prob = glp_create_prob();
1124 GNUNET_assert (mlp->prob != NULL);
1126 mlp->BIG_M = (double) BIG_M_VALUE;
1128 /* Get diversity coefficient from configuration */
1129 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1132 D = (double) tmp / 100;
1136 /* Get proportionality coefficient from configuration */
1137 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1140 R = (double) tmp / 100;
1144 /* Get utilization coefficient from configuration */
1145 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1148 U = (double) tmp / 100;
1152 /* Get quality metric coefficients from configuration */
1154 int i_distance = -1;
1155 int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
1156 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1158 /* initialize quality coefficients with default value 1.0 */
1162 if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
1164 if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
1168 if ((i_delay != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1169 "COEFFICIENT_QUALITY_DELAY",
1172 mlp->co_Q[i_delay] = (double) tmp / 100;
1174 mlp->co_Q[i_delay] = 1.0;
1176 if ((i_distance != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1177 "COEFFICIENT_QUALITY_DISTANCE",
1179 mlp->co_Q[i_distance] = (double) tmp / 100;
1181 mlp->co_Q[i_distance] = 1.0;
1183 /* Get minimum bandwidth per used address from configuration */
1184 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1190 b_min = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
1193 /* Get minimum number of connections from configuration */
1194 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
1201 /* Init network quotas */
1202 int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
1203 for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
1205 mlp->quota_index[c] = quotas[c];
1206 static char * entry_in = NULL;
1207 static char * entry_out = NULL;
1208 unsigned long long quota_in = 0;
1209 unsigned long long quota_out = 0;
1211 switch (quotas[c]) {
1212 case GNUNET_ATS_NET_UNSPECIFIED:
1213 entry_out = "UNSPECIFIED_QUOTA_OUT";
1214 entry_in = "UNSPECIFIED_QUOTA_IN";
1216 case GNUNET_ATS_NET_LOOPBACK:
1217 entry_out = "LOOPBACK_QUOTA_OUT";
1218 entry_in = "LOOPBACK_QUOTA_IN";
1220 case GNUNET_ATS_NET_LAN:
1221 entry_out = "LAN_QUOTA_OUT";
1222 entry_in = "LAN_QUOTA_IN";
1224 case GNUNET_ATS_NET_WAN:
1225 entry_out = "WAN_QUOTA_OUT";
1226 entry_in = "WAN_QUOTA_IN";
1228 case GNUNET_ATS_NET_WLAN:
1229 entry_out = "WLAN_QUOTA_OUT";
1230 entry_in = "WLAN_QUOTA_IN";
1236 if ((entry_in == NULL) || (entry_out == NULL))
1239 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_out, "a_out_str))
1241 if (0 == strcmp(quota_out_str, BIG_M_STRING) ||
1242 (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str, "a_out)))
1243 quota_out = mlp->BIG_M;
1245 GNUNET_free (quota_out_str);
1246 quota_out_str = NULL;
1248 else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
1250 quota_out = mlp->BIG_M;
1254 quota_out = mlp->BIG_M;
1257 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_in, "a_in_str))
1259 if (0 == strcmp(quota_in_str, BIG_M_STRING) ||
1260 (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, "a_in)))
1261 quota_in = mlp->BIG_M;
1263 GNUNET_free (quota_in_str);
1264 quota_in_str = NULL;
1266 else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
1268 quota_in = mlp->BIG_M;
1272 quota_in = mlp->BIG_M;
1275 /* Check if defined quota could make problem unsolvable */
1276 if (((n_min * b_min) > quota_out) && (GNUNET_ATS_NET_UNSPECIFIED != quotas[c]))
1278 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Inconsistent quota configuration value `%s': "
1279 "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);
1286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found `%s' quota %llu and `%s' quota %llu\n",
1287 entry_out, quota_out, entry_in, quota_in);
1288 GNUNET_STATISTICS_update ((struct GNUNET_STATISTICS_Handle *) stats, entry_out, quota_out, GNUNET_NO);
1289 GNUNET_STATISTICS_update ((struct GNUNET_STATISTICS_Handle *) stats, entry_in, quota_in, GNUNET_NO);
1290 mlp->quota_out[c] = quota_out;
1291 mlp->quota_in[c] = quota_in;
1294 /* Get minimum number of connections from configuration */
1295 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "ats",
1296 "ATS_EXEC_INTERVAL",
1298 mlp->exec_interval = i_exec;
1300 mlp->exec_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30);
1302 mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
1303 mlp->max_iterations = max_iterations;
1304 mlp->max_exec_duration = max_duration;
1305 mlp->auto_solve = GNUNET_YES;
1307 /* Redirect GLPK output to GNUnet logging */
1308 glp_error_hook((void *) mlp, &mlp_term_hook);
1310 /* Init LP solving parameters */
1311 glp_init_smcp(&mlp->control_param_lp);
1313 mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
1315 mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
1318 mlp->control_param_lp.it_lim = max_iterations;
1319 mlp->control_param_lp.tm_lim = max_duration.rel_value;
1321 /* Init MLP solving parameters */
1322 glp_init_iocp(&mlp->control_param_mlp);
1324 mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
1326 mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
1328 mlp->control_param_mlp.tm_lim = max_duration.rel_value;
1330 mlp->last_execution = GNUNET_TIME_UNIT_FOREVER_ABS;
1337 mlp->m_q = GNUNET_ATS_QualityPropertiesCount;
1338 mlp->semaphore = GNUNET_NO;
1343 update_quality (struct GAS_MLP_Handle *mlp, struct ATS_Address * address)
1345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating quality metrics for peer `%s'\n",
1346 GNUNET_i2s (&address->peer));
1348 GNUNET_assert (NULL != address);
1349 GNUNET_assert (NULL != address->mlp_information);
1350 GNUNET_assert (NULL != address->ats);
1352 struct MLP_information *mlpi = address->mlp_information;
1353 struct GNUNET_ATS_Information *ats = address->ats;
1354 GNUNET_assert (mlpi != NULL);
1357 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1359 int index = mlp_lookup_ats(address, mlp->q[c]);
1361 if (index == GNUNET_SYSERR)
1364 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating address for peer `%s' value `%s': %f\n",
1365 GNUNET_i2s (&address->peer),
1366 mlp_ats_to_string(mlp->q[c]),
1367 (double) ats[index].value);
1369 int i = mlpi->q_avg_i[c];
1370 double * qp = mlpi->q[c];
1371 qp[i] = (double) ats[index].value;
1374 for (t = 0; t < MLP_AVERAGING_QUEUE_LENGTH; t++)
1376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s': `%s' queue[%u]: %f\n",
1377 GNUNET_i2s (&address->peer),
1378 mlp_ats_to_string(mlp->q[c]),
1383 if (mlpi->q_avg_i[c] + 1 < (MLP_AVERAGING_QUEUE_LENGTH))
1384 mlpi->q_avg_i[c] ++;
1386 mlpi->q_avg_i[c] = 0;
1394 case GNUNET_ATS_QUALITY_NET_DELAY:
1396 for (c2 = 0; c2 < MLP_AVERAGING_QUEUE_LENGTH; c2++)
1398 if (mlpi->q[c][c2] != -1)
1400 double * t2 = mlpi->q[c] ;
1405 if ((c3 > 0) && (avg > 0))
1406 /* avg = 1 / ((q[0] + ... + q[l]) /c3) => c3 / avg*/
1407 mlpi->q_averaged[c] = (double) c3 / avg;
1409 mlpi->q_averaged[c] = 0.0;
1411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s': `%s' average sum: %f, average: %f, weight: %f\n",
1412 GNUNET_i2s (&address->peer),
1413 mlp_ats_to_string(mlp->q[c]),
1416 mlpi->q_averaged[c]);
1419 case GNUNET_ATS_QUALITY_NET_DISTANCE:
1421 for (c2 = 0; c2 < MLP_AVERAGING_QUEUE_LENGTH; c2++)
1423 if (mlpi->q[c][c2] != -1)
1425 double * t2 = mlpi->q[c] ;
1430 if ((c3 > 0) && (avg > 0))
1431 /* avg = 1 / ((q[0] + ... + q[l]) /c3) => c3 / avg*/
1432 mlpi->q_averaged[c] = (double) c3 / avg;
1434 mlpi->q_averaged[c] = 0.0;
1436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s': `%s' average sum: %f, average: %f, weight: %f\n",
1437 GNUNET_i2s (&address->peer),
1438 mlp_ats_to_string(mlp->q[c]),
1441 mlpi->q_averaged[c]);
1448 if ((mlpi->c_b != 0) && (mlpi->r_q[c] != 0))
1451 /* Get current number of columns */
1452 int found = GNUNET_NO;
1453 int cols = glp_get_num_cols(mlp->prob);
1454 int *ind = GNUNET_malloc (cols * sizeof (int) + 1);
1455 double *val = GNUNET_malloc (cols * sizeof (double) + 1);
1457 /* Get the matrix row of quality */
1458 int length = glp_get_mat_row(mlp->prob, mlp->r_q[c], ind, val);
1459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "cols %i, length %i c_b %i\n", cols, length, mlpi->c_b);
1461 /* Get the index if matrix row of quality */
1462 for (c4 = 1; c4 <= length; c4++ )
1464 if (mlpi->c_b == ind[c4])
1466 /* Update the value */
1467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating quality `%s' column `%s' row `%s' : %f -> %f\n",
1468 mlp_ats_to_string(mlp->q[c]),
1469 glp_get_col_name (mlp->prob, ind[c4]),
1470 glp_get_row_name (mlp->prob, mlp->r_q[c]),
1472 mlpi->q_averaged[c]);
1473 val[c4] = mlpi->q_averaged[c];
1479 if (found == GNUNET_NO)
1482 ind[length+1] = mlpi->c_b;
1483 val[length+1] = mlpi->q_averaged[c];
1484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%i ind[%i] val[%i]: %i %f\n", length+1, length+1, length+1, mlpi->c_b, mlpi->q_averaged[c]);
1485 glp_set_mat_row (mlp->prob, mlpi->r_q[c], length+1, ind, val);
1489 /* Get the index if matrix row of quality */
1490 glp_set_mat_row (mlp->prob, mlpi->r_q[c], length, ind, val);
1500 * Updates a single address in the MLP problem
1502 * If the address did not exist before in the problem:
1503 * The MLP problem has to be recreated and the problem has to be resolved
1505 * Otherwise the addresses' values can be updated and the existing base can
1508 * @param mlp the MLP Handle
1509 * @param addresses the address hashmap
1510 * the address has to be already removed from the hashmap
1511 * @param address the address to update
1514 GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1517 struct MLP_information *mlpi;
1518 struct GAS_MLP_SolutionContext ctx;
1520 GNUNET_STATISTICS_update (mlp->stats, "# MLP address updates", 1, GNUNET_NO);
1522 /* We add a new address */
1523 if (address->mlp_information == NULL)
1529 if (new == GNUNET_YES)
1531 mlpi = GNUNET_malloc (sizeof (struct MLP_information));
1534 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1538 for (c2 = 0; c2 < MLP_AVERAGING_QUEUE_LENGTH; c2++)
1539 mlpi->q[c][c2] = -1.0; /* -1.0: invalid value */
1540 mlpi->q_avg_i[c] = 0;
1541 mlpi->q_averaged[c] = 0.0;
1544 address->mlp_information = mlpi;
1545 mlp->addr_in_problem ++;
1546 GNUNET_STATISTICS_update (mlp->stats, "# addresses in MLP", 1, GNUNET_NO);
1548 /* Check for and add peer */
1549 struct ATS_Peer *peer = mlp_find_peer (mlp, &address->peer);
1553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding new peer `%s'\n",
1554 GNUNET_i2s (&address->peer));
1556 peer = GNUNET_malloc (sizeof (struct ATS_Peer));
1561 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
1567 memcpy (&peer->id, &address->peer, sizeof (struct GNUNET_PeerIdentity));
1568 GNUNET_assert(address->prev == NULL);
1569 GNUNET_assert(address->next == NULL);
1570 GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1571 GNUNET_CONTAINER_DLL_insert (mlp->peer_head, mlp->peer_tail, peer);
1573 GNUNET_STATISTICS_update (mlp->stats, "# peers in MLP", 1, GNUNET_NO);
1578 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding address to peer `%s'\n",
1579 GNUNET_i2s (&address->peer));
1581 GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
1583 update_quality (mlp, address);
1587 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating existing address to peer `%s'\n",
1588 GNUNET_i2s (&address->peer));
1590 update_quality (mlp, address);
1594 if (new == GNUNET_YES)
1596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recreating problem: new address\n");
1598 mlp_delete_problem (mlp);
1599 mlp_create_problem (mlp, addresses);
1600 mlp->presolver_required = GNUNET_YES;
1602 if (mlp->auto_solve == GNUNET_YES)
1603 GAS_mlp_solve_problem (mlp, &ctx);
1607 * Deletes a single address in the MLP problem
1609 * The MLP problem has to be recreated and the problem has to be resolved
1611 * @param mlp the MLP Handle
1612 * @param addresses the address hashmap
1613 * the address has to be already removed from the hashmap
1614 * @param address the address to delete
1617 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
1619 GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
1620 struct GAS_MLP_SolutionContext ctx;
1622 /* Free resources */
1623 if (address->mlp_information != NULL)
1625 GNUNET_free (address->mlp_information);
1626 address->mlp_information = NULL;
1628 mlp->addr_in_problem --;
1629 GNUNET_STATISTICS_update (mlp->stats, "# addresses in MLP", -1, GNUNET_NO);
1632 /* Remove from peer list */
1633 struct ATS_Peer *head = mlp_find_peer (mlp, &address->peer);
1634 GNUNET_assert (head != NULL);
1636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting address for `%s'\n", GNUNET_i2s (&address->peer));
1638 GNUNET_CONTAINER_DLL_remove (head->head, head->tail, address);
1639 if ((head->head == NULL) && (head->tail == NULL))
1641 /* No address for peer left, remove peer */
1643 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting peer `%s'\n", GNUNET_i2s (&address->peer));
1645 GNUNET_CONTAINER_DLL_remove (mlp->peer_head, mlp->peer_tail, head);
1648 GNUNET_STATISTICS_update (mlp->stats, "# peers in MLP", -1, GNUNET_NO);
1651 /* Update problem */
1652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recreating problem: new address\n");
1654 mlp_delete_problem (mlp);
1655 if ((GNUNET_CONTAINER_multihashmap_size (addresses) > 0) && (mlp->c_p > 0))
1657 mlp_create_problem (mlp, addresses);
1660 mlp->presolver_required = GNUNET_YES;
1661 if (mlp->auto_solve == GNUNET_YES)
1662 GAS_mlp_solve_problem (mlp, &ctx);
1667 mlp_get_preferred_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1670 struct ATS_PreferedAddress *aa = (struct ATS_PreferedAddress *) cls;
1671 struct ATS_Address *addr = value;
1672 struct MLP_information *mlpi = addr->mlp_information;
1675 if (mlpi->n == GNUNET_YES)
1678 if (mlpi->b > (double) UINT32_MAX)
1679 aa->bandwidth_out = UINT32_MAX;
1681 aa->bandwidth_out = (uint32_t) mlpi->b;
1682 aa->bandwidth_in = 0;
1690 * Get the preferred address for a specific peer
1692 * @param mlp the MLP Handle
1693 * @param addresses address hashmap
1694 * @param peer the peer
1695 * @return suggested address
1697 struct ATS_PreferedAddress *
1698 GAS_mlp_get_preferred_address (struct GAS_MLP_Handle *mlp,
1699 struct GNUNET_CONTAINER_MultiHashMap * addresses,
1700 const struct GNUNET_PeerIdentity *peer)
1702 struct ATS_PreferedAddress * aa = GNUNET_malloc (sizeof (struct ATS_PreferedAddress));
1704 aa->bandwidth_in = 0;
1705 aa->bandwidth_out = 0;
1706 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting preferred address for `%s'\n", GNUNET_i2s (peer));
1707 GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey, mlp_get_preferred_address_it, aa);
1713 * Changes the preferences for a peer in the MLP problem
1715 * @param mlp the MLP Handle
1716 * @param peer the peer
1717 * @param kind the kind to change the preference
1718 * @param score the score
1721 GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp,
1722 const struct GNUNET_PeerIdentity *peer,
1723 enum GNUNET_ATS_PreferenceKind kind,
1726 GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
1728 struct ATS_Peer *p = mlp_find_peer (mlp, peer);
1730 /* Here we have to do the matching */
1734 * Shutdown the MLP problem solving component
1735 * @param mlp the MLP handle
1738 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
1740 struct ATS_Peer * peer;
1741 struct ATS_Address *addr;
1743 GNUNET_assert (mlp != NULL);
1745 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
1747 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
1748 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
1751 /* clean up peer list */
1752 peer = mlp->peer_head;
1753 while (peer != NULL)
1755 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up peer `%s'\n", GNUNET_i2s (&peer->id));
1756 GNUNET_CONTAINER_DLL_remove(mlp->peer_head, mlp->peer_tail, peer);
1757 for (addr = peer->head; NULL != addr; addr = peer->head)
1759 GNUNET_CONTAINER_DLL_remove(peer->head, peer->tail, addr);
1760 GNUNET_free (addr->mlp_information);
1761 addr->mlp_information = NULL;
1764 peer = mlp->peer_head;
1766 mlp_delete_problem (mlp);
1768 /* Clean up GLPK environment */
1775 /* end of gnunet-service-ats_addresses_mlp.c */