/***************************************************************************** Copyright (C) 1994-2000 University of Maryland Copyright (C) 2008 University of Southern California Copyright (C) 2009-2010 University of Utah All Rights Reserved. Purpose: Various hull calculations. Notes: History: 06/15/09 ConvexRepresentation, Chun Chen 11/25/09 RectHull, Chun Chen *****************************************************************************/ #include #include #include #include #include #include #include #include #include namespace omega { int hull_debug = 0; Relation ConvexHull(NOT_CONST Relation &R) { Relation S = Approximate(consume_and_regurgitate(R)); if (!S.is_upper_bound_satisfiable()) return S; if (S.has_single_conjunct()) return S; return Farkas(Farkas(S,Basic_Farkas), Convex_Combination_Farkas); } Relation DecoupledConvexHull(NOT_CONST Relation &R) { Relation S = Approximate(consume_and_regurgitate(R)); if (!S.is_upper_bound_satisfiable()) return S; if (S.has_single_conjunct()) return S; return Farkas(Farkas(S,Decoupled_Farkas), Convex_Combination_Farkas); } Relation AffineHull(NOT_CONST Relation &R) { Relation S = Approximate(consume_and_regurgitate(R)); if (!S.is_upper_bound_satisfiable()) return S; return Farkas(Farkas(S,Basic_Farkas), Affine_Combination_Farkas); } Relation LinearHull(NOT_CONST Relation &R) { Relation S = Approximate(consume_and_regurgitate(R)); if (!S.is_upper_bound_satisfiable()) return S; return Farkas(Farkas(S,Basic_Farkas), Linear_Combination_Farkas); } Relation ConicHull(NOT_CONST Relation &R) { Relation S = Approximate(consume_and_regurgitate(R)); if (!S.is_upper_bound_satisfiable()) return S; return Farkas(Farkas(S,Basic_Farkas), Positive_Combination_Farkas); } Relation FastTightHull(NOT_CONST Relation &input_R, NOT_CONST Relation &input_H) { Relation R = Approximate(consume_and_regurgitate(input_R)); Relation H = Approximate(consume_and_regurgitate(input_H)); if (hull_debug) { fprintf(DebugFile,"[ Computing FastTightHull of:\n"); R.prefix_print(DebugFile); fprintf(DebugFile,"given known hull of:\n"); H.prefix_print(DebugFile); } if (!H.has_single_conjunct()) { if (hull_debug) fprintf(DebugFile, "] bailing out of FastTightHull, known hull not convex\n"); return H; } if (!H.is_obvious_tautology()) { R = Gist(R,copy(H)); R.simplify(1,0); } if (R.has_single_conjunct()) { R = Intersection(R,H); if (hull_debug) { fprintf(DebugFile, "] quick easy answer to FastTightHull\n"); R.prefix_print(DebugFile); } return R; } if (R.has_local(coefficient_of_constant_term)) { if (hull_debug) { fprintf(DebugFile, "] Can't handle recursive application of Farkas lemma\n"); } return H; } if (hull_debug) { fprintf(DebugFile,"Gist of R given H is:\n"); R.prefix_print(DebugFile); } if (1) { Set vars; int conjuncts = 0; for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) { conjuncts++; for (Variable_ID_Iterator v(*((*s)->variables())); v.live(); v++) { bool found = false; for (EQ_Iterator eq = (*s)->EQs(); eq.live(); eq.next()) if ((*eq).get_coef(*v) != 0) { if (!found) vars.insert(*v); found = true; break; } if (!found) for (GEQ_Iterator geq = (*s)->GEQs(); geq.live(); geq.next()) if ((*geq).get_coef(*v) != 0) { if (!found) vars.insert(*v); found = true; break; } } } // We now know which variables appear in R if (hull_debug) { fprintf(DebugFile,"Variables we need a better hull on are: "); foreach(v,Variable_ID,vars, fprintf(DebugFile," %s",v->char_name())); fprintf(DebugFile,"\n"); } Conjunct *c = H.single_conjunct(); int total=0; int copied = 0; for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { total++; foreach(v,Variable_ID,vars, if ((*eq).get_coef(v) != 0) { R.and_with_EQ(*eq); copied++; break; // out of variable loop } ); } for (GEQ_Iterator geq = c->GEQs(); geq.live(); geq.next()) { total++; foreach(v,Variable_ID,vars, if ((*geq).get_coef(v) != 0) { R.and_with_GEQ(*geq); copied++; break; // out of variable loop } ); } if (copied < total) { R = Approximate(R); if (hull_debug) { fprintf(DebugFile,"Decomposed relation, copied only %d of %d constraints\n",copied,total); fprintf(DebugFile,"Original R:\n"); R.prefix_print(DebugFile); fprintf(DebugFile,"Known hull:\n"); H.prefix_print(DebugFile); fprintf(DebugFile,"New R:\n"); R.prefix_print(DebugFile); } } } Relation F = Farkas(copy(R), Basic_Farkas, true); if (hull_debug) fprintf(DebugFile,"Farkas Difficulty = " coef_fmt "\n", farkasDifficulty); if (farkasDifficulty > 260) { if (hull_debug) { fprintf(DebugFile, "] bailing out, farkas is way too complex\n"); fprintf(DebugFile,"Farkas:\n"); F.prefix_print(DebugFile); } return H; } else if (farkasDifficulty > 130) { // Bail out if (hull_debug) { fprintf(DebugFile, coef_fmt " non-zeros in original farkas\n", farkasDifficulty); } Relation tmp = Farkas(R, Decoupled_Farkas, true); if (hull_debug) { fprintf(DebugFile, coef_fmt " non-zeros in decoupled farkas\n", farkasDifficulty); } if (farkasDifficulty > 260) { if (hull_debug) { fprintf(DebugFile, "] bailing out, farkas is way too complex\n"); fprintf(DebugFile,"Farkas:\n"); F.prefix_print(DebugFile); } return H; } else { if (farkasDifficulty > 130) R = Intersection(H, Farkas(tmp, Affine_Combination_Farkas, true)); else R = Intersection(H, Intersection(Farkas(tmp, Convex_Combination_Farkas, true), Farkas(F, Affine_Combination_Farkas, true))); if (hull_debug) { fprintf(DebugFile, "] bailing out, farkas is too complex, using affine hull\n"); fprintf(DebugFile,"Farkas:\n"); F.prefix_print(DebugFile); fprintf(DebugFile,"Affine hull:\n"); R.prefix_print(DebugFile); } return R; } } R = Intersection(H, Farkas(F, Convex_Combination_Farkas, true)); if (hull_debug) { fprintf(DebugFile, "] Result of FastTightHull:\n"); R.prefix_print(DebugFile); } return R; } namespace { bool parallel(const GEQ_Handle &g1, const GEQ_Handle &g2) { for(Constr_Vars_Iter cvi(g1, false); cvi; cvi++) { coef_t c1 = (*cvi).coef; coef_t c2 = g2.get_coef((*cvi).var); if (c1 != c2) return false; } { for(Constr_Vars_Iter cvi(g2, false); cvi; cvi++) { coef_t c1 = g1.get_coef((*cvi).var); coef_t c2 = (*cvi).coef; if (c1 != c2) return false; } } return true; } bool hull(const EQ_Handle &e, const GEQ_Handle &g, coef_t &hull) { int sign = 0; for(Constr_Vars_Iter cvi(e, false); cvi; cvi++) { coef_t c1 = (*cvi).coef; coef_t c2 = g.get_coef((*cvi).var); if (sign == 0) sign = (c1*c2>=0?1:-1); if (sign*c1 != c2) return false; } assert(sign != 0); { for(Constr_Vars_Iter cvi(g, false); cvi; cvi++) { coef_t c1 = e.get_coef((*cvi).var); coef_t c2 = (*cvi).coef; if (sign*c1 != c2) return false; } } hull = max(sign * e.get_const(), g.get_const()); if (hull_debug) { fprintf(DebugFile,"Hull of:\n %s\n", e.print_to_string().c_str()); fprintf(DebugFile," %s\n", g.print_to_string().c_str()); fprintf(DebugFile,"is " coef_fmt "\n\n",hull); } return true; } bool eq(const EQ_Handle &e1, const EQ_Handle &e2) { int sign = 0; for(Constr_Vars_Iter cvi(e1, false); cvi; cvi++) { coef_t c1 = (*cvi).coef; coef_t c2 = e2.get_coef((*cvi).var); if (sign == 0) sign = (c1*c2>=0?1:-1); if (sign*c1 != c2) return false; } assert(sign != 0); { for(Constr_Vars_Iter cvi(e2, false); cvi; cvi++) { coef_t c1 = e1.get_coef((*cvi).var); coef_t c2 = (*cvi).coef; if (sign*c1 != c2) return false; } } return sign * e1.get_const() == e2.get_const(); } } // This function is deprecated!!! Relation QuickHull(Relation &R) { Tuple Rs(1); Rs[1] = R; return QuickHull(Rs); } // This function is deprecated!!! Relation QuickHull(Tuple &Rs) { assert(!Rs.empty()); // if (Rs.size() == 1) return Rs[1]; Tuple l_Rs; for (int i = 1; i <= Rs.size(); i++) for (DNF_Iterator c(Rs[i].query_DNF()); c; c++) { Relation r = Relation(Rs[i], c.curr()); l_Rs.append(Approximate(r)); } if (l_Rs.size() == 1) return l_Rs[1]; Relation result = Relation::True(Rs[1]); result.copy_names(Rs[1]); use_ugly_names++; if (hull_debug > 1) for (int i = 1; i <= l_Rs.size(); i++) { fprintf(DebugFile,"#%d \n",i); l_Rs[i].prefix_print(DebugFile); } // Relation R = copy(Rs[1]); // for (int i = 2; i <= Rs.size(); i++) // R = Union(R,copy(Rs[i])); // #if 0 // if (!R.is_set()) { // if (R.n_inp() == R.n_out()) { // Relation AC = DeltasToRelation(Hull(Deltas(copy(R), // min(R.n_inp(),R.n_out()))), // R.n_inp(),R.n_out()); // Relation dH = Hull(Domain(copy(R)),false); // Relation rH = Hull(Range(copy(R)),false); // result = Intersection(AC,Cross_Product(dH,rH)); // } // else { // Relation dH = Hull(Domain(copy(R)),false); // Relation rH = Hull(Range(copy(R)),false); // result = Cross_Product(dH,rH); // assert(Must_Be_Subset(copy(R),copy(result))); // } // } // #endif Conjunct *first; l_Rs[1] = EQs_to_GEQs(l_Rs[1]); first = l_Rs[1].single_conjunct(); for (GEQ_Iterator candidate(first->GEQs()); candidate.live(); candidate.next()) { coef_t maxConstantTerm = (*candidate).get_const(); bool found = true; if (hull_debug > 1) { fprintf(DebugFile,"searching for bound on:\n %s\n", (*candidate).print_to_string().c_str()); } for (int i = 2; i <= l_Rs.size(); i++) { Conjunct *other = l_Rs[i].single_conjunct(); bool found_for_i = false; for (GEQ_Iterator target(other->GEQs()); target.live(); target.next()) { if (hull_debug > 2) { fprintf(DebugFile,"candidate:\n %s\n", (*candidate).print_to_string().c_str()); fprintf(DebugFile,"target:\n %s\n", (*target).print_to_string().c_str()); } if (parallel(*candidate,*target)) { if (hull_debug > 1) fprintf(DebugFile,"Found bound:\n %s\n", (*target).print_to_string().c_str()); maxConstantTerm = max(maxConstantTerm,(*target).get_const()); found_for_i = true; break; } } if (!found_for_i) { for (EQ_Iterator target_e(other->EQs()); target_e.live(); target_e.next()) { coef_t h; if (hull(*target_e,*candidate,h)) { if (hull_debug > 1) fprintf(DebugFile,"Found bound of " coef_fmt ":\n %s\n", h, (*target_e).print_to_string().c_str()); maxConstantTerm = max(maxConstantTerm,h); found_for_i = true; break; } }; if (!found_for_i) { if (hull_debug > 1) { fprintf(DebugFile,"No bound found in:\n"); fprintf(DebugFile, "%s", l_Rs[i].print_with_subs_to_string().c_str()); } //if nothing found found = false; break; } } } if (found) { GEQ_Handle h = result.and_with_GEQ(); copy_constraint(h,*candidate); if (hull_debug > 1) fprintf(DebugFile,"Setting constant term to " coef_fmt " in\n %s\n", maxConstantTerm, h.print_to_string().c_str()); h.update_const(maxConstantTerm - (*candidate).get_const()); if (hull_debug > 1) fprintf(DebugFile,"Updated constraint is\n %s\n", h.print_to_string().c_str()); } } for (EQ_Iterator candidate_eq(first->EQs()); candidate_eq.live(); candidate_eq.next()) { bool found = true; for (int i = 2; i <= l_Rs.size(); i++) { Conjunct *C = l_Rs[i].single_conjunct(); bool found_for_i = false; for (EQ_Iterator target(C->EQs()); target.live(); target.next()) { if (eq(*candidate_eq,*target)) { found_for_i = true; break; } } if (!found_for_i) { //if nothing found found = false; break; } } if (found) { EQ_Handle h = result.and_with_EQ(); copy_constraint(h,*candidate_eq); if (hull_debug > 1) fprintf(DebugFile,"Adding eq constraint: %s\n", h.print_to_string().c_str()); } } use_ugly_names--; if (hull_debug > 1) { fprintf(DebugFile,"quick hull is of:"); result.print_with_subs(DebugFile); } return result; } // Relation Hull2(Tuple &Rs, Tuple &active) { // assert(Rs.size() == active.size() && Rs.size() > 0); // Tuple l_Rs; // for (int i = 1; i <= Rs.size(); i++) // if (active[i]) // l_Rs.append(copy(Rs[i])); // if (l_Rs.size() == 0) // return Relation::False(Rs[1]); // try { // Relation r = l_Rs[1]; // for (int i = 2; i <= l_Rs.size(); i++) { // r = Union(r, copy(l_Rs[i])); // r.simplify(); // } // // Relation F = Farkas(r, Basic_Farkas, true); // // if (farkasDifficulty >= 500) // // throw std::overflow_error("loop convex hull too complicated."); // // F = Farkas(F, Convex_Combination_Farkas, true); // return Farkas(Farkas(r, Basic_Farkas, true), Convex_Combination_Farkas, true); // } // catch (std::overflow_error) { // return QuickHull(l_Rs); // } // } namespace { void printRs(Tuple &Rs) { fprintf(DebugFile,"Rs:\n"); for (int i = 1; i <= Rs.size(); i++) fprintf(DebugFile,"#%d : %s\n",i, Rs[i].print_with_subs_to_string().c_str()); } } Relation BetterHull(Tuple &Rs, bool stridesAllowed, bool checkSubsets, NOT_CONST Relation &input_knownHull = Relation::Null()) { Relation knownHull = consume_and_regurgitate(input_knownHull); static int OMEGA_WHINGE = -1; if (OMEGA_WHINGE < 0) { OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; } assert(!Rs.empty()); if (Rs.size() == 1) { if (stridesAllowed) return Rs[1]; else return Approximate(Rs[1]); } if (checkSubsets) { Tuple live(Rs.size()); if (hull_debug) { fprintf(DebugFile,"Checking subsets in hull computation:\n"); printRs(Rs); } int i; for(i=1;i <=Rs.size(); i++) live[i] = true; for(i=1;i <=Rs.size(); i++) for(int j=1;j <=Rs.size(); j++) if (i != j && live[j]) { if (hull_debug) fprintf(DebugFile,"checking %d Is_Obvious_Subset %d\n",i,j); if (Is_Obvious_Subset(copy(Rs[i]),copy(Rs[j]))) { if (hull_debug) fprintf(DebugFile,"yes...\n"); live[i] = false; break; } } for(i=1;i <=Rs.size(); i++) if (!live[i]) { if (i < Rs.size()) { Rs[i] = Rs[Rs.size()]; live[i] = live[Rs.size()]; } Rs[Rs.size()] = Relation(); Rs.delete_last(); i--; } } Relation hull; if (hull_debug) { fprintf(DebugFile,"Better Hull:\n"); printRs(Rs); fprintf(DebugFile,"known hull: %s\n", knownHull.print_with_subs_to_string().c_str()); } if (knownHull.is_null()) hull = QuickHull(Rs); else hull = Intersection(QuickHull(Rs),knownHull); // for (int i = 1; i <= Rs.size(); i++) // hull = RectHull(Union(hull, copy(Rs[i]))); // hull = Intersection(hull, knownHull); hull.simplify(); if (hull_debug) { fprintf(DebugFile,"quick hull: %s\n", hull.print_with_subs_to_string().c_str()); } Relation orig = Relation::False(Rs[1]); int i; for (i = 1; i <= Rs.size(); i++) orig = Union(orig,copy(Rs[i])); orig.simplify(); for (i = 1; i <= Rs.size(); i++) { if (!hull.is_obvious_tautology()) Rs[i] = Gist(Rs[i],copy(hull)); Rs[i].simplify(); if (Rs[i].is_obvious_tautology()) return hull; if (Rs[i].has_single_conjunct()) { Rs[i] = EQs_to_GEQs(Rs[i]); if (hull_debug) { fprintf(DebugFile,"Checking for hull constraints in:\n %s\n", Rs[i].print_with_subs_to_string().c_str()); } Conjunct *c = Rs[i].single_conjunct(); for (GEQ_Iterator g(c->GEQs()); g.live(); g.next()) { Relation tmp = Relation::True(Rs[i]); tmp.and_with_GEQ(*g); if (!Difference(copy(orig),tmp).is_upper_bound_satisfiable()) hull.and_with_GEQ(*g); } for (EQ_Iterator e(c->EQs()); e.live(); e.next()) { Relation tmp = Relation::True(Rs[i]); tmp.and_with_EQ(*e); if (!Difference(copy(orig),tmp).is_upper_bound_satisfiable()) hull.and_with_EQ(*e); } } } hull = FastTightHull(orig,hull); assert(hull.has_single_conjunct()); if (stridesAllowed) return hull; else return Approximate(hull); } Relation Hull(NOT_CONST Relation &S, bool stridesAllowed, int effort, NOT_CONST Relation &knownHull) { Relation R = consume_and_regurgitate(S); R.simplify(1,0); if (!R.is_upper_bound_satisfiable()) return R; Tuple Rs; for (DNF_Iterator c(R.query_DNF()); c.live(); ) { Rs.append(Relation(R,c.curr())); c.next(); } if (effort == 1) return BetterHull(Rs,stridesAllowed,false,knownHull); else return QuickHull(Rs); } Relation Hull(Tuple &Rs, Tuple &validMask, int effort, bool stridesAllowed, NOT_CONST Relation &knownHull) { // Use relation of index i only when validMask[i] != 0 Tuple Rs2; for(int i = 1; i <= Rs.size(); i++) { if (validMask[i]) { Rs[i].simplify(); for (DNF_Iterator c(Rs[i].query_DNF()); c.live(); ) { Rs2.append(Relation(Rs[i],c.curr())); c.next(); } } } assert(effort == 0 || effort == 1); if (effort == 1) return BetterHull(Rs2,stridesAllowed,true,knownHull); else return QuickHull(Rs2); } // This function is deprecated!!! Relation CheckForConvexRepresentation(NOT_CONST Relation &R_In) { Relation R = consume_and_regurgitate(R_In); Relation h = Hull(copy(R)); if (!Difference(copy(h),copy(R)).is_upper_bound_satisfiable()) return h; else return R; } // This function is deprecated!!! Relation CheckForConvexPairs(NOT_CONST Relation &S) { Relation R = consume_and_regurgitate(S); Relation hull = FastTightHull(copy(R),Relation::True(R)); R.simplify(1,0); if (!R.is_upper_bound_satisfiable() || R.number_of_conjuncts() < 2) return R; Tuple Rs; for (DNF_Iterator c(R.query_DNF()); c.live(); ) { Rs.append(Relation(R,c.curr())); c.next(); } bool *dead = new bool[Rs.size()+1]; int i; for(i = 1; i<=Rs.size();i++) dead[i] = false; for(i = 1; i<=Rs.size();i++) if (!dead[i]) for(int j = i+1; j<=Rs.size();j++) if (!dead[j]) { if (hull_debug) { fprintf(DebugFile,"Comparing #%d and %d\n",i,j); } Relation U = Union(copy(Rs[i]),copy(Rs[j])); Relation H_ij = FastTightHull(copy(U),copy(hull)); if (!Difference(copy(H_ij),U).is_upper_bound_satisfiable()) { Rs[i] = H_ij; dead[j] = true; if (hull_debug) { fprintf(DebugFile,"Combined them\n"); } } } i = 1; while(i<=Rs.size() && dead[i]) i++; assert(i<=Rs.size()); R = Rs[i]; i++; for(; i<=Rs.size();i++) if (!dead[i]) R = Union(R,Rs[i]); delete []dead; return R; } // // Supporting functions for ConvexRepresentation // namespace { struct Interval { std::list >::iterator pos; coef_t lb; coef_t ub; bool change; coef_t modulo; Interval(std::list >::iterator pos_, coef_t lb_, coef_t ub_): pos(pos_), lb(lb_), ub(ub_) {} friend bool operator<(const Interval &a, const Interval &b); }; bool operator<(const Interval &a, const Interval &b) { return a.lb < b.lb; } struct Modulo_Interval { coef_t modulo; coef_t start; coef_t size; Modulo_Interval(coef_t modulo_, coef_t start_, coef_t size_): modulo(modulo_), start(start_), size(size_) {} friend bool operator<(const Interval &a, const Interval &b); }; bool operator<(const Modulo_Interval &a, const Modulo_Interval &b) { if (a.modulo == b.modulo) { if (a.start == b.start) return a.size < b.size; else return a.start < b.start; } else return a.modulo < b.modulo; } void merge_intervals(std::list &intervals, coef_t modulo, std::list > &Rs, std::list >::iterator orig) { // normalize intervals for (std::list::iterator i = intervals.begin(); i != intervals.end(); i++) { (*i).modulo = modulo; (*i).change = false; if ((*i).ub - (*i).lb + 1>= modulo) { (*i).lb = 0; (*i).ub = modulo - 1; } else if ((*i).ub < 0 || (*i).lb >= modulo) { coef_t range = (*i).ub - (*i).lb; (*i).lb = int_mod((*i).lb, modulo); (*i).ub = (*i).lb + range; } } intervals.sort(); // merge neighboring intervals std::list::iterator p = intervals.begin(); while (p != intervals.end()) { std::list::iterator q = p; q++; while (q != intervals.end()) { if ((*p).ub + 1 >= (*q).lb) { Relation hull = ConvexHull(Union(copy((*(*p).pos).first), copy((*(*q).pos).first))); Relation remainder = Difference(Difference(copy(hull), copy((*(*p).pos).first)), copy((*(*q).pos).first)); if (!remainder.is_upper_bound_satisfiable()) { if ((*q).pos == orig) std::swap((*p).pos, (*q).pos); (*(*p).pos).first = hull; (*p).ub = max((*p).ub, (*q).ub); (*p).change = true; Rs.erase((*q).pos); q = intervals.erase(q); } else break; } else break; } bool p_moved = false; q = p; q++; while (q != intervals.end()) { if ((*q).ub >= modulo && int_mod((*q).ub, modulo) + 1 >= (*p).lb) { Relation hull = ConvexHull(Union(copy((*(*p).pos).first), copy((*(*q).pos).first))); Relation remainder = Difference(Difference(copy(hull), copy((*(*p).pos).first)), copy((*(*q).pos).first)); if (!remainder.is_upper_bound_satisfiable()) { if ((*p).pos == orig) std::swap((*p).pos, (*q).pos); (*(*q).pos).first = hull; coef_t t = (*p).ub - int_mod((*q).ub, modulo); if (t > 0) (*q).ub = (*q).ub + t; (*q).change = true; Rs.erase((*p).pos); p = intervals.erase(p); p_moved = true; break; } else q++; } else q++; } if (!p_moved) p++; } // merge by reducing the strengh of modulo std::list modulo_intervals; coef_t max_distance = modulo/2; for (std::list::iterator p = intervals.begin(); p != intervals.end(); p++) { if ((*p).lb >= max_distance) break; coef_t size = (*p).ub - (*p).lb; std::list::iterator q = p; q++; while (q != intervals.end()) { coef_t distance = (*q).lb - (*p).lb; if (distance > max_distance) break; if ((*q).ub - (*q).lb != size || int_mod(modulo, distance) != 0) { q++; continue; } int num_reduced = 0; coef_t looking_for = int_mod((*p).lb, distance); for (std::list::iterator k = intervals.begin(); k != intervals.end(); k++) { if ((*k).lb == looking_for && (*k).ub - (*k).lb == size) { num_reduced++; looking_for += distance; if (looking_for >= modulo) break; } else if ((*k).lb <= looking_for && (*k).ub >= looking_for + size) { looking_for += distance; if (looking_for >= modulo) break; } else if ((*k).lb > looking_for) break; } if (looking_for >= modulo && num_reduced > 1) modulo_intervals.push_back(Modulo_Interval(distance, int_mod((*p).lb, distance), size)); q++; } } modulo_intervals.sort(); // remove redundant reduced-strength intervals std::list::iterator p2 = modulo_intervals.begin(); while (p2 != modulo_intervals.end()) { std::list::iterator q2 = p2; q2++; while (q2 != modulo_intervals.end()) { if ((*p2).modulo == (*q2).modulo && (*p2).start == (*q2).start) q2 = modulo_intervals.erase(q2); else if (int_mod((*q2).modulo, (*p2).modulo) == 0 && (*p2).start == int_mod((*q2).start, (*p2).modulo) && (*p2).size >= (*q2).size) q2 = modulo_intervals.erase(q2); else q2++; } p2++; } // replace original intervals with new reduced-strength ones for (std::list::iterator i = modulo_intervals.begin(); i != modulo_intervals.end(); i++) { std::vector candidates; int num_replaced = 0; for (std::list::iterator j = intervals.begin(); j != intervals.end(); j++) if (int_mod((*j).modulo, (*i).modulo) == 0 && (*j).ub - (*j).lb >= (*i).size && (int_mod((*j).lb, (*i).modulo) == (*i).start || int_mod((*j).ub, (*i).modulo) == (*i).start + (*i).size)) { candidates.push_back(&((*(*j).pos).first)); if (int_mod((*j).lb, (*i).modulo) == (*i).start && (*j).ub - (*j).lb == (*i).size) num_replaced++; } if (num_replaced <= 1) continue; Relation R = copy(*candidates[0]); for (size_t k = 1; k < candidates.size(); k++) R = Union(R, copy(*candidates[k])); Relation hull = ConvexHull(copy(R)); Relation remainder = Difference(copy(hull), copy(R)); if (!remainder.is_upper_bound_satisfiable()) { std::list::iterator replaced_one = intervals.end(); for (std::list::iterator j = intervals.begin(); j != intervals.end();) if (int_mod((*j).modulo, (*i).modulo) == 0 && (*j).ub - (*j).lb >= (*i).size && (int_mod((*j).lb, (*i).modulo) == (*i).start || int_mod((*j).ub, (*i).modulo) == (*i).start + (*i).size)) { if (int_mod((*j).lb, (*i).modulo) == (*i).start && (*j).ub - (*j).lb == (*i).size) { if (replaced_one == intervals.end()) { (*(*j).pos).first = hull; (*j).lb = int_mod((*j).lb, (*i).modulo); (*j).ub = int_mod((*j).ub, (*i).modulo); (*j).modulo = (*i).modulo; (*j).change = true; replaced_one = j; j++; } else { if ((*j).pos == orig) { std::swap((*replaced_one).pos, (*j).pos); (*(*replaced_one).pos).first = (*(*j).pos).first; } Rs.erase((*j).pos); j = intervals.erase(j); } } else { if (int_mod((*j).lb, (*i).modulo) == (*i).start) (*j).lb = (*j).lb + (*i).size + 1; else (*j).ub = (*j).ub - (*i).size - 1; (*j).change = true; j++; } } else j++; } } } } // namespace // // Simplify a union of sets/relations to a minimal (may not be // optimal) number of convex regions. It intends to replace // CheckForConvexRepresentation and CheckForConvexPairs functions. // Relation ConvexRepresentation(NOT_CONST Relation &R) { Relation l_R = copy(R); if (!l_R.is_upper_bound_satisfiable() || l_R.number_of_conjuncts() < 2) return R; // separate each conjunct into smooth convex region and holes std::list > Rs; // pair(smooth region, hole condition) for (DNF_Iterator c(l_R.query_DNF()); c.live(); c++) { Relation r1 = Relation(l_R, c.curr()); Relation r2 = Approximate(copy(r1)); r1 = Gist(r1, copy(r2)); Rs.push_back(std::make_pair(r2, r1)); } try { bool change = true; while (change) { change = false; std::list >::iterator i = Rs.begin(); while (i != Rs.end()) { // find regions with identical hole conditions to merge { std::list >::iterator j = i; j++; while (j != Rs.end()) { if (!Difference(copy((*i).second), copy((*j).second)).is_upper_bound_satisfiable() && !Difference(copy((*j).second), copy((*i).second)).is_upper_bound_satisfiable()) { if (Must_Be_Subset(copy((*j).first), copy((*i).first))) { j = Rs.erase(j); } else if (Must_Be_Subset(copy((*i).first), copy((*j).first))) { (*i).first = (*j).first; j = Rs.erase(j); change = true; } else { Relation r; bool already_use_recthull = false; try { // chun's debug // throw std::runtime_error("dfdf"); r = ConvexHull(Union(copy((*i).first), copy((*j).first))); } catch (const std::overflow_error &e) { r = RectHull(Union(copy((*i).first), copy((*j).first))); already_use_recthull = true; } retry_recthull: Relation r2 = Difference(Difference(copy(r), copy((*i).first)), copy((*j).first)); if (!r2.is_upper_bound_satisfiable()) { // convex hull is tight (*i).first = r; j = Rs.erase(j); change = true; } else { if (!already_use_recthull) { r = RectHull(Union(copy((*i).first), copy((*j).first))); already_use_recthull = true; goto retry_recthull; } else j++; } } } else j++; } } // find identical smooth regions as candidates for hole merge std::list >::iterator> s; for (std::list >::iterator j = Rs.begin(); j != Rs.end(); j++) if (j != i) { if (!Intersection(Difference(copy((*i).first), copy((*j).first)), copy((*j).second)).is_upper_bound_satisfiable() && !Intersection(Difference(copy((*j).first), copy((*i).first)), copy((*i).second)).is_upper_bound_satisfiable()) s.push_back(j); } if (s.size() != 0) { // convert hole condition c1*x1+c2*x2+... = c*alpha+d to a pair of inequalities (*i).second = EQs_to_GEQs((*i).second, false); // find potential wildcards that can be used for hole conditions std::set nonsingle_wild; for (EQ_Iterator ei((*i).second.single_conjunct()); ei; ei++) if ((*ei).has_wildcards()) for (Constr_Vars_Iter cvi(*ei, true); cvi; cvi++) nonsingle_wild.insert(cvi.curr_var()); for (GEQ_Iterator gei((*i).second.single_conjunct()); gei; gei++) if ((*gei).has_wildcards()) { Constr_Vars_Iter cvi(*gei, true); Constr_Vars_Iter cvi2 = cvi; cvi2++; if (cvi2) { nonsingle_wild.insert(cvi.curr_var()); for (; cvi2; cvi2++) nonsingle_wild.insert(cvi2.curr_var()); } } // find hole condition in c*alpha+d1<=c1*x1+c2*x2+...<=c*alpha+d2 format for (GEQ_Iterator gei((*i).second.single_conjunct()); gei; gei++) if ((*gei).has_wildcards()) { coef_t c; Variable_ID v; { Constr_Vars_Iter cvi(*gei, true); v = cvi.curr_var(); c = cvi.curr_coef(); if (c < 0 || nonsingle_wild.find(v) != nonsingle_wild.end()) continue; } coef_t lb = posInfinity; for (GEQ_Iterator gei2((*i).second.single_conjunct()); gei2; gei2++) { if (!(*gei2 == *gei) && (*gei2).get_coef(v) != 0) { if (lb != posInfinity) { nonsingle_wild.insert(v); break; } bool match = true; for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) if (cvi2.curr_coef() != -((*gei2).get_coef(cvi2.curr_var()))) { match = false; break; } if (match) for (Constr_Vars_Iter cvi2(*gei2); cvi2; cvi2++) if (cvi2.curr_coef() != -((*gei).get_coef(cvi2.curr_var()))) { match = false; break; } if (!match) { nonsingle_wild.insert(v); break; } lb = -(*gei2).get_const(); } } if (nonsingle_wild.find(v) != nonsingle_wild.end()) continue; Relation stride_cond = Relation::True((*i).second); F_Exists *f_exists = stride_cond.and_with_and()->add_exists(); Variable_ID e = f_exists->declare(); F_And *f_root = f_exists->add_and(); GEQ_Handle h1 = f_root->add_GEQ(); GEQ_Handle h2 = f_root->add_GEQ(); for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) { Variable_ID v = cvi2.curr_var(); switch (v->kind()) { case Wildcard_Var: h1.update_coef(e, cvi2.curr_coef()); h2.update_coef(e, -cvi2.curr_coef()); break; case Global_Var: { Global_Var_ID g = v->get_global_var(); Variable_ID v2; if (g->arity() == 0) v2 = stride_cond.get_local(g); else v2 = stride_cond.get_local(g, v->function_of()); h1.update_coef(v2, cvi2.curr_coef()); h2.update_coef(v2, -cvi2.curr_coef()); break; } default: h1.update_coef(v, cvi2.curr_coef()); h2.update_coef(v, -cvi2.curr_coef()); } } h1.update_const((*gei).get_const()); h2.update_const(-lb); stride_cond.simplify(); Relation other_cond = Gist(copy((*i).second), copy(stride_cond)); // find regions with potential mergeable stride condition with this one std::list intervals; intervals.push_back(Interval(i, lb, (*gei).get_const())); for (std::list >::iterator>::iterator j = s.begin(); j != s.end(); j++) if (Must_Be_Subset(copy((**j).second), copy(other_cond))) { Relation stride_cond2 = Gist(copy((**j).second), copy(other_cond)); // interval can be removed if (stride_cond2.is_obvious_tautology()) { intervals.push_back(Interval(*j, 0, c-1)); continue; } stride_cond2 = EQs_to_GEQs(stride_cond2, false); coef_t lb, ub; GEQ_Iterator gei2(stride_cond2.single_conjunct()); coef_t sign = 0; for (Constr_Vars_Iter cvi(*gei2, true); cvi; cvi++) if (sign != 0) { sign = 0; break; } else if (cvi.curr_coef() == c) sign = 1; else if (cvi.curr_coef() == -c) sign = -1; else { sign = 0; break; } if (sign == 0) continue; bool match = true; for (Constr_Vars_Iter cvi(*gei2); cvi; cvi++) { Variable_ID v = cvi.curr_var(); if (v->kind() == Wildcard_Var) continue; else if (v->kind() == Global_Var) { Global_Var_ID g = v->get_global_var(); if (g->arity() == 0) v = (*i).second.get_local(g); else v = (*i).second.get_local(g, v->function_of()); } if (cvi.curr_coef() != sign * (*gei).get_coef(v)) { match = false; break; } } if (!match) continue; for (Constr_Vars_Iter cvi(*gei); cvi; cvi++) { Variable_ID v = cvi.curr_var(); if (v->kind() == Wildcard_Var) continue; else if (v->kind() == Global_Var) { Global_Var_ID g = v->get_global_var(); if (g->arity() == 0) v = stride_cond2.get_local(g); else v = stride_cond2.get_local(g, v->function_of()); } if (cvi.curr_coef() != sign * (*gei2).get_coef(v)) { match = false; break; } } if (!match) continue; if (sign > 0) ub = (*gei2).get_const(); else lb = -(*gei2).get_const(); gei2++; if (!gei2) continue; coef_t sign2 = 0; for (Constr_Vars_Iter cvi(*gei2, true); cvi; cvi++) if (sign2 != 0) { sign2 = 0; break; } else if (cvi.curr_coef() == c) sign2 = 1; else if (cvi.curr_coef() == -c) sign2 = -1; else { sign2 = 0; break; } if (sign2 != -sign) continue; for (Constr_Vars_Iter cvi(*gei2); cvi; cvi++) { Variable_ID v = cvi.curr_var(); if (v->kind() == Wildcard_Var) continue; else if (v->kind() == Global_Var) { Global_Var_ID g = v->get_global_var(); if (g->arity() == 0) v = (*i).second.get_local(g); else v = (*i).second.get_local(g, v->function_of()); } if (cvi.curr_coef() != sign2 * (*gei).get_coef(v)) { match = false; break; } } if (!match) continue; for (Constr_Vars_Iter cvi(*gei); cvi; cvi++) { Variable_ID v = cvi.curr_var(); if (v->kind() == Wildcard_Var) continue; else if (v->kind() == Global_Var) { Global_Var_ID g = v->get_global_var(); if (g->arity() == 0) v = stride_cond2.get_local(g); else v = stride_cond2.get_local(g, v->function_of()); } if (cvi.curr_coef() != sign2 * (*gei2).get_coef(v)) { match = false; break; } } if (!match) continue; if (sign2 > 0) ub = (*gei2).get_const(); else lb = -(*gei2).get_const(); gei2++; if (gei2) continue; intervals.push_back(Interval(*j, lb, ub)); } merge_intervals(intervals, c, Rs, i); // make current region the last one being updated bool invalid = false; for (std::list::iterator ii = intervals.begin(); ii != intervals.end(); ii++) if ((*ii).change && (*ii).pos == i) { invalid = true; intervals.push_back(*ii); intervals.erase(ii); break; } // update hole condition for each region for (std::list::iterator ii = intervals.begin(); ii != intervals.end(); ii++) if ((*ii).change) { change = true; if ((*ii).ub - (*ii).lb + 1 >= (*ii).modulo) (*(*ii).pos).second = copy(other_cond); else { Relation stride_cond = Relation::True((*i).second); F_Exists *f_exists = stride_cond.and_with_and()->add_exists(); Variable_ID e = f_exists->declare(); F_And *f_root = f_exists->add_and(); GEQ_Handle h1 = f_root->add_GEQ(); GEQ_Handle h2 = f_root->add_GEQ(); for (Constr_Vars_Iter cvi2(*gei); cvi2; cvi2++) { Variable_ID v = cvi2.curr_var(); switch (v->kind()) { case Wildcard_Var: h1.update_coef(e, (*ii).modulo); h2.update_coef(e, -(*ii).modulo); break; case Global_Var: { Global_Var_ID g = v->get_global_var(); Variable_ID v2; if (g->arity() == 0) v2 = stride_cond.get_local(g); else v2 = stride_cond.get_local(g, v->function_of()); h1.update_coef(v2, cvi2.curr_coef()); h2.update_coef(v2, -cvi2.curr_coef()); break; } default: h1.update_coef(v, cvi2.curr_coef()); h2.update_coef(v, -cvi2.curr_coef()); } } h1.update_const((*ii).ub); h2.update_const(-(*ii).lb); (*(*ii).pos).second = Intersection(copy(other_cond), stride_cond); (*(*ii).pos).second.simplify(); } } if (invalid) break; } } i++; } } } catch (const presburger_error &e) { throw e; } Relation R2 = Relation::False(l_R); for (std::list >::iterator i = Rs.begin(); i != Rs.end(); i++) R2 = Union(R2, Intersection((*i).first, (*i).second)); R2.simplify(0, 1); return R2; } // // Use gist and value range to calculate a quick rectangular hull. It // intends to replace all hull calculations (QuickHull, BetterHull, // FastTightHull) beyond the method of ConvexHull (dual // representations). In the future, it will support max(...)-like // upper bound. So RectHull complements ConvexHull in two ways: first // for relations that ConvexHull gets too complicated, second for // relations where different conjuncts have different symbolic upper // bounds. // Relation RectHull(NOT_CONST Relation &Rel) { Relation R = Approximate(consume_and_regurgitate(Rel)); if (!R.is_upper_bound_satisfiable()) return R; if (R.has_single_conjunct()) return R; std::vector input_names(R.n_inp()); for (int i = 1; i <= R.n_inp(); i++) input_names[i-1] = R.input_var(i)->name(); std::vector output_names(R.n_out()); for (int i = 1; i <= R.n_out(); i++) output_names[i-1] = R.output_var(i)->name(); DNF_Iterator c(R.query_DNF()); Relation r = Relation(R, c.curr()); c++; std::vector > bounds1(R.n_inp()); std::vector > bounds2(R.n_out()); { Relation t = Project_Sym(copy(r)); t.simplify(); for (int i = 1; i <= R.n_inp(); i++) { Tuple v; for (int j = 1; j <= R.n_inp(); j++) if (j != i) v.append(r.input_var(j)); for (int j = 1; j <= R.n_out(); j++) v.append(r.output_var(j)); Relation t2 = Project(copy(t), v); t2.query_variable_bounds(t2.input_var(i), bounds1[i-1].first, bounds1[i-1].second); } for (int i = 1; i <= R.n_out(); i++) { Tuple v; for (int j = 1; j <= R.n_out(); j++) if (j != i) v.append(r.output_var(j)); for (int j = 1; j <= R.n_inp(); j++) v.append(r.input_var(j)); Relation t2 = Project(copy(t), v); t2.query_variable_bounds(t2.output_var(i), bounds2[i-1].first, bounds2[i-1].second); } } while (c.live()) { Relation r2 = Relation(R, c.curr()); c++; Relation x = Gist(copy(r), Gist(copy(r), copy(r2), 1), 1); if (Difference(copy(r2), copy(x)).is_upper_bound_satisfiable()) x = Relation::True(R); Relation y = Gist(copy(r2), Gist(copy(r2), copy(r), 1), 1); if (Difference(copy(r), copy(y)).is_upper_bound_satisfiable()) y = Relation::True(R); r = Intersection(x, y); { Relation t = Project_Sym(copy(r2)); t.simplify(); for (int i = 1; i <= R.n_inp(); i++) { Tuple v; for (int j = 1; j <= R.n_inp(); j++) if (j != i) v.append(r2.input_var(j)); for (int j = 1; j <= R.n_out(); j++) v.append(r2.output_var(j)); Relation t2 = Project(copy(t), v); coef_t lbound, ubound; t2.query_variable_bounds(t2.input_var(i), lbound, ubound); bounds1[i-1].first = min(bounds1[i-1].first, lbound); bounds1[i-1].second = max(bounds1[i-1].second, ubound); } for (int i = 1; i <= R.n_out(); i++) { Tuple v; for (int j = 1; j <= R.n_out(); j++) if (j != i) v.append(r2.output_var(j)); for (int j = 1; j <= R.n_inp(); j++) v.append(r2.input_var(j)); Relation t2 = Project(copy(t), v); coef_t lbound, ubound; t2.query_variable_bounds(t2.output_var(i), lbound, ubound); bounds2[i-1].first = min(bounds2[i-1].first, lbound); bounds2[i-1].second = max(bounds2[i-1].second, ubound); } } Relation r3(R.n_inp(), R.n_out()); F_And *f_root = r3.add_and(); for (int i = 1; i <= R.n_inp(); i++) { if (bounds1[i-1].first != -posInfinity) { GEQ_Handle h = f_root->add_GEQ(); h.update_coef(r3.input_var(i), 1); h.update_const(-bounds1[i-1].first); } if (bounds1[i-1].second != posInfinity) { GEQ_Handle h = f_root->add_GEQ(); h.update_coef(r3.input_var(i), -1); h.update_const(bounds1[i-1].second); } } for (int i = 1; i <= R.n_out(); i++) { if (bounds2[i-1].first != -posInfinity) { GEQ_Handle h = f_root->add_GEQ(); h.update_coef(r3.output_var(i), 1); h.update_const(-bounds2[i-1].first); } if (bounds2[i-1].second != posInfinity) { GEQ_Handle h = f_root->add_GEQ(); h.update_coef(r3.output_var(i), -1); h.update_const(bounds2[i-1].second); } } r = Intersection(r, r3); r.simplify(); } for (int i = 1; i <= r.n_inp(); i++) r.name_input_var(i, input_names[i-1]); for (int i = 1; i <= r.n_out(); i++) r.name_output_var(i, output_names[i-1]); r.setup_names(); return r; } } // namespace