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"
37 * Translate glpk solver error codes to text
38 * @param retcode return code
39 * @return string with result
42 mlp_solve_to_string (int retcode)
49 return "invalid basis";
52 return "singular matrix";
55 return "ill-conditioned matrix";
58 return "invalid bounds";
61 return "solver failed";
64 return "objective lower limit reached";
67 return "objective upper limit reached";
70 return "iteration limit exceeded";
73 return "time limit exceeded";
76 return "no primal feasible solution";
79 return "root LP optimum not provided";
82 return "search terminated by application";
85 return "relative mip gap tolerance reached";
88 return "no dual feasible solution";
91 return "no convergence";
94 return "numerical instability";
97 return "invalid data";
100 return "result out of range";
104 return "unknown error";
108 return "unknown error";
113 * Translate glpk status error codes to text
114 * @param retcode return code
115 * @return string with result
118 mlp_status_to_string (int retcode)
122 return "solution is undefined";
125 return "solution is feasible";
128 return "solution is infeasible";
131 return "no feasible solution exists";
134 return "solution is optimal";
137 return "solution is unbounded";
141 return "unknown error";
145 return "unknown error";
149 * Intercept GLPK terminal output
150 * @param info the mlp handle
151 * @param s the string to print
152 * @return 0: glpk prints output on terminal, 0 != surpress output
155 mlp_term_hook (void *info, const char *s)
157 /* Not needed atm struct MLP_information *mlp = info; */
158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s", s);
163 * Create the MLP problem
165 * @param mlp the MLP handle
166 * @return GNUNET_OK or GNUNET_SYSERR
170 mlp_create_problem (struct GAS_MLP_Handle *mlp)
177 /* Set a problem name */
178 glp_set_prob_name (mlp->prob, "gnunet ats bandwidth distribution");
180 /* Set optimization direction to maximize */
181 glp_set_obj_dir (mlp->prob, GLP_MAX);
183 /* Adding invariant columns */
185 /* Diversity d column */
187 col = glp_add_cols (mlp->prob, 1);
190 glp_set_col_name (mlp->prob, col, "d");
191 /* Column objective function coefficient */
192 glp_set_obj_coef (mlp->prob, col, mlp->co_D);
193 /* Column lower bound = 0.0 */
194 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
196 /* Utilization u column */
198 col = glp_add_cols (mlp->prob, 1);
201 glp_set_col_name (mlp->prob, col, "u");
202 /* Column objective function coefficient */
203 glp_set_obj_coef (mlp->prob, col, mlp->co_U);
204 /* Column lower bound = 0.0 */
205 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
207 /* Relativity r column */
208 col = glp_add_cols (mlp->prob, 1);
211 glp_set_col_name (mlp->prob, col, "r");
212 /* Column objective function coefficient */
213 glp_set_obj_coef (mlp->prob, col, mlp->co_R);
214 /* Column lower bound = 0.0 */
215 glp_set_col_bnds (mlp->prob, col, GLP_LO, 0.0, 0.0);
217 /* Quality metric columns */
218 col = glp_add_cols(mlp->prob, mlp->m);
219 for (c = 0; c < mlp->m; c++)
221 mlp->c_q[c] = col + c;
222 GNUNET_asprintf (&name, "q_%u", mlp->q[c]);
223 glp_set_col_name (mlp->prob, col + c, name);
224 /* Column lower bound = 0.0 */
225 glp_set_col_bnds (mlp->prob, col + c, GLP_LO, 0.0, 0.0);
227 /* Coefficient == Qm */
228 glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
235 * Solves the LP problem
237 * @param mlp the MLP Handle
238 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
241 mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
244 struct GNUNET_TIME_Relative duration;
245 struct GNUNET_TIME_Absolute end;
246 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
249 * Presolver is required if the problem was modified and an existing
250 * valid basis is now invalid */
251 if (mlp->presolver_required == GNUNET_YES)
252 mlp->control_param_lp.presolve = GLP_ON;
254 mlp->control_param_lp.presolve = GLP_OFF;
256 /* Solve LP problem to have initial valid solution */
258 res = glp_simplex(mlp->prob, &mlp->control_param_lp);
261 /* The LP problem instance has been successfully solved. */
263 else if (res == GLP_EITLIM)
265 /* simplex iteration limit has been exceeded. */
266 // TODO Increase iteration limit?
268 else if (res == GLP_ETMLIM)
270 /* Time limit has been exceeded. */
271 // TODO Increase time limit?
275 /* Problem was ill-defined, retry with presolver */
276 if (mlp->presolver_required == GNUNET_NO)
278 mlp->presolver_required = GNUNET_YES;
283 /* Problem was ill-defined, no way to handle that */
284 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
286 "Solving LP problem failed: %s\n", mlp_solve_to_string(res));
287 return GNUNET_SYSERR;
291 end = GNUNET_TIME_absolute_get ();
292 duration = GNUNET_TIME_absolute_get_difference (start, end);
294 mlp->lp_total_duration =+ duration.rel_value;
296 GNUNET_STATISTICS_update (mlp->stats,"# LP problem solved", 1, GNUNET_NO);
297 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time", duration.rel_value, GNUNET_NO);
298 GNUNET_STATISTICS_set (mlp->stats,"# LP execution time average",
299 mlp->lp_total_duration / mlp->lp_solved, GNUNET_NO);
302 /* Analyze problem status */
303 res = glp_get_status (mlp->prob);
305 /* solution is optimal */
307 /* solution is feasible */
311 /* Problem was ill-defined, no way to handle that */
313 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
315 "Solving LP problem failed, no solution: %s\n", mlp_status_to_string(res));
316 return GNUNET_SYSERR;
320 /* solved sucessfully, no presolver required next time */
321 mlp->presolver_required = GNUNET_NO;
328 * Solves the MLP problem
330 * @param mlp the MLP Handle
331 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
334 mlp_solve_mlp_problem (struct GAS_MLP_Handle *mlp)
337 struct GNUNET_TIME_Relative duration;
338 struct GNUNET_TIME_Absolute end;
339 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get();
341 /* solve MLP problem */
342 res = glp_intopt(mlp->prob, &mlp->control_param_mlp);
346 /* The MLP problem instance has been successfully solved. */
348 else if (res == GLP_EITLIM)
350 /* simplex iteration limit has been exceeded. */
351 // TODO Increase iteration limit?
353 else if (res == GLP_ETMLIM)
355 /* Time limit has been exceeded. */
356 // TODO Increase time limit?
360 /* Problem was ill-defined, no way to handle that */
361 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
363 "Solving MLP problem failed: %s\n", mlp_solve_to_string(res));
364 return GNUNET_SYSERR;
367 end = GNUNET_TIME_absolute_get ();
368 duration = GNUNET_TIME_absolute_get_difference (start, end);
370 mlp->mlp_total_duration =+ duration.rel_value;
372 GNUNET_STATISTICS_update (mlp->stats,"# MLP problem solved", 1, GNUNET_NO);
373 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time", duration.rel_value, GNUNET_NO);
374 GNUNET_STATISTICS_set (mlp->stats,"# MLP execution time average",
375 mlp->mlp_total_duration / mlp->mlp_solved, GNUNET_NO);
377 /* Analyze problem status */
378 res = glp_mip_status(mlp->prob);
380 /* solution is optimal */
382 /* solution is feasible */
386 /* Problem was ill-defined, no way to handle that */
388 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
390 "Solving MLP problem failed, %s\n\n", mlp_status_to_string(res));
391 return GNUNET_SYSERR;
399 * Solves the MLP problem
401 * @param mlp the MLP Handle
402 * @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
405 mlp_solve_problem (struct GAS_MLP_Handle *mlp)
408 mlp->last_execution = GNUNET_TIME_absolute_get ();
409 res = mlp_solve_lp_problem (mlp);
410 if (res == GNUNET_OK)
411 res = mlp_solve_mlp_problem (mlp);
416 * Init the MLP problem solving component
418 * @param stats the GNUNET_STATISTICS handle
419 * @param max_duration maximum numbers of iterations for the LP/MLP Solver
420 * @param max_iterations maximum time limit for the LP/MLP Solver
421 * @return struct GAS_MLP_Handle * on success, NULL on fail
423 struct GAS_MLP_Handle *
424 GAS_mlp_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
425 const struct GNUNET_STATISTICS_Handle *stats,
426 struct GNUNET_TIME_Relative max_duration,
427 unsigned int max_iterations)
429 struct GAS_MLP_Handle * mlp = GNUNET_malloc (sizeof (struct GAS_MLP_Handle));
434 long long unsigned int tmp;
438 /* Init GLPK environment */
439 GNUNET_assert (glp_init_env() == 0);
441 /* Create initial MLP problem */
442 mlp->prob = glp_create_prob();
443 GNUNET_assert (mlp->prob != NULL);
445 /* Get diversity coefficient from configuration */
446 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
449 D = (double) tmp / 100;
453 /* Get proportionality coefficient from configuration */
454 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
457 R = (double) tmp / 100;
461 /* Get utilization coefficient from configuration */
462 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
465 U = (double) tmp / 100;
469 /* Get quality metric coefficients from configuration */
472 int q[GNUNET_ATS_QualityPropertiesCount] = GNUNET_ATS_QualityProperties;
474 for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
476 /* initialize quality coefficients with default value 1.0 */
480 if (q[c] == GNUNET_ATS_QUALITY_NET_DELAY)
482 if (q[c] == GNUNET_ATS_QUALITY_NET_DISTANCE)
486 if ((i_delay != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
487 "COEFFICIENT_QUALITY_DELAY",
490 mlp->co_Q[i_delay] = (double) tmp / 100;
492 mlp->co_Q[i_delay] = 1.0;
494 if ((i_distance != -1) && (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
495 "COEFFICIENT_QUALITY_DISTANCE",
497 mlp->co_Q[i_distance] = (double) tmp / 100;
499 mlp->co_Q[i_distance] = 1.0;
501 /* Get minimum bandwidth per used address from configuration */
502 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
509 /* Get minimum number of connections from configuration */
510 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
517 mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
518 mlp->max_iterations = max_iterations;
519 mlp->max_exec_duration = max_duration;
521 /* Redirect GLPK output to GNUnet logging */
522 glp_error_hook((void *) mlp, &mlp_term_hook);
524 /* Init LP solving parameters */
525 glp_init_smcp(&mlp->control_param_lp);
527 mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
529 mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
531 mlp->control_param_lp.it_lim = max_iterations;
532 mlp->control_param_lp.tm_lim = max_duration.rel_value;
534 /* Init MLP solving parameters */
535 glp_init_iocp(&mlp->control_param_mlp);
537 mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
539 mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
541 mlp->control_param_mlp.tm_lim = max_duration.rel_value;
543 mlp->last_execution = GNUNET_TIME_absolute_get_forever();
550 mlp->m = GNUNET_ATS_QualityPropertiesCount;
552 mlp_create_problem (mlp);
557 * Updates a single address in the MLP problem
559 * If the address did not exist before in the problem:
560 * The MLP problem has to be recreated and the problem has to be resolved
562 * Otherwise the addresses' values can be updated and the existing base can
565 * @param mlp the MLP Handle
566 * @param addresses the address hashmap
567 * @param address the address to update
570 GAS_mlp_address_update (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
574 struct MLP_information *mlpi;
577 GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
579 /* We add a new address */
580 if (address->mlp_information == NULL)
583 mlpi = GNUNET_malloc (sizeof (struct MLP_information));
584 address->mlp_information = mlpi;
586 /* Add bandwidth column */
587 col = glp_add_cols (mlp->prob, 2);
591 GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
592 glp_set_col_name (mlp->prob, mlpi->c_b , name);
594 /* Lower bound == 0 */
595 glp_set_col_bnds (mlp->prob, mlpi->c_b , GLP_LO, 0.0, 0.0);
596 /* Continuous value*/
597 glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
598 /* Objective function coefficient == 0 */
599 glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
601 /* Add usage column */
602 GNUNET_asprintf (&name, "n_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
603 glp_set_col_name (mlp->prob, mlpi->c_n, name);
605 /* Limit value : 0 <= value <= 1 */
606 glp_set_col_bnds (mlp->prob, mlpi->c_n, GLP_DB, 0.0, 1.0);
608 glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
609 /* Objective function coefficient == 0 */
610 glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
620 if (new == GNUNET_YES)
621 mlp->presolver_required = GNUNET_YES;
622 mlp_solve_problem (mlp);
626 * Deletes a single address in the MLP problem
628 * The MLP problem has to be recreated and the problem has to be resolved
630 * @param mlp the MLP Handle
631 * @param addresses the address hashmap
632 * @param address the address to delete
635 GAS_mlp_address_delete (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
637 GNUNET_STATISTICS_update (mlp->stats,"# LP address deletions", 1, GNUNET_NO);
640 if (address->mlp_information != NULL)
642 GNUNET_free (address->mlp_information);
643 address->mlp_information = NULL;
649 mlp->presolver_required = GNUNET_YES;
650 mlp_solve_problem (mlp);
654 * Deletes a single address in the MLP problem
656 * @param mlp the MLP Handle
657 * @param addresses the address hashmap
658 * @param address the address to change the preference
661 GAS_mlp_address_change_preference (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
663 GNUNET_STATISTICS_update (mlp->stats,"# LP address preference changes", 1, GNUNET_NO);
667 * Shutdown the MLP problem solving component
668 * @param mlp the MLP handle
671 GAS_mlp_done (struct GAS_MLP_Handle *mlp)
674 glp_delete_prob(mlp->prob);
676 /* Clean up GLPK environment */
683 /* end of gnunet-service-ats_addresses_mlp.c */