From 7098f651105c1b9f6d4dfb5679c89735a082de98 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Fri, 28 Apr 2017 15:44:24 -0400 Subject: [PATCH] New project structure --- .gitmodules | 3 + CMakeLists.txt | 20 + cmake/FindCPLEX.cmake | 17 + googletest | 1 + infinity/benchmark/CMakeLists.txt | 2 + infinity/benchmark/src/main.c | 467 ++++++ infinity/library/CMakeLists.txt | 20 + infinity/library/include/infinity/greedy-2d.h | 37 + .../library/include/infinity/greedy-bsearch.h | 33 + infinity/library/include/infinity/greedy-nd.h | 105 ++ infinity/library/include/infinity/greedy.h | 31 + infinity/library/src/greedy-2d.c | 738 +++++++++ infinity/library/src/greedy-bsearch.c | 246 +++ infinity/library/src/greedy-nd.c | 1331 +++++++++++++++++ infinity/library/src/greedy.c | 478 ++++++ infinity/library/tests/greedy-2d-test.cpp | 616 ++++++++ infinity/library/tests/greedy-nd-test.cpp | 457 ++++++ lifting/benchmark/CMakeLists.txt | 2 + lifting/benchmark/src/main.c | 639 ++++++++ lifting/library/CMakeLists.txt | 17 + lifting/library/include/lifting/lifting-mip.h | 32 + lifting/library/include/lifting/lifting.h | 53 + lifting/library/src/lifting-mip.c | 162 ++ lifting/library/src/lifting.c | 286 ++++ lifting/library/tests/fixtures/quads.txt | 1 + lifting/library/tests/fixtures/triangles.txt | 2 + lifting/library/tests/lifting-test.cpp | 715 +++++++++ multirow/CMakeLists.txt | 32 + multirow/include/multirow/cg.h | 95 ++ multirow/include/multirow/double.h | 50 + multirow/include/multirow/geometry.h | 30 + multirow/include/multirow/lfree2d.h | 58 + multirow/include/multirow/lp.h | 157 ++ multirow/include/multirow/mir.h | 26 + multirow/include/multirow/params.h | 90 ++ multirow/include/multirow/rational.h | 34 + multirow/include/multirow/stats.h | 33 + multirow/include/multirow/util.h | 98 ++ multirow/src/cg.c | 1021 +++++++++++++ multirow/src/double.c | 134 ++ multirow/src/geometry.c | 190 +++ multirow/src/lfree2d.c | 514 +++++++ multirow/src/lp.c | 724 +++++++++ multirow/src/mir.c | 63 + multirow/src/rational.c | 37 + multirow/src/stats.c | 120 ++ multirow/src/util.c | 134 ++ multirow/tests/cg-test.cpp | 267 ++++ multirow/tests/double-test.cpp | 164 ++ multirow/tests/geometry-test.cpp | 664 ++++++++ 50 files changed, 11246 insertions(+) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 cmake/FindCPLEX.cmake create mode 160000 googletest create mode 100644 infinity/benchmark/CMakeLists.txt create mode 100644 infinity/benchmark/src/main.c create mode 100644 infinity/library/CMakeLists.txt create mode 100644 infinity/library/include/infinity/greedy-2d.h create mode 100644 infinity/library/include/infinity/greedy-bsearch.h create mode 100644 infinity/library/include/infinity/greedy-nd.h create mode 100644 infinity/library/include/infinity/greedy.h create mode 100644 infinity/library/src/greedy-2d.c create mode 100644 infinity/library/src/greedy-bsearch.c create mode 100644 infinity/library/src/greedy-nd.c create mode 100644 infinity/library/src/greedy.c create mode 100644 infinity/library/tests/greedy-2d-test.cpp create mode 100644 infinity/library/tests/greedy-nd-test.cpp create mode 100644 lifting/benchmark/CMakeLists.txt create mode 100644 lifting/benchmark/src/main.c create mode 100644 lifting/library/CMakeLists.txt create mode 100644 lifting/library/include/lifting/lifting-mip.h create mode 100644 lifting/library/include/lifting/lifting.h create mode 100644 lifting/library/src/lifting-mip.c create mode 100644 lifting/library/src/lifting.c create mode 100644 lifting/library/tests/fixtures/quads.txt create mode 100644 lifting/library/tests/fixtures/triangles.txt create mode 100644 lifting/library/tests/lifting-test.cpp create mode 100644 multirow/CMakeLists.txt create mode 100644 multirow/include/multirow/cg.h create mode 100644 multirow/include/multirow/double.h create mode 100644 multirow/include/multirow/geometry.h create mode 100644 multirow/include/multirow/lfree2d.h create mode 100644 multirow/include/multirow/lp.h create mode 100644 multirow/include/multirow/mir.h create mode 100644 multirow/include/multirow/params.h create mode 100644 multirow/include/multirow/rational.h create mode 100644 multirow/include/multirow/stats.h create mode 100644 multirow/include/multirow/util.h create mode 100644 multirow/src/cg.c create mode 100644 multirow/src/double.c create mode 100644 multirow/src/geometry.c create mode 100644 multirow/src/lfree2d.c create mode 100644 multirow/src/lp.c create mode 100644 multirow/src/mir.c create mode 100644 multirow/src/rational.c create mode 100644 multirow/src/stats.c create mode 100644 multirow/src/util.c create mode 100644 multirow/tests/cg-test.cpp create mode 100644 multirow/tests/double-test.cpp create mode 100644 multirow/tests/geometry-test.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8cf8b5e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "googletest"] + path = googletest + url = https://github.com/google/googletest.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..61cf305 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) +project(multirow) + +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /Users/axavier/Applications/IBM/ILOG/CPLEX_Studio1262/cplex/lib/x86-64_osx/static_pic) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +find_package(CPLEX REQUIRED) +include_directories(${CPLEX_INCLUDE_DIR}) + +include_directories(multirow/include) +include_directories(lifting/library/include) +include_directories(infinity/library/include) + +add_subdirectory(googletest/googletest) +include_directories(${gtest_SOURCE_DIR}/include) + +add_subdirectory(multirow) +add_subdirectory(lifting/library) +add_subdirectory(lifting/benchmark) +add_subdirectory(infinity/library) +add_subdirectory(infinity/benchmark) diff --git a/cmake/FindCPLEX.cmake b/cmake/FindCPLEX.cmake new file mode 100644 index 0000000..c7a5ac6 --- /dev/null +++ b/cmake/FindCPLEX.cmake @@ -0,0 +1,17 @@ +find_library(CPLEX_LIBRARIES + NAMES cplex cplex1220 cplex1240 cplex1260 cplex1261 cplex1262) + +find_path(CPLEX_INCLUDE_DIR + NAMES ilcplex/cplex.h + PATHS /Users/axavier/Applications/IBM/ILOG/CPLEX_Studio1262/cplex/include) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + CPLEX + DEFAULT_MSG + CPLEX_LIBRARIES + CPLEX_INCLUDE_DIR) + +mark_as_advanced( + CPLEX_LIBRARIES + CPLEX_INCLUDE_DIR) diff --git a/googletest b/googletest new file mode 160000 index 0000000..aa148eb --- /dev/null +++ b/googletest @@ -0,0 +1 @@ +Subproject commit aa148eb2b7f70ede0eb10de34b6254826bfb34f4 diff --git a/infinity/benchmark/CMakeLists.txt b/infinity/benchmark/CMakeLists.txt new file mode 100644 index 0000000..d936db5 --- /dev/null +++ b/infinity/benchmark/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(infinity-benchmark.run src/main.c) +target_link_libraries (infinity-benchmark.run LINK_PUBLIC lifting_static infinity_static multirow_static) diff --git a/infinity/benchmark/src/main.c b/infinity/benchmark/src/main.c new file mode 100644 index 0000000..f76eb22 --- /dev/null +++ b/infinity/benchmark/src/main.c @@ -0,0 +1,467 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _XOPEN_SOURCE 500 + +#include +#include +#include + +#include +#include +#include +#include + +#include + +int ENABLE_LIFTING = 0; + +int MIN_N_ROWS = 2; +int MAX_N_ROWS = 2; + +int DUMP_CUT = 0; +int DUMP_CUT_N = 0; + +int GENERATE_MIR = 0; +int GENERATE_GREEDY = 0; +int KEEP_INTEGRALITY = 0; +int N_ROUNDS = 1; + +char BASIS_FILENAME[1000] = {0}; +char PROBLEM_FILENAME[1000] = {0}; +char KNOWN_SOLUTION_FILENAME[1000] = {0}; + +char OUTPUT_SOLUTION_FILENAME[1000] = {0}; +char OUTPUT_BASIS_FILENAME[1000] = {0}; +char LOG_FILENAME[1000] = {0}; +char STATS_FILENAME[1000] = {0}; + +FILE *LOG_FILE; + +int BOOST_VAR = -1; +double BOOST_FACTOR = 0.01; + +#define OPTION_WRITE_BASIS 1000 +#define OPTION_WRITE_SOLUTION 1001 + +static const struct option options_tab[] = +{ + {"help", no_argument, 0, 'h'}, + {"problem", required_argument, 0, 'p'}, + {"solution", required_argument, 0, 'x'}, + {"mir", no_argument, 0, 'm'}, + {"greedy", no_argument, 0, 'g'}, + {"rounds", required_argument, 0, 'r'}, + {"keep-integrality", no_argument, 0, 'k'}, + {"write-solution", required_argument, 0, OPTION_WRITE_SOLUTION}, + {"write-basis", required_argument, 0, OPTION_WRITE_BASIS}, + {"basis", required_argument, 0, 'b'}, + {"log", required_argument, 0, 'l'}, + {"stats", required_argument, 0, 's'}, + {"boost", required_argument, 0, 't'}, + {"lift", no_argument, 0, 'i'}, + {"rows", required_argument, 0, 'a'}, + {0, 0, 0, 0} +}; + +static void print_usage(char **argv) +{ + printf("Usage: %s [OPTION]...\n", argv[0]); + printf("Solves the given MILP using specified cutting-planes.\n\n"); + + printf("Parameters:\n"); + printf("%4s %-20s %s\n", "-b", "--basis=FILE", + "BAS file containing an optimal basis for the linear relaxation of " + "the problem"); + printf("%4s %-20s %s\n", "-g", "--greedy", + "generate greedy intersection cuts"); + printf("%4s %-20s %s\n", "-k", "--keep-integrality", + "do not relax integrality of variables"); + printf("%4s %-20s %s\n", "-l", "--log=FILE", + "write log to the specified file"); + printf("%4s %-20s %s\n", "-m", "--mir", "generate MIR cuts"); + printf("%4s %-20s %s\n", "-p", "--problem=FILE", "problem to be solved"); + printf("%4s %-20s %s\n", "-r", "--rounds=NUM", + "number of rounds of cutting planes to add"); + printf("%4s %-20s %s\n", "-s", "--stats=FILE", + "write statistics to the specified file"); + printf("%4s %-20s %s\n", "-x", "--solution=FILE", + "known integral solution (to check the validity of the cuts)"); + printf("%4s %-20s %s\n", "", "--write-solution=FILE", + "write solution found at the end of the procedure to given file"); + printf("%4s %-20s %s\n", "", "--write-basis=FILE", + "write optimal LP basis to given file"); + printf("%4s %-20s %s\n", "", "--lift", "enable trivial lifting"); + printf("%4s %-20s %s\n", "", "--rows=N", + "generate multi-row cuts from up to N rows"); +} + +static int parse_args(int argc, + char **argv) +{ + int rval = 0; + opterr = 0; + + while (1) + { + int c = 0; + int option_index = 0; + c = getopt_long(argc, argv, "l:p:s:x:hmgr:kb:t:ia:", options_tab, + &option_index); + + if (c < 0) break; + + switch (c) + { + case 'l': + strcpy(LOG_FILENAME, optarg); + break; + + case 'b': + strcpy(BASIS_FILENAME, optarg); + break; + + case 'g': + GENERATE_GREEDY = 1; + break; + + case 'k': + KEEP_INTEGRALITY = 1; + break; + + case 'm': + GENERATE_MIR = 1; + break; + + case 't': + BOOST_VAR = atoi(optarg); + break; + + case 'r': + N_ROUNDS = atoi(optarg); + break; + + case 'a': + MAX_N_ROWS = atoi(optarg); + break; + + case OPTION_WRITE_SOLUTION: + strcpy(OUTPUT_SOLUTION_FILENAME, optarg); + break; + + case OPTION_WRITE_BASIS: + strcpy(OUTPUT_BASIS_FILENAME, optarg); + break; + + case 'p': + strcpy(PROBLEM_FILENAME, optarg); + break; + + case 's': + strcpy(STATS_FILENAME, optarg); + break; + + case 'x': + strcpy(KNOWN_SOLUTION_FILENAME, optarg); + break; + + case 'h': + print_usage(argv); + exit(0); + + case 'i': + ENABLE_LIFTING = 1; + break; + + case ':': + fprintf(stderr, "%s: option '-%c' requires an argument\n", + argv[0], optopt); + rval = 1; + goto CLEANUP; + + case '?': + default: + fprintf(stderr, "%s: option '-%c' is invalid\n", argv[0], + optopt); + rval = 1; + goto CLEANUP; + + } + } + if ((strlen(PROBLEM_FILENAME) == 0)) + { + fprintf(stderr, "You must specify the problem.\n"); + rval = 1; + } + + if (KEEP_INTEGRALITY && (GENERATE_GREEDY + GENERATE_MIR > 0)) + { + fprintf(stderr, "Cutting planes cannot be added when integrality is " + "kept\n"); + rval = 1; + } + + if (N_ROUNDS < 1) + { + fprintf(stderr, "Invalid number of rounds.\n"); + rval = 1; + } + +CLEANUP: + if (rval) + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); + return rval; +} + +void print_header(int argc, + char *const *argv) +{ + char hostname[3000]; + gethostname(hostname, 1024); + + time_t now; + time(&now); + struct tm *ttm = localtime(&now); + + time_printf("multirow\n"); + time_printf("%s %04d-%02d-%02d %02d:%02d\n", hostname, ttm->tm_year + 1900, + ttm->tm_mon + 1, ttm->tm_mday, ttm->tm_hour, ttm->tm_min); + + time_printf("Compile-time parameters:\n"); + time_printf(" EPSILON: %e\n", EPSILON); + time_printf(" GREEDY_BIG_E: %e\n", GREEDY_BIG_E); + time_printf(" GREEDY_MAX_GAP: %e\n", GREEDY_MAX_GAP); + + char cmdline[5000] = {0}; + for (int i = 0; i < argc; i++) + sprintf(cmdline + strlen(cmdline), "%s ", argv[i]); + + time_printf("Command line arguments:\n"); + time_printf(" %s\n", cmdline); +} + +int main(int argc, + char **argv) +{ + int rval = 0; + double *x = 0; + struct CG *cg = 0; + + struct LP lp; + char *column_types = 0; + + rval = parse_args(argc, argv); + if (rval) return 1; + + if (LOG_FILENAME[0]) + { + LOG_FILE = fopen(LOG_FILENAME, "w"); + abort_if(!LOG_FILE, "could not open log file"); + } + + if_info_level + { + print_header(argc, argv); + } + + STATS_init(); + STATS_set_input_filename(PROBLEM_FILENAME); + progress_title(PROBLEM_FILENAME); + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = LP_create(&lp, "multirow"); + abort_if(rval, "LP_create failed"); + + rval = LP_read_problem(&lp, PROBLEM_FILENAME); + abort_if(rval, "LP_read_problem failed"); + + int ncols = LP_get_num_cols(&lp); + int nrows = LP_get_num_rows(&lp); + + log_info(" %d rows, %d cols\n", nrows, ncols); + + x = (double *) malloc(ncols * sizeof(double)); + abort_if(!x, "could not allocate x"); + + if (!KEEP_INTEGRALITY) + { + column_types = (char *) malloc(ncols * sizeof(char)); + abort_if(!column_types, "could not allocate column_types"); + + log_info("Storing column types...\n"); + rval = LP_get_column_types(&lp, column_types); + abort_if(rval, "LP_get_column_types failed"); + + log_info("Relaxing integrality...\n"); + rval = LP_relax(&lp); + abort_if(rval, "LP_relax failed"); + + log_info("Disabling presolve...\n"); + LP_disable_presolve(&lp); + } + + if(BASIS_FILENAME[0]) + { + rval = LP_read_basis(&lp, BASIS_FILENAME); + abort_if(rval, "LP_read_basis failed"); + } + + log_info("Optimizing...\n"); + int infeasible; + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + double cost = 0, xboost = 0; + if(BOOST_VAR > 0) + { + rval = LP_get_x(&lp, x); + abort_if(rval, "LP_get_x failed"); + xboost = x[BOOST_VAR]; + + for (int i = 0; i < ncols; i++) + log_info("x[%3d] = %12.8lf\n", i, x[i]); + } + + + double obj; + rval = LP_get_obj_val(&lp, &obj); + abort_if(rval, "LP_get_obj_val failed"); + + log_info(" opt = %lf\n", obj); + + STATS_set_obj_value(obj); + STATS_finish_round(); + + if(BOOST_VAR >= 0) + log_info("Boosting variable %d by %.2lf\n", BOOST_VAR, BOOST_FACTOR); + + if(OUTPUT_BASIS_FILENAME[0]) + { + rval = LP_write_basis(&lp, OUTPUT_BASIS_FILENAME); + abort_if(rval, "LP_write_basis failed"); + } + + if(GENERATE_MIR || GENERATE_GREEDY) + { + cg = (struct CG *) malloc(sizeof(struct CG)); + abort_if(!cg, "could not allocate cg"); + + log_info("Reading tableau rows...\n"); + rval = CG_init(&lp, column_types, cg); + abort_if(rval, "CG_init failed"); + + if (strlen(KNOWN_SOLUTION_FILENAME) > 0) + { + if(access(KNOWN_SOLUTION_FILENAME, F_OK) != -1) + { + rval = LP_read_solution(&lp, KNOWN_SOLUTION_FILENAME, x); + abort_if(rval, "LP_read_solution failed"); + + rval = CG_set_integral_solution(cg, x); + abort_if(rval, "CG_set_integral_solution failed"); + } + else + { + log_error("ERROR: Could not read solution file!\n"); + } + } + + rval = LP_get_x(&lp, x); + abort_if(rval, "LP_get_x failed"); + + rval = CG_set_basic_solution(cg, x); + abort_if(rval, "CG_set_basic_solution failed"); + + if (GENERATE_MIR) + { + log_info("Adding MIR cuts...\n"); + rval = CG_add_single_row_cuts( + cg, + (SingleRowGeneratorCallback) + MIR_generate_cut); + abort_if(rval, "CG_add_single_row_cuts failed"); + + log_info("Optimizing...\n"); + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + rval = LP_get_obj_val(&lp, &obj); + abort_if(rval, "LP_get_obj_val failed"); + + log_info(" opt = %lf\n", obj); + STATS_set_obj_value(obj); + + STATS_finish_round(); + } + + if (GENERATE_GREEDY) + { + for(int k = MIN_N_ROWS; k <= MAX_N_ROWS; k++) + { + log_info("Adding greedy intersection cuts (%d rows)...\n", k); + + rval = CG_add_multirow_cuts(cg, k, (MultirowGeneratorCallback) + GREEDY_generate_cut); + abort_if(rval, "CG_add_multirow_cuts failed"); + } + + log_info("Optimizing...\n"); + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + rval = LP_get_obj_val(&lp, &obj); + abort_if(rval, "LP_get_obj_val failed"); + + log_info(" opt = %lf\n", obj); + STATS_set_obj_value(obj); + + STATS_finish_round(); + } + + CG_free(cg); + } + + + if(BOOST_VAR > 0) + { + FILE *boost = fopen("boost.csv", "a"); + fprintf(boost, "%d, %.8lf, %.8lf, %.8lf\n", BOOST_VAR, obj, cost, + xboost); + } + + if(OUTPUT_SOLUTION_FILENAME[0]) + { + rval = LP_write_solution(&lp, OUTPUT_SOLUTION_FILENAME); + abort_if(rval, "LP_write_solution failed"); + } + + if(STATS_FILENAME[0]) + { + log_info("Writing stats to file %s...\n", STATS_FILENAME); + rval = STATS_print_yaml(STATS_FILENAME); + abort_if(rval, "STATS_print_yaml failed"); + } + +CLEANUP: + if (LOG_FILE) fclose(LOG_FILE); + if (x) free(x); + if (column_types) free(column_types); + LP_free(&lp); + + return rval; +} diff --git a/infinity/library/CMakeLists.txt b/infinity/library/CMakeLists.txt new file mode 100644 index 0000000..7d3ff5a --- /dev/null +++ b/infinity/library/CMakeLists.txt @@ -0,0 +1,20 @@ +set(COMMON_SOURCES + src/greedy-2d.c + src/greedy-nd.c + src/greedy-bsearch.c + src/greedy.c + include/infinity/greedy-2d.h + include/infinity/greedy-nd.h + include/infinity/greedy-bsearch.h + include/infinity/greedy.h) + +set(TEST_SOURCES + tests/greedy-2d-test.cpp + tests/greedy-nd-test.cpp) + +add_library(infinity_static ${COMMON_SOURCES}) +set_target_properties(infinity_static PROPERTIES OUTPUT_NAME lifting) +target_include_directories (infinity_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(infinity-test.run ${COMMON_SOURCES} ${TEST_SOURCES}) +target_link_libraries(infinity-test.run gtest_main multirow_static lifting_static) diff --git a/infinity/library/include/infinity/greedy-2d.h b/infinity/library/include/infinity/greedy-2d.h new file mode 100644 index 0000000..267fd6e --- /dev/null +++ b/infinity/library/include/infinity/greedy-2d.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MULTIROW_GREEDY_2D_H +#define MULTIROW_GREEDY_2D_H + +int GREEDY_2D_bound(const double *rays, + const double *bounds, + int nrays, + const double *f, + const double *p, + double *epsilon, + double *v1, + double *v2, + int *index1, + int *index2); + +int GREEDY_2D_generate_cut(const double *rays, + int nrays, + const double *f, + double *bounds); + + +#endif //MULTIROW_GREEDY_2D_H diff --git a/infinity/library/include/infinity/greedy-bsearch.h b/infinity/library/include/infinity/greedy-bsearch.h new file mode 100644 index 0000000..49160cc --- /dev/null +++ b/infinity/library/include/infinity/greedy-bsearch.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef MULTIROW_GREEDY_BSEARCH_H +#define MULTIROW_GREEDY_BSEARCH_H + +int create_sfree_mip(int nrows, + int nrays, + const double *f, + const double *rays, + const double *bounds, + double e, + struct LP *lp); + +int GREEDY_BSEARCH_compute_bounds(int nrows, + int nrays, + const double *f, + const double *rays, + double *bounds); + +#endif //MULTIROW_GREEDY_BSEARCH_H diff --git a/infinity/library/include/infinity/greedy-nd.h b/infinity/library/include/infinity/greedy-nd.h new file mode 100644 index 0000000..ce295a3 --- /dev/null +++ b/infinity/library/include/infinity/greedy-nd.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef MULTIROW_GREEDY_ND_H +#define MULTIROW_GREEDY_ND_H + +int GREEDY_ND_next_lattice_point(int dim, + const double *lb, + const double *ub, + double *p, + int *finished); + +int GREEDY_create_psi_lp(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + struct LP *lp); + +int GREEDY_ND_psi(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + const double *q, + const double q_scale, + struct LP *lp, + double *value); + +int GREEDY_ND_pi(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + const double *q, + const double q_scale, + struct LP *lp, + double *value); + +int GREEDY_ND_generate_cut(int nrows, + int nrays, + const double *f, + const double *rays, + double *beta); + +int GREEDY_ND_bound(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double *epsilon, + int *tx); + +int GREEDY_ND_cone_bound(int nrows, + int nrays, + const double *f, + const double *rays, + const int *rx, + const double *x, + const double *beta, + double *epsilon); + +int GREEDY_ND_find_violated_cone(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double epsilon, + int *rx, + double *sbar, + int *violated_found); + +int GREEDY_ND_find_tight_rays(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double epsilon, + int *tx); + +int GREEDY_ND_scale_to_ahull(int nrows, + int nrays, + const double *rays, + const int *rx, + const double *beta, + double epsilon, + const double *d, + double *alpha); + +#endif //MULTIROW_GREEDY_ND_H diff --git a/infinity/library/include/infinity/greedy.h b/infinity/library/include/infinity/greedy.h new file mode 100644 index 0000000..9e02cf1 --- /dev/null +++ b/infinity/library/include/infinity/greedy.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef MULTIROW_GREEDY_H +#define MULTIROW_GREEDY_H + +int GREEDY_write_sage_file(int nrows, + int nrays, + const double *f, + const double *rays, + const double *bounds, + const char *filename); + +int GREEDY_generate_cut(int nrows, + struct Row **rows, + const char *column_types, + struct Row *cut); + +#endif //MULTIROW_GREEDY_H diff --git a/infinity/library/src/greedy-2d.c b/infinity/library/src/greedy-2d.c new file mode 100644 index 0000000..b155288 --- /dev/null +++ b/infinity/library/src/greedy-2d.c @@ -0,0 +1,738 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include +#include +#include +#include + +#include + +static int get_bounding_box(int nrows, + int nrays, + const double *rays, + const double *bounds, + double epsilon, + double *lb, + double *ub) +{ + for(int i = 0; i < nrows; i++) + { + ub[i] = 0; + lb[i] = 0; + } + + for (int i=0; i < nrays; i++) + { + double e = fmin(epsilon, bounds[i]); + + for(int j = 0; j < nrows; j++) + { + double rij = rays[nrows * i + j] * e; + lb[j] = fmin(lb[j], floor(rij)); + ub[j] = fmax(ub[j], ceil(rij)); + } + } + + return 0; +} + +struct LatticeSequence +{ + int square; + int direction; + int steps; + int eol; + + int i; + int j; +}; + +static void lattice_sequence_init(struct LatticeSequence *seq) +{ + seq->steps = 0; + seq->square = 0; + seq->direction = 0; + seq->i = 0; + seq->j = 0; + seq->eol = 0; +} + +static int next_lattice_point(struct LatticeSequence *seq, + const double *lb, + const double *ub) +{ + int rval = 0; + + seq->eol = 1; + + for(int k = 0; k < 8 * (seq->square+1); k++) + { + if(seq->steps > 0) + { + seq->steps--; + + switch(seq->direction) + { + case 3: + seq->i++; + break; + + case 2: + seq->j--; + break; + + case 1: + seq->i--; + break; + + case 0: + seq->j++; + break; + } + } + + if(seq->steps == 0) + { + if(seq->direction > 0) + { + seq->direction--; + seq->steps = 2 * seq->square; + } + else + { + seq->square++; + seq->direction = 3; + seq->steps = 2 * seq->square; + seq->i--; + seq->j++; + } + } + + if(seq->i >= lb[0] && seq->i <= ub[0] && seq->j >= lb[1] && + seq->j <= ub[1]) + { + seq->eol = 0; + break; + } + } + + CLEANUP: + return rval; +} + + + +/* + * Returns lambda1 and lambda2 such that p = r1 * lambda1 + r2 * lambda2 + */ +static void find_linear_combination(const double *r1, + const double *r2, + const double *p, + double *lambda1, + double *lambda2) +{ + int rval = 0; + + double den = (r1[0] * r2[1] - r2[0] * r1[1]); + + if(DOUBLE_iszero(den)) den = 0.0; + + *lambda1 = (r2[1] * p[0] - r2[0] * p[1]) / den; + *lambda2 = (-r1[1] * p[0] + r1[0] * p[1]) / den; +} + + +static int generate_split(const double *f, + const double *d, + double *pi, + double *pi_zero, + long max_den) +{ + int rval = 0; + + Rational d1, d2; + double m; + + rval = DOUBLE_to_rational(d[0], 10, d1); + abort_if(rval, "DOUBLE_to_rational failed"); + + rval = DOUBLE_to_rational(d[1], 10, d2); + abort_if(rval, "DOUBLE_to_rational failed"); + + m = lcm(d1->den, d2->den); + + d1->den = m / d1->den; + d1->num *= d1->den; + + d2->den = m / d2->den; + d2->num *= d2->den; + + m = gcd(d1->num, d2->num); + if(m != 0) + { + d1->num /= m; + d2->num /= m; + + pi[0] = (double) d2->num; + pi[1] = - ((double) d1->num); + *pi_zero = floor(pi[0] * f[0] + pi[1] * f[1]); + } + else + { + pi[0] = pi[1] = INFINITY; + *pi_zero = INFINITY; + } + + CLEANUP: + return rval; +} + +/* + * Receives a list of rays r1,...,rn and a point p. Returns i1,i2 such that + * p belongs to cone(r_i1,r_i2). Also returns lambda1, lambda2 such that + * p = r_i1 * lambda1 + r_i2 * lambda2. + * + * The rays must be sorted in clockwise order. + */ +static int find_containing_cone(const double *rays, + const int nrays, + const double *p, + int *index1, + int *index2, + double *lambda1, + double *lambda2) +{ + int rval = 0; + + int i1, i2; + + for (i1 = 0; i1 < nrays; i1++) + { + i2 = (i1 + 1) % nrays; + + const double *r1 = &rays[i1 * 2]; + const double *r2 = &rays[i2 * 2]; + + double at1 = atan2(r1[1], r1[0]); + double at2 = atan2(r2[1], r2[0]) - at1; + if (at2 > 0) at2 -= 2 * M_PI; + if (at2 <= - M_PI) + { + log_verbose(" discarding obtuse rays\n"); + log_verbose(" r1=%.12lf %.12lf\n", r1[0], r1[1]); + log_verbose(" r2=%.12lf %.12lf\n", r2[0], r2[1]); + continue; + } + + find_linear_combination(r1, r2, p, lambda1, lambda2); + + log_verbose(" r1=%.12lf %.12lf\n", r1[0], r1[1]); + log_verbose(" r2=%.12lf %.12lf\n", r2[0], r2[1]); + log_verbose(" %.8lf %.8lf\n", *lambda1, *lambda2); + + if(DOUBLE_iszero(*lambda1)) *lambda1 = 0.0; + if(DOUBLE_iszero(*lambda2)) *lambda2 = 0.0; + + if ((*lambda1) >= 0 && (*lambda2) >= 0 && (*lambda1) <= 1e9 && + (*lambda2) <= 1e9) + { + log_verbose(" match!\n"); + break; + } + } + + if (i1 == nrays) + i1 = i2 = -1; + + *index1 = i1; + *index2 = i2; + +CLEANUP: + return rval; +} + + +/* + * Find lambda such that p lies on the line connecting lambda * r1 and lambda * r2 + */ +static int scale_cone_to_point(const double *r1, + const double *r2, + const double *p, + double *lambda) +{ + int rval = 0; + + double a = r1[0], b = r1[1]; + double c = r2[0], d = r2[1]; + double den = (b * c - a * d); + + //abort_iff(fabs(den) < 1e-9, "rays cannot be parallel (den=%.12lf)", den); + + *lambda = p[0] * (b - d) - p[1] * (a - c); + *lambda /= den; + +CLEANUP: + return rval; +} + +/* + * Find lambda such that p lies in the line connecting r1 and lambda * r2 + */ +static int shear_cone_to_point(const double *r1, + const double *r2, + const double *p, + double *lambda) +{ + int rval = 0; + + double a = r1[0], b = r1[1]; + double c = r2[0], d = r2[1]; + double den = d * (p[0] - a) - c * (p[1] - b); + + *lambda = b * p[0] - a * p[1]; + *lambda /= den; + +CLEANUP: + return rval; +} + +static int scale_to_chull(double *rays, int nrays, double *scale) +{ + int rval = 0; + + double *rays_extended = 0; + double *vertices = 0; + int nvertices; + + rays_extended = (double*) malloc(2 * (nrays + 1) * sizeof(double)); + vertices = (double*) malloc(2 * (nrays + 1) * sizeof(double)); + abort_if(!rays_extended, "could not allocate rays_extended"); + abort_if(!vertices, "could not allocate vertices"); + + memcpy(rays_extended, rays, 2 * nrays * sizeof(double)); + rays_extended[2*nrays] = 0; + rays_extended[2*nrays + 1] = 0; + + rval = chull_2d(rays_extended, nrays + 1, vertices, &nvertices); + abort_if(rval, "chull_2d failed"); + + for(int i = 0; i < nrays; i++) + scale[i] = 1.0; + + log_verbose(" convex hull:\n"); + for(int i = 0; i < nvertices; i++) + { + log_verbose(" v%-3d: %20.8lf %20.8lf\n", i, vertices[2 * i], + vertices[2 * i + 1]); + } + + log_verbose(" rays:\n"); + for(int i = 0; nrays >= 3 && i < nrays; i++) + { + int i1, i2; + double lambda1, lambda2, mu; + + rval = find_containing_cone(vertices, nvertices, &rays[2*i], &i1, &i2, + &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + log_verbose("%.8lf %.8lf\n", lambda1, lambda2); + + if(i1 < 0 || i2 < 0) continue; + + if(DOUBLE_iszero(lambda1)) + { + mu = lambda2; + } + else if(DOUBLE_iszero(lambda2)) + { + mu = lambda1; + } + else + { + rval = scale_vector_to_line(&vertices[2*i1], &vertices[2*i2], + &rays[2*i], &mu); + abort_if(rval, "scale_vector_to_line failed"); + } + + abort_if(!isfinite(mu), "mu should be finite"); + + //log_verbose(" r%-3d: %.2lf %.2lf %.2lf\n", i, rays[2*i], rays[2*i+1], mu); + + rays[2*i] *= mu; + rays[2*i+1] *= mu; + scale[i] = mu; + + log_verbose(" r%-3d: %20.12lf %20.12lf (scale %.8lf)\n", i, rays[2*i], + rays[2*i+1], scale[i]); + } + +CLEANUP: + if(rays_extended) free(rays_extended); + if(vertices) free(vertices); + return rval; +} + +#ifndef TEST_SOURCE + +int GREEDY_2D_bound(const double *rays, + const double *bounds, + int nrays, + const double *f, + const double *p, + double *epsilon, + double *v1, + double *v2, + int *index1, + int *index2) +{ + int rval = 0; + + int i1, i2, iexact = -1; + + double e1, e2; + const double *r1, *r2; + double lambda1, lambda2; + + double pp[2] = { p[0] - f[0], p[1] - f[1] }; + + rval = find_containing_cone(rays, nrays, pp, &i1, &i2, &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + + if(i1 < 0 || i2 < 0) + { + log_verbose(" no cone\n"); + *epsilon = INFINITY; + goto CLEANUP; + } + + if(DOUBLE_iszero(lambda1)) iexact = i2; + if(DOUBLE_iszero(lambda2)) iexact = i1; + + if(iexact >= 0) + { + int inext = (iexact + 1) % nrays; + int iprev = (iexact + (nrays-1)) % nrays; + + double mu1, mu2; + find_linear_combination(&rays[2 * iprev], &rays[2 * inext], + &rays[2 * iexact], &mu1, &mu2); + + log_verbose(" mu=%.12lf %.12lf\n", mu1, mu2); + + int should_enlarge_cone = 1; + if(!DOUBLE_leq(bounds[iexact], lambda1+lambda2)) should_enlarge_cone = 0; + if(!DOUBLE_geq(mu1, 0) || !DOUBLE_geq(mu2, 0)) should_enlarge_cone = 0; + if(isinf(mu1) || isinf(mu2)) should_enlarge_cone = 0; + if(fabs(mu1) > 1e9 || fabs(mu2) > 1e9) should_enlarge_cone = 0; + + if(should_enlarge_cone) + { + i1 = iprev; + i2 = inext; + } + else + { + v1[0] = v1[1] = INFINITY; + v2[0] = v2[1] = INFINITY; + *epsilon = (lambda1 + lambda2); + if(DOUBLE_leq(bounds[iexact], *epsilon)) + *epsilon = INFINITY; + + *index1 = *index2 = iexact; + goto CLEANUP; + } + + } + + if(bounds[i1] > bounds[i2]) + { + swap(i1, i2, int); + swap(lambda1, lambda2, double); + } + + *index1 = i1; + *index2 = i2; + + r1 = &rays[2 * i1]; + r2 = &rays[2 * i2]; + + log_verbose(" r%-4d %20.12lf %20.12lf\n", i1, r1[0], r1[1]); + log_verbose(" r%-4d %20.12lf %20.12lf\n", i2, r2[0], r2[1]); + log_verbose(" pp %20.12lf %20.12lf\n", pp[0], pp[1]); + log_verbose(" lambda %20.12lf %20.12lf\n", lambda1, lambda2); + + double r1bound[2] = { r1[0] * bounds[i1], r1[1] * bounds[i1] }; + + rval = scale_cone_to_point(r1, r2, pp, &e1); + abort_if(rval, "scale_cone_to_point failed"); + + rval = shear_cone_to_point(r1bound, r2, pp, &e2); + abort_if(rval, "scale_cone_to_point failed"); + + log_verbose(" e1=%20.12lf\n", e1); + log_verbose(" e2=%20.12lf\n", e2); + log_verbose(" b1=%20.12lf\n", bounds[i1]); + log_verbose(" b2=%20.12lf\n", bounds[i2]); + + switch(DOUBLE_cmp(e1, bounds[i1])) + { + case -1: + *epsilon = e1; + v1[0] = r1[0] * e1; + v1[1] = r1[1] * e1; + v2[0] = r2[0] * e1; + v2[1] = r2[1] * e1; + break; + + case 0: + case 1: + if(DOUBLE_geq(e2, bounds[i2])) + { + *epsilon = INFINITY; + } + else + { + *epsilon = e2; + v1[0] = r1[0] * bounds[i1]; + v1[1] = r1[1] * bounds[i1]; + v2[0] = r2[0] * e2; + v2[1] = r2[1] * e2; + } + } + + CLEANUP: + return rval; +} + + +int GREEDY_2D_generate_cut(const double *original_rays, + const int nrays, + const double *f, + double *bounds) +{ + log_verbose("GREEDY_2D_generate_cut\n"); + int rval = 0; + int count = 0; + + double *scale = 0; + double *rays = 0; + + double lb[2], ub[2]; + + for (int i = 0; i < nrays; i++) + bounds[i] = GREEDY_BIG_E; + + scale = (double*) malloc(nrays * sizeof(double)); + rays = (double*) malloc(2 * nrays * sizeof(double)); + abort_if(!rays, "could not allocate rays"); + abort_if(!scale, "could not allocate scale"); + + memcpy(rays, original_rays, 2 * nrays * sizeof(double)); + + rval = scale_to_chull(rays, nrays, scale); + abort_if(rval, "scale_to_chull failed"); + + long seq_count = 0; + + while(1) + { + log_verbose(" starting iteration %d...\n", count); + + abort_if(count++ > 2 * nrays, "infinite loop"); + + rval = get_bounding_box(2, nrays, rays, bounds, GREEDY_BIG_E, lb, ub); + abort_if(rval, "get_bounding_box failed"); + + log_verbose(" box=[%.2lf %.2lf] [%.2lf %.2lf]\n", lb[0], ub[0], lb[1], ub[1]); + + int best_i1, best_i2; + double best_v1[2]; + double best_v2[2]; + double best_p[2]; + double best_epsilon = INFINITY; + + struct LatticeSequence seq; + lattice_sequence_init(&seq); + + while(!seq.eol) + { + seq_count++; + double p[2] = { seq.i, seq.j }; + double v1[2], v2[2], epsilon; + int i1, i2; + + log_verbose(" p=%.2lf %.2lf\n", p[0], p[1]); + + rval = GREEDY_2D_bound(rays, bounds, nrays, f, p, &epsilon, v1, v2, + &i1, &i2); + abort_if(rval, "GREEDY_2D_bound failed"); + + log_verbose(" epsilon=%.2lf\n", epsilon); + + if(epsilon >= 0 && epsilon < best_epsilon) + { + log_verbose(" found smaller epsilon: %.8lf\n", epsilon); + + rval = get_bounding_box(2, nrays, rays, bounds, epsilon, lb, ub); + abort_if(rval, "get_bounding_box failed"); + + log_verbose(" p=%.2lf %.2lf\n", p[0], p[1]); + log_verbose(" box=[%.2lf %.2lf] [%.2lf %.2lf]\n", lb[0], ub[0], + lb[1], ub[1]); + log_verbose(" v1=%12.8lf %12.8lf\n", v1[0], v1[1]); + log_verbose(" v2=%12.8lf %12.8lf\n", v2[0], v2[1]); + log_verbose(" rays=[%d %d]\n", i1, i2); + + best_epsilon = epsilon; + best_v1[0] = v1[0]; + best_v1[1] = v1[1]; + best_v2[0] = v2[0]; + best_v2[1] = v2[1]; + best_p[0] = p[0]; + best_p[1] = p[1]; + best_i1 = i1; + best_i2 = i2; + } + + next_lattice_point(&seq, lb, ub); + if(seq_count > MAX_LATTICE_POINTS) + { + rval = ERR_MIP_TIMEOUT; + goto CLEANUP; + } + } + + if(isinf(best_epsilon)) + { + log_verbose(" best_epsilon is infinity\n"); + break; + } + + log_verbose(" updating bounds\n"); + if(isinf(best_v1[0])) + { + bounds[best_i1] = best_epsilon; + log_verbose(" bounds[%d]=%.8lf (exact)\n", best_i1, best_epsilon); + } + else + { + log_verbose(" v1=%.8lf %.8lf\n", best_v1[0], best_v1[1]); + log_verbose(" v2=%.8lf %.8lf\n", best_v2[0], best_v2[1]); + log_verbose(" i=%d %d\n", best_i1, best_i2); + + for (int i = 0; i < nrays; i++) + { + double lambda; + + rval = scale_vector_to_line(best_v1, best_v2, &rays[2 * i], &lambda); + abort_if(rval, "scale_vector_to_line failed"); + + if(!DOUBLE_geq(lambda, 0)) continue; + + bounds[i] = fmin(bounds[i], lambda); + log_verbose(" bounds[%d]=%.8lf\n", i, bounds[i]); + } + } + + //if(count > 0) + //{ + // for (int i = 0; i < nrays; i++) + // bounds[i] = fmin(bounds[i], best_epsilon); + + // break; + //} + + int is_split; + + for (int k = 0; k < nrays; k++) + { + if(bounds[k] < 100) continue; + is_split = 1; + + double *split_direction = &rays[2 * k]; + + log_verbose(" split_direction=%.2lf %.2lf\n", split_direction[0], + split_direction[1]); + + double pi[2]; + double pi_zero; + + rval = generate_split(f, split_direction, pi, &pi_zero, 10); + abort_if(rval, "generate_split failed"); + + log_verbose(" pi=%.2lf %.2lf\n", pi[0], pi[1]); + log_verbose(" pi_zero=%.2lf\n", pi_zero); + + if(isinf(pi[0])) + { + is_split = 0; + break; + } + + double lhs; + + // reject splits that have f on the boundary + lhs = f[0] * pi[0] + f[1] * pi[1]; + if(DOUBLE_eq(pi_zero, lhs) || DOUBLE_eq(lhs, pi_zero+1)) + { + log_verbose(" split rejected\n"); + is_split = 0; + } + + for (int i = 0; i < nrays && is_split; i++) + { + const double *r = &rays[2 * i]; + + lhs = (f[0] + r[0] * bounds[i]) * pi[0]; + lhs += (f[1] + r[1] * bounds[i]) * pi[1]; + + if (!(DOUBLE_leq(pi_zero, lhs) && DOUBLE_leq(lhs, pi_zero+1))) + { + log_verbose(" point %.4lf %.4lf falls outside of the split\n", + f[0] + r[0]*bounds[i], f[1] + r[1] * bounds[i]); + is_split = 0; + } + } + + if(is_split) + { + log_verbose(" split confirmed. stopping.\n"); + break; + } + } + + if(is_split) break; + + + } + + for(int i=0; i. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +int create_sfree_mip(int nrows, + int nrays, + const double *f, + const double *rays, + const double *bounds, + double e, + struct LP *lp) +{ + int rval = 0; + + double rhs; + char sense; + + int rmatbeg = 0; + int* rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc((nrows + nrays) * sizeof(int)); + rmatval = (double *) malloc((nrows + nrays) * sizeof(double)); + abort_if(!rmatind, "could not allocate rmatind"); + abort_if(!rmatval, "could not allocate rmatval"); + + rval = LP_create(lp, "greedy"); + abort_if(rval, "LP_create failed"); + + // create x (basic) variables + for (int i = 0; i < nrows; i++) + { + rval = LP_new_col(lp, 0, -MILP_INFINITY, MILP_INFINITY, 'I'); + abort_if(rval, "LP_new_col failed"); + } + + // create s (non-basic) variables + for (int i = 0; i < nrays; i++) + { + rval = LP_new_col(lp, 1.0, 0, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + } + + // add constraint \sum_{i=1}^m s_i \leq 1 + sense = 'L'; + rhs = 1.0; + + for (int i = 0; i < nrays; i++) + { + rmatind[i] = i + nrows; + rmatval[i] = 1.0; + } + + rval = LP_add_rows(lp, 1, nrays, &rhs, &sense, &rmatbeg, rmatind, rmatval); + abort_if(rval, "LP_add_rows failed"); + + // add constraints x_i - \sum_{j=1}^m min{e,e_j} s_j R_ji = f_i + for (int i = 0; i < nrows; i++) + { + int k = 0; + sense = 'E'; + rhs = f[i]; + + rmatind[k] = i; + rmatval[k] = 1.0; + k++; + + for (int j = 0; j < nrays; j++) + { + rmatind[k] = j + nrows; + rmatval[k] = -rays[nrows * j + i] * fmin(e, bounds[j]); + k++; + } + + rval = LP_add_rows(lp, 1, nrays + 1, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + +CLEANUP: + if (rmatind) free(rmatind); + if (rmatval) free(rmatval); + return rval; +} + +int GREEDY_BSEARCH_compute_bounds(int nrows, + int nrays, + const double *f, + const double *rays, + double *bounds) +{ + int rval = 0; + + struct LP lp; + double e_upper = 2 * GREEDY_BIG_E; + double e_lower = 0.0; + + int cplex_count = 0; + double cplex_time = 0; + + int iteration_count = 0; + + double *x = 0; + + x = (double *) malloc((nrays + nrows) * sizeof(double)); + abort_if(!x, "could not allocate x"); + + for (int i = 0; i < nrays; i++) + bounds[i] = GREEDY_BIG_E; + + for (int it = 0;; it++) + { + abort_if(it > 2*nrays, "stuck in an infinite loop"); + + log_verbose("Starting iteration %d...\n", it); + + iteration_count++; + + + int solution_found = 0; + int inner_count = 0; + + while (fabs(e_upper - e_lower) > GREEDY_MAX_GAP) + { + inner_count++; + + double e = (e_upper + e_lower) / 2; + log_verbose(" e=%.12lf\n", e); + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = create_sfree_mip(nrows, nrays, f, rays, bounds, e, &lp); + abort_if(rval, "create_sfree_mip failed"); + + if_verbose_level + { + rval = LP_write(&lp, "greedy.lp"); + abort_if(rval, "LP_write failed"); + } + + int infeasible; + cplex_count++; + + double initial_time = get_user_time(); + + log_verbose(" Optimizing...\n"); + rval = LP_optimize(&lp, &infeasible); + if (rval) + { + // Workaround for CPLEX bug. If CPLEX tell us that this problem + // is unbounded, we disable presolve and try again. + LP_free(&lp); + LP_open(&lp); + + rval = create_sfree_mip(nrows, nrays, f, rays, bounds, e, &lp); + abort_if(rval, "create_sfree_mip failed"); + + LP_disable_presolve(&lp); + + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + } + + cplex_time += get_user_time() - initial_time; + + if (infeasible) + { + e_lower = e; + log_verbose(" infeasible\n"); + if (e > GREEDY_BIG_E-1) + { + LP_free(&lp); + goto OUT; + } + } + else + { + log_verbose(" feasible\n"); + e_upper = e; + solution_found = 1; + + rval = LP_get_x(&lp, x); + abort_if(rval, "LP_get_x failed"); + } + + LP_free(&lp); + } + + if (solution_found) + { + for (int j = 0; j < nrays; j++) + { + if (!DOUBLE_geq(x[nrows + j], 0.001)) continue; + bounds[j] = fmin(bounds[j] * 0.99, e_lower * 0.99); + } + } + + log_verbose(" %d iterations %12.8lf gap\n", inner_count, e_upper - + e_lower); + + e_lower = e_upper; + e_upper = 2 * GREEDY_BIG_E; + } + +OUT: + log_debug(" %6d IPs (%.2lfms per call, %.2lfs total)\n", cplex_count, + cplex_time * 1000.0 / cplex_count, cplex_time); + + for(int i = 0; i < nrays; i++) + abort_if(DOUBLE_iszero(bounds[i]), "bounds should be positive"); + + if_verbose_level + { + time_printf("Bounds:\n"); + for (int k = 0; k < nrays; k++) + time_printf(" %12.8lf %12.8lf\n", k, bounds[k], 1 / bounds[k]); + } + +CLEANUP: + if (x) free(x); + return rval; +} diff --git a/infinity/library/src/greedy-nd.c b/infinity/library/src/greedy-nd.c new file mode 100644 index 0000000..d9fa7c6 --- /dev/null +++ b/infinity/library/src/greedy-nd.c @@ -0,0 +1,1331 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include +#include + +static long lp_count = 0; +static double lp_time = 0; + +static long epsilon_lp_count = 0; +static double epsilon_lp_time = 0; + +static long tight_lp_count = 0; +static double tight_lp_time = 0; + +static long violated_lp_count = 0; +static double violated_lp_time = 0; + +static long sfree_mip_count = 0; +static double sfree_mip_time = 0; + +static long scale_ahull_lp_count = 0; +static double scale_ahull_lp_time = 0; + +int find_interior_point_enum(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + const double epsilon, + double *x, + int *found) +{ + int rval = 0; + + abort_if(nrows != 3, "not implemented"); + + double *beta2 = 0; + beta2 = (double*) malloc(nrays * sizeof(double)); + abort_if(!beta2, "could not allocate beta2"); + + for(int i = 0; i < nrays; i++) + { + beta2[i] = fmin(epsilon, beta[i]); + } + + struct LP lp; + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = GREEDY_create_psi_lp(nrows, nrays, f, rays, beta2, &lp); + abort_if(rval, "GREEDY_create_psi_lp failed"); + + *found = 0; + double best_value = INFINITY; + + int M = 1; + for(int x1 = -M; x1 <= M; x1++) + for(int x2 = -M; x2 <= M; x2++) + for(int x3 = -M; x3 <= M; x3++) + { + double value; + double q[3] = {x1 - f[0], x2 - f[1], x3 - f[2]}; + + rval = GREEDY_ND_psi(nrows, nrays, f, rays, beta2, q, 1, &lp, &value); + abort_if(rval, "GREEDY_ND_psi failed"); + + if(value < best_value) + { + best_value = value; + x[0] = x1; + x[1] = x2; + x[2] = x3; + } + } + + if(best_value < 0.999) *found = 1; + +CLEANUP: + if(beta2) free(beta2); + LP_free(&lp); + return rval; +} +int find_interior_point_cplex(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + const double epsilon, + double *x, + int *found) +{ + int rval = 0; + struct LP lp; + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + lp_count++; + sfree_mip_count++; + double initial_time = get_user_time(); + + rval = create_sfree_mip(nrows, nrays, f, rays, beta, epsilon, &lp); + abort_if(rval, "greate_sfree_mip failed"); + + int infeasible; + double objval; + + log_debug(" solving sfree mip...\n"); + rval = LP_optimize(&lp, &infeasible); + if(rval == ERR_MIP_TIMEOUT) goto CLEANUP; + abort_if(rval, "LP_optimize failed"); + + if(infeasible) + { + log_debug(" mip is infeasible. Stopping.\n"); + *found = 0; + goto CLEANUP; + } + + rval = LP_get_x(&lp, x); + abort_if(rval, "LP_get_x failed"); + + if_verbose_level + { + for(int i = 0; i < nrows; i++) + log_verbose(" x%d = %.8lf\n", i, x[i]); + + for(int i = 0; i < nrays; i++) + if(x[i+nrows] > 0.00001) + log_verbose(" t%d = %.8lf\n", i, x[i+nrows]); + } + + rval = LP_get_obj_val(&lp, &objval); + abort_if(rval, "LP_get_obj_val failed"); + + log_debug(" obj = %.8lf\n", objval); + + if(objval >= 0.999) + { + log_debug(" set is lattice-free\n"); + *found = 0; + goto CLEANUP; + } + + *found = 1; + + sfree_mip_time += get_user_time() - initial_time; + lp_time += get_user_time() - initial_time; + +CLEANUP: + LP_free(&lp); + return rval; +} + +int GREEDY_create_psi_lp(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + struct LP *lp) +{ + int rval = 0; + + int rmatbeg = 0; + int* rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc(nrays * sizeof(int)); + rmatval = (double *) malloc(nrays * sizeof(double)); + abort_if(!rmatind, "could not allocate rmatind"); + abort_if(!rmatval, "could not allocate rmatval"); + + rval = LP_create(lp, "psi"); + abort_if(rval, "LP_create failed"); + + // create lambda variables + for (int i = 0; i < nrays; i++) + { + rval = LP_new_col(lp, 1.0, 0, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + } + + // create constraint 0 = \sum_{i=1}^m \lambda_i r^i_j beta_i + for (int j = 0; j < nrows; j++) + { + char sense = 'E'; + double rhs = 0; + int nz = 0; + + for (int i = 0; i < nrays; i++) + { + const double *ri = &rays[i * nrows]; + rmatind[nz] = i; + rmatval[nz] = ri[j] * beta[i]; + nz++; + } + + rval = LP_add_rows(lp, 1, nz, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + + rval = LP_relax(lp); + abort_if(rval, "LP_relax failed"); + + if_verbose_level + { + rval = LP_write(lp, "psi.lp"); + abort_if(rval, "LP_write failed"); + } + +CLEANUP: + if (rmatind) free(rmatind); + if (rmatval) free(rmatval); + return rval; +} + +int GREEDY_ND_pi(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + const double *q, + const double q_scale, + struct LP *lp, + double *value) +{ + int rval = 0; + + abort_if(nrows > 3, "not implemented"); + + double best_value = 1e100; + + if(nrows == 2) + { + int M = 2; + for(int k0 = -M; k0 <= M; k0++) + for(int k1 = -M; k1 <= M; k1++) + { + double qk[] = { frac(q[0] * q_scale) + k0, frac(q[1] * q_scale) + k1 }; + double value; + + rval = GREEDY_ND_psi(nrows, nrays, f, rays, beta, qk, 1, lp, + &value); + abort_if(rval, "GREEDY_ND_psi failed"); + + best_value = min(best_value, value); + } + } + + if(nrows == 3) + { + int M = 2; + for(int k0 = -M; k0 <= M; k0++) + for(int k1 = -M; k1 <= M; k1++) + for(int k2 = -M; k2 <= M; k2++) + { + double qk[] = { + frac(q[0] * q_scale) + k0, + frac(q[1] * q_scale) + k1, + frac(q[2] * q_scale) + k2 + }; + double value; + + rval = GREEDY_ND_psi(nrows, nrays, f, rays, beta, qk, 1, lp, + &value); + abort_if(rval, "GREEDY_ND_psi failed"); + + best_value = min(best_value, value); + } + } + + *value = best_value; + +CLEANUP: + return rval; +} + +/* + * Given a point f, a list of rays r1,...,rm, some real numbers b1,...,bm, a + * point q, and a real number q_scale, this function evaluates psi_B(q * + * q_scale), where B is the convex hull of {f + ri * bi}_i=1^m. + */ +int GREEDY_ND_psi(const int nrows, + const int nrays, + const double *f, + const double *rays, + const double *beta, + const double *q, + const double q_scale, + struct LP *lp, + double *value) +{ + int rval = 0; + + for(int j = 0; j < nrows; j++) + { + rval = LP_change_rhs(lp, j, q[j] * q_scale); + abort_if(rval, "LP_change_rhs failed"); + } + + int infeasible; + rval = LP_optimize(lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + if(infeasible) + { + *value = INFINITY; + } + else + { + rval = LP_get_obj_val(lp, value); + abort_if(rval, "LP_get_obj_val failed"); + } + +CLEANUP: + return rval; +} + +int GREEDY_ND_generate_cut(int nrows, + int nrays, + const double *f, + const double *rays, + double *beta) +{ + log_debug("GREEDY_ND_generate_cut\n"); + int rval = 0; + + double *x = 0; + + int *t = 0; + int *tx = 0; + + t = (int*) malloc(nrays * sizeof(int)); + tx = (int*) malloc(nrays * sizeof(int)); + abort_if(!t, "could not allocate t"); + abort_if(!tx, "could not allocate tx"); + + x = (double*) malloc((nrows + nrays) * sizeof(double)); + abort_if(!x, "could not allocate x"); + + for(int i = 0; i < nrays; i++) + beta[i] = GREEDY_BIG_E; + + int it = 0; + + //lp_time = 0; + //lp_count = 0; + + //epsilon_lp_count = 0; + //epsilon_lp_time = 0; + // + //sfree_mip_count = 0; + //sfree_mip_time = 0; + + //tight_lp_count = 0; + //tight_lp_time = 0; + + //violated_lp_count = 0; + //violated_lp_time = 0; + + //scale_ahull_lp_time = 0; + //scale_ahull_lp_count = 0; + + long x_count = 0; + double epsilon; + double previous_epsilon = -INFINITY; + + for(int i = 0; i < nrows; i++) + log_verbose(" f[%d] = %.12lf\n", i, f[i]); + + while(1) + { + it++; + abort_if(it > 10 * nrays, "infinite loop"); + + log_debug("Starting iteration %d...\n", it); + epsilon = INFINITY; + + for(int i = 0; i < nrays; i++) + t[i] = 0; + + for(int i = 0; i < nrows; i++) + x[i] = 0; + + while(1) + { + log_debug(" epsilon = %.8lf\n", epsilon); + + int found = 0; + + if(nrows == 3) + { + rval = find_interior_point_enum(nrows, nrays, f, rays, beta, epsilon, x, &found); + abort_if(rval, "find_interior_point_enum failed"); + } + + if(!found) + { + rval = find_interior_point_cplex(nrows, nrays, f, rays, beta, epsilon, x, &found); + if(rval == ERR_MIP_TIMEOUT) goto CLEANUP; + abort_if(rval, "find_interior_point_cplex failed"); + if(!found) break; + } + + log_debug(" found interior point:\n"); + for(int i = 0; i < nrows; i++) log_debug(" %.2lf\n", x[i]); + + x_count++; + abort_if(x_count > 1000, "infinite loop"); + + double epsilon_x; + rval = GREEDY_ND_bound(nrows, nrays, f, rays, x, beta, &epsilon_x, + tx); + abort_if(rval, "GREEDY_ND_bound failed"); + epsilon_x *= 0.999; + + if(isinf(epsilon_x)) break; + + log_debug(" epsilon_x = %.8lf\n", epsilon_x); + + if(DOUBLE_eq(epsilon_x, epsilon)) + { + for(int i = 0; i < nrays; i++) + if(tx[i]) t[i] = 1; + } + else if(epsilon_x < epsilon) + { + epsilon = epsilon_x; + for(int i = 0; i < nrays; i++) + t[i] = tx[i]; + } + } + + if(isinf(epsilon)) + break; + + int skip_ahull = 0; + + for(int i = 0; i < nrays; i++) + { + if(t[i]) + { + beta[i] = min(beta[i], epsilon); + beta[i] *= 0.999; + } + else if(!skip_ahull) + { + double alpha; + const double *d = &rays[i * nrows]; + + rval = GREEDY_ND_scale_to_ahull(nrows, nrays, rays, t, beta, + epsilon, d, &alpha); + abort_if(rval, "GREEDY_ND_scale_to_ahull failed"); + + if(DOUBLE_iszero(alpha)) + { + skip_ahull = 1; + continue; + } + + beta[i] = min(beta[i], alpha); + beta[i] *= 0.999; + } + + log_debug(" beta[%2d] = %.4lf\n", i, beta[i]); + } + + previous_epsilon = epsilon; + log_debug("epsilon = %.6lf\n", epsilon); + } + + log_debug(" %6ld lattice points, %ld iterations\n", x_count, it); + + log_debug(" %6ld MIPs (%.2lf ms per call, %.0lf ms total)\n", lp_count, + lp_time * 1000 / lp_count, lp_time * 1000); + + log_debug(" %6ld S-free MIPs (%.2lf ms per call, %.0lf ms total)\n", + sfree_mip_count, sfree_mip_time * 1000 / sfree_mip_count, + sfree_mip_time * 1000); + + log_debug(" %6ld epsilon LPs (%.2lf ms per call, %.0lf ms total)\n", + epsilon_lp_count, epsilon_lp_time * 1000 / epsilon_lp_count, + epsilon_lp_time * 1000); + + log_debug(" %6ld tight-rays LPs (%.2lf ms per call, %.0lf ms total)\n", + tight_lp_count, tight_lp_time * 1000 / tight_lp_count, + tight_lp_time * 1000); + + log_debug(" %6ld violated-cone LPs (%.2lf ms per call, %.0lf ms total)\n", + violated_lp_count, violated_lp_time * 1000 / violated_lp_count, + violated_lp_time * 1000); + + log_debug(" %6ld scale-to-ahull LPs (%.2lf ms per call, %.0lf ms total)\n", + scale_ahull_lp_count, scale_ahull_lp_time * 1000 / scale_ahull_lp_count, + scale_ahull_lp_time * 1000); + +CLEANUP: + if(x) free(x); + if(t) free(t); + if(tx) free(tx); + return rval; +} + +int GREEDY_ND_bound(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double *epsilon, + int *tx) +{ + int rval = 0; + + int found; + int *rx = 0; + double *fbar = 0; + double *sbar = 0; + + rx = (int*) malloc(nrays * sizeof(int)); + fbar = (double*) malloc(nrows * sizeof(double)); + sbar = (double*) malloc(nrays * sizeof(double)); + + abort_if(!rx, "could not allocate rx"); + abort_if(!fbar, "could not allocate fbar"); + abort_if(!sbar, "could not allocate sbar"); + + *epsilon = GREEDY_BIG_E; + double prev_epsilon; + int count = 0; + + while(1) + { + count++; + abort_if(count > 100, "infinite loop"); + + rval = GREEDY_ND_find_violated_cone(nrows, nrays, f, rays, x, beta, + *epsilon, rx, sbar, &found); + abort_if(rval, "GREEDY_ND_find_violated_cone failed"); + + if(!found) break; + + for(int i = 0; i < nrows; i++) + fbar[i] = x[i]; + + for(int j = 0; j < nrays; j++) + { + if(!rx[j]) continue; + const double *r = &rays[nrows * j]; + + for(int i = 0; i < nrows; i++) + fbar[i] -= min(*epsilon, beta[j]) * r[i] * sbar[j]; + } + + log_verbose("%.12lf %.12lf\n", f[0], f[1]); + log_verbose("%.12lf %.12lf\n", fbar[0], fbar[1]); + + prev_epsilon = *epsilon; + + rval = GREEDY_ND_cone_bound(nrows, nrays, fbar, rays, rx, x, beta, + epsilon); + abort_if(rval, "GREEDY_ND_cone_bound failed"); + + log_verbose(" e=%.12lf\n", *epsilon); + abort_if(prev_epsilon < *epsilon, "epsilon should never increase"); + } + + + for(int i = 0; i < nrays; i++) + tx[i] = 0; + + if(DOUBLE_geq(*epsilon, GREEDY_BIG_E)) + { + *epsilon = INFINITY; + goto CLEANUP; + } + else + { + rval = GREEDY_ND_find_tight_rays(nrows, nrays, fbar, rays, x, beta, *epsilon, tx); + abort_if(rval, "GREEDY_ND_find_tight_rays failed"); + } + +CLEANUP: + if(sbar) free(sbar); + if(fbar) free(fbar); + if(rx) free(rx); + return rval; +} + + +static int create_find_epsilon_lp(int nrows, + int nrays, + const double *f, + const double *rays, + const int *t, + const int *rx, + const double *x, + const double *beta, + struct LP *lp) +{ + int rval = 0; + + double rhs; + char sense; + + int nz = 0; + int *map = 0; + int rmatbeg = 0; + int* rmatind = 0; + double *rmatval = 0; + + map = (int*) malloc(nrays * sizeof(int)); + abort_if(!map, "could not allocate map"); + + rmatind = (int *) malloc((nrays + 1 + nrows) * sizeof(int)); + rmatval = (double *) malloc((nrays + 1 + nrows) * sizeof(double)); + abort_if(!rmatind, "could not allocate rmatind"); + abort_if(!rmatval, "could not allocate rmatval"); + + rval = LP_create(lp, "find_epsilon"); + abort_if(rval, "LP_create failed"); + + // create lambda variables + int rx_count = 0; + + for (int i = 0; i < nrays + 1; i++) + { + if(i < nrays && !rx[i]) continue; + + double pi = 0.0; + double lb = -MILP_INFINITY; + + if(i < nrays && !t[i]) + { + pi = 1.0; + lb = 0.0; + } + + rval = LP_new_col(lp, pi, lb, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + + if(i < nrays) + map[i] = rx_count++; + } + + log_verbose("rx_count=%d\n", rx_count); + + // create y variables + for (int i = 0; i < nrows; i++) + { + rval = LP_new_col(lp, 0.0, -MILP_INFINITY, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + } + + // create constraint y = \lambda_x x + \sum_{t \in T} \lambda_r (f + \beta_r r) + for (int j = 0; j < nrows; j++) + { + sense = 'E'; + rhs = 0.0; + nz = 0; + + for (int i = 0; i < nrays; i++) + { + if(!t[i]) continue; + const double *ri = &rays[i * nrows]; + + rmatind[nz] = map[i]; + rmatval[nz] = f[j] + beta[i] * ri[j]; + nz++; + } + + rmatind[nz] = rx_count; + rmatval[nz] = x[j]; + nz++; + + rmatind[nz] = rx_count + j + 1; + rmatval[nz] = -1.0; + nz++; + + rval = LP_add_rows(lp, 1, nz, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + + // create constraint y = f + \sum_{r \in Rx \setminus T) \lambda_r r + for (int j = 0; j < nrows; j++) + { + sense = 'E'; + rhs = f[j]; + nz = 0; + + for (int i = 0; i < nrays; i++) + { + if(!rx[i] || t[i]) continue; + const double *ri = &rays[i * nrows]; + + rmatind[nz] = map[i]; + rmatval[nz] = - ri[j]; + nz++; + } + + rmatind[nz] = rx_count + j + 1; + rmatval[nz] = 1.0; + nz++; + + rval = LP_add_rows(lp, 1, nz, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + + // create constraint \sum_{r \in T} \lambda_r + \lambda_x = 1 + sense = 'E'; + rhs = 1.0; + nz = 0; + + for (int i = 0; i < nrays; i++) + { + if(!t[i]) continue; + rmatind[nz] = map[i]; + rmatval[nz] = 1.0; + nz++; + } + + rmatind[nz] = rx_count; + rmatval[nz] = 1.0; + nz++; + + rval = LP_add_rows(lp, 1, nz, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + + rval = LP_relax(lp); + abort_if(rval, "LP_relax failed"); + + //rval = LP_write(lp, "find-epsilon.lp"); + //abort_if(rval, "LP_write failed"); + + +CLEANUP: + if(map) free(map); + if (rmatind) free(rmatind); + if (rmatval) free(rmatval); + return rval; +} + +int GREEDY_ND_cone_bound(int nrows, + int nrays, + const double *f, + const double *rays, + const int *rx, + const double *x, + const double *beta, + double *epsilon) +{ + log_verbose(" find_epsilon\n"); + int rval = 0; + + int *t = 0; + long it = 0; + + t = (int*) malloc(nrays * sizeof(int)); + abort_if(!t, "could not allocate t"); + + for(int i = 0; i < nrays; i++) + t[i] = 0; + + while(1) + { + it++; + log_verbose("Starting iteration %d...\n", it); + + struct LP lp; + + double initial_time = get_user_time(); + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = create_find_epsilon_lp(nrows, nrays, f, rays, t, rx, x, beta, &lp); + abort_if(rval, "create_find_epsilon_lp failed"); + + int infeasible; + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + lp_count++; + lp_time += get_user_time() - initial_time; + + epsilon_lp_count++; + epsilon_lp_time += get_user_time() - initial_time; + + if(infeasible) + { + *epsilon = INFINITY; + log_verbose(" infeasible\n"); + LP_free(&lp); + goto CLEANUP; + } + + double obj; + rval = LP_get_obj_val(&lp, &obj); + abort_if(rval, "LP_get_obj_val failed"); + + log_verbose(" obj=%.6lf\n", obj); + + for(int i = 0; i < nrays; i++) + { + if(!rx[i]) continue; + log_verbose(" beta[%d]=%.6lf\n", i, beta[i]); + } + + LP_free(&lp); + + double e_min = INFINITY; + double e_max = -INFINITY; + + for(int i = 0; i < nrays; i++) + { + if(!rx[i] || t[i]) continue; + e_min = min(e_min, beta[i]); + } + + for(int i = 0; i < nrays; i++) + { + if(!rx[i]) continue; + e_max = fmax(e_max, beta[i]); + } + + log_verbose(" e_max=%.6lf\n", e_max); + log_verbose(" e_min=%.6lf\n", e_min); + + if(DOUBLE_leq(obj, e_min)) + { + if(DOUBLE_geq(obj, e_max)) + *epsilon = INFINITY; + else + *epsilon = obj; + + goto CLEANUP; + } + else + { + for(int i = 0; i < nrays; i++) + if(rx[i] && DOUBLE_eq(beta[i], e_min)) + t[i] = 1; + } + } + +CLEANUP: + log_verbose(" e=%.6lf\n", *epsilon); + if(t) free(t); + return rval; +} + + +static int create_scale_to_ahull_lp(int nrows, + int nrays, + const double *rays, + const int *rx, + const double *beta, + double epsilon, + const double *d, + struct LP *lp) +{ + int rval = 0; + + double rhs; + char sense; + int nz; + + int rmatbeg = 0; + int* rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc((nrays + 1) * sizeof(int)); + rmatval = (double *) malloc((nrays + 1) * sizeof(double)); + abort_if(!rmatind, "could not allocate rmatind"); + abort_if(!rmatval, "could not allocate rmatval"); + + rval = LP_create(lp, "scale_to_ahull"); + abort_if(rval, "LP_create failed"); + + // create alpha variable + rval = LP_new_col(lp, 1.0, 0.0, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + + + // create lambda variables + for (int i = 0; i < nrays; i++) + { + rval = LP_new_col(lp, 0.0, -MILP_INFINITY, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + } + + // create constraint \sum_{r \in R_x} min(e, beta[r]) * r * \lambda_r = \alpha * d + for (int j = 0; j < nrows; j++) + { + sense = 'E'; + rhs = 0.0; + nz = 0; + + rmatind[nz] = 0; + rmatval[nz] = d[j]; + nz++; + + for (int i = 0; i < nrays; i++) + { + if(!rx[i]) continue; + + const double *ri = &rays[i * nrows]; + rmatind[nz] = 1 + i; + rmatval[nz] = - min(epsilon, beta[i]) * ri[j]; + if(DOUBLE_iszero(rmatval[nz])) continue; + + nz++; + } + + rval = LP_add_rows(lp, 1, nz, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + + // create constraint \sum_{r \in R_x} \lambda_r = 1 + sense = 'E'; + rhs = 1.0; + nz = 0; + + for (int i = 0; i < nrays; i++) + { + if(!rx[i]) continue; + + rmatind[nz] = 1 + i; + rmatval[nz] = 1.0; + nz++; + } + + rval = LP_add_rows(lp, 1, nz, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + + rval = LP_relax(lp); + abort_if(rval, "LP_relax failed"); + + //rval = LP_write(lp, "scale-to-ahull.lp"); + //abort_if(rval, "LP_write failed"); + //UTIL_pause(); + +CLEANUP: + if (rmatind) free(rmatind); + if (rmatval) free(rmatval); + return rval; +} + +int GREEDY_ND_scale_to_ahull(int nrows, + int nrays, + const double *rays, + const int *rx, + const double *beta, + double epsilon, + const double *d, + double *alpha) +{ + log_verbose(" scale_to_ahull\n"); + int rval = 0; + *alpha = INFINITY; + + struct LP lp; + double *x = 0; + + x = (double*) malloc((nrays + 1) * sizeof(double)); + abort_if(!x, "could not allocate x"); + + double initial_time = get_user_time(); + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = create_scale_to_ahull_lp(nrows, nrays, rays, rx, beta, epsilon, + d, &lp); + abort_if(rval, "create_scale_to_ahull_lp failed"); + + int infeasible; + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + lp_count++; + lp_time += get_user_time() - initial_time; + + scale_ahull_lp_count++; + scale_ahull_lp_time += get_user_time() - initial_time; + + if(infeasible) + goto CLEANUP; + + rval = LP_get_x(&lp, x); + abort_if(rval, "LP_get_x failed"); + + *alpha = x[0]; + +CLEANUP: + if(x) free(x); + LP_free(&lp); + return rval; +} + + +static int create_tight_rays_lp(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double epsilon, + double delta, + struct LP *lp) +{ + int rval = 0; + + double rhs; + char sense; + + int rmatbeg = 0; + int* rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc(nrays * sizeof(int)); + rmatval = (double *) malloc(nrays * sizeof(double)); + abort_if(!rmatind, "could not allocate rmatind"); + abort_if(!rmatval, "could not allocate rmatval"); + + rval = LP_create(lp, "tight_rays"); + abort_if(rval, "LP_create failed"); + + // create lambda variables + for (int i = 0; i < nrays; i++) + { + rval = LP_new_col(lp, 1.0, 0.0, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + } + + // create s variables + for (int i = 0; i < nrays; i++) + { + rval = LP_new_col(lp, 0.0, 0.0, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + } + + // create constraint x = f + \sum_{r \in R} min{e, beta[r]} * r * s_r + for (int j = 0; j < nrows; j++) + { + sense = 'E'; + rhs = x[j] - f[j]; + + for (int i = 0; i < nrays; i++) + { + const double *ri = &rays[i * nrows]; + rmatind[i] = nrays + i; + rmatval[i] = min(epsilon, beta[i]) * ri[j]; + if(DOUBLE_iszero(rmatval[i])) rmatval[i] = 0.0; + } + + rval = LP_add_rows(lp, 1, nrays, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + + // create constraint \sum_{r \in R} s_r = 1 + sense = 'E'; + rhs = 1.0; + + for (int i = 0; i < nrays; i++) + { + rmatind[i] = nrays + i; + rmatval[i] = 1.0; + } + + rval = LP_add_rows(lp, 1, nrays, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + + // create constraints \lambda_r + s_r \geq \delta + for (int i = 0; i < nrays; i++) + { + sense = 'G'; + rhs = delta; + + rmatind[0] = i; + rmatval[0] = 1.0; + + rmatind[1] = nrays + i; + rmatval[1] = 1.0; + + rval = LP_add_rows(lp, 1, 2, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + + rval = LP_relax(lp); + abort_if(rval, "LP_relax failed"); + + //rval = LP_write(lp, "tight-rays.lp"); + //abort_if(rval, "LP_write failed"); + + +CLEANUP: + if (rmatind) free(rmatind); + if (rmatval) free(rmatval); + return rval; +} + +int GREEDY_ND_find_tight_rays(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double epsilon, + int *tx) +{ + log_verbose(" find_tight_rays\n"); + int rval = 0; + const double delta = 0.001; + + struct LP lp; + double *sbar = 0; + + sbar = (double*) malloc(2 * nrays * sizeof(double)); + abort_if(!sbar, "could not allocate sbar"); + + double initial_time = get_user_time(); + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = create_tight_rays_lp(nrows, nrays, f, rays, x, beta, epsilon, + delta, &lp); + abort_if(rval, "create_tight_rays_lp failed"); + + int infeasible = 0; + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + lp_count++; + lp_time += get_user_time() - initial_time; + + tight_lp_count++; + tight_lp_time += get_user_time() - initial_time; + + abort_if(infeasible, "tight_rays_lp is infeasible"); + + rval = LP_get_x(&lp, sbar); + abort_if(rval, "LP_get_x failed"); + + for (int i = 0; i < nrays; i++) + tx[i] = DOUBLE_iszero(sbar[i]); + + for (int i = 0; i < nrays; i++) + log_verbose(" tx[%d]=%d\n", i, tx[i]); + +CLEANUP: + if(sbar) free(sbar); + LP_free(&lp); + return rval; +} + + +static int create_violated_cone_lp(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double epsilon, + struct LP *lp) +{ + int rval = 0; + + double rhs; + char sense; + + int rmatbeg = 0; + int* rmatind = 0; + double *rmatval = 0; + + rmatind = (int *) malloc(nrays * sizeof(int)); + rmatval = (double *) malloc(nrays * sizeof(double)); + abort_if(!rmatind, "could not allocate rmatind"); + abort_if(!rmatval, "could not allocate rmatval"); + + rval = LP_create(lp, "violated_cone"); + abort_if(rval, "LP_create failed"); + + // create s variables + for (int i = 0; i < nrays; i++) + { + rval = LP_new_col(lp, 1.0, 0.0, MILP_INFINITY, 'C'); + abort_if(rval, "LP_new_col failed"); + } + + // create constraint x = f + \sum(min{e, beta[r]} * r * s_r) + for (int j = 0; j < nrows; j++) + { + sense = 'E'; + rhs = x[j] - f[j]; + + for (int i = 0; i < nrays; i++) + { + const double *ri = &rays[i * nrows]; + rmatind[i] = i; + rmatval[i] = min(epsilon, beta[i]) * ri[j]; + if(DOUBLE_iszero(rmatval[i])) rmatval[i] = 0.0; + } + + rval = LP_add_rows(lp, 1, nrays, &rhs, &sense, &rmatbeg, rmatind, + rmatval); + abort_if(rval, "LP_add_rows failed"); + } + + rval = LP_relax(lp); + abort_if(rval, "LP_relax failed"); + + //rval = LP_write(lp, "violated-cone.lp"); + //abort_if(rval, "LP_write failed"); + //UTIL_pause(); + +CLEANUP: + if (rmatind) free(rmatind); + if (rmatval) free(rmatval); + return rval; +} + +int GREEDY_ND_find_violated_cone(int nrows, + int nrays, + const double *f, + const double *rays, + const double *x, + const double *beta, + double epsilon, + int *rx, + double *sbar, + int *violated_found) +{ + log_verbose(" find_violated_cone\n"); + int rval = 0; + + struct LP lp; + + double initial_time = get_user_time(); + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = create_violated_cone_lp(nrows, nrays, f, rays, x, beta, epsilon, &lp); + abort_if(rval, "create_violated_cone_lp failed"); + + int infeasible; + rval = LP_optimize(&lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + lp_count++; + lp_time += get_user_time() - initial_time; + + violated_lp_count++; + violated_lp_time += get_user_time() - initial_time; + + rval = LP_get_x(&lp, sbar); + abort_if(rval, "LP_get_x failed"); + + for(int i=0; i 1e-9); + + if(rx[i]) + { + double m = min(epsilon, beta[i]); + const double *r = &rays[nrows * i]; + log_verbose(" r[%d]=%.8lf %.8lf\n", i, r[0], r[1]); + log_verbose(" r[%d]=%.8lf %.8lf\n", i, m*r[0], m*r[1]); + } + } + } + +CLEANUP: + LP_free(&lp); + return rval; +} diff --git a/infinity/library/src/greedy.c b/infinity/library/src/greedy.c new file mode 100644 index 0000000..fdb8500 --- /dev/null +++ b/infinity/library/src/greedy.c @@ -0,0 +1,478 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +struct SortPair +{ + int index; + void *data; +}; + +static int _qsort_cmp_rays_angle(const void *p1, + const void *p2) +{ + double *r1 = (double *) (((struct SortPair *) p1)->data); + double *r2 = (double *) (((struct SortPair *) p2)->data); + return sign(atan2(r1[0], r1[1]) - atan2(r2[0], r2[1])); +} + +static int sort_rays_angle(double *rays, + int nrays, + double *beta) +{ + int rval = 0; + + int *map = 0; + double *rays_copy = 0; + double *beta_copy = 0; + struct SortPair *pairs = 0; + + pairs = (struct SortPair *) malloc(nrays * sizeof(struct SortPair)); + rays_copy = (double *) malloc(2 * nrays * sizeof(double)); + beta_copy = (double*) malloc(nrays * sizeof(double)); + abort_if(!pairs, "could not allocate pairs"); + abort_if(!rays_copy, "could not allocate rays_copy"); + abort_if(!beta_copy, "could not allocate beta_copy"); + + memcpy(rays_copy, rays, 2 * nrays * sizeof(double)); + memcpy(beta_copy, beta, nrays * sizeof(double)); + + for (int i = 0; i < nrays; i++) + { + pairs[i].index = i; + pairs[i].data = &rays[2 * i]; + } + + qsort(pairs, nrays, sizeof(struct SortPair), _qsort_cmp_rays_angle); + + for (int i = 0; i < nrays; i++) + { + beta[i] = beta_copy[pairs[i].index]; + memcpy(&rays[2 * i], &rays_copy[2 * pairs[i].index], 2 * + sizeof(double)); + } + +CLEANUP: + if (pairs) free(pairs); + if (rays_copy) free(rays_copy); + if (beta_copy) free(beta_copy); + return rval; +} + + +static int scale_rays(double *rays, + int nrays, + double *scale) +{ + int rval = 0; + + for (int i = 0; i < nrays; i++) + { + rays[2*i] *= scale[i]; + rays[2*i+1] *= scale[i]; + } + +CLEANUP: + return rval; +} + + +static void print_row(const struct Row *row) +{ + time_printf("Row:\n"); + for (int i = 0; i < row->nz; i++) + time_printf(" %.4lfx%d\n", row->pi[i], row->indices[i]); + time_printf(" <= %.4lf [%d]\n", row->pi_zero, row->head); +} + +static void print_rays(const int *map, + const int *indices, + const double *f, + const double *rays, + const double *scale, + int nrays, + int nz, + int nrows) +{ + time_printf("Mapping:\n"); + for (int i = 0; i < nz; i++) + time_printf(" %4d: %4d x%-4d (scale=%.4lf)\n", i, map[i], indices[i], + scale[i]); + + time_printf("Origin:\n"); + for (int i = 0; i < nrows; i++) + time_printf(" %20.12lf\n", f[i]); + + time_printf("Rays:\n"); + for (int i = 0; i < nrays; i++) + { + time_printf(" "); + + for (int j = 0; j < nrows; j++) + printf("%20.12lf ", rays[i * nrows + j]); + + printf("angle=%.4lf ", atan2(rays[i * nrows], rays[i * nrows + 1])); + printf("norm=%.4lf ", fabs(rays[i * nrows]) + fabs(rays[i * nrows + 1])); + + printf("[ "); + for (int j = 0; j < nz; j++) + if (map[j] == i) printf("%d ", indices[j]); + printf("]\n"); + } +} + +static void print_cut(const struct Row *cut) +{ + time_printf("Generated cut:\n"); + for (int i = 0; i < cut->nz; i++) + time_printf(" %.4lfx%d\n", cut->pi[i], cut->indices[i]); + time_printf(" <= %.4lf\n", cut->pi_zero); +} + +int GREEDY_write_sage_file(int nrows, + int nrays, + const double *f, + const double *rays, + const double *beta, + const char *filename) +{ + int rval = 0; + + FILE *fsage = fopen(filename, "w"); + abort_iff(!fsage, "could not open %s", filename); + + fprintf(fsage, "f=vector(["); + for (int i = 0; i < nrows; i++) + fprintf(fsage, "%.20lf,", f[i]); + fprintf(fsage, "])\n"); + + fprintf(fsage, "R=matrix([\n"); + for (int i = 0; i < nrays; i++) + { + fprintf(fsage, " ["); + for (int j = 0; j < nrows; j++) + { + fprintf(fsage, "%.20lf,", rays[i * nrows + j]); + } + fprintf(fsage, "],\n"); + } + fprintf(fsage, "])\n"); + + fprintf(fsage, "pi=vector([\n"); + for (int k = 0; k < nrays; k++) + fprintf(fsage, " %.12lf,\n", 1 / beta[k]); + fprintf(fsage, "])\n"); + +CLEANUP: + fclose(fsage); + return rval; +} + +static int create_cut(const int nrows, + const char *column_types, + const int nrays, + const double *f, + const int lfree_nrays, + const double *lfree_rays, + const double *beta, + const double *extracted_rays, + const int nvars, + const int *variable_to_ray, + const int *indices, + const double *scale, + struct Row *cut) +{ + int rval = 0; + + cut->nz = nvars; + cut->pi = (double *) malloc(nvars * sizeof(double)); + cut->indices = (int *) malloc(nvars * sizeof(int)); + abort_if(!cut->pi, "could not allocate cut->pi"); + abort_if(!cut->indices, "could not allocate cut->indices"); + + struct LP lp; + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = GREEDY_create_psi_lp(nrows, lfree_nrays, f, lfree_rays, beta, &lp); + abort_if(rval, "create_psi_lp failed"); + + for (int i = 0; i < nvars; i++) + { + double value; + const double *q = &extracted_rays[variable_to_ray[i] * nrows]; + + if(ENABLE_LIFTING && column_types[indices[i]] == MILP_INTEGER) + { + rval = GREEDY_ND_pi(nrows, lfree_nrays, f, lfree_rays, beta, q, + scale[i], &lp, &value); + abort_if(rval, "GREEDY_ND_pi failed"); + } + else + { + rval = GREEDY_ND_psi(nrows, lfree_nrays, f, lfree_rays, beta, q, + scale[i], &lp, &value); + abort_if(rval, "GREEDY_ND_psi failed"); + } + + log_verbose(" psi[%4d] = %20.12lf %d\n", indices[i], value); + + value *= 1.0001; + value = DOUBLE_max(value, 0.0001); + + cut->indices[i] = indices[i]; + cut->pi[i] = - value; + } + + cut->pi_zero = -1.0; + +CLEANUP: + LP_free(&lp); + return rval; +} + + +#ifndef TEST_SOURCE +int GREEDY_generate_cut(int nrows, + struct Row **rows, + const char *column_types, + struct Row *cut) +{ + int rval = 0; + double *f = 0; + long max_nrays = 0; + + f = (double *) malloc(nrows * sizeof(double)); + abort_if(!f, "could not allocate f"); + + for (int i = 0; i < nrows; i++) + { + f[i] = frac(rows[i]->pi_zero); + if (DOUBLE_eq(f[i], 1.0)) f[i] = 0.0; + + max_nrays += rows[i]->nz; + } + + int nz; + int nrays; + int *variable_to_ray = 0; + int *indices = 0; + double *extracted_rays = 0; + double *scale = 0; + + double *beta = 0; + double *scaled_rays = 0; + + int lfree_nrays = 0; + double *lfree_rays = 0; + + variable_to_ray = (int *) malloc(max_nrays * sizeof(int)); + indices = (int *) malloc(max_nrays * sizeof(int)); + scale = (double *) malloc(max_nrays * sizeof(double)); + extracted_rays = (double *) malloc(nrows * max_nrays * sizeof(double)); + abort_if(!variable_to_ray, "could not allocate variable_to_ray"); + abort_if(!indices, "could not allocate indices"); + abort_if(!scale, "could not allocate scale"); + abort_if(!extracted_rays, "could not allocate extracted_rays"); + + lfree_rays = (double*) malloc(nrows * (max_nrays + 100) * sizeof(double)); + abort_if(!lfree_rays, "could not allocate lfree_rays"); + + rval = CG_extract_rays_from_rows(nrows, rows, extracted_rays, &nrays, + variable_to_ray, scale, indices, &nz); + abort_if(rval, "CG_extract_rays_from_rows failed"); + + for (double norm_cutoff = 0.00; norm_cutoff <= 5.0; norm_cutoff+= 0.1) + { + lfree_nrays = 0; + + for (int i = 0; i < nrays; i++) + { + int keep = 1; + double *r = &extracted_rays[nrows * i]; + + for (int j = 0; j < lfree_nrays; j++) + { + double *q = &extracted_rays[nrows * j]; + double norm = 0; + + for(int k = 0; k < nrows; k++) + norm += fabs(r[k] - q[k]); + + if(norm <= norm_cutoff) + { + keep = 0; + break; + } + } + + if(keep) + { + memcpy(&lfree_rays[nrows * lfree_nrays], r, nrows * sizeof(double)); + lfree_nrays++; + } + } + + log_debug(" norm_cutoff=%8.2lf nrays=%8d\n", norm_cutoff, lfree_nrays); + + if(lfree_nrays < MAX_N_RAYS) break; + } + + if(ENABLE_LIFTING) + { + abort_if(nrows > 3, "not implemented"); + + int n_extra_rays; + double extra_rays[100]; + + if(nrows == 2) + { + extra_rays[0] = 0.0001; + extra_rays[1] = 0.0000; + + extra_rays[2] = -0.0001; + extra_rays[3] = 0.0001; + + extra_rays[4] = -0.0001; + extra_rays[5] = -0.0001; + + n_extra_rays = 3; + } + else if(nrows == 3) + { + extra_rays[0] = 0.0000; + extra_rays[1] = 0.0000; + extra_rays[2] = 0.0001; + + extra_rays[3] = 0.0001; + extra_rays[4] = 0.0000; + extra_rays[5] = -0.0001; + + extra_rays[6] = -0.0001; + extra_rays[7] = 0.0000; + extra_rays[8] = -0.0001; + + extra_rays[ 9] = -0.0001; + extra_rays[10] = -0.0001; + extra_rays[11] = -0.0001; + + n_extra_rays = 4; + } + + + for(int i = 0; i < n_extra_rays; i++) + { + double *r = &extra_rays[nrows * i]; + + double scale; + int found, index; + + rval = CG_find_ray(nrows, lfree_rays, lfree_nrays, r, &found, + &scale, &index); + abort_if(rval, "CG_find_ray failed"); + + if(!found) { + memcpy(&lfree_rays[nrows * lfree_nrays], r, nrows * + sizeof(double)); + lfree_nrays++; + } + } + } + + + if(lfree_nrays < 3) + { + rval = ERR_NO_CUT; + cut->pi = 0; + cut->indices = 0; + goto CLEANUP; + } + + log_debug("Extracted %d rays\n", lfree_nrays); + if_verbose_level + { + print_rays(variable_to_ray, indices, f, extracted_rays, scale, nrays, + nz, nrows); + } + + beta = (double *) malloc((lfree_nrays) * sizeof(double)); + abort_if(!beta, "could not allocate beta"); + + log_verbose("Computing lattice-free set...\n"); + + if(nrows == 2) + { + rval = sort_rays_angle(lfree_rays, lfree_nrays, beta); + abort_if(rval, "sort_rays_angle failed"); + + rval = GREEDY_2D_generate_cut(lfree_rays, lfree_nrays, f, beta); + if(rval) + { + rval = ERR_NO_CUT; + goto CLEANUP; + } + } + else + { + rval = GREEDY_ND_generate_cut(nrows, lfree_nrays, f, lfree_rays, beta); + if(rval) + { + rval = ERR_NO_CUT; + goto CLEANUP; + } + } + + + if(DUMP_CUT) + { + char filename[100]; + sprintf(filename, "cut-%03d.sage", DUMP_CUT_N++); + + time_printf("Writing %s...\n", filename); + rval = GREEDY_write_sage_file(nrows, lfree_nrays, f, lfree_rays, beta, + filename); + abort_if(rval, "GREEDY_write_sage_file failed"); + } + + rval = create_cut(nrows, column_types, nrays, f, lfree_nrays, lfree_rays, + beta, extracted_rays, nz, variable_to_ray, indices, scale, cut); + abort_if(rval, "create_cut failed"); + + if_verbose_level print_cut(cut); + +CLEANUP: + if (f) free(f); + if (variable_to_ray) free(variable_to_ray); + if (beta) free(beta); + if (indices) free(indices); + if (scale) free(scale); + if (scaled_rays) free(scaled_rays); + if (lfree_rays) free(lfree_rays); + return rval; +} +#endif // TEST_SOURCE diff --git a/infinity/library/tests/greedy-2d-test.cpp b/infinity/library/tests/greedy-2d-test.cpp new file mode 100644 index 0000000..05ce119 --- /dev/null +++ b/infinity/library/tests/greedy-2d-test.cpp @@ -0,0 +1,616 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#define TEST_SOURCE + +extern "C" { +#include +#include +#include "../src/greedy-2d.c" +} + +#define BOUNDS_EPSILON 0.01 + +TEST(Greedy2DTest, test_generate_cut_1) +{ + int rval = 0; + double bounds[100]; + double f[] = {1 / 4.0, 3 / 4.0}; + double rays[] = {-2 / 5.0, 5 / 7.0, 0.0, 1.0, 1.0, 1.0, 4 / 5.0, -2 / 3.0, + -1.0, 0.0}; + + rval = GREEDY_2D_generate_cut(rays, 5, f, bounds); + abort_if(rval, "GREEDY_2D_generate_cut failed"); + + EXPECT_NEAR(23 / 50.0, bounds[0], BOUNDS_EPSILON); + EXPECT_NEAR(23 / 42.0, bounds[1], BOUNDS_EPSILON); + EXPECT_NEAR(9 / 11.0, bounds[2], BOUNDS_EPSILON); + EXPECT_NEAR(9 / 11.0, bounds[3], BOUNDS_EPSILON); + EXPECT_NEAR(23 / 50.0, bounds[4], BOUNDS_EPSILON); + + CLEANUP: + if (rval) FAIL(); +} + +TEST(Greedy2DTest, test_generate_cut_2) +{ + int rval = 0; + double bounds[100]; + double f[] = {1 / 2.0, 1 / 2.0}; + double rays[] = { + -1.0, -1.0, + -1.0, 1.0, + 1.0, 1.0, + 1.0, 0.0, + 1.0, -1.0 + }; + + rval = GREEDY_2D_generate_cut(rays, 5, f, bounds); + abort_if(rval, "GREEDY_2D_generate_cut failed"); + + EXPECT_NEAR(0.5, bounds[0], BOUNDS_EPSILON); + EXPECT_NEAR(0.5, bounds[1], BOUNDS_EPSILON); + EXPECT_NEAR(0.5, bounds[2], BOUNDS_EPSILON); + EXPECT_EQ(GREEDY_BIG_E, bounds[3]); + EXPECT_NEAR(0.5, bounds[4], BOUNDS_EPSILON); + + CLEANUP: + if (rval) FAIL(); +} + +TEST(Greedy2DTest, test_generate_cut_3) +{ + int rval = 0; + double bounds[100]; + double f[] = {5 / 22.0, 0.0}; + double rays[] = {-1 / 22.0, 0.0, 0.0, 1 / 18.0, 1 / 22.0, 0.0}; + + rval = GREEDY_2D_generate_cut(rays, 3, f, bounds); + abort_if(rval, "GREEDY_2D_generate_cut failed"); + + EXPECT_NEAR(5.0, bounds[0], BOUNDS_EPSILON); + EXPECT_NEAR(17.0, bounds[2], BOUNDS_EPSILON); + EXPECT_EQ(GREEDY_BIG_E, bounds[1]); + + CLEANUP: + if (rval) FAIL(); +} + +TEST(Greedy2DTest, scale_to_chull_test) +{ + int rval = 0; + + double rays[] = { + 0, 1, + 1, 1, + 0.5, 0.25, + 1, 0, + -1, -1, + -0.25, 0 + }; + + double scale[6]; + int nrays = 6; + + rval = scale_to_chull(rays, nrays, scale); + abort_if(rval, "scale_to_chull failed"); + + EXPECT_NEAR(rays[0], 0.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[1], 1.0, BOUNDS_EPSILON); + + EXPECT_NEAR(rays[2], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[3], 1.0, BOUNDS_EPSILON); + + EXPECT_NEAR(rays[4], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[5], 0.5, BOUNDS_EPSILON); + + EXPECT_NEAR(rays[6], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[7], 0.0, BOUNDS_EPSILON); + + EXPECT_NEAR(rays[8],-1.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[9],-1.0, BOUNDS_EPSILON); + + EXPECT_NEAR(rays[10],-0.5, BOUNDS_EPSILON); + EXPECT_NEAR(rays[11], 0.0, BOUNDS_EPSILON); + + EXPECT_NEAR(scale[0], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(scale[1], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(scale[2], 2.0, BOUNDS_EPSILON); + EXPECT_NEAR(scale[3], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(scale[4], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(scale[5], 2.0, BOUNDS_EPSILON); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Greedy2DTest, scale_to_chull_test_2) +{ + int rval = 0; + + double rays[] = { + 1, 1, + 0.5, 0.25, + 1, 0, + }; + + double scale[3]; + int nrays = 3; + + rval = scale_to_chull(rays, nrays, scale); + abort_if(rval, "scale_to_chull failed"); + + EXPECT_NEAR(rays[0], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[1], 1.0, BOUNDS_EPSILON); + + EXPECT_NEAR(rays[2], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[3], 0.5, BOUNDS_EPSILON); + + EXPECT_NEAR(rays[4], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(rays[5], 0.0, BOUNDS_EPSILON); + + EXPECT_NEAR(scale[0], 1.0, BOUNDS_EPSILON); + EXPECT_NEAR(scale[1], 2.0, BOUNDS_EPSILON); + EXPECT_NEAR(scale[2], 1.0, BOUNDS_EPSILON); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(Greedy2DTest, find_containing_cone_test) +{ + int rval = 0; + + double rays[] = {-1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, -1.0, -1.0, -1.0}; + double p1[] = {-1.0, 1.0}; + double p2[] = {0.0, 1.0}; + double p3[] = {1.0, 0.0}; + double p4[] = {-1.0, 0.0}; + int index1, index2; + double lambda1, lambda2; + + rval = find_containing_cone(rays, 5, p1, &index1, &index2, &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + + EXPECT_EQ(0, index1); + EXPECT_EQ(1, index2); + + rval = find_containing_cone(rays, 5, p2, &index1, &index2, &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + + EXPECT_EQ(0, index1); + EXPECT_EQ(1, index2); + + rval = find_containing_cone(rays, 5, p3, &index1, &index2, &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + + EXPECT_EQ(1, index1); + EXPECT_EQ(2, index2); + + rval = find_containing_cone(rays, 5, p4, &index1, &index2, &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + + EXPECT_EQ(4, index1); + EXPECT_EQ(0, index2); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(Greedy2DTest, find_containing_cone_test_2) +{ + int rval = 0; + + double rays[] = {0.0, 0.5, 1.0, 0.0, -0.5, 0.5}; + double p[] = {0.5, -0.5}; + int index1, index2; + double lambda1, lambda2; + + rval = find_containing_cone(rays, 3, p, &index1, &index2, &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + + EXPECT_EQ(-1, index1); + EXPECT_EQ(-1, index2); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(Greedy2DTest, find_containing_cone_test_3) +{ + int rval = 0; + + double rays[] = { + -1, -0.1, + 1, -0.1, + 1, -0.5, + -1, -0.5, + }; + + double p[] = { 0, -2 }; + int index1, index2; + double lambda1, lambda2; + + rval = find_containing_cone(rays, 4, p, &index1, &index2, &lambda1, &lambda2); + abort_if(rval, "find_containing_cone failed"); + + EXPECT_EQ(2, index1); + EXPECT_EQ(3, index2); + + CLEANUP: + if(rval) FAIL(); +} + +//TEST(Greedy2DTest, test_generate_cut_4) +//{ +// int rval = 0; +// double bounds[100]; +// double f[] = {5 / 22.0, 10 / 19.0}; +// double rays[] = {0, -1 / 38.0, -1 / 22.0, -1 / 38.0, 0, 1 / 38.0, -1 / 22.0, +// 0, 1 / 22.0, 0, 1 / 22.0, 1 / 38.0}; +// +// rval = GREEDY_2D_generate_cut(rays, 6, f, bounds); +// abort_if(rval, "GREEDY_2D_generate_cut failed"); +// +// EXPECT_NEAR(20.0, bounds[0], BOUNDS_EPSILON); +// EXPECT_NEAR(20.0, bounds[1], BOUNDS_EPSILON); +// EXPECT_NEAR(18.0, bounds[2], BOUNDS_EPSILON); +// EXPECT_NEAR(18.0, bounds[5], BOUNDS_EPSILON); +// EXPECT_EQ(GREEDY_BIG_E, bounds[3]); +// EXPECT_EQ(GREEDY_BIG_E, bounds[4]); +// +// CLEANUP: +// if (rval) FAIL(); +//} +// +//TEST(Greedy2DTest, test_generate_cut_5) +//{ +// int rval = 0; +// double bounds[100]; +// double f[] = {0.22727272727272729291, 0.52631578947368418131}; +// double rays[] = {0.00000000000000000000, -0.02631578947368420907, +// -0.04545454545454545581, -0.02631578947368420907, +// 0.00000000000000000000, 0.02631578947368420907, +// -0.04545454545454545581, 0.00000000000000000000, +// 0.04545454545454545581, 0.00000000000000000000, +// 0.04545454545454545581, 0.02631578947368420907}; +// +// rval = GREEDY_2D_generate_cut(rays, 6, f, bounds); +// abort_if(rval, "GREEDY_2D_generate_cut failed"); +// +// EXPECT_NEAR(20.0, bounds[0], BOUNDS_EPSILON); +// EXPECT_NEAR(20.0, bounds[1], BOUNDS_EPSILON); +// EXPECT_NEAR(18.0, bounds[2], BOUNDS_EPSILON); +// EXPECT_NEAR(18.0, bounds[5], BOUNDS_EPSILON); +// EXPECT_EQ(GREEDY_BIG_E, bounds[3]); +// EXPECT_EQ(GREEDY_BIG_E, bounds[4]); +// +// CLEANUP: +// if (rval) FAIL(); +//} +// +// +// +//TEST(Greedy2DTest, get_peak_ray_test_1) +//{ +// int rval = 0; +// +// double rays[] = {-2.0, 0.0, -1.0, 1.0, 1.0, 1.0, 2.0, 0.0, 1.0, -1.0, -1.0, -1.0 }; +// int nrays = 6; +// +// double normal1[] = { 0.0, 1.0 }; +// double normal2[] = { 1.0, 1.0 }; +// double normal3[] = { 0.0, -1.0 }; +// double normal4[] = {-1.0, -1.0 }; +// int index; +// +// rval = get_peak_ray(rays, nrays, normal1, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_TRUE(index == 1 || index == 2); +// +// rval = get_peak_ray(rays, nrays, normal3, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_TRUE(index == 4 || index == 5); +// +// rval = get_peak_ray(rays, nrays, normal2, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(2, index); +// +// rval = get_peak_ray(rays, nrays, normal4, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(5, index); +// +// CLEANUP: +// if(rval) FAIL(); +//} +// +//TEST(Greedy2DTest, get_peak_ray_test_2) +//{ +// int rval = 0; +// +// double rays[] = { -1e100, 0, 0, 1, 1e100, 0, 1, -1, -1,-1 }; +// int nrays = 5; +// +// double normal1[] = { 0.0, 1.0 }; +// double normal2[] = { 1.0, 1.0 }; +// double normal3[] = { 1.0, 0.0 }; +// double normal4[] = { 0.0, -1.0 }; +// double normal5[] = { -1.0, -1.0 }; +// double normal6[] = { -1.0, 0.0 }; +// int index; +// +// rval = get_peak_ray(rays, nrays, normal1, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(1, index); +// +// rval = get_peak_ray(rays, nrays, normal4, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(3, index); +// +// rval = get_peak_ray(rays, nrays, normal2, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(2, index); +// +// rval = get_peak_ray(rays, nrays, normal5, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(0, index); +// +// rval = get_peak_ray(rays, nrays, normal3, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(2, index); +// +// rval = get_peak_ray(rays, nrays, normal6, &index); +// abort_if(rval, "get_peak_ray failed"); +// EXPECT_EQ(0, index); +// +// CLEANUP: +// if(rval) FAIL(); +//} +// +//TEST(Greedy2DTest, get_peak_ray_test_3) +//{ +// int rval = 0; +// +// double rays[] = { -1, 1, 0, 1, 1, 1}; +// int nrays = 3; +// +// double normal1[] = { 0.0, -1.0 }; +// int index; +// +// rval = get_peak_ray(rays, nrays, normal1, &index); +// EXPECT_EQ(ERR_NOT_FOUND, rval); +// rval = 0; +// +// CLEANUP: +// if(rval) FAIL(); +//} +// +//TEST(Greedy2DTest, nearest_lattice_point_test_1) +//{ +// int rval = 0; +// +// double z0a[] = {100, 1}; +// double z0b[] = {-100, 1}; +// double g[] = {-1, 0}; +// +// double x1[] = {-1.34, 1}; +// double x2[] = {-3, 1}; +// +// double z1[2]; +// double z2[2]; +// +// rval = nearest_lattice_points(g, z0a, x1, z1, z2); +// abort_if(rval, "nearest_lattice_points failed"); +// EXPECT_DOUBLE_EQ(-1, z1[0]); +// EXPECT_DOUBLE_EQ(1, z1[1]); +// EXPECT_DOUBLE_EQ(-2, z2[0]); +// EXPECT_DOUBLE_EQ(1, z2[1]); +// +// rval = nearest_lattice_points(g, z0b, x1, z1, z2); +// abort_if(rval, "nearest_lattice_points failed"); +// EXPECT_DOUBLE_EQ(-1, z1[0]); +// EXPECT_DOUBLE_EQ(1, z1[1]); +// EXPECT_DOUBLE_EQ(-2, z2[0]); +// EXPECT_DOUBLE_EQ(1, z2[1]); +// +// rval = nearest_lattice_points(g, z0a, x2, z1, z2); +// abort_if(rval, "nearest_lattice_points failed"); +// EXPECT_DOUBLE_EQ(-3, z1[0]); +// EXPECT_DOUBLE_EQ(1, z1[1]); +// EXPECT_DOUBLE_EQ(-3, z2[0]); +// EXPECT_DOUBLE_EQ(1, z2[1]); +// +// CLEANUP: +// if(rval) FAIL(); +//} +// +//TEST(Greedy2DTest, nearest_lattice_point_test_2) +//{ +// int rval = 0; +// +// double z0[] = {1, 1}; +// double g[] = {1, 2}; +// double x[] = {3.35, 5.7}; +// +// double z1[2]; +// double z2[2]; +// +// rval = nearest_lattice_points(g, z0, x, z1, z2); +// abort_if(rval, "nearest_lattice_points failed"); +// EXPECT_DOUBLE_EQ(3, z1[0]); +// EXPECT_DOUBLE_EQ(5, z1[1]); +// +// CLEANUP: +// if(rval) FAIL(); +//} +// +//TEST(Greedy2DTest, find_normal_test) +//{ +// int rval = 0; +// +// double d1[] = {1.0, 0.0}; +// double d2[] = {1.0, 1.0}; +// +// double x1[] = {1.0, 1.0}; +// double x2[] = {1.0, -1.0}; +// +// double n[2]; +// +// rval = find_normal(d1, x1, n); +// abort_if(rval, "find_normal failed"); +// EXPECT_DOUBLE_EQ(0, n[0]); +// EXPECT_DOUBLE_EQ(1, n[1]); +// +// rval = find_normal(d1, x2, n); +// abort_if(rval, "find_normal failed"); +// EXPECT_DOUBLE_EQ(0, n[0]); +// EXPECT_DOUBLE_EQ(-1, n[1]); +// +// rval = find_normal(d2, x1, n); +// abort_if(rval, "find_normal failed"); +// EXPECT_DOUBLE_EQ(0, n[0]); +// EXPECT_DOUBLE_EQ(0, n[1]); +// +// rval = find_normal(d2, x2, n); +// abort_if(rval, "find_normal failed"); +// EXPECT_DOUBLE_EQ(1, n[0]); +// EXPECT_DOUBLE_EQ(-1, n[1]); +// +// CLEANUP: +// if(rval) FAIL(); +//} +// +//TEST(Greedy2DTest, check_rays_parallel) +//{ +// double r1[] = {1.0, 2.0}; +// double r2[] = {2.0, 4.0}; +// double r3[] = {-2.0, -4.0}; +// double r4[] = {0.0, 0.0}; +// +// int match; +// double scale; +// +// check_rays_parallel(r1, r2, &match, &scale); +// EXPECT_TRUE(match); +// EXPECT_DOUBLE_EQ(scale, 0.5); +// +// check_rays_parallel(r1, r1, &match, &scale); +// EXPECT_TRUE(match); +// EXPECT_DOUBLE_EQ(scale, 1.0); +// +// check_rays_parallel(r2, r3, &match, &scale); +// EXPECT_FALSE(match); +// +// check_rays_parallel(r1, r4, &match, &scale); +// EXPECT_FALSE(match); +//} +// +//TEST(Greedy2DTest, find_ray) +//{ +// double rays[] = {1.0, 2.0, -1.0, 0.0, -5.0, -5.0}; +// int nrays = 3; +// +// double r1[] = {1.0, 2.0}; +// double r2[] = {-2.0, 0.0}; +// double r3[] = {-1.0, -1.0}; +// double r4[] = {7.0, 1.0}; +// double r5[] = {-1.0, -2.0}; +// +// int found, index; +// double scale; +// +// find_ray(rays, nrays, r1, &found, &scale, &index); +// EXPECT_TRUE(found); +// EXPECT_EQ(index, 0); +// EXPECT_DOUBLE_EQ(scale, 1.0); +// +// find_ray(rays, nrays, r2, &found, &scale, &index); +// EXPECT_TRUE(found); +// EXPECT_EQ(index, 1); +// EXPECT_DOUBLE_EQ(scale, 2.0); +// +// find_ray(rays, nrays, r3, &found, &scale, &index); +// EXPECT_TRUE(found); +// EXPECT_EQ(index, 2); +// EXPECT_DOUBLE_EQ(scale, 1 / 5.0); +// +// find_ray(rays, nrays, r4, &found, &scale, &index); +// EXPECT_FALSE(found); +// +// find_ray(rays, nrays, r5, &found, &scale, &index); +// EXPECT_FALSE(found); +//} +// +//TEST(Greedy2DTest, extract_rays_from_two_sparse_rows_test) +//{ +// int rval = 0; +// +// char ctypes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +// double pi1[] = {-3.0, 1.0, -2.0, -1.5, -3.0}; +// int indices1[] = {2, 3, 6, 7, 9}; +// +// double pi2[] = {1.0, -2.0, -3.0, -1.0}; +// int indices2[] = {0, 2, 5, 7}; +// +// struct Row row1 = {.nz = 5, .head = 3, .pi_zero = 0, .pi = pi1, .indices = indices1}; +// struct Row row2 = {.nz = 4, .head = 0, .pi_zero = 0, .pi = pi2, .indices = indices2}; +// +// int nrays; +// int nz; +// +// double rays[100]; +// int indices[100]; +// double ray_scale[100]; +// int variable_to_ray[100]; +// +// rval = extract_rays_from_two_sparse_rows(&row1, &row2, ctypes, rays, &nrays, +// variable_to_ray, ray_scale, indices, &nz); +// abort_if(rval, "extract_rays_from_two_sparse_rows failed"); +// +// EXPECT_EQ(nrays, 3); +// EXPECT_EQ(nz, 5); +// +// EXPECT_DOUBLE_EQ(rays[0], 3.0); +// EXPECT_DOUBLE_EQ(rays[1], 2.0); +// EXPECT_DOUBLE_EQ(rays[2], 0.0); +// EXPECT_DOUBLE_EQ(rays[3], 3.0); +// EXPECT_DOUBLE_EQ(rays[4], 2.0); +// EXPECT_DOUBLE_EQ(rays[5], 0.0); +// +// EXPECT_EQ(indices[0], 2); +// EXPECT_EQ(indices[1], 5); +// EXPECT_EQ(indices[2], 6); +// EXPECT_EQ(indices[3], 7); +// EXPECT_EQ(indices[4], 9); +// +// EXPECT_EQ(variable_to_ray[0], 0); +// EXPECT_EQ(variable_to_ray[1], 1); +// EXPECT_EQ(variable_to_ray[2], 2); +// EXPECT_EQ(variable_to_ray[3], 0); +// EXPECT_EQ(variable_to_ray[4], 2); +// +// EXPECT_DOUBLE_EQ(ray_scale[0], 1.0); +// EXPECT_DOUBLE_EQ(ray_scale[1], 1.0); +// EXPECT_DOUBLE_EQ(ray_scale[2], 1.0); +// EXPECT_DOUBLE_EQ(ray_scale[3], 0.5); +// EXPECT_DOUBLE_EQ(ray_scale[4], 1.5); +// +// CLEANUP: +// if (rval) FAIL(); +//} +// diff --git a/infinity/library/tests/greedy-nd-test.cpp b/infinity/library/tests/greedy-nd-test.cpp new file mode 100644 index 0000000..41b8fa2 --- /dev/null +++ b/infinity/library/tests/greedy-nd-test.cpp @@ -0,0 +1,457 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +extern "C" { +#include +#include +#include +#include +} + +int ENABLE_LIFTING = 0; +int MIN_N_ROWS = 2; +int MAX_N_ROWS = 2; +int DUMP_CUT = 0; +int DUMP_CUT_N = 0; + +TEST(GreedyNDTest, find_violated_cone_test) +{ + int rval = 0; + + int nrows = 2; + int nrays = 4; + double f[] = { 0.5, 0.5 }; + double rays[] = + { + -1.0, 1.0, + 1.0, 1.0, + 1.0, -1.0, + -1.0, -1.0 + }; + + double x[] = { 1.0, 0.5 }; + double beta[] = { INFINITY, INFINITY, INFINITY, INFINITY }; + + double sbar[nrays]; + int rx[nrays]; + int found; + + rval = GREEDY_ND_find_violated_cone(nrows, nrays, f, rays, x, beta, + 1.0, rx, sbar, &found); + abort_if(rval, "GREEDY_ND_find_violated_cone failed"); + + EXPECT_TRUE(found); + EXPECT_FALSE(rx[0]); + EXPECT_TRUE(rx[1]); + EXPECT_TRUE(rx[2]); + EXPECT_FALSE(rx[3]); + + rval = GREEDY_ND_find_violated_cone(nrows, nrays, f, rays, x, beta, + 0.5, rx, sbar, &found); + abort_if(rval, "GREEDY_ND_find_violated_cone failed"); + + EXPECT_FALSE(found); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, find_tight_rays_test_1) +{ + int rval = 0; + + double f[] = { 0.5, 0.5 }; + double rays[] = + { + 1.0, 1.0, + 1.0, -1.0, + -1.0, -1.0, + -1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0 + }; + double beta[] = { INFINITY, INFINITY, INFINITY, INFINITY, INFINITY, + INFINITY }; + double epsilon = 0.5; + double x[] = { 1.0, 1.0 }; + + int tx[6]; + + rval = GREEDY_ND_find_tight_rays(2, 6, f, rays, x, beta, epsilon, tx); + abort_if(rval, "GREEDY_ND_find_tight_rays failed"); + + EXPECT_TRUE(tx[0]); + EXPECT_FALSE(tx[1]); + EXPECT_FALSE(tx[2]); + EXPECT_FALSE(tx[3]); + EXPECT_FALSE(tx[4]); + EXPECT_FALSE(tx[5]); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, find_tight_rays_test_2) +{ + int rval = 0; + + double f[] = { 0.5, 0.5 }; + double rays[] = + { + 1.0, 1.0, + 1.0, -1.0, + -1.0, -1.0, + -1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0 + }; + double beta[] = { 0.5, 0.5, 0.5, 0.5, INFINITY, INFINITY }; + double epsilon = 1.0; + double x[] = { 1.0, 1.0 }; + + int tx[6]; + + rval = GREEDY_ND_find_tight_rays(2, 6, f, rays, x, beta, epsilon, tx); + abort_if(rval, "GREEDY_ND_find_tight_rays failed"); + + EXPECT_TRUE(tx[0]); + EXPECT_FALSE(tx[1]); + EXPECT_FALSE(tx[2]); + EXPECT_FALSE(tx[3]); + EXPECT_TRUE(tx[4]); + EXPECT_TRUE(tx[5]); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, cone_bound_test_1) +{ + int rval = 0; + + double f[] = { 0.5, 0.5 }; + double rays[] = + { + 1.0, 1.0, + 1.0, -1.0, + -1.0, -1.0, + -1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0 + }; + double beta[] = { INFINITY, INFINITY, INFINITY, INFINITY, INFINITY, + INFINITY }; + + double x[] = { 1.0, 1.0 }; + int rx1[] = { 1, 0, 0, 0, 0, 0 }; + int rx2[] = { 0, 0, 0, 0, 1, 1 }; + + double epsilon; + + rval = GREEDY_ND_cone_bound(2, 6, f, rays, rx1, x, beta, &epsilon); + abort_if(rval, "GREEDY_ND_cone_bound failed"); + EXPECT_NEAR(0.5, epsilon, 1e-6); + + rval = GREEDY_ND_cone_bound(2, 6, f, rays, rx2, x, beta, &epsilon); + abort_if(rval, "GREEDY_ND_cone_bound failed"); + EXPECT_NEAR(1.0, epsilon, 1e-6); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, cone_bound_test_2) +{ + int rval = 0; + + double f[] = { 0.0, 0.0 }; + double rays[] = + { + 0.0, 1.0, + 1.0, 0.0 + }; + int rx[] = { 1, 1 }; + + double beta1[] = { 100, 100 }; + double beta2[] = { 0.5, 100 }; + double beta3[] = { 0.5, 1.0 }; + + double x1[] = { 0.5, 0.5 }; + double x2[] = { 0.5, 0.25 }; + + double epsilon; + + rval = GREEDY_ND_cone_bound(2, 2, f, rays, rx, x1, beta1, &epsilon); + abort_if(rval, "GREEDY_ND_cone_bound failed"); + EXPECT_NEAR(1.0, epsilon, 1e-6); + + rval = GREEDY_ND_cone_bound(2, 2, f, rays, rx, x1, beta2, &epsilon); + abort_if(rval, "GREEDY_ND_cone_bound failed"); + EXPECT_EQ(INFINITY, epsilon); + + rval = GREEDY_ND_cone_bound(2, 2, f, rays, rx, x2, beta2, &epsilon); + abort_if(rval, "GREEDY_ND_cone_bound failed"); + EXPECT_NEAR(1.0, epsilon, 1e-6); + + rval = GREEDY_ND_cone_bound(2, 2, f, rays, rx, x2, beta3, &epsilon); + abort_if(rval, "GREEDY_ND_cone_bound failed"); + EXPECT_EQ(INFINITY, epsilon); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, bound_test_1) +{ + int rval = 0; + + double f[] = { 0.5, 0.5 }; + double rays[] = + { + 1.0, 1.0, + 1.0, -1.0, + -1.0, -1.0, + -1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0 + }; + double beta1[] = { INFINITY, INFINITY, INFINITY, INFINITY, INFINITY, INFINITY }; + double beta2[] = { 0.5, 0.5, 0.5, 0.5, INFINITY, INFINITY }; + double beta3[] = { 0.5, 0.5, 0.5, 0.5, 1.0, 1.0 }; + double x[] = { 1.0, 1.0 }; + + double epsilon; + int tx[6]; + + rval = GREEDY_ND_bound(2, 6, f, rays, x, beta1, &epsilon, tx); + abort_if(rval, "GREEDY_ND_bound failed"); + EXPECT_NEAR(epsilon, 0.5, 1e-6); + EXPECT_TRUE(tx[0]); + EXPECT_FALSE(tx[1]); + EXPECT_FALSE(tx[2]); + EXPECT_FALSE(tx[3]); + EXPECT_FALSE(tx[4]); + EXPECT_FALSE(tx[5]); + + rval = GREEDY_ND_bound(2, 6, f, rays, x, beta2, &epsilon, tx); + abort_if(rval, "GREEDY_ND_bound failed"); + EXPECT_NEAR(epsilon, 1.0, 1e-6); + EXPECT_TRUE(tx[0]); + EXPECT_FALSE(tx[1]); + EXPECT_FALSE(tx[2]); + EXPECT_FALSE(tx[3]); + EXPECT_TRUE(tx[4]); + EXPECT_TRUE(tx[5]); + + rval = GREEDY_ND_bound(2, 6, f, rays, x, beta3, &epsilon, tx); + abort_if(rval, "GREEDY_ND_bound failed"); + EXPECT_EQ(epsilon, INFINITY); + EXPECT_FALSE(tx[0]); + EXPECT_FALSE(tx[1]); + EXPECT_FALSE(tx[2]); + EXPECT_FALSE(tx[3]); + EXPECT_FALSE(tx[4]); + EXPECT_FALSE(tx[5]); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, psi_test) +{ + int rval = 0; + + double f[] = { 0.5, 0.5 }; + double rays[] = + { + 1.0, 1.0, + 1.0, -1.0, + -1.0, -1.0, + -1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0 + }; + double beta[] = { 0.5, 0.5, 0.5, 0.5, 1.0, 1.0 }; + + double q1[] = { 1.0, 1.0 }; + double q2[] = { -2.0, 0.0 }; + + double value; + struct LP lp; + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = GREEDY_create_psi_lp(2, 6, f, rays, beta, &lp); + abort_if(rval, "GREEDY_create_psi_lp failed"); + + rval = GREEDY_ND_psi(2, 6, f, rays, beta, q1, 1.0, &lp, &value); + abort_if(rval, "GREDDY_ND_psi failed"); + EXPECT_NEAR(value, 2.0, 1e-6); + + rval = GREEDY_ND_psi(2, 6, f, rays, beta, q2, 2.0, &lp, &value); + abort_if(rval, "GREDDY_ND_psi failed"); + EXPECT_NEAR(value, 8.0, 1e-6); + +CLEANUP: + LP_free(&lp); + if(rval) FAIL(); +} + +TEST(GreedyNDTest, psi_test_2) +{ + int rval = 0; + + double f[] = { 0.5, 0.5, 0.5 }; + double rays[] = + { + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, -0.5, + -0.5, 0.5, 0.5, + 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, -0.5, + 0.5, 0.5, 0.5, + + }; + + double beta[] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; + + double q1[] = { 0.5, 0.5, 0.5 }; + double q2[] = { 1, 0, 0 }; + + double value; + struct LP lp; + + rval = LP_open(&lp); + abort_if(rval, "LP_open failed"); + + rval = GREEDY_create_psi_lp(3, 8, f, rays, beta, &lp); + abort_if(rval, "GREEDY_create_psi_lp failed"); + + rval = GREEDY_ND_psi(3, 8, f, rays, beta, q1, 1.0, &lp, &value); + abort_if(rval, "GREDDY_ND_psi failed"); + EXPECT_NEAR(value, 1.0, 1e-6); + + rval = GREEDY_ND_psi(3, 8, f, rays, beta, q2, 1.0, &lp, &value); + abort_if(rval, "GREDDY_ND_psi failed"); + EXPECT_NEAR(value, 2.0, 1e-6); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, generate_cut_test_1) +{ + int rval = 0; + + double f[] = { 0.5, 0.5 }; + double rays[] = + { + 1.0, 1.0, + 1.0, -1.0, + -1.0, -1.0, + -1.0, 1.0, + 0.0, 1.0, + 1.0, 0.0 + }; + double beta[6]; + + rval = GREEDY_ND_generate_cut(2, 6, f, rays, beta); + abort_if(rval, "GREEDY_ND_generate_cut failed"); + + EXPECT_NEAR(beta[0], 0.5, 1e-6); + EXPECT_NEAR(beta[1], 0.5, 1e-6); + EXPECT_NEAR(beta[2], 0.5, 1e-6); + EXPECT_NEAR(beta[3], 0.5, 1e-6); + EXPECT_NEAR(beta[4], 1.0, 1e-6); + EXPECT_NEAR(beta[5], 1.0, 1e-6); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNDTest, generate_cut_test_2) +{ + int rval = 0; + + double f[] = { 0.75, 0.75, 0.75}; + double rays[] = + { + 1.0, 0.0, 0.0, + -1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, -1.0, 0.0, + 0.0, 0.0, 1.0, + 0.0, 0.0, -1.0 + }; + double beta[6]; + + rval = GREEDY_ND_generate_cut(3, 6, f, rays, beta); + abort_if(rval, "GREEDY_ND_generate_cut failed"); + + EXPECT_NEAR(beta[0], 0.75, 1e-6); + EXPECT_NEAR(beta[1], 2.25, 1e-6); + EXPECT_NEAR(beta[2], 0.75, 1e-6); + EXPECT_NEAR(beta[3], 2.25, 1e-6); + EXPECT_NEAR(beta[4], 0.75, 1e-6); + EXPECT_NEAR(beta[5], 2.25, 1e-6); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(GreedyNTest, scale_to_ahull_test) +{ + int rval = 0; + + double rays[] = + { + 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, + 1.0, 0.0, 0.0, + -1.0, 0.0, 0.0 + }; + + int rx[] = { 1, 1, 1 , 0 }; + double beta[] = { INFINITY, INFINITY, INFINITY, INFINITY }; + double epsilon = 1.0; + + double d1[] = { 1.0, 1.0, 1.0 }; + double d2[] = { 2.0, 2.0, 0.0 }; + double d3[] = { -1.0, -1.0, -1.0 }; + + double alpha; + + rval = GREEDY_ND_scale_to_ahull(3, 4, rays, rx, beta, epsilon, d1, &alpha); + abort_if(rval, "GREEDY_ND_scale_to_ahull failed"); + EXPECT_DOUBLE_EQ(1 / 3.0, alpha); + + rval = GREEDY_ND_scale_to_ahull(3, 4, rays, rx, beta, epsilon, d2, &alpha); + abort_if(rval, "GREEDY_ND_scale_to_ahull failed"); + EXPECT_DOUBLE_EQ(0.25, alpha); + + rval = GREEDY_ND_scale_to_ahull(3, 4, rays, rx, beta, epsilon, d3, &alpha); + abort_if(rval, "GREEDY_ND_scale_to_ahull failed"); + EXPECT_EQ(INFINITY, alpha); + +CLEANUP: + if(rval) FAIL(); +} + diff --git a/lifting/benchmark/CMakeLists.txt b/lifting/benchmark/CMakeLists.txt new file mode 100644 index 0000000..1219149 --- /dev/null +++ b/lifting/benchmark/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(lifting-benchmark.run src/main.c) +target_link_libraries (lifting-benchmark.run LINK_PUBLIC lifting_static multirow_static) diff --git a/lifting/benchmark/src/main.c b/lifting/benchmark/src/main.c new file mode 100644 index 0000000..dc2bd70 --- /dev/null +++ b/lifting/benchmark/src/main.c @@ -0,0 +1,639 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _XOPEN_SOURCE 500 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +char LOG_FILENAME[1000] = {0}; +char STATS_FILENAME[1000] = {0}; +char SETS_FILENAME[1000] = {0}; +char ANSWERS_FILENAME[1000] = {0}; +unsigned int SEED = 0; + +#define ALGORITHM_BOUND 0 +#define ALGORITHM_NAIVE 1 +#define ALGORITHM_MIP 2 + +int SELECT_NAIVE_ALGORITHM = 0; +int SELECT_BOUND_ALGORITHM = 0; +int SELECT_MIP_ALGORITHM = 0; + +int ENABLE_PREPROCESSING = 0; +int ENABLE_SHEAR = 0; + +int CHECK_ANSWERS = 0; +int WRITE_ANSWERS = 0; + +int USE_FIXED_BOUNDS = 0; +int NAIVE_BIG_M = 0; +int N_SAMPLES_PER_SET = 10; + +int N_ANSWERS = 0; +double ANSWERS[100000]; + +double PRE_M[100000]; +double CENTER[100000]; + +FILE *LOG_FILE; +FILE *STATS_FILE; +FILE *ANSWERS_FILE; + +int BOUNDING_BOX_PADDING = 5; + +static const struct option options_tab[] = +{ + {"help", no_argument, 0, 'h'}, + {"sets", required_argument, 0, 'b'}, + {"log", required_argument, 0, 'l'}, + {"stats", required_argument, 0, 'o'}, + {"seed", required_argument, 0, 's'}, + {"fixed-bounds", required_argument, 0, 'f'}, + {"naive", no_argument, 0, 'n'}, + {"bound", no_argument, 0, 'u'}, + {"preprocess", no_argument, 0, 'p'}, + {"shear", no_argument, 0, 'e'}, + {"write-answers", required_argument, 0, 'w'}, + {"check-answers", required_argument, 0, 'c'}, + {"samples", required_argument, 0, 'a'}, + {"mip", no_argument, 0, 'm'}, + {0, 0, 0, 0} +}; + +static void print_usage(char **argv) +{ + printf("Usage: %s [OPTION]...\n", argv[0]); + printf("Performs trivial lifting using different algorithms.\n\n"); + + printf("Parameters:\n"); + printf("%4s %-20s %s\n", "-l", "--log=FILE", + "write log to the specified file"); + printf("%4s %-20s %s\n", "-b", "--sets=FILE", + "file containing lattice-free sets"); + printf("%4s %-20s %s\n", "-o", "--stats=FILE", + "write statistics to the specified file"); + printf("%4s %-20s %s\n", "-s", "--seed=NUM", + "random seed"); + printf("%4s %-20s %s\n", "-f", "--fixed-bounds=NUM", + "user fixed bounds for naive algorithm"); + printf("%4s %-20s %s\n", "-n", "--naive", + "select naive algorithm"); + printf("%4s %-20s %s\n", "-m", "--mip", + "select MIP algorithm"); + printf("%4s %-20s %s\n", "-u", "--bound", + "select bound algorithm"); + printf("%4s %-20s %s\n", "-p", "--preprocess", + "enable pre-processing step in bound algorithm"); + printf("%4s %-20s %s\n", "-e", "--shear", + "apply shear transformation to sets"); + printf("%4s %-20s %s\n", "-w", "--write-answers=FILE", + "write computed coefficients to given file"); + printf("%4s %-20s %s\n", "-c", "--check-answers=FILE", + "check computed coefficients against given file"); + printf("%4s %-20s %s\n", "-a", "--samples=NUM", + "use specified number of samples per set"); +} + +void stats_printf(const char *fmt, + ...) +{ + if(STATS_FILE) + { + va_list args; + va_start(args, fmt); + vfprintf(STATS_FILE, fmt, args); + va_end(args); + + fflush(STATS_FILE); + } +} + +static int parse_args(int argc, + char **argv) +{ + int rval = 0; + opterr = 0; + + while (1) + { + int c = 0; + int option_index = 0; + c = getopt_long(argc, argv, "hb:k:s:f:o:nupew:c:a:m", options_tab, + &option_index); + + if (c < 0) break; + + switch (c) + { + case 'l': + strcpy(LOG_FILENAME, optarg); + break; + + case 's': + { + int count = sscanf(optarg, "%u", &SEED); + abort_if(count != 1, "invalid seed"); + break; + } + + case 'a': + { + int count = sscanf(optarg, "%d", &N_SAMPLES_PER_SET); + abort_if(count != 1, "invalid number of samples"); + abort_if(N_SAMPLES_PER_SET <= 0, "invalid number of samples"); + break; + } + + case 'f': + { + USE_FIXED_BOUNDS = 1; + int count = sscanf(optarg, "%d", &NAIVE_BIG_M); + abort_if(count != 1, "invalid fixed bound"); + break; + } + + case 'o': + strcpy(STATS_FILENAME, optarg); + break; + + case 'b': + strcpy(SETS_FILENAME, optarg); + break; + + case 'w': + strcpy(ANSWERS_FILENAME, optarg); + WRITE_ANSWERS = 1; + break; + + case 'c': + strcpy(ANSWERS_FILENAME, optarg); + CHECK_ANSWERS = 1; + break; + + case 'n': + SELECT_NAIVE_ALGORITHM = 1; + break; + + case 'm': + SELECT_MIP_ALGORITHM = 1; + break; + + case 'u': + SELECT_BOUND_ALGORITHM = 1; + break; + + case 'p': + ENABLE_PREPROCESSING = 1; + break; + + case 'e': + ENABLE_SHEAR = 1; + break; + + case 'h': + print_usage(argv); + exit(0); + + case ':': + fprintf(stderr, "%s: option '-%c' requires an argument\n", + argv[0], optopt); + rval = 1; + goto CLEANUP; + + case '?': + default: + fprintf(stderr, "%s: option '-%c' is invalid\n", argv[0], + optopt); + rval = 1; + goto CLEANUP; + + } + } + + if ((strlen(SETS_FILENAME) == 0)) + { + fprintf(stderr, "You must specify a file containing the lattice-free " + "sets.\n"); + rval = 1; + } + + if (SELECT_NAIVE_ALGORITHM + SELECT_BOUND_ALGORITHM + SELECT_MIP_ALGORITHM != 1) + { + fprintf(stderr, "You must select exactly one algorithm.\n"); + rval = 1; + } + + if (CHECK_ANSWERS + WRITE_ANSWERS > 1) + { + fprintf(stderr, "Cannot write and check answers at same time.\n"); + rval = 1; + } + +CLEANUP: + if (rval) + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); + return rval; +} + +void print_header(int argc, + char *const *argv) +{ + char hostname[3000]; + gethostname(hostname, 1024); + + time_t now; + time(&now); + struct tm *ttm = localtime(&now); + + time_printf("benchmark.run\n"); + time_printf("%s %04d-%02d-%02d %02d:%02d\n", hostname, ttm->tm_year + 1900, + ttm->tm_mon + 1, ttm->tm_mday, ttm->tm_hour, ttm->tm_min); + + time_printf("Compile-time parameters:\n"); + time_printf(" EPSILON: %e\n", EPSILON); + + char cmdline[5000] = {0}; + for (int i = 0; i < argc; i++) + sprintf(cmdline + strlen(cmdline), "%s ", argv[i]); + + time_printf("Command line arguments:\n"); + time_printf(" %s\n", cmdline); +} + +int benchmark_set_sample(int algorithm, + int set_idx, + const struct LFreeSet2D *set, + const double *rays, + const double *pre_m, + const double center, + int *lb, + int *ub, + int current_sample, + int *wrong_answer) +{ + int rval = 0; + + for (int i = 0; i < N_RAYS; i++) + { + double ray[2] = { rays[2 * i], rays[2 * i + 1] }; + double value; + + if(ENABLE_PREPROCESSING) + { + double r0 = ray[0], r1 = ray[1]; + ray[0] = pre_m[0] * r0 + pre_m[2] * r1; + ray[1] = pre_m[1] * r0 + pre_m[3] * r1; + + ray[0] = ray[0] - floor(set->f[0] + ray[0]); + ray[1] = ray[1] + floor(center + 0.5 - set->f[1] - ray[1]); + } + + log_debug(" Ray %d (%.6lf,%.6lf)...\n", i, ray[0], ray[1]); + + switch (algorithm) + { + case ALGORITHM_BOUND: + rval = LIFTING_2D_bound(set->n_halfspaces, set->halfspaces, ray, + &value); + abort_if(rval, "LIFTING_2D_bound failed"); + break; + + case ALGORITHM_NAIVE: + rval = LIFTING_2D_naive(set->n_halfspaces, set->halfspaces, ray, + lb, ub, &value); + abort_if(rval, "LIFTING_2D_naive failed"); + break; + + case ALGORITHM_MIP: + rval = LIFTING_2D_mip(set->n_halfspaces, set->halfspaces, ray, + &value); + abort_if(rval, "LIFTING_2D_mip failed"); + break; + + default: + abort_if(1, "Invalid algorithm"); + } + + if(current_sample == 0) + { + if(WRITE_ANSWERS) + { + abort_iff(!DOUBLE_geq(value, 0), + "value should be non-negative (%.8lf)", value); + fprintf(ANSWERS_FILE, "%d %d %.20lf\n", set_idx, i, value); + } + + if(CHECK_ANSWERS) + { + int count; + int expected_set_idx, expected_i; + double expected_value; + + while(1) + { + count = fscanf(ANSWERS_FILE, "%d %d %lf ", + &expected_set_idx, &expected_i, &expected_value); + + abort_if(count != 3, "error reading answer"); + if(set_idx == expected_set_idx && i == expected_i) break; + } + + double delta = fabs(value - expected_value); + if(delta > 1e-3) + { + log_warn(" wrong answer (set=%d ray=%d answer=%.8lf" + " expected=%.8lf delta=%.8lf)\n", set_idx, + i, value, expected_value, delta); + *wrong_answer = 1; + + LFREE_2D_print_set(set); + } + } + + log_verbose(" %4d %12.8lf\n", j, value); + } + } + +CLEANUP: + return rval; +} + +int benchmark_set(int algorithm, + int set_idx, + const struct LFreeSet2D *set, + const double *rays, + const double *pre_m, + const double center, + int *wrong_answer) +{ + int rval = 0; + int lb[2], ub[2]; + + if(algorithm == ALGORITHM_NAIVE) + { + if (USE_FIXED_BOUNDS) + { + ub[0] = ub[1] = NAIVE_BIG_M; + lb[0] = lb[1] = -NAIVE_BIG_M; + } + else + { + rval = LFREE_2D_get_bounding_box(set, lb, ub); + abort_if(rval, "LFREE_2D_get_bounding_box failed"); + + ub[0] += BOUNDING_BOX_PADDING; + ub[1] += BOUNDING_BOX_PADDING; + lb[0] -= BOUNDING_BOX_PADDING; + lb[0] -= BOUNDING_BOX_PADDING; + } + + ub[0] = fmin(ub[0], MAX_BOX_SIZE); + ub[1] = fmin(ub[1], MAX_BOX_SIZE); + lb[0] = fmax(lb[0], -MAX_BOX_SIZE); + lb[1] = fmax(lb[1], -MAX_BOX_SIZE); + + if(ub[0] == MAX_BOX_SIZE || ub[1] == MAX_BOX_SIZE || lb[0] == + -MAX_BOX_SIZE || lb[1] == -MAX_BOX_SIZE) + { + log_info(" bounding box has been clipped\n"); + } + } + + for (int k = 0; k < N_SAMPLES_PER_SET; k ++) + { + rval = benchmark_set_sample(algorithm, set_idx, set, rays, pre_m, + center, lb, ub, k, wrong_answer); + abort_if(rval, "benchmark_set_sample failed"); + } + +CLEANUP: + return rval; +} + +int benchmark(int n_sets, struct LFreeSet2D *sets, double *rays, + int algorithm) +{ + int rval = 0; + + log_info("Running benchmark...\n"); + + double total_initial_time = get_user_time(); + stats_printf("cpu_time:\n"); + + for (int j = 0; j < n_sets; j++) + { + log_debug("Set %d...\n", j); + + double set_initial_time = get_user_time(); + + struct LFreeSet2D *set = &sets[j]; + double *pre_m = &PRE_M[j * 4]; + double center = CENTER[j]; + + rval = LFREE_2D_print_set(set); + abort_if(rval, "LFREE_2D_print_set failed"); + + int wrong_answer = 0; + + rval = benchmark_set(algorithm, j, set, rays, pre_m, center, &wrong_answer); + abort_if(rval, "benchmark_set failed"); + + double set_duration = get_user_time() - set_initial_time; + double avg = (set_duration / N_SAMPLES_PER_SET) * 1000; + + if(wrong_answer) avg = 1000000; + + stats_printf(" %d: %.8lf\n", j, avg); + log_info(" %3d: %12.3lf ms\n", j, avg); + } + + double total_duration = get_user_time() - total_initial_time; + + log_info(" %.3lf ms per set \n", + total_duration / (n_sets * N_SAMPLES_PER_SET) * 1000); + + if(algorithm == ALGORITHM_MIP) + { + log_info(" %.3lf s spent on LP_create\n", MIP_TIME_CREATE); + log_info(" %.3lf s spent on LP_optimize\n", MIP_TIME_OPTIMIZE); + } + +CLEANUP: + return rval; +} + +int main(int argc, char **argv) +{ + int rval = 0; + double *rays = 0; + struct LFreeSet2D sets[MAX_N_SETS]; + + rval = parse_args(argc, argv); + if (rval) return 1; + + print_header(argc, argv); + + if (LOG_FILENAME[0]) + { + LOG_FILE = fopen(LOG_FILENAME, "w"); + abort_if(!LOG_FILE, "could not open log file"); + log_info("Writing log to file: %s\n", LOG_FILENAME); + } + + if (STATS_FILENAME[0]) + { + STATS_FILE = fopen(STATS_FILENAME, "w"); + abort_if(!STATS_FILE, "could not open stats file"); + log_info("Writing stats to file: %s\n", STATS_FILENAME); + } + + if (WRITE_ANSWERS) + { + N_SAMPLES_PER_SET = 1; + ANSWERS_FILE = fopen(ANSWERS_FILENAME, "w"); + abort_if(!ANSWERS_FILE, "could not open answers file"); + log_info("Writing answers to file: %s\n", ANSWERS_FILENAME); + } + + if (CHECK_ANSWERS) + { + ANSWERS_FILE = fopen(ANSWERS_FILENAME, "r"); + abort_if(!ANSWERS_FILE, "could not open answers file"); + log_info("Reading answers from file: %s\n", ANSWERS_FILENAME); + } + + if(SEED == 0) + { + struct timeval t1; + gettimeofday(&t1, NULL); + SEED = (unsigned int) (t1.tv_usec * t1.tv_sec) % 10000; + } + + log_info("Random seed: %u\n", SEED); + srand(SEED); + + log_info("Generating %d random rays...\n", N_RAYS); + + rays = (double*) malloc(2 * N_RAYS * sizeof(double)); + abort_if(!rays, "could not allocate rays"); + + for (int i = 0; i < N_RAYS; i++) + { + double *ray = &rays[2 * i]; + ray[0] = DOUBLE_random(0.0, 1.0); + ray[1] = DOUBLE_random(0.0, 1.0); + } + + int algorithm = ALGORITHM_BOUND; + if(SELECT_MIP_ALGORITHM) algorithm = ALGORITHM_MIP; + else if(SELECT_NAIVE_ALGORITHM) algorithm = ALGORITHM_NAIVE; + + if(algorithm == ALGORITHM_NAIVE) + { + log_info("Enabling naive algorithm\n"); + + if(USE_FIXED_BOUNDS) + log_info("Using fixed big M: %d\n", NAIVE_BIG_M); + else + log_info("Enabling bounding boxes\n"); + } + else + { + log_info("Enabling bound algorithm\n"); + } + + log_info("Setting %d samples per set\n", N_SAMPLES_PER_SET); + + if(ENABLE_PREPROCESSING) + log_info("Enabling pre-processing\n"); + + log_info("Reading sets from file...\n"); + FILE *sets_file = fopen(SETS_FILENAME, "r"); + abort_iff(!sets_file, "could not read file %s", SETS_FILENAME); + + int line = 0; + int n_sets = 0; + while(!feof(sets_file)) + { + line++; + struct LFreeSet2D *set = &sets[n_sets]; + LFREE_2D_init(set, 4, 4, 4); + + rval = LFREE_2D_read_next(sets_file, set); + abort_iff(rval, "LFREE_2D_read_next failed (line %d)", line); + + if(ENABLE_SHEAR) + { + double m[4] = { 51.0, 5.0, 10.0, 1.0 }; + rval = LFREE_2D_transform_set(set, m); + abort_iff(rval, "LFREE_2D_transform_set failed (line %d)", line); + } + + double dx = -floor(set->f[0]); + double dy = -floor(set->f[1]); + rval = LFREE_2D_translate_set(set, dx, dy); + abort_iff(rval, "LFREE_2D_translate_set failed (line %d)", line); + + if(ENABLE_PREPROCESSING) + { + double *pre_m = &PRE_M[n_sets * 4]; + double *center = &CENTER[n_sets]; + rval = LFREE_2D_preprocess(set, pre_m, center); + abort_iff(rval, "LFREE_2D_preprocess failed (line %d)", line); + } + + rval = LFREE_2D_compute_halfspaces(set); + abort_iff(rval, "LFREE_2D_compute_halfspaces failed (line %d)", line); + + rval = LIFTING_2D_verify(set); + if(rval) + { + log_warn(" skipping invalid set on line %d\n", line); + continue; + } + + n_sets++; + if(n_sets >= MAX_N_SETS) break; + } + + fclose(sets_file); + log_info("Successfully read %d sets\n", n_sets); + + rval = benchmark(n_sets, sets, rays, algorithm); + abort_if(rval, "benchmark failed"); + + log_info("Done.\n"); + +CLEANUP: + if (LOG_FILE) fclose(LOG_FILE); + if (STATS_FILE) fclose(STATS_FILE); + if (ANSWERS_FILE) fclose(ANSWERS_FILE); + if (rays) free(rays); + return rval; +} diff --git a/lifting/library/CMakeLists.txt b/lifting/library/CMakeLists.txt new file mode 100644 index 0000000..e26cc0c --- /dev/null +++ b/lifting/library/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories(include) + +set(COMMON_SOURCES + src/lifting.c + src/lifting-mip.c + include/lifting/lifting-mip.h + include/lifting/lifting.h) + +set(TEST_SOURCES + tests/lifting-test.cpp) + +add_library(lifting_static ${COMMON_SOURCES}) +set_target_properties(lifting_static PROPERTIES OUTPUT_NAME lifting) +target_include_directories (lifting_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(lifting-test.run ${COMMON_SOURCES} ${TEST_SOURCES}) +target_link_libraries(lifting-test.run gtest_main multirow_static) diff --git a/lifting/library/include/lifting/lifting-mip.h b/lifting/library/include/lifting/lifting-mip.h new file mode 100644 index 0000000..4e4cecb --- /dev/null +++ b/lifting/library/include/lifting/lifting-mip.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2016 Laurent Poirrier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIFTING_MIP_H +#define LIFTING_MIP_H + +extern double MIP_TIME_OPTIMIZE; +extern double MIP_TIME_CREATE; + +int LIFTING_2D_mip_init(); + +void LIFTING_2D_mip_cleanup(); + +int LIFTING_2D_mip(int n_halfspaces, + const double *halfspaces, + const double *ray, + double *value); + +#endif diff --git a/lifting/library/include/lifting/lifting.h b/lifting/library/include/lifting/lifting.h new file mode 100644 index 0000000..c261404 --- /dev/null +++ b/lifting/library/include/lifting/lifting.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIFTING_H +#define LIFTING_H + +#include + +int LIFTING_2D_psi(int n_halfspaces, + const double *halfspaces, + const double *ray, + double *value); + +int LIFTING_2D_optimize_continuous(int n_halfspaces, + const double *halfspaces, + double alpha2, + double *alpha1, + double *value); + +int LIFTING_2D_lift_fixed(int n_halfspaces, + const double *halfspaces, + const double *ray, + double k1, + double *opt); + +int LIFTING_2D_naive(int n_halfspaces, + const double *halfspaces, + const double *ray, + const int *lb, + const int *ub, + double *value); + +int LIFTING_2D_bound(int n_halfspaces, + const double *halfspaces, + const double *ray, + double *value); + +int LIFTING_2D_verify(struct LFreeSet2D *set); + +#endif //LIFTING_H diff --git a/lifting/library/src/lifting-mip.c b/lifting/library/src/lifting-mip.c new file mode 100644 index 0000000..580d6e1 --- /dev/null +++ b/lifting/library/src/lifting-mip.c @@ -0,0 +1,162 @@ +/* Copyright (c) 2016 Laurent Poirrier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +double MIP_TIME_OPTIMIZE = 0; +double MIP_TIME_CREATE = 0; + +/** + * Returns non-zero if the cplex_env part of struct LP is initialized. + * + * @param lp pointer to a struct LP + */ +int LP_is_open(struct LP *lp) +{ + return (lp->cplex_env != 0); +} + +/** + * Frees the cplex_lp part of struct LP, leaving the environment open. + * + * @param lp pointer to a struct LP + */ +void LP_destroy(struct LP *lp) +{ + if(!lp) return; + if(!lp->cplex_env) return; + + if(lp->cplex_lp) + CPXfreeprob(lp->cplex_env, &(lp->cplex_lp)); + + lp->cplex_lp = 0; +} + +/** + * Static data for LIFTING_2D_mip(). + * + * Warning: Because of this, LIFTING_2D_mip() is not thread safe. + */ +static struct LP LIFTING_2D_mip_lp = {0, 0}; + +/** + * Opens the cplex environment of the struct LP used by LIFTING_2D_mip(). + * + * This is called automatically by LIFTING_2D_mip(). + */ +int LIFTING_2D_mip_init() +{ + struct LP *lp = &LIFTING_2D_mip_lp; + + if(!LP_is_open(lp)) + { + if(LP_open(lp)) return (-1); + } + + return (0); +} + +/** + * Closes the cplex environment of the struct LP used by LIFTING_2D_mip(). + * + * This function should be called by the user of LIFTING_2D_mip(), if + * cleaning up the environment is desired. + */ +void LIFTING_2D_mip_cleanup() +{ + struct LP *lp = &LIFTING_2D_mip_lp; + + LP_free(lp); +} + +/* + * Computes the lifting coefficient of a ray by formulating the lifting + * problem as a MIP. + * + * @param[in] n_halfspaces number of facets of the lattice-free set used + * @param[in] halfspaces description of the lattice-free set used + * @param[in] ray ray to lift + * @param[out] value lifting coefficient + */ +int LIFTING_2D_mip(int n_halfspaces, + const double *halfspaces, + const double *ray, + double *value) +{ + struct LP *lp = &LIFTING_2D_mip_lp; + int rval = 0; + + double initial_time = get_user_time(); + + rval = LIFTING_2D_mip_init(); + abort_if(rval, "LIFTING_2D_mip_init failed"); + + rval = LP_create(lp, "lifting2d"); + abort_if(rval, "LP_create failed"); + + rval = LP_new_col(lp, 1.0, 0.0, MILP_INFINITY, 'C'); + rval |= LP_new_col(lp, 0.0, -MILP_INFINITY, MILP_INFINITY, 'I'); + rval |= LP_new_col(lp, 0.0, -MILP_INFINITY, MILP_INFINITY, 'I'); + abort_if(rval, "LP_new_col failed"); + + double rhs; + char sense = 'G'; + int beg = 0; + int ind[3] = {0, 1, 2}; + double val[3]; + + val[0] = 1.0; + + for(int i = 0; i < n_halfspaces; i++) + { + double a0 = halfspaces[i * 2 + 0]; + double a1 = halfspaces[i * 2 + 1]; + + rhs = a0 * ray[0] + a1 * ray[1]; + + val[1] = -a0; + val[2] = -a1; + + rval |= LP_add_rows(lp, 1, 3, &rhs, &sense, &beg, ind, val); + } + + MIP_TIME_CREATE += get_user_time() - initial_time; + + abort_if(rval, "LP_add_rows failed"); + + int infeasible; + + initial_time = get_user_time(); + + rval = LP_optimize(lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + abort_if(infeasible, "LIFTING_2D_mip infeasible"); + + MIP_TIME_OPTIMIZE += get_user_time() - initial_time; + + double obj; + + rval = LP_get_obj_val(lp, &obj); + abort_if(rval, "LP_get_obj_val failed"); + + *value = obj; + +CLEANUP: + LP_destroy(lp); + return (rval); +} diff --git a/lifting/library/src/lifting.c b/lifting/library/src/lifting.c new file mode 100644 index 0000000..e890538 --- /dev/null +++ b/lifting/library/src/lifting.c @@ -0,0 +1,286 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _XOPEN_SOURCE 700 + +#include +#include + +#include +#include +#include +#include +#include + +/** + * Verifies if the set is well formed. + */ +int LIFTING_2D_verify(struct LFreeSet2D *set) +{ + int rval = 0; + + abort_if(set->n_halfspaces == 0, "Halfspaces not found"); + + for(int i = 0; i < set->n_lattice_points; i++) + { + double *t = &set->lattice_points[2 * i]; + double r[2] = {t[0] - set->f[0], + t[1] - set->f[1]}; + + double value; + + rval = LIFTING_2D_psi(set->n_halfspaces, set->halfspaces, r, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + + double delta = fabs(value - 1); + if(delta > 0.0001) + { + log_debug("Lattice point (%.2lf, %.2lf) is " + "not on the boundary (delta=%.6lf)", t[0], t[1], delta); + rval = 1; + goto CLEANUP; + } + } + +CLEANUP: + return rval; +} + +int LIFTING_2D_psi(int n_halfspaces, + const double *halfspaces, + const double *ray, + double *value) +{ + int rval = 0; + + *value = -INFINITY; + + for(int i = 0; i < n_halfspaces; i++) + { + const double *h = &halfspaces[2 * i]; + double v = ray[0] * h[0] + ray[1] * h[1]; + *value = DOUBLE_max(v, *value); + } + +CLEANUP: + return rval; +} + +int LIFTING_2D_optimize_continuous(int n_halfspaces, + const double *halfspaces, + double alpha2, + double *alpha1, + double *value) +{ + int rval = 0; + + *value = -INFINITY; + + for(int r = 0; r < n_halfspaces; r++) + { + for(int s = r+1; s < n_halfspaces; s++) + { + const double *ar = &halfspaces[r * 2]; + const double *as = &halfspaces[s * 2]; + + double xr = ar[0]; + double xs = as[0]; + if(DOUBLE_eq(xr, xs)) continue; + + double br = - xs / (xr - xs); + double bs = xr / (xr - xs); + if(!DOUBLE_geq(br, 0)) continue; + if(!DOUBLE_geq(bs, 0)) continue; + + double yr = ar[1] * alpha2; + double ys = as[1] * alpha2; + + double obj = yr * br + ys * bs; + + if(DOUBLE_geq(obj, *value)) + { + *value = obj; + + if(DOUBLE_neq(xr, 0)) + *alpha1 = - (yr - obj) / xr; + else + *alpha1 = - (ys - obj) / xs; + } + } + } + +CLEANUP: + return rval; +} + +int LIFTING_2D_lift_fixed(int n_halfspaces, + const double *halfspaces, + const double *ray, + double k1, + double *opt) +{ + int rval = 0; + + double k0; + double value; + + rval = LIFTING_2D_optimize_continuous(n_halfspaces, halfspaces, + ray[1] + k1, &k0, &value); + abort_if(rval, "LIFTING_2D_optimize_continuous failed"); + + double delta = k0 - ray[0]; + + double r_ceil[2] = { ray[0] + ceil(delta), ray[1] + k1 }; + double r_floor[2] = { ray[0] + floor(delta), ray[1] + k1 }; + + log_debug(" delta=%.6lf value=%.6lf\n", delta, value); + log_debug(" r_ceil=%12.6lf %12.6lf\n", r_ceil[0], r_ceil[1]); + log_debug(" r_floor=%12.6lf %12.6lf\n", r_floor[0], r_floor[1]); + + double value_ceil, value_floor; + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r_ceil, &value_ceil); + abort_if(rval, "LIFTING_2D_psi failed"); + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r_floor, &value_floor); + abort_if(rval, "LIFTING_2D_psi failed"); + + *opt = min(value_ceil, value_floor); + +CLEANUP: + return rval; +} + +int LIFTING_2D_naive(int n_halfspaces, + const double *halfspaces, + const double *ray, + const int *lb, + const int *ub, + double *value) +{ + int rval = 0; + + *value = INFINITY; + int best_k0 = 0; + int best_k1 = 0; + + int margin = 10; + + for(int k0 = lb[0] - margin; k0 <= ub[0] + margin; k0++) + { + for(int k1 = lb[1] - margin; k1 <= ub[1] + margin; k1++) + { + double q[2] = { ray[0] + k0, ray[1] + k1 }; + double value_q; + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, q, &value_q); + abort_if(rval, "LIFTING_2D_ps failed"); + + if(value_q < *value) + { + best_k0 = k0; + best_k1 = k1; + *value = value_q; + log_debug(" k=%6d %6d value=%12.6lf\n", k0, k1, *value); + } + } + } + +// log_debug(" best_k=(%d %d) value=%.6lf\n", best_k0, best_k1, *value); + +CLEANUP: + return rval; +} + +int LIFTING_2D_bound(int n_halfspaces, + const double *halfspaces, + const double *ray, + double *value) +{ + int rval = 0; + + double eta_star, eta_plus, eta_minus; + double xi_plus, xi_minus; + double m_plus, m_minus; + double ignored; + int k1 = 1; + + rval = LIFTING_2D_lift_fixed(n_halfspaces, halfspaces, ray, 0, &eta_star); + abort_if(rval, "LIFTING_2D_lift_fixed failed"); + + rval = LIFTING_2D_optimize_continuous(n_halfspaces, halfspaces, 1, &ignored, + &xi_plus); + abort_if(rval, "LIFTING_2D_optimize_continuous failed"); + + rval = LIFTING_2D_optimize_continuous(n_halfspaces, halfspaces, -1, + &ignored, &xi_minus); + abort_if(rval, "LIFTING_2D_optimize_continuous failed"); + + m_plus = m_minus = INFINITY; + + log_debug("Level 0:\n"); + log_debug(" eta star = %.6lf\n", eta_star); + log_debug(" next slack plus = %.6lf\n", k1 + ray[1] - m_plus); + log_debug(" next slack minus = %.6lf\n", k1 - ray[1] - m_minus); + log_debug(" xi plus = %.6lf\n", xi_plus); + log_debug(" xi minus = %.6lf\n", xi_minus); + + while((k1 <= fabs(ray[1])) || (k1 + ray[1] <= m_plus) || (k1 - ray[1] <= m_minus)) + { + log_debug("Level %d:\n", k1); + + double h_plus, h_minus; + + rval = LIFTING_2D_optimize_continuous(n_halfspaces, halfspaces, ray[1] + + k1, &ignored, &h_plus); + abort_if(rval, "LIFTING_2D_optimize_continuous failed"); + + rval = LIFTING_2D_optimize_continuous(n_halfspaces, halfspaces, ray[1] + - k1, &ignored, &h_minus); + abort_if(rval, "LIFTING_2D_optimize_continuous failed"); + + rval = LIFTING_2D_lift_fixed(n_halfspaces, halfspaces, ray, k1, + &eta_plus); + abort_if(rval, "LIFTING_2D_lift_fixed failed"); + + rval = LIFTING_2D_lift_fixed(n_halfspaces, halfspaces, ray, -k1, + &eta_minus); + abort_if(rval, "LIFTING_2D_lift_fixed failed"); + + eta_star = fmin(eta_star, fmin(eta_plus, eta_minus)); + + m_plus = ceil(eta_star / xi_plus); + m_minus = ceil(eta_star / xi_minus); + + log_debug(" eta plus = %.6lf\n", eta_plus); + log_debug(" eta minus = %.6lf\n", eta_minus); + log_debug(" eta star = %.6lf\n", eta_star); + log_debug(" h minus = %.6lf\n", h_minus); + log_debug(" h plus = %.6lf\n", h_plus); + + k1++; + + log_debug(" next slack plus = %.6lf\n", k1 + ray[1] - m_plus); + log_debug(" next slack minus = %.6lf\n", k1 - ray[1] - m_minus); + } + + log_debug("Done\n"); + + *value = eta_star; + +CLEANUP: + return rval; +} diff --git a/lifting/library/tests/fixtures/quads.txt b/lifting/library/tests/fixtures/quads.txt new file mode 100644 index 0000000..bc64e3d --- /dev/null +++ b/lifting/library/tests/fixtures/quads.txt @@ -0,0 +1 @@ +0.5 0.5 4 -0.5 0.5 0.5 1.5 1.5 0.5 0.5 -0.5 4 0 0 0 1 1 1 1 0 diff --git a/lifting/library/tests/fixtures/triangles.txt b/lifting/library/tests/fixtures/triangles.txt new file mode 100644 index 0000000..b13a331 --- /dev/null +++ b/lifting/library/tests/fixtures/triangles.txt @@ -0,0 +1,2 @@ +0.7875 1.7875 3 1 2 0.4875 2.0875 -0.0625 -16.0625 3 1 2 0 -14 0 -15 +0.5 0.5 3 0 0 2 0 0 2 3 0 1 1 0 1 1 diff --git a/lifting/library/tests/lifting-test.cpp b/lifting/library/tests/lifting-test.cpp new file mode 100644 index 0000000..12b7cf5 --- /dev/null +++ b/lifting/library/tests/lifting-test.cpp @@ -0,0 +1,715 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +} + +#define E 1e-6 + +TEST(Lifting2DTest, lift_fixed_test_2) +{ + int rval = 0; + + double opt; + + int n_halfspaces = 3; + double halfspaces[] = { + -1.12796209, -2.25592417, + 2.38125329, 11.19606810, + 7.35264174, 6.22467965, + }; + + double r[] = { 0.48231629, 0.70551355 }; + + rval = LIFTING_2D_lift_fixed(n_halfspaces, halfspaces, r, -1, &opt); + abort_if(rval, "LIFTING_lift_fixed failed"); + EXPECT_NEAR(opt, 1.24826670, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, psi_test) +{ + int rval = 0; + + int n_halfspaces = 5; + double halfspaces[] = { + -1 / 2.0, 0, + -1 / 2.0, -1 / 2.0, + -1 / 4.0, 1 / 2.0, + -1 / 6.0, -5 / 6.0, + 5 / 16.0, 1 / 8.0 + }; + + double r1[] = { 0, 0 }; + double r2[] = { 2, 3 }; + double r3[] = { 1 / 3.0, 1 / 2.0 }; + double r4[] = { -4.0, 0 }; + double r5[] = { 1, -1/2.0 }; + double r6[] = { -1/2.0, 1/2.0 }; + double r7[] = { 1/2.0, 1/2.0 }; + double value; + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r1, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + EXPECT_NEAR(value, 0.0, E); + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r2, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + EXPECT_NEAR(value, 1.0, E); + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r3, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + EXPECT_NEAR(value, 1 / 6.0, E); + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r4, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + EXPECT_NEAR(value, 2.0, E); + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r5, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + EXPECT_NEAR(value, 1 / 4.0, E); + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r6, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + EXPECT_NEAR(value, 0.375, E); + + rval = LIFTING_2D_psi(n_halfspaces, halfspaces, r7, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + EXPECT_NEAR(value, 7 / 32.0, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, psi_test_2) +{ + int rval = 0; + + LFreeSet2D set; + + set.f[0] = 0; + set.f[1] = 0.359; + + double vertices[] = { + -1.700870, -0.796700, + 0.068700, 0.032200, + 1.106900, 1.111100, + -0.131990, 0.986800, + }; + + double lattice_points[] = { + 0.000000, 0.000000, + 1.000000, 1.000000, + 0.000000, 1.000000, + -1.000000, 0.000000, + }; + + + int ub[] = { 10, 10 }; + int lb[] = { -12, -12 }; + + double r0[] = { 0.37, 0.19 }; + double value; + + rval = LFREE_2D_init(&set, 100, 100, 100); + abort_if(rval, "LFREE_2D_init failed"); + + set.n_vertices = 4; + set.vertices = vertices; + + set.n_lattice_points = 4; + set.lattice_points = lattice_points; + + rval = LFREE_2D_compute_halfspaces(&set); + abort_if(rval, "LFREE_2D_compute_halfspaces failed"); + + rval = LFREE_2D_print_set(&set); + abort_if(rval, "LFREE_2D_print_set failed"); + + rval = LIFTING_2D_naive(set.n_halfspaces, set.halfspaces, r0, lb, ub, &value); + abort_if(rval, "LIFTING_2D_psi failed"); + + EXPECT_NEAR(value, 0.48846868, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, naive_test_1) +{ + int rval = 0; + + int n_halfspaces = 5; + double halfspaces[] = { + -1 / 2.0, 0, + -1 / 2.0, -1 / 2.0, + -1 / 4.0, 1 / 2.0, + -1 / 6.0, -5 / 6.0, + 5 / 16.0, 1 / 8.0 + }; + + double r1[] = { 1/2.0, 1/2.0 }; + double r2[] = { 0, 1/2.0 }; + double r3[] = { 1/3.0, 2/3.0 }; + double r4[] = { 2/5.0, 3/7.0 }; + int lb[] = { -10, -10 }; + int ub[] = { 10, 10 }; + + double value; + + rval = LIFTING_2D_naive(n_halfspaces, halfspaces, r1, lb, ub, &value); + abort_if(rval, "LIFTING_2D_naive failed"); + EXPECT_NEAR(value, 0.21875, E); + + rval = LIFTING_2D_naive(n_halfspaces, halfspaces, r2, lb, ub, &value); + abort_if(rval, "LIFTING_2D_naive failed"); + EXPECT_NEAR(value, 0.25, E); + + rval = LIFTING_2D_naive(n_halfspaces, halfspaces, r3, lb, ub, &value); + abort_if(rval, "LIFTING_2D_naive failed"); + EXPECT_NEAR(value, 0.222222, E); + + rval = LIFTING_2D_naive(n_halfspaces, halfspaces, r4, lb, ub, &value); + abort_if(rval, "LIFTING_2D_naive failed"); + EXPECT_NEAR(value, 0.178571, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, naive_test_2) +{ + int rval = 0; + + int n_halfspaces = 3; + double halfspaces[] = { + -1.12796209, -2.25592417, + 2.38125329, 11.19606810, + 7.35264174, 6.22467965, + }; + + double r[] = { 0.48231629, 0.70551355 }; + int lb[] = { -50, -50 }; + int ub[] = { 50, 50 }; + + double value; + + rval = LIFTING_2D_naive(n_halfspaces, halfspaces, r, lb, ub, &value); + abort_if(rval, "LIFTING_2D_naive failed"); + EXPECT_NEAR(value, 1.24826671, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, bound_test_1) +{ + int rval = 0; + + int n_halfspaces = 5; + double halfspaces[] = { + -1 / 2.0, 0, + -1 / 2.0, -1 / 2.0, + -1 / 4.0, 1 / 2.0, + -1 / 6.0, -5 / 6.0, + 5 / 16.0, 1 / 8.0 + }; + + double r1[] = { 1/2.0, 1/2.0 }; + double r2[] = { 0, 1/2.0 }; + double r3[] = { 1/3.0, 2/3.0 }; + double r4[] = { 2/5.0, 3/7.0 }; + + double value; + + rval = LIFTING_2D_bound(n_halfspaces, halfspaces, r1, &value); + abort_if(rval, "LIFTING_2D_bound failed"); + EXPECT_NEAR(value, 0.21875, E); + + rval = LIFTING_2D_bound(n_halfspaces, halfspaces, r2, &value); + abort_if(rval, "LIFTING_2D_bound failed"); + EXPECT_NEAR(value, 0.25, E); + + rval = LIFTING_2D_bound(n_halfspaces, halfspaces, r3, &value); + abort_if(rval, "LIFTING_2D_bound failed"); + EXPECT_NEAR(value, 0.222222, E); + + rval = LIFTING_2D_bound(n_halfspaces, halfspaces, r4, &value); + abort_if(rval, "LIFTING_2D_bound failed"); + EXPECT_NEAR(value, 0.178571, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, bound_test_2) +{ + int rval = 0; + + int n_halfspaces = 3; + double halfspaces[] = { + -1.12796209, -2.25592417, + 2.38125329, 11.19606810, + 7.35264174, 6.22467965, + }; + + double r[] = { 0.48231629, 0.70551355 }; + + double value; + + rval = LIFTING_2D_bound(n_halfspaces, halfspaces, r, &value); + abort_if(rval, "LIFTING_2D_bound failed"); + EXPECT_NEAR(value, 1.24826671, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, mip_test_1) +{ + int rval = 0; + + int n_halfspaces = 5; + double halfspaces[] = { + -1 / 2.0, 0, + -1 / 2.0, -1 / 2.0, + -1 / 4.0, 1 / 2.0, + -1 / 6.0, -5 / 6.0, + 5 / 16.0, 1 / 8.0 + }; + + double r1[] = { 1/2.0, 1/2.0 }; + double r2[] = { 0, 1/2.0 }; + double r3[] = { 1/3.0, 2/3.0 }; + double r4[] = { 2/5.0, 3/7.0 }; + + double value; + + rval = LIFTING_2D_mip(n_halfspaces, halfspaces, r1, &value); + abort_if(rval, "LIFTING_2D_mip failed"); + EXPECT_NEAR(value, 0.21875, E); + + rval = LIFTING_2D_mip(n_halfspaces, halfspaces, r2, &value); + abort_if(rval, "LIFTING_2D_mip failed"); + EXPECT_NEAR(value, 0.25, E); + + rval = LIFTING_2D_mip(n_halfspaces, halfspaces, r3, &value); + abort_if(rval, "LIFTING_2D_mip failed"); + EXPECT_NEAR(value, 0.222222, E); + + rval = LIFTING_2D_mip(n_halfspaces, halfspaces, r4, &value); + abort_if(rval, "LIFTING_2D_mip failed"); + EXPECT_NEAR(value, 0.178571, E); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(Lifting2DTest, verify_test_1) +{ + int rval = 0; + + double vertices[] = { + 0.361588, -0.411851, + 2.550110, 1.000000, + 1.009740, 1.000000, + -0.836467, 0.952742, + }; + + double lattice_points[] = { + 1.000000, 0.000000, + 2.000000, 1.000000, + 1.000000, 1.000000, + 0.000000, 0.000000, + }; + + LFreeSet2D set; + + rval = LFREE_2D_init(&set, 4, 4, 4); + abort_if(rval, "LFREE_2D_init failed"); + + set.n_vertices = 4; + set.vertices = vertices; + + set.n_lattice_points = 4; + set.lattice_points = lattice_points; + + set.f[0] = 0.918857; + set.f[1] = 0.952742; + + rval = LFREE_2D_compute_halfspaces(&set); + abort_if(rval, "LFREE_2D_compute_halfspaces failed"); + + rval = LIFTING_2D_verify(&set); + EXPECT_EQ(rval, 1); + rval = 0; + +CLEANUP: + if(rval) FAIL(); +} + +TEST(LFreeSetTest, read_next_test) +{ + int rval = 0; + + FILE *triangles = fopen("../lifting/library/tests/fixtures/triangles.txt", "r"); + abort_if(!triangles, "could not read triangles.txt"); + + LFreeSet2D set; + LFREE_2D_init(&set, 100, 100, 100); + + rval = LFREE_2D_read_next(triangles, &set); + abort_if(rval, "LFREE_2D_read_next failed"); + + EXPECT_NEAR(set.f[0], 0.7875, E); + EXPECT_NEAR(set.f[1], 1.7875, E); + + EXPECT_NEAR(set.vertices[0], 1, E); + EXPECT_NEAR(set.vertices[1], 2, E); + + EXPECT_NEAR(set.vertices[2], 0.4875, E); + EXPECT_NEAR(set.vertices[3], 2.0875, E); + + EXPECT_NEAR(set.vertices[4], -0.0625, E); + EXPECT_NEAR(set.vertices[5], -16.0625, E); + + EXPECT_NEAR(set.lattice_points[0], 1, E); + EXPECT_NEAR(set.lattice_points[1], 2, E); + + EXPECT_NEAR(set.lattice_points[2], 0, E); + EXPECT_NEAR(set.lattice_points[3], -14, E); + + EXPECT_NEAR(set.lattice_points[4], 0, E); + EXPECT_NEAR(set.lattice_points[5], -15, E); + + rval = LFREE_2D_compute_halfspaces(&set); + abort_if(rval, "LFREE_2D_compute_halfspaces failed"); + + EXPECT_NEAR(set.halfspaces[0], 0.686274, E); + EXPECT_NEAR(set.halfspaces[1], 4.019607, E); + + EXPECT_NEAR(set.halfspaces[2], -3.235294, E); + EXPECT_NEAR(set.halfspaces[3], 0.098039, E); + + EXPECT_NEAR(set.halfspaces[4], 5.0, E); + EXPECT_NEAR(set.halfspaces[5], -0.294117, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(LFreeSetTest, read_next_quadrilateral_test) +{ + int rval = 0; + + FILE *file = fopen("../lifting/library/tests/fixtures/quads.txt", "r"); + abort_if(!file, "could not read quads.txt"); + + LFreeSet2D set; + LFREE_2D_init(&set, 100, 100, 100); + + rval = LFREE_2D_read_next(file, &set); + abort_if(rval, "LFREE_2D_read_next failed"); + + EXPECT_NEAR(set.f[0], 0.5, E); + EXPECT_NEAR(set.f[1], 0.5, E); + + EXPECT_NEAR(set.vertices[0], -0.5, E); + EXPECT_NEAR(set.vertices[1], 0.5, E); + + EXPECT_NEAR(set.vertices[2], 0.5, E); + EXPECT_NEAR(set.vertices[3], 1.5, E); + + EXPECT_NEAR(set.vertices[4], 1.5, E); + EXPECT_NEAR(set.vertices[5], 0.5, E); + + EXPECT_NEAR(set.vertices[6], 0.5, E); + EXPECT_NEAR(set.vertices[7], -0.5, E); + + EXPECT_NEAR(set.lattice_points[0], 0, E); + EXPECT_NEAR(set.lattice_points[1], 0, E); + + EXPECT_NEAR(set.lattice_points[2], 0, E); + EXPECT_NEAR(set.lattice_points[3], 1, E); + + EXPECT_NEAR(set.lattice_points[4], 1, E); + EXPECT_NEAR(set.lattice_points[5], 1, E); + + EXPECT_NEAR(set.lattice_points[6], 1, E); + EXPECT_NEAR(set.lattice_points[7], 0, E); + + rval = LFREE_2D_compute_halfspaces(&set); + abort_if(rval, "LFREE_2D_compute_halfspaces failed"); + + EXPECT_NEAR(set.halfspaces[0], -1, E); + EXPECT_NEAR(set.halfspaces[1], 1, E); + + EXPECT_NEAR(set.halfspaces[2], 1, E); + EXPECT_NEAR(set.halfspaces[3], 1, E); + + EXPECT_NEAR(set.halfspaces[4], 1, E); + EXPECT_NEAR(set.halfspaces[5], -1, E); + + EXPECT_NEAR(set.halfspaces[6], -1, E); + EXPECT_NEAR(set.halfspaces[7], -1, E); + +CLEANUP: + if(rval) FAIL(); +} + +//TEST(LFreeSetTest, get_bounding_box_test) +//{ +// int rval = 0; +// +// FILE *triangles = fopen("../tests/fixtures/triangles.txt", "r"); +// abort_if(!triangles, "could not read triangles.txt"); +// +// LFreeSet2D set; +// LFREE_2D_init(&set, 100, 100, 100); +// +// int ub[2], lb[2]; +// +// rval = LFREE_2D_read_next(triangles, &set); +// abort_if(rval, "LFREE_2D_read_next failed"); +// +// rval = LFREE_2D_get_bounding_box(&set, lb, ub); +// abort_if(rval, "LFREE_2D_get_bounding_box failed"); +// +// EXPECT_NEAR(lb[0], -1.0, E); +// EXPECT_NEAR(ub[0], 1.0, E); +// +// EXPECT_NEAR(lb[1], -18.0, E); +// EXPECT_NEAR(ub[1], 2, E); +// +//CLEANUP: +// if(rval) FAIL(); +//} + +TEST(LFreeSetTest, preprocess_test) +{ + int rval = 0; + + double vertices[] = { + 100/11.0 ,167/33.0, + 10/11.0 , 97/33.0, + 20/11.0 , 39/11.0 + }; + + double lattice_points[] = { + 1, 3, + 4, 4, + 5, 4 + }; + + double pre_m[4]; + double center; + + struct LFreeSet2D set; + set.f[0] = 9 / 2.0; + set.f[1] = 4.0; + + set.n_vertices = 3; + set.vertices = vertices; + + set.n_lattice_points = 3; + set.lattice_points = lattice_points; + + rval = LFREE_2D_preprocess(&set, pre_m, ¢er); + abort_if(rval, "LFREE_2D_preprocess failed"); + + EXPECT_NEAR(set.f[0], 1 / 2.0, E); + EXPECT_NEAR(set.f[1], 1 / 2.0, E); + + EXPECT_NEAR(set.vertices[0], 21 / 11.0, E); + EXPECT_NEAR(set.vertices[1], 5 / 33.0, E); + + EXPECT_NEAR(set.vertices[2], 1 / 11.0, E); + EXPECT_NEAR(set.vertices[3], -5 / 33.0, E); + + EXPECT_NEAR(set.vertices[4], -9 / 11.0, E); + EXPECT_NEAR(set.vertices[5], 15 / 11.0, E); + + EXPECT_NEAR(center, 20 / 33.0, E); + +CLEANUP: + if(rval) FAIL(); + +}TEST(LFreeSetTest, preprocess_test_3) +{ + int rval = 0; + + double vertices[] = { + 1.0, -4.0, + 1.0, 2.0, + -0.2, 0.8 + }; + + double lattice_points[] = { + 1, 0, + 0, 1, + 0, 0 + }; + + double pre_m[4]; + double center; + + struct LFreeSet2D set; + set.f[0] = 1 / 2.0; + set.f[1] = 1 / 2.0; + + set.n_vertices = 3; + set.vertices = vertices; + + set.n_lattice_points = 3; + set.lattice_points = lattice_points; + + rval = LFREE_2D_preprocess(&set, pre_m, ¢er); + abort_if(rval, "LFREE_2D_preprocess failed"); + + EXPECT_NEAR(set.vertices[0], -4, E); + EXPECT_NEAR(set.vertices[1], 1, E); + + EXPECT_NEAR(set.vertices[2], 2, E); + EXPECT_NEAR(set.vertices[3], 1, E); + + EXPECT_NEAR(set.vertices[4], 0.8, E); + EXPECT_NEAR(set.vertices[5], -0.2, E); + + EXPECT_NEAR(center, 0.4, E); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(LFreeSetTest, preprocess_test_2) +{ + int rval = 0; + + double vertices[] = { + 22, 69/7.0, + -3, -11/7.0, + -8, -26/7.0 + }; + + double lattice_points[] = { + -4, -2, + -2, -1, + 7, 3 + }; + + double r[] = { 2/3.0, 2/6.0 }; + double r0, r1; + double value; + + double pre_m[4]; + + struct LFreeSet2D set; + set.f[0] = 2 / 3.0; + set.f[1] = 1 / 6.0; + + rval = LFREE_2D_init(&set, 10, 10, 10); + abort_if(rval, "LFREE_2D_init failed"); + + set.n_vertices = 3; + set.vertices = vertices; + + set.n_lattice_points = 3; + set.lattice_points = lattice_points; + + rval = LFREE_2D_print_set(&set); + abort_if(rval, "LFREE_2D_print_set failed"); + + rval = LFREE_2D_compute_halfspaces(&set); + abort_if(rval, "LFREE_2D_compute_halfspaces failed"); + + rval = LIFTING_2D_bound(set.n_halfspaces, set.halfspaces, r, &value); + abort_if(rval, "LIFTING_2D_bound failed"); + + log_debug("%.6lf\n", value); + +CLEANUP: + if(rval) FAIL(); +} + + +TEST(LFreeSetTest, preprocess_test_4) +{ + int rval = 0; + + double vertices[] = { + -5/2.0, -1, + 17/4.0, 2, + 7/2.0, 2, + -13/4.0, -1 + }; + + double lattice_points[] = { + 2, 1, + -3, -1, + -1, 0, + 4, 2 + }; + + double pre_m[4]; + double center; + + struct LFreeSet2D set; + set.f[0] = 1 / 2.0; + set.f[1] = 1 / 2.0; + + set.n_vertices = 4; + set.vertices = vertices; + + set.n_lattice_points = 4; + set.lattice_points = lattice_points; + + rval = LFREE_2D_preprocess(&set, pre_m, ¢er); + abort_if(rval, "LFREE_2D_preprocess failed"); + + LFREE_2D_print_set(&set); + + EXPECT_NEAR(set.vertices[0], -1, E); + EXPECT_NEAR(set.vertices[1], 1/2.0, E); + + EXPECT_NEAR(set.vertices[2], 1/2.0, E); + EXPECT_NEAR(set.vertices[3], -1/4.0, E); + + EXPECT_NEAR(set.vertices[4], 2, E); + EXPECT_NEAR(set.vertices[5], 1/2.0, E); + + EXPECT_NEAR(set.vertices[6], 1/2.0, E); + EXPECT_NEAR(set.vertices[7], 5/4.0, E); + + EXPECT_NEAR(pre_m[0], -2, E); + EXPECT_NEAR(pre_m[1], -1, E); + EXPECT_NEAR(pre_m[2], 5, E); + EXPECT_NEAR(pre_m[3], 2, E); + + EXPECT_NEAR(center, 0.5, E); + + CLEANUP: + if(rval) FAIL(); +} diff --git a/multirow/CMakeLists.txt b/multirow/CMakeLists.txt new file mode 100644 index 0000000..ab7d940 --- /dev/null +++ b/multirow/CMakeLists.txt @@ -0,0 +1,32 @@ +set(COMMON_SOURCES + src/cg.c + src/double.c + src/geometry.c + src/lfree2d.c + src/lp.c + src/mir.c + src/util.c + src/rational.c + src/stats.c + include/multirow/cg.h + include/multirow/double.h + include/multirow/geometry.h + include/multirow/lfree2d.h + include/multirow/lp.h + include/multirow/rational.h + include/multirow/stats.h + include/multirow/params.h + include/multirow/util.h) + +set(TEST_SOURCES + tests/double-test.cpp + tests/cg-test.cpp + tests/geometry-test.cpp) + +add_library(multirow_static ${COMMON_SOURCES}) +set_target_properties(multirow_static PROPERTIES OUTPUT_NAME lifting) +target_link_libraries(multirow_static ${CPLEX_LIBRARIES}) +target_include_directories (multirow_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(multirow-test.run ${COMMON_SOURCES} ${TEST_SOURCES}) +target_link_libraries(multirow-test.run ${CPLEX_LIBRARIES} gtest_main) diff --git a/multirow/include/multirow/cg.h b/multirow/include/multirow/cg.h new file mode 100644 index 0000000..db2a07a --- /dev/null +++ b/multirow/include/multirow/cg.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MULTIROW_CG_H +#define MULTIROW_CG_H + +#include "lp.h" + +struct CG +{ + struct LP *lp; + struct Row **tableau_rows; + int *cstat; + int *rstat; + double *ub; + double *lb; + int nrows; + int ncols; + char *column_types; + + double last_obj_value; + + double *integral_solution; + double *basic_solution; + double *current_solution; +}; + +typedef int (*SingleRowGeneratorCallback)(const struct Row *row, + char *column_types, + struct Row *cut); + +typedef int (*MultirowGeneratorCallback)(int nrows, + struct Row **rows, + char *column_types, + struct Row *cut); + +int CG_init(struct LP *lp, + char *column_types, + struct CG *cg); + +void CG_free(struct CG *cg); + +int CG_add_single_row_cuts(struct CG *cg, + SingleRowGeneratorCallback generate); + +int CG_add_multirow_cuts(struct CG *cg, + int nrows, + MultirowGeneratorCallback generate); + +int CG_set_integral_solution(struct CG *cg, + double *valid_solution); + +int CG_set_basic_solution(struct CG *cg, + double *basic_solution); + +int CG_extract_rays_from_rows(int nrows, + struct Row **rows, + double *rays, + int *nrays, + int *variable_to_ray, + double *ray_scale, + int *indices, + int *nz); + +int CG_boost_variable(int var, + double factor, + int nrows, + double *rays, + int *variable_to_ray, + double *ray_scale, + int *indices, + int nz); + +int CG_find_ray(int dim, + const double *rays, + int nrays, + const double *r, + int *found, + double *scale, + int *index); + +#endif //MULTIROW_CG_H diff --git a/multirow/include/multirow/double.h b/multirow/include/multirow/double.h new file mode 100644 index 0000000..47987ed --- /dev/null +++ b/multirow/include/multirow/double.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MULTIROW_DOUBLE_H +#define MULTIROW_DOUBLE_H + +#include + +int DOUBLE_sgn(double a); + +int DOUBLE_cmp(double a, + double b); + +int DOUBLE_geq(double a, + double b); + +int DOUBLE_leq(double a, + double b); + +int DOUBLE_eq(double a, + double b); + +int DOUBLE_neq(double a, + double b); + +double DOUBLE_max(double a, + double b); + +int DOUBLE_to_rational(double a, + long max_den, + Rational r); + +#define DOUBLE_iszero(a) (fabs(a) < EPSILON) + +double DOUBLE_random(double min, double max); + +#endif //MULTIROW_DOUBLE_H diff --git a/multirow/include/multirow/geometry.h b/multirow/include/multirow/geometry.h new file mode 100644 index 0000000..ccb09e8 --- /dev/null +++ b/multirow/include/multirow/geometry.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MULTIROW_GEOMETRY_H +#define MULTIROW_GEOMETRY_H + +int scale_vector_to_line(const double *ray1, + const double *ray2, + const double *p, + double *lambda); + +int chull_2d(const double *original_points, + int npoints, + double *vertices, + int *nvertices); + +#endif //MULTIROW_GEOMETRY_H diff --git a/multirow/include/multirow/lfree2d.h b/multirow/include/multirow/lfree2d.h new file mode 100644 index 0000000..f1f6278 --- /dev/null +++ b/multirow/include/multirow/lfree2d.h @@ -0,0 +1,58 @@ + +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LFREE_2D_H +#define LFREE_2D_H + +struct LFreeSet2D +{ + double f[2]; + + int n_vertices; + double *vertices; + + int n_lattice_points; + double *lattice_points; + + int n_halfspaces; + double *halfspaces; +}; + +int LFREE_2D_init(struct LFreeSet2D *set, + int n_vertices, + int n_lattice_points, + int max_n_halfspaces); + +void LFREE_2D_free(struct LFreeSet2D *set); + +int LFREE_2D_read_next(FILE *file, struct LFreeSet2D *set); + +int LFREE_2D_compute_halfspaces(struct LFreeSet2D *set); + +int LFREE_2D_preprocess(struct LFreeSet2D *set, double *m, double *center); + +int LFREE_2D_transform_set(struct LFreeSet2D *set, const double *m); + +int LFREE_2D_print_set(const struct LFreeSet2D *set); + +int LFREE_2D_translate_set(struct LFreeSet2D *set, double dx, double dy); + +int LFREE_2D_get_bounding_box(const struct LFreeSet2D *set, + int *lb, + int *ub); + +#endif //LFREE_2D_H diff --git a/multirow/include/multirow/lp.h b/multirow/include/multirow/lp.h new file mode 100644 index 0000000..0e17628 --- /dev/null +++ b/multirow/include/multirow/lp.h @@ -0,0 +1,157 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _PROJECT_LP_H_ +#define _PROJECT_LP_H_ + +#include +#include "params.h" + +#define MILP_INTEGER 0 +#define MILP_CONTINUOUS 1 +#define MILP_INFINITY CPX_INFBOUND + +struct LP +{ + CPXENVptr cplex_env; + CPXLPptr cplex_lp; +}; + +struct Row +{ + int nz; + int head; + double pi_zero; + double *pi; + int *indices; +}; + +static const int MAX_NAME_LENGTH = 100; + +int LP_open(struct LP *lp); + +int LP_create(struct LP *lp, + const char *name); + +int LP_write(struct LP *lp, + const char *fname); + +void LP_free(struct LP *lp); + +void LP_free_row(struct Row *row); + +int LP_new_row(struct LP *lp, + char sense, + double rhs); + +int LP_new_col(struct LP *lp, + double obj, + double lb, + double ub, + char type); + +int LP_add_row(struct LP *lp, + struct Row *row); + +int LP_add_rows(struct LP *lp, + int newrows, + int newnz, + double *rhs, + char *sense, + int *rmatbeg, + int *rmatind, + double *rmatval); + +int LP_add_cols(struct LP *lp, + int newcols, + int newnz, + double *obj, + int *cmatbeg, + int *cmatind, + double *cmatval, + double *lb, + double *ub); + +int LP_change_bound(struct LP *lp, + int col, + char lower_or_upper, + double bnd); + +int LP_optimize(struct LP *lp, + int *infeasible); + +int LP_get_obj_val(struct LP *lp, + double *obj); + +int LP_get_x(struct LP *lp, + double *x); + +int LP_get_y(struct LP *lp, + double *y); + +int LP_get_num_cols(const struct LP *lp); + +int LP_get_num_rows(const struct LP *lp); + +int LP_read_problem(struct LP *lp, + const char *filename); + +int LP_relax(struct LP *lp); + +int LP_get_column_types(struct LP *lp, + char *column_types); + +int LP_get_tableau(struct LP *lp, + struct Row **rows, + int *cstat, + int *rstat, + double *ub, + double *lb); + +int LP_unflip_row_coefficients(int *cstat, + double *lb, + double *ub, + struct Row *cut); + +void LP_print_row(struct Row *row); + +int LP_flip_row_coefficients(int *cstat, + double *lb, + double *ub, + struct Row *cut); + +int LP_read_solution(struct LP *lp, + char *filename, + double *x); + +void LP_disable_presolve(struct LP *lp); + +int LP_write_solution(struct LP *lp, + char *filename); + +int LP_write_basis(struct LP *lp, + char *filename); + +int LP_read_basis(struct LP *lp, + char *filename); + +int LP_write_sage_file(struct LP *lp, + double *x, + char *filename); + +int LP_change_rhs(struct LP *lp, int index, double value); + +#endif diff --git a/multirow/include/multirow/mir.h b/multirow/include/multirow/mir.h new file mode 100644 index 0000000..4f37b43 --- /dev/null +++ b/multirow/include/multirow/mir.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MULTIROW_MIR_H +#define MULTIROW_MIR_H + +#include "lp.h" + +int MIR_generate_cut(const struct Row *row, + const char *column_types, + struct Row *cut); + +#endif //MULTIROW_MIR_H diff --git a/multirow/include/multirow/params.h b/multirow/include/multirow/params.h new file mode 100644 index 0000000..7a48be7 --- /dev/null +++ b/multirow/include/multirow/params.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PROJECT_PARAMS_H +#define PROJECT_PARAMS_H + +/* + * Error margin for floating point comparisons. + */ +#define EPSILON 1e-5 + +/* + * Available log levels, in decreasing level of verboseness, are: + * + * LOG_LEVEL_VERBOSE + * LOG_LEVEL_DEBUG + * LOG_LEVEL_INFO + * LOG_LEVEL_WARNING + * LOG_LEVEL_ERROR + */ +#define LOG_LEVEL LOG_LEVEL_INFO + +/* + * Maximum bounding-box size for naive algorithm + */ +#define MAX_BOX_SIZE 10000 + +/* + * Maximum number of sets that should be considered + */ +#define MAX_N_SETS 1000 + +/* + * Number of rays that should be generated per set. + */ +#define N_RAYS 100 + +#define ONLY_CUT -1 + +/* + * Time limit for the computation (user time, in seconds). + */ +#define MAX_TOTAL_TIME -1 + +/* + * Time limit for each CPLEX LP or MIP (in seconds). + */ +#define CPLEX_TIMEOUT 1.0 +#define CG_TIMEOUT 900 + +#define MAX_N_RAYS 100 +#define MAX_SELECTED_COMBINATIONS 5000 +#define MAX_SELECTED_ROWS 300 +#define MAX_LATTICE_POINTS 100000 + +/* + * Greedy cut parameters + */ +#define GREEDY_BIG_E 1e3 +#define GREEDY_MAX_GAP 1e-4 + +#define MAX_CUT_DYNAMISM 1e8 +#define INTEGRALITY_THRESHOLD 0.49 + +extern int BOOST_VAR; +extern double BOOST_FACTOR; +extern int DUMP_CUT; +extern int DUMP_CUT_N; + +extern int ENABLE_LIFTING; +extern int MIN_N_ROWS; +extern int MAX_N_ROWS; + +#define ERR_NO_CUT 2 +#define ERR_MIP_TIMEOUT 3 + +#endif //PROJECT_PARAMS_H diff --git a/multirow/include/multirow/rational.h b/multirow/include/multirow/rational.h new file mode 100644 index 0000000..5f0cc49 --- /dev/null +++ b/multirow/include/multirow/rational.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MULTIROW_RATIONAL_H +#define MULTIROW_RATIONAL_H + +struct _rational +{ + long num; + unsigned long den; +}; + +typedef struct _rational Rational[1]; + +long gcd(long a, + long b); + +long lcm(long a, + long b); + +#endif //MULTIROW_RATIONAL_H diff --git a/multirow/include/multirow/stats.h b/multirow/include/multirow/stats.h new file mode 100644 index 0000000..9ab7dcc --- /dev/null +++ b/multirow/include/multirow/stats.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef MULTIROW_STATS_H +#define MULTIROW_STATS_H + +void STATS_init(); + +void STATS_set_input_filename(char *filename); + +void STATS_set_obj_value(double obj); + +void STATS_increment_generated_cuts(); + +void STATS_increment_added_cuts(); + +int STATS_print_yaml(char *filename); + +void STATS_finish_round(); + +#endif diff --git a/multirow/include/multirow/util.h b/multirow/include/multirow/util.h new file mode 100644 index 0000000..e8156c9 --- /dev/null +++ b/multirow/include/multirow/util.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _PROJECT_UTIL_H_ +#define _PROJECT_UTIL_H_ + +#include +#include +#include "params.h" + +void time_printf(const char *fmt, + ...); + +#define LOG_LEVEL_ERROR 10 +#define LOG_LEVEL_WARNING 20 +#define LOG_LEVEL_INFO 30 +#define LOG_LEVEL_DEBUG 40 +#define LOG_LEVEL_VERBOSE 50 + +#define if_error_level if(LOG_LEVEL >= LOG_LEVEL_ERROR) +#define if_warning_level if(LOG_LEVEL >= LOG_LEVEL_WARNING) +#define if_info_level if(LOG_LEVEL < LOG_LEVEL_INFO) +#define if_debug_level if(LOG_LEVEL >= LOG_LEVEL_DEBUG) +#define if_verbose_level if(LOG_LEVEL >= LOG_LEVEL_VERBOSE) + +#if LOG_LEVEL < LOG_LEVEL_VERBOSE +#define log_verbose(...) +#else +#define log_verbose(...) time_printf( __VA_ARGS__) +#endif + +#if LOG_LEVEL < LOG_LEVEL_DEBUG +#define log_debug(...) +#else +#define log_debug(...) time_printf( __VA_ARGS__) +#endif + +#if LOG_LEVEL < LOG_LEVEL_INFO +#define log_info(...) +#else +#define log_info(...) time_printf( __VA_ARGS__) +#endif + +#if LOG_LEVEL < LOG_LEVEL_WARNING +#define log_warn(...) +#else +#define log_warn(...) time_printf( __VA_ARGS__) +#endif + +#if LOG_LEVEL < LOG_LEVEL_ERROR +#define log_error(...) +#else +#define log_error(...) time_printf( __VA_ARGS__) +#endif + +#define abort_if(cond, msg) if(cond) { \ + time_printf(msg " (%d) (%s:%d)\n", rval, __FILE__, __LINE__); \ + rval = 1; goto CLEANUP; } + +#define abort_iff(cond, msg, ...) if(cond) { \ + time_printf(msg " (%d) (%s:%d)\n", __VA_ARGS__, rval, __FILE__, \ + __LINE__); \ + rval = 1; goto CLEANUP; } + +#define UNUSED(x) (void)(x) + +#define min(x,y) ((x) < (y) ? (x) : (y)) +#define sign(x) ((x) > 0 ? 1 : ((x) < 0 ? -1 : 0)) +#define swap(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0) + +double get_user_time(void); + +double get_real_time(); + +double frac(double x); + +extern FILE *LOG_FILE; + +void progress_reset(); +void progress_set_total(long total); +void progress_increment(); +void progress_print(); +void progress_title(char *title); + +#endif diff --git a/multirow/src/cg.c b/multirow/src/cg.c new file mode 100644 index 0000000..3f3bc1f --- /dev/null +++ b/multirow/src/cg.c @@ -0,0 +1,1021 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include +#include + +static double cg_initial_time; + +static int select_rows(struct CG *cg, int *row_selected) +{ + int rval = 0; + + long histogram[100] = {0}; + + for(int i = 0; i < cg->nrows; i++) + { + row_selected[i] = 1; + struct Row *r1 = cg->tableau_rows[i]; + + if(r1->head < 0 || cg->column_types[r1->head] != MILP_INTEGER) + { + row_selected[i] = 0; + continue; + } + + double dist = fabs(0.5 - frac(r1->pi_zero)); + for(int k = 0; k < 100; k++) + if(dist <= (k / 100.0)) + histogram[k]++; + } + + double dist_cutoff = 0.5; + for(int k = 0; k < 100; k++) + { + if(histogram[k] > MAX_SELECTED_ROWS) + { + dist_cutoff = k / 100.0; + break; + } + } + + int selected_count = 0; + for(int i = 0; i < cg->nrows; i++) + { + if(!row_selected[i]) continue; + + struct Row *r1 = cg->tableau_rows[i]; + double dist = fabs(0.5 - frac(r1->pi_zero)); + + if(dist > dist_cutoff || selected_count >= MAX_SELECTED_ROWS) + { + row_selected[i] = 0; + continue; + } + + selected_count++; + } + +CLEANUP: + return rval; +} + +static int next_combination(int n, + int k, + int *array, + int inc_index, + int *finished) +{ + int i; + int rval = 0; + + array[inc_index]++; + *finished = 0; + + for(i = inc_index; i < k; i++) + { + if(array[i] < n - i) + break; + + if(i+1 < k) + array[i+1]++; + else + { + *finished = 1; + return 0; + } + } + + for(int j = 1; j <= i; j++) + array[i - j] = array[i] + j; + + return 0; +} + + +static int ray_norm(int dim, + const double *ray, + double *norm) +{ + *norm = 0; + + for(int i = 0; i < dim; i++) + *norm += fabs(ray[i]); + + return 0; +} + + +/* + * Checks whether two rays are parallel. Also returns the scaling factor, + * in case they are. + */ +static int check_rays_parallel(int dim, + const double *r1, + const double *r2, + int *match, + double *scale) +{ + int rval = 0; + + *match = 1; + + double r1_norm, r2_norm; + int iszero_r1, iszero_r2; + + rval = ray_norm(dim, r1, &r1_norm); + abort_if(rval, "ray_norm failed"); + + rval = ray_norm(dim, r2, &r2_norm); + abort_if(rval, "ray_norm failed"); + + iszero_r1 = DOUBLE_iszero(r1_norm); + iszero_r2 = DOUBLE_iszero(r2_norm); + + if (iszero_r1 || iszero_r2) + { + *match = iszero_r1 && iszero_r2; + } + else + { + for(int i = 0; i < dim; i++) + { + if(DOUBLE_sgn(r1[i]) != DOUBLE_sgn(r2[i]) || + DOUBLE_neq(r1[i] * r2_norm, r2[i] * r1_norm)) + { + *match = 0; + break; + } + + log_verbose(" %.12lf equals %.12lf\n", r1[i] * r2_norm, r2[i] * r1_norm); + } + } + + (*scale) = r1_norm / r2_norm; + +CLEANUP: + return rval; +} + + +static void print_row(const struct CG *cg, + const struct Row *row) +{ + double *x = cg->integral_solution; + double *y = cg->basic_solution; + + time_printf("Row:\n"); + for (int i = 0; i < row->nz; i++) + { +// if(DOUBLE_iszero(x[row->indices[i]])) continue; + + time_printf(" %12.6lf x%-5d", row->pi[i], row->indices[i]); + printf(" %12.6lf", x[row->indices[i]]); + printf(" %12.6lf\n", y[row->indices[i]]); + } + time_printf(" <= %20.6lf [%d]\n", row->pi_zero, row->head); +} + + +static int evaluate_row_pair(const struct Row *row1, + const struct Row *row2, + double *score) +{ + int rval = 0; + + int i1 = 0; + int i2 = 0; + int hit = 0; + + while (i1 < row1->nz || i2 < row2->nz) + { + int idx1 = INT_MAX; + int idx2 = INT_MAX; + + if (i1 < row1->nz) + idx1 = row1->indices[i1]; + + if (i2 < row2->nz) + idx2 = row2->indices[i2]; + + if(idx1 == idx2) hit++; + + int idx_min = min(idx1, idx2); + + if (idx1 == idx_min) + i1++; + + if (idx2 == idx_min) + i2++; + } + + *score = (hit * 2.0) / (row1->nz + row2->nz); + + CLEANUP: + return rval; +} + +static double replace_x(const double *pi, + const int *indices, + int nz, + const double *x) +{ + double lhs = 0; + + for (int i = 0; i < nz; i++) + { + double pii = pi[i]; + int idx = indices[i]; + + if (!DOUBLE_iszero(pii) && !DOUBLE_iszero(x[idx])) + log_verbose(" %12.8lf * %12.8lf (x%d)\n", pii, x[idx], idx); + + lhs += pii * x[idx]; + } + + return lhs; +} + + +static int copy_solution(struct CG *cg, + double *from, + double **to) +{ + int rval = 0; + int ncols = LP_get_num_cols(cg->lp); + + if (!*to) + { + *to = (double *) malloc(ncols * sizeof(double)); + abort_if(!*to, "could not allocate cg->integral_solution"); + } + + memcpy(*to, from, sizeof(double) * ncols); + +CLEANUP: + return rval; +} + + +static int check_cut(struct CG *cg, + struct Row *cut) +{ + int rval = 0; + + if_verbose_level + { + time_printf("Checking cut:\n"); + for (int i = 0; i < cut->nz; i++) + { + if(DOUBLE_iszero(cg->integral_solution[cut->indices[i]])) continue; + time_printf(" %12.8lf x%d", cut->pi[i], cut->indices[i]); + if (cg->integral_solution) + printf(" (=%12.8lf)", cg->integral_solution[cut->indices[i]]); + printf("\n"); + } + time_printf(" <= %.4lf\n", cut->pi_zero); + } + + if (cg->basic_solution) + { + log_verbose("Basic solution check:\n"); + + double lhs = replace_x(cut->pi, cut->indices, cut->nz, + cg->basic_solution); + + log_verbose(" %.8lf > %.8lf\n", lhs, cut->pi_zero); + abort_iff(!DOUBLE_geq(lhs, cut->pi_zero), "Cut fails to cut " + "basic solution: %12.8lf < %12.8lf", lhs, cut->pi_zero); + } + + if (cg->integral_solution) + { + log_verbose("Integral solution check:\n"); + + double lhs = replace_x(cut->pi, cut->indices, cut->nz, + cg->integral_solution); + + log_verbose(" %.8lf <= %.8lf\n", lhs, cut->pi_zero); + abort_iff(!DOUBLE_leq(lhs, cut->pi_zero), "Cut cuts off known integral " + "solution: %12.8lf >= %12.8lf", lhs, cut->pi_zero); + } + +CLEANUP: + return rval; +} + + +static int add_cut(struct CG *cg, + struct Row *cut, + int *ignored) +{ + int rval; + double *x = 0; + double lhs; + + rval = LP_unflip_row_coefficients(cg->cstat, cg->lb, cg->ub, cut); + abort_if(rval, "LP_unflip_row_coefficients failed"); + + rval = check_cut(cg, cut); + abort_if(rval, "check_cut failed"); + + lhs = replace_x(cut->pi, cut->indices, cut->nz, + cg->current_solution); + + *ignored = 0; + STATS_increment_generated_cuts(); + + if(DOUBLE_leq(lhs, cut->pi_zero)) + { + log_verbose("Ignoring cut (%12.8lf <= %12.8lf)\n", lhs, cut->pi_zero); + *ignored = 1; + } + else + { + rval = LP_add_row(cg->lp, cut); + abort_if(rval, "LP_add_row failed"); + + int infeasible; + log_verbose("Reoptimizing...\n"); + rval = LP_optimize(cg->lp, &infeasible); + abort_if(rval, "LP_optimize failed"); + + double obj; + rval = LP_get_obj_val(cg->lp, &obj); + abort_if(rval, "LP_get_obj_val failed"); + + if(DOUBLE_neq(obj, cg->last_obj_value)) + log_info(" opt = %lf\n", obj); + + cg->last_obj_value = obj; + + x = (double*) malloc(cg->ncols * sizeof(double)); + abort_if(!x, "could not allocate x"); + + rval = LP_get_x(cg->lp, x); + abort_if(rval, "LP_get_x failed"); + + rval = copy_solution(cg, x, &cg->current_solution); + abort_if(rval, "copy_solution failed"); + + STATS_increment_added_cuts(); + } + +CLEANUP: + if(x) free(x); + return rval; +} + +#ifndef TEST_SOURCE + +/* + * Looks for a certain ray at an array of rays. Matches rays that are + * parallel to the given one. Returns whether a matching ray was found, + * the index of the matching ray and the scaling factor. + */ +int CG_find_ray(int dim, + const double *rays, + int nrays, + const double *r, + int *found, + double *scale, + int *index) +{ + *found = 0; + + for (int i = 0; i < nrays; i++) + { + int match; + check_rays_parallel(dim, r, &rays[dim * i], &match, scale); + + if (match) + { + *index = i; + *found = 1; + goto CLEANUP; + } + } + +CLEANUP: + return 0; +} + +int CG_extract_rays_from_rows(int nrows, + struct Row **rows, + double *rays, + int *nrays, + int *variable_to_ray, + double *ray_scale, + int *indices, + int *nz) +{ + int rval = 0; + + (*nz) = 0; + (*nrays) = 0; + + int *i = 0; + int *idx = 0; + + + i = (int*) malloc(nrows * sizeof(int)); + idx = (int*) malloc(nrows * sizeof(int)); + abort_if(!i, "could not allocate i"); + abort_if(!idx, "could not allocate idx"); + + for(int j = 0; j < nrows; j++) + i[j] = 0; + + while (1) + { + double *r = &rays[nrows * (*nrays)]; + + int idx_min = INT_MAX; + + for(int j = 0; j < nrows; j++) + { + r[j] = 0.0; + + if(i[j] < rows[j]->nz) + idx[j] = rows[j]->indices[i[j]]; + else + idx[j] = INT_MAX; + + idx_min = min(idx_min, idx[j]); + } + + if(idx_min == INT_MAX) + break; + + for(int j = 0; j < nrows; j++) + { + if(idx[j] > idx_min) continue; + r[j] = -rows[j]->pi[i[j]]; + i[j]++; + } + + for(int j = 0; j < nrows; j++) + if(idx_min == rows[j]->head) + goto NEXT_RAY; + + int found; + double scale; + int ray_index; + + + log_verbose(" extracted ray (%d):\n", idx_min); + for(int j=0; j < nrows; j++) + log_verbose(" r[%d] = %.12lf\n", j, r[j]); + + rval = CG_find_ray(nrows, rays, *nrays, r, &found, &scale, &ray_index); + abort_if(rval, "CG_find_ray failed"); + + if (!found) + { + log_verbose(" ray is new\n"); + scale = 1.0; + ray_index = (*nrays)++; + } + else + { + log_verbose(" ray equals:\n"); + double *q = &rays[ray_index * nrows]; + for(int j=0; j < nrows; j++) + log_verbose(" r[%d] = %.12lf\n", j, q[j]); + } + + ray_scale[(*nz)] = scale; + indices[(*nz)] = idx_min; + variable_to_ray[(*nz)] = ray_index; + (*nz)++; + + NEXT_RAY: + ; + } + + for(int j = 0; j < *nrays; j++) + { + double *r = &rays[nrows * j]; + + double max_scale = 0.0; + + for(int k = 0; k < *nz; k++) + { + if(variable_to_ray[k] != j) continue; + if(ray_scale[k] < max_scale) continue; + max_scale = ray_scale[k]; + } + + abort_if(max_scale == 0.0, "max_scale is zero"); + + for(int k = 0; k < *nz; k++) + if(variable_to_ray[k] == j) ray_scale[k] /= max_scale; + + for(int k = 0; k < nrows; k++) + r[k] *= max_scale; + } + +CLEANUP: + if(idx) free(idx); + if(i) free(i); + return rval; +} + + +int CG_init(struct LP *lp, + char *column_types, + struct CG *cg) +{ + int rval = 0; + + cg->lp = lp; + cg->column_types = column_types; + + cg->tableau_rows = 0; + cg->cstat = 0; + cg->rstat = 0; + cg->ub = 0; + cg->lb = 0; + cg->last_obj_value = NAN; + + cg->integral_solution = 0; + cg->basic_solution = 0; + cg->current_solution = 0; + + int nrows = LP_get_num_rows(lp); + int ncols = LP_get_num_cols(lp); + + cg->nrows = nrows; + cg->ncols = ncols; + + cg->cstat = (int *) malloc(ncols * sizeof(int)); + cg->rstat = (int *) malloc(nrows * sizeof(int)); + cg->ub = (double *) malloc(ncols * sizeof(double)); + cg->lb = (double *) malloc(ncols * sizeof(double)); + + abort_if(!cg->cstat, "could not allocate cg->cstat"); + abort_if(!cg->rstat, "could not allocate cg->rstat"); + abort_if(!cg->ub, "could not allocate cg->ub"); + abort_if(!cg->lb, "could not allocate cg->lb"); + + cg->tableau_rows = (struct Row **) malloc(nrows * sizeof(struct Row *)); + abort_if(!cg->tableau_rows, "could not allocate cg->tableau_rows"); + + rval = LP_get_tableau(lp, cg->tableau_rows, cg->cstat, cg->rstat, cg->ub, + cg->lb); + abort_if(rval, "LP_get_tableau failed"); + + cg_initial_time = get_user_time(); + +CLEANUP: + return rval; +} + + +void CG_free(struct CG *cg) +{ + if (!cg) return; + if (cg->ub) free(cg->ub); + if (cg->lb) free(cg->lb); + if (cg->cstat) free(cg->cstat); + if (cg->rstat) free(cg->rstat); + + if (cg->integral_solution) free(cg->integral_solution); + if (cg->basic_solution) free(cg->basic_solution); + if (cg->current_solution) free(cg->current_solution); + + if (cg->tableau_rows) + { + for (int i = 0; i < cg->nrows; i++) + LP_free_row(cg->tableau_rows[i]); + free(cg->tableau_rows); + } + + free(cg); +} + + +int CG_add_single_row_cuts(struct CG *cg, + SingleRowGeneratorCallback generate) +{ + int rval = 0; + + for (int i = 0; i < cg->nrows; i++) + { + struct Row *row = cg->tableau_rows[i]; + + if (frac(row->pi_zero) < EPSILON) + continue; + + if (cg->column_types[row->head] != MILP_INTEGER) + continue; + + log_verbose("Generating cut %d...\n", i); + + struct Row *cut = 0; + + cut = (struct Row *) malloc(sizeof(struct Row)); + abort_if(!cut, "could not allocate cut"); + + rval = generate(row, cg->column_types, cut); + abort_if(rval, "generate failed"); + + int ignored; + rval = add_cut(cg, cut, &ignored); + abort_if(rval, "add_cut failed"); + + LP_free_row(cut); + } + +CLEANUP: + return rval; +} + +int estimate_multirow_cut_count(struct CG *cg, + int nrows, + int *row_selected, + int *row_affinity, + long *total_count, + double score_cutoff) +{ + int rval = 0; + int *row_indices = 0; + int finished = 0; + + *total_count = 0; + + row_indices = (int*) malloc(nrows * sizeof(int)); + abort_if(!row_indices, "could not allocate row_indices"); + + for(int i = 0; i < nrows; i++) + row_indices[i] = nrows - i - 1; + + for(int i = 0; i < cg->nrows * cg->nrows; i++) + row_affinity[i] = -1; + + do + { + int inc_index = 0; + int is_rhs_integer = 1; + int valid_combination = 1; + + for(int i = 0; i < nrows; i++) + { + struct Row *r = cg->tableau_rows[row_indices[i]]; + + if(!row_selected[row_indices[i]]) + { + valid_combination = 0; + inc_index = i; + } + + double df = fabs(frac(r->pi_zero) - 0.5); + if(df < INTEGRALITY_THRESHOLD) is_rhs_integer = 0; + } + + if(is_rhs_integer) valid_combination = 0; + + for(int i = 0; valid_combination && i < nrows; i++) + { + for(int j = i+1; valid_combination && j < nrows; j++) + { + int i1 = row_indices[i]; + int i2 = row_indices[j]; + if(i2 < i1) swap(i1, i2, int); + + int k = cg->nrows * i1 + i2; + + if(row_affinity[k] < 0) + { + struct Row *row1 = cg->tableau_rows[i1]; + struct Row *row2 = cg->tableau_rows[i2]; + double score; + + rval = evaluate_row_pair(row1, row2, &score); + abort_if(rval, "evaluate_row_pair failed"); + + row_affinity[k] = 1; + if(score < score_cutoff) row_affinity[k] = 0; + } + + if(!row_affinity[k]) + { + valid_combination = 0; + goto NEXT_COMBINATION; + } + } + } + + if(valid_combination) + { + (*total_count)++; + } + + NEXT_COMBINATION: + next_combination(cg->nrows, nrows, row_indices, inc_index, &finished); + } + while(!finished); + +CLEANUP: + if(row_indices) free(row_indices); + return rval; +} + +int cut_dynamism(struct Row *cut, double *dynamism) +{ + double largest = -INFINITY; + double smallest = INFINITY; + + for (int i = 0; i < cut->nz; i++) + { + smallest = fmin(fabs(cut->pi[i]), smallest); + largest = fmax(fabs(cut->pi[i]), largest); + } + + *dynamism = largest / smallest; + + return 0; +} + + +int CG_add_multirow_cuts(struct CG *cg, + int nrows, + MultirowGeneratorCallback generate) +{ + int rval = 0; + int *row_indices = 0; + struct Row **rows = 0; + int *row_affinity = 0; + int *row_selected = 0; + + int finished; + long count = 0; + long total_count = 0; + + row_indices = (int*) malloc(nrows * sizeof(int)); + rows = (struct Row **) malloc(nrows * sizeof(struct Row *)); + row_affinity = (int*) malloc((cg->nrows * cg->nrows) * sizeof(int)); + row_selected = (int*) malloc(cg->nrows * sizeof(int)); + + abort_if(!row_indices, "could not allocate row_indices"); + abort_if(!rows, "could not allocate rows"); + abort_if(!row_affinity, "could not allocate row_affinity"); + abort_if(!row_selected, "could not allocate row_selected"); + + rval = select_rows(cg, row_selected); + abort_if(rval, "select_rows failed"); + + log_info(" Finding combinations...\n"); + for(double cutoff = 0.05; cutoff <= 1.0; cutoff += 0.05) + { + double cg_current_time = get_user_time() - cg_initial_time; + if(cg_current_time > CG_TIMEOUT) break; + + rval = estimate_multirow_cut_count(cg, nrows, row_selected, row_affinity, &total_count, cutoff); + abort_if(rval, "estimate_two_row_cuts_count failed"); + + log_debug(" %8d combinations [%.2lf]\n", total_count, cutoff); + + if(total_count < MAX_SELECTED_COMBINATIONS) + { + log_info(" %8d combinations [%.2lf]\n", total_count, cutoff); + break; + } + } + + for(int i = 0; i < nrows; i++) + row_indices[i] = nrows - i - 1; + + total_count = min(total_count, MAX_SELECTED_COMBINATIONS); + + progress_set_total(total_count); + progress_reset(); + + do + { + double cg_current_time = get_user_time() - cg_initial_time; + if(cg_current_time > CG_TIMEOUT) break; + + int inc_index = 0; + int is_rhs_integer = 1; + int valid_combination = 1; + + for(int i = 0; i < nrows; i++) + { + rows[i] = cg->tableau_rows[row_indices[i]]; + + if(!row_selected[row_indices[i]]) + { + valid_combination = 0; + inc_index = i; + } + + double df = fabs(frac(rows[i]->pi_zero) - 0.5); + if(df < INTEGRALITY_THRESHOLD) is_rhs_integer = 0; + } + + if(is_rhs_integer) valid_combination = 0; + + for(int i = 0; valid_combination && i < nrows; i++) + { + + for(int j = i+1; valid_combination && j < nrows; j++) + { + int i1 = row_indices[i]; + int i2 = row_indices[j]; + if(i2 < i1) swap(i1, i2, int); + + int k = cg->nrows * i1 + i2; + + abort_if(row_affinity[k] < 0, "row_affinity not computed"); + if(!row_affinity[k]) valid_combination = 0; + + if_verbose_level + { + struct Row *row1 = cg->tableau_rows[i1]; + struct Row *row2 = cg->tableau_rows[i2]; + double score; + + rval = evaluate_row_pair(row1, row2, &score); + abort_if(rval, "evaluate_row_pair failed"); + + log_verbose("%4d %4d %.2lf\n", i1, i2, score); + } + } + } + + if(valid_combination) + { + count++; + if(count > MAX_SELECTED_COMBINATIONS) + { + log_info(" maximum number of combinations reached. stopping.\n"); + break; + } + + if_debug_level + if(ONLY_CUT > 0 && count != ONLY_CUT) + goto NEXT_COMBINATION; + + if_debug_level + { + time_printf("Generating cut %d from [ ", count); + for(int i = 0; i < nrows; i++) + printf("%d ", row_indices[i]); + printf("]...\n"); + } + + if(LOG_LEVEL == LOG_LEVEL_INFO) + { + progress_print(); + progress_increment(); + } + + struct Row *cut = 0; + + cut = (struct Row*) malloc(sizeof(struct Row)); + abort_if(!cut, "could not allocate cut"); + + cut->pi = 0; + cut->indices = 0; + + double initial_time = get_user_time(); + + rval = generate(nrows, rows, cg->column_types, cut); + if(rval == ERR_NO_CUT) + { + rval = 0; + log_verbose("combination does not yield cut\n"); + LP_free_row(cut); + goto NEXT_COMBINATION; + } + else abort_iff(rval, "generate failed (cut %d)", count); + + double elapsed_time = get_user_time() - initial_time; + log_debug(" generate: %.2lf ms\n", elapsed_time * 1000); + + double dynamism; + rval = cut_dynamism(cut, &dynamism); + abort_if(rval, "cut_dynamism failed"); + + if(dynamism > MAX_CUT_DYNAMISM) + { + log_debug("Discarding cut (dynamism=%.2lf)\n", dynamism); + LP_free_row(cut); + goto NEXT_COMBINATION; + } + + int ignored; + rval = add_cut(cg, cut, &ignored); + if(rval) + { + log_warn("invalid cut skipped (cut %d)\n", count); + rval = 0; + + LP_free_row(cut); + goto NEXT_COMBINATION; + + } + + if_debug_level if(!ignored) + { + DUMP_CUT = 1; + generate(nrows, rows, cg->column_types, cut); + DUMP_CUT = 0; + } + + LP_free_row(cut); + } + + NEXT_COMBINATION: + next_combination(cg->nrows, nrows, row_indices, inc_index, &finished); + } + while(!finished); + +CLEANUP: + if(row_selected) free(row_selected); + if(row_affinity) free(row_affinity); + if(row_indices) free(row_indices); + if(rows) free(rows); + return rval; +} + + +int CG_set_integral_solution(struct CG *cg, + double *valid_solution) +{ + return copy_solution(cg, valid_solution, &cg->integral_solution); +} + + +int CG_set_basic_solution(struct CG *cg, + double *basic_solution) +{ + int rval = 0; + + rval = copy_solution(cg, basic_solution, &cg->basic_solution); + abort_if(rval, "copy_solution failed"); + + rval = copy_solution(cg, basic_solution, &cg->current_solution); + abort_if(rval, "copy_solution failed"); + +CLEANUP: + return rval; +} + +int CG_boost_variable(int var, + double factor, + int nrows, + double *rays, + int *variable_to_ray, + double *ray_scale, + int *indices, + int nz) +{ + int rval = 0; + + int selected_ray = -1; + for(int j = 0; j < nz; j++) + { + if(indices[j] == var) + { + selected_ray = variable_to_ray[j]; + break; + } + } + + if(selected_ray < 0) goto CLEANUP; + + double *r = &rays[nrows * selected_ray]; + for(int k = 0; k < nrows; k++) + r[k] *= factor; + + for(int j = 0; j < nz; j++) + { + if(variable_to_ray[j] == selected_ray) + ray_scale[j] /= factor; + } + +CLEANUP: + return rval; +} +#endif // TEST_SOURCE diff --git a/multirow/src/double.c b/multirow/src/double.c new file mode 100644 index 0000000..89f92a6 --- /dev/null +++ b/multirow/src/double.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include + +int DOUBLE_sgn(double a) +{ + return (a > 0 ? 1 : (a < 0 ? -1 : 0)); +} + +int DOUBLE_cmp(double a, + double b) +{ + if(isnan(a) || isnan(b)) return 1; + + double size = min(fabs(a) + fabs(b), DBL_MAX); + double zero_cutoff = EPSILON * size; + double diff = (a-b); + + // Do not use relative epsilon for numbers close to zero + if(fabs(a) < EPSILON || fabs(b) < EPSILON) zero_cutoff = EPSILON; + + return (fabs(diff) < zero_cutoff ? 0 : DOUBLE_sgn(diff)); +} + +int DOUBLE_geq(double a, + double b) +{ + return DOUBLE_cmp(a, b) >= 0; +} + +int DOUBLE_leq(double a, + double b) +{ + return DOUBLE_cmp(a, b) <= 0; +} + +int DOUBLE_eq(double a, + double b) +{ + return DOUBLE_cmp(a, b) == 0; +} + +int DOUBLE_neq(double a, + double b) +{ + return DOUBLE_cmp(a, b) != 0; +} + +double DOUBLE_max(double a, + double b) +{ + return (DOUBLE_cmp(a,b) > 0 ? a : b); +} + +double DOUBLE_random(double min, double max) +{ + return min + ((double) rand() / ((double) RAND_MAX / (max - min))); +} + +int DOUBLE_to_rational(double a, + long max_den, + Rational r) +{ + int rval = 0; + + abort_if(!isfinite(a), "a must be finite"); + abort_if(max_den < 1, "max_den must be positive"); + + long shift = floor(a); + a = frac(a); + + long n1 = 0, d1 = 1; + long n2 = 1, d2 = 1; + + log_verbose("a=%.8lf\n", a); + + while(d1 + d2 < max_den) + { + double mid = ((double) (n1 + n2) / (d1 + d2)); + log_verbose(" d1=%ld %d\n", n1, d1); + log_verbose(" d2=%ld %d\n", n2, d2); + log_verbose(" mid=%.8lf\n", mid); + + + if(a > mid) + { + n1 = n1 + n2; + d1 = d1 + d2; + } + else + { + n2 = n1 + n2; + d2 = d1 + d2; + } + } + + double err1 = fabs(a - ((double) n1 / d1)); + double err2 = fabs(a - ((double) n2 / d2)); + + if(err1 < err2) + { + r->num = n1; + r->den = d1; + } + else + { + r->num = n2; + r->den = d2; + } + + r->num += ((r->den) * shift); + +CLEANUP: + return rval; +} diff --git a/multirow/src/geometry.c b/multirow/src/geometry.c new file mode 100644 index 0000000..abbd5bf --- /dev/null +++ b/multirow/src/geometry.c @@ -0,0 +1,190 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +struct SortPair +{ + int index; + void *data; +}; + +#if defined(__APPLE__) +static int _qsort_cmp_angle_distance(void *p_orig, + const void *p1, + const void *p2) +#else +static int _qsort_cmp_angle_distance(const void *p1, + const void *p2, + void *p_orig) +#endif +{ + double *orig = (double *) p_orig; + double *r1 = (double*) p1; + double *r2 = (double*) p2; + + double norm1 = fabs(r1[0] - orig[0]) + fabs(r1[1] - orig[1]); + double norm2 = fabs(r2[0] - orig[0]) + fabs(r2[1] - orig[1]); + + double angle1 = atan2(r1[0] - orig[0], r1[1] - orig[1]); + double angle2 = atan2(r2[0] - orig[0], r2[1] - orig[1]); + + if(DOUBLE_neq(angle1, angle2)) + return sign(angle2 - angle1); + else + return sign(norm1 - norm2); +} + +static double turn_direction(const double *r1, + const double *r2, + const double *r3) +{ + return (r2[0]-r1[0]) * (r3[1]-r1[1]) - (r2[1]-r1[1]) * (r3[0]-r1[0]); +} + +int chull_2d(const double *original_points, + int npoints, + double *vertices, + int *nvertices) +{ + int rval = 0; + int pivot_idx; + double pivot[2]; + + int *stack = 0; + int stack_length = 0; + double *points = 0; + + stack = (int*) malloc(npoints * sizeof(int)); + points = (double*) malloc(2 * npoints * sizeof(double)); + abort_if(!stack, "could not allocate stack"); + abort_if(!points, "could not allocate points"); + + memcpy(points, original_points, 2 * npoints * sizeof(double)); + + // find point with lowest y-coordinate + pivot_idx = 0; + memcpy(pivot, points, 2 * sizeof(double)); + + for (int i = 0; i < npoints; i++) + { + double *point = &points[2*i]; + + if(point[1] > pivot[1]) continue; + if(point[1] == pivot[1] && point[0] > pivot[0]) continue; + + memcpy(pivot, point, 2 * sizeof(double)); + pivot_idx = i; + } + + // remove pivot + swap(points[2*(npoints-1)], points[2*pivot_idx], double); + swap(points[2*(npoints-1)+1], points[2*pivot_idx+1], double); + npoints--; + + // sort points counterclockwise +#if defined(__APPLE__) + qsort_r(points, npoints, 2 * sizeof(double), pivot, _qsort_cmp_angle_distance); +#else + qsort_r(points, npoints, 2 * sizeof(double), _qsort_cmp_angle_distance, + pivot); +#endif + + // adds pivot and first point to the stack + stack[stack_length++] = npoints; + stack[stack_length++] = 0; + + int k = 1; + while(k < npoints) + { + double *p0 = &points[2*stack[stack_length-2]]; + double *p1 = &points[2*stack[stack_length-1]]; + double *p2 = &points[2*k]; + + double t = turn_direction(p0, p1, p2); + + if(DOUBLE_leq(t, 0)) + stack_length--; + + if(DOUBLE_geq(t, 0)) + { + stack[stack_length++] = k; + k++; + } + } + + for(int i = 0; i < stack_length; i++) + { + int j = stack_length - i - 1; + vertices[2*j] = points[2*stack[i]]; + vertices[2*j+1] = points[2*stack[i]+1]; + } + + *nvertices = stack_length; + + CLEANUP: + if(points) free(points); + if(stack) free(stack); + return rval; +} + +/* + * Find lambda such that lambda * p lies on the line connecting ray1 to ray2 + */ +int scale_vector_to_line(const double *ray1, + const double *ray2, + const double *p, + double *lambda) +{ + int rval = 0; + + double a = ray1[0], b = ray1[1]; + double c = ray2[0], d = ray2[1]; + + double norm1 = fabs(ray1[0]) + fabs(ray1[1]); + double norm2 = fabs(ray2[0]) + fabs(ray2[1]); + + log_verbose("r1=%12.8e %12.8e\n", ray1[0], ray1[1]); + log_verbose("r2=%12.8e %12.8e\n", ray2[0], ray2[1]); + log_verbose("p=%12.8e %12.8e\n\n", p[0], p[1]); + + if (norm1 > norm2) + { + swap(a, c, double); + swap(b, d, double); + swap(norm1, norm2, double); + } + + if (DOUBLE_iszero(norm1 / norm2)) + { + *lambda = (b * c - a * d) / (c * p[1] - d * p[0]); + } + else + { + *lambda = b * c - a * d; + *lambda /= p[0] * (b - d) - p[1] * (a - c); + } + +CLEANUP: + return rval; +} diff --git a/multirow/src/lfree2d.c b/multirow/src/lfree2d.c new file mode 100644 index 0000000..490c6a0 --- /dev/null +++ b/multirow/src/lfree2d.c @@ -0,0 +1,514 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _XOPEN_SOURCE 700 + +#include +#include + +#include +#include +#include +#include + +int LFREE_2D_init(struct LFreeSet2D *set, + int n_vertices, + int n_lattice_points, + int n_halfspaces) +{ + int rval = 0; + + set->vertices = (double *) malloc(2 * n_vertices * sizeof(double)); + set->lattice_points = (double *) malloc( 2 * n_lattice_points * sizeof(double)); + set->halfspaces = (double *) malloc(2 * n_halfspaces * sizeof(double)); + + abort_if(!set->vertices, "could not allocate set->vertices"); + abort_if(!set->lattice_points, "could not allocate set->lattice_points"); + abort_if(!set->halfspaces, "could not allocate set->halfspaces"); + +CLEANUP: + return rval; +} + +void LFREE_2D_free(struct LFreeSet2D *set) +{ + if(set->vertices) free(set->vertices); + if(set->lattice_points) free(set->lattice_points); + if(set->halfspaces) free(set->halfspaces); +} + +/** + * Computes the bounding box around the given set. + * + * @param[in] set the set whose bounding box should be computed + * @param[out] lb two-dimensional vector representing lower-left corner + * of the bounding box + * @param[out] ub two-dimensional vector representing upper-right corner + * of the bounding box + */ +int LFREE_2D_get_bounding_box(const struct LFreeSet2D *set, + int *lb, + int *ub) +{ + int rval = 0; + + ub[0] = ub[1] = INT_MIN; + lb[0] = lb[1] = INT_MAX; + + for (int i = 0; i < set->n_vertices; i++) + { + double *vertex = &set->vertices[2 * i]; + + ub[0] = (int) fmax(ub[0], ceil(vertex[0])); + ub[1] = (int) fmax(ub[1], ceil(vertex[1])); + + lb[0] = (int) fmin(lb[0], floor(vertex[0])); + lb[1] = (int) fmin(lb[1], floor(vertex[1])); + } + +CLEANUP: + return rval; +} + +int LFREE_2D_print_set(const struct LFreeSet2D *set) +{ + int rval = 0; + + log_debug(" f=%12.6lf %12.6lf\n", set->f[0], set->f[1]); + + for (int i = 0; i < set->n_vertices; i++) + { + double *vertex = &set->vertices[2 * i]; + log_debug(" v%-3d=%12.6lf %12.6lf\n", i, vertex[0], vertex[1]); + } + + for (int i = 0; i < set->n_lattice_points; i++) + { + double *lattice_point = &set->lattice_points[2 * i]; + log_debug(" t%-3d=%12.6lf %12.6lf\n", i, lattice_point[0], lattice_point[1]); + } + + for(int i = 0; i < set->n_halfspaces; i++) + { + double *halfspace = &set->halfspaces[2 * i]; + log_debug(" h%-3d=%12.6lf %12.6lf\n", i, halfspace[0], halfspace[1]); + } + +CLEANUP: + return rval; +} + +/** + * Computes the halfspace representation for a given set. + * + * To construct a LFreeSet2D, it is only necessary to specify + * the vertices and the interior point. This function computes + * the halfspace representation. It assumes that the vertices are + * sorted in either clockwise or counter-clockwise order. + * The set is modified in-place. + * + * @param set the set whose halfspace representation should + * be computed + * + */ +int LFREE_2D_compute_halfspaces(struct LFreeSet2D *set) +{ + int rval = 0; + + int k = 0; + int expected_sgn = 0; + double *f = set->f; + + for (int i0 = 0; i0 < set->n_vertices; i0++) + { + int i1 = (i0 + 1) % set->n_vertices; + int i2 = (i0 + 2) % set->n_vertices; + + double *v0 = &set->vertices[2 * i0]; + double *v1 = &set->vertices[2 * i1]; + double *v2 = &set->vertices[2 * i2]; + + double x0 = v0[0], y0 = v0[1]; + double x1 = v1[0], y1 = v1[1]; + double x2 = v2[0], y2 = v2[1]; + + int sgn = DOUBLE_sgn( + -x1 * y0 + x2 * y0 + x0 * y1 - x2 * y1 - x0 * y2 + x1 * y2); + + abort_if(sgn == 0, "vertices should not be aligned"); + if(expected_sgn == 0) expected_sgn = sgn; + abort_if(expected_sgn != sgn, "wrong orientation of vertices"); + + double *halfspace = &set->halfspaces[2 * (k++)]; + + halfspace[0] = y0 - y1; + halfspace[1] = x1 - x0; + + double rhs = y0 * x1 - x0 * y1; + rhs -= (y0 - y1) * f[0]; + rhs -= (x1 - x0) * f[1]; + + halfspace[0] /= rhs; + halfspace[1] /= rhs; + } + + set->n_halfspaces = k; + +CLEANUP: + return rval; +} + +/** + * Translates a given set by a certain amount. + * + * @param set the set to be translated + * @param dx offset to be applied to the first component + * @param dy offset to be applied to the second component + */ +int LFREE_2D_translate_set(struct LFreeSet2D *set, double dx, double dy) +{ + set->f[0] += dx; + set->f[1] += dy; + + for (int i = 0; i < set->n_vertices; i++) + { + double *vertex = &set->vertices[2 * i]; + vertex[0] += dx; + vertex[1] += dy; + } + + for (int i = 0; i < set->n_lattice_points; i++) + { + double *lattice_point = &set->lattice_points[2 * i]; + lattice_point[0] += dx; + lattice_point[1] += dy; + } + + return 0; +} + +/** + * Computes the inverse of the given matrix and left-multiplies it + * by the given ray. The matrix is given by [[x1,y1],[x2,y2]]. The + * ray is modified in-place. + */ +static void apply_inverse(double x1, + double y1, + double x2, + double y2, + double *ray) +{ + double a = ray[0]; + double b = ray[1]; + double det = x2 * y1 - x1 * y2; + + ray[0] = (b * x2 - a * y2) / det; + ray[1] = (a * y1 - b * x1) / det; +} + +/** + * Applies a unimodular affine transformation to the set, making + * lattice points 1 and 2 orthogonal. Returns the transformation + * applied. + * + * @param[in] set the set to be transformed + * @param[out] m two-by-two matrix representing the transformation + * applied + */ +static int make_t1_t2_orthogonal(struct LFreeSet2D *set, double *m) +{ + int rval = 0; + + double x1 = set->lattice_points[2]; + double y1 = set->lattice_points[3]; + + double x2 = set->lattice_points[4]; + double y2 = set->lattice_points[5]; + + double det = x2 * y1 - x1 * y2; + abort_if(DOUBLE_iszero(det), "determinant should not be zero"); + + apply_inverse(x1, y1, x2, y2, set->f); + + for (int i = 0; i < set->n_vertices; i++) + { + double *vertex = &set->vertices[2 * i]; + apply_inverse(x1, y1, x2, y2, vertex); + } + + for (int i = 0; i < set->n_lattice_points; i++) + { + double *lattice_point = &set->lattice_points[2 * i]; + apply_inverse(x1, y1, x2, y2, lattice_point); + } + + apply_inverse(x1, y1, x2, y2, &m[0]); + apply_inverse(x1, y1, x2, y2, &m[2]); + +CLEANUP: + return rval; +} + +static int apply_m_to_vect(double *v, const double *m) +{ + double v0 = v[0], v1 = v[1]; + v[0] = m[0] * v0 + m[1] * v1; + v[1] = m[2] * v0 + m[3] * v1; + + return 0; +} + +/** + * Applies a unimodular affine transformation to the set, making + * the four lattice points the unit square. + * + * Must be called on quadrilaterals only, and assumes that the three + * first lattice points already correspond to (0,0), (0,1) and (1,0). + * The set is modified in place. The transformation is also applied + * to a given matrix pre_m. + */ +static int make_square(struct LFreeSet2D *set, double *pre_m) +{ + int rval = 0; + double m[4]; + + abort_if(set->n_vertices != 4, "make_square requires quadrilaterals"); + + double *t = &set->lattice_points[2 * 3]; + if(t[0] == 1 && t[1] == 1) + { + m[0] = 1; m[1] = 0; + m[2] = 0; m[3] = 1; + } + else if(t[0] == 1 && t[1] == -1) + { + m[0] = 1; m[1] = 0; + m[2] = 1; m[3] = 1; + } + else if(t[0] == -1 && t[1] == 1) + { + m[0] = 1; m[1] = 1; + m[2] = 0; m[3] = 1; + } + else + { + abort_if(1, "invalid state"); + } + + rval = LFREE_2D_transform_set(set, m); + abort_if(rval, "LFREE_2D_transform_set failed"); + + apply_m_to_vect(&pre_m[0], m); + apply_m_to_vect(&pre_m[2], m); + +CLEANUP: + return rval; +} + + +int find_thin_direction(const struct LFreeSet2D *set, + double *thin_direction, + double *width) +{ + int rval = 0; + + int n_candidates = 3; + double candidates[] = { + 0, 1, + 1, 0, + 1, 1 + }; + + double best_width = INFINITY; + double *best_candidate = 0; + + for (int i = 0; i < n_candidates; i++) + { + double *d = &candidates[2 * i]; + double max_val = -INFINITY; + double min_val = INFINITY; + + for (int j = 0; j < set->n_vertices; j++) + { + double *vertex = &set->vertices[2 * j]; + double val = vertex[0] * d[0] + vertex[1] * d[1]; + + max_val = fmax(max_val, val); + min_val = fmin(min_val, val); + } + + double delta = fabs(max_val - min_val); + + log_verbose("d=%.3lf %.3lf width=%.3lf\n", d[0], d[1], delta); + + if(delta < best_width) + { + best_width = delta; + best_candidate = d; + } + } + + memcpy(thin_direction, best_candidate, 2 * sizeof(double)); + *width = best_width; + +CLEANUP: + return rval; +} + +int LFREE_2D_transform_set(struct LFreeSet2D *set, const double *m) +{ + int rval = 0; + + rval = apply_m_to_vect(set->f, m); + abort_if(rval, "apply_m_to_vect failed"); + + for (int i = 0; i < set->n_vertices; i++) + { + double *vertex = &set->vertices[2 * i]; + + rval = apply_m_to_vect(vertex, m); + abort_if(rval, "apply_m_to_vect failed"); + } + + for (int i = 0; i < set->n_lattice_points; i++) + { + double *lattice_point = &set->lattice_points[2 * i]; + + rval = apply_m_to_vect(lattice_point, m); + abort_if(rval, "apply_m_to_vect failed"); + } + +CLEANUP: + return rval; +} + +int LFREE_2D_preprocess(struct LFreeSet2D *set, double *pre_m, double *center) +{ + int rval = 0; + double direction[2], m[4], width; + double translate[2]; + + pre_m[0] = 1; pre_m[1] = 0; + pre_m[2] = 0; pre_m[3] = 1; + + rval = LFREE_2D_translate_set(set, -set->lattice_points[0], + -set->lattice_points[1]); + abort_if(rval, "LFREE_2D_translate set failed"); + + rval = make_t1_t2_orthogonal(set, pre_m); + abort_if(rval, "make_t1_t2_orthogonal failed"); + + if(set->n_vertices == 4) + { + rval = make_square(set, pre_m); + abort_if(rval, "make_square failed"); + } + + rval = find_thin_direction(set, direction, &width); + abort_if(rval, "find_thin_direction failed"); + + log_debug("d=%.2lf %.2lf\n", direction[0], direction[1]); + + if(direction[0] == 0 && direction[1] == 1) + { + m[0] = 1; m[1] = 0; + m[2] = 0; m[3] = 1; + translate[0] = translate[1] = 0; + } + else if(direction[0] == 1 && direction[1] == 0) + { + m[0] = 0; m[1] = 1; + m[2] = 1; m[3] = 0; + translate[0] = translate[1] = 0; + } + else if(direction[0] == 1 && direction[1] == 1) + { + m[0] = 1; m[1] = 0; + m[2] = -1; m[3] = -1; + translate[0] = 0; + translate[1] = 1; + } + else + { + abort_if(1, "invalid direction"); + } + + apply_m_to_vect(&pre_m[0], m); + apply_m_to_vect(&pre_m[2], m); + + rval = LFREE_2D_transform_set(set, m); + abort_if(rval, "LFREE_2D_transform_set failed"); + + rval = LFREE_2D_translate_set(set, translate[0], translate[1]); + abort_if(rval, "LFREE_2D_translate_set failed"); + + double max_v1 = -INFINITY; + double min_v1 = INFINITY; + + for(int i = 0; i < set->n_vertices; i++) + { + double *v = &set->vertices[2 * i]; + max_v1 = fmax(max_v1, v[1]); + min_v1 = fmin(min_v1, v[1]); + } + + *center = (max_v1 + min_v1) / 2; + width = max_v1 - min_v1; + + log_debug("width = %.2lf\n", width); + abort_iff(width >= 3, "lattice width too large: %.2lf", width); + +CLEANUP: + return rval; +} + +int LFREE_2D_read_next(FILE *file, struct LFreeSet2D *set) +{ + int rval = 0; + ssize_t count; + + double *f = set->f; + count = fscanf(file, "%lf %lf ", &f[0], &f[1]); + abort_if(count != 2, "could not read f"); + + count = fscanf(file, "%d ", &set->n_vertices); + abort_if(count != 1, "could not read n_vertices"); + abort_if(set->n_vertices != 3 && set->n_vertices != 4, + "only triangles or quadrilaterals supported"); + + for(int i = 0; i < set->n_vertices; i++) + { + double *vertex = &set->vertices[2 * i]; + count = fscanf(file, "%lf %lf ", &vertex[0], &vertex[1]); + abort_iff(count != 2, "could not read vertex %d", i+1); + } + + count = fscanf(file, "%d ", &set->n_lattice_points); + abort_if(count != 1, "could not read n_lattice_points"); + abort_if(set->n_lattice_points != set->n_vertices, "number of vertices " + "should be the same as number of lattice points"); + + for(int i = 0; i < set->n_lattice_points; i++) + { + double *lattice_point = &set->lattice_points[2 * i]; + count = fscanf(file, "%lf %lf ", &lattice_point[0], &lattice_point[1]); + abort_iff(count != 2, "could not read lattice_point %d", i+1); + } + +CLEANUP: + return rval; +} + diff --git a/multirow/src/lp.c b/multirow/src/lp.c new file mode 100644 index 0000000..0372c49 --- /dev/null +++ b/multirow/src/lp.c @@ -0,0 +1,724 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +int LP_open(struct LP *lp) +{ + int rval = 0; + + lp->cplex_lp = 0; + lp->cplex_env = CPXopenCPLEX(&rval); + abort_if(rval, "CPXopenCPLEX failed"); + + CPXsetintparam(lp->cplex_env, CPX_PARAM_DATACHECK, CPX_ON); + CPXsetintparam(lp->cplex_env, CPX_PARAM_NUMERICALEMPHASIS, CPX_ON); + CPXsetlogfile(lp->cplex_env, 0); + +CLEANUP: + return rval; +} + +void LP_disable_presolve(struct LP *lp) +{ + CPXsetintparam(lp->cplex_env, CPX_PARAM_PREIND, CPX_OFF); + CPXsetintparam(lp->cplex_env, CPX_PARAM_REPEATPRESOLVE, CPX_OFF); +} + +int LP_create(struct LP *lp, + const char *name) +{ + int rval = 0; + char nambuf[MAX_NAME_LENGTH]; + + abort_if(!lp->cplex_env, "cplex_env is null"); + + strncpy(nambuf, name, MAX_NAME_LENGTH); + nambuf[MAX_NAME_LENGTH - 1] = '\0'; + + lp->cplex_lp = CPXcreateprob(lp->cplex_env, &rval, nambuf); + abort_if(rval, "CPXcreateprob failed"); + +CLEANUP: + return rval; +} + +void LP_free(struct LP *lp) +{ + if (!lp) return; + if (!lp->cplex_env) return; + + if (lp->cplex_lp) + CPXfreeprob(lp->cplex_env, &(lp->cplex_lp)); + + CPXcloseCPLEX(&lp->cplex_env); + lp->cplex_env = 0; +} + +void LP_free_row(struct Row *row) +{ + if (!row) return; + + if (row->pi) free(row->pi); + if (row->indices) free(row->indices); + free(row); +} + +int LP_add_rows(struct LP *lp, + int newrows, + int newnz, + double *rhs, + char *sense, + int *rmatbeg, + int *rmatind, + double *rmatval) +{ + int rval = 0; + + rval = CPXaddrows(lp->cplex_env, lp->cplex_lp, 0, newrows, newnz, rhs, + sense, rmatbeg, rmatind, rmatval, 0, 0); + abort_if(rval, "CPXaddrows failed"); + +CLEANUP: + return rval; +} + +int LP_add_row(struct LP *lp, + struct Row *row) +{ + int rval = 0; + + char sense = 'L'; + int rmatbeg = 0; + + rval = CPXaddrows(lp->cplex_env, lp->cplex_lp, 0, 1, row->nz, &row->pi_zero, + &sense, &rmatbeg, row->indices, row->pi, 0, 0); + abort_if(rval, "CPXaddrows failed"); + +CLEANUP: + return rval; +} + +int LP_add_cols(struct LP *lp, + int newcols, + int newnz, + double *obj, + int *cmatbeg, + int *cmatind, + double *cmatval, + double *lb, + double *ub) +{ + int rval = 0; + + rval = CPXaddcols(lp->cplex_env, lp->cplex_lp, newcols, newnz, obj, cmatbeg, + cmatind, cmatval, lb, ub, 0); + abort_if(rval, "CPXaddcols failed"); + +CLEANUP: + return rval; +} + +int LP_change_bound(struct LP *lp, + int col, + char lower_or_upper, + double bnd) +{ + int rval = 0; + + rval = CPXchgbds(lp->cplex_env, lp->cplex_lp, 1, &col, &lower_or_upper, + &bnd); + abort_if(rval, "CPXchgbds failed"); + +CLEANUP: + return rval; +} + +int LP_get_obj_val(struct LP *lp, + double *obj) +{ + int rval = 0; + + rval = CPXgetobjval(lp->cplex_env, lp->cplex_lp, obj); + abort_if(rval, "CPXgetobjval failed"); + +CLEANUP: + return rval; +} + +int LP_get_x(struct LP *lp, + double *x) +{ + int rval = 0; + + int ncols = CPXgetnumcols(lp->cplex_env, lp->cplex_lp); + abort_if(!ncols, "No columns in LP"); + + rval = CPXgetx(lp->cplex_env, lp->cplex_lp, x, 0, ncols - 1); + abort_if(rval, "CPXgetx failed"); + +CLEANUP: + return rval; +} + +int LP_get_y(struct LP *lp, + double *y) +{ + int rval = 0; + + int nrows = CPXgetnumrows(lp->cplex_env, lp->cplex_lp); + abort_if(!nrows, "No rows in LP"); + + rval = CPXgetpi(lp->cplex_env, lp->cplex_lp, y, 0, nrows - 1); + abort_iff(rval, "CPXgetpi failed (errno = %d)", rval); + +CLEANUP: + return rval; +} + +int LP_get_num_cols(const struct LP *lp) +{ + return CPXgetnumcols(lp->cplex_env, lp->cplex_lp); +} + +int LP_get_num_rows(const struct LP *lp) +{ + return CPXgetnumrows(lp->cplex_env, lp->cplex_lp); +} + +int LP_new_row(struct LP *lp, + char sense, + double rhs) +{ + int rval = 0; + + rval = CPXnewrows(lp->cplex_env, lp->cplex_lp, 1, &rhs, &sense, 0, 0); + abort_if(rval, "CPXnewrows failed"); + +CLEANUP: + return rval; +} + +int LP_new_col(struct LP *lp, + double obj, + double lb, + double ub, + char type) +{ + int rval = 0; + + rval = CPXnewcols(lp->cplex_env, lp->cplex_lp, 1, &obj, &lb, &ub, &type, 0); + abort_if(rval, "CPXnewcols failed"); + +CLEANUP: + return rval; +} + +int LP_optimize(struct LP *lp, + int *infeasible) +{ + int rval = 0, solstat; + + *infeasible = 0; + +// LP_SOLVE_COUNT++; + + if_verbose_level + { + int numrows = CPXgetnumrows(lp->cplex_env, lp->cplex_lp); + int numcols = CPXgetnumcols(lp->cplex_env, lp->cplex_lp); + + time_printf("Optimizing MILP (%d rows %d cols)...\n", numrows, numcols); + } + + double initial_time = get_user_time(); + int problem_type = CPXgetprobtype(lp->cplex_env, lp->cplex_lp); + + switch (problem_type) + { + case CPXPROB_LP: + rval = CPXdualopt(lp->cplex_env, lp->cplex_lp); + log_verbose(" dual opt\n"); + abort_if(rval, "CPXdualopt failed"); + break; + + case CPXPROB_MILP: + case CPXPROB_FIXEDMILP: + rval = CPXmipopt(lp->cplex_env, lp->cplex_lp); + log_verbose(" mip opt\n"); + abort_if(rval, "CPXmipopt failed"); + break; + + default: + abort_iff(1, "Invalid problem type: %d", problem_type); + } + + solstat = CPXgetstat(lp->cplex_env, lp->cplex_lp); + switch (solstat) + { + case CPX_STAT_INFEASIBLE: + case CPXMIP_INFEASIBLE: + log_verbose(" infeasible\n"); + *infeasible = 1; + goto CLEANUP; + + case CPXMIP_OPTIMAL: + case CPXMIP_OPTIMAL_INFEAS: + case CPXMIP_OPTIMAL_TOL: + case CPX_STAT_OPTIMAL: + case CPX_STAT_OPTIMAL_INFEAS: + case CPX_STAT_UNBOUNDED: + break; + + default: + abort_iff(1, "Invalid solution status: %d", solstat); + } + + double objval; + rval = LP_get_obj_val(lp, &objval); + abort_if(rval, "LP_get_obj_val failed"); + + log_verbose(" obj val = %.4lf\n", objval); + log_verbose(" time = %.4lf\n", get_user_time() - initial_time); + +CLEANUP: + return rval; +} + +int LP_write(struct LP *lp, + const char *fname) +{ + int rval = 0; + char nambuf[MAX_NAME_LENGTH]; + + FILE *f = fopen(fname, "w"); + abort_iff(!f, "could not open file %s", fname); + fclose(f); + + strncpy(nambuf, fname, MAX_NAME_LENGTH); + nambuf[MAX_NAME_LENGTH - 1] = '\0'; + + log_info("Writing LP to file %s...\n", fname); + rval = CPXwriteprob(lp->cplex_env, lp->cplex_lp, nambuf, "RLP"); + abort_if(rval, "CPXwriteprob failed"); + +CLEANUP: + return rval; +} + +int LP_read_problem(struct LP *lp, + const char *filename) +{ + int rval = 0; + + log_info("Reading problem %s...\n", filename); + + rval = CPXreadcopyprob(lp->cplex_env, lp->cplex_lp, filename, 0); + abort_if(rval, "CPXreadcopyprob failed"); + +CLEANUP: + return rval; +} + +int LP_relax(struct LP *lp) +{ + int rval = 0; + + rval = CPXchgprobtype(lp->cplex_env, lp->cplex_lp, CPXPROB_LP); + abort_if(rval, "CPXchgprobtype failed"); + +CLEANUP: + return rval; +} + +int LP_get_column_types(struct LP *lp, + char *column_types) +{ + int rval = 0; + char *cplex_ctype = 0; + + int ncols = LP_get_num_cols(lp); + + cplex_ctype = (char *) malloc(ncols * sizeof(char)); + abort_if(!cplex_ctype, "could not allocate cplex_ctype"); + + rval = CPXgetctype(lp->cplex_env, lp->cplex_lp, cplex_ctype, 0, ncols - 1); + abort_if(rval, "CPXgetctype failed"); + + for (int i = 0; i < ncols; i++) + { + switch (cplex_ctype[i]) + { + case CPX_BINARY: + case CPX_INTEGER: + column_types[i] = MILP_INTEGER; + break; + + case CPX_CONTINUOUS: + column_types[i] = MILP_CONTINUOUS; + break; + + default: + abort_iff(1, "Invalid column type: %d", cplex_ctype[i]); + } + } + +CLEANUP: + if (cplex_ctype) free(cplex_ctype); + return rval; +} + +int LP_get_tableau(struct LP *lp, + struct Row **rows, + int *cstat, + int *rstat, + double *ub, + double *lb) +{ + int rval = 0; + + int nrows = LP_get_num_rows(lp); + int ncols = LP_get_num_cols(lp); + + int *head = 0; + double *rhs = 0; + double *pi = 0; + double *slacks = 0; + + rhs = (double *) malloc(nrows * sizeof(double)); + head = (int *) malloc(nrows * sizeof(int)); + slacks = (double*) malloc(nrows * sizeof(double)); + + abort_if(!head, "could not allocate head"); + abort_if(!rhs, "could not allocate rhs"); + abort_if(!slacks, "could not allocate slacks"); + + rval = CPXgetbhead(lp->cplex_env, lp->cplex_lp, head, rhs); + abort_if(rval, "CPXgetbhead failed"); + + rval = CPXgetslack(lp->cplex_env, lp->cplex_lp, slacks, 0, nrows-1); + abort_if(rval, "CPXgetslack failed"); + + rval = CPXgetub(lp->cplex_env, lp->cplex_lp, ub, 0, ncols - 1); + abort_if(rval, "CPXgetub failed"); + + rval = CPXgetlb(lp->cplex_env, lp->cplex_lp, lb, 0, ncols - 1); + abort_if(rval, "CPXgetlb failed"); + + rval = CPXgetbase(lp->cplex_env, lp->cplex_lp, cstat, rstat); + abort_if(rval, "CPXgetbase failed"); + + for (int i = 0; i < nrows; i++) + abort_if(!DOUBLE_iszero(slacks[i]), "model contains slack variables"); + + pi = (double *) malloc(ncols * sizeof(double)); + abort_if(!pi, "could not allocate pi"); + + for (int i = 0; i < nrows; i++) + { + int nz = 0; + rows[i] = (struct Row *) malloc(sizeof(struct Row)); + abort_if(!rows[i], "could not allocate rows[i]"); + + struct Row *row = rows[i]; + + rval = CPXbinvarow(lp->cplex_env, lp->cplex_lp, i, pi); + abort_if(rval, "CPXbinvarow failed"); + + for (int j = 0; j < ncols; j++) + { + if (fabs(pi[j]) < EPSILON) + continue; + + if (cstat[j] == CPX_AT_LOWER) + rhs[i] += lb[j] * pi[j]; + + if (cstat[j] == CPX_AT_UPPER) + rhs[i] += ub[j] * pi[j]; + + nz++; + } + + row->nz = nz; + row->pi_zero = rhs[i]; + row->head = head[i]; + + row->pi = (double *) malloc(nz * sizeof(double)); + row->indices = (int *) malloc(nz * sizeof(int)); + + abort_if(!row->pi, "could not allocate row->pi"); + abort_if(!row->indices, "could not allocate row->indices"); + + if (fabs(row->pi_zero) < EPSILON) + row->pi_zero = 0; + + int k = 0; + for (int j = 0; j < ncols; j++) + { + if (fabs(pi[j]) < EPSILON) + continue; + + row->pi[k] = pi[j]; + row->indices[k++] = j; + } + + rval = LP_flip_row_coefficients(cstat, lb, ub, row); + abort_if(rval, "LP_flip_row_coefficients failed"); + } + +CLEANUP: + if(slacks) free(slacks); + if (head) free(head); + if (rhs) free(rhs); + if (pi) free(pi); + return rval; +} + +int LP_flip_row_coefficients(int *cstat, + double *lb, + double *ub, + struct Row *cut) +{ + for (int j = 0; j < cut->nz; j++) + { + int idx = cut->indices[j]; + double pij = cut->pi[j]; + + if (cstat[idx] == CPX_AT_LOWER) + cut->pi_zero -= lb[idx] * pij; + + if (cstat[idx] == CPX_AT_UPPER) + { + cut->pi_zero -= ub[idx] * pij; + pij = -pij; + } + + cut->indices[j] = idx; + cut->pi[j] = pij; + } + + return 0; +} + +int LP_unflip_row_coefficients(int *cstat, + double *lb, + double *ub, + struct Row *cut) +{ + for (int j = 0; j < cut->nz; j++) + { + int idx = cut->indices[j]; + double pij = cut->pi[j]; + + if (cstat[idx] == CPX_AT_LOWER) + cut->pi_zero += lb[idx] * pij; + + if (cstat[idx] == CPX_AT_UPPER) + { + pij = -pij; + cut->pi_zero += ub[idx] * pij; + } + + cut->indices[j] = idx; + cut->pi[j] = pij; + } + + return 0; +} + +void LP_print_row(struct Row *row) +{ + for (int i = 0; i < row->nz; i++) + printf("%.2lfx%d ", row->pi[i], row->indices[i]); + + printf(" <= %.2lf\n", row->pi_zero); +} + +int LP_read_solution(struct LP *lp, + char *filename, + double *x) +{ + int rval = 0; + int ncols = LP_get_num_cols(lp); + + log_info("Reading solution from %s\n", filename); + + FILE *fsol = fopen(filename, "rb"); + abort_iff(!fsol, "Could not open file %s", filename) + + for (int i = 0; i < ncols; i++) + { + int count = fscanf(fsol, "%le", &x[i]); + abort_iff(count != 1, "Unexpected EOF when reading %s", filename); + } + + fclose(fsol); + +CLEANUP: + return rval; +} + +int LP_write_solution(struct LP *lp, + char *filename) +{ + int rval = 0; + long ncols = LP_get_num_cols(lp); + + double *x = 0; + + x = (double *) malloc(ncols * sizeof(double)); + abort_if(!x, "could not allocate x"); + + rval = LP_get_x(lp, x); + abort_if(rval, "LP_get_x failed"); + + log_info("Writing solution to file %s\n", filename); + + FILE *fsol = fopen(filename, "w"); + abort_iff(!fsol, "Could not open file %s", filename) + + for (int i = 0; i < ncols; i++) + fprintf(fsol, "%.12e\n", x[i]); + + fclose(fsol); + +CLEANUP: + if (x) free(x); + return rval; +} + +int LP_write_basis(struct LP *lp, + char *filename) +{ + int rval = 0; + + log_info("Writing basis to file %s...\n", filename); + rval = CPXmbasewrite(lp->cplex_env, lp->cplex_lp, filename); + abort_if(rval, "CPXmbasewrite failed"); + +CLEANUP: + return rval; +} + +int LP_read_basis(struct LP *lp, + char *filename) +{ + int rval = 0; + + log_info("Reading basis from file %s...\n", filename); + rval = CPXreadcopybase(lp->cplex_env, lp->cplex_lp, filename); + abort_if(rval, "CPXreadcopybase failed"); + +CLEANUP: + return rval; +} + + +int LP_write_sage_file(struct LP *lp, + double *x, + char *filename) +{ + int rval = 0; + + FILE *fsage = 0; + + + int ncols = LP_get_num_cols(lp); + int nrows = LP_get_num_rows(lp); + + int nz; + int dummy; + + int *indices = 0; + double *rhs = 0; + double *values = 0; + int *head = 0; + + fsage = fopen(filename, "w"); + abort_if(!fsage, "fopen failed"); + + indices = (int*) malloc(ncols * sizeof(int)); + values = (double*) malloc(ncols * sizeof(double)); + rhs = (double*) malloc(nrows * sizeof(double)); + head = (int*) malloc(nrows * sizeof(int)); + + abort_if(!indices, "could not allocate indices"); + abort_if(!values, "could not allocate values"); + abort_if(!rhs, "could not allocate rhs"); + abort_if(!head, "could not allocate head"); + + rval = CPXgetrhs(lp->cplex_env, lp->cplex_lp, rhs, 0, nrows-1); + abort_if(rval, "CPXgetrhs failed"); + + fprintf(fsage, "b = vector(["); + for (int i = 0; i < nrows; i++) + fprintf(fsage, " %.8lf,\n", rhs[i]); + fprintf(fsage, "])\n"); + + fprintf(fsage, "A = matrix(QQ, %d, %d, sparse=True)\n", nrows, ncols); + + for (int i = 0; i < nrows; i++) + { + rval = CPXgetrows(lp->cplex_env, lp->cplex_lp, &nz, &dummy, indices, + values, ncols, &dummy, i, i); + abort_if(rval, "CPXgetrows failed"); + + for (int k = 0; k < nz; k++) + fprintf(fsage, "A[%5d,%5d] = %20.8lf\n", i, indices[k], values[k]); + } + + rval = CPXgetbhead(lp->cplex_env, lp->cplex_lp, head, rhs); + abort_if(rval, "CPXgetbhead failed"); + + fprintf(fsage, "B=["); + for (int i = 0; i < nrows; i++) + fprintf(fsage, " %d,\n", head[i]); + fprintf(fsage, "]\n"); + + fprintf(fsage, "x=vector(["); + for (int i = 0; i < ncols; i++) + fprintf(fsage, " %.8lf,\n", x[i]); + fprintf(fsage, "])\n"); + + fclose(fsage); + + CLEANUP: + if(indices) free(indices); + if(values) free(values); + if(rhs) free(rhs); + if(head) free(head); + return rval; +} + +int LP_change_rhs(struct LP *lp, int index, double value) +{ + int rval = 0; + + rval = CPXchgrhs(lp->cplex_env, lp->cplex_lp, 1, &index, &value); + abort_if(rval, "CPXchgrhs failed"); + +CLEANUP: + return rval; +} diff --git a/multirow/src/mir.c b/multirow/src/mir.c new file mode 100644 index 0000000..39ab5bc --- /dev/null +++ b/multirow/src/mir.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +static double h(double a) +{ + return fmax(a, 0); +} + +static double f(double a, + double b) +{ + return frac(b) * floor(a) + fmin(frac(a), frac(b)); +} + +int MIR_generate_cut(const struct Row *row, + const char *column_types, + struct Row *cut) +{ + int rval = 0; + + cut->pi = (double*) malloc(row->nz * sizeof(double)); + cut->indices = (int*) malloc(row->nz * sizeof(int)); + abort_if(!cut->pi, "could not allocate cut->pi"); + abort_if(!cut->indices, "could not allocate cut->indices"); + + cut->head = 0; + cut->nz = row->nz; + cut->pi_zero = -frac(row->pi_zero) * ceil(row->pi_zero); + + for (int i = 0; i < cut->nz; i++) + { + int idx = row->indices[i]; + double value = row->pi[i]; + + cut->indices[i] = idx; + + if (column_types[idx] == MILP_INTEGER) + cut->pi[i] = -f(value, row->pi_zero); + else + cut->pi[i] = -h(value); + } + +CLEANUP: + return rval; +} diff --git a/multirow/src/rational.c b/multirow/src/rational.c new file mode 100644 index 0000000..0b538a2 --- /dev/null +++ b/multirow/src/rational.c @@ -0,0 +1,37 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +long gcd(long a, + long b) +{ + long temp; + + while (b != 0) + { + temp = a % b; + a = b; + b = temp; + } + + return a; +} + +long lcm(long a, + long b) +{ + long g = gcd(a, b); + return (a/g) * b; +} diff --git a/multirow/src/stats.c b/multirow/src/stats.c new file mode 100644 index 0000000..7eb5e5f --- /dev/null +++ b/multirow/src/stats.c @@ -0,0 +1,120 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define MAX_LENGTH 1000 +#define MAX_ROUNDS 100 + +#include +#include +#include +#include + +char input_filename[MAX_LENGTH]; + +unsigned long long generated_cuts_count[MAX_LENGTH]; +unsigned long long added_cuts_count[MAX_LENGTH]; + +int n_rounds = 0; +unsigned long long total_added_cuts = 0; +unsigned long long total_generated_cuts = 0; + +double obj_value[MAX_ROUNDS]; + +double prev_time; +double runtime[MAX_ROUNDS]; + +void STATS_init() +{ + for(int i = 0; i < MAX_ROUNDS; i++) + { + obj_value[i] = NAN; + generated_cuts_count[i] = 0; + added_cuts_count[i] = 0; + runtime[i] = 0; + } + + prev_time = get_user_time(); +} + +void STATS_set_input_filename(char *filename) +{ + strcpy(input_filename, filename); +} + +void STATS_set_obj_value(double obj) +{ + obj_value[n_rounds] = obj; +} + +void STATS_increment_generated_cuts() +{ + generated_cuts_count[n_rounds]++; + total_generated_cuts++; +} + +void STATS_increment_added_cuts() +{ + added_cuts_count[n_rounds]++; + total_added_cuts++; +} + +void STATS_finish_round() +{ + double now = get_user_time(); + runtime[n_rounds] = now - prev_time; + prev_time = now; + + n_rounds++; +} + +int STATS_print_yaml(char *filename) +{ + int rval = 0; + + FILE *f = fopen(filename, "w"); + abort_iff(!f, "could not open file %s", filename); + + fprintf(f, "input-filename:\n %s\n", input_filename); + + fprintf(f, "obj-value:\n"); + for(int i = 0; i < n_rounds; i++) + fprintf(f, " %d: %.6lf\n", i, obj_value[i]); + + fprintf(f, "generated-cuts:\n"); + for(int i = 0; i < n_rounds; i++) + fprintf(f, " %d: %lld\n", i, generated_cuts_count[i]); + + fprintf(f, "added-cuts:\n"); + for(int i = 0; i < n_rounds; i++) + fprintf(f, " %d: %lld\n", i, added_cuts_count[i]); + + fprintf(f, "user-cpu-time:\n"); + for(int i = 0; i < n_rounds; i++) + fprintf(f, " %d: %.3lf\n", i, runtime[i]); + + fprintf(f, "time-per-cut:\n"); + for(int i = 0; i < n_rounds; i++) + { + if(generated_cuts_count[i] > 0) + fprintf(f, " %d: %.3lf\n", i, runtime[i] / generated_cuts_count[i]); + } + + fclose(f); + +CLEANUP: + return rval; +} + diff --git a/multirow/src/util.c b/multirow/src/util.c new file mode 100644 index 0000000..5a4b8d6 --- /dev/null +++ b/multirow/src/util.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +FILE *LOG_FILE = 0; +double INITIAL_TIME = 0; + +double get_user_time() +{ + struct rusage ru; + + getrusage(RUSAGE_SELF, &ru); + + return ((double) ru.ru_utime.tv_sec) + + ((double) ru.ru_utime.tv_usec) / 1000000.0; +} + +double get_real_time() +{ + return (double) time (0); +} + +void time_printf(const char *fmt, + ...) +{ + if (INITIAL_TIME == 0) + INITIAL_TIME = get_user_time(); + + printf("[%10.2lf] ", get_user_time() - INITIAL_TIME); + + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + fflush(stdout); + + if(LOG_FILE) + { + fprintf(LOG_FILE, "[%10.2lf] ", get_user_time() - INITIAL_TIME); + + va_list args; + va_start(args, fmt); + vfprintf(LOG_FILE, fmt, args); + va_end(args); + + fflush(LOG_FILE); + } +} + +double frac(double x) +{ + return x - floor(x); +} + +static long eta_count; +static long eta_total; +static time_t eta_start; +static time_t eta_last; +static char eta_title[1000] = {0}; + +void progress_reset() +{ + eta_count = 0; + time(&eta_start); + eta_last = eta_start; +} + +void progress_set_total(long total) +{ + eta_total = total; +} + +void progress_increment() +{ + eta_count++; +} + +void progress_title(char *title) +{ + strncpy(eta_title, title, 1000); +} + +void progress_print() +{ + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + + if (eta_count == 0) return; + + time_t eta_now; + time(&eta_now); + + if(fabs(difftime(eta_now, eta_last)) < 30.0) + return; + + eta_last = eta_now; + + double diff = difftime(eta_now, eta_start); + double eta = diff / eta_count * eta_total; + + int length = 10; + + time_t eta_date = eta_start + eta; + struct tm *ttm = localtime(&eta_date); + + fprintf(stdout, + "[%-40s] %*s%3.0f%% ETA: %04d-%02d-%02d %02d:%02d %*ld / %*ld\n", + eta_title, 0, "", 100.0 * eta_count / eta_total, ttm->tm_year + + 1900, ttm->tm_mon + 1, ttm->tm_mday, ttm->tm_hour, ttm->tm_min, + length, eta_count, length, eta_total); + fflush(stdout); +} diff --git a/multirow/tests/cg-test.cpp b/multirow/tests/cg-test.cpp new file mode 100644 index 0000000..7ffe00f --- /dev/null +++ b/multirow/tests/cg-test.cpp @@ -0,0 +1,267 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define TEST_SOURCE + +#include +#include + +extern "C" { +#include +#include "../src/cg.c" +} + +int BOOST_VAR = -1; +double BOOST_FACTOR = 1.0; + +TEST(CGTest, next_combination_test_1) +{ + int rval = 0; + + int v[] = { 1, 0 }; + int n = 10; + int k = 2; + + int finished; + int count = 0; + + do + { + count++; + next_combination(n, k, v, 0, &finished); + } + while(!finished); + + EXPECT_EQ(count, 45); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(CGTest, next_combination_test_2) +{ + int rval = 0; + + int v[] = { 2, 1, 0 }; + int n = 5; + int k = 3; + + int finished; + int count = 0; + + do + { + count++; + next_combination(n, k, v, 0, &finished); + } + while(!finished); + + EXPECT_EQ(count, 10); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(CGTest, check_rays_parallel_test) +{ + int rval = 0; + + double r1[] = { 1.0, 1.0, 1.0 }; + double r2[] = { 2.0, 2.0, 2.0 }; + double r3[] = { 0.0, 0.0, 0.0 }; + double r4[] = { -1.0, -1.0, -1.0 }; + + int match; + double scale; + + rval = check_rays_parallel(3, r1, r2, &match, &scale); + abort_if(rval, "check_rays_parallel failed"); + EXPECT_TRUE(match); + EXPECT_DOUBLE_EQ(scale, 0.5); + + rval = check_rays_parallel(3, r1, r3, &match, &scale); + abort_if(rval, "check_rays_parallel failed"); + EXPECT_FALSE(match); + + rval = check_rays_parallel(3, r1, r4, &match, &scale); + abort_if(rval, "check_rays_parallel failed"); + EXPECT_FALSE(match); + +CLEANUP: + if(rval) FAIL(); + +} + +TEST(CGTest, extract_rays_from_rows_test) +{ + int rval = 0; + + double pi1[] = { 1.0, 1.0, 1.0, 2.0, 1.0 }; + int indices1[] = { 1, 7, 8, 12, 14 }; + struct Row row1 = + { + .nz = 5, + .head = 1, + .pi_zero = 1.0, + .pi = pi1, + .indices = indices1 + }; + + double pi2[] = { 1.0, 1.0, 2.0, 1.0, 1.0, 1.0 }; + int indices2[] = { 5, 8, 12, 13, 14, 16 }; + struct Row row2 = + { + .nz = 6, + .head = 13, + .pi_zero = 1.0, + .pi = pi2, + .indices = indices2 + }; + + double pi3[] = { 1.0, 1.0, 1.0, 1.0 }; + int indices3[] = { 3, 7, 10, 16 }; + struct Row row3 = + { + .nz = 4, + .head = 7, + .pi_zero = 1.0, + .pi = pi3, + .indices = indices3 + }; + + struct Row *rows[] = { &row1, &row2, &row3 }; + + int nz; + int nrays; + int indices[1000]; + int variable_to_ray[1000]; + double rays[1000]; + double ray_scale[1000]; + + rval = CG_extract_rays_from_rows(3, rows, rays, &nrays, variable_to_ray, + ray_scale, indices, &nz); + abort_if(rval, "CG_extract_rays_from_rows failed"); + + EXPECT_EQ(nrays, 4); + EXPECT_EQ(nz, 7); + + EXPECT_DOUBLE_EQ(rays[0], 0.0); + EXPECT_DOUBLE_EQ(rays[1], 0.0); + EXPECT_DOUBLE_EQ(rays[2], -1.0); + + EXPECT_DOUBLE_EQ(rays[3], 0.0); + EXPECT_DOUBLE_EQ(rays[4], -1.0); + EXPECT_DOUBLE_EQ(rays[5], 0.0); + + EXPECT_DOUBLE_EQ(rays[6], -2.0); + EXPECT_DOUBLE_EQ(rays[7], -2.0); + EXPECT_DOUBLE_EQ(rays[8], 0.0); + + EXPECT_DOUBLE_EQ(rays[ 9], 0.0); + EXPECT_DOUBLE_EQ(rays[10], -1.0); + EXPECT_DOUBLE_EQ(rays[11], -1.0); + + EXPECT_EQ(indices[0], 3); + EXPECT_EQ(indices[1], 5); + EXPECT_EQ(indices[2], 8); + EXPECT_EQ(indices[3], 10); + EXPECT_EQ(indices[4], 12); + EXPECT_EQ(indices[5], 14); + EXPECT_EQ(indices[6], 16); + + EXPECT_EQ(ray_scale[0], 1.0); + EXPECT_EQ(ray_scale[1], 1.0); + EXPECT_EQ(ray_scale[2], 0.5); + EXPECT_EQ(ray_scale[3], 1.0); + EXPECT_EQ(ray_scale[4], 1.0); + EXPECT_EQ(ray_scale[5], 0.5); + EXPECT_EQ(ray_scale[6], 1.0); + + EXPECT_EQ(variable_to_ray[0], 0); + EXPECT_EQ(variable_to_ray[1], 1); + EXPECT_EQ(variable_to_ray[2], 2); + EXPECT_EQ(variable_to_ray[3], 0); + EXPECT_EQ(variable_to_ray[4], 2); + EXPECT_EQ(variable_to_ray[5], 2); + EXPECT_EQ(variable_to_ray[6], 3); + + CLEANUP: + if(rval) FAIL(); +} + +TEST(CGTest, boost_variable_test) +{ + int rval = 0; + + int nrows = 3; + double rays[] = { + 0, 0, -1, + 0, -1, 0, + -2, -2, 0, + 0, -1, -1 + }; + + int nz = 7; + int indices[] = { 3, 5, 8, 10, 12, 14, 16 }; + int variable_to_ray[] = { 0, 1, 2, 0, 2, 2, 3 }; + double ray_scale[] = { 1, 1, 0.5, 1, 1, 0.5, 1 }; + + rval = CG_boost_variable(8, 200, nrows, rays, variable_to_ray, ray_scale, indices, nz); + abort_if(rval, "boost_variable failed"); + + EXPECT_DOUBLE_EQ(rays[0], 0.0); + EXPECT_DOUBLE_EQ(rays[1], 0.0); + EXPECT_DOUBLE_EQ(rays[2], -1.0); + + EXPECT_DOUBLE_EQ(rays[3], 0.0); + EXPECT_DOUBLE_EQ(rays[4], -1.0); + EXPECT_DOUBLE_EQ(rays[5], 0.0); + + EXPECT_DOUBLE_EQ(rays[6], -400.0); + EXPECT_DOUBLE_EQ(rays[7], -400.0); + EXPECT_DOUBLE_EQ(rays[8], 0.0); + + EXPECT_DOUBLE_EQ(rays[ 9], 0.0); + EXPECT_DOUBLE_EQ(rays[10], -1.0); + EXPECT_DOUBLE_EQ(rays[11], -1.0); + + EXPECT_EQ(indices[0], 3); + EXPECT_EQ(indices[1], 5); + EXPECT_EQ(indices[2], 8); + EXPECT_EQ(indices[3], 10); + EXPECT_EQ(indices[4], 12); + EXPECT_EQ(indices[5], 14); + EXPECT_EQ(indices[6], 16); + + EXPECT_EQ(ray_scale[0], 1.0); + EXPECT_EQ(ray_scale[1], 1.0); + EXPECT_EQ(ray_scale[2], 0.0025); + EXPECT_EQ(ray_scale[3], 1.0); + EXPECT_EQ(ray_scale[4], 0.005); + EXPECT_EQ(ray_scale[5], 0.0025); + EXPECT_EQ(ray_scale[6], 1.0); + + EXPECT_EQ(variable_to_ray[0], 0); + EXPECT_EQ(variable_to_ray[1], 1); + EXPECT_EQ(variable_to_ray[2], 2); + EXPECT_EQ(variable_to_ray[3], 0); + EXPECT_EQ(variable_to_ray[4], 2); + EXPECT_EQ(variable_to_ray[5], 2); + EXPECT_EQ(variable_to_ray[6], 3); + +CLEANUP: + if(rval) FAIL(); +} diff --git a/multirow/tests/double-test.cpp b/multirow/tests/double-test.cpp new file mode 100644 index 0000000..a077108 --- /dev/null +++ b/multirow/tests/double-test.cpp @@ -0,0 +1,164 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +extern "C" { + #include + #include +} + +TEST(DoubleTest, double_cmp_test) +{ + EXPECT_EQ(0, DOUBLE_cmp(1/3.0, 2/6.0)); + + + EXPECT_EQ(1, DOUBLE_cmp(5.0, 3.0)); + EXPECT_EQ(0, DOUBLE_cmp(5.0, 5.0)); + EXPECT_EQ(-1, DOUBLE_cmp(5.0, 10.0)); + + EXPECT_EQ(1, DOUBLE_cmp(5.1, 5.0)); + + EXPECT_EQ(0, DOUBLE_cmp(1000000005, 1000000003)); + EXPECT_EQ(0, DOUBLE_cmp(1000000005, 1000000005)); + EXPECT_EQ(0, DOUBLE_cmp(1000000005, 1000000007)); + + + EXPECT_EQ(1, DOUBLE_cmp(0.00005, 0.00003)); + EXPECT_EQ(0, DOUBLE_cmp(0.00005, 0.00005)); + EXPECT_EQ(-1, DOUBLE_cmp(0.00005, 0.00007)); + + EXPECT_EQ(0, DOUBLE_cmp(0.5 * EPSILON, 0.3 * EPSILON)); + EXPECT_EQ(0, DOUBLE_cmp(0.5 * EPSILON, 0.5 * EPSILON)); + EXPECT_EQ(0, DOUBLE_cmp(0.5 * EPSILON, 0.7 * EPSILON)); + + EXPECT_EQ(0, DOUBLE_cmp(0.0, 0.0)); + EXPECT_EQ(0, DOUBLE_cmp(0.0, -0.0)); + EXPECT_EQ(0, DOUBLE_cmp(-0.0, -0.0)); + EXPECT_EQ(0, DOUBLE_cmp(0.1 * EPSILON, 0.0)); + EXPECT_EQ(0, DOUBLE_cmp(0.0, 0.1 * EPSILON)); + EXPECT_EQ(0, DOUBLE_cmp(-0.1 * EPSILON, 0.0)); + EXPECT_EQ(0, DOUBLE_cmp(0.0, -0.1 * EPSILON)); + + EXPECT_EQ(0, DOUBLE_cmp(DBL_MAX, DBL_MAX)); + EXPECT_EQ(1, DOUBLE_cmp(DBL_MAX, -DBL_MAX)); + EXPECT_EQ(-1, DOUBLE_cmp(-DBL_MAX, DBL_MAX)); + EXPECT_EQ(1, DOUBLE_cmp(DBL_MAX, DBL_MAX / 2)); + EXPECT_EQ(1, DOUBLE_cmp(DBL_MAX, -DBL_MAX / 2)); + EXPECT_EQ(-1, DOUBLE_cmp(-DBL_MAX, DBL_MAX / 2)); + + EXPECT_EQ(0, DOUBLE_cmp(INFINITY, INFINITY)); + EXPECT_EQ(1, DOUBLE_cmp(INFINITY, -INFINITY)); + EXPECT_EQ(-1, DOUBLE_cmp(-INFINITY, INFINITY)); + EXPECT_EQ(0, DOUBLE_cmp(-INFINITY, -INFINITY)); + EXPECT_EQ(1, DOUBLE_cmp(INFINITY, DBL_MAX)); + EXPECT_EQ(-1, DOUBLE_cmp(-INFINITY, DBL_MIN)); + + EXPECT_EQ(1, DOUBLE_cmp(NAN, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, 0.0f)); + EXPECT_EQ(1, DOUBLE_cmp(-0.0f, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, -0.0f)); + EXPECT_EQ(1, DOUBLE_cmp(0.0f, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, INFINITY)); + EXPECT_EQ(1, DOUBLE_cmp(INFINITY, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, -INFINITY)); + EXPECT_EQ(1, DOUBLE_cmp(-INFINITY, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, DBL_MAX)); + EXPECT_EQ(1, DOUBLE_cmp(DBL_MAX, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, -DBL_MAX)); + EXPECT_EQ(1, DOUBLE_cmp(-DBL_MAX, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, DBL_MIN)); + EXPECT_EQ(1, DOUBLE_cmp(DBL_MIN, NAN)); + EXPECT_EQ(1, DOUBLE_cmp(NAN, -DBL_MIN)); + EXPECT_EQ(1, DOUBLE_cmp(-DBL_MIN, NAN)); +} + +TEST(DoubleTest, double_iszero_test) +{ + EXPECT_TRUE(DOUBLE_iszero(0.0)); + EXPECT_TRUE(DOUBLE_iszero(-0.0)); + EXPECT_TRUE(DOUBLE_iszero(0.1 * EPSILON)); + EXPECT_TRUE(DOUBLE_iszero(-0.1 * EPSILON)); +} + +TEST(DoubleTest, double_eq_test) +{ + EXPECT_TRUE(DOUBLE_eq(1.0, 1.0)); + EXPECT_FALSE(DOUBLE_eq(1.0, 3.0)); + EXPECT_TRUE(DOUBLE_eq(1/3.0, 2/6.0)); +} + +TEST(DoubleTest, double_max_test) +{ + EXPECT_EQ(0.1, DOUBLE_max(0.0, 0.1)); + EXPECT_EQ(0.5, DOUBLE_max(0.5, 0.1)); +} + +TEST(DoubleTest, double_geq_test) +{ + EXPECT_TRUE(DOUBLE_geq(1.0, 0.5)); + EXPECT_TRUE(DOUBLE_geq(0.46000000, 0.45953916)); +} + +TEST(DoubleTest, double_to_rational_test) +{ + int rval = 0; + + Rational r; + + rval = DOUBLE_to_rational(M_PI, 1, r); + abort_if(rval, "DOUBLE_to failed"); + EXPECT_EQ(3, r->num); + EXPECT_EQ(1, r->den); + + rval = DOUBLE_to_rational(M_PI, 10, r); + abort_if(rval, "DOUBLE_to failed"); + EXPECT_EQ(22, r->num); + EXPECT_EQ(7, r->den); + + rval = DOUBLE_to_rational(M_PI, 100, r); + abort_if(rval, "DOUBLE_to failed"); + EXPECT_EQ(311, r->num); + EXPECT_EQ(99, r->den); + + rval = DOUBLE_to_rational(M_PI, 1000, r); + abort_if(rval, "DOUBLE_to failed"); + EXPECT_EQ(355, r->num); + EXPECT_EQ(113, r->den); + + rval = DOUBLE_to_rational(M_PI, 10000, r); + abort_if(rval, "DOUBLE_to failed"); + EXPECT_EQ(355, r->num); + EXPECT_EQ(113, r->den); + + rval = DOUBLE_to_rational(-M_PI, 10000, r); + abort_if(rval, "DOUBLE_to failed"); + EXPECT_EQ(-355, r->num); + EXPECT_EQ(113, r->den); + +CLEANUP: + if(rval) FAIL(); +} + +TEST(DoubleTest, gcd_test) +{ + EXPECT_EQ(1, gcd(11, 7)); + EXPECT_EQ(2, gcd(6, 4)); + EXPECT_EQ(2, gcd(4, 6)); + EXPECT_EQ(5, gcd(5, 10)); +} + diff --git a/multirow/tests/geometry-test.cpp b/multirow/tests/geometry-test.cpp new file mode 100644 index 0000000..2e88ea6 --- /dev/null +++ b/multirow/tests/geometry-test.cpp @@ -0,0 +1,664 @@ +/* Copyright (c) 2015 Alinson Xavier + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +extern "C" { +#include +#include +#include +} + +TEST(GeometryTest, interpolate_coefficients_1) +{ + int rval = 0; + + double r1[] = {1.0, 2.0}; + double r2[] = {3.0, 1.0}; + double p[] = {2.0, 2.0}; + + double lambda; + + rval = scale_vector_to_line(r1, r2, p, &lambda); + abort_if(rval, "scale_vector_to_line failed"); + + EXPECT_DOUBLE_EQ(5 / 6.0, lambda); + + CLEANUP: + if (rval) FAIL(); +} + +TEST(GeometryTest, interpolate_coefficients_2) +{ + int rval = 0; + + double r1[] = {1.0, 3.0}; + double r2[] = {1e90, 1e90}; + + double p1[] = {2.0, 3.0}; + double p2[] = {1.0, 1.0}; + double p3[] = {1.0, 3.0}; + + double lambda1; + double lambda2; + double lambda3; + + rval = scale_vector_to_line(r1, r2, p1, &lambda1); + abort_if(rval, "scale_vector_to_line failed"); + + rval = scale_vector_to_line(r1, r2, p2, &lambda2); + abort_if(rval, "scale_vector_to_line failed"); + + rval = scale_vector_to_line(r1, r2, p3, &lambda3); + abort_if(rval, "scale_vector_to_line failed"); + + EXPECT_DOUBLE_EQ(2.0, lambda1); + EXPECT_DOUBLE_EQ(INFINITY, lambda2); + EXPECT_DOUBLE_EQ(1.0, lambda3); + + rval = scale_vector_to_line(r2, r1, p1, &lambda1); + abort_if(rval, "scale_vector_to_line failed"); + + rval = scale_vector_to_line(r2, r1, p2, &lambda2); + abort_if(rval, "scale_vector_to_line failed"); + + rval = scale_vector_to_line(r2, r1, p3, &lambda3); + abort_if(rval, "scale_vector_to_line failed"); + + EXPECT_DOUBLE_EQ(2.0, lambda1); + EXPECT_DOUBLE_EQ(INFINITY, lambda2); + EXPECT_DOUBLE_EQ(1.0, lambda3); + + CLEANUP: + if (rval) FAIL(); +} + +TEST(GeometryTest, interpolate_coefficients_3) +{ + int rval = 0; + + double r1[] = {-1.0, 1.0}; + double r2[] = {-1.0, -1.0}; + + double p1[] = {-1.0, 0.0}; + double lambda1; + + rval = scale_vector_to_line(r1, r2, p1, &lambda1); + abort_if(rval, "scale_vector_to_line failed"); + + EXPECT_DOUBLE_EQ(1.0, lambda1); + + CLEANUP: + if (rval) FAIL(); +} + +TEST(GeometryTest, chull_2d_test) +{ + int rval = 0; + + int npoints = 9; + double points[] = + { + 0.0, 0.0, + 0.0, 1.0, + 0.0, 2.0, + 1.0, 0.0, + 1.0, 1.0, + 1.0, 2.0, + 2.0, 0.0, + 2.0, 1.0, + 2.0, 2.0 + }; + + double vertices[100]; + int nvertices; + + rval = chull_2d(points, npoints, vertices, &nvertices); + abort_if(rval, "chull_2d failed"); + + for(int i=0; i