Add single-row cut generator
This commit is contained in:
35
onerow/library/CMakeLists.txt
Normal file
35
onerow/library/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
include_directories(include)
|
||||
|
||||
set(COMMON_SOURCES
|
||||
src/cplex_helper.cpp
|
||||
src/geometry.cpp
|
||||
src/gomory_cut_generator.cpp
|
||||
src/knapsack2.cpp
|
||||
src/mir_cut_generator.cpp
|
||||
src/single_row_cut_generator.cpp
|
||||
src/stats.cpp
|
||||
src/wedge_cut_generator.cpp
|
||||
include/onerow/cplex_helper.hpp
|
||||
include/onerow/cplex_helper.tpp
|
||||
include/onerow/geometry.hpp
|
||||
include/onerow/gomory_cut_generator.hpp
|
||||
include/onerow/knapsack2.hpp
|
||||
include/onerow/mir_cut_generator.hpp
|
||||
include/onerow/params.hpp
|
||||
include/onerow/single_row_cut_generator.hpp
|
||||
include/onerow/stats.hpp
|
||||
include/onerow/wedge_cut_generator.hpp)
|
||||
|
||||
set(TEST_SOURCES
|
||||
tests/knapsack2_test.cpp
|
||||
tests/rational_test.cpp
|
||||
tests/single_row_generator_test.cpp
|
||||
tests/wedge_cut_generator_test.cpp)
|
||||
|
||||
add_library(onerow_static ${COMMON_SOURCES})
|
||||
set_target_properties(onerow_static PROPERTIES OUTPUT_NAME onerow)
|
||||
target_include_directories (onerow_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(onerow_static qxx_static ${GMP_LIBRARIES} ${CPLEX_LIBRARIES})
|
||||
|
||||
add_executable(onerow-test.run ${COMMON_SOURCES} ${TEST_SOURCES})
|
||||
target_link_libraries(onerow-test.run gtest_main qxx_static ${GMP_LIBRARIES} ${CPLEX_LIBRARIES})
|
||||
141
onerow/library/include/onerow/cplex_helper.hpp
Normal file
141
onerow/library/include/onerow/cplex_helper.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CPLEX_HELPER_HPP_
|
||||
#define CPLEX_HELPER_HPP_
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include "single_row_cut_generator.hpp"
|
||||
using std::set;
|
||||
using std::vector;
|
||||
|
||||
struct CplexRow {
|
||||
int nz;
|
||||
double pi_zero;
|
||||
double *pi;
|
||||
int *indices;
|
||||
int depth;
|
||||
int head;
|
||||
double dynamism;
|
||||
|
||||
bool operator<(const CplexRow &other) const;
|
||||
double get_violation(double *x);
|
||||
void print(double *x);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class provides useful methods for dealing with CPLEX.
|
||||
*/
|
||||
class CplexHelper {
|
||||
private:
|
||||
const CPXENVptr env;
|
||||
const CPXLPptr lp;
|
||||
bool *is_integer;
|
||||
int n_cuts;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a helper associated with the provided environment and problem.
|
||||
*
|
||||
* @param env Pointer to the ILOG CPLEX environment.
|
||||
* @param lp Pointer to a CPLEX LP problem object.
|
||||
*/
|
||||
CplexHelper(CPXENVptr env, CPXLPptr lp);
|
||||
|
||||
~CplexHelper();
|
||||
|
||||
/**
|
||||
* Adds the specified constraints to the model.
|
||||
*
|
||||
* @param cuts Set of constraints to add.
|
||||
*/
|
||||
void add_cut(Constraint *cut);
|
||||
|
||||
/**
|
||||
* For each fractional row of the current tableau, adds as many single row
|
||||
* cuts as possible. The cuts are generated by the provided generator class.
|
||||
*
|
||||
* @tparam Generator Class used to generate the cuts.
|
||||
* @returns The number of cuts added.
|
||||
*/
|
||||
template<class Generator>
|
||||
int add_single_row_cuts();
|
||||
|
||||
/**
|
||||
* Gets a single row from the current tableau.
|
||||
*
|
||||
* @param index Index of the row to fetch.
|
||||
* @returns The selected tableau row.
|
||||
*/
|
||||
Row* get_tableau_row(int index);
|
||||
|
||||
void read_basis();
|
||||
void read_columns();
|
||||
|
||||
/**
|
||||
* Solves the current problem, and prints some solution information.
|
||||
*
|
||||
* @returns The solution status, as returned by CPXgetstat.
|
||||
*/
|
||||
int solve(bool save_stats = false);
|
||||
|
||||
void dump_constraint(const Constraint &c, const char *msg = "");
|
||||
|
||||
void print_basis();
|
||||
|
||||
void flush_cuts();
|
||||
|
||||
void eta_print();
|
||||
void eta_reset();
|
||||
|
||||
CplexRow constraint_to_cplex_row(const Constraint &cut);
|
||||
|
||||
Constraint cplex_row_to_constraint(const CplexRow &cplex_row);
|
||||
|
||||
void print_solution(double *x);
|
||||
|
||||
void find_good_rows();
|
||||
|
||||
int n_rows;
|
||||
int n_cols;
|
||||
|
||||
double *ub;
|
||||
double *lb;
|
||||
int *cstat;
|
||||
|
||||
CplexRow *cplex_rows;
|
||||
|
||||
int eta_count;
|
||||
int eta_total;
|
||||
time_t eta_start;
|
||||
|
||||
set<CplexRow> cut_buffer;
|
||||
|
||||
double *first_solution;
|
||||
double *current_solution;
|
||||
double *optimal_solution;
|
||||
|
||||
long total_cuts;
|
||||
|
||||
int current_round;
|
||||
|
||||
int n_good_rows;
|
||||
int *good_rows;
|
||||
};
|
||||
|
||||
#include "cplex_helper.tpp"
|
||||
|
||||
#endif /* CPLEX_HELPER_HPP_ */
|
||||
104
onerow/library/include/onerow/cplex_helper.tpp
Normal file
104
onerow/library/include/onerow/cplex_helper.tpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CPLEX_HELPER_TPP_
|
||||
#define CPLEX_HELPER_TPP_
|
||||
#include <cmath>
|
||||
#include <omp.h>
|
||||
#include <ctime>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <onerow/stats.hpp>
|
||||
#include <onerow/cplex_helper.hpp>
|
||||
#include <onerow/params.hpp>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
#include <gperftools/heap-profiler.h>
|
||||
#include <gperftools/malloc_extension.h>
|
||||
|
||||
|
||||
template<class Generator>
|
||||
int CplexHelper::add_single_row_cuts()
|
||||
{
|
||||
total_cuts = 0;
|
||||
|
||||
if(n_good_rows < 0)
|
||||
find_good_rows();
|
||||
|
||||
eta_reset();
|
||||
eta_count = 0;
|
||||
eta_total = n_good_rows;
|
||||
std::thread eta(&CplexHelper::eta_print, this);
|
||||
|
||||
Stats::start_timer();
|
||||
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
for (int i = 0; i < n_good_rows; i++)
|
||||
{
|
||||
Row *row = get_tableau_row(good_rows[i]);
|
||||
//Row *row = good_rows[i];
|
||||
|
||||
Generator generator(*row);
|
||||
|
||||
while (generator.has_next())
|
||||
{
|
||||
Constraint *cut = generator.next();
|
||||
|
||||
if (cut->pi.nz() == 0)
|
||||
{
|
||||
delete cut;
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef PRETEND_TO_ADD_CUTS
|
||||
|
||||
delete(cut);
|
||||
|
||||
#else
|
||||
|
||||
#pragma omp critical(cplex)
|
||||
{
|
||||
add_cut(cut);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_EXTENDED_STATISTICS
|
||||
Stats::add_generated_cut(current_round, cut->depth);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma omp critical
|
||||
{
|
||||
eta_count++;
|
||||
}
|
||||
|
||||
delete row;
|
||||
}
|
||||
|
||||
Stats::end_timer();
|
||||
|
||||
eta.join();
|
||||
|
||||
flush_cuts();
|
||||
time_printf("Added %d violated cuts...\n", total_cuts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CPLEX_HELPER_TPP_ */
|
||||
56
onerow/library/include/onerow/geometry.hpp
Normal file
56
onerow/library/include/onerow/geometry.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GEOMETRY_HPP_
|
||||
#define GEOMETRY_HPP_
|
||||
|
||||
#include<qxx/rational.hpp>
|
||||
#include<qxx/svec.hpp>
|
||||
typedef q::mpq rational;
|
||||
typedef q::svec svec;
|
||||
|
||||
/**
|
||||
* Models a two-dimensional rational point (x,y).
|
||||
*/
|
||||
struct Point {
|
||||
|
||||
rational x;
|
||||
rational y;
|
||||
|
||||
Point();
|
||||
Point(rational x, rational y);
|
||||
|
||||
Point operator+(const Point& p) const;
|
||||
Point operator-(const Point& p) const;
|
||||
Point operator*(rational scale) const;
|
||||
rational operator*(const Point& p) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Models a two-dimensional rational line, defined by a pair of points p1 and p2.
|
||||
*/
|
||||
struct Line {
|
||||
Point p1;
|
||||
Point p2;
|
||||
|
||||
Line();
|
||||
Line(Point p1, Point p2);
|
||||
Line(rational x1, rational y1, rational x2, rational y2);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Line &l);
|
||||
std::ostream& operator<<(std::ostream& os, const Point &p);
|
||||
|
||||
#endif /* GEOMETRY_HPP_ */
|
||||
37
onerow/library/include/onerow/gomory_cut_generator.hpp
Normal file
37
onerow/library/include/onerow/gomory_cut_generator.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GOMORY_CUT_GENERATOR_HPP_
|
||||
#define GOMORY_CUT_GENERATOR_HPP_
|
||||
|
||||
#include "single_row_cut_generator.hpp"
|
||||
|
||||
/**
|
||||
* This class can be used to generate classic (not fractional) Gomory cuts.
|
||||
* The cuts are only valid for models with integral, non-negative variables.
|
||||
*/
|
||||
class GomoryCutGenerator: public SingleRowCutGenerator {
|
||||
private:
|
||||
bool finished;
|
||||
|
||||
public:
|
||||
GomoryCutGenerator(Row &row);
|
||||
~GomoryCutGenerator();
|
||||
|
||||
bool has_next();
|
||||
Constraint* next();
|
||||
};
|
||||
|
||||
#endif /* GOMORY_CUT_GENERATOR_HPP_ */
|
||||
54
onerow/library/include/onerow/knapsack2.hpp
Normal file
54
onerow/library/include/onerow/knapsack2.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef KNAPSACK_2_HPP_
|
||||
#define KNAPSACK_2_HPP_
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include "qxx/rational.hpp"
|
||||
#include "geometry.hpp"
|
||||
using std::vector;
|
||||
using std::pair;
|
||||
|
||||
// **************************************************************************
|
||||
//
|
||||
// **************************************************************************
|
||||
#define KNAPSACK2_LEFT 0
|
||||
#define KNAPSACK2_RIGHT 1
|
||||
#define KNAPSACK2_BOTH 2
|
||||
#define KNAPSACK2_RAY 3
|
||||
|
||||
class PairRational {
|
||||
public:
|
||||
size_t operator()(const pair<rational, rational> &k) const;
|
||||
};
|
||||
|
||||
struct Knapsack2Vertex {
|
||||
|
||||
int side;
|
||||
q::dvec lower, upper, opposed;
|
||||
|
||||
};
|
||||
|
||||
|
||||
// **************************************************************************
|
||||
//
|
||||
// **************************************************************************
|
||||
class Knapsack2 {
|
||||
|
||||
public:
|
||||
Knapsack2();
|
||||
Knapsack2(rational f, rational r1);
|
||||
~Knapsack2();
|
||||
|
||||
void clear();
|
||||
void eval(rational f, rational r1);
|
||||
|
||||
private:
|
||||
void push(int side,
|
||||
const q::dvec &l, const q::dvec &u, const q::dvec &o);
|
||||
|
||||
public:
|
||||
|
||||
vector<Knapsack2Vertex> list;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
35
onerow/library/include/onerow/mir_cut_generator.hpp
Normal file
35
onerow/library/include/onerow/mir_cut_generator.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MIR_CUT_GENERATOR_HPP_
|
||||
#define MIR_CUT_GENERATOR_HPP_
|
||||
|
||||
#include "single_row_cut_generator.hpp"
|
||||
|
||||
class MIRCutGenerator: public SingleRowCutGenerator {
|
||||
private:
|
||||
bool finished;
|
||||
rational f(rational q1, rational q2);
|
||||
rational h(rational q);
|
||||
|
||||
public:
|
||||
MIRCutGenerator(Row &row);
|
||||
~MIRCutGenerator();
|
||||
|
||||
bool has_next();
|
||||
Constraint* next();
|
||||
};
|
||||
|
||||
#endif /* MIR_CUT_GENERATOR_HPP_ */
|
||||
41
onerow/library/include/onerow/params.hpp
Normal file
41
onerow/library/include/onerow/params.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PARAMS_HPP_
|
||||
#define PARAMS_HPP_
|
||||
|
||||
const double ZERO_CUTOFF = 1e-8;
|
||||
|
||||
const double MAX_CUT_DYNAMISM = 1e6;
|
||||
const double MIN_CUT_VIOLATION = 1e-6;
|
||||
|
||||
const long REDUCE_FACTOR_RHS = 1000000;
|
||||
const long REDUCE_FACTOR_R1 = 1000;
|
||||
const long REDUCE_FACTOR_COEFFICIENT = 1000000;
|
||||
|
||||
const int MAX_CUT_DEPTH = 200;
|
||||
|
||||
const int ETA_UPDATE_INTERVAL = 1;
|
||||
const unsigned int MAX_CUT_BUFFER_SIZE = 100;
|
||||
|
||||
const double COEFFICIENT_SAFETY_MARGIN = 0.00001;
|
||||
|
||||
#define INTERSECTION_CUT_USE_DOUBLE
|
||||
|
||||
#define ENABLE_TRIVIAL_LIFTING
|
||||
#define ENABLE_EXTENDED_STATISTICS
|
||||
//#define PRETEND_TO_ADD_CUTS
|
||||
|
||||
#endif /* PARAMS_HPP_ */
|
||||
107
onerow/library/include/onerow/single_row_cut_generator.hpp
Normal file
107
onerow/library/include/onerow/single_row_cut_generator.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SINGLE_ROW_CUT_GENERATOR_HPP_
|
||||
#define SINGLE_ROW_CUT_GENERATOR_HPP_
|
||||
|
||||
#include <ilcplex/cplex.h>
|
||||
#include <vector>
|
||||
#include "geometry.hpp"
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* Models a linear constraint.
|
||||
*/
|
||||
struct Constraint {
|
||||
|
||||
/**
|
||||
* Vector that holds the coefficients of the variables.
|
||||
*/
|
||||
svec pi;
|
||||
|
||||
/**
|
||||
* The right hand side of the constraint.
|
||||
*/
|
||||
rational pi_zero;
|
||||
|
||||
int depth;
|
||||
|
||||
/**
|
||||
* Comparator used to sort the constraints.
|
||||
*
|
||||
* @returns bool True if this constraint should come before the given
|
||||
* constraint when sorting.
|
||||
*/
|
||||
bool operator<(const Constraint &other) const;
|
||||
|
||||
bool operator==(const Constraint &other) const;
|
||||
|
||||
rational get_violation(const rational *x);
|
||||
};
|
||||
|
||||
/**
|
||||
* Models a single row from the simplex tableau.
|
||||
*/
|
||||
struct Row {
|
||||
/**
|
||||
* Constraint
|
||||
*/
|
||||
Constraint c;
|
||||
|
||||
/**
|
||||
* Index of the basic variable this row corresponds to.
|
||||
*/
|
||||
int basic_var_index;
|
||||
|
||||
bool* is_integer;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single row cut generator receives a row from the simplex tableau and generates one
|
||||
* or more cuts that invalidate the current basic solution.
|
||||
*/
|
||||
class SingleRowCutGenerator {
|
||||
protected:
|
||||
const Row& row;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructs a new generator that will generate cuts from the provided tableau row.
|
||||
*/
|
||||
SingleRowCutGenerator(Row &r);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~SingleRowCutGenerator() {};
|
||||
|
||||
/**
|
||||
* Returns true if more cuts can be generated from the tableau row.
|
||||
*/
|
||||
virtual bool has_next() = 0;
|
||||
|
||||
/**
|
||||
* Retrieves a cut generated from the tableau row.
|
||||
*
|
||||
* @throws std::out_of_bounds if no more cuts can be generated from the current tableau row.
|
||||
*/
|
||||
virtual Constraint* next() = 0;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Constraint &c);
|
||||
std::ostream& operator<<(std::ostream& os, const Row &r);
|
||||
|
||||
#endif
|
||||
41
onerow/library/include/onerow/stats.hpp
Normal file
41
onerow/library/include/onerow/stats.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef STATS_HPP_
|
||||
#define STATS_HPP_
|
||||
|
||||
#include <string>
|
||||
using std::string;
|
||||
|
||||
namespace Stats {
|
||||
void init();
|
||||
void add_cut(int depth);
|
||||
void add_generated_cut(int round, int depth);
|
||||
void set_solution(int round, double sol, string status);
|
||||
void set_input_filename(string n);
|
||||
|
||||
void add_trivial_lifting_m(unsigned long m);
|
||||
void add_coefficient(bool integral);
|
||||
|
||||
void write_stats(string filename);
|
||||
|
||||
void start_timer();
|
||||
void end_timer();
|
||||
};
|
||||
|
||||
double get_current_time(void);
|
||||
void time_printf(const char *fmt, ...);
|
||||
|
||||
#endif /* STATS_HPP_ */
|
||||
132
onerow/library/include/onerow/wedge_cut_generator.hpp
Normal file
132
onerow/library/include/onerow/wedge_cut_generator.hpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WEDGE_CUT_GENERATOR_HPP_
|
||||
#define WEDGE_CUT_GENERATOR_HPP_
|
||||
|
||||
#include "params.hpp"
|
||||
#include "qxx/dmat.hpp"
|
||||
#include "geometry.hpp"
|
||||
#include "knapsack2.hpp"
|
||||
#include "single_row_cut_generator.hpp"
|
||||
|
||||
/**
|
||||
* Models a two-dimensional intersection cut, and its associated convex set.
|
||||
* The convex set is given by
|
||||
* \f[
|
||||
* C = \{ x \in R^n : (d^i)^T(x-f) \leq 1 \},
|
||||
* \f]
|
||||
* where f is a point in the interior of C.
|
||||
*/
|
||||
class IntersectionCut {
|
||||
public:
|
||||
Point f;
|
||||
Point *d;
|
||||
const int n_faces;
|
||||
|
||||
/**
|
||||
* Constructs a new intersection cut and its associated convex set from
|
||||
* the data provided.
|
||||
*
|
||||
* @param f A fractional point in the interior of the convex set.
|
||||
* @param n_faces The number of faces of the convex set.
|
||||
* @param lines (Optional) Array of lines that support each face of the
|
||||
* convex.
|
||||
*/
|
||||
IntersectionCut(Point f, int n_faces, const Line *lines = 0);
|
||||
~IntersectionCut();
|
||||
|
||||
/**
|
||||
* Sets the desired face of the convex set.
|
||||
*
|
||||
* @param index Index of the face.
|
||||
* @param line Line that supports the face.
|
||||
*/
|
||||
void set_face(int index, Line line);
|
||||
|
||||
rational get_continuous_coefficient(rational rx, rational ry);
|
||||
|
||||
double get_trivial_lifting_coefficient_double(double rx, double ry);
|
||||
rational get_trivial_lifting_coefficient(rational rx, rational ry);
|
||||
void pre_lifting();
|
||||
|
||||
private:
|
||||
double d_r0x, d_p, d_d0x, d_d0y, d_d1x, d_d1y;
|
||||
rational p, r0x;
|
||||
bool pre_lifting_ready;
|
||||
};
|
||||
|
||||
/**
|
||||
* Models an intersection cut associated with a wedge.
|
||||
*/
|
||||
class WedgeCut : public IntersectionCut {
|
||||
public:
|
||||
/**
|
||||
* Constructs a wedge cut from the provided data.
|
||||
*
|
||||
* @param f A fractional point in the interior of the wedge.
|
||||
* @param left A point on the left face of the wedge.
|
||||
* @param apex The apex of the wedge.
|
||||
* @param right A point on the right face of the wedge.
|
||||
*/
|
||||
WedgeCut(Point f, Point left, Point apex, Point right);
|
||||
};
|
||||
|
||||
/**
|
||||
* Models an intersection cut associated with a split.
|
||||
*/
|
||||
class SplitCut : public IntersectionCut {
|
||||
public:
|
||||
/**
|
||||
* Constructs a split cut from the provided data.
|
||||
*
|
||||
* @param f A fractional point in the interior of the split
|
||||
* @param left A point on the left face of the split
|
||||
* @param right A poit on the right face of the split
|
||||
* @param direction The direction in which the split in unbounded.
|
||||
*/
|
||||
SplitCut(Point f, Point left, Point right, Point direction);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class can be used to generate wedge cuts.
|
||||
*/
|
||||
class WedgeCutGenerator: public SingleRowCutGenerator {
|
||||
private:
|
||||
bool finished;
|
||||
q::dvec f, r1;
|
||||
int r1_offset;
|
||||
int cur_facet;
|
||||
Knapsack2 knapsack;
|
||||
static int max_depth;
|
||||
int n_knapsacks;
|
||||
|
||||
private:
|
||||
Constraint *cut;
|
||||
void eval_next();
|
||||
static q::dvec intersection(
|
||||
q::dvec a, q::dvec b, q::dvec c, q::dvec d);
|
||||
|
||||
public:
|
||||
WedgeCutGenerator(Row &row);
|
||||
~WedgeCutGenerator();
|
||||
|
||||
bool has_next();
|
||||
Constraint* next();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* WEDGE_CUT_GENERATOR_HPP_ */
|
||||
533
onerow/library/src/cplex_helper.cpp
Normal file
533
onerow/library/src/cplex_helper.cpp
Normal file
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <ilcplex/cplex.h>
|
||||
#include <qxx/rational.hpp>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
#include <onerow/single_row_cut_generator.hpp>
|
||||
#include <onerow/cplex_helper.hpp>
|
||||
#include <onerow/geometry.hpp>
|
||||
#include <onerow/stats.hpp>
|
||||
#include <onerow/params.hpp>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
static bool debug = false;
|
||||
|
||||
CplexHelper::CplexHelper(CPXENVptr _env, CPXLPptr _lp) :
|
||||
env(_env), lp(_lp), is_integer(0), n_cuts(0), n_rows(0), ub(0), lb(0),
|
||||
cstat(0), cplex_rows(0), first_solution(0), current_solution(0),
|
||||
optimal_solution(0), current_round(0), n_good_rows(-1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CplexHelper::~CplexHelper()
|
||||
{
|
||||
if (cplex_rows)
|
||||
{
|
||||
for (int i = 0; i < n_rows; i++)
|
||||
{
|
||||
delete[] cplex_rows[i].pi;
|
||||
delete[] cplex_rows[i].indices;
|
||||
}
|
||||
|
||||
delete[] cplex_rows;
|
||||
}
|
||||
|
||||
if(ub) delete[] ub;
|
||||
if(lb) delete[] lb;
|
||||
if(cstat) delete[] cstat;
|
||||
|
||||
if (is_integer)
|
||||
delete[] is_integer;
|
||||
}
|
||||
|
||||
|
||||
#define return_if_neq(a,b) if((a)<(b)) return true; if((a)>(b)) return false;
|
||||
|
||||
bool CplexRow::operator<(const CplexRow &other) const
|
||||
{
|
||||
return_if_neq(fabs(pi_zero-0.5), fabs(other.pi_zero-0.5));
|
||||
|
||||
return_if_neq(nz, other.nz);
|
||||
|
||||
for (int i = 0; i < nz; i++)
|
||||
{
|
||||
return_if_neq(indices[i], other.indices[i]);
|
||||
double diff = pi[i] - other.pi[i];
|
||||
if(diff < -ZERO_CUTOFF) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CplexRow::print(double *solution)
|
||||
{
|
||||
for (int i = 0; i < nz; i++)
|
||||
{
|
||||
printf("%lf x%d ", pi[i], indices[i]);
|
||||
|
||||
if (solution)
|
||||
printf("(%lf) ", solution[indices[i]]);
|
||||
}
|
||||
|
||||
printf("<= %lf ", pi_zero);
|
||||
|
||||
if (solution)
|
||||
printf("violation=%lf", get_violation(solution));
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
double CplexRow::get_violation(double *solution)
|
||||
{
|
||||
double v = 0;
|
||||
for (int i = 0; i < nz; i++)
|
||||
{
|
||||
v += pi[i] * solution[indices[i]];
|
||||
}
|
||||
v -= pi_zero;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Constraint CplexHelper::cplex_row_to_constraint(const CplexRow &cplex_row)
|
||||
{
|
||||
Constraint constraint;
|
||||
constraint.pi.resize(n_cols);
|
||||
|
||||
rational pi_zero = rational(cplex_row.pi_zero);
|
||||
|
||||
for (int j = 0; j < cplex_row.nz; j++)
|
||||
{
|
||||
int index = cplex_row.indices[j];
|
||||
rational pij = rational(cplex_row.pi[j]);
|
||||
|
||||
if (cstat[index] == CPX_AT_LOWER)
|
||||
pi_zero -= rational(lb[index]) * pij;
|
||||
|
||||
if (cstat[index] == CPX_AT_UPPER)
|
||||
{
|
||||
pi_zero -= rational(ub[index]) * pij;
|
||||
pij = -pij;
|
||||
}
|
||||
|
||||
constraint.pi.push_nz(cplex_row.indices[j], pij.reduce(REDUCE_FACTOR_COEFFICIENT));
|
||||
}
|
||||
|
||||
constraint.pi_zero = rational(pi_zero).reduce(REDUCE_FACTOR_RHS);
|
||||
|
||||
return constraint;
|
||||
}
|
||||
|
||||
|
||||
CplexRow CplexHelper::constraint_to_cplex_row(const Constraint &cut)
|
||||
{
|
||||
CplexRow cplex_row;
|
||||
cplex_row.pi = new double[n_cols];
|
||||
cplex_row.indices = new int[cut.pi.nz()];
|
||||
cplex_row.pi_zero = cut.pi_zero.get_double();
|
||||
cplex_row.depth = cut.depth;
|
||||
cplex_row.nz = cut.pi.nz();
|
||||
|
||||
double max_pi = -INFINITY;
|
||||
double min_pi = INFINITY;
|
||||
|
||||
int cut_nz = cut.pi.nz();
|
||||
for (int j = 0; j < cut_nz; j++)
|
||||
{
|
||||
int index = cut.pi.index(j);
|
||||
double pij = cut.pi.value(j).get_double();
|
||||
|
||||
if (fabs(pij) < ZERO_CUTOFF)
|
||||
pij = 0;
|
||||
|
||||
max_pi = std::max(max_pi, fabs(pij));
|
||||
|
||||
if (fabs(pij) > 0)
|
||||
min_pi = std::min(min_pi, fabs(pij));
|
||||
|
||||
if (cstat[index] == CPX_AT_LOWER)
|
||||
cplex_row.pi_zero += lb[index] * pij;
|
||||
|
||||
if (cstat[index] == CPX_AT_UPPER)
|
||||
{
|
||||
pij = -pij;
|
||||
cplex_row.pi_zero += ub[index] * pij;
|
||||
}
|
||||
|
||||
cplex_row.indices[j] = index;
|
||||
cplex_row.pi[j] = pij;
|
||||
}
|
||||
|
||||
cplex_row.dynamism = max_pi / min_pi;
|
||||
|
||||
return cplex_row;
|
||||
}
|
||||
|
||||
|
||||
void CplexHelper::add_cut(Constraint *cut)
|
||||
{
|
||||
|
||||
CplexRow cplex_row = constraint_to_cplex_row(*cut);
|
||||
double violation = cplex_row.get_violation(current_solution);
|
||||
|
||||
if (optimal_solution)
|
||||
assert(cplex_row.get_violation(optimal_solution) <= MIN_CUT_VIOLATION);
|
||||
|
||||
if (first_solution)
|
||||
assert(cplex_row.get_violation(first_solution) >= MIN_CUT_VIOLATION);
|
||||
|
||||
#pragma omp critical
|
||||
if (cplex_row.dynamism < MAX_CUT_DYNAMISM && violation >= MIN_CUT_VIOLATION)
|
||||
{
|
||||
auto p = cut_buffer.insert(cplex_row);
|
||||
|
||||
// duplicate cut
|
||||
if (!p.second)
|
||||
{
|
||||
delete[] cplex_row.pi;
|
||||
delete[] cplex_row.indices;
|
||||
}
|
||||
|
||||
if (cut_buffer.size() >= MAX_CUT_BUFFER_SIZE)
|
||||
{
|
||||
flush_cuts();
|
||||
solve(false);
|
||||
}
|
||||
}
|
||||
// rejected cut
|
||||
else
|
||||
{
|
||||
delete[] cplex_row.pi;
|
||||
delete[] cplex_row.indices;
|
||||
}
|
||||
|
||||
delete cut;
|
||||
}
|
||||
|
||||
|
||||
void CplexHelper::flush_cuts()
|
||||
{
|
||||
int begin = 0;
|
||||
char sense = 'L';
|
||||
|
||||
for (CplexRow cplex_row : cut_buffer)
|
||||
{
|
||||
Stats::add_cut(cplex_row.depth);
|
||||
|
||||
total_cuts++;
|
||||
|
||||
CPXaddrows(env, lp, 0, 1, cplex_row.nz, &cplex_row.pi_zero, &sense,
|
||||
&begin, cplex_row.indices, cplex_row.pi, NULL, NULL);
|
||||
|
||||
delete[] cplex_row.pi;
|
||||
delete[] cplex_row.indices;
|
||||
}
|
||||
|
||||
cut_buffer.clear();
|
||||
}
|
||||
|
||||
|
||||
Row* CplexHelper::get_tableau_row(int index)
|
||||
{
|
||||
Row *row = new Row;
|
||||
row->basic_var_index = cplex_rows[index].head;
|
||||
row->is_integer = is_integer;
|
||||
row->c = cplex_row_to_constraint(cplex_rows[index]);
|
||||
|
||||
if (optimal_solution)
|
||||
assert(cplex_rows[index].get_violation(optimal_solution) <= 0.001);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
printf("ROW %d\n", index);
|
||||
cplex_rows[index].print(optimal_solution);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
int CplexHelper::solve(bool should_end_round)
|
||||
{
|
||||
// Optimize
|
||||
int status = CPXlpopt(env, lp);
|
||||
if (status)
|
||||
{
|
||||
fprintf(stderr, "Could not optimize (%d)\n", status);
|
||||
throw std::runtime_error("CplexHelper::solve_mip");
|
||||
}
|
||||
|
||||
// Get status
|
||||
char buffer[512];
|
||||
double objval;
|
||||
|
||||
status = CPXgetstat(env, lp);
|
||||
|
||||
CPXgetstatstring(env, status, buffer);
|
||||
CPXgetobjval(env, lp, &objval);
|
||||
time_printf(" %.6lf [%s] \n", objval, buffer);
|
||||
|
||||
assert(status == 1);
|
||||
|
||||
// Store current solution
|
||||
if (!current_solution)
|
||||
current_solution = new double[n_cols];
|
||||
|
||||
CPXgetx(env, lp, current_solution, 0, n_cols - 1);
|
||||
|
||||
// During first round, store basis and fractional solution
|
||||
if (current_round == 0)
|
||||
{
|
||||
read_basis();
|
||||
|
||||
first_solution = new double[n_cols];
|
||||
for (int i = 0; i < n_cols; i++)
|
||||
first_solution[i] = current_solution[i];
|
||||
}
|
||||
|
||||
if (should_end_round)
|
||||
Stats::set_solution(current_round++, objval, string(buffer));
|
||||
|
||||
return objval;
|
||||
}
|
||||
|
||||
|
||||
void CplexHelper::read_columns()
|
||||
{
|
||||
n_rows = CPXgetnumrows(env, lp);
|
||||
n_cols = CPXgetnumcols(env, lp);
|
||||
|
||||
is_integer = new bool[n_cols];
|
||||
|
||||
char ctype[n_cols];
|
||||
CPXgetctype(env, lp, ctype, 0, n_cols - 1);
|
||||
|
||||
for (int i = 0; i < n_cols; i++)
|
||||
{
|
||||
switch (ctype[i])
|
||||
{
|
||||
case CPX_BINARY:
|
||||
case CPX_INTEGER:
|
||||
case CPX_SEMIINT:
|
||||
is_integer[i] = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
is_integer[i] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
time_printf("Fetched %d rows, %d cols.\n", n_rows, n_cols);
|
||||
}
|
||||
|
||||
|
||||
void CplexHelper::read_basis()
|
||||
{
|
||||
time_printf("Reading basis...\n");
|
||||
|
||||
ub = new double[n_cols];
|
||||
lb = new double[n_cols];
|
||||
cstat = new int[n_cols];
|
||||
|
||||
int *head = new int[n_rows];
|
||||
int *rstat = new int[n_rows];
|
||||
double *rhs = new double[n_rows];
|
||||
|
||||
CPXgetbhead(env, lp, head, rhs);
|
||||
CPXgetub(env, lp, ub, 0, n_cols - 1);
|
||||
CPXgetlb(env, lp, lb, 0, n_cols - 1);
|
||||
CPXgetbase(env, lp, cstat, rstat);
|
||||
|
||||
cplex_rows = new CplexRow[n_rows];
|
||||
assert(cplex_rows != 0);
|
||||
|
||||
eta_reset();
|
||||
eta_count = 0;
|
||||
eta_total = n_rows;
|
||||
std::thread eta(&CplexHelper::eta_print, this);
|
||||
|
||||
for (int i = 0; i < n_rows; i++)
|
||||
{
|
||||
int nz = 0;
|
||||
double pi[n_cols];
|
||||
|
||||
CPXbinvarow(env, lp, i, pi);
|
||||
|
||||
for (int j = 0; j < n_cols; j++)
|
||||
{
|
||||
if (fabs(pi[j]) < ZERO_CUTOFF)
|
||||
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++;
|
||||
}
|
||||
|
||||
cplex_rows[i].nz = nz;
|
||||
cplex_rows[i].depth = 0;
|
||||
cplex_rows[i].pi = new double[nz];
|
||||
cplex_rows[i].indices = new int[nz];
|
||||
cplex_rows[i].pi_zero = rhs[i];
|
||||
cplex_rows[i].head = head[i];
|
||||
|
||||
if(fabs(cplex_rows[i].pi_zero) < ZERO_CUTOFF)
|
||||
cplex_rows[i].pi_zero = 0;
|
||||
|
||||
int k = 0;
|
||||
for (int j = 0; j < n_cols; j++)
|
||||
{
|
||||
if (fabs(pi[j]) < ZERO_CUTOFF)
|
||||
continue;
|
||||
cplex_rows[i].pi[k] = pi[j];
|
||||
cplex_rows[i].indices[k++] = j;
|
||||
}
|
||||
|
||||
eta_count++;
|
||||
}
|
||||
|
||||
eta.join();
|
||||
|
||||
delete[] head;
|
||||
delete[] rstat;
|
||||
delete[] rhs;
|
||||
}
|
||||
|
||||
|
||||
void CplexHelper::print_basis()
|
||||
{
|
||||
int n_rows = CPXgetnumrows(env, lp);
|
||||
int n_cols = CPXgetnumcols(env, lp);
|
||||
|
||||
double y[n_rows];
|
||||
double ub[n_cols];
|
||||
double lb[n_cols];
|
||||
double dj[n_cols];
|
||||
|
||||
CPXgetub(env, lp, ub, 0, n_cols - 1);
|
||||
CPXgetlb(env, lp, lb, 0, n_cols - 1);
|
||||
CPXgetdj(env, lp, dj, 0, n_cols - 1);
|
||||
|
||||
cout << "basis inverse:" << endl;
|
||||
for (int i = 0; i < n_rows; i++)
|
||||
{
|
||||
CPXbinvrow(env, lp, i, y);
|
||||
for (int k = 0; k < n_rows; k++)
|
||||
cout << rational(y[k]) << " ";
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
int cstat[n_cols];
|
||||
int rstat[n_rows];
|
||||
CPXgetbase(env, lp, cstat, rstat);
|
||||
|
||||
cout << "column status:" << endl;
|
||||
for (int i = 0; i < n_cols; i++)
|
||||
{
|
||||
cout << i << ": " << cstat[i] << " " << lb[i] << "..." << ub[i] << " "
|
||||
<< dj[i] << " " << (is_integer[i] ? "int" : "cont") << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CplexHelper::print_solution(double *x)
|
||||
{
|
||||
for (int i = 0; i < n_cols; i++)
|
||||
if (fabs(x[i]) > ZERO_CUTOFF)
|
||||
time_printf(" x%d = %.6lf\n", i, x[i]);
|
||||
}
|
||||
|
||||
void CplexHelper::eta_reset()
|
||||
{
|
||||
time(&eta_start);
|
||||
}
|
||||
|
||||
void CplexHelper::eta_print()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
sleep(ETA_UPDATE_INTERVAL);
|
||||
|
||||
if (eta_count == 0)
|
||||
{
|
||||
printf("\r\r%3.0f%% ETA: unknown\r", 0.0);
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eta_count >= eta_total)
|
||||
break;
|
||||
|
||||
time_t eta_now;
|
||||
time(&eta_now);
|
||||
|
||||
double diff = difftime(eta_now, eta_start);
|
||||
double eta = diff / eta_count * eta_total;
|
||||
|
||||
time_t eta_date = eta_start + eta;
|
||||
tm *ttm = localtime(&eta_date);
|
||||
|
||||
printf("\r%3.0f%% ", 100.0 * eta_count / eta_total);
|
||||
printf("ETA: %04d-%02d-%02d %02d:%02d:%02d %d / %d\r",
|
||||
ttm->tm_year + 1900, ttm->tm_mon + 1, ttm->tm_mday,
|
||||
ttm->tm_hour, ttm->tm_min, ttm->tm_sec,
|
||||
eta_count, eta_total);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf("\r \r");
|
||||
}
|
||||
|
||||
void CplexHelper::find_good_rows()
|
||||
{
|
||||
n_good_rows = 0;
|
||||
good_rows = new int[n_rows];
|
||||
|
||||
time_printf("Finding interesting rows...\n");
|
||||
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
for (int i = 0; i < n_rows; i++)
|
||||
{
|
||||
Row *row = get_tableau_row(i);
|
||||
|
||||
if (row->c.pi_zero.frac() != 0 &&
|
||||
row->is_integer[row->basic_var_index])
|
||||
{
|
||||
good_rows[n_good_rows++] = i;
|
||||
}
|
||||
|
||||
delete row;
|
||||
}
|
||||
|
||||
time_printf(" %d rows found\n", n_good_rows, n_rows);
|
||||
}
|
||||
77
onerow/library/src/geometry.cpp
Normal file
77
onerow/library/src/geometry.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <onerow/geometry.hpp>
|
||||
|
||||
Point::Point() :
|
||||
x(0), y(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Point::Point(rational _x, rational _y) :
|
||||
x(_x), y(_y)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Point Point::operator+(const Point& p) const
|
||||
{
|
||||
return Point(x + p.x, y + p.y);
|
||||
}
|
||||
|
||||
Point Point::operator-(const Point& p) const
|
||||
{
|
||||
return Point(x - p.x, y - p.y);
|
||||
}
|
||||
|
||||
rational Point::operator*(const Point& p) const
|
||||
{
|
||||
return x * p.x + y * p.y;
|
||||
}
|
||||
|
||||
Point Point::operator*(rational scale) const
|
||||
{
|
||||
return Point(scale * x, scale * y);
|
||||
}
|
||||
|
||||
Line::Line()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Line::Line(Point _p1, Point _p2) :
|
||||
p1(_p1), p2(_p2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Line::Line(rational x1, rational y1, rational x2, rational y2) :
|
||||
p1(Point(x1, y1)), p2(Point(x2, y2))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Point &p)
|
||||
{
|
||||
os << "(" << p.x << "," << p.y << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Line &l)
|
||||
{
|
||||
os << l.p1 << "--" << l.p2;
|
||||
return os;
|
||||
}
|
||||
50
onerow/library/src/gomory_cut_generator.cpp
Normal file
50
onerow/library/src/gomory_cut_generator.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <onerow/gomory_cut_generator.hpp>
|
||||
|
||||
GomoryCutGenerator::GomoryCutGenerator(Row &r) :
|
||||
SingleRowCutGenerator(r), finished(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GomoryCutGenerator::~GomoryCutGenerator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool GomoryCutGenerator::has_next()
|
||||
{
|
||||
return !finished;
|
||||
}
|
||||
|
||||
Constraint* GomoryCutGenerator::next()
|
||||
{
|
||||
if (!has_next())
|
||||
throw std::out_of_range("");
|
||||
|
||||
Constraint *cut = new Constraint;
|
||||
int nz = row.c.pi.nz();
|
||||
cut->pi_zero = row.c.pi_zero.floor();
|
||||
|
||||
cut->pi.resize(row.c.pi.size());
|
||||
for (int i = 0; i < nz; i++)
|
||||
cut->pi.push(row.c.pi.index(i), row.c.pi.value(i).floor());
|
||||
|
||||
finished = true;
|
||||
return cut;
|
||||
}
|
||||
248
onerow/library/src/knapsack2.cpp
Normal file
248
onerow/library/src/knapsack2.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <qxx/rational.hpp>
|
||||
#include <qxx/dlu.hpp>
|
||||
|
||||
#include <onerow/knapsack2.hpp>
|
||||
#include <onerow/geometry.hpp>
|
||||
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
|
||||
|
||||
// **************************************************************************
|
||||
//
|
||||
// **************************************************************************
|
||||
//#define DEBUG(s...) gmp_printf(s)
|
||||
#define DEBUG(s...)
|
||||
|
||||
// **************************************************************************
|
||||
//
|
||||
// **************************************************************************
|
||||
Knapsack2::Knapsack2()
|
||||
{
|
||||
}
|
||||
|
||||
Knapsack2::Knapsack2(rational f, rational r1)
|
||||
{
|
||||
eval(f, r1);
|
||||
}
|
||||
|
||||
Knapsack2::~Knapsack2()
|
||||
{
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
//
|
||||
// **************************************************************************
|
||||
void Knapsack2::clear()
|
||||
{
|
||||
list.clear();
|
||||
}
|
||||
|
||||
|
||||
// **************************************************************************
|
||||
//
|
||||
// **************************************************************************
|
||||
void Knapsack2::push(int side,
|
||||
const q::dvec &l, const q::dvec &u, const q::dvec &o)
|
||||
{
|
||||
Knapsack2Vertex v;
|
||||
|
||||
v.side = side;
|
||||
v.lower = l;
|
||||
v.upper = u;
|
||||
v.opposed = o;
|
||||
v.lower.resize(2);
|
||||
v.upper.resize(2);
|
||||
v.opposed.resize(2);
|
||||
|
||||
list.push_back(v);
|
||||
}
|
||||
|
||||
//int hit_count = 0;
|
||||
//int miss_count = 0;
|
||||
|
||||
// **************************************************************************
|
||||
//
|
||||
// **************************************************************************
|
||||
void Knapsack2::eval(rational fx, rational r1x)
|
||||
{
|
||||
clear();
|
||||
|
||||
rational fr1frac, fr1floor, hslope;
|
||||
q::dvec a(3), b(3), p(3), f(3), g(3), h(3), r1(3), wy(3), wa(3), wb(3);
|
||||
q::dmat w(3, 3), u(3, 3), ta(3, 3), tb(3, 3), tx(3, 3);
|
||||
|
||||
a[0] = 0; a[1] = 0; a[2] = 1;
|
||||
b[0] = 1; b[1] = 0; b[2] = 1;
|
||||
p[2] = 1;
|
||||
f[0] = fx.frac(); f[1] = 0; f[2] = 1;
|
||||
g[2] = 1;
|
||||
h[2] = 1;
|
||||
r1[0] = r1x; r1[1] = 1; r1[2] = 0;
|
||||
|
||||
w.set_identity();
|
||||
w(0, 2) = fx.floor();
|
||||
tb(0, 1) = 1; tb(0, 2) = 1; tb(1, 2) = 1;
|
||||
tb(2, 0) = tb(2, 1) = tb(2, 2) = 1;
|
||||
ta(0, 1) = 1; ta(1, 2) = 1;
|
||||
ta(2, 0) = ta(2, 1) = ta(2, 2) = 1;
|
||||
|
||||
int it = 0;
|
||||
|
||||
wa = w * a;
|
||||
DEBUG("-: A vertex (0, 0)\t\t--> v A (%Qd, %Qd)\n",
|
||||
wa[0].v, wa[1].v);
|
||||
wb = w * b;
|
||||
DEBUG("-: B vertex (1, 0)\t\t--> v B (%Qd, %Qd)\n",
|
||||
wb[0].v, wb[1].v);
|
||||
|
||||
while (1) {
|
||||
DEBUG("%d: -----------------------------\n", it);
|
||||
DEBUG("%d: f = (%Qd, %Qd, %Qd)\n",
|
||||
it, f[0].v, f[1].v, f[2].v);
|
||||
DEBUG("%d: r1 = (%Qd, %Qd, %Qd)\n",
|
||||
it, r1[0].v, r1[1].v, r1[2].v);
|
||||
DEBUG("%d: a = (%Qd, %Qd, %Qd)\n",
|
||||
it, a[0].v, a[1].v, a[2].v);
|
||||
DEBUG("%d: b = (%Qd, %Qd, %Qd)\n",
|
||||
it, b[0].v, b[1].v, b[2].v);
|
||||
|
||||
// Step 1
|
||||
fr1frac = (f[0] + r1[0]).frac();
|
||||
fr1floor = (f[0] + r1[0]).floor();
|
||||
|
||||
if (f[0] == fr1frac) {
|
||||
wy = w * r1;
|
||||
DEBUG("%d: AB ray (%Qd, %Qd)"
|
||||
"\t\t--> r AB (%Qd, %Qd)\n",
|
||||
it, r1[0].v, r1[1].v, wy[0].v, wy[1].v);
|
||||
push(KNAPSACK2_RAY, wa, wy, wb);
|
||||
break;
|
||||
} else if (f[0] < fr1frac) {
|
||||
DEBUG("%d: hit right\n", it);
|
||||
|
||||
g[1] = (q::mpq(1) - f[0]) / (fr1frac - f[0]);
|
||||
g[0] = q::mpq(1) + fr1floor * g[1];
|
||||
|
||||
b[1] = g[1].floor();
|
||||
b[0] = q::mpq(1) + fr1floor * b[1];
|
||||
|
||||
p[1] = b[1] + 1;
|
||||
p[0] = q::mpq(1) + fr1floor * p[1];
|
||||
|
||||
if (b[1] == g[1]) {
|
||||
wy = w * b;
|
||||
DEBUG("%d: AB vertex (%Qd, %Qd)"
|
||||
"\t\t--> v AB (%Qd, %Qd)\n",
|
||||
it, b[0].v, b[1].v, wy[0].v, wy[1].v);
|
||||
push(KNAPSACK2_BOTH, wa, wy, wb);
|
||||
break;
|
||||
}
|
||||
|
||||
tx(0, 0) = 0;
|
||||
tx(1, 0) = 0;
|
||||
tx(2, 0) = 1;
|
||||
tx.set_col(1, b);
|
||||
tx.set_col(2, p);
|
||||
|
||||
u = tb * tx.inv();
|
||||
|
||||
//tb.dump("tb");
|
||||
//tx.dump("tx");
|
||||
//u.dump("u");
|
||||
|
||||
wy = w * b;
|
||||
|
||||
push(KNAPSACK2_RIGHT, wb, wy, wa);
|
||||
|
||||
wb = wy;
|
||||
|
||||
DEBUG("%d: B vertex (%Qd, %Qd)"
|
||||
"\t\t--> v B (%Qd, %Qd)"
|
||||
" opposed (%Qd, %Qd)\n",
|
||||
it, b[0].v, b[1].v, wb[0].v, wb[1].v,
|
||||
wa[0].v, wa[1].v);
|
||||
|
||||
hslope = b[0] / b[1];
|
||||
h[1] = f[0] / (hslope - r1[0]);
|
||||
h[0] = hslope * h[1];
|
||||
} else {
|
||||
DEBUG("%d: hit left\n", it);
|
||||
|
||||
g[1] = f[0] / (f[0] - fr1frac);
|
||||
g[0] = fr1floor * g[1];
|
||||
|
||||
a[1] = g[1].floor();
|
||||
a[0] = fr1floor * a[1];
|
||||
|
||||
p[1] = a[1] + 1;
|
||||
p[0] = fr1floor * p[1];
|
||||
|
||||
if (a[1] == g[1]) {
|
||||
wy = w * a;
|
||||
DEBUG("%d: AB vertex (%Qd, %Qd)"
|
||||
"\t\t--> v AB (%Qd, %Qd)\n",
|
||||
it, a[0].v, a[1].v, wy[0].v, wy[1].v);
|
||||
push(KNAPSACK2_BOTH, wa, wy, wb);
|
||||
break;
|
||||
}
|
||||
|
||||
tx.set_col(0, a);
|
||||
tx(0, 1) = 1;
|
||||
tx(1, 1) = 0;
|
||||
tx(2, 1) = 1;
|
||||
tx.set_col(2, p);
|
||||
|
||||
u = ta * tx.inv();
|
||||
|
||||
//tb.dump("ta");
|
||||
//u.dump("u");
|
||||
|
||||
wy = w * a;
|
||||
|
||||
push(KNAPSACK2_LEFT, wa, wy, wb);
|
||||
|
||||
wa = wy;
|
||||
|
||||
DEBUG("%d: A vertex (%Qd, %Qd)"
|
||||
"\t\t--> v A (%Qd, %Qd)"
|
||||
" opposed (%Qd, %Qd)\n",
|
||||
it, a[0].v, a[1].v, wa[0].v, wa[1].v,
|
||||
wb[0].v, wb[1].v);
|
||||
|
||||
hslope = (a[0] - 1) / a[1];
|
||||
h[1] = (f[0] - 1) / (hslope - r1[0]);
|
||||
h[0] = q::mpq(1) + (hslope * h[1]);
|
||||
}
|
||||
|
||||
a = u * a;
|
||||
b = u * b;
|
||||
f = u * h;
|
||||
r1 = u * r1;
|
||||
r1[0] /= r1[1];
|
||||
r1[1] = 1;
|
||||
|
||||
w = w * u.inv();
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
169
onerow/library/src/main.cpp
Normal file
169
onerow/library/src/main.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <ilcplex/cplex.h>
|
||||
|
||||
#include "cplex_helper.hpp"
|
||||
#include "cplex_helper.tpp"
|
||||
#include "stats.hpp"
|
||||
|
||||
#include <valgrind/callgrind.h>
|
||||
#include <gperftools/heap-profiler.h>
|
||||
|
||||
#include "gomory_cut_generator.hpp"
|
||||
#include "mir_cut_generator.hpp"
|
||||
#include "wedge_cut_generator.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *input_filename = 0;
|
||||
char *stats_filename = 0;
|
||||
char *sol_filename = 0;
|
||||
bool enable_gomory_cuts = false;
|
||||
bool enable_wedge_cuts = false;
|
||||
bool enable_mir_cuts = false;
|
||||
|
||||
int c;
|
||||
extern char *optarg;
|
||||
char usage[] = "usage: %s [-gmw] -f model.mps [-s stats.yaml]\n";
|
||||
|
||||
void read_params(int argc, char **argv)
|
||||
{
|
||||
while ((c = getopt(argc, argv, "gwmf:s:x:")) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'g':
|
||||
enable_gomory_cuts = true;
|
||||
break;
|
||||
case 'w':
|
||||
enable_wedge_cuts = true;
|
||||
break;
|
||||
case 'm':
|
||||
enable_mir_cuts = true;
|
||||
break;
|
||||
case 'f':
|
||||
input_filename = optarg;
|
||||
break;
|
||||
case 's':
|
||||
stats_filename = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
sol_filename = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_filename)
|
||||
{
|
||||
fprintf(stderr, "%s: missing model filename\n", argv[0]);
|
||||
fprintf(stderr, usage, argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
read_params(argc, argv);
|
||||
|
||||
Stats::init();
|
||||
|
||||
int status;
|
||||
CPXENVptr env = CPXopenCPLEX(&status);
|
||||
CPXLPptr lp = CPXcreateprob(env, &status, "");
|
||||
CplexHelper cplexHelper(env, lp);
|
||||
|
||||
CPXsetlogfile(env, NULL);
|
||||
CPXsetintparam(env, CPX_PARAM_PREIND, CPX_OFF); // disable presolve
|
||||
CPXsetintparam(env, CPX_PARAM_DATACHECK, CPX_ON); // check consistency
|
||||
CPXsetintparam(env, CPX_PARAM_NUMERICALEMPHASIS, CPX_ON); // numerical precision
|
||||
|
||||
Stats::set_input_filename(string(input_filename));
|
||||
|
||||
// reads input file
|
||||
time_printf("Reading input file: %s...\n", input_filename);
|
||||
status = CPXreadcopyprob(env, lp, input_filename, NULL);
|
||||
if (status)
|
||||
{
|
||||
fprintf(stderr, "could not read input file (%d)\n", status);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cplexHelper.read_columns();
|
||||
|
||||
// read solution
|
||||
if(sol_filename)
|
||||
{
|
||||
time_printf("Reading solution file: %s...\n", sol_filename);
|
||||
FILE *f = fopen(sol_filename, "r");
|
||||
if(!f)
|
||||
{
|
||||
fprintf(stderr, "Could not open solution file (%s).", sol_filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
double *solution = new double[cplexHelper.n_cols];
|
||||
for(int i=0; i<cplexHelper.n_cols; i++)
|
||||
fscanf(f, "%lf", &solution[i]);
|
||||
|
||||
cplexHelper.optimal_solution = solution;
|
||||
}
|
||||
|
||||
|
||||
// relaxes integrality
|
||||
CPXchgprobtype(env, lp, CPXPROB_LP);
|
||||
|
||||
time_printf("Solving first relaxation...\n");
|
||||
cplexHelper.solve(true);
|
||||
|
||||
|
||||
if (enable_gomory_cuts)
|
||||
{
|
||||
time_printf("Generating Gomory cuts...\n");
|
||||
cplexHelper.add_single_row_cuts<GomoryCutGenerator>();
|
||||
cplexHelper.solve(true);
|
||||
}
|
||||
|
||||
if (enable_mir_cuts)
|
||||
{
|
||||
time_printf("Generating MIR cuts...\n");
|
||||
cplexHelper.add_single_row_cuts<MIRCutGenerator>();
|
||||
cplexHelper.solve(true);
|
||||
}
|
||||
|
||||
if (enable_wedge_cuts)
|
||||
{
|
||||
time_printf("Generating wedge cuts...\n");
|
||||
cplexHelper.add_single_row_cuts<WedgeCutGenerator>();
|
||||
cplexHelper.solve(true);
|
||||
}
|
||||
|
||||
if(stats_filename != 0)
|
||||
{
|
||||
time_printf("Writting stats: %s...\n", stats_filename);
|
||||
Stats::write_stats(string(stats_filename));
|
||||
}
|
||||
|
||||
time_printf("Done.\n");
|
||||
CPXcloseCPLEX(&env);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
74
onerow/library/src/mir_cut_generator.cpp
Normal file
74
onerow/library/src/mir_cut_generator.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <onerow/stats.hpp>
|
||||
#include <onerow/mir_cut_generator.hpp>
|
||||
|
||||
MIRCutGenerator::MIRCutGenerator(Row &r) :
|
||||
SingleRowCutGenerator(r), finished(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MIRCutGenerator::~MIRCutGenerator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool MIRCutGenerator::has_next()
|
||||
{
|
||||
return !finished;
|
||||
}
|
||||
|
||||
rational MIRCutGenerator::h(rational a)
|
||||
{
|
||||
if (a > 0) return a;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
rational MIRCutGenerator::f(rational a, rational b)
|
||||
{
|
||||
if (a.frac() <= b.frac())
|
||||
return b.frac() * a.floor() + a.frac();
|
||||
else
|
||||
return b.frac() * a.ceil();
|
||||
}
|
||||
|
||||
Constraint* MIRCutGenerator::next()
|
||||
{
|
||||
if (!has_next())
|
||||
throw std::out_of_range("");
|
||||
|
||||
Constraint *cut = new Constraint;
|
||||
int nz = row.c.pi.nz();
|
||||
cut->pi.resize(row.c.pi.size());
|
||||
cut->pi_zero = -row.c.pi_zero.frac() * row.c.pi_zero.ceil();
|
||||
|
||||
for (int i = 0; i < nz; i++)
|
||||
{
|
||||
int idx = row.c.pi.index(i);
|
||||
|
||||
if (row.is_integer[idx])
|
||||
cut->pi.push(idx, -f(row.c.pi.value(i), row.c.pi_zero));
|
||||
else
|
||||
cut->pi.push(idx, -h(row.c.pi.value(i)));
|
||||
}
|
||||
|
||||
cut->depth = 0;
|
||||
|
||||
finished = true;
|
||||
return cut;
|
||||
}
|
||||
72
onerow/library/src/single_row_cut_generator.cpp
Normal file
72
onerow/library/src/single_row_cut_generator.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <onerow/single_row_cut_generator.hpp>
|
||||
|
||||
SingleRowCutGenerator::SingleRowCutGenerator(Row &r) : row(r)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#define return_if_neq(a,b) if((a)<(b)) return true; if((a)>(b)) return false;
|
||||
|
||||
bool Constraint::operator<(const Constraint &other) const
|
||||
{
|
||||
return_if_neq(pi.nz(), other.pi.nz());
|
||||
return_if_neq(pi_zero, other.pi_zero);
|
||||
|
||||
int nz = pi.nz();
|
||||
for (int i = 0; i < nz; i++)
|
||||
{
|
||||
return_if_neq(pi.index(i), other.pi.index(i));
|
||||
return_if_neq(pi.value(i), other.pi.value(i));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraint::operator==(const Constraint &other) const
|
||||
{
|
||||
return !(operator<(other) || other.operator<(*this));
|
||||
}
|
||||
|
||||
rational Constraint::get_violation(const rational *x)
|
||||
{
|
||||
rational v(0);
|
||||
|
||||
int nz = pi.nz();
|
||||
for (int i = 0; i < nz; i++)
|
||||
v += pi.value(i) * x[pi.index(i)];
|
||||
v -= pi_zero;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Constraint &c)
|
||||
{
|
||||
int nz = c.pi.nz();
|
||||
for (int k = 0; k < nz; k++)
|
||||
{
|
||||
os << c.pi.value(k) << " x" << c.pi.index(k) << " ";
|
||||
}
|
||||
os << "<= " << c.pi_zero;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Row &r)
|
||||
{
|
||||
os << "[" << r.basic_var_index << "] " << r.c;
|
||||
return os;
|
||||
}
|
||||
226
onerow/library/src/stats.cpp
Normal file
226
onerow/library/src/stats.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cassert>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <onerow/params.hpp>
|
||||
#include <onerow/stats.hpp>
|
||||
|
||||
namespace Stats
|
||||
{
|
||||
const int MAX_ROUNDS = 100;
|
||||
const int MAX_TIMERS = 100;
|
||||
|
||||
string input_filename;
|
||||
|
||||
double opt_value[MAX_ROUNDS];
|
||||
string opt_sol_status[MAX_ROUNDS];
|
||||
|
||||
unsigned long n_cuts_total = 0;
|
||||
unsigned long n_cuts_depth[MAX_CUT_DEPTH] = { 0 };
|
||||
|
||||
unsigned long n_generated_cuts_total = 0;
|
||||
unsigned long n_generated_cuts_round[MAX_CUT_DEPTH] = { 0 };
|
||||
unsigned long n_generated_cuts_depth[MAX_CUT_DEPTH] = { 0 };
|
||||
|
||||
unsigned long trivial_lifting_m_count = 0;
|
||||
unsigned long trivial_lifting_m_sum = 0;
|
||||
unsigned long trivial_lifting_m_max = 0;
|
||||
|
||||
unsigned long n_coefficients = 0;
|
||||
unsigned long n_integral_coefficients = 0;
|
||||
|
||||
int n_timers = 0;
|
||||
double current_timer_start;
|
||||
double timers[MAX_TIMERS] = {0};
|
||||
|
||||
void init()
|
||||
{
|
||||
input_filename = "";
|
||||
|
||||
for (int i = 0; i < MAX_ROUNDS; i++)
|
||||
{
|
||||
opt_value[i] = INFINITY;
|
||||
opt_sol_status[i] = "";
|
||||
}
|
||||
}
|
||||
|
||||
void add_cut(int depth)
|
||||
{
|
||||
n_cuts_total++;
|
||||
n_cuts_depth[depth]++;
|
||||
}
|
||||
|
||||
void add_generated_cut(int round, int depth)
|
||||
{
|
||||
n_generated_cuts_total++;
|
||||
n_generated_cuts_round[round]++;
|
||||
n_generated_cuts_depth[depth]++;
|
||||
}
|
||||
|
||||
void add_trivial_lifting_m(unsigned long m)
|
||||
{
|
||||
trivial_lifting_m_count++;
|
||||
trivial_lifting_m_sum += m;
|
||||
trivial_lifting_m_max = std::max(trivial_lifting_m_max, m);
|
||||
}
|
||||
|
||||
void add_coefficient(bool integral)
|
||||
{
|
||||
n_coefficients++;
|
||||
if(integral) n_integral_coefficients++;
|
||||
}
|
||||
|
||||
void set_solution(int round, double sol, string status)
|
||||
{
|
||||
opt_value[round] = sol;
|
||||
opt_sol_status[round] = status;
|
||||
}
|
||||
|
||||
void set_input_filename(string n)
|
||||
{
|
||||
input_filename = n;
|
||||
}
|
||||
|
||||
void write_stats(string f)
|
||||
{
|
||||
FILE *out = fopen(f.c_str(), "w");
|
||||
|
||||
fprintf(out, "input_file:\n %s\n", input_filename.c_str());
|
||||
|
||||
// solution value and status
|
||||
fprintf(out, "sol_value:\n");
|
||||
for (int i = 0; i < MAX_ROUNDS; i++)
|
||||
if (opt_value[i] < INFINITY)
|
||||
fprintf(out, " %d: %-.6f\n", i, opt_value[i]);
|
||||
|
||||
fprintf(out, "sol_status:\n");
|
||||
for (int i = 0; i < MAX_ROUNDS; i++)
|
||||
if (opt_value[i] < INFINITY)
|
||||
fprintf(out, " %d: %s\n", i, opt_sol_status[i].c_str());
|
||||
|
||||
// added cuts
|
||||
if(n_cuts_total > 0)
|
||||
{
|
||||
fprintf(out, "n_added_cuts:\n");
|
||||
|
||||
fprintf(out, " total:\n %ld\n", n_cuts_total);
|
||||
|
||||
fprintf(out, " depth:\n");
|
||||
for (int i = 0; i < MAX_CUT_DEPTH; i++)
|
||||
if (n_cuts_depth[i] > 0)
|
||||
fprintf(out, " %d: %ld\n", i, n_cuts_depth[i]);
|
||||
}
|
||||
|
||||
// generated cuts
|
||||
if(n_generated_cuts_total > 0)
|
||||
{
|
||||
fprintf(out, "n_generated_cuts:\n");
|
||||
|
||||
fprintf(out, " total:\n %ld\n", n_generated_cuts_total);
|
||||
|
||||
fprintf(out, " round:\n");
|
||||
for (int i = 0; i < MAX_ROUNDS; i++)
|
||||
if (n_generated_cuts_round[i] > 0)
|
||||
fprintf(out, " %d: %ld\n", i, n_generated_cuts_round[i]);
|
||||
|
||||
fprintf(out, " depth:\n");
|
||||
for (int i = 0; i < MAX_CUT_DEPTH; i++)
|
||||
if (n_generated_cuts_depth[i] > 0)
|
||||
fprintf(out, " %d: %ld\n", i, n_generated_cuts_depth[i]);
|
||||
}
|
||||
|
||||
// trivial lifting
|
||||
if(trivial_lifting_m_count > 0)
|
||||
{
|
||||
double integral_coefficients = ((double) n_integral_coefficients) / n_coefficients;
|
||||
double average_m = ((double) (trivial_lifting_m_sum))
|
||||
/ trivial_lifting_m_count;
|
||||
double slowdown = integral_coefficients * average_m;
|
||||
|
||||
fprintf(out, "trivial_lifting:\n");
|
||||
fprintf(out, " max_m: %ld\n", trivial_lifting_m_max);
|
||||
fprintf(out, " average_m: %.6lf\n", average_m);
|
||||
fprintf(out, " integral_coefficients: %.6lf\n", integral_coefficients);
|
||||
fprintf(out, " slowdown: %.6lf\n", slowdown);
|
||||
}
|
||||
|
||||
if(n_timers > 0)
|
||||
{
|
||||
fprintf(out, "timers:\n");
|
||||
for(int i=0; i<n_timers; i++)
|
||||
fprintf(out, " %d: %.4lf\n", i+1, timers[i]);
|
||||
}
|
||||
|
||||
fprintf(out, "cut_speed:\n");
|
||||
for(int i=0; i<n_timers; i++)
|
||||
fprintf(out, " round_%d: %.4lf\n", i+1, timers[i] / n_generated_cuts_round[i+1]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void start_timer()
|
||||
{
|
||||
assert(current_timer_start == 0);
|
||||
|
||||
time_printf("Starting timer %d...\n", n_timers+1);
|
||||
current_timer_start = get_current_time();
|
||||
timers[n_timers] = 0;
|
||||
}
|
||||
|
||||
void end_timer()
|
||||
{
|
||||
timers[n_timers] = get_current_time() - current_timer_start;
|
||||
|
||||
time_printf("Ending timer %d: %.2lfs\n ", n_timers+1, timers[n_timers]);
|
||||
|
||||
current_timer_start = 0;
|
||||
n_timers++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
double get_current_time(void)
|
||||
{
|
||||
struct rusage ru;
|
||||
|
||||
getrusage (RUSAGE_SELF, &ru);
|
||||
|
||||
return ((double) ru.ru_utime.tv_sec) +
|
||||
((double) ru.ru_utime.tv_usec)/1000001.0;
|
||||
}
|
||||
|
||||
double initial_time = 0;
|
||||
|
||||
void time_printf(const char *fmt, ...)
|
||||
{
|
||||
if(initial_time == 0)
|
||||
initial_time = get_current_time();
|
||||
|
||||
printf("[%10.2lf] ", get_current_time() - initial_time);
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
390
onerow/library/src/wedge_cut_generator.cpp
Normal file
390
onerow/library/src/wedge_cut_generator.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <gmp.h>
|
||||
#include <onerow/wedge_cut_generator.hpp>
|
||||
#include <onerow/stats.hpp>
|
||||
#include <onerow/params.hpp>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::min;
|
||||
|
||||
static bool debug = false;
|
||||
|
||||
IntersectionCut::IntersectionCut(Point _f, int _n, const Line *l) :
|
||||
f(_f), n_faces(_n), pre_lifting_ready(false)
|
||||
{
|
||||
d = new Point[n_faces];
|
||||
if (l != 0)
|
||||
for (int i = 0; i < n_faces; i++)
|
||||
set_face(i, l[i]);
|
||||
}
|
||||
|
||||
IntersectionCut::~IntersectionCut()
|
||||
{
|
||||
delete[] d;
|
||||
}
|
||||
|
||||
void IntersectionCut::set_face(int index, Line line)
|
||||
{
|
||||
if (index < 0 || index >= n_faces)
|
||||
throw std::out_of_range("");
|
||||
|
||||
rational x1 = line.p1.x;
|
||||
rational y1 = line.p1.y;
|
||||
rational x2 = line.p2.x;
|
||||
rational y2 = line.p2.y;
|
||||
|
||||
rational h[3] = { -y2 + y1, x2 - x1, x1 * y2 - x2 * y1 };
|
||||
rational rhs = -(h[2] + h[0] * f.x + h[1] * f.y);
|
||||
|
||||
if (rhs == rational(0))
|
||||
throw std::invalid_argument("");
|
||||
|
||||
d[index].x = h[0] / rhs;
|
||||
d[index].y = h[1] / rhs;
|
||||
}
|
||||
|
||||
rational IntersectionCut::get_continuous_coefficient(rational rx, rational ry)
|
||||
{
|
||||
#ifdef ENABLE_EXTENDED_STATISTICS
|
||||
Stats::add_coefficient(false);
|
||||
#endif
|
||||
|
||||
rational max_coeff(-100000, 1);
|
||||
for (int i = 0; i < n_faces; i++)
|
||||
{
|
||||
rational coeff = rx * d[i].x + ry * d[i].y;
|
||||
if (coeff > max_coeff)
|
||||
max_coeff = coeff;
|
||||
}
|
||||
return max_coeff;
|
||||
}
|
||||
|
||||
void IntersectionCut::pre_lifting()
|
||||
{
|
||||
rational r0y;
|
||||
rational fx = f.x, fy = f.y;
|
||||
rational d0x = d[0].x, d0y = d[0].y;
|
||||
rational d1x = d[1].x, d1y = d[1].y;
|
||||
rational apex_x, apex_y;
|
||||
rational w1 = rational(1) + (d0x * fx + d0y * fy);
|
||||
rational w2 = rational(1) + (d1x * fx + d1y * fy);
|
||||
|
||||
rational m = (d0x*d1y-d0y*d1x);
|
||||
|
||||
if (m == 0)
|
||||
{
|
||||
r0x = -d0y/d0x;
|
||||
r0y = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
apex_x = (d1y * w1 - d0y * w2) / m;
|
||||
apex_y = (-d1x * w1 + d0x * w2) / m;
|
||||
|
||||
r0x = apex_x - fx;
|
||||
r0y = apex_y - fy;
|
||||
|
||||
r0x /= r0y;
|
||||
r0y = 1;
|
||||
}
|
||||
|
||||
assert(d0x < 0);
|
||||
assert(d1x > 0);
|
||||
|
||||
p = r0x*d0x + r0y*d0y;
|
||||
assert(p == r0x*d1x + r0y*d1y);
|
||||
assert(p >= 0);
|
||||
|
||||
this->d_p = p.get_double();
|
||||
this->d_d1x = d1x.get_double();
|
||||
this->d_d1y = d1y.get_double();
|
||||
this->d_d0x = d0x.get_double();
|
||||
this->d_d0y = d0y.get_double();
|
||||
this->d_r0x = r0x.get_double();
|
||||
|
||||
pre_lifting_ready = true;
|
||||
}
|
||||
|
||||
double IntersectionCut::get_trivial_lifting_coefficient_double(double rx, double ry)
|
||||
{
|
||||
if(!pre_lifting_ready) pre_lifting();
|
||||
|
||||
#ifdef ENABLE_EXTENDED_STATISTICS
|
||||
Stats::add_coefficient(true);
|
||||
#endif
|
||||
|
||||
double a = 100000;
|
||||
|
||||
unsigned long k2 = 0;
|
||||
unsigned long M = 10000;
|
||||
|
||||
while (k2 < M)
|
||||
{
|
||||
double b = (k2*d_r0x-rx);
|
||||
|
||||
double a1 = d_d1x * (rx + ceil(b)) + d_d1y * k2;
|
||||
double a2 = d_d0x * (rx + floor(b)) + d_d0y * k2;
|
||||
|
||||
assert(rx + ceil(b) + 0.0001 >= d_r0x*k2);
|
||||
assert(rx + floor(b) - 0.0001 <= d_r0x*k2);
|
||||
|
||||
assert(a1 >= 0);
|
||||
assert(a2 >= 0);
|
||||
|
||||
if(a1 < a) a = a1;
|
||||
if(a2 < a) a = a2;
|
||||
|
||||
if(fabs(d_p) < ZERO_CUTOFF) break;
|
||||
|
||||
M = ceil(a/d_p);
|
||||
|
||||
k2++;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_EXTENDED_STATISTICS
|
||||
Stats::add_trivial_lifting_m(k2);
|
||||
#endif
|
||||
|
||||
assert(a >= 0);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
rational IntersectionCut::get_trivial_lifting_coefficient(rational rx, rational ry)
|
||||
{
|
||||
if(!pre_lifting_ready) pre_lifting();
|
||||
|
||||
#ifdef ENABLE_EXTENDED_STATISTICS
|
||||
Stats::add_coefficient(true);
|
||||
#endif
|
||||
|
||||
rational a = rational(100000, 1);
|
||||
|
||||
unsigned long k2 = 0;
|
||||
unsigned long M = 10000;
|
||||
rational b = -rx;
|
||||
|
||||
while (k2 < M)
|
||||
{
|
||||
rational r_k2((signed) k2);
|
||||
rational a1 = d[1].x*(rx + b.ceil()) + d[1].y * r_k2;
|
||||
rational a2 = d[0].x*(rx + b.floor()) + d[0].y * r_k2;
|
||||
|
||||
assert(rx + b.ceil() >= r0x*r_k2);
|
||||
assert(rx + b.floor() <= r0x*r_k2);
|
||||
|
||||
assert(a1 >= 0);
|
||||
assert(a2 >= 0);
|
||||
|
||||
if(a1 < a) a = a1;
|
||||
if(a2 < a) a = a2;
|
||||
|
||||
if(p == 0) break;
|
||||
M = (a/p).ceil().get_double();
|
||||
|
||||
k2++;
|
||||
b += r0x;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
WedgeCut::WedgeCut(Point _f, Point left, Point apex, Point right) :
|
||||
IntersectionCut(_f, 2)
|
||||
{
|
||||
set_face(0, Line(left, apex));
|
||||
set_face(1, Line(apex, right));
|
||||
}
|
||||
|
||||
SplitCut::SplitCut(Point _f, Point left, Point right, Point direction) :
|
||||
IntersectionCut(_f, 2)
|
||||
{
|
||||
set_face(0, Line(left, left + direction));
|
||||
set_face(1, Line(right, right + direction));
|
||||
}
|
||||
|
||||
WedgeCutGenerator::WedgeCutGenerator(Row &r) :
|
||||
SingleRowCutGenerator(r), finished(false),
|
||||
f(2), r1(2),
|
||||
r1_offset(-1),
|
||||
cur_facet(-1),
|
||||
n_knapsacks(0)
|
||||
{
|
||||
f[0] = r.c.pi_zero.frac();
|
||||
f[1] = 0;
|
||||
cut = new Constraint;
|
||||
|
||||
eval_next();
|
||||
}
|
||||
|
||||
WedgeCutGenerator::~WedgeCutGenerator()
|
||||
{
|
||||
delete cut;
|
||||
}
|
||||
|
||||
bool WedgeCutGenerator::has_next()
|
||||
{
|
||||
return !finished;
|
||||
}
|
||||
|
||||
Constraint* WedgeCutGenerator::next()
|
||||
{
|
||||
if (!has_next())
|
||||
throw std::out_of_range("");
|
||||
|
||||
Constraint *old_cut = cut;
|
||||
|
||||
cut = new Constraint;
|
||||
eval_next();
|
||||
|
||||
return(old_cut);
|
||||
}
|
||||
|
||||
q::dvec WedgeCutGenerator::intersection(
|
||||
q::dvec a, q::dvec b, q::dvec c, q::dvec d)
|
||||
{
|
||||
q::dmat g(2,2);
|
||||
|
||||
g.set_col(0, b - a);
|
||||
g.set_col(1, c - d);
|
||||
|
||||
q::dvec mult = g.inv() * (c - a);
|
||||
|
||||
q::dvec x = a + (b - a) * mult[0];
|
||||
|
||||
assert(x == c + (d - c) * mult[1]);
|
||||
|
||||
return(x);
|
||||
}
|
||||
|
||||
void WedgeCutGenerator::eval_next()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (0 <= cur_facet && cur_facet < (int) knapsack.list.size() && cur_facet <= MAX_CUT_DEPTH)
|
||||
break;
|
||||
|
||||
r1_offset++;
|
||||
|
||||
if (r1_offset >= row.c.pi.nz())
|
||||
{
|
||||
finished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int r1_index = row.c.pi.index(r1_offset);
|
||||
r1[0] = -row.c.pi.value(r1_offset).reduce(REDUCE_FACTOR_R1);
|
||||
r1[1] = 1;
|
||||
|
||||
if (r1_index == row.basic_var_index)
|
||||
continue;
|
||||
|
||||
if (!row.is_integer[r1_index])
|
||||
continue;
|
||||
|
||||
if (r1[0] == 0)
|
||||
continue;
|
||||
|
||||
knapsack.clear();
|
||||
knapsack.eval(f[0], r1[0]);
|
||||
|
||||
n_knapsacks++;
|
||||
cur_facet = 0;
|
||||
}
|
||||
|
||||
Line lines[2];
|
||||
int side = knapsack.list[cur_facet].side;
|
||||
q::dvec &a1 = knapsack.list[cur_facet].lower;
|
||||
q::dvec &a2 = knapsack.list[cur_facet].upper;
|
||||
q::dvec &o = knapsack.list[cur_facet].opposed;
|
||||
|
||||
if (side == KNAPSACK2_RAY)
|
||||
{
|
||||
a2 = o;
|
||||
|
||||
q::dvec b1 = a1 + r1;
|
||||
q::dvec b2 = a2 + r1;
|
||||
|
||||
lines[0] = Line(a1[0], a1[1], b1[0], b1[1]);
|
||||
lines[1] = Line(a2[0], a2[1], b2[0], b2[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
q::dvec apex = intersection(a1, a2, f, f + r1);
|
||||
|
||||
if(side == KNAPSACK2_RIGHT)
|
||||
{
|
||||
lines[1] = Line(a1[0], a1[1], a2[0], a2[1]);
|
||||
lines[0] = Line(o[0], o[1], apex[0], apex[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines[0] = Line(a1[0], a1[1], a2[0], a2[1]);
|
||||
lines[1] = Line(o[0], o[1], apex[0], apex[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if(debug)
|
||||
cout << endl << lines[0] << " " << lines[1] << endl;
|
||||
|
||||
cur_facet++;
|
||||
|
||||
IntersectionCut ic(Point(f[0], f[1]), 2, lines);
|
||||
cut->pi.clear();
|
||||
cut->pi_zero = -1;
|
||||
cut->pi.resize(row.c.pi.size());
|
||||
cut->depth = cur_facet-1;
|
||||
|
||||
Point ray;
|
||||
rational alpha, rx, ry;
|
||||
|
||||
for (int l = 0; l < row.c.pi.nz(); l++)
|
||||
{
|
||||
int j = row.c.pi.index(l);
|
||||
if (j == row.basic_var_index)
|
||||
continue;
|
||||
|
||||
rx = -row.c.pi.value(l);
|
||||
ry = (l == r1_offset ? 1 : 0);
|
||||
|
||||
if (row.is_integer[j] && l != r1_offset)
|
||||
{
|
||||
#ifdef INTERSECTION_CUT_USE_DOUBLE
|
||||
cut->pi.push(j, -ic.get_trivial_lifting_coefficient_double(
|
||||
rx.get_double(), ry.get_double()));
|
||||
#else
|
||||
cut->pi.push(j, -ic.get_trivial_lifting_coefficient(rx, ry));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
cut->pi.push(j, -ic.get_continuous_coefficient(rx, ry));
|
||||
}
|
||||
|
||||
if(debug)
|
||||
cout << "r" << j << ": (" << rx << ", " << ry << ") " << cut->pi[j] << endl;
|
||||
}
|
||||
}
|
||||
49
onerow/library/tests/knapsack2_test.cpp
Normal file
49
onerow/library/tests/knapsack2_test.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <gtest/gtest.h>
|
||||
#include <qxx/rational.hpp>
|
||||
#include <onerow/knapsack2.hpp>
|
||||
typedef q::mpq rational;
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
TEST(Knapsack2Test, test_1)
|
||||
{
|
||||
Knapsack2 k(rational(5,7), rational(3,5));
|
||||
|
||||
// cout << "list:\n";
|
||||
// for(auto v : k.list)
|
||||
// cout << v.lower << " " << v.upper << " " << v.side << " " << v.opposed << endl;
|
||||
|
||||
// cout << "has ray? " << k.has_ray << endl;
|
||||
}
|
||||
|
||||
TEST(Knapsack2Test, test_2)
|
||||
{
|
||||
Knapsack2 k(rational(1,2), rational(1,2));
|
||||
|
||||
// cout << "left:\n";
|
||||
// each(k.left, v)
|
||||
// cout << v->active << v->opposed << endl;
|
||||
//
|
||||
// cout << "right:\n";
|
||||
// each(k.right, v)
|
||||
// cout << v->active << v->opposed << endl;
|
||||
//
|
||||
// cout << "has ray? " << k.has_ray << endl;
|
||||
}
|
||||
66
onerow/library/tests/rational_test.cpp
Normal file
66
onerow/library/tests/rational_test.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <gtest/gtest.h>
|
||||
#include <qxx/rational.hpp>
|
||||
typedef q::mpq rational;
|
||||
|
||||
TEST(RationalTest, reduce_test)
|
||||
{
|
||||
EXPECT_EQ(rational(5), rational(5).reduce(100));
|
||||
EXPECT_EQ(rational(0), rational(0).reduce(100));
|
||||
EXPECT_EQ(rational(1,3), rational(1/3.0).reduce(100));
|
||||
EXPECT_EQ(rational(1,2), rational(1,2).reduce(100));
|
||||
EXPECT_EQ(rational(-1,2), rational(-1,2).reduce(100));
|
||||
}
|
||||
|
||||
TEST(RationalTest, minus_test)
|
||||
{
|
||||
EXPECT_EQ(rational(-1,3), -rational(1,3));
|
||||
}
|
||||
|
||||
TEST(RationalTest, plus_minus_times_test)
|
||||
{
|
||||
rational a(47,17);
|
||||
rational b(113,98);
|
||||
|
||||
EXPECT_EQ(rational(6527,1666), a+b);
|
||||
EXPECT_EQ(rational(2685,1666), a-b);
|
||||
EXPECT_EQ(rational(-2685,1666), -a+b);
|
||||
EXPECT_EQ(rational(5311,1666), a*b);
|
||||
}
|
||||
|
||||
TEST(RationalTest, floor_ceil_frac_test)
|
||||
{
|
||||
EXPECT_EQ(rational(45,17).floor(), rational(2));
|
||||
EXPECT_EQ(rational(45,17).ceil(), rational(3));
|
||||
EXPECT_EQ(rational(45,17).frac(), rational(11,17));
|
||||
|
||||
EXPECT_EQ(rational(-45,17).floor(), rational(-3));
|
||||
EXPECT_EQ(rational(-45,17).ceil(), rational(-2));
|
||||
EXPECT_EQ(rational(-45,17).frac(), rational(6,17));
|
||||
|
||||
EXPECT_EQ(rational(1,2).frac(), rational(1,2));
|
||||
EXPECT_EQ(rational(-1,2).frac(), rational(1,2));
|
||||
}
|
||||
|
||||
TEST(RationalTest, double_to_rational_test)
|
||||
{
|
||||
EXPECT_FLOAT_EQ(rational(1/3.0).get_double(), 1/3.0);
|
||||
EXPECT_FLOAT_EQ(rational(127/15.0).get_double(), 127/15.0);
|
||||
EXPECT_FLOAT_EQ(rational(127/183.0).get_double(), 127/183.0);
|
||||
EXPECT_FLOAT_EQ(rational(513/3577.0).get_double(), 513/3577.0);
|
||||
}
|
||||
19
onerow/library/tests/single_row_generator_test.cpp
Normal file
19
onerow/library/tests/single_row_generator_test.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <gtest/gtest.h>
|
||||
#include <onerow/single_row_cut_generator.hpp>
|
||||
|
||||
267
onerow/library/tests/wedge_cut_generator_test.cpp
Normal file
267
onerow/library/tests/wedge_cut_generator_test.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <gtest/gtest.h>
|
||||
#include <onerow/wedge_cut_generator.hpp>
|
||||
|
||||
using std::set;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
TEST(IntersectionCutTest, set_face_test)
|
||||
{
|
||||
IntersectionCut ic(Point(rational(1,2),rational(1,4)), 1);
|
||||
|
||||
// should calculate d correctly
|
||||
ic.set_face(0, Line(rational(127,64), rational(0), rational(67,64), rational(1)));
|
||||
EXPECT_EQ(ic.d[0].x, rational(4,5));
|
||||
EXPECT_EQ(ic.d[0].y, rational(3,4));
|
||||
|
||||
// shoud work with any sequence of points
|
||||
ic.set_face(0, Line(rational(67,64), rational(1), rational(127,64), rational(0)));
|
||||
EXPECT_EQ(ic.d[0].x, rational(4,5));
|
||||
EXPECT_EQ(ic.d[0].y, rational(3,4));
|
||||
|
||||
|
||||
// should throw exception on badly defined lines
|
||||
EXPECT_THROW(ic.set_face(0, Line(rational(1), rational(1), rational(1), rational(1))), std::invalid_argument);
|
||||
|
||||
// should throw exception on lines crossing f
|
||||
EXPECT_THROW(ic.set_face(0, Line(rational(1,2), rational(1,4), rational(1), rational(1))), std::invalid_argument);
|
||||
|
||||
// should not throw exception on good lines
|
||||
EXPECT_NO_THROW(ic.set_face(0, Line(rational(0), rational(0), rational(1), rational(1))));
|
||||
|
||||
// should throw exception on index out of range
|
||||
EXPECT_THROW(ic.set_face(-1, Line()), std::out_of_range);
|
||||
EXPECT_THROW(ic.set_face( 1, Line()), std::out_of_range);
|
||||
EXPECT_THROW(ic.set_face(50, Line()), std::out_of_range);
|
||||
}
|
||||
|
||||
TEST(IntersectionCutTest, set_face_test_2)
|
||||
{
|
||||
IntersectionCut ic(Point(rational(1,2),0), 1);
|
||||
|
||||
ic.set_face(0, Line(1, 0, 0, 1));
|
||||
EXPECT_EQ(ic.d[0].x, rational(2));
|
||||
EXPECT_EQ(ic.d[0].y, rational(2));
|
||||
|
||||
ic.set_face(0, Line(0, 1, 1, 0));
|
||||
EXPECT_EQ(ic.d[0].x, rational(2));
|
||||
EXPECT_EQ(ic.d[0].y, rational(2));
|
||||
}
|
||||
|
||||
TEST(IntersectionCutTest, get_continuous_coefficient_test)
|
||||
{
|
||||
IntersectionCut ic(Point(rational(5,7),rational(0)), 2);
|
||||
ic.set_face(0, Line(3, 4, rational(29,7), rational(40,7)));
|
||||
ic.set_face(1, Line(rational(29,7), rational(40,7), rational(2), rational(2)));
|
||||
|
||||
// should calculate d correctly
|
||||
EXPECT_EQ(rational(-21, 8), ic.d[0].x);
|
||||
EXPECT_EQ(rational( 7, 4), ic.d[0].y);
|
||||
EXPECT_EQ(rational( 91,12), ic.d[1].x);
|
||||
EXPECT_EQ(rational(-35, 8), ic.d[1].y);
|
||||
|
||||
// should calculate continuous coefficients correctly
|
||||
EXPECT_EQ(rational(91,12), ic.get_continuous_coefficient(rational(1),rational(0)));
|
||||
EXPECT_EQ(rational( 7, 4), ic.get_continuous_coefficient(rational(0),rational(1)));
|
||||
EXPECT_EQ(rational( 7,40), ic.get_continuous_coefficient(rational(3,5),1));
|
||||
EXPECT_EQ(rational(77,72), ic.get_continuous_coefficient(rational(1,3),rational(1,3)));
|
||||
}
|
||||
|
||||
TEST(IntersectionCutTest, get_trivial_lifting_coefficient_test)
|
||||
{
|
||||
IntersectionCut ic(Point(rational(5,7),rational(0)), 2);
|
||||
ic.set_face(0, Line(3, 4, rational(29,7), rational(40,7)));
|
||||
ic.set_face(1, Line(rational(29,7), rational(40,7), rational(2), rational(2)));
|
||||
|
||||
// should calculate d correctly
|
||||
EXPECT_EQ(rational(-21, 8), ic.d[0].x);
|
||||
EXPECT_EQ(rational( 7, 4), ic.d[0].y);
|
||||
EXPECT_EQ(rational( 91,12), ic.d[1].x);
|
||||
EXPECT_EQ(rational(-35, 8), ic.d[1].y);
|
||||
|
||||
// should calculate lifted coefficients correctly
|
||||
EXPECT_EQ(rational(399,704), ic.get_trivial_lifting_coefficient(rational(119,264),rational(0)));
|
||||
}
|
||||
|
||||
TEST(IntersectionCutTest, get_trivial_lifting_coefficient_test_2)
|
||||
{
|
||||
IntersectionCut ic(Point(rational(1,2),rational(0)), 2);
|
||||
ic.set_face(0, Line(0,0,0,1));
|
||||
ic.set_face(1, Line(1,0,0,1));
|
||||
|
||||
// should calculate d correctly
|
||||
EXPECT_EQ(rational(-2), ic.d[0].x);
|
||||
EXPECT_EQ( rational(0), ic.d[0].y);
|
||||
EXPECT_EQ( rational(2), ic.d[1].x);
|
||||
EXPECT_EQ( rational(2), ic.d[1].y);
|
||||
|
||||
// should calculate lifted coefficients correctly
|
||||
EXPECT_EQ(rational(1), ic.get_trivial_lifting_coefficient(rational(-1,2),0));
|
||||
}
|
||||
|
||||
TEST(WedgeCutGenerator, generate_test_1)
|
||||
{
|
||||
Row r;
|
||||
r.basic_var_index = 999;
|
||||
r.is_integer = new bool[3];
|
||||
r.c.pi.resize(3);
|
||||
r.c.pi.push(0, rational(-3,5));
|
||||
r.c.pi.push(1, rational(-1));
|
||||
r.c.pi.push(2, rational(1));
|
||||
r.c.pi_zero = rational(5,7);
|
||||
|
||||
std::set<Constraint> expected_cuts;
|
||||
rational cut_matrix[] = {
|
||||
rational(-14,25), rational(-7,2), rational(-7,5),
|
||||
rational(-7,20), rational(-7,2), rational(-91,44),
|
||||
rational(-7,40), rational(-91,12), rational(-21,8),
|
||||
rational(0), rational(-35,3), rational(-35,4),
|
||||
// rational(-21,10), rational(0), rational(-7,5),
|
||||
// rational(-21,10), rational(-7,2), rational(0),
|
||||
};
|
||||
|
||||
for(int i=0; i<3; i++)
|
||||
r.is_integer[i] = (i == 0);
|
||||
|
||||
for(int i=0; i<4; i++)
|
||||
{
|
||||
Constraint c;
|
||||
c.pi.resize(3);
|
||||
c.pi_zero = -1;
|
||||
for(int j=0; j<3; j++)
|
||||
c.pi.push(j, cut_matrix[i*3+j]);
|
||||
expected_cuts.insert(c);
|
||||
}
|
||||
|
||||
std::set<Constraint> actual_cuts;
|
||||
|
||||
WedgeCutGenerator wcg(r);
|
||||
while(wcg.has_next())
|
||||
actual_cuts.insert(*wcg.next());
|
||||
|
||||
// cout << "EXPECTED:" << endl;
|
||||
// for(Constraint c : expected_cuts)
|
||||
// cout << c << endl;
|
||||
//
|
||||
// cout << "ACTUAL:" << endl;
|
||||
// for(Constraint c : actual_cuts)
|
||||
// cout << c << endl;
|
||||
|
||||
EXPECT_TRUE(expected_cuts == actual_cuts);
|
||||
}
|
||||
|
||||
TEST(WedgeCutGenerator, generate_test_2)
|
||||
{
|
||||
Row r;
|
||||
r.basic_var_index = 999;
|
||||
r.is_integer = new bool[3];
|
||||
r.c.pi.resize(3);
|
||||
r.c.pi.push(0, rational(1,2));
|
||||
r.c.pi.push(1, rational(-1));
|
||||
r.c.pi.push(2, rational(1));
|
||||
r.c.pi_zero = rational(1,2);
|
||||
|
||||
set<Constraint> expected_cuts;
|
||||
rational cut_matrix[] = {
|
||||
rational(-1), rational(-2), rational(-2)
|
||||
};
|
||||
|
||||
for(int i=0; i<5; i++)
|
||||
r.is_integer[i] = (i == 0);
|
||||
|
||||
for(int i=0; i<1; i++)
|
||||
{
|
||||
Constraint c;
|
||||
c.pi.resize(3);
|
||||
c.pi_zero = -1;
|
||||
for(int j=0; j<3; j++)
|
||||
{
|
||||
c.pi.push(j, cut_matrix[i*3+j]);
|
||||
}
|
||||
expected_cuts.insert(c);
|
||||
}
|
||||
|
||||
set<Constraint> actual_cuts;
|
||||
WedgeCutGenerator wcg(r);
|
||||
while(wcg.has_next())
|
||||
actual_cuts.insert(*wcg.next());
|
||||
|
||||
// cout << "EXPECTED:" << endl;
|
||||
// for(Constraint c : expected_cuts)
|
||||
// cout << c << endl;
|
||||
//
|
||||
// cout << "ACTUAL:" << endl;
|
||||
// for(Constraint c : actual_cuts)
|
||||
// cout << c << endl;
|
||||
|
||||
EXPECT_TRUE(expected_cuts == actual_cuts);
|
||||
}
|
||||
|
||||
TEST(WedgeCutGenerator, generate_test_3)
|
||||
{
|
||||
bool is_integer[] = { true, false, false, true, true, true, true, true, false,
|
||||
false, false, false, false, false };
|
||||
Row r;
|
||||
r.basic_var_index = 0;
|
||||
r.is_integer = is_integer;
|
||||
r.c.pi.resize(14);
|
||||
r.c.pi.push(0, rational(1));
|
||||
r.c.pi.push(1, rational(7338,415411));
|
||||
r.c.pi.push(2, rational(1271,71708));
|
||||
r.c.pi.push(3, rational(19,577624));
|
||||
r.c.pi.push(4, rational(-19,577624));
|
||||
r.c.pi.push(5, rational(7,189263));
|
||||
r.c.pi.push(6, rational(-7,189263));
|
||||
r.c.pi.push(7, rational(-5,83513));
|
||||
r.c.pi.push(8, rational(157727,47951));
|
||||
r.c.pi.push(9, rational(-157727,47951));
|
||||
r.c.pi.push(10, rational(536435,145039));
|
||||
r.c.pi.push(11, rational(-536435,145039));
|
||||
r.c.pi.push(12, rational(237478,39665));
|
||||
r.c.pi.push(13, rational(237478,39665));
|
||||
r.c.pi_zero = rational(631975,707651);
|
||||
|
||||
rational solution[14];
|
||||
solution[0] = rational(0);
|
||||
solution[1] = rational(0);
|
||||
solution[2] = rational(0);
|
||||
solution[3] = rational(0);
|
||||
solution[4] = rational(0);
|
||||
solution[5] = rational(0);
|
||||
solution[6] = rational(0);
|
||||
solution[7] = rational(1);
|
||||
solution[8] = rational(5531144031239411,576460752303423488);
|
||||
solution[9] = rational(8350617490688559,1152921504606846976);
|
||||
solution[10] = rational(6818955564856927,576460752303423488);
|
||||
solution[11] = rational(6377917030130699,4611686018427387904);
|
||||
solution[12] = rational(2547758494699757,18014398509481984);
|
||||
solution[13] = rational(0);
|
||||
|
||||
cout << r.c.get_violation(solution).get_double() << endl;
|
||||
EXPECT_TRUE(r.c.get_violation(solution) < 0.001);
|
||||
|
||||
WedgeCutGenerator wcg(r);
|
||||
while(wcg.has_next())
|
||||
{
|
||||
Constraint *c = wcg.next();
|
||||
cout << *c << endl;
|
||||
cout << c->get_violation(solution).get_double() << endl;
|
||||
EXPECT_TRUE(c->get_violation(solution) < 0.001);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user