/*
This file is part of GNUnet.
Copyright (C) 2011-2014 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file ats/plugin_ats_mlp.c
* @brief ats mlp problem solver
* @author Matthias Wachs
* @author Christian Grothoff
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_ats_service.h"
#include "gnunet_ats_plugin.h"
#include "gnunet-service-ats_addresses.h"
#include "gnunet_statistics_service.h"
#include
#include
#define BIG_M_VALUE (UINT32_MAX) /10
#define BIG_M_STRING "unlimited"
#define MLP_AVERAGING_QUEUE_LENGTH 3
#define MLP_MAX_EXEC_DURATION GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 10)
#define MLP_MAX_ITERATIONS 4096
#define MLP_DEFAULT_D 1.0
#define MLP_DEFAULT_R 1.0
#define MLP_DEFAULT_U 1.0
#define MLP_DEFAULT_QUALITY 1.0
#define MLP_DEFAULT_MIN_CONNECTIONS 4
#define MLP_DEFAULT_PEER_PREFERENCE 1.0
#define MLP_NaN -1
#define MLP_UNDEFINED 0
#define GLP_YES 1.0
#define GLP_NO 0.0
enum MLP_Output_Format
{
MLP_MPS,
MLP_CPLEX,
MLP_GLPK
};
enum QualityMetrics
{
RQ_QUALITY_METRIC_DELAY = 0,
RQ_QUALITY_METRIC_DISTANCE = 1,
RQ_QUALITY_METRIC_COUNT = 2
};
static const char *
print_quality_type (enum QualityMetrics qm)
{
switch (qm){
case RQ_QUALITY_METRIC_DELAY:
return "delay";
case RQ_QUALITY_METRIC_DISTANCE:
return "distance";
default:
GNUNET_break (0);
return NULL;
}
}
struct MLP_Solution
{
int lp_res;
int lp_presolv;
int mip_res;
int mip_presolv;
double lp_objective_value;
double mlp_objective_value;
double mlp_gap;
double lp_mlp_gap;
int p_elements;
int p_cols;
int p_rows;
int n_peers;
int n_addresses;
};
struct ATS_Peer
{
struct GNUNET_PeerIdentity id;
/* Was this peer already added to the current problem? */
int processed;
/* constraint 2: 1 address per peer*/
unsigned int r_c2;
/* constraint 9: relativity */
unsigned int r_c9;
/* Legacy preference value */
double f;
};
struct MLP_Problem
{
/**
* GLPK (MLP) problem object
*/
glp_prob *prob;
/* Number of addresses in problem */
unsigned int num_addresses;
/* Number of peers in problem */
unsigned int num_peers;
/* Number of elements in problem matrix */
unsigned int num_elements;
/* Row index constraint 2: */
unsigned int r_c2;
/* Row index constraint 4: minimum connections */
unsigned int r_c4;
/* Row index constraint 6: maximize diversity */
unsigned int r_c6;
/* Row index constraint 8: utilization*/
unsigned int r_c8;
/* Row index constraint 9: relativity*/
unsigned int r_c9;
/* Row indices quality metrics */
int r_q[RQ_QUALITY_METRIC_COUNT];
/* Row indices ATS network quotas */
int r_quota[GNUNET_NT_COUNT];
/* Column index Diversity (D) column */
int c_d;
/* Column index Utilization (U) column */
int c_u;
/* Column index Proportionality (R) column */
int c_r;
/* Column index quality metrics */
int c_q[RQ_QUALITY_METRIC_COUNT];
/* Problem matrix */
/* Current index */
unsigned int ci;
/* Row index array */
int *ia;
/* Column index array */
int *ja;
/* Column index value */
double *ar;
};
struct MLP_Variables
{
/* Big M value for bandwidth capping */
double BIG_M;
/* MIP Gap */
double mip_gap;
/* LP MIP Gap */
double lp_mip_gap;
/* Number of quality metrics @deprecated, use RQ_QUALITY_METRIC_COUNT */
int m_q;
/* Number of quality metrics */
int m_rc;
/* Quality metric coefficients*/
double co_Q[RQ_QUALITY_METRIC_COUNT];
/* Ressource costs coefficients*/
double co_RC[RQ_QUALITY_METRIC_COUNT];
/* Diversity coefficient */
double co_D;
/* Utility coefficient */
double co_U;
/* Relativity coefficient */
double co_R;
/* Minimum bandwidth assigned to an address */
unsigned int b_min;
/* Minimum number of addresses with bandwidth assigned */
unsigned int n_min;
/* Quotas */
/* Array mapping array index to ATS network */
int quota_index[GNUNET_NT_COUNT];
/* Outbound quotas */
unsigned long long quota_out[GNUNET_NT_COUNT];
/* Inbound quotas */
unsigned long long quota_in[GNUNET_NT_COUNT];
/* ATS ressource costs
* array with GNUNET_ATS_QualityPropertiesCount elements
* contains mapping to GNUNET_ATS_Property
* */
int rc[RQ_QUALITY_METRIC_COUNT];
};
/**
* MLP Handle
*/
struct GAS_MLP_Handle
{
struct GNUNET_ATS_PluginEnvironment *env;
/**
* Exclude peer from next result propagation
*/
const struct GNUNET_PeerIdentity *exclude_peer;
/**
* Encapsulation for the MLP problem
*/
struct MLP_Problem p;
/**
* Encapsulation for the MLP problem variables
*/
struct MLP_Variables pv;
/**
* Encapsulation for the MLP solution
*/
struct MLP_Solution ps;
/**
* Bulk lock
*/
int stat_bulk_lock;
/**
* Number of changes while solver was locked
*/
int stat_bulk_requests;
/**
* GLPK LP control parameter
*/
glp_smcp control_param_lp;
/**
* GLPK LP control parameter
*/
glp_iocp control_param_mlp;
/**
* Peers with pending address requests
*/
struct GNUNET_CONTAINER_MultiPeerMap *requested_peers;
/**
* Was the problem updated since last solution
*/
int stat_mlp_prob_updated;
/**
* Has the problem size changed since last solution
*/
int stat_mlp_prob_changed;
/**
* Solve the problem automatically when updates occur?
* Default: GNUNET_YES
* Can be disabled for test and measurements
*/
int opt_mlp_auto_solve;
/**
* Write all MILP problems to a MPS file
*/
int opt_dump_problem_all;
/**
* Write all MILP problem solutions to a file
*/
int opt_dump_solution_all;
/**
* Write MILP problems to a MPS file when solver fails
*/
int opt_dump_problem_on_fail;
/**
* Write MILP problem solutions to a file when solver fails
*/
int opt_dump_solution_on_fail;
/**
* solve feasibility only
*/
int opt_dbg_feasibility_only;
/**
* solve autoscale the problem
*/
int opt_dbg_autoscale_problem;
/**
* use the intopt presolver instead of simplex
*/
int opt_dbg_intopt_presolver;
/**
* Print GLPK output
*/
int opt_dbg_glpk_verbose;
/**
* solve autoscale the problem
*/
int opt_dbg_optimize_relativity;
/**
* solve autoscale the problem
*/
int opt_dbg_optimize_diversity;
/**
* solve autoscale the problem
*/
int opt_dbg_optimize_quality;
/**
* solve autoscale the problem
*/
int opt_dbg_optimize_utility;
/**
* Output format
*/
enum MLP_Output_Format opt_log_format;
};
/**
* Address specific MLP information
*/
struct MLP_information
{
/**
* Bandwidth assigned outbound
*/
uint32_t b_out;
/**
* Bandwidth assigned inbound
*/
uint32_t b_in;
/**
* Address selected
*/
int n;
/**
* bandwidth column index
*/
signed int c_b;
/**
* address usage column
*/
signed int c_n;
/* row indexes */
/**
* constraint 1: bandwidth capping
*/
unsigned int r_c1;
/**
* constraint 3: minimum bandwidth
*/
unsigned int r_c3;
};
/**
*
* NOTE: Do not modify this documentation. This documentation is based on
* gnunet.org:/vcs/fsnsg/ats-paper.git/tech-doku/ats-tech-guide.tex
* use build_txt.sh to generate plaintext output
*
* The MLP solver (mlp) tries to finds an optimal bandwidth assignmentby
* optimizing an mixed integer programming problem. The MLP solver uses a
* number of constraints to find the best adddress for a peer and an optimal
* bandwidth assignment. mlp uses the GNU Linear Programming Kit to solve the
* MLP problem.
*
* We defined a constraint system to find an optimal bandwidth assignment.
* This constraint system uses as an input data addresses, bandwidth quotas,
* preferences and quality values. This constraint system is stored in an
* matrix based equotation system.
*
* 5 Using GLPK
*
* A (M)LP problem consists of a target function to optimizes, constraints
* and rows and columns. FIXME GLP uses three arrays to index the matrix: two
* integer arrays storing the row and column indices in the matrix and an
* float array to store the coeeficient.
*
* To solve the problem we first find an initial solution for the LP problem
* using the LP solver and then find an MLP solution based on this solution
* using the MLP solver.
*
* Solving (M)LP problems has the property that finding an initial solution
* for the LP problem is computationally expensive and finding the MLP
* solution is cheaper. This is especially interesting an existing LP
* solution can be reused if only coefficients in the matrix have changed
* (addresses updated). Only when the problem size changes (addresses added
* or deleted) a new LP solution has to be found.
*
* Intended usage
* The mlp solver solves the bandwidth assignment problem only on demand when
* an address suggestion is requested. When an address is requested mlp the
* solves the mlp problem and if the active address or the bandwidth assigned
* changes it calls the callback to addresses. The mlp solver gets notified
* about new addresses (adding sessions), removed addresses (address
* deletions) and address updates. To benefit from the mlp properties
* mentioned in section 5 the solver rembers if since the last solution
* addresses were added or deleted (problem size changed, problem has to be
* rebuild and solved from sratch) or if addresses were updated and the
* existing solution can be reused.
*
* 5.1 Input data
*
* The quotas for each network segment are passed by addresses. MLP can be
* adapted using configuration settings and uses the following parameters:
* * MLP_MAX_DURATION:
* Maximum duration for a MLP solution procees (default: 3 sec.)
* * MLP_MAX_ITERATIONS:
* Maximum number of iterations for a MLP solution process (default:
* 1024)
* * MLP_MIN_CONNECTIONS:
* Minimum number of desired connections (default: 4)
* * MLP_MIN_BANDWIDTH:
* Minimum amount of bandwidth assigned to an address (default: 1024)
* * MLP_COEFFICIENT_D:
* Diversity coefficient (default: 1.0)
* * MLP_COEFFICIENT_R:
* Relativity coefficient (default: 1.0)
* * MLP_COEFFICIENT_U:
* Utilization coefficient (default: 1.0)
* * MLP_COEFFICIENT_D:
* Diversity coefficient (default: 1.0)
* * MLP_COEFFICIENT_QUALITY_DELAY:
* Quality delay coefficient (default: 1.0)
* * MLP_COEFFICIENT_QUALITY_DISTANCE:
* Quality distance coefficient (default: 1.0)
* * MLP_COEFFICIENT_QUALITY_DISTANCE:
* Quality distance coefficient (default: 1.0)
* * MLP_COEFFICIENT_QUALITY_DISTANCE:
* Quality distance coefficient (default: 1.0)
* * MLP_COEFFICIENT_QUALITY_DISTANCE:
* Quality distance coefficient (default: 1.0)
*
* 5.2 Data structures used
*
* mlp has for each known peer a struct ATS_Peer containing information about
* a specific peer. The address field solver_information contains information
* about the mlp properties of this address.
*
* 5.3 Initializing
*
* During initialization mlp initializes the GLPK libray used to solve the
* MLP problem: it initializes the glpk environment and creates an initial LP
* problem. Next it loads the configuration values from the configuration or
* uses the default values configured in -addresses_mlp.h. The quotas used
* are given by addresses but may have to be adjusted. mlp uses a upper limit
* for the bandwidth assigned called BIG M and a minimum amount of bandwidth
* an address gets assigned as well as a minium desired number of
* connections. If the configured quota is bigger than BIG M, it is reduced
* to BIG M. If the configured quota is smaller than MLP_MIN_CONNECTIONS
* *MLP_MIN_BANDWIDTH it is increased to this value.
*
* 5.4 Shutdown
*/
#define LOG(kind,...) GNUNET_log_from (kind, "ats-mlp",__VA_ARGS__)
/**
* Print debug output for mlp problem creation
*/
#define DEBUG_MLP_PROBLEM_CREATION GNUNET_NO
/**
* Intercept GLPK terminal output
* @param info the mlp handle
* @param s the string to print
* @return 0: glpk prints output on terminal, 0 != surpress output
*/
static int
mlp_term_hook (void *info, const char *s)
{
struct GAS_MLP_Handle *mlp = info;
if (mlp->opt_dbg_glpk_verbose)
LOG (GNUNET_ERROR_TYPE_ERROR, "%s", s);
return 1;
}
/**
* Reset peers for next problem creation
*
* @param cls not used
* @param key the key
* @param value ATS_Peer
* @return #GNUNET_OK
*/
static int
reset_peers (void *cls,
const struct GNUNET_PeerIdentity *key,
void *value)
{
struct ATS_Peer *peer = value;
peer->processed = GNUNET_NO;
return GNUNET_OK;
}
/**
* Delete the MLP problem and free the constrain matrix
*
* @param mlp the MLP handle
*/
static void
mlp_delete_problem (struct GAS_MLP_Handle *mlp)
{
int c;
if (mlp == NULL)
return;
if (mlp->p.prob != NULL)
{
glp_delete_prob(mlp->p.prob);
mlp->p.prob = NULL;
}
/* delete row index */
if (mlp->p.ia != NULL)
{
GNUNET_free (mlp->p.ia);
mlp->p.ia = NULL;
}
/* delete column index */
if (mlp->p.ja != NULL)
{
GNUNET_free (mlp->p.ja);
mlp->p.ja = NULL;
}
/* delete coefficients */
if (mlp->p.ar != NULL)
{
GNUNET_free (mlp->p.ar);
mlp->p.ar = NULL;
}
mlp->p.ci = 0;
mlp->p.prob = NULL;
mlp->p.c_d = MLP_UNDEFINED;
mlp->p.c_r = MLP_UNDEFINED;
mlp->p.r_c2 = MLP_UNDEFINED;
mlp->p.r_c4 = MLP_UNDEFINED;
mlp->p.r_c6 = MLP_UNDEFINED;
mlp->p.r_c9 = MLP_UNDEFINED;
for (c = 0; c < RQ_QUALITY_METRIC_COUNT ; c ++)
mlp->p.r_q[c] = MLP_UNDEFINED;
for (c = 0; c < GNUNET_NT_COUNT; c ++)
mlp->p.r_quota[c] = MLP_UNDEFINED;
mlp->p.ci = MLP_UNDEFINED;
GNUNET_CONTAINER_multipeermap_iterate (mlp->requested_peers,
&reset_peers, NULL);
}
/**
* Translate glpk status error codes to text
* @param retcode return code
* @return string with result
*/
static const char *
mlp_status_to_string (int retcode)
{
switch (retcode) {
case GLP_UNDEF:
return "solution is undefined";
case GLP_FEAS:
return "solution is feasible";
case GLP_INFEAS:
return "solution is infeasible";
case GLP_NOFEAS:
return "no feasible solution exists";
case GLP_OPT:
return "solution is optimal";
case GLP_UNBND:
return "solution is unbounded";
default:
GNUNET_break (0);
return "unknown error";
}
}
/**
* Translate glpk solver error codes to text
* @param retcode return code
* @return string with result
*/
static const char *
mlp_solve_to_string (int retcode)
{
switch (retcode) {
case 0:
return "ok";
case GLP_EBADB:
return "invalid basis";
case GLP_ESING:
return "singular matrix";
case GLP_ECOND:
return "ill-conditioned matrix";
case GLP_EBOUND:
return "invalid bounds";
case GLP_EFAIL:
return "solver failed";
case GLP_EOBJLL:
return "objective lower limit reached";
case GLP_EOBJUL:
return "objective upper limit reached";
case GLP_EITLIM:
return "iteration limit exceeded";
case GLP_ETMLIM:
return "time limit exceeded";
case GLP_ENOPFS:
return "no primal feasible solution";
case GLP_ENODFS:
return "no dual feasible solution";
case GLP_EROOT:
return "root LP optimum not provided";
case GLP_ESTOP:
return "search terminated by application";
case GLP_EMIPGAP:
return "relative mip gap tolerance reached";
case GLP_ENOFEAS:
return "no dual feasible solution";
case GLP_ENOCVG:
return "no convergence";
case GLP_EINSTAB:
return "numerical instability";
case GLP_EDATA:
return "invalid data";
case GLP_ERANGE:
return "result out of range";
default:
GNUNET_break (0);
return "unknown error";
}
}
struct CountContext
{
const struct GNUNET_CONTAINER_MultiPeerMap *map;
int result;
};
static int
mlp_create_problem_count_addresses_it (void *cls,
const struct GNUNET_PeerIdentity *key,
void *value)
{
struct CountContext *cctx = cls;
/* Check if we have to add this peer due to a pending request */
if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (cctx->map, key))
cctx->result++;
return GNUNET_OK;
}
static int
mlp_create_problem_count_addresses (const struct GNUNET_CONTAINER_MultiPeerMap *requested_peers,
const struct GNUNET_CONTAINER_MultiPeerMap *addresses)
{
struct CountContext cctx;
cctx.map = requested_peers;
cctx.result = 0;
GNUNET_CONTAINER_multipeermap_iterate (addresses,
&mlp_create_problem_count_addresses_it, &cctx);
return cctx.result;
}
static int
mlp_create_problem_count_peers_it (void *cls,
const struct GNUNET_PeerIdentity *key,
void *value)
{
struct CountContext *cctx = cls;
/* Check if we have to addresses for the requested peer */
if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (cctx->map, key))
cctx->result++;
return GNUNET_OK;
}
static int
mlp_create_problem_count_peers (const struct GNUNET_CONTAINER_MultiPeerMap *requested_peers,
const struct GNUNET_CONTAINER_MultiPeerMap *addresses)
{
struct CountContext cctx;
cctx.map = addresses;
cctx.result = 0;
GNUNET_CONTAINER_multipeermap_iterate (requested_peers,
&mlp_create_problem_count_peers_it, &cctx);
return cctx.result;
}
/**
* Updates an existing value in the matrix
*
* Extract the row, updates the value and updates the row in the problem
*
* @param p the mlp problem
* @param row the row to create the value in
* @param col the column to create the value in
* @param val the value to set
* @param line calling line for debbuging
* @return GNUNET_YES value changed, GNUNET_NO value did not change, GNUNET_SYSERR
* on error
*/
static int
mlp_create_problem_update_value (struct MLP_Problem *p,
int row, int col, double val,
int line)
{
int c_cols;
int c_elems;
int c1;
int res;
int found;
double *val_array;
int *ind_array;
GNUNET_assert (NULL != p->prob);
/* Get number of columns and prepare data structure */
c_cols = glp_get_num_cols(p->prob);
if (0 >= c_cols)
return GNUNET_SYSERR;
val_array = GNUNET_malloc ((c_cols +1)* sizeof (double));
GNUNET_assert (NULL != val_array);
ind_array = GNUNET_malloc ((c_cols+1) * sizeof (int));
GNUNET_assert (NULL != ind_array);
/* Extract the row */
/* Update the value */
c_elems = glp_get_mat_row (p->prob, row, ind_array, val_array);
found = GNUNET_NO;
for (c1 = 1; c1 < (c_elems+1); c1++)
{
if (ind_array[c1] == col)
{
found = GNUNET_YES;
break;
}
}
if (GNUNET_NO == found)
{
ind_array[c_elems+1] = col;
val_array[c_elems+1] = val;
LOG (GNUNET_ERROR_TYPE_DEBUG, "[P] Setting value in [%s : %s] to `%.2f'\n",
glp_get_row_name (p->prob, row), glp_get_col_name (p->prob, col),
val);
glp_set_mat_row (p->prob, row, c_elems+1, ind_array, val_array);
GNUNET_free (ind_array);
GNUNET_free (val_array);
return GNUNET_YES;
}
else
{
/* Update value */
LOG (GNUNET_ERROR_TYPE_DEBUG, "[P] Updating value in [%s : %s] from `%.2f' to `%.2f'\n",
glp_get_row_name (p->prob, row), glp_get_col_name (p->prob, col),
val_array[c1], val);
if (val != val_array[c1])
res = GNUNET_YES;
else
res = GNUNET_NO;
val_array[c1] = val;
/* Update the row in the matrix */
glp_set_mat_row (p->prob, row, c_elems, ind_array, val_array);
}
GNUNET_free (ind_array);
GNUNET_free (val_array);
return res;
}
/**
* Creates a new value in the matrix
*
* Sets the row and column index in the problem array and increments the
* position field
*
* @param p the mlp problem
* @param row the row to create the value in
* @param col the column to create the value in
* @param val the value to set
* @param line calling line for debbuging
*/
static void
mlp_create_problem_set_value (struct MLP_Problem *p,
int row, int col, double val,
int line)
{
if ((p->ci) >= p->num_elements)
{
LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: line %u: Request for index %u bigger than array size of %u\n",
line, p->ci + 1, p->num_elements);
GNUNET_break (0);
return;
}
if ((0 == row) || (0 == col))
{
GNUNET_break (0);
LOG (GNUNET_ERROR_TYPE_ERROR, "[P]: Invalid call from line %u: row = %u, col = %u\n",
line, row, col);
}
p->ia[p->ci] = row ;
p->ja[p->ci] = col;
p->ar[p->ci] = val;
#if DEBUG_MLP_PROBLEM_CREATION
LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: line %u: Set value [%u,%u] in index %u == %.2f\n",
line, p->ia[p->ci], p->ja[p->ci], p->ci, p->ar[p->ci]);
#endif
p->ci++;
}
static int
mlp_create_problem_create_column (struct MLP_Problem *p, char *name,
unsigned int type, unsigned int bound, double lb, double ub,
double coef)
{
int col = glp_add_cols (p->prob, 1);
glp_set_col_name (p->prob, col, name);
glp_set_col_bnds (p->prob, col, bound, lb, ub);
glp_set_col_kind (p->prob, col, type);
glp_set_obj_coef (p->prob, col, coef);
#if DEBUG_MLP_PROBLEM_CREATION
LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: Added column [%u] `%s': %.2f\n",
col, name, coef);
#endif
return col;
}
static int
mlp_create_problem_create_constraint (struct MLP_Problem *p, char *name,
unsigned int bound, double lb, double ub)
{
char * op;
int row = glp_add_rows (p->prob, 1);
/* set row name */
glp_set_row_name (p->prob, row, name);
/* set row bounds: <= 0 */
glp_set_row_bnds (p->prob, row, bound, lb, ub);
switch (bound)
{
case GLP_UP:
GNUNET_asprintf(&op, "-inf <= x <= %.2f", ub);
break;
case GLP_DB:
GNUNET_asprintf(&op, "%.2f <= x <= %.2f", lb, ub);
break;
case GLP_FX:
GNUNET_asprintf(&op, "%.2f == x == %.2f", lb, ub);
break;
case GLP_LO:
GNUNET_asprintf(&op, "%.2f <= x <= inf", lb);
break;
default:
GNUNET_asprintf(&op, "ERROR");
break;
}
#if DEBUG_MLP_PROBLEM_CREATION
LOG (GNUNET_ERROR_TYPE_DEBUG, "[P]: Added row [%u] `%s': %s\n",
row, name, op);
#endif
GNUNET_free (op);
return row;
}
/**
* Create the
* - address columns b and n
* - address dependent constraint rows c1, c3
* - peer dependent rows c2 and c9
* - Set address dependent entries in problem matrix as well
*/
static int
mlp_create_problem_add_address_information (void *cls,
const struct GNUNET_PeerIdentity *key,
void *value)
{
struct GAS_MLP_Handle *mlp = cls;
struct MLP_Problem *p = &mlp->p;
struct ATS_Address *address = value;
struct ATS_Peer *peer;
struct MLP_information *mlpi;
char *name;
double cur_bigm;
uint32_t addr_net;
uint32_t addr_net_index;
unsigned long long max_quota;
int c;
/* Check if we have to add this peer due to a pending request */
if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains(mlp->requested_peers, key))
return GNUNET_OK;
mlpi = address->solver_information;
if (NULL == mlpi)
{
fprintf (stderr, "%s %p\n",GNUNET_i2s (&address->peer), address);
GNUNET_break (0);
return GNUNET_OK;
}
addr_net = address->properties.scope;
for (addr_net_index = 0; addr_net_index < GNUNET_NT_COUNT; addr_net_index++)
{
if (mlp->pv.quota_index[addr_net_index] == addr_net)
break;
}
if (addr_net_index >= GNUNET_NT_COUNT)
{
GNUNET_break (0);
return GNUNET_OK;
}
max_quota = 0;
for (c = 0; c < GNUNET_NT_COUNT; c++)
{
if (mlp->pv.quota_out[c] > max_quota)
max_quota = mlp->pv.quota_out[c];
if (mlp->pv.quota_in[c] > max_quota)
max_quota = mlp->pv.quota_in[c];
}
if (max_quota > mlp->pv.BIG_M)
cur_bigm = (double) mlp->pv.BIG_M;
else
cur_bigm = max_quota;
/* Get peer */
peer = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers, key);
GNUNET_assert (NULL != peer);
if (peer->processed == GNUNET_NO)
{
/* Add peer dependent constraints */
/* Add c2) One address active per peer */
GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&address->peer));
peer->r_c2 = mlp_create_problem_create_constraint (p, name, GLP_FX, 1.0, 1.0);
GNUNET_free (name);
if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
{
if (GNUNET_YES == mlp->opt_dbg_optimize_relativity)
{
/* Add c9) Relativity */
GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&address->peer));
peer->r_c9 = mlp_create_problem_create_constraint (p, name, GLP_LO, 0.0, 0.0);
GNUNET_free (name);
/* c9) set coefficient */
mlp_create_problem_set_value (p, peer->r_c9, p->c_r, -peer->f , __LINE__);
}
}
peer->processed = GNUNET_YES;
}
/* Reset addresses' solver information */
mlpi->c_b = 0;
mlpi->c_n = 0;
mlpi->n = 0;
mlpi->r_c1 = 0;
mlpi->r_c3 = 0;
/* Add bandwidth column */
GNUNET_asprintf (&name, "b_%s_%s_%p", GNUNET_i2s (&address->peer), address->plugin, address);
if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
{
mlpi->c_b = mlp_create_problem_create_column (p, name, GLP_CV, GLP_LO, 0.0, 0.0, 0.0);
}
else
{
/* Maximize for bandwidth assignment in feasibility testing */
mlpi->c_b = mlp_create_problem_create_column (p, name, GLP_CV, GLP_LO, 0.0, 0.0, 1.0);
}
GNUNET_free (name);
/* Add address active column */
GNUNET_asprintf (&name, "n_%s_%s_%p", GNUNET_i2s (&address->peer), address->plugin, address);
mlpi->c_n = mlp_create_problem_create_column (p, name, GLP_IV, GLP_DB, 0.0, 1.0, 0.0);
GNUNET_free (name);
/* Add address dependent constraints */
/* Add c1) bandwidth capping: b_t + (-M) * n_t <= 0 */
GNUNET_asprintf(&name, "c1_%s_%s_%p", GNUNET_i2s(&address->peer), address->plugin, address);
mlpi->r_c1 = mlp_create_problem_create_constraint (p, name, GLP_UP, 0.0, 0.0);
GNUNET_free (name);
/* c1) set b = 1 coefficient */
mlp_create_problem_set_value (p, mlpi->r_c1, mlpi->c_b, 1, __LINE__);
/* c1) set n = - min (M, quota) coefficient */
cur_bigm = (double) mlp->pv.quota_out[addr_net_index];
if (cur_bigm > mlp->pv.BIG_M)
cur_bigm = (double) mlp->pv.BIG_M;
mlp_create_problem_set_value (p, mlpi->r_c1, mlpi->c_n, -cur_bigm, __LINE__);
/* Add constraint c 3) minimum bandwidth
* b_t + (-n_t * b_min) >= 0
* */
GNUNET_asprintf(&name, "c3_%s_%s_%p", GNUNET_i2s(&address->peer), address->plugin, address);
mlpi->r_c3 = mlp_create_problem_create_constraint (p, name, GLP_LO, 0.0, 0.0);
GNUNET_free (name);
/* c3) set b = 1 coefficient */
mlp_create_problem_set_value (p, mlpi->r_c3, mlpi->c_b, 1, __LINE__);
/* c3) set n = -b_min coefficient */
mlp_create_problem_set_value (p, mlpi->r_c3, mlpi->c_n, - ((double )mlp->pv.b_min), __LINE__);
/* Set coefficient entries in invariant rows */
/* Feasbility */
/* c 4) minimum connections */
mlp_create_problem_set_value (p, p->r_c4, mlpi->c_n, 1, __LINE__);
/* c 2) 1 address peer peer */
mlp_create_problem_set_value (p, peer->r_c2, mlpi->c_n, 1, __LINE__);
/* c 10) obey network specific quotas
* (1)*b_1 + ... + (1)*b_m <= quota_n
*/
mlp_create_problem_set_value (p, p->r_quota[addr_net_index], mlpi->c_b, 1, __LINE__);
/* Optimality */
if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
{
/* c 6) maximize diversity */
mlp_create_problem_set_value (p, p->r_c6, mlpi->c_n, 1, __LINE__);
/* c 9) relativity */
if (GNUNET_YES == mlp->opt_dbg_optimize_relativity)
mlp_create_problem_set_value (p, peer->r_c9, mlpi->c_b, 1, __LINE__);
/* c 8) utility */
if (GNUNET_YES == mlp->opt_dbg_optimize_utility)
mlp_create_problem_set_value (p, p->r_c8, mlpi->c_b, 1, __LINE__);
/* c 7) Optimize quality */
/* For all quality metrics, set quality of this address */
if (GNUNET_YES == mlp->opt_dbg_optimize_quality)
{
mlp_create_problem_set_value (p,
p->r_q[RQ_QUALITY_METRIC_DELAY],
mlpi->c_b,
address->norm_delay.norm,
__LINE__);
mlp_create_problem_set_value (p,
p->r_q[RQ_QUALITY_METRIC_DISTANCE],
mlpi->c_b,
address->norm_distance.norm,
__LINE__);
}
}
return GNUNET_OK;
}
/**
* Create the invariant columns c4, c6, c10, c8, c7
*/
static void
mlp_create_problem_add_invariant_rows (struct GAS_MLP_Handle *mlp, struct MLP_Problem *p)
{
int c;
/* Feasibility */
/* Row for c4) minimum connection */
/* Number of minimum connections is min(|Peers|, n_min) */
p->r_c4 = mlp_create_problem_create_constraint (p, "c4", GLP_LO, (mlp->pv.n_min > p->num_peers) ? p->num_peers : mlp->pv.n_min, 0.0);
/* Rows for c 10) Enforce network quotas */
for (c = 0; c < GNUNET_NT_COUNT; c++)
{
char * text;
GNUNET_asprintf(&text, "c10_quota_ats_%s",
GNUNET_NT_to_string(mlp->pv.quota_index[c]));
p->r_quota[c] = mlp_create_problem_create_constraint (p, text, GLP_DB, 0.0, mlp->pv.quota_out[c]);
GNUNET_free (text);
}
/* Optimality */
if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
{
char *name;
/* Add row for c6) Maximize for diversity */
if (GNUNET_YES == mlp->opt_dbg_optimize_diversity)
{
p->r_c6 = mlp_create_problem_create_constraint (p, "c6", GLP_FX, 0.0, 0.0);
/* Set c6 ) Setting -D */
mlp_create_problem_set_value (p, p->r_c6, p->c_d, -1, __LINE__);
}
/* Adding rows for c 8) Maximize utility */
if (GNUNET_YES == mlp->opt_dbg_optimize_utility)
{
p->r_c8 = mlp_create_problem_create_constraint (p, "c8", GLP_FX, 0.0, 0.0);
/* -u */
mlp_create_problem_set_value (p, p->r_c8, p->c_u, -1, __LINE__);
}
/* For all quality metrics:
* c 7) Maximize quality, austerity */
if (GNUNET_YES == mlp->opt_dbg_optimize_quality)
{
for (c = 0; c < mlp->pv.m_q; c++)
{
GNUNET_asprintf (&name,
"c7_q%i_%s", c,
print_quality_type (c));
p->r_q[c] = mlp_create_problem_create_constraint (p, name, GLP_FX, 0.0, 0.0);
GNUNET_free (name);
mlp_create_problem_set_value (p,
p->r_q[c],
p->c_q[c], -1, __LINE__);
}
}
}
}
/**
* Create the invariant columns d, u, r, q0 ... qm
*/
static void
mlp_create_problem_add_invariant_columns (struct GAS_MLP_Handle *mlp, struct MLP_Problem *p)
{
if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
{
char *name;
int c;
/* Diversity d column */
if (GNUNET_YES == mlp->opt_dbg_optimize_diversity)
p->c_d = mlp_create_problem_create_column (p, "d", GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_D);
/* Utilization u column */
if (GNUNET_YES == mlp->opt_dbg_optimize_utility)
p->c_u = mlp_create_problem_create_column (p, "u", GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_U);
/* Relativity r column */
if (GNUNET_YES == mlp->opt_dbg_optimize_relativity)
p->c_r = mlp_create_problem_create_column (p, "r", GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_R);
/* Quality metric columns */
if (GNUNET_YES == mlp->opt_dbg_optimize_quality)
{
for (c = 0; c < mlp->pv.m_q; c++)
{
GNUNET_asprintf (&name, "q_%u", c);
p->c_q[c] = mlp_create_problem_create_column (p, name, GLP_CV, GLP_LO, 0.0, 0.0, mlp->pv.co_Q[c]);
GNUNET_free (name);
}
}
}
}
/**
* Create the MLP problem
*
* @param mlp the MLP handle
* @return #GNUNET_OK or #GNUNET_SYSERR
*/
static int
mlp_create_problem (struct GAS_MLP_Handle *mlp)
{
struct MLP_Problem *p = &mlp->p;
int res = GNUNET_OK;
GNUNET_assert (p->prob == NULL);
GNUNET_assert (p->ia == NULL);
GNUNET_assert (p->ja == NULL);
GNUNET_assert (p->ar == NULL);
/* Reset MLP problem struct */
/* create the glpk problem */
p->prob = glp_create_prob ();
GNUNET_assert (NULL != p->prob);
p->num_peers = mlp_create_problem_count_peers (mlp->requested_peers, mlp->env->addresses);
p->num_addresses = mlp_create_problem_count_addresses (mlp->requested_peers,
mlp->env->addresses);
/* Create problem matrix: 10 * #addresses + #q * #addresses + #q, + #peer + 2 + 1 */
p->num_elements = (10 * p->num_addresses + mlp->pv.m_q * p->num_addresses +
mlp->pv.m_q + p->num_peers + 2 + 1);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Rebuilding problem for %u peer(s) and %u addresse(s) and %u quality metrics == %u elements\n",
p->num_peers,
p->num_addresses,
mlp->pv.m_q,
p->num_elements);
/* Set a problem name */
glp_set_prob_name (p->prob, "GNUnet ATS bandwidth distribution");
/* Set optimization direction to maximize */
glp_set_obj_dir (p->prob, GLP_MAX);
/* Create problem matrix */
/* last +1 caused by glpk index starting with one: [1..elements]*/
p->ci = 1;
/* row index */
p->ia = GNUNET_malloc (p->num_elements * sizeof (int));
/* column index */
p->ja = GNUNET_malloc (p->num_elements * sizeof (int));
/* coefficient */
p->ar = GNUNET_malloc (p->num_elements * sizeof (double));
if ((NULL == p->ia) || (NULL == p->ja) || (NULL == p->ar))
{
LOG (GNUNET_ERROR_TYPE_ERROR, _("Problem size too large, cannot allocate memory!\n"));
return GNUNET_SYSERR;
}
/* Adding invariant columns */
mlp_create_problem_add_invariant_columns (mlp, p);
/* Adding address independent constraint rows */
mlp_create_problem_add_invariant_rows (mlp, p);
/* Adding address dependent columns constraint rows */
GNUNET_CONTAINER_multipeermap_iterate (mlp->env->addresses,
&mlp_create_problem_add_address_information,
mlp);
/* Load the matrix */
LOG (GNUNET_ERROR_TYPE_DEBUG, "Loading matrix\n");
glp_load_matrix(p->prob, (p->ci)-1, p->ia, p->ja, p->ar);
if (GNUNET_YES == mlp->opt_dbg_autoscale_problem)
{
glp_scale_prob (p->prob, GLP_SF_AUTO);
}
return res;
}
/**
* Solves the LP problem
*
* @param mlp the MLP Handle
* @return #GNUNET_OK if could be solved, #GNUNET_SYSERR on failure
*/
static int
mlp_solve_lp_problem (struct GAS_MLP_Handle *mlp)
{
int res = 0;
int res_status = 0;
res = glp_simplex(mlp->p.prob, &mlp->control_param_lp);
if (0 == res)
LOG(GNUNET_ERROR_TYPE_DEBUG, "Solving LP problem: %s\n",
mlp_solve_to_string (res));
else
LOG(GNUNET_ERROR_TYPE_DEBUG, "Solving LP problem failed: %s\n",
mlp_solve_to_string (res));
/* Analyze problem status */
res_status = glp_get_status (mlp->p.prob);
switch (res_status) {
case GLP_OPT: /* solution is optimal */
LOG (GNUNET_ERROR_TYPE_INFO,
"Solving LP problem: %s, %s\n",
mlp_solve_to_string(res),
mlp_status_to_string(res_status));
return GNUNET_OK;
default:
LOG (GNUNET_ERROR_TYPE_ERROR,
"Solving LP problem failed: %s %s\n",
mlp_solve_to_string(res),
mlp_status_to_string(res_status));
return GNUNET_SYSERR;
}
}
/**
* Propagates the results when MLP problem was solved
*
* @param cls the MLP handle
* @param key the peer identity
* @param value the address
* @return #GNUNET_OK to continue
*/
static int
mlp_propagate_results (void *cls,
const struct GNUNET_PeerIdentity *key,
void *value)
{
struct GAS_MLP_Handle *mlp = cls;
struct ATS_Address *address;
struct MLP_information *mlpi;
double mlp_bw_in = MLP_NaN;
double mlp_bw_out = MLP_NaN;
double mlp_use = MLP_NaN;
/* Check if we have to add this peer due to a pending request */
if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (mlp->requested_peers,
key))
{
return GNUNET_OK;
}
address = value;
GNUNET_assert (address->solver_information != NULL);
mlpi = address->solver_information;
mlp_bw_in = glp_mip_col_val(mlp->p.prob, mlpi->c_b);/* FIXME */
if (mlp_bw_in > (double) UINT32_MAX)
{
LOG (GNUNET_ERROR_TYPE_DEBUG, "Overflow in assigned bandwidth, reducing ...\n" );
mlp_bw_in = (double) UINT32_MAX;
}
mlp_bw_out = glp_mip_col_val(mlp->p.prob, mlpi->c_b);
if (mlp_bw_out > (double) UINT32_MAX)
{
LOG (GNUNET_ERROR_TYPE_DEBUG, "Overflow in assigned bandwidth, reducing ...\n" );
mlp_bw_out = (double) UINT32_MAX;
}
mlp_use = glp_mip_col_val(mlp->p.prob, mlpi->c_n);
/*
* Debug: solution
* LOG (GNUNET_ERROR_TYPE_INFO, "MLP result address: `%s' `%s' length %u session %u, mlp use %.3f\n",
* GNUNET_i2s(&address->peer), address->plugin,
* address->addr_len, address->session_id);
*/
if (GLP_YES == mlp_use)
{
/* This address was selected by the solver to be used */
mlpi->n = GNUNET_YES;
if (GNUNET_NO == address->active)
{
/* Address was not used before, enabling address */
LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : enabling address\n",
(1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
address->active = GNUNET_YES;
address->assigned_bw_in = mlp_bw_in;
mlpi->b_in = mlp_bw_in;
address->assigned_bw_out = mlp_bw_out;
mlpi->b_out = mlp_bw_out;
if ((NULL == mlp->exclude_peer) || (0 != GNUNET_memcmp (&address->peer, mlp->exclude_peer)))
mlp->env->bandwidth_changed_cb (mlp->env->cls, address);
return GNUNET_OK;
}
else if (GNUNET_YES == address->active)
{
/* Address was used before, check for bandwidth change */
if ((mlp_bw_out != address->assigned_bw_out) ||
(mlp_bw_in != address->assigned_bw_in))
{
LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : bandwidth changed\n",
(1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
address->assigned_bw_in = mlp_bw_in;
mlpi->b_in = mlp_bw_in;
address->assigned_bw_out = mlp_bw_out;
mlpi->b_out = mlp_bw_out;
if ((NULL == mlp->exclude_peer) || (0 != GNUNET_memcmp (&address->peer, mlp->exclude_peer)))
mlp->env->bandwidth_changed_cb (mlp->env->cls, address);
return GNUNET_OK;
}
}
else
GNUNET_break (0);
}
else if (GLP_NO == mlp_use)
{
/* This address was selected by the solver to be not used */
mlpi->n = GNUNET_NO;
if (GNUNET_NO == address->active)
{
/* Address was not used before, nothing to do */
LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : no change\n",
(1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
return GNUNET_OK;
}
else if (GNUNET_YES == address->active)
{
/* Address was used before, disabling address */
LOG (GNUNET_ERROR_TYPE_DEBUG, "%s %.2f : disabling address\n",
(1 == mlp_use) ? "[x]": "[ ]", mlp_bw_out);
address->active = GNUNET_NO;
/* Set bandwidth to 0 */
address->assigned_bw_in = 0;
mlpi->b_in = 0;
address->assigned_bw_out = 0;
mlpi->b_out = 0;
return GNUNET_OK;
}
else
GNUNET_break (0);
}
else
GNUNET_break (0);
return GNUNET_OK;
}
static void
notify (struct GAS_MLP_Handle *mlp,
enum GAS_Solver_Operation op,
enum GAS_Solver_Status stat,
enum GAS_Solver_Additional_Information add)
{
mlp->env->info_cb (mlp->env->cls,
op,
stat,
add);
}
static void
mlp_branch_and_cut_cb (glp_tree *tree, void *info)
{
struct GAS_MLP_Handle *mlp = info;
double mlp_obj = 0;
switch (glp_ios_reason (tree))
{
case GLP_ISELECT:
/* Do nothing here */
break;
case GLP_IPREPRO:
/* Do nothing here */
break;
case GLP_IROWGEN:
/* Do nothing here */
break;
case GLP_IHEUR:
/* Do nothing here */
break;
case GLP_ICUTGEN:
/* Do nothing here */
break;
case GLP_IBRANCH:
/* Do nothing here */
break;
case GLP_IBINGO:
/* A better solution was found */
mlp->ps.mlp_gap = glp_ios_mip_gap (tree);
mlp_obj = glp_mip_obj_val (mlp->p.prob);
mlp->ps.lp_mlp_gap = (abs(mlp_obj - mlp->ps.lp_objective_value)) / (abs(mlp_obj) + DBL_EPSILON);
LOG (GNUNET_ERROR_TYPE_INFO,
"Found better integer solution, current gaps: %.3f <= %.3f, %.3f <= %.3f\n",
mlp->ps.mlp_gap, mlp->pv.mip_gap,
mlp->ps.lp_mlp_gap, mlp->pv.lp_mip_gap);
if (mlp->ps.mlp_gap <= mlp->pv.mip_gap)
{
LOG (GNUNET_ERROR_TYPE_INFO,
"Current LP/MLP gap of %.3f smaller than tolerated gap of %.3f, terminating search\n",
mlp->ps.lp_mlp_gap, mlp->pv.lp_mip_gap);
glp_ios_terminate (tree);
}
if (mlp->ps.lp_mlp_gap <= mlp->pv.lp_mip_gap)
{
LOG (GNUNET_ERROR_TYPE_INFO,
"Current LP/MLP gap of %.3f smaller than tolerated gap of %.3f, terminating search\n",
mlp->ps.lp_mlp_gap, mlp->pv.lp_mip_gap);
glp_ios_terminate (tree);
}
break;
default:
break;
}
//GNUNET_break (0);
}
/**
* Solves the MLP problem
*
* @param solver the MLP Handle
* @return #GNUNET_OK if could be solved, #GNUNET_SYSERR on failure
*/
static int
GAS_mlp_solve_problem (void *solver)
{
struct GAS_MLP_Handle *mlp = solver;
char *filename;
int res_lp = 0;
int mip_res = 0;
int mip_status = 0;
struct GNUNET_TIME_Absolute start_total;
struct GNUNET_TIME_Absolute start_cur_op;
struct GNUNET_TIME_Relative dur_total;
struct GNUNET_TIME_Relative dur_setup;
struct GNUNET_TIME_Relative dur_lp;
struct GNUNET_TIME_Relative dur_mlp;
GNUNET_assert(NULL != solver);
dur_lp = GNUNET_TIME_UNIT_ZERO;
if (GNUNET_YES == mlp->stat_bulk_lock)
{
mlp->stat_bulk_requests++;
return GNUNET_NO;
}
notify(mlp, GAS_OP_SOLVE_START, GAS_STAT_SUCCESS,
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
start_total = GNUNET_TIME_absolute_get();
if (0 == GNUNET_CONTAINER_multipeermap_size(mlp->requested_peers))
{
notify(mlp, GAS_OP_SOLVE_STOP, GAS_STAT_SUCCESS, GAS_INFO_NONE);
return GNUNET_OK; /* No pending requests */
}
if (0 == GNUNET_CONTAINER_multipeermap_size(mlp->env->addresses))
{
notify(mlp, GAS_OP_SOLVE_STOP, GAS_STAT_SUCCESS, GAS_INFO_NONE);
return GNUNET_OK; /* No addresses available */
}
if ((GNUNET_NO == mlp->stat_mlp_prob_changed)
&& (GNUNET_NO == mlp->stat_mlp_prob_updated))
{
LOG(GNUNET_ERROR_TYPE_DEBUG, "No changes to problem\n");
notify(mlp, GAS_OP_SOLVE_STOP, GAS_STAT_SUCCESS, GAS_INFO_NONE);
return GNUNET_OK;
}
if (GNUNET_YES == mlp->stat_mlp_prob_changed)
{
LOG(GNUNET_ERROR_TYPE_DEBUG, "Problem size changed, rebuilding\n");
notify(mlp, GAS_OP_SOLVE_SETUP_START, GAS_STAT_SUCCESS, GAS_INFO_FULL);
mlp_delete_problem (mlp);
if (GNUNET_SYSERR == mlp_create_problem (mlp))
{
notify(mlp, GAS_OP_SOLVE_SETUP_STOP, GAS_STAT_FAIL, GAS_INFO_FULL);
return GNUNET_SYSERR;
}
notify(mlp, GAS_OP_SOLVE_SETUP_STOP, GAS_STAT_SUCCESS, GAS_INFO_FULL);
if (GNUNET_NO == mlp->opt_dbg_intopt_presolver)
{
mlp->control_param_lp.presolve = GLP_YES; /* LP presolver, we need lp solution */
mlp->control_param_mlp.presolve = GNUNET_NO; /* No presolver, we have LP solution */
}
else
{
mlp->control_param_lp.presolve = GNUNET_NO; /* LP presolver, we need lp solution */
mlp->control_param_mlp.presolve = GLP_YES; /* No presolver, we have LP solution */
dur_lp = GNUNET_TIME_UNIT_ZERO;
}
}
else
{
LOG(GNUNET_ERROR_TYPE_DEBUG, "Problem was updated, resolving\n");
}
/* Reset solution info */
mlp->ps.lp_objective_value = 0.0;
mlp->ps.mlp_gap = 1.0;
mlp->ps.mlp_objective_value = 0.0;
mlp->ps.lp_mlp_gap = 0.0;
dur_setup = GNUNET_TIME_absolute_get_duration (start_total);
/* Run LP solver */
if (GNUNET_NO == mlp->opt_dbg_intopt_presolver)
{
notify(mlp, GAS_OP_SOLVE_MLP_LP_START, GAS_STAT_SUCCESS,
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
LOG(GNUNET_ERROR_TYPE_DEBUG,
"Running LP solver %s\n",
(GLP_YES == mlp->control_param_lp.presolve)? "with presolver": "without presolver");
start_cur_op = GNUNET_TIME_absolute_get();
/* Solve LP */
/* Only for debugging:
* Always use LP presolver:
* mlp->control_param_lp.presolve = GLP_YES; */
res_lp = mlp_solve_lp_problem(mlp);
if (GNUNET_OK == res_lp)
{
mlp->ps.lp_objective_value = glp_get_obj_val (mlp->p.prob);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"LP solution was: %.3f\n",
mlp->ps.lp_objective_value);
}
dur_lp = GNUNET_TIME_absolute_get_duration (start_cur_op);
notify(mlp, GAS_OP_SOLVE_MLP_LP_STOP,
(GNUNET_OK == res_lp) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
}
if (GNUNET_YES == mlp->opt_dbg_intopt_presolver)
res_lp = GNUNET_OK;
/* Run MLP solver */
if ((GNUNET_OK == res_lp) || (GNUNET_YES == mlp->opt_dbg_intopt_presolver))
{
LOG(GNUNET_ERROR_TYPE_DEBUG, "Running MLP solver \n");
notify(mlp, GAS_OP_SOLVE_MLP_MLP_START, GAS_STAT_SUCCESS,
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
start_cur_op = GNUNET_TIME_absolute_get();
/* Solve MIP */
/* Only for debugging, always use LP presolver */
if (GNUNET_YES == mlp->opt_dbg_intopt_presolver)
mlp->control_param_mlp.presolve = GNUNET_YES;
mip_res = glp_intopt (mlp->p.prob, &mlp->control_param_mlp);
switch (mip_res)
{
case 0:
/* Successful */
LOG (GNUNET_ERROR_TYPE_INFO,
"Solving MLP problem: %s\n",
mlp_solve_to_string (mip_res));
break;
case GLP_ETMLIM: /* Time limit reached */
case GLP_EMIPGAP: /* MIP gap tolerance limit reached */
case GLP_ESTOP: /* Solver was instructed to stop*/
/* Semi-successful */
LOG (GNUNET_ERROR_TYPE_INFO,
"Solving MLP problem solution was interupted: %s\n",
mlp_solve_to_string (mip_res));
break;
case GLP_EBOUND:
case GLP_EROOT:
case GLP_ENOPFS:
case GLP_ENODFS:
case GLP_EFAIL:
default:
/* Fail */
LOG (GNUNET_ERROR_TYPE_INFO,
"Solving MLP problem failed: %s\n",
mlp_solve_to_string (mip_res));
break;
}
/* Analyze problem status */
mip_status = glp_mip_status(mlp->p.prob);
switch (mip_status)
{
case GLP_OPT: /* solution is optimal */
LOG (GNUNET_ERROR_TYPE_WARNING,
"Solution of MLP problem is optimal: %s, %s\n",
mlp_solve_to_string (mip_res),
mlp_status_to_string (mip_status));
mip_res = GNUNET_OK;
break;
case GLP_FEAS: /* solution is feasible but not proven optimal */
if ( (mlp->ps.mlp_gap <= mlp->pv.mip_gap) ||
(mlp->ps.lp_mlp_gap <= mlp->pv.lp_mip_gap) )
{
LOG (GNUNET_ERROR_TYPE_INFO,
"Solution of MLP problem is feasible and solution within gap constraints: %s, %s\n",
mlp_solve_to_string (mip_res),
mlp_status_to_string (mip_status));
mip_res = GNUNET_OK;
}
else
{
LOG (GNUNET_ERROR_TYPE_WARNING,
"Solution of MLP problem is feasible but solution not within gap constraints: %s, %s\n",
mlp_solve_to_string (mip_res),
mlp_status_to_string (mip_status));
mip_res = GNUNET_SYSERR;
}
break;
case GLP_UNDEF: /* Solution undefined */
case GLP_NOFEAS: /* No feasible solution */
default:
LOG (GNUNET_ERROR_TYPE_ERROR,
"Solving MLP problem failed: %s %s\n",
mlp_solve_to_string (mip_res),
mlp_status_to_string (mip_status));
mip_res = GNUNET_SYSERR;
break;
}
dur_mlp = GNUNET_TIME_absolute_get_duration (start_cur_op);
dur_total = GNUNET_TIME_absolute_get_duration (start_total);
notify(mlp, GAS_OP_SOLVE_MLP_MLP_STOP,
(GNUNET_OK == mip_res) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
}
else
{
/* Do not execute mip solver since lp solution is invalid */
dur_mlp = GNUNET_TIME_UNIT_ZERO;
dur_total = GNUNET_TIME_absolute_get_duration (start_total);
notify(mlp, GAS_OP_SOLVE_MLP_MLP_STOP, GAS_STAT_FAIL,
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
mip_res = GNUNET_SYSERR;
}
/* Notify about end */
notify(mlp, GAS_OP_SOLVE_STOP,
((GNUNET_OK == mip_res) && (GNUNET_OK == mip_res)) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? GAS_INFO_FULL : GAS_INFO_UPDATED);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Execution time for %s solve: (total/setup/lp/mlp) : %llu %llu %llu %llu\n",
(GNUNET_YES == mlp->stat_mlp_prob_changed) ? "full" : "updated",
(unsigned long long) dur_total.rel_value_us,
(unsigned long long) dur_setup.rel_value_us,
(unsigned long long) dur_lp.rel_value_us,
(unsigned long long) dur_mlp.rel_value_us);
/* Save stats */
mlp->ps.lp_res = res_lp;
mlp->ps.mip_res = mip_res;
mlp->ps.lp_presolv = mlp->control_param_lp.presolve;
mlp->ps.mip_presolv = mlp->control_param_mlp.presolve;
mlp->ps.p_cols = glp_get_num_cols(mlp->p.prob);
mlp->ps.p_rows = glp_get_num_rows(mlp->p.prob);
mlp->ps.p_elements = mlp->p.num_elements;
/* Propagate result*/
notify (mlp, GAS_OP_SOLVE_UPDATE_NOTIFICATION_START,
(GNUNET_OK == res_lp) && (GNUNET_OK == mip_res) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
GAS_INFO_NONE);
if ((GNUNET_OK == res_lp) && (GNUNET_OK == mip_res))
{
GNUNET_CONTAINER_multipeermap_iterate(mlp->env->addresses,
&mlp_propagate_results, mlp);
}
notify (mlp, GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP,
(GNUNET_OK == res_lp) && (GNUNET_OK == mip_res) ? GAS_STAT_SUCCESS : GAS_STAT_FAIL,
GAS_INFO_NONE);
struct GNUNET_TIME_Absolute time = GNUNET_TIME_absolute_get();
if ( (GNUNET_YES == mlp->opt_dump_problem_all) ||
(mlp->opt_dump_problem_on_fail && ((GNUNET_OK != res_lp) || (GNUNET_OK != mip_res))) )
{
/* Write problem to disk */
switch (mlp->opt_log_format) {
case MLP_CPLEX:
GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.cplex", mlp->p.num_peers,
mlp->p.num_addresses, time.abs_value_us);
glp_write_lp (mlp->p.prob, NULL, filename);
break;
case MLP_GLPK:
GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.glpk", mlp->p.num_peers,
mlp->p.num_addresses, time.abs_value_us);
glp_write_prob (mlp->p.prob, 0, filename);
break;
case MLP_MPS:
GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.mps", mlp->p.num_peers,
mlp->p.num_addresses, time.abs_value_us);
glp_write_mps (mlp->p.prob, GLP_MPS_FILE, NULL, filename);
break;
default:
break;
}
LOG(GNUNET_ERROR_TYPE_ERROR, "Dumped problem to file: `%s' \n", filename);
GNUNET_free(filename);
}
if ( (mlp->opt_dump_solution_all) ||
(mlp->opt_dump_solution_on_fail && ((GNUNET_OK != res_lp) || (GNUNET_OK != mip_res))) )
{
/* Write solution to disk */
GNUNET_asprintf(&filename, "problem_p_%u_a%u_%llu.sol", mlp->p.num_peers,
mlp->p.num_addresses, time.abs_value_us);
glp_print_mip(mlp->p.prob, filename);
LOG(GNUNET_ERROR_TYPE_ERROR, "Dumped solution to file: `%s' \n", filename);
GNUNET_free(filename);
}
/* Reset change and update marker */
mlp->control_param_lp.presolve = GLP_NO;
mlp->stat_mlp_prob_updated = GNUNET_NO;
mlp->stat_mlp_prob_changed = GNUNET_NO;
if ((GNUNET_OK == res_lp) && (GNUNET_OK == mip_res))
return GNUNET_OK;
else
return GNUNET_SYSERR;
}
/**
* Add a single address to the solve
*
* @param solver the solver Handle
* @param address the address to add
* @param network network type of this address
*/
static void
GAS_mlp_address_add (void *solver,
struct ATS_Address *address,
uint32_t network)
{
struct GAS_MLP_Handle *mlp = solver;
if (GNUNET_NT_COUNT <= network)
{
GNUNET_break (0);
return;
}
if (NULL == address->solver_information)
{
address->solver_information = GNUNET_new (struct MLP_information);
}
else
LOG (GNUNET_ERROR_TYPE_ERROR,
_("Adding address for peer `%s' multiple times\n"),
GNUNET_i2s(&address->peer));
/* Is this peer included in the problem? */
if (NULL ==
GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
&address->peer))
{
/* FIXME: should this be an error? */
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Adding address for peer `%s' without address request\n",
GNUNET_i2s(&address->peer));
return;
}
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Adding address for peer `%s' with address request \n",
GNUNET_i2s(&address->peer));
/* Problem size changed: new address for peer with pending request */
mlp->stat_mlp_prob_changed = GNUNET_YES;
if (GNUNET_YES == mlp->opt_mlp_auto_solve)
GAS_mlp_solve_problem (solver);
}
/**
* Transport properties for this address have changed
*
* @param solver solver handle
* @param address the address
*/
static void
GAS_mlp_address_property_changed (void *solver,
struct ATS_Address *address)
{
struct MLP_information *mlpi = address->solver_information;
struct GAS_MLP_Handle *mlp = solver;
if (NULL == mlp->p.prob)
return; /* There is no MLP problem to update yet */
if (NULL == mlpi)
{
LOG (GNUNET_ERROR_TYPE_INFO,
_("Updating address property for peer `%s' %p not added before\n"),
GNUNET_i2s (&address->peer),
address);
GNUNET_break (0);
return;
}
if (NULL ==
GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
&address->peer))
{
/* Peer is not requested, so no need to update problem */
return;
}
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Updating properties for peer `%s'\n",
GNUNET_i2s(&address->peer));
if (GNUNET_YES == mlp->opt_dbg_feasibility_only)
return;
/* Update c7) [r_q[index]][c_b] = f_q * q_averaged[type_index] */
if ( (GNUNET_YES ==
mlp_create_problem_update_value (&mlp->p,
mlp->p.r_q[RQ_QUALITY_METRIC_DELAY],
mlpi->c_b,
address->norm_delay.norm,
__LINE__)) ||
(GNUNET_YES ==
mlp_create_problem_update_value (&mlp->p,
mlp->p.r_q[RQ_QUALITY_METRIC_DISTANCE],
mlpi->c_b,
address->norm_distance.norm,
__LINE__)) )
{
mlp->stat_mlp_prob_updated = GNUNET_YES;
if (GNUNET_YES == mlp->opt_mlp_auto_solve)
GAS_mlp_solve_problem (solver);
}
}
/**
* Find the active address in the set of addresses of a peer
* @param cls destination
* @param key peer id
* @param value address
* @return #GNUNET_OK
*/
static int
mlp_get_preferred_address_it (void *cls,
const struct GNUNET_PeerIdentity *key,
void *value)
{
static int counter = 0;
struct ATS_Address **aa = cls;
struct ATS_Address *addr = value;
struct MLP_information *mlpi = addr->solver_information;
if (mlpi == NULL)
return GNUNET_YES;
/*
* Debug output
* GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
* "MLP [%u] Peer `%s' %s length %u session %u active %s mlp active %s\n",
* counter, GNUNET_i2s (&addr->peer), addr->plugin, addr->addr_len, addr->session_id,
* (GNUNET_YES == addr->active) ? "active" : "inactive",
* (GNUNET_YES == mlpi->n) ? "active" : "inactive");
*/
if (GNUNET_YES == mlpi->n)
{
(*aa) = addr;
(*aa)->assigned_bw_in = mlpi->b_in;
(*aa)->assigned_bw_out = mlpi->b_out;
return GNUNET_NO;
}
counter++;
return GNUNET_YES;
}
static double
get_peer_pref_value (struct GAS_MLP_Handle *mlp,
const struct GNUNET_PeerIdentity *peer)
{
double res;
const double *preferences;
int c;
preferences = mlp->env->get_preferences (mlp->env->cls, peer);
res = 0.0;
for (c = 0; c < GNUNET_ATS_PREFERENCE_END; c++)
{
/* fprintf (stderr, "VALUE[%u] %s %.3f \n",
* c, GNUNET_i2s (&cur->addr->peer), t[c]); */
res += preferences[c];
}
res /= GNUNET_ATS_PREFERENCE_END;
res += 1.0;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Peer preference for peer `%s' == %.2f\n",
GNUNET_i2s(peer), res);
return res;
}
/**
* Get the preferred address for a specific peer
*
* @param solver the MLP Handle
* @param peer the peer
*/
static void
GAS_mlp_get_preferred_address (void *solver,
const struct GNUNET_PeerIdentity *peer)
{
struct GAS_MLP_Handle *mlp = solver;
struct ATS_Peer *p;
struct ATS_Address *res;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Getting preferred address for `%s'\n",
GNUNET_i2s (peer));
/* Is this peer included in the problem? */
if (NULL ==
GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
peer))
{
LOG (GNUNET_ERROR_TYPE_INFO, "Adding peer `%s' to list of requested_peers with requests\n",
GNUNET_i2s (peer));
p = GNUNET_new (struct ATS_Peer);
p->id = (*peer);
p->f = get_peer_pref_value (mlp, peer);
GNUNET_CONTAINER_multipeermap_put (mlp->requested_peers,
peer, p,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
/* Added new peer, we have to rebuild problem before solving */
mlp->stat_mlp_prob_changed = GNUNET_YES;
if ((GNUNET_YES == mlp->opt_mlp_auto_solve)&&
(GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains(mlp->env->addresses,
peer)))
{
mlp->exclude_peer = peer;
GAS_mlp_solve_problem (mlp);
mlp->exclude_peer = NULL;
}
}
/* Get prefered address */
res = NULL;
GNUNET_CONTAINER_multipeermap_get_multiple (mlp->env->addresses, peer,
&mlp_get_preferred_address_it, &res);
if (NULL != res)
mlp->env->bandwidth_changed_cb (mlp->env->cls,
res);
}
/**
* Deletes a single address in the MLP problem
*
* The MLP problem has to be recreated and the problem has to be resolved
*
* @param solver the MLP Handle
* @param address the address to delete
*/
static void
GAS_mlp_address_delete (void *solver,
struct ATS_Address *address)
{
struct GAS_MLP_Handle *mlp = solver;
struct MLP_information *mlpi;
struct ATS_Address *res;
int was_active;
mlpi = address->solver_information;
if (NULL != mlpi)
{
/* Remove full address */
GNUNET_free (mlpi);
address->solver_information = NULL;
}
was_active = address->active;
address->active = GNUNET_NO;
address->assigned_bw_in = 0;
address->assigned_bw_out = 0;
/* Is this peer included in the problem? */
if (NULL ==
GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers,
&address->peer))
{
LOG (GNUNET_ERROR_TYPE_INFO,
"Deleting address for peer `%s' without address request \n",
GNUNET_i2s(&address->peer));
return;
}
LOG (GNUNET_ERROR_TYPE_INFO,
"Deleting address for peer `%s' with address request \n",
GNUNET_i2s (&address->peer));
/* Problem size changed: new address for peer with pending request */
mlp->stat_mlp_prob_changed = GNUNET_YES;
if (GNUNET_YES == mlp->opt_mlp_auto_solve)
{
GAS_mlp_solve_problem (solver);
}
if (GNUNET_YES == was_active)
{
GAS_mlp_get_preferred_address (solver, &address->peer);
res = NULL;
GNUNET_CONTAINER_multipeermap_get_multiple (mlp->env->addresses,
&address->peer,
&mlp_get_preferred_address_it,
&res);
if (NULL == res)
{
/* No alternative address, disconnecting peer */
mlp->env->bandwidth_changed_cb (mlp->env->cls, address);
}
}
}
/**
* Start a bulk operation
*
* @param solver the solver
*/
static void
GAS_mlp_bulk_start (void *solver)
{
struct GAS_MLP_Handle *s = solver;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Locking solver for bulk operation ...\n");
GNUNET_assert (NULL != solver);
s->stat_bulk_lock ++;
}
static void
GAS_mlp_bulk_stop (void *solver)
{
struct GAS_MLP_Handle *s = solver;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Unlocking solver from bulk operation ...\n");
GNUNET_assert (NULL != solver);
if (s->stat_bulk_lock < 1)
{
GNUNET_break (0);
return;
}
s->stat_bulk_lock --;
if (0 < s->stat_bulk_requests)
{
GAS_mlp_solve_problem (solver);
s->stat_bulk_requests= 0;
}
}
/**
* Stop notifying about address and bandwidth changes for this peer
*
* @param solver the MLP handle
* @param peer the peer
*/
static void
GAS_mlp_stop_get_preferred_address (void *solver,
const struct GNUNET_PeerIdentity *peer)
{
struct GAS_MLP_Handle *mlp = solver;
struct ATS_Peer *p = NULL;
GNUNET_assert (NULL != solver);
GNUNET_assert (NULL != peer);
if (NULL != (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers, peer)))
{
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multipeermap_remove (mlp->requested_peers, peer, p));
GNUNET_free (p);
mlp->stat_mlp_prob_changed = GNUNET_YES;
if (GNUNET_YES == mlp->opt_mlp_auto_solve)
{
GAS_mlp_solve_problem (solver);
}
}
}
/**
* Changes the preferences for a peer in the MLP problem
*
* @param solver the MLP Handle
* @param peer the peer
* @param kind the kind to change the preference
* @param pref_rel the relative score
*/
static void
GAS_mlp_address_change_preference (void *solver,
const struct GNUNET_PeerIdentity *peer,
enum GNUNET_ATS_PreferenceKind kind,
double pref_rel)
{
struct GAS_MLP_Handle *mlp = solver;
struct ATS_Peer *p;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Changing preference for address for peer `%s' to %.2f\n",
GNUNET_i2s(peer),
pref_rel);
GNUNET_STATISTICS_update (mlp->env->stats,
"# LP address preference changes", 1, GNUNET_NO);
/* Update the constraints with changed preferences */
/* Update relativity constraint c9 */
if (NULL == (p = GNUNET_CONTAINER_multipeermap_get (mlp->requested_peers, peer)))
{
LOG (GNUNET_ERROR_TYPE_INFO,
"Updating preference for unknown peer `%s'\n",
GNUNET_i2s(peer));
return;
}
if (GNUNET_NO == mlp->opt_dbg_feasibility_only)
{
p->f = get_peer_pref_value (mlp, peer);
mlp_create_problem_update_value (&mlp->p,
p->r_c9,
mlp->p.c_r,
- p->f,
__LINE__);
/* Problem size changed: new address for peer with pending request */
mlp->stat_mlp_prob_updated = GNUNET_YES;
if (GNUNET_YES == mlp->opt_mlp_auto_solve)
GAS_mlp_solve_problem (solver);
}
}
/**
* Get application feedback for a peer
*
* @param solver the solver handle
* @param application the application
* @param peer the peer to change the preference for
* @param scope the time interval for this feedback: [now - scope .. now]
* @param kind the kind to change the preference
* @param score the score
*/
static void
GAS_mlp_address_preference_feedback (void *solver,
struct GNUNET_SERVICE_Client *application,
const struct GNUNET_PeerIdentity *peer,
const struct GNUNET_TIME_Relative scope,
enum GNUNET_ATS_PreferenceKind kind,
double score)
{
struct GAS_PROPORTIONAL_Handle *s = solver;
GNUNET_assert (NULL != solver);
GNUNET_assert (NULL != peer);
GNUNET_assert (NULL != s);
}
static int
mlp_free_peers (void *cls,
const struct GNUNET_PeerIdentity *key, void *value)
{
struct GNUNET_CONTAINER_MultiPeerMap *map = cls;
struct ATS_Peer *p = value;
GNUNET_assert (GNUNET_YES ==
GNUNET_CONTAINER_multipeermap_remove (map, key, value));
GNUNET_free (p);
return GNUNET_OK;
}
/**
* Shutdown the MLP problem solving component
*
* @param cls the solver handle
* @return NULL
*/
void *
libgnunet_plugin_ats_mlp_done (void *cls)
{
struct GNUNET_ATS_SolverFunctions *sf = cls;
struct GAS_MLP_Handle *mlp = sf->cls;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Shutting down mlp solver\n");
mlp_delete_problem (mlp);
GNUNET_CONTAINER_multipeermap_iterate (mlp->requested_peers,
&mlp_free_peers,
mlp->requested_peers);
GNUNET_CONTAINER_multipeermap_destroy (mlp->requested_peers);
mlp->requested_peers = NULL;
/* Clean up GLPK environment */
glp_free_env();
GNUNET_free (mlp);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Shutdown down of mlp solver complete\n");
return NULL;
}
void *
libgnunet_plugin_ats_mlp_init (void *cls)
{
static struct GNUNET_ATS_SolverFunctions sf;
struct GNUNET_ATS_PluginEnvironment *env = cls;
struct GAS_MLP_Handle * mlp = GNUNET_new (struct GAS_MLP_Handle);
float f_tmp;
unsigned long long tmp;
unsigned int b_min;
unsigned int n_min;
int c;
char *outputformat;
struct GNUNET_TIME_Relative max_duration;
long long unsigned int max_iterations;
/* Init GLPK environment */
int res = glp_init_env();
switch (res) {
case 0:
LOG (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
"initialization successful");
break;
case 1:
LOG (GNUNET_ERROR_TYPE_DEBUG, "GLPK: `%s'\n",
"environment is already initialized");
break;
case 2:
LOG (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
"initialization failed (insufficient memory)");
GNUNET_free(mlp);
return NULL;
break;
case 3:
LOG (GNUNET_ERROR_TYPE_ERROR, "Could not init GLPK: `%s'\n",
"initialization failed (unsupported programming model)");
GNUNET_free(mlp);
return NULL;
break;
default:
break;
}
mlp->opt_dump_problem_all = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DUMP_PROBLEM_ALL");
if (GNUNET_SYSERR == mlp->opt_dump_problem_all)
mlp->opt_dump_problem_all = GNUNET_NO;
mlp->opt_dump_solution_all = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DUMP_SOLUTION_ALL");
if (GNUNET_SYSERR == mlp->opt_dump_solution_all)
mlp->opt_dump_solution_all = GNUNET_NO;
mlp->opt_dump_problem_on_fail = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DUMP_PROBLEM_ON_FAIL");
if (GNUNET_SYSERR == mlp->opt_dump_problem_on_fail)
mlp->opt_dump_problem_on_fail = GNUNET_NO;
mlp->opt_dump_solution_on_fail = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DUMP_SOLUTION_ON_FAIL");
if (GNUNET_SYSERR == mlp->opt_dump_solution_on_fail)
mlp->opt_dump_solution_on_fail = GNUNET_NO;
mlp->opt_dbg_glpk_verbose = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_GLPK_VERBOSE");
if (GNUNET_SYSERR == mlp->opt_dbg_glpk_verbose)
mlp->opt_dbg_glpk_verbose = GNUNET_NO;
mlp->opt_dbg_feasibility_only = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_FEASIBILITY_ONLY");
if (GNUNET_SYSERR == mlp->opt_dbg_feasibility_only)
mlp->opt_dbg_feasibility_only = GNUNET_NO;
if (GNUNET_YES == mlp->opt_dbg_feasibility_only)
LOG (GNUNET_ERROR_TYPE_WARNING,
"MLP solver is configured to check feasibility only!\n");
mlp->opt_dbg_autoscale_problem = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_AUTOSCALE_PROBLEM");
if (GNUNET_SYSERR == mlp->opt_dbg_autoscale_problem)
mlp->opt_dbg_autoscale_problem = GNUNET_NO;
if (GNUNET_YES == mlp->opt_dbg_autoscale_problem)
LOG (GNUNET_ERROR_TYPE_WARNING,
"MLP solver is configured automatically scale the problem!\n");
mlp->opt_dbg_intopt_presolver = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_INTOPT_PRESOLVE");
if (GNUNET_SYSERR == mlp->opt_dbg_intopt_presolver)
mlp->opt_dbg_intopt_presolver = GNUNET_NO;
if (GNUNET_YES == mlp->opt_dbg_intopt_presolver)
LOG (GNUNET_ERROR_TYPE_WARNING,
"MLP solver is configured use the mlp presolver\n");
mlp->opt_dbg_optimize_diversity = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_OPTIMIZE_DIVERSITY");
if (GNUNET_SYSERR == mlp->opt_dbg_optimize_diversity)
mlp->opt_dbg_optimize_diversity = GNUNET_YES;
if (GNUNET_NO == mlp->opt_dbg_optimize_diversity)
LOG (GNUNET_ERROR_TYPE_WARNING,
"MLP solver is not optimizing for diversity\n");
mlp->opt_dbg_optimize_relativity= GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_OPTIMIZE_RELATIVITY");
if (GNUNET_SYSERR == mlp->opt_dbg_optimize_relativity)
mlp->opt_dbg_optimize_relativity = GNUNET_YES;
if (GNUNET_NO == mlp->opt_dbg_optimize_relativity)
LOG (GNUNET_ERROR_TYPE_WARNING,
"MLP solver is not optimizing for relativity\n");
mlp->opt_dbg_optimize_quality = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_OPTIMIZE_QUALITY");
if (GNUNET_SYSERR == mlp->opt_dbg_optimize_quality)
mlp->opt_dbg_optimize_quality = GNUNET_YES;
if (GNUNET_NO == mlp->opt_dbg_optimize_quality)
LOG (GNUNET_ERROR_TYPE_WARNING,
"MLP solver is not optimizing for quality\n");
mlp->opt_dbg_optimize_utility = GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
"ats", "MLP_DBG_OPTIMIZE_UTILITY");
if (GNUNET_SYSERR == mlp->opt_dbg_optimize_utility)
mlp->opt_dbg_optimize_utility = GNUNET_YES;
if (GNUNET_NO == mlp->opt_dbg_optimize_utility)
LOG (GNUNET_ERROR_TYPE_WARNING,
"MLP solver is not optimizing for utility\n");
if ( (GNUNET_NO == mlp->opt_dbg_optimize_utility) &&
(GNUNET_NO == mlp->opt_dbg_optimize_quality) &&
(GNUNET_NO == mlp->opt_dbg_optimize_relativity) &&
(GNUNET_NO == mlp->opt_dbg_optimize_utility) &&
(GNUNET_NO == mlp->opt_dbg_feasibility_only))
{
LOG (GNUNET_ERROR_TYPE_ERROR,
_("MLP solver is not optimizing for anything, changing to feasibility check\n"));
mlp->opt_dbg_feasibility_only = GNUNET_YES;
}
if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (env->cfg,
"ats", "MLP_LOG_FORMAT", &outputformat))
mlp->opt_log_format = MLP_CPLEX;
else
{
GNUNET_STRINGS_utf8_toupper(outputformat, outputformat);
if (0 == strcmp (outputformat, "MPS"))
{
mlp->opt_log_format = MLP_MPS;
}
else if (0 == strcmp (outputformat, "CPLEX"))
{
mlp->opt_log_format = MLP_CPLEX;
}
else if (0 == strcmp (outputformat, "GLPK"))
{
mlp->opt_log_format = MLP_GLPK;
}
else
{
LOG (GNUNET_ERROR_TYPE_WARNING,
"Invalid log format `%s' in configuration, using CPLEX!\n",
outputformat);
mlp->opt_log_format = MLP_CPLEX;
}
GNUNET_free (outputformat);
}
mlp->pv.BIG_M = (double) BIG_M_VALUE;
mlp->pv.mip_gap = (double) 0.0;
if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
"MLP_MAX_MIP_GAP", &f_tmp))
{
if ((f_tmp < 0.0) || (f_tmp > 1.0))
{
LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
"MIP gap", f_tmp);
}
else
{
mlp->pv.mip_gap = f_tmp;
LOG (GNUNET_ERROR_TYPE_INFO, "Using %s of %.3f\n",
"MIP gap", f_tmp);
}
}
mlp->pv.lp_mip_gap = (double) 0.0;
if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
"MLP_MAX_LP_MIP_GAP", &f_tmp))
{
if ((f_tmp < 0.0) || (f_tmp > 1.0))
{
LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
"LP/MIP", f_tmp);
}
else
{
mlp->pv.lp_mip_gap = f_tmp;
LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
"LP/MIP", f_tmp);
}
}
/* Get timeout for iterations */
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time(env->cfg, "ats",
"MLP_MAX_DURATION", &max_duration))
{
max_duration = MLP_MAX_EXEC_DURATION;
}
/* Get maximum number of iterations */
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_size(env->cfg, "ats",
"MLP_MAX_ITERATIONS", &max_iterations))
{
max_iterations = MLP_MAX_ITERATIONS;
}
/* Get diversity coefficient from configuration */
mlp->pv.co_D = MLP_DEFAULT_D;
if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
"MLP_COEFFICIENT_D", &f_tmp))
{
if ((f_tmp < 0.0))
{
LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
"MLP_COEFFICIENT_D", f_tmp);
}
else
{
mlp->pv.co_D = f_tmp;
LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
"MLP_COEFFICIENT_D", f_tmp);
}
}
/* Get relativity coefficient from configuration */
mlp->pv.co_R = MLP_DEFAULT_R;
if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
"MLP_COEFFICIENT_R", &f_tmp))
{
if ((f_tmp < 0.0))
{
LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
"MLP_COEFFICIENT_R", f_tmp);
}
else
{
mlp->pv.co_R = f_tmp;
LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
"MLP_COEFFICIENT_R", f_tmp);
}
}
/* Get utilization coefficient from configuration */
mlp->pv.co_U = MLP_DEFAULT_U;
if (GNUNET_SYSERR != GNUNET_CONFIGURATION_get_value_float (env->cfg, "ats",
"MLP_COEFFICIENT_U", &f_tmp))
{
if ((f_tmp < 0.0))
{
LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid %s configuration %f \n"),
"MLP_COEFFICIENT_U", f_tmp);
}
else
{
mlp->pv.co_U = f_tmp;
LOG (GNUNET_ERROR_TYPE_INFO, "Using %s gap of %.3f\n",
"MLP_COEFFICIENT_U", f_tmp);
}
}
/* Get quality metric coefficients from configuration */
for (c = 0; c < RQ_QUALITY_METRIC_COUNT; c++)
{
/* initialize quality coefficients with default value 1.0 */
mlp->pv.co_Q[c] = MLP_DEFAULT_QUALITY;
}
if (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
"MLP_COEFFICIENT_QUALITY_DELAY",
&tmp))
mlp->pv.co_Q[RQ_QUALITY_METRIC_DELAY] = (double) tmp / 100;
else
mlp->pv.co_Q[RQ_QUALITY_METRIC_DELAY] = MLP_DEFAULT_QUALITY;
if (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
"MLP_COEFFICIENT_QUALITY_DISTANCE",
&tmp))
mlp->pv.co_Q[RQ_QUALITY_METRIC_DISTANCE] = (double) tmp / 100;
else
mlp->pv.co_Q[RQ_QUALITY_METRIC_DISTANCE] = MLP_DEFAULT_QUALITY;
/* Get minimum bandwidth per used address from configuration */
if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
"MLP_MIN_BANDWIDTH",
&tmp))
b_min = tmp;
else
{
b_min = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
}
/* Get minimum number of connections from configuration */
if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_size (env->cfg, "ats",
"MLP_MIN_CONNECTIONS",
&tmp))
n_min = tmp;
else
n_min = MLP_DEFAULT_MIN_CONNECTIONS;
/* Init network quotas */
for (c = 0; c < GNUNET_NT_COUNT; c++)
{
mlp->pv.quota_index[c] = c;
mlp->pv.quota_out[c] = env->out_quota[c];
mlp->pv.quota_in[c] = env->in_quota[c];
LOG (GNUNET_ERROR_TYPE_INFO,
"Quota for network `%s' (in/out) %llu/%llu\n",
GNUNET_NT_to_string (c),
mlp->pv.quota_out[c],
mlp->pv.quota_in[c]);
/* Check if defined quota could make problem unsolvable */
if ((n_min * b_min) > mlp->pv.quota_out[c])
{
LOG (GNUNET_ERROR_TYPE_INFO,
_("Adjusting inconsistent outbound quota configuration for network `%s', is %llu must be at least %llu\n"),
GNUNET_NT_to_string(mlp->pv.quota_index[c]),
mlp->pv.quota_out[c],
(n_min * b_min));
mlp->pv.quota_out[c] = (n_min * b_min);
}
if ((n_min * b_min) > mlp->pv.quota_in[c])
{
LOG (GNUNET_ERROR_TYPE_INFO,
_("Adjusting inconsistent inbound quota configuration for network `%s', is %llu must be at least %llu\n"),
GNUNET_NT_to_string(mlp->pv.quota_index[c]),
mlp->pv.quota_in[c],
(n_min * b_min));
mlp->pv.quota_in[c] = (n_min * b_min);
}
/* Check if bandwidth is too big to make problem solvable */
if (mlp->pv.BIG_M < mlp->pv.quota_out[c])
{
LOG (GNUNET_ERROR_TYPE_INFO,
_("Adjusting outbound quota configuration for network `%s'from %llu to %.0f\n"),
GNUNET_NT_to_string(mlp->pv.quota_index[c]),
mlp->pv.quota_out[c],
mlp->pv.BIG_M);
mlp->pv.quota_out[c] = mlp->pv.BIG_M ;
}
if (mlp->pv.BIG_M < mlp->pv.quota_in[c])
{
LOG (GNUNET_ERROR_TYPE_INFO,
_("Adjusting inbound quota configuration for network `%s' from %llu to %.0f\n"),
GNUNET_NT_to_string(mlp->pv.quota_index[c]),
mlp->pv.quota_in[c],
mlp->pv.BIG_M);
mlp->pv.quota_in[c] = mlp->pv.BIG_M ;
}
}
mlp->env = env;
sf.cls = mlp;
sf.s_add = &GAS_mlp_address_add;
sf.s_address_update_property = &GAS_mlp_address_property_changed;
sf.s_get = &GAS_mlp_get_preferred_address;
sf.s_get_stop = &GAS_mlp_stop_get_preferred_address;
sf.s_pref = &GAS_mlp_address_change_preference;
sf.s_feedback = &GAS_mlp_address_preference_feedback;
sf.s_del = &GAS_mlp_address_delete;
sf.s_bulk_start = &GAS_mlp_bulk_start;
sf.s_bulk_stop = &GAS_mlp_bulk_stop;
/* Setting MLP Input variables */
mlp->pv.b_min = b_min;
mlp->pv.n_min = n_min;
mlp->pv.m_q = RQ_QUALITY_METRIC_COUNT;
mlp->stat_mlp_prob_changed = GNUNET_NO;
mlp->stat_mlp_prob_updated = GNUNET_NO;
mlp->opt_mlp_auto_solve = GNUNET_YES;
mlp->requested_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
mlp->stat_bulk_requests = 0;
mlp->stat_bulk_lock = 0;
/* Setup GLPK */
/* Redirect GLPK output to GNUnet logging */
glp_term_hook (&mlp_term_hook, (void *) mlp);
/* Init LP solving parameters */
glp_init_smcp(&mlp->control_param_lp);
mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
if (GNUNET_YES == mlp->opt_dbg_glpk_verbose)
mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
mlp->control_param_lp.it_lim = max_iterations;
mlp->control_param_lp.tm_lim = max_duration.rel_value_us / 1000LL;
/* Init MLP solving parameters */
glp_init_iocp(&mlp->control_param_mlp);
/* Setting callback function */
mlp->control_param_mlp.cb_func = &mlp_branch_and_cut_cb;
mlp->control_param_mlp.cb_info = mlp;
mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
mlp->control_param_mlp.mip_gap = mlp->pv.mip_gap;
if (GNUNET_YES == mlp->opt_dbg_glpk_verbose)
mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
mlp->control_param_mlp.tm_lim = max_duration.rel_value_us / 1000LL;
LOG (GNUNET_ERROR_TYPE_DEBUG, "solver ready\n");
return &sf;
}
/* end of plugin_ats_mlp.c */