You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
394 lines
7.6 KiB
394 lines
7.6 KiB
/*
|
|
* 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);
|
|
if(row.reduced_costs[r1_index] < row.cost_cutoff)
|
|
continue;
|
|
|
|
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;
|
|
}
|
|
}
|