Merge branch 'gtsp-comb'
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
#!/bin/bash
|
||||
n=$1
|
||||
m=$2
|
||||
t=0
|
||||
all_t=""
|
||||
|
||||
TIMEFORMAT=%U
|
||||
mkdir -p out/
|
||||
printf "%12s\t%12s\n" "seed" "user-time (s)"
|
||||
|
||||
TIMEFORMAT=%U
|
||||
for i in $(seq 0 9); do
|
||||
t=$( { time bin/hw2.run --gtsp -s $i -n $n > out/gtsp-n${n}-s${i}.log; } 2>&1 )
|
||||
t=$( { time bin/hw2.run --gtsp -s "$i" -n "$n" -m "$m" 2>&1 > tmp/gtsp-m${m}-n${n}-s${i}.log; } 2>&1 )
|
||||
all_t="$all_t $t"
|
||||
printf "%12d\t%12.3f\n" $i $t
|
||||
done
|
||||
|
||||
echo
|
||||
echo $all_t | scripts/mean.r
|
||||
echo "$all_t" | scripts/mean.r
|
||||
|
||||
@@ -85,8 +85,6 @@ static int BNC_solve_node(struct BNC *bnc, int depth)
|
||||
int rval = 0;
|
||||
double *x = (double *) NULL;
|
||||
|
||||
log_debug("Optimizing...\n");
|
||||
|
||||
int is_infeasible;
|
||||
rval = LP_optimize(lp, &is_infeasible);
|
||||
abort_if(rval, "LP_optimize failed\n");
|
||||
@@ -155,7 +153,10 @@ static int BNC_solve_node(struct BNC *bnc, int depth)
|
||||
log_info(" obj val = %.2lf **\n", objval);
|
||||
|
||||
if (bnc->problem_solution_found)
|
||||
bnc->problem_solution_found(bnc->problem_data, bnc->best_x);
|
||||
{
|
||||
rval = bnc->problem_solution_found(bnc->problem_data, bnc->best_x);
|
||||
abort_if(rval, "problem_solution_found failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -210,11 +211,17 @@ static int BNC_branch_node(struct BNC *bnc, double *x, int depth)
|
||||
|
||||
static int BNC_is_integral(double *x, int num_cols)
|
||||
{
|
||||
#ifdef ALLOW_FRACTIONAL_SOLUTIONS
|
||||
UNUSED(num_cols);
|
||||
UNUSED(x);
|
||||
return 1;
|
||||
#else
|
||||
for (int i = 0; i < num_cols; i++)
|
||||
if (x[i] > LP_EPSILON && x[i] < 1.0 - LP_EPSILON)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int BNC_find_best_branching_var(double *x, int num_cols)
|
||||
|
||||
@@ -11,6 +11,8 @@ void graph_init(struct Graph *graph)
|
||||
graph->adj = 0;
|
||||
graph->node_count = 0;
|
||||
graph->edge_count = 0;
|
||||
graph->x_coordinates = 0;
|
||||
graph->y_coordinates = 0;
|
||||
}
|
||||
|
||||
void graph_free(struct Graph *graph)
|
||||
@@ -20,6 +22,8 @@ void graph_free(struct Graph *graph)
|
||||
if (graph->edges) free(graph->edges);
|
||||
if (graph->nodes) free(graph->nodes);
|
||||
if (graph->adj) free(graph->adj);
|
||||
if (graph->x_coordinates) free(graph->x_coordinates);
|
||||
if (graph->y_coordinates) free(graph->y_coordinates);
|
||||
}
|
||||
|
||||
int graph_build(
|
||||
@@ -86,7 +90,7 @@ int graph_build(
|
||||
n = &graph->nodes[b];
|
||||
n->adj[n->degree].neighbor_index = a;
|
||||
n->adj[n->degree].edge_index = i;
|
||||
n->adj[n->degree].neighbor = &graph->nodes[b];
|
||||
n->adj[n->degree].neighbor = &graph->nodes[a];
|
||||
n->adj[n->degree].edge = &graph->edges[i];
|
||||
n->degree++;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ struct Graph
|
||||
struct Edge *edges;
|
||||
struct Node *nodes;
|
||||
|
||||
double *x_coordinates;
|
||||
double *y_coordinates;
|
||||
|
||||
struct Adjacency *adj;
|
||||
};
|
||||
|
||||
|
||||
491
src/gtsp-comb.c
Normal file
491
src/gtsp-comb.c
Normal file
@@ -0,0 +1,491 @@
|
||||
#include "gtsp.h"
|
||||
#include "util.h"
|
||||
#include <assert.h>
|
||||
|
||||
int add_comb_cut(
|
||||
struct LP *lp,
|
||||
struct Graph *graph,
|
||||
int current_component,
|
||||
int *clusters,
|
||||
int *components,
|
||||
int *component_sizes,
|
||||
int *teeth,
|
||||
int tooth_count,
|
||||
double *x)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
char sense = 'G';
|
||||
|
||||
const int node_count = graph->node_count;
|
||||
const int edge_count = graph->edge_count;
|
||||
|
||||
struct Row *cut = 0;
|
||||
int *rmatind = 0;
|
||||
double *rmatval = 0;
|
||||
|
||||
rmatind = (int *) malloc((node_count + edge_count) * sizeof(int));
|
||||
rmatval = (double *) malloc((node_count + edge_count) * sizeof(double));
|
||||
abort_if(!rmatind, "could not allocate rmatind");
|
||||
abort_if(!rmatval, "could not allocate rmatval");
|
||||
|
||||
double rhs = -component_sizes[current_component] - tooth_count +
|
||||
(tooth_count + 1) / 2;
|
||||
|
||||
int nz = 0;
|
||||
|
||||
// Edges inside handle
|
||||
for (int i = 0; i < edge_count; i++)
|
||||
{
|
||||
struct Edge *e = &graph->edges[i];
|
||||
if (components[clusters[e->from->index]] != current_component) continue;
|
||||
if (components[clusters[e->to->index]] != current_component) continue;
|
||||
|
||||
rmatind[nz] = node_count + e->index;
|
||||
rmatval[nz] = -1.0;
|
||||
nz++;
|
||||
|
||||
log_debug(" handle (%d %d)\n", e->from->index, e->to->index);
|
||||
}
|
||||
|
||||
// Edges inside each tooth
|
||||
for (int i = 0; i < edge_count; i++)
|
||||
{
|
||||
struct Edge *e = &graph->edges[i];
|
||||
struct Node *from = e->from;
|
||||
struct Node *to = e->to;
|
||||
|
||||
if (teeth[clusters[from->index]] < 0) continue;
|
||||
if (teeth[clusters[to->index]] < 0) continue;
|
||||
if (teeth[clusters[from->index]] != teeth[clusters[to->index]])
|
||||
continue;
|
||||
|
||||
log_debug(" tooth (%d %d)\n", e->from->index, e->to->index);
|
||||
|
||||
rmatind[nz] = node_count + e->index;
|
||||
rmatval[nz] = -1.0;
|
||||
nz++;
|
||||
}
|
||||
|
||||
// // Lifting of the nodes
|
||||
// for (int i = 0; i < node_count; i++)
|
||||
// {
|
||||
// double val;
|
||||
// struct Node *n = &graph->nodes[i];
|
||||
// int c = clusters[n->index];
|
||||
//
|
||||
// if (components[c] == current_component)
|
||||
// val = (teeth[c] < 0 ? 1.0 : 0.0);
|
||||
// else
|
||||
// val = (teeth[c] < 0 ? 0.0 : 0.0);
|
||||
//
|
||||
// if (val == 0.0) continue;
|
||||
//
|
||||
// rmatind[nz] = n->index;
|
||||
// rmatval[nz] = val;
|
||||
// nz++;
|
||||
//
|
||||
// rhs = val;
|
||||
// }
|
||||
|
||||
#if LOG_LEVEL >= LOG_LEVEL_DEBUG
|
||||
log_debug("Generated cut:\n");
|
||||
for (int i = 0; i < nz; i++)
|
||||
{
|
||||
if (OPTIMAL_X[rmatind[i]] < LP_EPSILON) continue;
|
||||
|
||||
if (rmatind[i] >= node_count)
|
||||
{
|
||||
struct Edge *e = &graph->edges[rmatind[i] - node_count];
|
||||
log_debug(" %.2lf x%d (%d %d %.4lf)\n", rmatval[i], rmatind[i],
|
||||
e->from->index, e->to->index, OPTIMAL_X[rmatind[i]]);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debug(" %.2lf x%d (%.4lf)\n", rmatval[i], rmatind[i],
|
||||
OPTIMAL_X[rmatind[i]]);
|
||||
}
|
||||
}
|
||||
log_debug(" %c %.2lf\n", sense, rhs);
|
||||
#endif
|
||||
|
||||
if (OPTIMAL_X)
|
||||
{
|
||||
double sum = 0;
|
||||
for (int i = 0; i < nz; i++)
|
||||
sum += rmatval[i] * OPTIMAL_X[rmatind[i]];
|
||||
log_debug("%.2lf >= %.2lf\n", sum, rhs);
|
||||
abort_if(sum <= rhs - LP_EPSILON, "cannot add invalid cut");
|
||||
}
|
||||
|
||||
double lhs = 0.0;
|
||||
for (int i = 0; i < nz; i++)
|
||||
lhs += rmatval[i] * x[rmatind[i]];
|
||||
|
||||
log_debug("Violation: %.4lf >= %.4lf\n", lhs, rhs);
|
||||
|
||||
if (lhs + LP_EPSILON > rhs) goto CLEANUP;
|
||||
|
||||
cut = (struct Row *) malloc(sizeof(struct Row));
|
||||
abort_if(!cut, "could not allocate cut");
|
||||
|
||||
cut->nz = nz;
|
||||
cut->sense = sense;
|
||||
cut->rhs = rhs;
|
||||
cut->rmatval = rmatval;
|
||||
cut->rmatind = rmatind;
|
||||
|
||||
rval = LP_add_cut(lp, cut);
|
||||
abort_if(rval, "LP_add_cut failed");
|
||||
|
||||
CLEANUP:
|
||||
if (rmatind) free(rmatind);
|
||||
if (rmatval) free(rmatval);
|
||||
return rval;
|
||||
}
|
||||
|
||||
int find_components(
|
||||
struct Graph *graph, double *x, int *components, int *component_sizes)
|
||||
{
|
||||
int rval = 0;
|
||||
struct Node **stack = 0;
|
||||
|
||||
const int node_count = graph->node_count;
|
||||
|
||||
for (int i = 0; i < node_count; i++)
|
||||
{
|
||||
components[i] = -1;
|
||||
graph->nodes[i].mark = 0;
|
||||
}
|
||||
|
||||
int stack_top = 0;
|
||||
stack = (struct Node **) malloc(node_count * sizeof(struct Node *));
|
||||
abort_if(!stack, "could not allocate stack");
|
||||
|
||||
for (int i = 0; i < node_count; i++)
|
||||
{
|
||||
struct Node *root = &graph->nodes[i];
|
||||
if (root->mark) continue;
|
||||
|
||||
stack[stack_top++] = root;
|
||||
|
||||
while (stack_top > 0)
|
||||
{
|
||||
struct Node *n = stack[--stack_top];
|
||||
components[n->index] = i;
|
||||
|
||||
for (int j = 0; j < n->degree; j++)
|
||||
{
|
||||
struct Adjacency *adj = &n->adj[j];
|
||||
struct Node *neighbor = adj->neighbor;
|
||||
|
||||
if (neighbor->mark) continue;
|
||||
|
||||
double x_e = x[adj->edge->index];
|
||||
if (x_e < LP_EPSILON) continue;
|
||||
if (x_e > 1 - LP_EPSILON) continue;
|
||||
|
||||
stack[stack_top++] = neighbor;
|
||||
neighbor->mark = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < node_count; i++)
|
||||
component_sizes[i] = 0;
|
||||
|
||||
for (int i = 0; i < node_count; i++)
|
||||
component_sizes[components[i]]++;
|
||||
|
||||
log_debug("Components:\n");
|
||||
for (int i = 0; i < graph->node_count; i++)
|
||||
log_debug(" %d %d\n", i, components[i]);
|
||||
|
||||
log_debug("Component sizes:\n");
|
||||
for (int i = 0; i < graph->node_count; i++)
|
||||
log_debug(" %d %d\n", i, component_sizes[i]);
|
||||
|
||||
CLEANUP:
|
||||
if (stack) free(stack);
|
||||
return rval;
|
||||
}
|
||||
|
||||
int find_teeth(
|
||||
struct Graph *graph,
|
||||
double *x,
|
||||
int current_component,
|
||||
int *components,
|
||||
int *teeth,
|
||||
int *tooth_count)
|
||||
{
|
||||
const int node_count = graph->node_count;
|
||||
const int edge_count = graph->edge_count;
|
||||
|
||||
for (int i = 0; i < node_count; i++)
|
||||
{
|
||||
graph->nodes[i].mark = 0;
|
||||
teeth[i] = -1;
|
||||
}
|
||||
|
||||
*tooth_count = 0;
|
||||
|
||||
for (int i = 0; i < edge_count; i++)
|
||||
{
|
||||
struct Edge *e = &graph->edges[i];
|
||||
struct Node *from = e->from;
|
||||
struct Node *to = e->to;
|
||||
|
||||
if (x[e->index] < 1 - LP_EPSILON) continue;
|
||||
|
||||
if (to->mark || from->mark) continue;
|
||||
|
||||
int z = 0;
|
||||
if (components[from->index] == current_component) z++;
|
||||
if (components[to->index] == current_component) z++;
|
||||
if (z != 1) continue;
|
||||
|
||||
to->mark = 1;
|
||||
from->mark = 1;
|
||||
|
||||
teeth[to->index] = *tooth_count;
|
||||
teeth[from->index] = *tooth_count;
|
||||
|
||||
(*tooth_count)++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_shrunken_graph(
|
||||
double *shrunken_x,
|
||||
struct Graph *shrunken_graph,
|
||||
int const cluster_count);
|
||||
|
||||
static int shrink_clusters(
|
||||
const struct GTSP *data,
|
||||
double *x,
|
||||
struct Graph *shrunken_graph,
|
||||
double *shrunken_x)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
double *x_coords = 0;
|
||||
double *y_coords = 0;
|
||||
int *cluster_sizes = 0;
|
||||
|
||||
const int *clusters = data->clusters;
|
||||
const int cluster_count = data->cluster_count;
|
||||
const struct Graph *graph = data->graph;
|
||||
|
||||
int *edges = 0;
|
||||
int *edge_map = 0;
|
||||
int edge_count = (cluster_count * (cluster_count - 1)) / 2;
|
||||
|
||||
edge_map = (int *) malloc(cluster_count * cluster_count * sizeof(int));
|
||||
abort_if(!edge_map, "could not allocate edge_map");
|
||||
|
||||
edges = (int *) malloc(2 * edge_count * sizeof(int));
|
||||
abort_if(!edges, "could not allocate edges");
|
||||
|
||||
cluster_sizes = (int *) malloc(cluster_count * sizeof(int));
|
||||
x_coords = (double *) malloc(cluster_count * sizeof(double));
|
||||
y_coords = (double *) malloc(cluster_count * sizeof(double));
|
||||
|
||||
abort_if(!cluster_sizes, "could not allocate cluster_sizes");
|
||||
abort_if(!x_coords, "could not allocate x_coords");
|
||||
abort_if(!y_coords, "could not allocate y_coords");
|
||||
|
||||
for (int i = 0; i < cluster_count; i++)
|
||||
{
|
||||
x_coords[i] = 0.0;
|
||||
y_coords[i] = 0.0;
|
||||
cluster_sizes[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < graph->node_count; i++)
|
||||
{
|
||||
struct Node *n = &graph->nodes[i];
|
||||
int c = clusters[n->index];
|
||||
|
||||
cluster_sizes[c]++;
|
||||
x_coords[c] += graph->x_coordinates[n->index];
|
||||
y_coords[c] += graph->y_coordinates[n->index];
|
||||
}
|
||||
|
||||
for (int i = 0; i < cluster_count; i++)
|
||||
{
|
||||
x_coords[i] = x_coords[i] / cluster_sizes[i];
|
||||
y_coords[i] = y_coords[i] / cluster_sizes[i];
|
||||
}
|
||||
|
||||
shrunken_graph->x_coordinates = x_coords;
|
||||
shrunken_graph->y_coordinates = y_coords;
|
||||
|
||||
int curr_edge = 0;
|
||||
for (int i = 0; i < cluster_count; i++)
|
||||
{
|
||||
for (int j = i + 1; j < cluster_count; j++)
|
||||
{
|
||||
edges[curr_edge * 2] = i;
|
||||
edges[curr_edge * 2 + 1] = j;
|
||||
edge_map[i * cluster_count + j] = curr_edge;
|
||||
edge_map[j * cluster_count + i] = curr_edge;
|
||||
curr_edge++;
|
||||
}
|
||||
}
|
||||
|
||||
assert(curr_edge == edge_count);
|
||||
|
||||
rval = graph_build(cluster_count, edge_count, edges, 0, shrunken_graph);
|
||||
abort_if(rval, "graph_build failed");
|
||||
|
||||
for (int i = 0; i < edge_count; i++)
|
||||
shrunken_x[i] = 0.0;
|
||||
|
||||
for (int i = 0; i < graph->edge_count; i++)
|
||||
{
|
||||
struct Edge *e = &graph->edges[i];
|
||||
|
||||
int from = clusters[e->from->index];
|
||||
int to = clusters[e->to->index];
|
||||
int shunk_e_index = edge_map[from * cluster_count + to];
|
||||
|
||||
shrunken_x[shunk_e_index] += x[graph->node_count + e->index];
|
||||
}
|
||||
|
||||
CLEANUP:
|
||||
if (edges) free(edges);
|
||||
if (edge_map) free(edge_map);
|
||||
if (cluster_sizes) free(cluster_sizes);
|
||||
return rval;
|
||||
}
|
||||
|
||||
int find_comb_cuts(struct LP *lp, struct GTSP *data)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
double *x = 0;
|
||||
double *shrunken_x = 0;
|
||||
|
||||
int *teeth = 0;
|
||||
int *components = 0;
|
||||
int *component_sizes = 0;
|
||||
|
||||
int num_cols = LP_get_num_cols(lp);
|
||||
x = (double *) malloc(num_cols * sizeof(double));
|
||||
abort_if(!x, "could not allocate x");
|
||||
|
||||
rval = LP_get_x(lp, x);
|
||||
abort_if(rval, "LP_get_x failed");
|
||||
|
||||
struct Graph shrunken_graph;
|
||||
graph_init(&shrunken_graph);
|
||||
|
||||
const int cluster_count = data->cluster_count;
|
||||
const int shrunken_edge_count = (cluster_count * (cluster_count - 1)) / 2;
|
||||
|
||||
shrunken_x = (double *) malloc(shrunken_edge_count * sizeof(double));
|
||||
abort_if(!shrunken_x, "could not allocate shrunken_x");
|
||||
|
||||
rval = shrink_clusters(data, x, &shrunken_graph, shrunken_x);
|
||||
abort_if(rval, "shrink_clusters failed");
|
||||
|
||||
#if LOG_LEVEL >= LOG_LEVEL_DEBUG
|
||||
rval = write_shrunken_graph(shrunken_x, &shrunken_graph, cluster_count);
|
||||
abort_if(rval, "write_shrunken_graph failed");
|
||||
#endif
|
||||
|
||||
teeth = (int *) malloc(cluster_count * sizeof(int));
|
||||
components = (int *) malloc(cluster_count * sizeof(int));
|
||||
component_sizes = (int *) malloc(cluster_count * sizeof(int));
|
||||
|
||||
abort_if(!teeth, "could not allocate teeth");
|
||||
abort_if(!components, "could not allocate components");
|
||||
abort_if(!component_sizes, "could not allocate component_sizes");
|
||||
|
||||
rval = find_components(&shrunken_graph, shrunken_x, components,
|
||||
component_sizes);
|
||||
abort_if(rval, "find_components failed");
|
||||
|
||||
#if LOG_LEVEL >= LOG_LEVEL_DEBUG
|
||||
int original_cut_pool_size = lp->cut_pool_size;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < cluster_count; i++)
|
||||
{
|
||||
if (component_sizes[i] < 3) continue;
|
||||
|
||||
int tooth_count;
|
||||
rval = find_teeth(&shrunken_graph, shrunken_x, i, components, teeth,
|
||||
&tooth_count);
|
||||
abort_if(rval, "find_teeth failed");
|
||||
|
||||
log_debug("Component %d has %d teeth:\n", i, tooth_count);
|
||||
for (int j = 0; j < cluster_count; j++)
|
||||
{
|
||||
if (teeth[j] < 0) continue;
|
||||
log_debug(" %d %d\n", j, teeth[j]);
|
||||
}
|
||||
|
||||
if (tooth_count % 2 == 0) continue;
|
||||
|
||||
rval = add_comb_cut(lp, data->graph, i, data->clusters, components,
|
||||
component_sizes, teeth, tooth_count, x);
|
||||
abort_if(rval, "add_comb_cut failed");
|
||||
}
|
||||
|
||||
log_debug(" %d combs\n", lp->cut_pool_size - original_cut_pool_size);
|
||||
|
||||
CLEANUP:
|
||||
graph_free(&shrunken_graph);
|
||||
if (teeth) free(teeth);
|
||||
if (components) free(components);
|
||||
if (component_sizes) free(component_sizes);
|
||||
if (shrunken_x) free(shrunken_x);
|
||||
return rval;
|
||||
}
|
||||
|
||||
int write_shrunken_graph(
|
||||
double *shrunken_x,
|
||||
struct Graph *shrunken_graph,
|
||||
int const cluster_count)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
FILE *file = 0;
|
||||
|
||||
file = fopen("gtsp-shrunken.in", "w");
|
||||
abort_if(!file, "could not open file");
|
||||
|
||||
fprintf(file, "%d %d\n", (*shrunken_graph).node_count, cluster_count);
|
||||
for (int i = 0; i < (*shrunken_graph).node_count; i++)
|
||||
{
|
||||
fprintf(file, "%.2lf %.2lf %d\n", (*shrunken_graph).x_coordinates[i],
|
||||
(*shrunken_graph).y_coordinates[i], i);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
file = fopen("gtsp-shrunken.out", "w");
|
||||
abort_if(!file, "could not open file");
|
||||
|
||||
int positive_edge_count = 0;
|
||||
for (int i = 0; i < (*shrunken_graph).edge_count; i++)
|
||||
if (shrunken_x[i] > LP_EPSILON)
|
||||
positive_edge_count++;
|
||||
|
||||
fprintf(file, "%d %d\n", (*shrunken_graph).node_count,
|
||||
(*shrunken_graph).edge_count);
|
||||
|
||||
fprintf(file, "%d\n", positive_edge_count);
|
||||
|
||||
for (int i = 0; i < (*shrunken_graph).edge_count; i++)
|
||||
if (shrunken_x[i] > LP_EPSILON)
|
||||
fprintf(file, "%d %d %.4lf\n",
|
||||
(*shrunken_graph).edges[i].from->index,
|
||||
(*shrunken_graph).edges[i].to->index, shrunken_x[i]);
|
||||
fclose(file);
|
||||
|
||||
CLEANUP:
|
||||
return rval;
|
||||
}
|
||||
6
src/gtsp-comb.h
Normal file
6
src/gtsp-comb.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef PROJECT_GTSP_COMB_H
|
||||
#define PROJECT_GTSP_COMB_H
|
||||
|
||||
int find_comb_cuts(struct LP *lp, struct GTSP *data);
|
||||
|
||||
#endif //PROJECT_GTSP_COMB_H
|
||||
@@ -213,12 +213,9 @@ int find_exact_subtour_cuts(
|
||||
abort_if(rval, "find_exact_subtour_cuts_cluster_to_cluster failed");
|
||||
|
||||
added_cuts_count = lp->cut_pool_size - original_cut_pool_size;
|
||||
log_debug(" %d cluster-to-cluster\n", added_cuts_count);
|
||||
if (added_cuts_count > 0)
|
||||
{
|
||||
log_debug("Added %d cluster-to-cluster subtour cuts\n",
|
||||
added_cuts_count);
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
// Constraints (2.2)
|
||||
original_cut_pool_size = lp->cut_pool_size;
|
||||
@@ -227,11 +224,9 @@ int find_exact_subtour_cuts(
|
||||
abort_if(rval, "find_exact_subtour_cuts_node_to_cluster failed");
|
||||
|
||||
added_cuts_count = lp->cut_pool_size - original_cut_pool_size;
|
||||
log_debug(" %d node-to-cluster\n", added_cuts_count);
|
||||
if (added_cuts_count > 0)
|
||||
{
|
||||
log_debug("Added %d node-to-cluster subtour cuts\n", added_cuts_count);
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
// Constraints (2.3)
|
||||
original_cut_pool_size = lp->cut_pool_size;
|
||||
@@ -240,11 +235,9 @@ int find_exact_subtour_cuts(
|
||||
abort_if(rval, "find_exact_subtour_cuts_node_to_node failed");
|
||||
|
||||
added_cuts_count = lp->cut_pool_size - original_cut_pool_size;
|
||||
log_debug(" %d node-to-node\n", added_cuts_count);
|
||||
if (added_cuts_count > 0)
|
||||
{
|
||||
log_debug("Added %d node-to-node subtour cuts\n", added_cuts_count);
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
CLEANUP:
|
||||
graph_free(&digraph);
|
||||
|
||||
288
src/gtsp.c
288
src/gtsp.c
@@ -2,36 +2,23 @@
|
||||
#include <stdlib.h>
|
||||
#include <float.h>
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include "gtsp.h"
|
||||
#include "geometry.h"
|
||||
#include "util.h"
|
||||
#include "flow.h"
|
||||
#include "branch_and_cut.h"
|
||||
#include "math.h"
|
||||
#include "gtsp-subtour.h"
|
||||
#include "gtsp-comb.h"
|
||||
|
||||
double *OPTIMAL_X = 0;
|
||||
|
||||
static int get_edge_num(int node_count, int from, int to)
|
||||
{
|
||||
int idx = node_count;
|
||||
|
||||
for (int k = 0; k < from; k++)
|
||||
idx += node_count - k - 1;
|
||||
|
||||
idx += to - from - 1;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int GTSP_init_data(struct GTSP *data)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
data->clusters = 0;
|
||||
data->cluster_count = 0;
|
||||
data->x_coordinates = 0;
|
||||
data->y_coordinates = 0;
|
||||
|
||||
data->graph = (struct Graph *) malloc(sizeof(struct Graph));
|
||||
abort_if(!data->graph, "could not allocate data->graph");
|
||||
@@ -52,8 +39,6 @@ void GTSP_free(struct GTSP *data)
|
||||
free(data->graph);
|
||||
|
||||
if (data->clusters) free(data->clusters);
|
||||
if (data->x_coordinates) free(data->x_coordinates);
|
||||
if (data->y_coordinates) free(data->y_coordinates);
|
||||
}
|
||||
|
||||
int GTSP_create_random_problem(
|
||||
@@ -156,8 +141,8 @@ abort_if(!data->graph, "could not allocate data->graph");
|
||||
data->graph = graph;
|
||||
data->clusters = clusters;
|
||||
data->cluster_count = cluster_count;
|
||||
data->x_coordinates = x_coords;
|
||||
data->y_coordinates = y_coords;
|
||||
graph->x_coordinates = x_coords;
|
||||
graph->y_coordinates = y_coords;
|
||||
data->dist_matrix = dist_matrix;
|
||||
data->vertex_set = cluster_member;
|
||||
|
||||
@@ -226,38 +211,12 @@ int GTSP_init_lp(struct LP *lp, struct GTSP *data)
|
||||
int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
int round = 0;
|
||||
|
||||
int violation_total = 3;
|
||||
int violation_current = 0;
|
||||
double violations[] = {1.0, 0.1, LP_EPSILON};
|
||||
int current_round = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
round++;
|
||||
|
||||
log_debug("Finding subtour cuts, round %d, violation %.4lf...\n", round,
|
||||
violations[violation_current]);
|
||||
|
||||
int original_cut_pool_size = lp->cut_pool_size;
|
||||
rval = find_exact_subtour_cuts(lp, data, violations[violation_current]);
|
||||
abort_if(rval, "find_exact_subtour_cuts failed");
|
||||
|
||||
if (lp->cut_pool_size - original_cut_pool_size == 0)
|
||||
if (current_round > 0)
|
||||
{
|
||||
if (++violation_current < violation_total)
|
||||
{
|
||||
log_debug("No cuts found. Decreasing minimum cut violation.\n");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debug("No additional cuts found.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int is_infeasible;
|
||||
rval = LP_optimize(lp, &is_infeasible);
|
||||
abort_if(rval, "LP_optimize failed");
|
||||
@@ -265,6 +224,36 @@ int GTSP_add_cutting_planes(struct LP *lp, struct GTSP *data)
|
||||
if (is_infeasible) break;
|
||||
}
|
||||
|
||||
current_round++;
|
||||
|
||||
int original_cut_pool_size;
|
||||
int added_cuts_count;
|
||||
|
||||
original_cut_pool_size = lp->cut_pool_size;
|
||||
log_debug("Finding subtour cuts, round %d...\n", current_round);
|
||||
|
||||
rval = find_exact_subtour_cuts(lp, data, LP_EPSILON);
|
||||
abort_if(rval, "find_exact_subtour_cuts failed");
|
||||
|
||||
added_cuts_count = lp->cut_pool_size - original_cut_pool_size;
|
||||
if (added_cuts_count > 0)
|
||||
continue;
|
||||
|
||||
#ifdef ENABLE_COMB_INEQUALITIES
|
||||
original_cut_pool_size = lp->cut_pool_size;
|
||||
log_debug("Finding comb cuts, round %d...\n", current_round);
|
||||
|
||||
rval = find_comb_cuts(lp, data);
|
||||
abort_if(rval, "find_comb_cuts failed");
|
||||
|
||||
added_cuts_count = lp->cut_pool_size - original_cut_pool_size;
|
||||
if (added_cuts_count > 0)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
CLEANUP:
|
||||
return rval;
|
||||
}
|
||||
@@ -278,12 +267,14 @@ int GTSP_write_problem(struct GTSP *data, char *filename)
|
||||
file = fopen(filename, "w");
|
||||
abort_if(!file, "could not open file");
|
||||
|
||||
fprintf(file, "%d %d\n", data->graph->node_count, data->cluster_count);
|
||||
const struct Graph *graph = data->graph;
|
||||
|
||||
for (int i = 0; i < data->graph->node_count; i++)
|
||||
fprintf(file, "%d %d\n", graph->node_count, data->cluster_count);
|
||||
|
||||
for (int i = 0; i < graph->node_count; i++)
|
||||
{
|
||||
fprintf(file, "%.2lf %.2lf %d\n", data->x_coordinates[i],
|
||||
data->y_coordinates[i], data->clusters[i]);
|
||||
fprintf(file, "%.2lf %.2lf %d\n", graph->x_coordinates[i],
|
||||
graph->y_coordinates[i], data->clusters[i]);
|
||||
}
|
||||
|
||||
CLEANUP:
|
||||
@@ -322,12 +313,13 @@ int GTSP_write_solution(struct GTSP *data, char *filename, double *x)
|
||||
return rval;
|
||||
}
|
||||
|
||||
int GTSP_read_solution(char *filename, double **p_x)
|
||||
int GTSP_read_solution(struct GTSP *gtsp, char *filename, double **p_x)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
int node_count;
|
||||
int edge_count;
|
||||
int *edge_map = 0;
|
||||
|
||||
double *x;
|
||||
|
||||
@@ -351,31 +343,138 @@ int GTSP_read_solution(char *filename, double **p_x)
|
||||
rval = fscanf(file, "%d", &edge_count);
|
||||
abort_if(rval != 1, "invalid input format (positive edge count)");
|
||||
|
||||
edge_map = (int *) malloc(node_count * node_count * sizeof(int));
|
||||
abort_if(!edge_map, "could not allocate edge_map");
|
||||
|
||||
int k = node_count;
|
||||
for (int i = 0; i < node_count; i++)
|
||||
{
|
||||
for (int j = i + 1; j < node_count; j++)
|
||||
{
|
||||
if (gtsp->clusters[i] == gtsp->clusters[j]) continue;
|
||||
edge_map[i * node_count + j] = k;
|
||||
edge_map[j * node_count + i] = k;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < edge_count; i++)
|
||||
{
|
||||
int from, to, edge;
|
||||
rval = fscanf(file, "%d %d", &from, &to);
|
||||
abort_if(rval != 2, "invalid input format (edge endpoints)");
|
||||
double val;
|
||||
rval = fscanf(file, "%d %d %lf", &from, &to, &val);
|
||||
abort_if(rval != 3, "invalid input format (edge endpoints)");
|
||||
|
||||
if (from > to) swap(from, to);
|
||||
|
||||
edge = get_edge_num(node_count, from, to);
|
||||
edge = edge_map[from * node_count + to];
|
||||
abort_if(edge > num_cols, "invalid edge");
|
||||
|
||||
x[from] += 0.5;
|
||||
x[to] += 0.5;
|
||||
x[edge] = 1;
|
||||
x[from] += val / 2;
|
||||
x[to] += val / 2;
|
||||
x[edge] = val;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_cols; i++)
|
||||
{
|
||||
if (x[i] <= LP_EPSILON) continue;
|
||||
log_debug(" x%-3d = %.2f\n", i, x[i]);
|
||||
log_debug(" x%-5d = %.6f\n", i, x[i]);
|
||||
}
|
||||
|
||||
*p_x = x;
|
||||
rval = 0;
|
||||
|
||||
CLEANUP:
|
||||
if (edge_map) free(edge_map);
|
||||
return rval;
|
||||
}
|
||||
|
||||
int GTSP_check_solution(struct GTSP *data, double *x)
|
||||
{
|
||||
int rval = 0;
|
||||
int *cluster_mark = 0;
|
||||
|
||||
struct Node **stack = 0;
|
||||
int stack_top = 0;
|
||||
|
||||
struct Graph *graph = data->graph;
|
||||
const int node_count = graph->node_count;
|
||||
const int edge_count = graph->edge_count;
|
||||
|
||||
cluster_mark = (int *) malloc(data->cluster_count * sizeof(int));
|
||||
abort_if(!cluster_mark, "could not allocate cluster_mark");
|
||||
|
||||
stack = (struct Node **) malloc(graph->node_count * sizeof(struct Node *));
|
||||
abort_if(!stack, "could not allocate stack");
|
||||
|
||||
for (int i = 0; i < node_count + edge_count; i++)
|
||||
{
|
||||
abort_iff(x[i] < 1.0 - LP_EPSILON && x[i] > LP_EPSILON,
|
||||
"solution is not integral: x%d = %.4lf", i, x[i]);
|
||||
|
||||
abort_iff(x[i] > 1.0 + LP_EPSILON || x[i] < 0.0 - LP_EPSILON,
|
||||
"value out of bounds: x%d = %.4lf", i, x[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < node_count; i++)
|
||||
graph->nodes[i].mark = 0;
|
||||
|
||||
for (int i = 0; i < data->cluster_count; i++)
|
||||
cluster_mark[i] = 0;
|
||||
|
||||
int initial;
|
||||
for (initial = 0; initial < node_count; initial++)
|
||||
if (x[initial] > 1.0 - LP_EPSILON) break;
|
||||
|
||||
abort_if(initial == node_count, "no initial node");
|
||||
|
||||
stack[stack_top++] = &graph->nodes[initial];
|
||||
graph->nodes[initial].mark = 1;
|
||||
|
||||
while (stack_top > 0)
|
||||
{
|
||||
struct Node *n = stack[--stack_top];
|
||||
cluster_mark[data->clusters[n->index]]++;
|
||||
|
||||
for (int i = 0; i < n->degree; i++)
|
||||
{
|
||||
struct Adjacency *adj = &n->adj[i];
|
||||
struct Node *neighbor = adj->neighbor;
|
||||
|
||||
if (neighbor->mark) continue;
|
||||
if (x[node_count + adj->edge->index] < LP_EPSILON) continue;
|
||||
|
||||
stack[stack_top++] = neighbor;
|
||||
neighbor->mark = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < data->cluster_count; i++)
|
||||
abort_if(cluster_mark[i] != 1, "cluster not visited exactly one time");
|
||||
|
||||
log_info(" solution is valid\n");
|
||||
|
||||
CLEANUP:
|
||||
if (stack) free(stack);
|
||||
if (cluster_mark) free(cluster_mark);
|
||||
return rval;
|
||||
}
|
||||
|
||||
int GTSP_solution_found(struct GTSP *data, double *x)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
char filename[100];
|
||||
|
||||
sprintf(filename, "tmp/gtsp-m%d-n%d-s%d.out", data->cluster_count,
|
||||
data->graph->node_count, SEED);
|
||||
|
||||
log_info("Writting solution to file %s\n", filename);
|
||||
rval = GTSP_write_solution(data, filename, x);
|
||||
abort_if(rval, "GTSP_write_solution failed");
|
||||
|
||||
log_info("Checking solution...\n");
|
||||
rval = GTSP_check_solution(data, x);
|
||||
abort_if(rval, "GTSP_check_solution failed");
|
||||
|
||||
CLEANUP:
|
||||
return rval;
|
||||
}
|
||||
@@ -387,11 +486,12 @@ static const struct option options_tab[] = {{"help", no_argument, 0, 'h'},
|
||||
{"optimal", required_argument, 0, 'x'},
|
||||
{"seed", required_argument, 0, 's'},
|
||||
{(char *) 0, (int) 0, (int *) 0, (int) 0}};
|
||||
|
||||
static int input_node_count = -1;
|
||||
static int input_cluster_count = -1;
|
||||
static int grid_size = 100;
|
||||
|
||||
static char input_x_filename[1000] = {0};
|
||||
|
||||
static void GTSP_print_usage()
|
||||
{
|
||||
printf("Parameters:\n");
|
||||
@@ -433,8 +533,7 @@ static int GTSP_parse_args(int argc, char **argv)
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
rval = GTSP_read_solution(optarg, &OPTIMAL_X);
|
||||
abort_if(rval, "GTSP_read_solution failed");
|
||||
strcpy(input_x_filename, optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
@@ -443,12 +542,14 @@ static int GTSP_parse_args(int argc, char **argv)
|
||||
|
||||
case ':':
|
||||
fprintf(stderr, "option '-%c' requires an argument\n", optopt);
|
||||
return 1;
|
||||
rval = 1;
|
||||
goto CLEANUP;
|
||||
|
||||
case '?':
|
||||
default:
|
||||
fprintf(stderr, "option '-%c' is invalid\n", optopt);
|
||||
return 1;
|
||||
rval = 1;
|
||||
goto CLEANUP;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -481,18 +582,6 @@ static int GTSP_parse_args(int argc, char **argv)
|
||||
return rval;
|
||||
}
|
||||
|
||||
int GTSP_solution_found(struct GTSP *data, double *x)
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
log_info("Writting integral solution to file gtsp.out\n");
|
||||
rval = GTSP_write_solution(data, "gtsp.out", x);
|
||||
abort_if(rval, "GTSP_write_solution failed");
|
||||
|
||||
CLEANUP:
|
||||
return rval;
|
||||
}
|
||||
|
||||
double FLOW_CPU_TIME = 0;
|
||||
double LP_SOLVE_TIME = 0;
|
||||
double LP_CUT_POOL_TIME = 0;
|
||||
@@ -530,7 +619,14 @@ int GTSP_main(int argc, char **argv)
|
||||
int init_val ;
|
||||
|
||||
init_val = inital_tour_value(&data);
|
||||
log_info("Writing random instance to file gtsp.in\n");
|
||||
|
||||
char filename[100];
|
||||
sprintf(filename, "input/gtsp-m%d-n%d-s%d.in", input_cluster_count,
|
||||
input_node_count, SEED);
|
||||
log_info("Writing random instance to file %s\n", filename);
|
||||
rval = GTSP_write_problem(&data, filename);
|
||||
abort_if(rval, "GTSP_write_problem failed");
|
||||
|
||||
rval = GTSP_write_problem(&data, "gtsp.in");
|
||||
abort_if(rval, "GTSP_write_problem failed");
|
||||
|
||||
@@ -542,11 +638,28 @@ int GTSP_main(int argc, char **argv)
|
||||
bnc.problem_solution_found = (int (*)(
|
||||
void *, double *)) GTSP_solution_found;
|
||||
|
||||
if (OPTIMAL_X)
|
||||
double opt_val = 0.0;
|
||||
|
||||
if (strlen(input_x_filename) == 0)
|
||||
{
|
||||
sprintf(input_x_filename, "optimal/gtsp-m%d-n%d-s%d.out",
|
||||
input_cluster_count, input_node_count, SEED);
|
||||
|
||||
FILE *file = fopen(input_x_filename, "r");
|
||||
|
||||
if (!file)
|
||||
input_x_filename[0] = 0;
|
||||
else
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
if (strlen(input_x_filename) > 0)
|
||||
{
|
||||
rval = GTSP_read_solution(&data, input_x_filename, &OPTIMAL_X);
|
||||
abort_if(rval, "GTSP_read_solution failed");
|
||||
|
||||
log_info("Optimal solution is available. Cuts will be checked.\n");
|
||||
|
||||
double opt_val = 0.0;
|
||||
for (int i = 0; i < data.graph->edge_count; i++)
|
||||
{
|
||||
struct Edge *e = &data.graph->edges[i];
|
||||
@@ -560,9 +673,9 @@ int GTSP_main(int argc, char **argv)
|
||||
rval = BNC_init_lp(&bnc);
|
||||
abort_if(rval, "BNC_init_lp failed");
|
||||
|
||||
log_info("Writing LP to file gtsp.lp...\n");
|
||||
rval = LP_write(bnc.lp, "gtsp.lp");
|
||||
abort_if(rval, "LP_write failed");
|
||||
// log_info("Writing LP to file gtsp.lp...\n");
|
||||
// rval = LP_write(bnc.lp, "gtsp.lp");
|
||||
// abort_if(rval, "LP_write failed");
|
||||
|
||||
log_info("Starting branch-and-cut solver...\n");
|
||||
rval = BNC_solve(&bnc);
|
||||
@@ -573,6 +686,13 @@ int GTSP_main(int argc, char **argv)
|
||||
log_info("Optimal integral solution:\n");
|
||||
log_info(" obj value = %.2lf **\n", bnc.best_obj_val);
|
||||
|
||||
if (OPTIMAL_X)
|
||||
{
|
||||
abort_iff(bnc.best_obj_val - LP_EPSILON > opt_val,
|
||||
"Solution is not optimal: %.4lf > %.4lf", bnc.best_obj_val,
|
||||
opt_val);
|
||||
}
|
||||
|
||||
log_info("Branch-and-bound nodes: %d\n", BNC_NODE_COUNT);
|
||||
log_info("Max-flow calls: %d\n", FLOW_MAX_FLOW_COUNT);
|
||||
log_info("Max-flow computation time: %.2lf\n", FLOW_CPU_TIME);
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
//
|
||||
// Created by isoron on 18/03/15.
|
||||
//
|
||||
|
||||
#ifndef _PROJECT_GTSP_H_
|
||||
#define _PROJECT_GTSP_H_
|
||||
|
||||
@@ -21,9 +17,6 @@ struct GTSP
|
||||
|
||||
int *clusters;
|
||||
int cluster_count;
|
||||
|
||||
double *x_coordinates;
|
||||
double *y_coordinates;
|
||||
int** dist_matrix;
|
||||
struct CLUSTER *vertex_set;
|
||||
};
|
||||
|
||||
10
src/lp.c
10
src/lp.c
@@ -165,10 +165,11 @@ int LP_optimize(struct LP *lp, int *infeasible)
|
||||
|
||||
*infeasible = 0;
|
||||
|
||||
#if LOG_LEVEL >= LOG_LEVEL_DEBUG
|
||||
int numrows = CPXgetnumrows(lp->cplex_env, lp->cplex_lp);
|
||||
int numcols = CPXgetnumcols(lp->cplex_env, lp->cplex_lp);
|
||||
|
||||
log_debug("Optimizing LP (%d rows %d cols)...\n", numrows, numcols);
|
||||
#endif
|
||||
|
||||
double time_before = get_current_time();
|
||||
rval = CPXdualopt(lp->cplex_env, lp->cplex_lp);
|
||||
@@ -250,7 +251,7 @@ int LP_remove_old_cuts(struct LP *lp)
|
||||
{
|
||||
struct Row *cut = lp->cut_pool[j];
|
||||
|
||||
if (cut->cplex_row_index == i - count) cut->cplex_row_index = 0;
|
||||
if (cut->cplex_row_index == i - count) cut->cplex_row_index = -1;
|
||||
else if (cut->cplex_row_index > i - count) cut->cplex_row_index--;
|
||||
}
|
||||
|
||||
@@ -298,7 +299,7 @@ int LP_remove_old_cuts(struct LP *lp)
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
log_info("Found and removed %d old cuts\n", count);
|
||||
log_debug("Found and removed %d old cuts\n", count);
|
||||
rval = CPXdualopt(lp->cplex_env, lp->cplex_lp);
|
||||
abort_if(rval, "CPXoptimize failed");
|
||||
}
|
||||
@@ -388,6 +389,9 @@ int LP_add_cut(struct LP *lp, struct Row *cut)
|
||||
if (lp->cut_pool[i]->hash != cut->hash) continue;
|
||||
if (!compare_cuts(lp->cut_pool[i], cut))
|
||||
{
|
||||
log_verbose("Discarding duplicate cut (same as cplex row %d)\n",
|
||||
lp->cut_pool[i]->cplex_row_index);
|
||||
|
||||
free(cut->rmatval);
|
||||
free(cut->rmatind);
|
||||
free(cut);
|
||||
|
||||
10
src/params.h
Normal file
10
src/params.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef PROJECT_PARAMS_H
|
||||
#define PROJECT_PARAMS_H
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||
|
||||
#define ENABLE_COMB_INEQUALITIES
|
||||
|
||||
//#define ALLOW_FRACTIONAL_SOLUTIONS
|
||||
|
||||
#endif //PROJECT_PARAMS_H
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _PROJECT_UTIL_H_
|
||||
|
||||
#include <string.h>
|
||||
#include "params.h"
|
||||
|
||||
#define LOG_LEVEL_ERROR 10
|
||||
#define LOG_LEVEL_WARNING 20
|
||||
@@ -9,8 +10,6 @@
|
||||
#define LOG_LEVEL_DEBUG 40
|
||||
#define LOG_LEVEL_VERBOSE 50
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_DEBUG
|
||||
|
||||
#if LOG_LEVEL < LOG_LEVEL_VERBOSE
|
||||
#define log_verbose(...)
|
||||
#else
|
||||
@@ -42,7 +41,11 @@
|
||||
#endif
|
||||
|
||||
#define abort_if(cond, msg) if(cond) { \
|
||||
fprintf(stderr, "%20s:%d " msg "\n", __FILE__, __LINE__); \
|
||||
fprintf(stderr, "%28s:%d " msg "\n", __FILE__, __LINE__); \
|
||||
rval = 1; goto CLEANUP; }
|
||||
|
||||
#define abort_iff(cond, msg, ...) if(cond) { \
|
||||
fprintf(stderr, "%28s:%d " msg "\n", __FILE__, __LINE__, __VA_ARGS__); \
|
||||
rval = 1; goto CLEANUP; }
|
||||
|
||||
#define swap(x, y) do \
|
||||
|
||||
Reference in New Issue
Block a user