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"
36 #define DEBUG_ATS GNUNET_YES
37 #define VERY_BIG_DOUBLE_VALUE DBL_MAX
40 * Translate glpk solver error codes to text
41 * @param retcode return code
42 * @return string with result
45 mlp_solve_to_string (int retcode)
52 return "invalid basis";
55 return "singular matrix";
58 return "ill-conditioned matrix";
61 return "invalid bounds";
64 return "solver failed";
67 return "objective lower limit reached";
70 return "objective upper limit reached";
73 return "iteration limit exceeded";
76 return "time limit exceeded";
79 return "no primal feasible solution";
82 return "root LP optimum not provided";
85 return "search terminated by application";
88 return "relative mip gap tolerance reached";
91 return "no dual feasible solution";
94 return "no convergence";
97 return "numerical instability";
100 return "invalid data";
103 return "result out of range";
107 return "unknown error";
111 return "unknown error";
116 * Translate glpk status error codes to text
117 * @param retcode return code
118 * @return string with result
121 mlp_status_to_string (int retcode)
125 return "solution is undefined";
128 return "solution is feasible";
131 return "solution is infeasible";
134 return "no feasible solution exists";
137 return "solution is optimal";
140 return "solution is unbounded";
144 return "unknown error";
148 return "unknown error";
152 * Intercept GLPK terminal output
153 * @param info the mlp handle
154 * @param s the string to print
155 * @return 0: glpk prints output on terminal, 0 != surpress output
158 mlp_term_hook (void *info, const char *s)
160 /* Not needed atm struct MLP_information *mlp = info; */
161 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", s);
166 * Delete the MLP problem and free the constrain matrix
168 * @param mlp the MLP handle
171 mlp_delete_problem (struct GAS_MLP_Handle *mlp)
175 glp_delete_prob(mlp->prob);
177 /* delete row index */
179 GNUNET_free (mlp->ia);
181 /* delete column index */
183 GNUNET_free (mlp->ja);
185 /* delete coefficients */
187 GNUNET_free (mlp->ar);
192 * Adds the problem constraints for all addresses
193 * Required for problem recreation after address deletion
195 * @param addresses all addresses
199 mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
201 //double M = VERY_BIG_DOUBLE_VALUE;
202 unsigned int n_addresses;
205 n_addresses = GNUNET_CONTAINER_multihashmap_size(addresses);
207 /* Required indices in the constrain matrix
209 * feasibility constraints:
211 * c 1) bandwidth capping
212 * #rows: |n_addresses|
213 * #indices: 2 * |n_addresses|
215 * c 2) one active address per peer
217 * #indices: |n_addresses|
219 * c 3) minium bandwidth assigned
220 * #rows: |n_addresses|
221 * #indices: 2 * |n_addresses|
223 * c 4) minimum number of active connections
225 * #indices: |n_addresses|
227 * c 5) maximum ressource consumption
228 * #rows: |ressources|
229 * #indices: |n_addresses|
231 * Sum for feasibility constraints:
232 * #rows: 3 * |n_addresses| + |ressources| + |peers| + 1
233 * #indices: 7 * |n_addresses|
235 * optimality constraints:
239 int pi = (7 * n_addresses);
243 int *ia = GNUNET_malloc (pi * sizeof (int));
247 int *ja = GNUNET_malloc (pi * sizeof (int));
251 double *ar= GNUNET_malloc (pi * sizeof (double));
255 /* Adding constraint rows */
256 /* Feasibility constraints */
258 /* c 1) bandwidth capping */
263 * Create the MLP problem
265 * @param mlp the MLP handle
266 * @return GNUNET_OK or GNUNET_SYSERR
269 mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
277 /* Set a problem name */
278 glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution");
280 /* Set optimization direction to maximize */
281 glp_set_obj_dir (mlp->prob, GLP_MAX);
283 /* Adding invariant columns */
285 /* Diversity d column */
287 col = glp_add_cols (mlp->prob, 1);
290 glp_set_col_name (mlp->prob, col, "d");
291 /* Column objective function coefficient */
292 glp_set_obj_coef (mlp->prob, col, mlp->co_D);
293 /* Column lower bound = 0.0 */
294 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
296 /* Utilization u column */
298 col = glp_add_cols (mlp->prob, 1);
301 glp_set_col_name (mlp->prob, col, "u");
302 /* Column objective function coefficient */
303 glp_set_obj_coef (mlp->prob, col, mlp->co_U);
304 /* Column lower bound = 0.0 */
305 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
307 /* Relativity r column */
308 col = glp_add_cols (mlp->prob, 1);
311 glp_set_col_name (mlp->prob, col, "r");
312 /* Column objective function coefficient */
313 glp_set_obj_coef (mlp->prob, col, mlp->co_R);
314 /* Column lower bound = 0.0 */
315 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
317 /* Quality metric columns */
318 col = glp_add_cols(mlp->prob, mlp->m);
319 for (c = 0; c < mlp->m; c++)
321 mlp->c_q[c] = col + c;
322 GNUNET_asprintf (&name, "q_%u", mlp->q[c]);
323 glp_set_col_name (mlp->prob, col + c, name);
324 /* Column lower bound = 0.0 */
325 glp_set_col_bnds (mlp->prob, col + c, GLP_LO, 0.0, 0.0);
327 /* Coefficient == Qm */
328 glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
331 /* Add columns for existing addresses */
337 * Solves the LP problem
339 * @param mlp the MLP Handle
340 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
343 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
346 struct GNUNET_TIME_Relative duration;
347 struct GNUNET_TIME_Absolute end;
348 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
351 * Presolver is required if the problem was modified and an existing
352 * valid basis is now invalid */
353 if (mlp->presolver_required == GNUNET_YES)
354 mlp->control_param_lp.presolve = GLP_ON;
356 mlp->control_param_lp.presolve = GLP_OFF;
358 /* Solve LP problem to have initial valid solution */
360 res = glp_simplex(mlp->prob, &mlp->control_param_lp);
363 /* The LP problem instance has been successfully solved. */
365 else if (res == GLP_EITLIM)
367 /* simplex iteration limit has been exceeded. */
368 // TODO Increase iteration limit?
370 else if (res == GLP_ETMLIM)
372 /* Time limit has been exceeded. */
373 // TODO Increase time limit?
377 /* Problem was ill-defined, retry with presolver */
378 if (mlp->presolver_required == GNUNET_NO)
380 mlp->presolver_required = GNUNET_YES;
385 /* Problem was ill-defined, no way to handle that */
386 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
388 "Solving LP problem failed: %s\n", mlp_solve_to_string(res));
389 return GNUNET_SYSERR;
393 end = GNUNET_TIME_absolute_get ();
394 duration = GNUNET_TIME_absolute_get_difference (start, end);
396 mlp->lp_total_duration =+ duration.rel_value;
398 GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
399 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time", duration.rel_value, GNUNET_NO);
400 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average",
401 mlp->lp_total_duration / mlp->lp_solved, GNUNET_NO);
404 /* Analyze problem status */
405 res = glp_get_status (mlp->prob);
407 /* solution is optimal */
409 /* solution is feasible */
413 /* Problem was ill-defined, no way to handle that */
415 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
417 "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res));
418 return GNUNET_SYSERR;
422 /* solved sucessfully, no presolver required next time */
423 mlp->presolver_required = GNUNET_NO;
430 * Solves the MLP problem
432 * @param mlp the MLP Handle
433 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
436 mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
439 struct GNUNET_TIME_Relative duration;
440 struct GNUNET_TIME_Absolute end;
441 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
443 /* solve MLP problem */
444 res = glp_intopt(mlp->prob, &mlp->control_param_mlp);
448 /* The MLP problem instance has been successfully solved. */
450 else if (res == GLP_EITLIM)
452 /* simplex iteration limit has been exceeded. */
453 // TODO Increase iteration limit?
455 else if (res == GLP_ETMLIM)
457 /* Time limit has been exceeded. */
458 // TODO Increase time limit?
462 /* Problem was ill-defined, no way to handle that */
463 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
465 "Solving MLP problem failed: %s\n", mlp_solve_to_string(res));
466 return GNUNET_SYSERR;
469 end = GNUNET_TIME_absolute_get ();
470 duration = GNUNET_TIME_absolute_get_difference (start, end);
472 mlp->mlp_total_duration =+ duration.rel_value;
474 GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
475 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time", duration.rel_value, GNUNET_NO);
476 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average",
477 mlp->mlp_total_duration / mlp->mlp_solved, GNUNET_NO);
479 /* Analyze problem status */
480 res = glp_mip_status(mlp->prob);
482 /* solution is optimal */
484 /* solution is feasible */
488 /* Problem was ill-defined, no way to handle that */
490 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
492 "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res));
493 return GNUNET_SYSERR;
500 int mlp_solve_problem (struct GAS_MLP_Handle *mlp);
503 mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
505 struct GAS_MLP_Handle *mlp = cls;
507 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
509 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
513 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
515 if (mlp->addr_in_problem != 0)
516 mlp_solve_problem(mlp);
521 * Solves the MLP problem
523 * @param mlp the MLP Handle
524 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
527 mlp_solve_problem (struct GAS_MLP_Handle *mlp)
530 mlp->last_execution = GNUNET_TIME_absolute_get ();
531 res = mlp_solve_lp_problem (mlp);
532 if (res == GNUNET_OK)
533 res = mlp_solve_mlp_problem (mlp);
534 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
536 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
537 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
539 mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
544 * Init the MLP problem solving component
546 * @param stats the GNUNET_STATISTICS handle
547 * @param max_duration maximum numbers of iterations for the LP/MLP Solver
548 * @param max_iterations maximum time limit for the LP/MLP Solver
549 * @return struct GAS_MLP_Handle * on success, NULL on fail
551 struct GAS_MLP_Handle *
552 GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
553 const struct GNUNET_STATISTICS_Handle *stats,
554 struct GNUNET_TIME_Relative max_duration,
555 unsigned int max_iterations)
557 struct GAS_MLP_Handle * mlp = GNUNET_malloc (sizeof (struct GAS_MLP_Handle));
562 long long unsigned int tmp;
565 struct GNUNET_TIME_Relative i_exec;
567 /* Init GLPK environment */
568 GNUNET_assert (glp_init_env() == 0);
570 /* Create initial MLP problem */
571 mlp->prob = glp_create_prob();
572 GNUNET_assert (mlp->prob != NULL);
574 /* Get diversity coefficient from configuration */
575 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
578 D = (double) tmp / 100;
582 /* Get proportionality coefficient from configuration */
583 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
586 R = (double) tmp / 100;
590 /* Get utilization coefficient from configuration */
591 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
594 U = (double) tmp / 100;
598 /* Get quality metric coefficients from configuration */
601 int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
603 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
605 /* initialize quality coefficients with default value 1.0 */
609 if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
611 if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
615 if ((i_delay != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
616 "COEFFICIENT_QUALITY_DELAY",
619 mlp->co_Q[i_delay] = (double) tmp / 100;
621 mlp->co_Q[i_delay] = 1.0;
623 if ((i_distance != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
624 "COEFFICIENT_QUALITY_DISTANCE",
626 mlp->co_Q[i_distance] = (double) tmp / 100;
628 mlp->co_Q[i_distance] = 1.0;
630 /* Get minimum bandwidth per used address from configuration */
631 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
638 /* Get minimum number of connections from configuration */
639 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
646 /* Get minimum number of connections from configuration */
647 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "ats",
650 mlp->exec_interval = i_exec;
652 mlp->exec_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30);
654 mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
655 mlp->max_iterations = max_iterations;
656 mlp->max_exec_duration = max_duration;
658 /* Redirect GLPK output to GNUnet logging */
659 glp_error_hook((void *) mlp, &mlp_term_hook);
661 /* Init LP solving parameters */
662 glp_init_smcp(&mlp->control_param_lp);
664 mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
666 mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
668 mlp->control_param_lp.it_lim = max_iterations;
669 mlp->control_param_lp.tm_lim = max_duration.rel_value;
671 /* Init MLP solving parameters */
672 glp_init_iocp(&mlp->control_param_mlp);
674 mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
676 mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
678 mlp->control_param_mlp.tm_lim = max_duration.rel_value;
680 mlp->last_execution = GNUNET_TIME_absolute_get_forever();
687 mlp->m = GNUNET_ATS_QualityPropertiesCount;
693 * Updates a single address in the MLP problem
695 * If the address did not exist before in the problem:
696 * The MLP problem has to be recreated and the problem has to be resolved
698 * Otherwise the addresses' values can be updated and the existing base can
701 * @param mlp the MLP Handle
702 * @param addresses the address hashmap
703 * @param address the address to update
706 GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
710 struct MLP_information *mlpi;
713 GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
715 /* We add a new address */
716 if (address->mlp_information == NULL)
721 if (mlp->prob == NULL)
723 mlp_create_problem(mlp, addresses);
724 mlp_add_constraints_all_addresses (mlp, addresses);
728 if (new == GNUNET_YES)
730 mlpi = GNUNET_malloc (sizeof (struct MLP_information));
731 address->mlp_information = mlpi;
732 mlp->addr_in_problem ++;
734 /* Add bandwidth column */
735 col = glp_add_cols (mlp->prob, 2);
739 GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
740 glp_set_col_name (mlp->prob, mlpi->c_b , name);
742 /* Lower bound == 0 */
743 glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0);
744 /* Continuous value*/
745 glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
746 /* Objective function coefficient == 0 */
747 glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
749 /* Add usage column */
750 GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
751 glp_set_col_name (mlp->prob, mlpi->c_n, name);
753 /* Limit value : 0 <= value <= 1 */
754 glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0);
756 glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
757 /* Objective function coefficient == 0 */
758 glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
764 if (new == GNUNET_YES)
765 mlp->presolver_required = GNUNET_YES;
766 mlp_solve_problem (mlp);
770 * Deletes a single address in the MLP problem
772 * The MLP problem has to be recreated and the problem has to be resolved
774 * @param mlp the MLP Handle
775 * @param addresses the address hashmap
776 * @param address the address to delete
779 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
781 GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
784 if (address->mlp_information != NULL)
786 GNUNET_free (address->mlp_information);
787 address->mlp_information = NULL;
789 mlp->addr_in_problem --;
795 mlp->presolver_required = GNUNET_YES;
796 mlp_solve_problem (mlp);
800 * Deletes a single address in the MLP problem
802 * @param mlp the MLP Handle
803 * @param addresses the address hashmap
804 * @param address the address to change the preference
807 GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
809 GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
813 * Shutdown the MLP problem solving component
814 * @param mlp the MLP handle
817 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
819 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
821 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
822 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
825 mlp_delete_problem (mlp);
827 /* Clean up GLPK environment */
834 /* end of gnunet-service-ats_addresses_mlp.c */