/* Copyright (c) 2015 Alinson Xavier * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include static int get_bounding_box(int nrows, int nrays, const double *rays, const double *bounds, double epsilon, double *lb, double *ub) { for(int i = 0; i < nrows; i++) { ub[i] = 0; lb[i] = 0; } for (int i=0; i < nrays; i++) { double e = fmin(epsilon, bounds[i]); for(int j = 0; j < nrows; j++) { double rij = rays[nrows * i + j] * e; lb[j] = fmin(lb[j], floor(rij)); ub[j] = fmax(ub[j], ceil(rij)); } } return 0; } struct LatticeSequence { int square; int direction; int steps; int eol; int i; int j; }; static void lattice_sequence_init(struct LatticeSequence *seq) { seq->steps = 0; seq->square = 0; seq->direction = 0; seq->i = 0; seq->j = 0; seq->eol = 0; } static int next_lattice_point(struct LatticeSequence *seq, const double *lb, const double *ub) { int rval = 0; seq->eol = 1; for(int k = 0; k < 8 * (seq->square+1); k++) { if(seq->steps > 0) { seq->steps--; switch(seq->direction) { case 3: seq->i++; break; case 2: seq->j--; break; case 1: seq->i--; break; case 0: seq->j++; break; } } if(seq->steps == 0) { if(seq->direction > 0) { seq->direction--; seq->steps = 2 * seq->square; } else { seq->square++; seq->direction = 3; seq->steps = 2 * seq->square; seq->i--; seq->j++; } } if(seq->i >= lb[0] && seq->i <= ub[0] && seq->j >= lb[1] && seq->j <= ub[1]) { seq->eol = 0; break; } } CLEANUP: return rval; } /* * Returns lambda1 and lambda2 such that p = r1 * lambda1 + r2 * lambda2 */ static void find_linear_combination(const double *r1, const double *r2, const double *p, double *lambda1, double *lambda2) { int rval = 0; double den = (r1[0] * r2[1] - r2[0] * r1[1]); if(DOUBLE_iszero(den)) den = 0.0; *lambda1 = (r2[1] * p[0] - r2[0] * p[1]) / den; *lambda2 = (-r1[1] * p[0] + r1[0] * p[1]) / den; } static int generate_split(const double *f, const double *d, double *pi, double *pi_zero, long max_den) { int rval = 0; Rational d1, d2; double m; rval = DOUBLE_to_rational(d[0], 10, d1); abort_if(rval, "DOUBLE_to_rational failed"); rval = DOUBLE_to_rational(d[1], 10, d2); abort_if(rval, "DOUBLE_to_rational failed"); m = lcm(d1->den, d2->den); d1->den = m / d1->den; d1->num *= d1->den; d2->den = m / d2->den; d2->num *= d2->den; m = gcd(d1->num, d2->num); if(m != 0) { d1->num /= m; d2->num /= m; pi[0] = (double) d2->num; pi[1] = - ((double) d1->num); *pi_zero = floor(pi[0] * f[0] + pi[1] * f[1]); } else { pi[0] = pi[1] = INFINITY; *pi_zero = INFINITY; } CLEANUP: return rval; } /* * Receives a list of rays r1,...,rn and a point p. Returns i1,i2 such that * p belongs to cone(r_i1,r_i2). Also returns lambda1, lambda2 such that * p = r_i1 * lambda1 + r_i2 * lambda2. * * The rays must be sorted in clockwise order. */ static int find_containing_cone(const double *rays, const int nrays, const double *p, int *index1, int *index2, double *lambda1, double *lambda2) { int rval = 0; int i1, i2; for (i1 = 0; i1 < nrays; i1++) { i2 = (i1 + 1) % nrays; const double *r1 = &rays[i1 * 2]; const double *r2 = &rays[i2 * 2]; double at1 = atan2(r1[1], r1[0]); double at2 = atan2(r2[1], r2[0]) - at1; if (at2 > 0) at2 -= 2 * M_PI; if (at2 <= - M_PI) { log_verbose(" discarding obtuse rays\n"); log_verbose(" r1=%.12lf %.12lf\n", r1[0], r1[1]); log_verbose(" r2=%.12lf %.12lf\n", r2[0], r2[1]); continue; } find_linear_combination(r1, r2, p, lambda1, lambda2); log_verbose(" r1=%.12lf %.12lf\n", r1[0], r1[1]); log_verbose(" r2=%.12lf %.12lf\n", r2[0], r2[1]); log_verbose(" %.8lf %.8lf\n", *lambda1, *lambda2); if(DOUBLE_iszero(*lambda1)) *lambda1 = 0.0; if(DOUBLE_iszero(*lambda2)) *lambda2 = 0.0; if ((*lambda1) >= 0 && (*lambda2) >= 0 && (*lambda1) <= 1e9 && (*lambda2) <= 1e9) { log_verbose(" match!\n"); break; } } if (i1 == nrays) i1 = i2 = -1; *index1 = i1; *index2 = i2; CLEANUP: return rval; } /* * Find lambda such that p lies on the line connecting lambda * r1 and lambda * r2 */ static int scale_cone_to_point(const double *r1, const double *r2, const double *p, double *lambda) { int rval = 0; double a = r1[0], b = r1[1]; double c = r2[0], d = r2[1]; double den = (b * c - a * d); //abort_iff(fabs(den) < 1e-9, "rays cannot be parallel (den=%.12lf)", den); *lambda = p[0] * (b - d) - p[1] * (a - c); *lambda /= den; CLEANUP: return rval; } /* * Find lambda such that p lies in the line connecting r1 and lambda * r2 */ static int shear_cone_to_point(const double *r1, const double *r2, const double *p, double *lambda) { int rval = 0; double a = r1[0], b = r1[1]; double c = r2[0], d = r2[1]; double den = d * (p[0] - a) - c * (p[1] - b); *lambda = b * p[0] - a * p[1]; *lambda /= den; CLEANUP: return rval; } static int scale_to_chull(double *rays, int nrays, double *scale) { int rval = 0; double *rays_extended = 0; double *vertices = 0; int nvertices; rays_extended = (double*) malloc(2 * (nrays + 1) * sizeof(double)); vertices = (double*) malloc(2 * (nrays + 1) * sizeof(double)); abort_if(!rays_extended, "could not allocate rays_extended"); abort_if(!vertices, "could not allocate vertices"); memcpy(rays_extended, rays, 2 * nrays * sizeof(double)); rays_extended[2*nrays] = 0; rays_extended[2*nrays + 1] = 0; rval = chull_2d(rays_extended, nrays + 1, vertices, &nvertices); abort_if(rval, "chull_2d failed"); for(int i = 0; i < nrays; i++) scale[i] = 1.0; log_verbose(" convex hull:\n"); for(int i = 0; i < nvertices; i++) { log_verbose(" v%-3d: %20.8lf %20.8lf\n", i, vertices[2 * i], vertices[2 * i + 1]); } log_verbose(" rays:\n"); for(int i = 0; nrays >= 3 && i < nrays; i++) { int i1, i2; double lambda1, lambda2, mu; rval = find_containing_cone(vertices, nvertices, &rays[2*i], &i1, &i2, &lambda1, &lambda2); abort_if(rval, "find_containing_cone failed"); log_verbose("%.8lf %.8lf\n", lambda1, lambda2); if(i1 < 0 || i2 < 0) continue; if(DOUBLE_iszero(lambda1)) { mu = lambda2; } else if(DOUBLE_iszero(lambda2)) { mu = lambda1; } else { rval = scale_vector_to_line(&vertices[2*i1], &vertices[2*i2], &rays[2*i], &mu); abort_if(rval, "scale_vector_to_line failed"); } abort_if(!isfinite(mu), "mu should be finite"); //log_verbose(" r%-3d: %.2lf %.2lf %.2lf\n", i, rays[2*i], rays[2*i+1], mu); rays[2*i] *= mu; rays[2*i+1] *= mu; scale[i] = mu; log_verbose(" r%-3d: %20.12lf %20.12lf (scale %.8lf)\n", i, rays[2*i], rays[2*i+1], scale[i]); } CLEANUP: if(rays_extended) free(rays_extended); if(vertices) free(vertices); return rval; } static int bound(const double *rays, const double *bounds, int nrays, const double *f, const double *p, double *epsilon, double *v1, double *v2, int *index1, int *index2) { int rval = 0; int i1, i2, iexact = -1; double e1, e2; const double *r1, *r2; double lambda1, lambda2; double pp[2] = { p[0] - f[0], p[1] - f[1] }; rval = find_containing_cone(rays, nrays, pp, &i1, &i2, &lambda1, &lambda2); abort_if(rval, "find_containing_cone failed"); if(i1 < 0 || i2 < 0) { log_verbose(" no cone\n"); *epsilon = INFINITY; goto CLEANUP; } if(DOUBLE_iszero(lambda1)) iexact = i2; if(DOUBLE_iszero(lambda2)) iexact = i1; if(iexact >= 0) { int inext = (iexact + 1) % nrays; int iprev = (iexact + (nrays-1)) % nrays; double mu1, mu2; find_linear_combination(&rays[2 * iprev], &rays[2 * inext], &rays[2 * iexact], &mu1, &mu2); log_verbose(" mu=%.12lf %.12lf\n", mu1, mu2); int should_enlarge_cone = 1; if(!DOUBLE_leq(bounds[iexact], lambda1+lambda2)) should_enlarge_cone = 0; if(!DOUBLE_geq(mu1, 0) || !DOUBLE_geq(mu2, 0)) should_enlarge_cone = 0; if(isinf(mu1) || isinf(mu2)) should_enlarge_cone = 0; if(fabs(mu1) > 1e9 || fabs(mu2) > 1e9) should_enlarge_cone = 0; if(should_enlarge_cone) { i1 = iprev; i2 = inext; } else { v1[0] = v1[1] = INFINITY; v2[0] = v2[1] = INFINITY; *epsilon = (lambda1 + lambda2); if(DOUBLE_leq(bounds[iexact], *epsilon)) *epsilon = INFINITY; *index1 = *index2 = iexact; goto CLEANUP; } } if(bounds[i1] > bounds[i2]) { swap(i1, i2, int); swap(lambda1, lambda2, double); } *index1 = i1; *index2 = i2; r1 = &rays[2 * i1]; r2 = &rays[2 * i2]; log_verbose(" r%-4d %20.12lf %20.12lf\n", i1, r1[0], r1[1]); log_verbose(" r%-4d %20.12lf %20.12lf\n", i2, r2[0], r2[1]); log_verbose(" pp %20.12lf %20.12lf\n", pp[0], pp[1]); log_verbose(" lambda %20.12lf %20.12lf\n", lambda1, lambda2); { double r1bound[2] = {r1[0] * bounds[i1], r1[1] * bounds[i1]}; rval = scale_cone_to_point(r1, r2, pp, &e1); abort_if(rval, "scale_cone_to_point failed"); rval = shear_cone_to_point(r1bound, r2, pp, &e2); abort_if(rval, "scale_cone_to_point failed"); log_verbose(" e1=%20.12lf\n", e1); log_verbose(" e2=%20.12lf\n", e2); log_verbose(" b1=%20.12lf\n", bounds[i1]); log_verbose(" b2=%20.12lf\n", bounds[i2]); } switch(DOUBLE_cmp(e1, bounds[i1])) { case -1: *epsilon = e1; v1[0] = r1[0] * e1; v1[1] = r1[1] * e1; v2[0] = r2[0] * e1; v2[1] = r2[1] * e1; break; case 0: case 1: if(DOUBLE_geq(e2, bounds[i2])) { *epsilon = INFINITY; } else { *epsilon = e2; v1[0] = r1[0] * bounds[i1]; v1[1] = r1[1] * bounds[i1]; v2[0] = r2[0] * e2; v2[1] = r2[1] * e2; } } CLEANUP: return rval; } #ifndef TEST_SOURCE int INFINITY_2D_generate_lfree(const struct MultiRowModel *model, struct ConvLFreeSet *lfree) { int rval = 0; int count = 0; int nrays = model->rays.nrays; double *scale = 0; double lb[2], ub[2]; double *f = lfree->f; double *beta = lfree->beta; double *rays = lfree->rays.values; lfree->nrows = 2; lfree->rays.nrays = nrays; memcpy(rays, model->rays.values, 2 * nrays * sizeof(double)); memcpy(f, model->f, 2 * sizeof(double)); for (int i = 0; i < nrays; i++) beta[i] = GREEDY_BIG_E; scale = (double*) malloc(nrays * sizeof(double)); abort_if(!scale, "could not allocate scale"); rval = scale_to_chull(rays, nrays, scale); abort_if(rval, "scale_to_chull failed"); long seq_count = 0; while(1) { log_verbose(" starting iteration %d...\n", count); abort_if(count++ > 2 * nrays, "infinite loop"); rval = get_bounding_box(2, nrays, rays, beta, GREEDY_BIG_E, lb, ub); abort_if(rval, "get_bounding_box failed"); log_verbose(" box=[%.2lf %.2lf] [%.2lf %.2lf]\n", lb[0], ub[0], lb[1], ub[1]); int best_i1, best_i2; double best_v1[2]; double best_v2[2]; double best_p[2]; double best_epsilon = INFINITY; struct LatticeSequence seq; lattice_sequence_init(&seq); while(!seq.eol) { seq_count++; double p[2] = { seq.i, seq.j }; double v1[2], v2[2], epsilon; int i1, i2; log_verbose(" p=%.2lf %.2lf\n", p[0], p[1]); rval = bound(rays, beta, nrays, f, p, &epsilon, v1, v2, &i1, &i2); abort_if(rval, "bound failed"); log_verbose(" epsilon=%.2lf\n", epsilon); if(epsilon >= 0 && epsilon < best_epsilon) { log_verbose(" found smaller epsilon: %.8lf\n", epsilon); rval = get_bounding_box(2, nrays, rays, beta, epsilon, lb, ub); abort_if(rval, "get_bounding_box failed"); log_verbose(" p=%.2lf %.2lf\n", p[0], p[1]); log_verbose(" box=[%.2lf %.2lf] [%.2lf %.2lf]\n", lb[0], ub[0], lb[1], ub[1]); log_verbose(" v1=%12.8lf %12.8lf\n", v1[0], v1[1]); log_verbose(" v2=%12.8lf %12.8lf\n", v2[0], v2[1]); log_verbose(" rays=[%d %d]\n", i1, i2); best_epsilon = epsilon; best_v1[0] = v1[0]; best_v1[1] = v1[1]; best_v2[0] = v2[0]; best_v2[1] = v2[1]; best_p[0] = p[0]; best_p[1] = p[1]; best_i1 = i1; best_i2 = i2; } next_lattice_point(&seq, lb, ub); if(seq_count > MAX_LATTICE_POINTS) { rval = ERR_MIP_TIMEOUT; goto CLEANUP; } } if(isinf(best_epsilon)) { log_verbose(" best_epsilon is infinity\n"); break; } log_verbose(" updating beta\n"); if(isinf(best_v1[0])) { beta[best_i1] = best_epsilon; log_verbose(" beta[%d]=%.8lf (exact)\n", best_i1, best_epsilon); } else { log_verbose(" v1=%.8lf %.8lf\n", best_v1[0], best_v1[1]); log_verbose(" v2=%.8lf %.8lf\n", best_v2[0], best_v2[1]); log_verbose(" i=%d %d\n", best_i1, best_i2); for (int i = 0; i < nrays; i++) { double lambda; rval = scale_vector_to_line(best_v1, best_v2, &rays[2 * i], &lambda); abort_if(rval, "scale_vector_to_line failed"); if(!DOUBLE_geq(lambda, 0)) continue; beta[i] = fmin(beta[i], lambda); log_verbose(" beta[%d]=%.8lf\n", i, beta[i]); } } //if(count > 0) //{ // for (int i = 0; i < nrays; i++) // beta[i] = fmin(beta[i], best_epsilon); // break; //} int is_split; for (int k = 0; k < nrays; k++) { if(beta[k] < 100) continue; is_split = 1; double *split_direction = &rays[2 * k]; log_verbose(" split_direction=%.2lf %.2lf\n", split_direction[0], split_direction[1]); double pi[2]; double pi_zero; rval = generate_split(f, split_direction, pi, &pi_zero, 10); abort_if(rval, "generate_split failed"); log_verbose(" pi=%.2lf %.2lf\n", pi[0], pi[1]); log_verbose(" pi_zero=%.2lf\n", pi_zero); if(isinf(pi[0])) { is_split = 0; break; } double lhs; // reject splits that have f on the boundary lhs = f[0] * pi[0] + f[1] * pi[1]; if(DOUBLE_eq(pi_zero, lhs) || DOUBLE_eq(lhs, pi_zero+1)) { log_verbose(" split rejected\n"); is_split = 0; } for (int i = 0; i < nrays && is_split; i++) { const double *r = &rays[2 * i]; lhs = (f[0] + r[0] * beta[i]) * pi[0]; lhs += (f[1] + r[1] * beta[i]) * pi[1]; if (!(DOUBLE_leq(pi_zero, lhs) && DOUBLE_leq(lhs, pi_zero+1))) { log_verbose(" point %.4lf %.4lf falls outside of the split\n", f[0] + r[0]*beta[i], f[1] + r[1] * beta[i]); is_split = 0; } } if(is_split) { log_verbose(" split confirmed. stopping.\n"); break; } } if(is_split) break; } CLEANUP: if(scale) free(scale); return rval; } #endif // TEST_SOURCE