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
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 * Intercept GLPK terminal output
152 * @param info the mlp handle
153 * @param s the string to print
154 * @return 0: glpk prints output on terminal, 0 != surpress output
157 mlp_term_hook (void *info, const char *s)
159 /* Not needed atm struct MLP_information *mlp = info; */
160 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", s);
165 * Create the MLP problem
167 * @param mlp the MLP handle
168 * @return GNUNET_OK or GNUNET_SYSERR
171 mlp_create_problem (struct GAS_MLP_Handle *mlp)
178 /* Set a problem name */
179 glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution");
181 /* Set optimization direction to maximize */
182 glp_set_obj_dir (mlp->prob, GLP_MAX);
184 /* Adding invariant columns */
186 /* Diversity d column */
188 col = glp_add_cols (mlp->prob, 1);
191 glp_set_col_name (mlp->prob, col, "d");
192 /* Column objective function coefficient */
193 glp_set_obj_coef (mlp->prob, col, mlp->co_D);
194 /* Column lower bound = 0.0 */
195 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
197 /* Utilization u column */
199 col = glp_add_cols (mlp->prob, 1);
202 glp_set_col_name (mlp->prob, col, "u");
203 /* Column objective function coefficient */
204 glp_set_obj_coef (mlp->prob, col, mlp->co_U);
205 /* Column lower bound = 0.0 */
206 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
208 /* Relativity r column */
209 col = glp_add_cols (mlp->prob, 1);
212 glp_set_col_name (mlp->prob, col, "r");
213 /* Column objective function coefficient */
214 glp_set_obj_coef (mlp->prob, col, mlp->co_R);
215 /* Column lower bound = 0.0 */
216 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
218 /* Quality metric columns */
219 col = glp_add_cols(mlp->prob, mlp->m);
220 for (c = 0; c < mlp->m; c++)
222 mlp->c_q[c] = col + c;
223 GNUNET_asprintf (&name, "q_%u", mlp->q[c]);
224 glp_set_col_name (mlp->prob, col + c, name);
225 /* Column lower bound = 0.0 */
226 glp_set_col_bnds (mlp->prob, col + c, GLP_LO, 0.0, 0.0);
228 /* Coefficient == Qm */
229 glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
236 * Solves the LP problem
238 * @param mlp the MLP Handle
239 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
242 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
245 struct GNUNET_TIME_Relative duration;
246 struct GNUNET_TIME_Absolute end;
247 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
250 * Presolver is required if the problem was modified and an existing
251 * valid basis is now invalid */
252 if (mlp->presolver_required == GNUNET_YES)
253 mlp->control_param_lp.presolve = GLP_ON;
255 mlp->control_param_lp.presolve = GLP_OFF;
257 /* Solve LP problem to have initial valid solution */
259 res = glp_simplex(mlp->prob, &mlp->control_param_lp);
262 /* The LP problem instance has been successfully solved. */
264 else if (res == GLP_EITLIM)
266 /* simplex iteration limit has been exceeded. */
267 // TODO Increase iteration limit?
269 else if (res == GLP_ETMLIM)
271 /* Time limit has been exceeded. */
272 // TODO Increase time limit?
276 /* Problem was ill-defined, retry with presolver */
277 if (mlp->presolver_required == GNUNET_NO)
279 mlp->presolver_required = GNUNET_YES;
284 /* Problem was ill-defined, no way to handle that */
285 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
287 "Solving LP problem failed: %s\n", mlp_solve_to_string(res));
288 return GNUNET_SYSERR;
292 end = GNUNET_TIME_absolute_get ();
293 duration = GNUNET_TIME_absolute_get_difference (start, end);
295 mlp->lp_total_duration =+ duration.rel_value;
297 GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
298 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time", duration.rel_value, GNUNET_NO);
299 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average",
300 mlp->lp_total_duration / mlp->lp_solved, GNUNET_NO);
303 /* Analyze problem status */
304 res = glp_get_status (mlp->prob);
306 /* solution is optimal */
308 /* solution is feasible */
312 /* Problem was ill-defined, no way to handle that */
314 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
316 "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res));
317 return GNUNET_SYSERR;
321 /* solved sucessfully, no presolver required next time */
322 mlp->presolver_required = GNUNET_NO;
329 * Solves the MLP problem
331 * @param mlp the MLP Handle
332 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
335 mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
338 struct GNUNET_TIME_Relative duration;
339 struct GNUNET_TIME_Absolute end;
340 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
342 /* solve MLP problem */
343 res = glp_intopt(mlp->prob, &mlp->control_param_mlp);
347 /* The MLP problem instance has been successfully solved. */
349 else if (res == GLP_EITLIM)
351 /* simplex iteration limit has been exceeded. */
352 // TODO Increase iteration limit?
354 else if (res == GLP_ETMLIM)
356 /* Time limit has been exceeded. */
357 // TODO Increase time limit?
361 /* Problem was ill-defined, no way to handle that */
362 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
364 "Solving MLP problem failed: %s\n", mlp_solve_to_string(res));
365 return GNUNET_SYSERR;
368 end = GNUNET_TIME_absolute_get ();
369 duration = GNUNET_TIME_absolute_get_difference (start, end);
371 mlp->mlp_total_duration =+ duration.rel_value;
373 GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
374 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time", duration.rel_value, GNUNET_NO);
375 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average",
376 mlp->mlp_total_duration / mlp->mlp_solved, GNUNET_NO);
378 /* Analyze problem status */
379 res = glp_mip_status(mlp->prob);
381 /* solution is optimal */
383 /* solution is feasible */
387 /* Problem was ill-defined, no way to handle that */
389 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
391 "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res));
392 return GNUNET_SYSERR;
399 int mlp_solve_problem (struct GAS_MLP_Handle *mlp);
402 mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
404 struct GAS_MLP_Handle *mlp = cls;
406 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
408 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
414 if (mlp->addr_in_problem != 0)
415 mlp_solve_problem(mlp);
420 * Solves the MLP problem
422 * @param mlp the MLP Handle
423 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
426 mlp_solve_problem (struct GAS_MLP_Handle *mlp)
429 mlp->last_execution = GNUNET_TIME_absolute_get ();
430 res = mlp_solve_lp_problem (mlp);
431 if (res == GNUNET_OK)
432 res = mlp_solve_mlp_problem (mlp);
433 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
435 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
436 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
438 mlp->mlp_task = GNUNET_SCHEDULER_add_delayed (mlp->exec_interval, &mlp_scheduler, mlp);
443 * Init the MLP problem solving component
445 * @param stats the GNUNET_STATISTICS handle
446 * @param max_duration maximum numbers of iterations for the LP/MLP Solver
447 * @param max_iterations maximum time limit for the LP/MLP Solver
448 * @return struct GAS_MLP_Handle * on success, NULL on fail
450 struct GAS_MLP_Handle *
451 GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
452 const struct GNUNET_STATISTICS_Handle *stats,
453 struct GNUNET_TIME_Relative max_duration,
454 unsigned int max_iterations)
456 struct GAS_MLP_Handle * mlp = GNUNET_malloc (sizeof (struct GAS_MLP_Handle));
461 long long unsigned int tmp;
464 struct GNUNET_TIME_Relative i_exec;
466 /* Init GLPK environment */
467 GNUNET_assert (glp_init_env() == 0);
469 /* Create initial MLP problem */
470 mlp->prob = glp_create_prob();
471 GNUNET_assert (mlp->prob != NULL);
473 /* Get diversity coefficient from configuration */
474 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
477 D = (double) tmp / 100;
481 /* Get proportionality coefficient from configuration */
482 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
485 R = (double) tmp / 100;
489 /* Get utilization coefficient from configuration */
490 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
493 U = (double) tmp / 100;
497 /* Get quality metric coefficients from configuration */
500 int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
502 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
504 /* initialize quality coefficients with default value 1.0 */
508 if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
510 if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
514 if ((i_delay != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
515 "COEFFICIENT_QUALITY_DELAY",
518 mlp->co_Q[i_delay] = (double) tmp / 100;
520 mlp->co_Q[i_delay] = 1.0;
522 if ((i_distance != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
523 "COEFFICIENT_QUALITY_DISTANCE",
525 mlp->co_Q[i_distance] = (double) tmp / 100;
527 mlp->co_Q[i_distance] = 1.0;
529 /* Get minimum bandwidth per used address from configuration */
530 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
537 /* Get minimum number of connections from configuration */
538 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
545 /* Get minimum number of connections from configuration */
546 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_time (cfg, "ats",
549 mlp->exec_interval = i_exec;
551 mlp->exec_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30);
553 mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
554 mlp->max_iterations = max_iterations;
555 mlp->max_exec_duration = max_duration;
557 /* Redirect GLPK output to GNUnet logging */
558 glp_error_hook((void *) mlp, &mlp_term_hook);
560 /* Init LP solving parameters */
561 glp_init_smcp(&mlp->control_param_lp);
563 mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
565 mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
567 mlp->control_param_lp.it_lim = max_iterations;
568 mlp->control_param_lp.tm_lim = max_duration.rel_value;
570 /* Init MLP solving parameters */
571 glp_init_iocp(&mlp->control_param_mlp);
573 mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
575 mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
577 mlp->control_param_mlp.tm_lim = max_duration.rel_value;
579 mlp->last_execution = GNUNET_TIME_absolute_get_forever();
586 mlp->m = GNUNET_ATS_QualityPropertiesCount;
588 mlp_create_problem (mlp);
593 * Updates a single address in the MLP problem
595 * If the address did not exist before in the problem:
596 * The MLP problem has to be recreated and the problem has to be resolved
598 * Otherwise the addresses' values can be updated and the existing base can
601 * @param mlp the MLP Handle
602 * @param addresses the address hashmap
603 * @param address the address to update
606 GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
610 struct MLP_information *mlpi;
613 GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
615 /* We add a new address */
616 if (address->mlp_information == NULL)
619 mlpi = GNUNET_malloc (sizeof (struct MLP_information));
620 address->mlp_information = mlpi;
622 /* Add bandwidth column */
623 col = glp_add_cols (mlp->prob, 2);
627 GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
628 glp_set_col_name (mlp->prob, mlpi->c_b , name);
630 /* Lower bound == 0 */
631 glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0);
632 /* Continuous value*/
633 glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
634 /* Objective function coefficient == 0 */
635 glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
637 /* Add usage column */
638 GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
639 glp_set_col_name (mlp->prob, mlpi->c_n, name);
641 /* Limit value : 0 <= value <= 1 */
642 glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0);
644 glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
645 /* Objective function coefficient == 0 */
646 glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
651 mlp->addr_in_problem ++;
659 if (new == GNUNET_YES)
660 mlp->presolver_required = GNUNET_YES;
661 mlp_solve_problem (mlp);
665 * Deletes a single address in the MLP problem
667 * The MLP problem has to be recreated and the problem has to be resolved
669 * @param mlp the MLP Handle
670 * @param addresses the address hashmap
671 * @param address the address to delete
674 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
676 GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
679 if (address->mlp_information != NULL)
681 GNUNET_free (address->mlp_information);
682 address->mlp_information = NULL;
684 mlp->addr_in_problem --;
690 mlp->presolver_required = GNUNET_YES;
691 mlp_solve_problem (mlp);
695 * Deletes a single address in the MLP problem
697 * @param mlp the MLP Handle
698 * @param addresses the address hashmap
699 * @param address the address to change the preference
702 GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
704 GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
708 * Shutdown the MLP problem solving component
709 * @param mlp the MLP handle
712 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
714 if (mlp->mlp_task != GNUNET_SCHEDULER_NO_TASK)
716 GNUNET_SCHEDULER_cancel(mlp->mlp_task);
717 mlp->mlp_task = GNUNET_SCHEDULER_NO_TASK;
721 glp_delete_prob(mlp->prob);
723 /* Clean up GLPK environment */
730 /* end of gnunet-service-ats_addresses_mlp.c */