#include "gnunet-service-ats_addresses_mlp.h"
#include "gnunet_statistics_service.h"
#include "glpk.h"
-#include "float.h"
-#define WRITE_MLP GNUNET_YES
-#define DEBUG_ATS GNUNET_YES
-/* A very big value */
-#define M DBL_MAX
+#define WRITE_MLP GNUNET_NO
+#define DEBUG_ATS GNUNET_NO
+#define VERBOSE_GLPK GNUNET_NO
/**
* Translate glpk solver error codes to text
return "unknown error";
}
+/**
+ * Translate ATS properties to text
+ * Just intended for debugging
+ *
+ * @param retcode return code
+ * @return string with result
+ */
+const char *
+mlp_ats_to_string (int ats_index)
+{
+ switch (ats_index) {
+ case GNUNET_ATS_ARRAY_TERMINATOR:
+ return "GNUNET_ATS_ARRAY_TERMINATOR";
+ break;
+ case GNUNET_ATS_UTILIZATION_UP:
+ return "GNUNET_ATS_UTILIZATION_UP";
+ break;
+ case GNUNET_ATS_UTILIZATION_DOWN:
+ return "GNUNET_ATS_UTILIZATION_DOWN";
+ break;
+ case GNUNET_ATS_COST_LAN:
+ return "GNUNET_ATS_COST_LAN";
+ break;
+ case GNUNET_ATS_COST_WAN:
+ return "GNUNET_ATS_COST_LAN";
+ break;
+ case GNUNET_ATS_COST_WLAN:
+ return "GNUNET_ATS_COST_WLAN";
+ break;
+ case GNUNET_ATS_NETWORK_TYPE:
+ return "GNUNET_ATS_NETWORK_TYPE";
+ break;
+ case GNUNET_ATS_QUALITY_NET_DELAY:
+ return "GNUNET_ATS_QUALITY_NET_DELAY";
+ break;
+ case GNUNET_ATS_QUALITY_NET_DISTANCE:
+ return "GNUNET_ATS_QUALITY_NET_DISTANCE";
+ break;
+ default:
+ return "unknown";
+ break;
+ }
+ GNUNET_break (0);
+ return "unknown error";
+}
+
/**
* Find a peer in the DLL
* @param the peer to find
struct ATS_Address *address = value;
struct MLP_information *mlpi;
unsigned int row_index;
+ char *name;
GNUNET_assert (address->mlp_information != NULL);
mlpi = (struct MLP_information *) address->mlp_information;
*/
row_index = glp_add_rows (mlp->prob, 1);
mlpi->r_c1 = row_index;
+ /* set row name */
+ GNUNET_asprintf(&name, "c1_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
+ glp_set_row_name (mlp->prob, row_index, name);
+ GNUNET_free (name);
/* set row bounds: <= 0 */
glp_set_row_bnds (mlp->prob, row_index, GLP_UP, 0.0, 0.0);
-
mlp->ia[mlp->ci] = row_index;
mlp->ja[mlp->ci] = mlpi->c_b;
mlp->ar[mlp->ci] = 1;
mlp->ia[mlp->ci] = row_index;
mlp->ja[mlp->ci] = mlpi->c_n;
- mlp->ar[mlp->ci] = -M;
+ mlp->ar[mlp->ci] = -mlp->BIG_M;
mlp->ci++;
/* c 3) minimum bandwidth
*/
row_index = glp_add_rows (mlp->prob, 1);
+ /* set row name */
+ GNUNET_asprintf(&name, "c3_%s_%s", GNUNET_i2s(&address->peer), address->plugin);
+ glp_set_row_name (mlp->prob, row_index, name);
+ GNUNET_free (name);
mlpi->r_c3 = row_index;
/* set row bounds: >= 0 */
glp_set_row_bnds (mlp->prob, row_index, GLP_LO, 0.0, 0.0);
mlp->ia[mlp->ci] = row_index;
mlp->ja[mlp->ci] = mlpi->c_n;
- mlp->ar[mlp->ci] = -mlp->b_min;
+ mlp->ar[mlp->ci] = - (double) mlp->b_min;
mlp->ci++;
-#if 0
+
/* c 4) minimum connections
* (1)*n_1 + ... + (1)*n_m >= n_min
*/
mlp->ja[mlp->ci] = mlpi->c_n;
mlp->ar[mlp->ci] = 1;
mlp->ci++;
-#endif
return GNUNET_OK;
}
+/**
+ * Find the required ATS information for an address
+ *
+ * @param addr the address
+ * @param ats_index the desired ATS index
+ *
+ * @return the index on success, otherwise GNUNET_SYSERR
+ */
+
+static int
+mlp_lookup_ats (struct ATS_Address *addr, int ats_index)
+{
+ struct GNUNET_ATS_Information * ats = addr->ats;
+ int c;
+ int found = GNUNET_NO;
+ for (c = 0; c < addr->ats_count; c++)
+ {
+ if (ats[c].type == ats_index)
+ {
+ found = GNUNET_YES;
+ break;
+ }
+ }
+ if (found == GNUNET_YES)
+ return c;
+ else
+ return GNUNET_SYSERR;
+}
/**
* Adds the problem constraints for all addresses
mlp_add_constraints_all_addresses (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
{
unsigned int n_addresses;
- int row_index;
- //int c;
+ int c;
+ char *name;
/* Problem matrix*/
n_addresses = GNUNET_CONTAINER_multihashmap_size(addresses);
*
* c 7) quality
* #rows: |quality properties|
- * #indices:|quality properties| + |n_addresses|
+ * #indices: |n_addresses| + |quality properties|
+ *
+ * c 8) utilization
+ * #rows: 1
+ * #indices: |n_addresses| + 1
+ *
+ * c 9) relativity
+ * #rows: |peers|
+ * #indices: |n_addresses| + |peers|
* */
- int pi = ((7 * n_addresses) /*+ (2 * n_addresses + mlp->m_q + 1)*/);
+ /* last +1 caused by glpk index starting with one */
+ int pi = ((7 * n_addresses) + (4 * n_addresses + mlp->m_q + mlp->c_p + 2) + 1);
mlp->cm_size = pi;
- mlp->ci = 0;
+ mlp->ci = 1;
/* row index */
int *ia = GNUNET_malloc (pi * sizeof (int));
int min = mlp->n_min;
if (mlp->n_min > mlp->c_p)
min = mlp->c_p;
-/*
+
mlp->r_c4 = glp_add_rows (mlp->prob, 1);
- glp_set_row_bnds (mlp->prob, mlp->r_c4, GLP_FX, min, min);
-*/
+ glp_set_row_name (mlp->prob, mlp->r_c4, "c4");
+ glp_set_row_bnds (mlp->prob, mlp->r_c4, GLP_LO, min, min);
+
/* Add row for c6) */
-#if 0
+
mlp->r_c6 = glp_add_rows (mlp->prob, 1);
/* Set type type to fix */
glp_set_row_bnds (mlp->prob, mlp->r_c6, GLP_FX, 0.0, 0.0);
ja[mlp->ci] = mlp->c_d;
ar[mlp->ci] = -1;
mlp->ci++;
-#endif
+
GNUNET_CONTAINER_multihashmap_iterate (addresses, create_constraint_it, mlp);
/* Adding constraint rows
*
* c 2) 1 address per peer
* sum (n_p1_1 + ... + n_p1_n) = 1
- */
-return;
- /* Adding rows for c 2) */
- row_index = glp_add_rows (mlp->prob, mlp->c_p);
+ *
+ * c 8) utilization
+ * sum (f_p * b_p1_1 + ... + f_p * b_p1_n) - u = 0
+ *
+ * c 9) relativity
+ * V p : sum (bt_1 + ... +bt_n) - f_p * r = 0
+ * */
+
+ /* Adding rows for c 8) */
+ mlp->r_c8 = glp_add_rows (mlp->prob, mlp->c_p);
+ glp_set_row_name (mlp->prob, mlp->r_c8, "c8");
+ /* Set row bound == 0 */
+ glp_set_row_bnds (mlp->prob, mlp->r_c8, GLP_FX, 0.0, 0.0);
+ /* -u */
+ ia[mlp->ci] = mlp->r_c8;
+ ja[mlp->ci] = mlp->c_u;
+ ar[mlp->ci] = -1;
+ mlp->ci++;
struct ATS_Peer * peer = mlp->peer_head;
while (peer != NULL)
{
struct ATS_Address *addr = peer->head;
- struct MLP_information *mlpi = (struct MLP_information *) addr->mlp_information;
- /* Adding row for c 2) */
+ struct MLP_information *mlpi = NULL;
+
+ /* Adding rows for c 2) */
+ peer->r_c2 = glp_add_rows (mlp->prob, 1);
+ GNUNET_asprintf(&name, "c2_%s", GNUNET_i2s(&peer->id));
+ glp_set_row_name (mlp->prob, peer->r_c2, name);
+ GNUNET_free (name);
/* Set row bound == 1 */
- glp_set_row_bnds (mlp->prob, row_index, GLP_FX, 1.0, 1.0);
+ glp_set_row_bnds (mlp->prob, peer->r_c2, GLP_FX, 1.0, 1.0);
+
+ /* Adding rows for c 9) */
+ peer->r_c9 = glp_add_rows (mlp->prob, 1);
+ GNUNET_asprintf(&name, "c9_%s", GNUNET_i2s(&peer->id));
+ glp_set_row_name (mlp->prob, peer->r_c9, name);
+ GNUNET_free (name);
+ /* Set row bound == 0 */
+ glp_set_row_bnds (mlp->prob, peer->r_c9, GLP_LO, 0.0, 0.0);
+
+ /* Set -r */
+ ia[mlp->ci] = peer->r_c9;
+ ja[mlp->ci] = mlp->c_r;
+ ar[mlp->ci] = -1;
+ mlp->ci++;
while (addr != NULL)
{
- ia[mlp->ci] = row_index;
+ mlpi = (struct MLP_information *) addr->mlp_information;
+
+ ia[mlp->ci] = peer->r_c2;
ja[mlp->ci] = mlpi->c_n;
ar[mlp->ci] = 1;
mlp->ci++;
+ ia[mlp->ci] = mlp->r_c8;
+ ja[mlp->ci] = mlpi->c_b;
+ ar[mlp->ci] = peer->f;
+ mlp->ci++;
+
+ ia[mlp->ci] = peer->r_c9;
+ ja[mlp->ci] = mlpi->c_b;
+ ar[mlp->ci] = 1;
+ mlp->ci++;
+
addr = addr->next;
}
peer = peer->next;
}
+ /* c 7) For all quality metrics */
- /* For all quality metrics */
-/*
for (c = 0; c < mlp->m_q; c++)
{
struct ATS_Peer *p = mlp->peer_head;
+ struct ATS_Address *addr = p->head;
+ struct MLP_information * mlpi;
+ double value = 1.0;
+
while (p != NULL)
{
- ia[mlp->ci] = row_index;
+ /* Adding rows for c 7) */
+ mlp->r_q[c] = glp_add_rows (mlp->prob, 1);
+ GNUNET_asprintf(&name, "c7_q%i_atsi_%i", c, mlp->q[c]);
+ glp_set_row_name (mlp->prob, mlp->r_q[c], name);
+ GNUNET_free (name);
+ /* Set row bound == 0 */
+ glp_set_row_bnds (mlp->prob, mlp->r_q[c], GLP_LO, 0.0, 0.0);
+
+ /* Set -q_m */
+ ia[mlp->ci] = mlp->r_q[c];
ja[mlp->ci] = mlp->c_q[c];
- ar[mlp->ci] = mlp->;
+ ar[mlp->ci] = -1;
mlp->ci++;
+ while (addr != NULL)
+ {
+ mlpi = addr->mlp_information;
+ /* lookup ATS information */
+ int index = mlp_lookup_ats(addr, mlp->q[c]);
+
+ if (index != GNUNET_SYSERR)
+ {
+ value = (double) addr->ats[index].value;
+#if DEBUG_ATS
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Quality %i with ATS property `%s' has index %i in addresses ats information has value %f\n", c, mlp_ats_to_string(mlp->q[c]), index, (double) addr->ats[index].value);
+#endif
+ }
+#if DEBUG_ATS
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Quality %i with ATS property `%s' not existing\n", c, mlp_ats_to_string(mlp->q[c]), index);
+#endif
+ mlpi = addr->mlp_information;
+
+ mlpi->r_q[c] = mlp->r_q[c];
+ mlpi->c_q[c] = mlpi->c_b;
+ mlpi->q[c] = value;
+
+ ia[mlp->ci] = mlp->r_q[c];
+ ja[mlp->ci] = mlpi->c_b;
+ ar[mlp->ci] = p->f * value;
+ mlp->ci++;
+
+ addr = addr->next;
+ }
p = p->next;
}
}
-*/
}
mlpi->c_b = col;
mlpi->c_n = col + 1;
- GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
- "ats-mlp",
- "Culoumn %i %i\n", mlpi->c_b, mlpi->c_n);
-
GNUNET_asprintf (&name, "b_%s_%s", GNUNET_i2s (&address->peer), address->plugin);
glp_set_col_name (mlp->prob, mlpi->c_b , name);
GNUNET_free (name);
/* Continuous value*/
glp_set_col_kind (mlp->prob, mlpi->c_b , GLP_CV);
/* Objective function coefficient == 0 */
- glp_set_obj_coef (mlp->prob, mlpi->c_b , 1);
+ glp_set_obj_coef (mlp->prob, mlpi->c_b , 0);
/* Add usage column */
/* Integer value*/
glp_set_col_kind (mlp->prob, mlpi->c_n, GLP_IV);
/* Objective function coefficient == 0 */
- glp_set_obj_coef (mlp->prob, mlpi->c_n, 1);
+ glp_set_obj_coef (mlp->prob, mlpi->c_n, 0);
return GNUNET_OK;
}
mlp_create_problem (struct GAS_MLP_Handle *mlp, struct GNUNET_CONTAINER_MultiHashMap * addresses)
{
int res = GNUNET_OK;
-/*
int col;
int c;
char *name;
-*/
+
GNUNET_assert (mlp->prob == NULL);
/* create the glpk problem */
/* Set optimization direction to maximize */
glp_set_obj_dir (mlp->prob, GLP_MAX);
- /* Setting initial index == 1
- * glpk matrix is from 1 .. number_elements*/
-
- mlp->ci = 1;
/* Adding invariant columns */
/* Diversity d column */
-#if 0
+
col = glp_add_cols (mlp->prob, 1);
mlp->c_d = col;
/* Column name */
/* Coefficient == Qm */
glp_set_obj_coef (mlp->prob, col + c, mlp->co_Q[c]);
}
-#endif
+
/* Add columns for addresses */
GNUNET_CONTAINER_multihashmap_iterate (addresses, create_columns_it, mlp);
return GNUNET_OK;
}
-int mlp_solve_problem (struct GAS_MLP_Handle *mlp);
+int GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp);
static void
mlp_scheduler (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduled problem solving\n");
#endif
if (mlp->addr_in_problem != 0)
- mlp_solve_problem(mlp);
+ GAS_mlp_solve_problem(mlp);
}
* @return GNUNET_OK if could be solved, GNUNET_SYSERR on failure
*/
int
-mlp_solve_problem (struct GAS_MLP_Handle *mlp)
+GAS_mlp_solve_problem (struct GAS_MLP_Handle *mlp)
{
int res;
mlp->last_execution = GNUNET_TIME_absolute_get ();
glp_print_mip (mlp->prob, name);
GNUNET_free (name);
# endif
-
-
if (res != GNUNET_OK)
{
#if DEBUG_ATS
return GNUNET_SYSERR;
}
- res = glp_mip_status(mlp->prob);
-
- if (res != GNUNET_OK)
- {
#if DEBUG_ATS
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "LP Problem solving failed\n");
-#endif
- return GNUNET_SYSERR;
- }
-
-#if DEBUG_ATS
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved: %i %s\n", res, mlp_status_to_string(res));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Problem solved\n");
#endif
/* Process result */
mlp->stats = (struct GNUNET_STATISTICS_Handle *) stats;
mlp->max_iterations = max_iterations;
mlp->max_exec_duration = max_duration;
+ mlp->auto_solve = GNUNET_YES;
/* Redirect GLPK output to GNUnet logging */
glp_error_hook((void *) mlp, &mlp_term_hook);
/* Init LP solving parameters */
glp_init_smcp(&mlp->control_param_lp);
-#if DEBUG_ATS
+#if VERBOSE_GLPK
mlp->control_param_lp.msg_lev = GLP_MSG_ALL;
#else
mlp->control_param_lp.msg_lev = GLP_MSG_OFF;
/* Init MLP solving parameters */
glp_init_iocp(&mlp->control_param_mlp);
-#if DEBUG_ATS
+#if VERBOSE_GLPK
mlp->control_param_mlp.msg_lev = GLP_MSG_ALL;
#else
mlp->control_param_mlp.msg_lev = GLP_MSG_OFF;
mlp->last_execution = GNUNET_TIME_absolute_get_forever();
+
+ mlp->BIG_M = (double) UINT32_MAX;
mlp->co_D = D;
mlp->co_R = R;
mlp->co_U = U;
return mlp;
}
+
/**
* Updates a single address in the MLP problem
*
{
int new;
struct MLP_information *mlpi;
- int c;
GNUNET_STATISTICS_update (mlp->stats,"# LP address updates", 1, GNUNET_NO);
if (new == GNUNET_YES)
{
mlpi = GNUNET_malloc (sizeof (struct MLP_information));
+
+ int c;
+ for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
+ {
+ mlpi->c_q[c] = 0;
+ mlpi->r_q[c] = 0;
+ mlpi->q[c] = 0.0;
+ }
+
address->mlp_information = mlpi;
mlp->addr_in_problem ++;
if (peer == NULL)
{
#if DEBUG_ATS
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding new peer `%s'\n", GNUNET_i2s (&address->peer));
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding new peer `%s'\n",
+ GNUNET_i2s (&address->peer));
#endif
peer = GNUNET_malloc (sizeof (struct ATS_Peer));
peer->head = NULL;
peer->tail = NULL;
+ int c;
for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
{
peer->f_q[c] = 1.0;
}
+ peer->f = 1.0;
memcpy (&peer->id, &address->peer, sizeof (struct GNUNET_PeerIdentity));
GNUNET_assert(address->prev == NULL);
else
{
#if DEBUG_ATS
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding address to peer `%s'\n", GNUNET_i2s (&address->peer));
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Adding address to peer `%s'\n",
+ GNUNET_i2s (&address->peer));
#endif
GNUNET_CONTAINER_DLL_insert (peer->head, peer->tail, address);
}
else
{
#if DEBUG_ATS
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Updating existing address to peer `%s'\n", GNUNET_i2s (&address->peer));
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Updating existing address to peer `%s'\n",
+ GNUNET_i2s (&address->peer));
#endif
+ mlpi = address->mlp_information;
+ int c;
+ for (c = 0; c < GNUNET_ATS_QualityPropertiesCount; c++)
+ {
+ int index = mlp_lookup_ats(address, mlp->q[c]);
+ if ((index != GNUNET_SYSERR) && (mlpi->c_q[c] != 0) && (mlpi->r_q[c] != 0))
+ {
+ if (mlpi->q[c] == (double) address->ats[index].value)
+ break;
+#if DEBUG_ATS
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Updating address for peer `%s' value `%s'from %f to %f\n",
+ GNUNET_i2s (&address->peer),
+ mlp_ats_to_string(mlp->q[c]),
+ mlpi->q[c],
+ (double) address->ats[index].value);
+#endif
+ switch (mlp->q[c])
+ {
+ case GNUNET_ATS_QUALITY_NET_DELAY:
+ mlpi->q[c] = (double) address->ats[index].value;
+ break;
+ case GNUNET_ATS_QUALITY_NET_DISTANCE:
+ mlpi->q[c] = (double) address->ats[index].value;
+ break;
+ default:
+ break;
+ }
+
+ /* Get current number of columns */
+ int cols = glp_get_num_cols(mlp->prob);
+ int *ind = GNUNET_malloc (cols * sizeof (int));
+ double *val = GNUNET_malloc (cols * sizeof (double));
+
+ /* Get the matrix row of quality */
+ cols = glp_get_mat_row(mlp->prob, mlp->r_q[c], ind, val);
+
+ int c2;
+ /* Get the index if matrix row of quality */
+ for (c2 = 1; c2 <= cols; c2++ )
+ {
+#if DEBUG_ATS
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Existing element column %i : %f\n",
+ ind[c2], val[c2]);
+#endif
+ if ((mlpi->c_b == ind[c2]) && (val[c2] != mlpi->q[c]))
+ {
+ /* Update the value */
+ val[c2] = mlpi->q[c];
+#if DEBUG_ATS
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "New element column %i : %f\n",
+ ind[c2], val[c2]);
+#endif
+ }
+ }
+
+ /* Get the index if matrix row of quality */
+ glp_set_mat_row (mlp->prob, mlpi->r_q[c], cols, ind, val);
+
+ GNUNET_free (ind);
+ GNUNET_free (val);
+ }
+ }
}
/* Recalculate */
mlp_create_problem (mlp, addresses);
mlp->presolver_required = GNUNET_YES;
}
- mlp_solve_problem (mlp);
+ if (mlp->auto_solve == GNUNET_YES)
+ GAS_mlp_solve_problem (mlp);
}
/**
mlp_delete_problem (mlp);
if ((GNUNET_CONTAINER_multihashmap_size (addresses) > 0) && (mlp->c_p > 0))
{
-#if DEBUG_ATS
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "mlp_create_problem %i\n",__LINE__);
-#endif
mlp_create_problem (mlp, addresses);
/* Recalculate */
mlp->presolver_required = GNUNET_YES;
- mlp_solve_problem (mlp);
+ if (mlp->auto_solve == GNUNET_YES)
+ GAS_mlp_solve_problem (mlp);
}
}