diff options
Diffstat (limited to 'lib/omega/src/closure.cc')
-rw-r--r-- | lib/omega/src/closure.cc | 2100 |
1 files changed, 2100 insertions, 0 deletions
diff --git a/lib/omega/src/closure.cc b/lib/omega/src/closure.cc new file mode 100644 index 0000000..416a3e7 --- /dev/null +++ b/lib/omega/src/closure.cc @@ -0,0 +1,2100 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + Copyright (C) 2009-2011 West Pomeranian University of Technology, Szczecin + All Rights Reserved. + + Purpose: + All calculations of closure are now here. + + Notes: + Related paper: + - "Transitive closure of infinite graphs and its applications", + Wayne Kelly, William Pugh, Evan Rosser and Tatiana Shpeisman, IJPP 1996. + - "Computing the Transitive Closure of a Union of Affine Integer Tuple + Relations", Anna Beletska, Denis Barthou, Wlodzimierz Bielecki and + Albert Cohen, COCOA 2009. + - "An Iterative Algorithm of Computing the Transitive Closure of a Union + of Parameterized Affine Integer Tuple Relations", Bielecki Wlodzimierz, + Klimek Tomasz, Palkowski Marek and Anna Beletska, COCOA 2010. + + History: + 12/27/09 move ConicClosure here, Chun Chen + 01/19/11 new closure algorithms, Klimek Tomzsz + 02/02/11 move VennDiagramFrom here, Chun Chen +*****************************************************************************/ + +#include <typeinfo> +#include <assert.h> +#include <omega.h> +#include <omega/hull.h> +#include <basic/Iterator.h> +#include <basic/List.h> +#include <basic/SimpleList.h> + +namespace omega { + +void InvestigateClosure(Relation r, Relation r_closure, Relation bounds); +void print_given_bounds(const Relation & R1, NOT_CONST Relation& input_Bounds); +#define printConjunctClosure (closure_presburger_debug & 0x1) +#define detailedClosureDebug (closure_presburger_debug & 0x2) + + +#ifdef TC_STATS +extern int clock_diff(); +extern void start_clock(); +FILE *statsfile; +int singles, totals=0; +#endif + +int closure_presburger_debug = 0; + + +Relation VennDiagramForm(NOT_CONST Relation &Context_In, + Tuple<Relation> &Rs, + int next, + bool anyPositives, + int weight) { + Relation Context = consume_and_regurgitate(Context_In); + if (hull_debug) { + fprintf(DebugFile,"[VennDiagramForm, next = %d, anyPositives = %d, weight = %d \n", next,anyPositives,weight); + fprintf(DebugFile,"context:\n"); + Context.prefix_print(DebugFile); + } + if (anyPositives && weight > 3) { + Context.simplify(); + if (!Context.is_upper_bound_satisfiable()) { + if (hull_debug) + fprintf(DebugFile,"] not satisfiable\n"); + return Context; + } + weight = 0; + } + if (next > Rs.size()) { + if (!anyPositives) { + if (hull_debug) + fprintf(DebugFile,"] no positives\n"); + return Relation::False(Context); + } + Context.simplify(); + if (hull_debug) { + fprintf(DebugFile,"] answer is:\n"); + Context.prefix_print(DebugFile); + } + return Context; + } + Relation Pos = VennDiagramForm(Intersection(copy(Context),copy(Rs[next])), + Rs, + next+1, + true, + weight+2); + Relation Neg = VennDiagramForm(Difference(Context,copy(Rs[next])), + Rs, + next+1, + anyPositives, + weight+1); + if (hull_debug) { + fprintf(DebugFile,"] VennDiagramForm\n"); + fprintf(DebugFile,"pos part:\n"); + Pos.prefix_print(DebugFile); + fprintf(DebugFile,"neg part:\n"); + Neg.prefix_print(DebugFile); + } + return Union(Pos,Neg); +} + + +Relation VennDiagramForm(Tuple<Relation> &Rs, NOT_CONST Relation &Context_In) { + Relation Context = consume_and_regurgitate(Context_In); + if (Context.is_null()) Context = Relation::True(Rs[1]); + if (hull_debug) { + fprintf(DebugFile,"Starting computation of VennDiagramForm\n"); + fprintf(DebugFile,"Context:\n"); + Context.prefix_print(DebugFile); + for(int i = 1; i <= Rs.size(); i++) { + fprintf(DebugFile,"#%d:\n",i); + Rs[i].prefix_print(DebugFile); + } + } + return VennDiagramForm(Context,Rs,1,false,0); +} + +Relation VennDiagramForm(NOT_CONST Relation &R_In, NOT_CONST Relation &Context_In) { + Relation R = consume_and_regurgitate(R_In); + Relation Context = consume_and_regurgitate(Context_In); + Tuple<Relation> Rs; + for (DNF_Iterator c(R.query_DNF()); c.live(); ) { + Rs.append(Relation(R,c.curr())); + c.next(); + } + return VennDiagramForm(Rs,Context); +} + + +Relation ConicClosure (NOT_CONST Relation &R) { + int n = R.n_inp(); + if (n != R.n_out()) + throw std::invalid_argument("conic closure must have the same input arity and output arity"); + + return DeltasToRelation(ConicHull(Deltas(R)), n, n); +} + + +bool is_lex_forward(Relation R) { + if(R.n_inp() != R.n_out()) { + fprintf(stderr, "relation has wrong inputs/outpts\n"); + exit(1); + } + Relation forw(R.n_inp(), R.n_out()); + F_Or * o = forw.add_or(); + for(int a = 1; a <= forw.n_inp(); a++) { + F_And * andd = o->add_and(); + GEQ_Handle g = andd->add_GEQ(); + g.update_coef(input_var(a), -1); + g.update_coef(output_var(a), 1); + g.update_const(1); + for(int b = 1; b < a; b++) { + EQ_Handle e = andd->add_EQ(); + e.update_coef(input_var(a),1); + e.update_coef(output_var(a),-1); + } + } + Relation test = Difference(R, forw); + return !test.is_upper_bound_satisfiable(); +} + + +static Relation compose_n(NOT_CONST Relation &input_r, int n) { + Relation r = consume_and_regurgitate(input_r); + if (n == 1) + return r; + else + return Composition(r, compose_n(copy(r), n-1)); +} /* compose_n */ + + + + +Relation approx_closure(NOT_CONST Relation &input_r, int n) { + Relation r = consume_and_regurgitate(input_r); + Relation r_closure; + + r_closure=r; + int i; + for(i=2; i<=n; i++) + r_closure=Union(r_closure,compose_n(copy(r), n)); + r_closure = Union(r_closure, Relation::Unknown(r_closure)); + + return r_closure; +} /* approx_closure */ + + +static bool is_closure_itself(NOT_CONST Relation &r) { + return Must_Be_Subset(Composition(copy(r),copy(r)),copy(r)); +} + + +/***** + * get a D form of the Relation (single conjunct). + * D = {[ i_1,i_2,...,i_m] -> [j_1, j_2, ..., j_m ] : + * (forall p, 1<= p <= m) L_p <= j_p - i_p <= U_p && + * j_p - i_p == M_p alpha_p}; + * Right now only wildcards that are in stride constraints are treated. + *****/ + +Relation get_D_form (Relation & R) { + Relation D(R.n_inp(), R.n_out()); + + R.make_level_carried_to(R.n_inp()); + assert(R.has_single_conjunct()); + int n_zero=0; + for (DNF_Iterator d(R.query_DNF()); d.live(); d.next()) + n_zero=d.curr()->query_guaranteed_leading_0s(); + + Relation Diff=Deltas(copy(R)); + + if (detailedClosureDebug) { + fprintf(DebugFile, "The relation projected onto differencies is:\n"); + Diff.print_with_subs(DebugFile); + } + + + /* now form D */ + + int i; + coef_t l,u; + F_And * N = D.add_and(); + GEQ_Handle g; + for (i=1; i<=Diff.n_set(); i++) { + Diff.query_variable_bounds(Diff.set_var(i), l,u); +/* if (i== n_zero+1 && l==negInfinity) + l=1; */ + if (l!=negInfinity) { + g=N->add_GEQ(); + g.update_coef(D.input_var(i),-1); + g.update_coef(D.output_var(i),1); + g.update_const(-l); + g.finalize(); + } + if (u!=posInfinity) { + g=N->add_GEQ(); + g.update_coef(D.input_var(i),1); + g.update_coef(D.output_var(i),-1); + g.update_const(u); + g.finalize(); + } + } + + /* add all stride constrains if they do exist */ + + Conjunct *c = Diff.single_conjunct(); + + if (c->locals().size()>0) {// there are local variables + // now go through all the equalities + + coef_t coef=0; + int pos=0; + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + // constraint is in stride form if it has 2 vars, + // one of which is wildcard. Count number if vars and wildcard vars + int nwild=0,nvar=0; + + for (Constr_Vars_Iter cvi(*eq, false); cvi; cvi++) { + if ((*cvi).var->kind() == Global_Var) + continue; + else if ((*cvi).var->kind() == Wildcard_Var) { + coef=(*cvi).coef; + nwild++; + } + else + pos=(*cvi).var->get_position(); + nvar++; + } + if (nvar==2 && nwild==1) { //stride constraint + EQ_Handle e=N->add_stride(coef); + e.update_coef(D.input_var(pos),-1); + e.update_coef(D.output_var(pos),1); + e.finalize(); + } + } + } // end search of stride constrains + + D.finalize(); + D.simplify(); + return D; +} /* end get_D_form */ + +/**** + * get relation A x A describing a region of domain and range: + * A=Hull(Domain(R), Range(R)) intersection IterationSpace + * returns cross product A x A + ***/ + +Relation form_region(const Relation &R, const Relation& IterationSpace) { + Relation H=Union(Domain(copy(R)), Range(copy(R))); + H.simplify(1,1); + H = EQs_to_GEQs(H); + H=Hull(H); + Relation A=Intersection(H, copy(IterationSpace)); + Relation A1=A; + return Cross_Product(A,A1); +} + +Relation form_region1(const Relation &R, const Relation& IterationSpace) { + Relation Dom=Intersection(Domain(copy(R)), copy(IterationSpace)); + Relation Ran=Intersection(Range(copy(R)), copy(IterationSpace)); + return Cross_Product(Dom,Ran); +} + + +/**** + * Check if we can use D instead of R + * i.e. D intersection (A cross A) is Must_Be_Subset of R + ***/ + +bool isD_OK(Relation &R, Relation &D, Relation &AxA) { + Relation B=Intersection(copy(D), copy(AxA)); + B.simplify(); + + if (detailedClosureDebug) { + fprintf(DebugFile, "Intersection of D and AxA is:\n"); + B.print_with_subs(DebugFile); + } + assert (Must_Be_Subset(copy(R),copy(B))); + + return Must_Be_Subset(B, copy(R)); +} + + + +/**** + * check if the constraint is a stride one. Here we say that an equality + * constraint is a stride constraint if it has exatly one wildcard. + * The function returns number of the wildcards in the constraint. + * So if we know that constraint is from the relation in D form, then + * it cannot have more than 1 wildcard variables, and the result of + * this functions can be treated as bool. + ***/ + +static int is_stride(const EQ_Handle &eq) { + int n=0; + + for (Constr_Vars_Iter cvi(eq,true); cvi; cvi++) + n++; + + return n; +} + + + +/***** + * check if the constraint is in the form i_k' - i_k comp_op c + * return v - the number of the var and the type of the comp_op: + * 1 - >, -1 - <, 0 - not in the right form + * if this is equality constraint in the right form any 1 or -1 can be + * returned + ******/ + +static coef_t is_constraint_in_D_form(Relation &r, const Constraint_Handle &h, int &v) { + v=-1; + coef_t c_out = 0; + for (int i = 1; i <= r.n_inp(); i++) { + coef_t c_in = h.get_coef(r.input_var(i)); + if (c_in) { + if (v!=-1) + return 0; + v=i; + c_out = h.get_coef(r.output_var(i)); + + // special case for modular constraint -- by chun 04/02/2009 + if (h.has_wildcards() && typeid(h) == typeid(EQ_Handle)) { + coef_t g = 0; + for (Constr_Vars_Iter cvi(h, true); cvi; cvi++) + g = gcd(g, abs(cvi.curr_coef())); + c_in = int_mod_hat(c_in, g); + c_out = int_mod_hat(c_out, g); + + if (g == 2) { + if (c_in * c_out == 1) { + c_out = -1; + } + else + return 0; + } + else if (c_in * c_out != -1) + return 0; + } + // other cases + else if (c_in * c_out != -1) + return 0; + } + } + return c_out; +} + + +/*** + * Check if relation is in the D form + * D = {[ i_1,i_2,...,i_m] -> [j_1, j_2, ..., j_m ] : + * (forall p, 1<= p <= m) L_p <= j_p - i_p <= U_p && + * j_p - i_p == M_p alpha_p}; + * Right now we do not check for multiple stride constraints for one var. + * Probably they cannot exist in simplified conjunct + * This function will be used in assertions + *****/ + +bool is_in_D_form(Relation & D) { + /* check that D has one conjunct */ + + if (! D.has_single_conjunct()) + return false; + + Conjunct * c=D.single_conjunct(); + + if (D.global_decls()->size() != 0) // there are symbolic vars + return false; + + if (D.n_inp() != D.n_out()) + return false; + + int n=D.n_inp(); + + Tuple<int> bl(n), bu(n); + + for (int i=1; i<= n; i++) + bl[i]=bu[i]=0; + + int v; + coef_t res; + + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + if ((res=is_constraint_in_D_form(D,*eq,v))==0) + return false; + int n_wild=is_stride(*eq); + if (n_wild>=2) + return false; + if (n_wild==0) { // not stride constraint + if (bl[v] || bu[v]) + return false; + bl[v]=bu[v]=1; + } + } + + for (GEQ_Iterator geq = c->GEQs(); geq.live(); geq.next()) { + if ((res=is_constraint_in_D_form(D,*geq,v))==0) + return false; + if ((res>0 && bl[v]) || (res<0 && bu[v])) + return false; + if (res>0) + bl[v]=1; + else + bu[v]=1; + } + + return true; +} + + +#define get_D_plus_form(R) (get_D_closure(R,1)) +#define get_D_star_form(R) (get_D_closure(R,0)) + +/**** + * Get D+ or D* from the relation that is in D form + * To get D+ calculate: + * D+= {[i1, i2 .. i_m] -> {j1, j2, ..., j_m]: + * exists s s.t. s>=1 and + * (forall p, 1<= p <= m) L_p * s<= j_p - i_p <= U_p*s && + * j_p - i_p == M_p alpha_p}; + * To get D* calculate almost the same relation but s>=0. + * Parameter n is 1 for getting D+ and 0 for D* + ****/ + + +Relation get_D_closure(Relation & D, int n) { + assert (is_in_D_form(D)); + assert(n==0 || n==1); + + Conjunct *c=D.single_conjunct(); + + Relation R(D.n_inp(), D.n_out()); + + F_Exists * ex = R.add_exists(); + Variable_ID s = ex->declare("s"); + F_And * N = ex->add_and(); + + /* add s>=1 or s>=0 */ + + GEQ_Handle geq= N->add_GEQ(); + geq.update_coef(s,1); + geq.update_const(-n); + geq.finalize(); + + + /* copy and modify all the EQs */ + + for (EQ_Iterator j= c->EQs(); j.live(); j.next()) { + EQ_Handle eq=N->add_EQ(); + copy_constraint(eq, *j); + + // if it's stride constraint do not change it + + if (!is_stride(*j)) { + /* eq is j_k -i_k = c, replace c buy s*c */ + + eq.update_coef(s, (*j).get_const()); + eq.update_const(-(*j).get_const()); + } + eq.finalize(); + } + + /* copy and modify all the GEQs */ + + for (GEQ_Iterator gi= c->GEQs(); gi.live(); gi.next()) { + geq=N->add_GEQ(); + copy_constraint(geq, *gi); + + /* geq is j_k -i_k >=c or i_k-j_k >=c, replace c buy s*c */ + + geq.update_coef(s,(*gi).get_const()); + geq.update_const(-(*gi).get_const()); + geq.finalize(); + } + + R.finalize(); + + if (detailedClosureDebug) { + fprintf(DebugFile, "Simplified D%c is:\n", n==1?'+':'*'); + R.print_with_subs(DebugFile); + } + + return R; +} + + +/*** + * Check if we can easily calculate the D* (D* will be convex). + * We can calculate D* if all differences have both lower and upper + * bounds to be non -/+ infinity + ***/ + + +bool can_get_D_star_form(Relation &D) { + assert(is_in_D_form(D)); + Conjunct *c=D.single_conjunct(); + + int n=D.n_inp(); + Tuple<int> bl(n), bu(n); + int i; + + for (i=1; i<=n; i++) + bl[i]=bu[i]=0; + + for (EQ_Iterator eq = c->EQs(); eq.live(); eq.next()) { + // do not check stride constraints + if (!is_stride(*eq)) { + for (i=1; i<=n; i++) { + if ((*eq).get_coef(D.input_var(i)) !=0 ) + bl[i]=bu[i]=1; + } + } + } + + + for (GEQ_Iterator geq = c->GEQs(); geq.live(); geq.next()) { + for (i=1; i<=n; i++) { + coef_t k; + if ((k=(*geq).get_coef(D.input_var(i))) != 0) { + if (k>0) + bu[i]=1; + else + bl[i]=1; + } + } + } + + for (i=1; i<=n; i++) + if (!bl[i] || !bu[i]) + return false; + + return true; +} + + + +/***** + * Check whether the relation intersect with identity or not + ****/ + +bool does_intersect_with_identity(Relation &R) { + assert (R.n_inp() == R.n_out()); + + Relation I=Identity(R.n_inp()); + Relation C=Intersection(I, copy(R)); + return C.is_upper_bound_satisfiable(); +} + +bool does_include_identity(Relation &R) { + Relation I=Identity(R.n_inp()); + return Must_Be_Subset(I, copy(R)); +} + +/***** + * Bill's closure: check if it is possible to calculate transitive closure + * of the relation using the Bill's algorithm. + * Return the transitive closure relation if it is possible and null relation + * otherwise + ****/ + +bool Bill_closure(Relation &R, Relation& IterationSpace, Relation & R_plus, Relation & R_star) { +#ifdef TC_STATS + fprintf(statsfile,"start bill closure\n"); +#endif + + if (does_include_identity(R)) + return false; + + if (detailedClosureDebug) { + fprintf(DebugFile, "\nApplying Bill's method to calculate transitive closure\n"); + } + + // get D and AxA + Relation D=get_D_form(R); + + + if (detailedClosureDebug) { + fprintf(DebugFile,"\n D form for the relation:\n"); + D.print_with_subs(DebugFile); + } + + Relation AxA=form_region1(R, IterationSpace); + + if (detailedClosureDebug) { + fprintf(DebugFile, "\n AxA for the relation:\n"); + AxA.print_with_subs(DebugFile); + } + + // compute R_+ + + R_plus=Intersection(get_D_plus_form(D), copy(AxA)); + + if (detailedClosureDebug) { + fprintf(DebugFile, "\nR_+= D+ intersection AxA is:\n"); + R_plus.print_with_subs(DebugFile); + } + + // compute R_* + R_star=Intersection(get_D_star_form(D), form_region(R,IterationSpace)); + + if (detailedClosureDebug) { + fprintf(DebugFile, "\nR_*= D* intersection AxA is:\n"); + R_star.print_with_subs(DebugFile); + } + +/* Check that R_+ is acyclic. + Given the way we constructed R_+, R_+=(R_+)+. + As a result it's enough to verify that R_+ intersection I = 0, + to prove that R_+ is acyclic. +*/ + + if (does_intersect_with_identity(R_plus)) { + if (detailedClosureDebug) { + fprintf(DebugFile,"R_+ is not acyclic.\n"); + } + return false; + } + + //Check R_+ - R is Must_Be_Subset of R o R_+ + + if (!Must_Be_Subset(Difference(copy(R_plus), copy(R)), Composition(copy(R), copy(R_plus)))) { +#if defined(TC_STATS) + fprintf(statsfile, "R_+ -R is not a Must_Be_Subset of R o R_+\n"); + fprintf(statsfile, "Bill Method is not applicable\n"); +#endif + return false; + } + if (detailedClosureDebug) { + fprintf(DebugFile, "R_+ -R is a Must_Be_Subset of R o R_+ - good\n"); + } + +// if we are here than all tests worked, and R_+ is transitive closure +// of R. + +#if defined(TC_STATS) + fprintf(statsfile,"\nAll three tests succeeded -- exact closure found\n"); + fprintf(statsfile, "Transitive closure is R_+\n"); +#endif +// assert(isD_OK(R,D,AxA)); + return true; +} + + +/********************************************************************** + * print the relation given the bounds on the iteration space + * If the bounds are unknown (Bounds is Null), then just print relation + * itself + ****/ + +void print_given_bounds( const Relation& R1, NOT_CONST Relation& input_Bounds) { + Relation & Bounds = (Relation &)input_Bounds; + Relation r; + if (Bounds.is_null()) + r=R1; + else + r = Gist(copy(R1),copy(Bounds),1); + r.print_with_subs(DebugFile); +} + +/********************************************************************** + * Investigate closure: + * checks if the copmuted approximation on the Transitive closure + * is upper and lower bound. If it's both - it's exact. + * This function doesn't return any value. It's just prints a lot + * of debug output + * INPUT: + * r - relation + * r_closure - approximation on r+. + * F - iteration space + **********************************************************************/ + +void InvestigateClosure(Relation r, Relation r_closure, Relation F) { + Relation r3; + bool LB_res, UB_res; + + if (!F.is_null()) + F=Cross_Product(copy(F),copy(F)); + + fprintf(DebugFile, "\n\n--->investigating the closure of the relation:\n"); + print_given_bounds(r,F); + + fprintf(DebugFile, "\nComputed closure is:\n"); + print_given_bounds(r_closure,F); + + r3=Composition(copy(r),copy(r_closure)); + r3.simplify(1,1); + + r3=Union(r3,Composition(copy(r_closure),copy(r))); + r3.simplify(1,1); + + r3=Union(r3,copy(r)); + r3.simplify(1,1); + + Relation remainder = Difference(copy(r3),copy(r_closure)); + + if (!F.is_null()) { + r3=Gist(r3,F,1); + } + r3.simplify(1,1); + + if (!F.is_null()) { + r_closure=Gist(r_closure,F,1); + } + r_closure.simplify(1,1); + + LB_res= Must_Be_Subset(copy(r_closure),copy(r3)); + + UB_res=Must_Be_Subset(copy(r3),copy(r_closure)); + + fprintf(DebugFile,"\nThe results of checking closure (gist) are:\n"); + fprintf(DebugFile,"LB - %s, UB - %s\n", LB_res?"YES":"NO", UB_res?"YES":"NO"); + + if (!UB_res) { + remainder.simplify(2,2); + fprintf(DebugFile,"Dependences not included include:\n"); + print_given_bounds(remainder,F); + } +} + + + +/**** + * Transitive closure of the relation containing single conjunct + ****/ + +bool ConjunctTransitiveClosure (NOT_CONST Relation & input_R, Relation & IterationSpace, Relation & R_plus, Relation & R_star) { + Relation R = consume_and_regurgitate(input_R); + assert(R.has_single_conjunct()); + + if (printConjunctClosure) { + fprintf(DebugFile,"\nTaking closure of the single conjunct: [\n"); + R.print_with_subs(DebugFile); + } +#ifdef TC_STATS + fprintf(statsfile,"start conjuncttransitiveclosure\n"); + singles++; +#endif + + if (is_closure_itself(copy(R))) { +#ifdef TC_STATS + fprintf(statsfile, "Relation is closure itself\n"); +#endif + int ndim_all, ndim_domain; + R.dimensions(ndim_all,ndim_domain); + if (ndim_all == ndim_domain +1) { + Relation ispace = Cross_Product(Domain(copy(R)),Range(copy(R))); + Relation R_zero = Intersection(copy(ispace),Identity(R.n_inp())); + R_star = Hull(Union(copy(R),R_zero),true,1,ispace); + R_plus=R; + if (printConjunctClosure) { + fprintf(DebugFile, "\n] For this relation R+=R\n"); + fprintf(DebugFile,"R*:\n"); + R_star.print_with_subs(DebugFile); + } + return true; + } + else { + R_star=R; + R_plus=R; + if (printConjunctClosure) { + fprintf(DebugFile, "\n] For this relation R+=R, not appropriate for R*\n"); + } + return false; + } + } + else { + bool done=false; + if (!IterationSpace.is_null()) { +// Bill's closure requires the information about Iteration Space. +// So if IterationSpace is NULL, i.e. unknown( e.g. when calling from parser, +// we do not do Bill's closure + + done = Bill_closure(R, IterationSpace, R_plus, R_star); +#ifdef TC_STATS + fprintf(statsfile,"Bill closure is %sapplicable\n",done?"":"not "); +#endif + if (printConjunctClosure) { + if (!done) + fprintf(DebugFile, "Bill's closure is not applicable\n"); + else { + fprintf(DebugFile, "Bill's closure is applicable\n"); + fprintf (DebugFile, " For R:\n"); + R.print_with_subs(DebugFile); + fprintf(DebugFile, "R+ is:\n"); + R_plus.print_with_subs(DebugFile); + fprintf(DebugFile, "R* is:\n"); + R_star.print_with_subs(DebugFile); + fprintf(DebugFile, "\n"); + InvestigateClosure(R, R_plus, IterationSpace); + } + } + } + if (done) { + if (printConjunctClosure) { + fprintf(DebugFile, "]\n"); + } + return true; + } + else { + // do and check approximate closure (several compositions) + R_plus = approx_closure(copy(R), 2); +#ifdef TC_STATS + fprintf(statsfile,"Approximating closure with 2 compositions\n"); +#endif + if (printConjunctClosure) { + fprintf(DebugFile, "Doing approximate closure\n"); + InvestigateClosure(R, R_plus, IterationSpace); + } + } //end else (!done after Bill Closure or Iteration space is NULL) + + if (printConjunctClosure) { + fprintf(DebugFile, "]\n"); + } + } + return false; +} + + +/********************************************************************* + * try to get conjunct transitive closure. + * it we can get it easy get it, return true. + * if not - return false + ********************************************************************/ + + +bool TryConjunctTransitiveClosure (NOT_CONST Relation & input_R, Relation & IterationSpace, Relation & R_plus) { + Relation R = consume_and_regurgitate(input_R); + assert(R.has_single_conjunct()); +#ifdef TC_STATS + fprintf(statsfile,"start tryconjuncttransitiveclosure\n"); + singles++; +#endif + + if (printConjunctClosure) { + fprintf(DebugFile,"\nTrying to take closure of the single conjunct: [\n"); + R.print_with_subs(DebugFile); + } + + if (is_closure_itself(copy(R))) { +#ifdef TC_STATS + fprintf(statsfile, "Relation is closure itself, leave alone (try)\n"); +#endif + if (printConjunctClosure) + fprintf(DebugFile, "\n ]The relation is closure itself. Leave it alone\n"); + return false; + } + else { + bool done; + assert(!IterationSpace.is_null()); + Relation R_star; + done = Bill_closure(R, IterationSpace, R_plus, R_star); +#ifdef TC_STATS + fprintf(statsfile, "Bill closure is %sapplicable (try)\n", done?"":"NOT "); +#endif + if (printConjunctClosure) { + if (!done) + fprintf(DebugFile, "]Bill's closure is not applicable\n"); + else { + fprintf(DebugFile, "]Bill's closure is applicable\n"); + fprintf (DebugFile, " For R:\n"); + R.print_with_subs(DebugFile); + fprintf(DebugFile, "R+ is:\n"); + R_plus.print_with_subs(DebugFile); + fprintf(DebugFile, "R* is:\n"); + R_star.print_with_subs(DebugFile); + fprintf(DebugFile, "\n"); + InvestigateClosure(R, R_plus, IterationSpace); + } + } + return done; + } + //return false; +} + + +bool Equal (const Relation & r1, const Relation & r2) { + bool res=Must_Be_Subset (copy(r1), copy(r2)); + if (!res) + return false; + return Must_Be_Subset (copy(r2),copy(r1)); +} + + +void appendClausesToList(Simple_List<Relation> &L, Relation &R) { + R.make_level_carried_to(R.n_inp()); + R.simplify(2,2); + for(int depth = R.n_inp(); depth >= -1; depth--) + for (DNF_Iterator d(R.query_DNF()); d.live(); d.next()) + if (d.curr()->query_guaranteed_leading_0s() == depth) { + L.append(Relation(R, d.curr())); + } +} + +void printRelationList(Simple_List<Relation> &L) { + for (Simple_List_Iterator<Relation> li(L); li.live(); li.next()) { + li.curr().print_with_subs(DebugFile); + } +} + +/**** + * Transitive closure of the relation containing multiple conjuncts + * New (Bill's) version + ***/ + +Relation TransitiveClosure0(NOT_CONST Relation &input_r, int maxExpansion, NOT_CONST Relation & input_IterationSpace) { + Relation r = consume_and_regurgitate(input_r); + Relation IterationSpace = consume_and_regurgitate(input_IterationSpace); + + if (closure_presburger_debug) + fprintf(DebugFile, "\n\n[Transitive closure\n\n"); + + Relation result; + +#ifdef TC_STATS +#define TC_RUNS 1 + int in_conj = copy(r).query_DNF()->length(); + totals++; + fprintf(statsfile,"%d closure run\n", totals); + if(is_in_D_form(copy(r))) + fprintf(statsfile, "Relation initially in D form\n"); + else + fprintf(statsfile, "Relation initially NOT in D form\n"); + if(is_lex_forward(copy(r))) + fprintf(statsfile, "Relation is initially lex forw\n"); + else + fprintf(statsfile, "Relation is NOT initially lex forw\n"); + start_clock(); + for(int tc_loop = 1; tc_loop <= TC_RUNS; tc_loop++) { + singles = 0; +#endif + + assert(!r.is_null()); + assert(r.n_inp() == r.n_out()); + + if (r.max_ufs_arity() > 0) { + assert(r.max_ufs_arity() == 0 && "Can't take transitive closure with UFS yet."); + + fprintf(stderr, "Can't take transitive closure with UFS yet."); + exit(1); + } + + r.simplify(2,2); + if (!r.is_upper_bound_satisfiable()) { +#ifdef TC_STATS + int totalTime = clock_diff(); + fprintf(statsfile, "Relation is unsatisfiable\n"); + fprintf(statsfile, "input conj: %d output conj: %d #singe conj closures: %d time: %d\n", + in_conj, copy(result).query_DNF()->length(), + singles, + totalTime/TC_RUNS); +#endif + + + if (closure_presburger_debug) + fprintf(DebugFile, "]TC : relation is false\n"); + return r; + } + + IterationSpace = Hull(Union(Domain(copy(r)),Range(copy(r))), true, 1, IterationSpace); + + if (detailedClosureDebug) { + fprintf(DebugFile, "r is:\n"); + r.print_with_subs(DebugFile); + fprintf(DebugFile, "IS is:\n"); + IterationSpace.print_with_subs(DebugFile); + } + Relation dom = Domain(copy(r)); + dom.simplify(2,1); + Relation rng = Range(copy(r)); + rng.simplify(2,1); + Relation AC = ConicClosure(Restrict_Range(Restrict_Domain(copy(r),copy(rng)),copy(dom))); + Relation UB = Union(copy(r),Join(copy(r),Join(AC,copy(r)))); + UB.simplify(2,1); + + if (detailedClosureDebug) { + fprintf(DebugFile, "UB is:\n"); + UB.print_with_subs(DebugFile); + } + result = Relation::False(r); + Simple_List<Relation> firstChoice,secondChoice; + + r.simplify(2,2); + + Relation test = Difference(copy(r),Composition(copy(r),copy(r))); + test.simplify(2,2); + if (r.number_of_conjuncts() > test.number_of_conjuncts()) { + Relation test2 = Union(copy(test),Composition(copy(test),copy(test))); + test2.simplify(2,2); + if (Must_Be_Subset(copy(r),copy(test2))) r = test; + else if (detailedClosureDebug) { + fprintf(DebugFile, "Transitive reduction not possible:\n"); + fprintf(DebugFile, "R is:\n"); + r.print_with_subs(DebugFile); + fprintf(DebugFile, "test2 is:\n"); + test2.print_with_subs(DebugFile); + } + } + + r.make_level_carried_to(r.n_inp()); + if (detailedClosureDebug) { + fprintf(DebugFile, "r is:\n"); + r.print_with_subs(DebugFile); + } + for(int depth = r.n_inp(); depth >= -1; depth--) + for (DNF_Iterator d(r.query_DNF()); d.live(); d.next()) + if (d.curr()->query_guaranteed_leading_0s() == depth) { + Relation C(r, d.curr()); + firstChoice.append(C); + } + + bool first_conj=true; + for (Simple_List_Iterator<Relation> sli(firstChoice); sli; sli++) { + if (first_conj) + first_conj=false; + else { + Relation C_plus; + bool change=TryConjunctTransitiveClosure( + copy(sli.curr()), IterationSpace, C_plus); + if (change) + sli.curr()=C_plus; + } + } + + //compute closure + int maxClauses = 3+firstChoice.size()*(1+maxExpansion); + + int resultConjuncts = 0; + int numFails = 0; + bool resultInexact = false; + while (!firstChoice.empty() || !secondChoice.empty()) { + Relation R_plus, R_star; + + if (detailedClosureDebug) { + fprintf(DebugFile,"Main loop of TC:\n"); + if (!firstChoice.empty()) { + fprintf(DebugFile,"First choice:\n"); + printRelationList(firstChoice); + } + if (!secondChoice.empty()) { + fprintf(DebugFile,"Second choice:\n"); + printRelationList(secondChoice); + } + } + + Relation R; + if (!firstChoice.empty()) + R = firstChoice.remove_front(); + else R = secondChoice.remove_front(); + + if (detailedClosureDebug) { + fprintf(DebugFile, "Working with conjunct:\n"); + R.print_with_subs(DebugFile); + } + + bool known=ConjunctTransitiveClosure(copy(R),IterationSpace, R_plus, R_star); + + if (!known && numFails < firstChoice.size()) { + numFails++; + firstChoice.append(R); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nTry another conjunct, R is not suitable\n"); + R.print_with_subs(DebugFile); + } + continue; + } + + + if (detailedClosureDebug) { + fprintf(DebugFile,"\nR+ is:\n"); + R_plus.print_with_subs(DebugFile); + if (known) { + fprintf(DebugFile, "Known R? is :\n"); + R_star.print_with_subs(DebugFile); + } + else + fprintf(DebugFile, "The R* for this relation is not calculated\n"); + } + + Relation R_z; + if (known) { + R_z=Difference(copy(R_star),copy(R_plus)); + known = R_z.is_upper_bound_satisfiable(); + if (known) { + int d = R.single_conjunct()->query_guaranteed_leading_0s(); + R_z.make_level_carried_to(min(R.n_inp(),d+1)); + if (R_z.query_DNF()->length() > 1) known = false; + if (detailedClosureDebug) { + fprintf(DebugFile, "\nForced R_Z to be level carried at level %d\n",min(R.n_inp(),d+1)); + } + } + if (detailedClosureDebug) { + if (known) { + fprintf(DebugFile, "\nDifference between R? and R+ is:\n"); + R_z.print_with_subs(DebugFile); + } + else + fprintf(DebugFile, "\nR_z is unusable\n"); + } + } + else R_z = Relation::False(r); + + if (!known) + numFails++; + else numFails = 0; + if (!known && numFails <= firstChoice.size()) { + firstChoice.append(R); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nTry another conjunct, Rz is avaiable for R:\n"); + R.print_with_subs(DebugFile); + } + continue; + } + + //make N empty list + Relation N = Relation::False(r); + + //append R+ to T + result = Union(result, copy(R_plus)); + resultConjuncts++; + + int expansion = maxClauses - (resultConjuncts + 2*firstChoice.size() + secondChoice.size()); + if (expansion < 0) expansion = 0; + if (detailedClosureDebug) { + fprintf(DebugFile,"Max clauses = %d\n",maxClauses); + fprintf(DebugFile,"result conjuncts = %d\n",resultConjuncts); + fprintf(DebugFile,"firstChoice's = %d\n",firstChoice.size()); + fprintf(DebugFile,"secondChoice's = %d\n",secondChoice.size()); + fprintf(DebugFile,"Allowed expansion is %d\n",expansion); + } + + bool firstPart=true; + if (!known && expansion == 0) { + if (detailedClosureDebug) { + fprintf(DebugFile,"Expansion = 0, R? unknown, skipping composition\n"); + } + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 1\n"); + resultInexact = true; + } + else + for (Simple_List_Iterator<Relation> s(firstChoice); + firstPart? + (s.live()?true: + (s = Simple_List_Iterator<Relation>(secondChoice), + firstPart = false, + s.live())) + :s.live(); + s.next()) { + assert(s.live()); + Relation C=(s.curr()); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nComposing chosen conjunct with C:\n"); + C.print_with_subs(DebugFile); + } + + if (!known) { + if (detailedClosureDebug) { + fprintf(DebugFile, "\nR? is unknown! No debug info here yet\n"); + } + Relation C1=Composition(copy(C), copy(R_plus)); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nGenerating \n"); + C1.print_with_subs(DebugFile); + } + C1.simplify(); + Relation newStuff = + Difference( + Difference(copy(C1),copy(C)), + copy(R_plus)); + newStuff.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "New Stuff:\n"); + newStuff.print_with_subs(DebugFile); + } + bool C1_contains_new_stuff = newStuff.is_upper_bound_satisfiable(); + if (C1_contains_new_stuff) { + if (newStuff.has_single_conjunct()) + C1 = newStuff; + if (expansion) { + N = Union(N,copy(C1)); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 2\n"); + resultInexact = true; + break; + } + } + else C1 = Relation::False(C1); + + Relation C2(Composition(copy(R_plus),copy(C))); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nGenerating \n"); + C2.print_with_subs(DebugFile); + } + newStuff = + Difference( + Difference( + Difference(copy(C2),copy(C)), + copy(C1)), + copy(R_plus)); + newStuff.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "New Stuff:\n"); + newStuff.print_with_subs(DebugFile); + } + if (newStuff.is_upper_bound_satisfiable()) { + if (newStuff.has_single_conjunct()) + C2 = newStuff; + if (expansion) { + N = Union(N,copy(C2)); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 3\n"); + resultInexact = true; + break; + } + } + else C2 = Relation::False(C2); + + if (C1_contains_new_stuff) { + Relation C3(Composition(copy(R_plus),copy(C1))); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nGenerating \n"); + C3.print_with_subs(DebugFile); + } + newStuff = + Difference( + Difference( + Difference( + Difference(copy(C3),copy(C)), + copy(C1)), + copy(C2)), + copy(R_plus)); + newStuff.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "New Stuff:\n"); + newStuff.print_with_subs(DebugFile); + } + if (newStuff.is_upper_bound_satisfiable()) { + if (newStuff.has_single_conjunct()) + C3 = newStuff; + if (expansion) { + N = Union(N,C3); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 4\n"); + resultInexact = true; + break; + } + } + } + + } + else { + Relation C_Rz(Composition(copy(C),copy(R_z))); + if (detailedClosureDebug) { + fprintf(DebugFile, "C o Rz is:\n"); + C_Rz.print_with_subs(DebugFile); + } + + Relation Rz_C_Rz(Composition(copy(R_z),copy(C_Rz))); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nRz o C o Rz is:\n"); + Rz_C_Rz.print_with_subs(DebugFile); + } + + if (Equal(C,Rz_C_Rz)) { +#if defined(TC_STATS) + fprintf(statsfile,"weak test selects C?\n"); +#endif + Relation tmp = Composition(C,copy(R_star)); + tmp.simplify(); + Relation tmp2 = Composition(copy(R_star),copy(tmp)); + tmp2.simplify(); + if (Must_Be_Subset(copy(tmp2),copy(tmp))) + *s = tmp; + else + *s = tmp2; + if (detailedClosureDebug) { + fprintf(DebugFile,"\nC is equal to Rz o C o Rz so R? o C o R? replaces C\n"); + fprintf(DebugFile, "R? o C o R? is:\n"); + (*s).print_with_subs(DebugFile); + } + } + else { +#if defined(TC_STATS) + fprintf(statsfile,"weak test fails\n"); +#endif + if (Equal(C, C_Rz)) { + *s=Composition(copy(C),copy(R_star)); + Relation p(Composition(copy(R_plus), copy(*s))); + p.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nC is equal to C o Rz, so C o Rz replaces C\n"); + fprintf (DebugFile, "C o R? is:\n"); + (*s).print_with_subs(DebugFile); + fprintf (DebugFile, "R+ o C o R? is added to list N. It's :\n"); + p.print_with_subs(DebugFile); + } + if (!Is_Obvious_Subset(copy(p),copy(R_plus)) + && !Is_Obvious_Subset(copy(p),copy(C))) { + if (expansion) { + p.simplify(2,2); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 5\n"); + resultInexact = true; + break; + } + } + } + else { + Relation Rz_C(Composition(copy(R_z),copy(C))); + + if (Equal(C,Rz_C)) { + *s=Composition(copy(R_star),copy(C)); + Relation Rstar_C_Rplus(Composition(copy(*s),copy(R_plus))); + Rstar_C_Rplus.simplify(); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nC is equal to Rz o C , so R? o C replaces C\n"); + fprintf (DebugFile, "R? o C is:\n"); + (*s).print_with_subs(DebugFile); + fprintf (DebugFile, "R+ o C is added to list N. It's :\n"); + Rstar_C_Rplus.print_with_subs(DebugFile); + } + if (!Is_Obvious_Subset(copy(Rstar_C_Rplus),copy(R_plus)) + && !Is_Obvious_Subset(copy(Rstar_C_Rplus),copy(C))) { + if (expansion) + N = Union(N,Rstar_C_Rplus); + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 6\n"); + resultInexact = true; + break; + } + } + } + else { + if (detailedClosureDebug) { + fprintf(DebugFile, "\nHave to handle it the hard way\n"); + } + Relation C1=Composition(copy(C), copy(R_plus)); + C1.simplify(); + if (!Is_Obvious_Subset(copy(C1),copy(R_plus)) + && !Is_Obvious_Subset(copy(C1),copy(C))) { + if (expansion) { + N = Union(N,copy(C1)); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) fprintf(DebugFile,"RESULT BECOMES INEXACT 7\n"); + resultInexact = true; + break; + } + } + + Relation C2(Composition(copy(R_plus),copy(C))); + C2.simplify(); + if (!Is_Obvious_Subset(copy(C2),copy(R_plus)) + && !Is_Obvious_Subset(copy(C2),copy(C))) { + if (expansion) { + N = Union(N,C2); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) { + fprintf(DebugFile,"RESULT BECOMES INEXACT 8\n"); + fprintf(DebugFile,"Have to discard:\n"); + C2.print_with_subs(DebugFile); + } + resultInexact = true; + break; + } + } + Relation C3(Composition(copy(R_plus),C1)); + C3.simplify(); + if (!Is_Obvious_Subset(copy(C3),copy(R_plus)) && !Is_Obvious_Subset(copy(C3),copy(C))) { + if (expansion) { + N = Union(N,C3); + expansion--; + } + else { + if (!resultInexact && detailedClosureDebug) + fprintf(DebugFile,"RESULT BECOMES INEXACT 9\n"); + resultInexact = true; + break; + } + } + } + } + } + } + } + + //now we processed the first conjunct. + if (detailedClosureDebug) { + N.simplify(2,2); + fprintf(DebugFile, "\nNew conjuncts:\n"); + N.print_with_subs(DebugFile); + } + + N.simplify(2,2); + appendClausesToList(secondChoice,N); + } + + //Did we do all conjuncts? If not, make T be inexact + result.copy_names(r); + + result.simplify(2,2); + + if (!result.is_exact()) { + result = Lower_Bound(result); + resultInexact = true; + } + if (resultInexact) { + Relation test(Composition(copy(result),copy(result))); + test.simplify(2,2); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nResult is:\n"); + result.print_with_subs(DebugFile); + fprintf(DebugFile, "\nResult composed with itself is:\n"); + test.print_with_subs(DebugFile); + } + if (!Must_Be_Subset(test,copy(result))) { + result = Union(result,Intersection(UB, Relation::Unknown(result))); + } + } + +#ifdef TC_STATS + { + Relation rcopy = result; + Relation test2(Composition(copy(rcopy),copy(rcopy))); + test2.simplify(2,2); + test2.remove_disjunction_with_unknown(); + rcopy.remove_disjunction_with_unknown(); + if (detailedClosureDebug) { + fprintf(DebugFile, "\nResult is:\n"); + rcopy.print_with_subs(DebugFile); + fprintf(DebugFile, "\nResult composed with itself is:\n"); + test2.print_with_subs(DebugFile); + } + if (!Must_Be_Subset(test2,copy(rcopy))) { + fprintf(statsfile,"multi TC result is inexact\n"); + } + else + fprintf(statsfile,"TC result is exact%s\n", (resultInexact || !rcopy.is_exact())?" despite perceived inexactness":""); + } +#endif + +#ifdef TC_STATS + } + int totalTime = clock_diff(); + fprintf(statsfile, "input conj: %d output conj: %d #singe conj closures: %d time: %d\n", + in_conj, copy(result).query_DNF()->length(), + singles, + totalTime/TC_RUNS); +#endif + + if (closure_presburger_debug || detailedClosureDebug) { + if (detailedClosureDebug) { + fprintf(DebugFile, "\nThe transitive closure is :\n"); + result.print_with_subs(DebugFile); + } + fprintf(DebugFile, "\n\n] END Transitive closure\n\n"); + } + return result; +} + + +Relation TransitiveClosure(NOT_CONST Relation &input_r, + int maxExpansion, + NOT_CONST Relation & input_IterationSpace) { + Relation r = consume_and_regurgitate(input_r); + Relation IterationSpace = consume_and_regurgitate(input_IterationSpace); + if (r.is_null()) + return r; + if (r.n_out() == 0) + throw std::invalid_argument("transitive closure does not apply to set"); + if (r.n_inp() != r.n_out()) + throw std::invalid_argument("transitive closure must has the same input and output arity"); + + if (closure_presburger_debug) { + fprintf(DebugFile,"\nComputing Transitive closure of:\n"); + r.print_with_subs(DebugFile); + fprintf(DebugFile,"\nIteration space is:\n"); + IterationSpace.print_with_subs(DebugFile); + } + if (!r.is_upper_bound_satisfiable()) { + if (closure_presburger_debug) + fprintf(DebugFile, "]TC : relation is false\n"); + return r; + } + + Relation UB = DeltasToRelation(ConicHull(Project_Sym(Deltas(copy(r)))), + r.n_inp(),r.n_out()); + if (closure_presburger_debug) { + fprintf(DebugFile,"UB is:\n"); + UB.print_with_subs(DebugFile); + } + + Relation conditions = Restrict_Domain(copy(UB),Domain(copy(r))); + conditions.simplify(); + if (closure_presburger_debug) { + fprintf(DebugFile,"Forward reachable is:\n"); + conditions.print_with_subs(DebugFile); + } + conditions = Composition(Inverse(copy(UB)),conditions); + conditions.simplify(); + if (closure_presburger_debug) { + fprintf(DebugFile,"Backward/forward reachable is:\n"); + conditions.print_with_subs(DebugFile); + } + conditions = Range(conditions); + conditions.simplify(); + // conditions = Approximate(conditions); + // conditions.simplify(); + conditions = VennDiagramForm(conditions); + conditions.simplify(); + + if (closure_presburger_debug) { + fprintf(DebugFile,"Condition regions are:\n"); + conditions.print_with_subs(DebugFile); + } + + if (conditions.is_obvious_tautology()) { + return TransitiveClosure0(r, maxExpansion, IterationSpace); + } + else { + Relation answer = Relation::False(r); + answer.copy_names(r); + answer.setup_names(); + + for (DNF_Iterator c(conditions.query_DNF()); c.live(); c.next()) { + Relation tmp = Relation(conditions, c.curr()); + if (closure_presburger_debug) { + fprintf(DebugFile,"\nComputing Transitive closure:\n"); + fprintf(DebugFile,"\nRegion:\n"); + tmp.prefix_print(DebugFile); + } + + Relation tmp3 = Restrict_Domain(copy(r),tmp); + tmp3.simplify(2,2); + if (closure_presburger_debug) { + fprintf(DebugFile,"\nRelation:\n"); + tmp3.prefix_print(DebugFile); + } + + answer = Union(answer, TransitiveClosure0(tmp3, maxExpansion,copy(IterationSpace))); + } + return answer; + } +} + + +/* ********************************* */ +/* Function check if relation */ +/* belong to d-form or */ +/* uniform relaion class */ +/* ********************************* */ + +Relation is_DForm_or_Uniform(NOT_CONST Relation &r){ + + Relation s = consume_and_regurgitate(r); + Relation Rtmp, Rdelta, delta; + + delta = Deltas(copy(s)); + Rdelta = DeltasToRelation(copy(delta), s.n_inp(), s.n_out()); + Rtmp = DeltasToRelation(Project_Sym(delta), s.n_inp(), s.n_out()); + + Rtmp = Restrict_Domain(Rtmp, Domain(copy(Rdelta))); + Rtmp = Restrict_Range(Rtmp, Range(Rdelta)); + + Rdelta = copy(Rtmp); + + Rtmp = Restrict_Domain(Rtmp, Domain(copy(s))); + Rtmp = Restrict_Range(Rtmp, Range(copy(s))); + + if (Must_Be_Subset( copy(Rtmp), copy(s)) && \ + Must_Be_Subset(copy(s), copy(Rtmp))) { + Rtmp = Relation::Null(); + } + else { + Rtmp = Rdelta = Relation::Null(); + } + + return Rdelta; + } + + + + /* ********************************* */ + /* Get a conjunction for */ + /* a given number from set */ + /* of relations */ + /* ********************************* */ + +Relation getConjunctionNr(NOT_CONST Relation &r, int conjNr) { + + Relation s = consume_and_regurgitate(r); + int i = 1; + + for (DNF_Iterator c(s.query_DNF()); c; c++,i++) { + if ( i == conjNr ) { + return Relation(s, c.curr()); + } + } + + return Relation::False(s.n_inp(), s.n_out()); + + } + + +/* ********************************* */ +/* Get a common region for */ +/* a given set of relations */ +/* ********************************* */ + +Relation getCommonRegion( NOT_CONST Relation &r, const long* relTab, const long relCount) { + + Relation s = consume_and_regurgitate(r); + Relation commonRegion, Rcurr; + long i = 0; + + Rcurr = getConjunctionNr( copy(s), relTab[0]); + commonRegion = Union(Domain(copy(Rcurr)), Range(copy(Rcurr))); + + for( i=1; i < relCount; i++ ){ + Rcurr = getConjunctionNr( copy(s), relTab[i]); + commonRegion = Intersection( commonRegion, Union( Domain(copy(Rcurr)), Range(copy(Rcurr))) ); + } + + return commonRegion; + } + + +/* ********************************* */ +/* Get a set of relations */ +/* ********************************* */ + +Relation getRelationsSet( NOT_CONST Relation &r, const long* relTab, const long relCount) { + + Relation s = consume_and_regurgitate(r); + Relation R = Relation::False(s.n_inp(), s.n_out()); + long i = 0; + + for( i=0; i < relCount; i++ ){ + R = Union( R, getConjunctionNr( copy(s), relTab[i]) ); + } + + return R; + } + + +/* ********************************* */ +/* Get a set of relations */ +/* from a common region */ +/* ********************************* */ + +Relation relationsOnCommonRegion( NOT_CONST Relation &r, NOT_CONST Relation ®ion ) { + + Relation set = consume_and_regurgitate(r); + Relation reg = consume_and_regurgitate(region); + Relation R = Relation::True(set.n_inp(), set.n_out()); + + R = Restrict_Domain(R, copy(reg)); + R.simplify(2,1); + R = Restrict_Range(R, reg); + R.simplify(2,1); + + R = Intersection(R, set); + + return R; + + } + + +Relation compose_N(NOT_CONST Relation &input_r) { + Relation r = consume_and_regurgitate(input_r); + Relation powerR, powerR2; + + r = Union(r, Identity(r.n_inp())); + powerR = copy(r); + + for(;;){ + if (powerR.number_of_conjuncts() > 50) { + powerR = Relation::Null(); + return powerR; + } + + powerR2 = Composition(copy(powerR), copy(r)); + powerR2.simplify(2,1); + + if (Must_Be_Subset( copy(powerR2), copy(powerR))) { + powerR2 = Relation::Null(); + return powerR; + } + + powerR = Relation::Null(); + powerR = copy(powerR2); + powerR2 = Relation::Null(); + } +} + + +/****************************** */ +/* Check exactness of R+ */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/****************************** */ + +bool checkExactness(NOT_CONST Relation &r, NOT_CONST Relation &rplus){ + + +Relation s1 = consume_and_regurgitate(r); +Relation s2 = consume_and_regurgitate(rplus); +Relation R; + +R = Composition(copy(s1), copy(s2)); +R = Union(s1, R); + + if( Must_Be_Subset(copy(s2), copy(R)) && \ + Must_Be_Subset(copy(R), copy(s2))) { + R = Relation::Null(); + s1 = Relation::Null(); + return true; + } + + R = Relation::Null(); + s1 = Relation::Null(); + + return false; + +} + +/************************************** */ +/* Calculate approximation of R* */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/************************************** */ + + +Relation ApproxClosure(NOT_CONST Relation &r) { + + Relation s = consume_and_regurgitate(r); + Relation R = Relation::False(s.n_inp(), s.n_out()); + Relation tc = Identity(s.n_inp()); + Relation Rtmp; + + + for (DNF_Iterator c(s.query_DNF()); c; c++) { + Rtmp = Hull(Project_Sym(Deltas(Relation(s, c.curr()))), false, 1, Relation::Null()); + R = Union(R, TransitiveClosure(DeltasToRelation(Rtmp,s.n_inp(),s.n_out()), 1, Relation::Null())); + } + + for (DNF_Iterator c(R.query_DNF()); c; c++) { + Rtmp = Union(Identity(s.n_inp()), Relation(R, c.curr())); + tc = Composition(tc, Rtmp); + tc = Hull(tc, false, 1, Relation::Null()); + } + + tc = Restrict_Domain(tc,Domain(copy(s))); + tc.simplify(2,1); + tc = Restrict_Range(tc,Range(s)); + tc.simplify(2,1); + tc = Intersection(tc, Relation::Unknown(tc)); + + return tc; +} + + +/************************************** */ +/* Calculate R* on unbounded region */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/************************************** */ + +Relation ClosureOnUnboundedRegion(NOT_CONST Relation &r) { + + Relation s = consume_and_regurgitate(r); + Relation R = Relation::False(s.n_inp(), s.n_out()); + Relation tc = Identity(s.n_inp()); + Relation Rtmp,tcTmp; + + for (DNF_Iterator c(s.query_DNF()); c; c++) { + Rtmp = is_DForm_or_Uniform(Relation(s, c.curr())); + + if (!(Rtmp.is_null())) { + tcTmp = TransitiveClosure(Rtmp, 1, Relation::Null()); + + if (!(tcTmp.is_exact())){ + tcTmp = R = Relation::Null(); + /* fprintf(DebugFile,"\nTC is inexact!"); */ + return tcTmp; + } + } + else { + R = Relation::Null(); + /* fprintf(DebugFile,"\nR is not d-form relation!"); */ + return Relation::Null(); + } + + R = Union(R, tcTmp); + } + + for (DNF_Iterator c(R.query_DNF()); c; c++) { + Rtmp = Union(Identity(s.n_inp()), Relation(R, c.curr())); + tc = Composition(tc, Rtmp); + tc.simplify(2,1); + } + + tc = Difference(tc, Identity(s.n_inp())); + + return tc; + +} + + + + +/******************************* */ +/* Try to select sets of domain */ +/* and range */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/******************************* */ + +Relation SelectRegionForClosure(NOT_CONST Relation &r){ + + Relation s = consume_and_regurgitate(r); + Relation DR = Union(Domain(copy(s)),Range(copy(s))); + Relation region,tc,tcTmp; + + region = SimpleHull(copy(DR)); + region.simplify(2,1); + + tc = ClosureOnUnboundedRegion(copy(s)); + + if (tc.is_null()) { + return tc; + } + + tcTmp = Restrict_Domain(copy(tc),copy(region)); + tcTmp.simplify(2,1); + tcTmp = Restrict_Range(tcTmp,region); + tcTmp.simplify(2,1); + + if (checkExactness(copy(s), copy(tcTmp))) { + s = tc = Relation::Null(); + return tcTmp; + } + + tcTmp = Relation::Null(); + region = Hull(DR,false,1,Relation::Null()); + + tcTmp = Restrict_Domain(copy(tc),copy(region)); + tcTmp.simplify(2,1); + tcTmp = Restrict_Range(tcTmp,region); + tcTmp.simplify(2,1); + + if (checkExactness(copy(s), copy(tcTmp))) { + s = tc = Relation::Null(); + return tcTmp; + } + + tcTmp = Relation::Null(); + + tc = Restrict_Domain(tc,Domain(copy(s))); + tc.simplify(2,1); + tc = Restrict_Range(tc,Domain(copy(s))); + tc.simplify(2,1); + + if (checkExactness(copy(s), copy(tc))) { + s = Relation::Null(); + return tc; + } + + tc = Relation::Null(); + + return ApproxClosure(s); + +} + + + + +/************************************** */ +/* Calculate R* */ +/* */ +/* Tomasz Klimek 05-06-2010 */ +/************************************** */ + +Relation calculateTransitiveClosure(NOT_CONST Relation &r) { + + Relation s = consume_and_regurgitate(r); + Relation tc = Relation::False(s.n_inp(), s.n_out()); + long* relationsSet = NULL; + Relation commonRegion, regionTmp; + Relation inputRelations; + long i,j=-1; + long N,M; + Relation R; + + + commonRegion = SelectRegionForClosure(copy(s)); + + if (commonRegion.is_null()) { + return ApproxClosure(s); + } + + if (commonRegion.is_exact()) { + return commonRegion; + } + + commonRegion = Relation::Null(); + N = M = s.number_of_conjuncts(); + relationsSet = (long*)calloc(N,sizeof(long)); + + if (relationsSet == NULL) { + return Relation::False(s.n_inp(), s.n_out()); + } + + for (; N > 1;) { + for ( i=0; i<N; i++ ) { + if ( i < j ) { + continue; + } + else if ( j == -1 ) { + relationsSet[i] = 1; + } + else if ( i > j ) { + relationsSet[i] = relationsSet[i-1] + 1; + } + else if ( i == j ) { + relationsSet[i] += 1; + } + if ( relationsSet[i] <= M ) { + j = i; + } + else { + j = i - 1; + break; + } + } + + if ( j+1 == N) { + /* fprintf(DebugFile,"\n"); + for(i=0;i<N;i++){ + fprintf(DebugFile," %ld", relationsSet[i]); + } + fprintf(DebugFile,"\n"); */ + + commonRegion = getCommonRegion( copy(s), relationsSet, N); + commonRegion.simplify(2,1); + inputRelations = getRelationsSet( copy(s), relationsSet, N); + inputRelations.simplify(2,1); + + /* ******************* */ + /* Check on rectangle */ + /* ******************* */ + regionTmp = SimpleHull(copy(commonRegion)); + regionTmp.simplify(2,1); + R = relationsOnCommonRegion( copy(inputRelations), regionTmp); + R.simplify(2,1); + regionTmp = SelectRegionForClosure(R); + + if (regionTmp.is_exact()) { + /* fprintf(DebugFile,"\nDescribed on rectangle region\n"); */ + tc = Union( tc, regionTmp ); + } + else { + /* ******************* */ + /* Check on hull */ + /* ******************* */ + + R = Relation::Null(); + regionTmp = Relation::Null(); + regionTmp = Hull(copy(commonRegion),false,1,Relation::Null()); + regionTmp.simplify(2,1); + R = relationsOnCommonRegion( copy(inputRelations), regionTmp); + R.simplify(2,1); + regionTmp = SelectRegionForClosure(R); + + if (regionTmp.is_exact()) { + /* fprintf(DebugFile,"\nDescribed on Hull\n"); */ + tc = Union( tc, regionTmp); + } + else { + /* ********************************** */ + /* Check on sets of domain and range */ + /* ********************************** */ + + R = Relation::Null(); + regionTmp = Relation::Null(); + R = relationsOnCommonRegion( copy(inputRelations), copy(commonRegion) ); + R.simplify(2,1); + regionTmp = SelectRegionForClosure(R); + + if (regionTmp.is_exact()) { + /* fprintf(DebugFile,"\nDescribed on sets of doamin and range\n"); */ + tc = Union( tc, regionTmp ); + } + else { + commonRegion = Relation::Null(); + inputRelations = Relation::Null(); + regionTmp = Relation::Null(); + R = Relation::Null(); + + return ApproxClosure(s); + } + } + } + + commonRegion = Relation::Null(); + inputRelations = Relation::Null(); + + regionTmp = Relation::Null(); + R = Relation::Null(); + } + + if ( j == -1 ) N--; + + } + + R = Relation::Null(); + + for (DNF_Iterator c(s.query_DNF()); c; c++) { + if (!Must_Be_Subset(Relation(s, c.curr()), copy(tc))) { + /* fprintf(DebugFile,"\nIs not a subset\n"); */ + tc = Union( tc, SelectRegionForClosure(Relation(s, c.curr()))); + } + } + + if (!(tc.is_exact())){ + return ApproxClosure(s); + } + + tc = compose_N(tc); + + if (tc.is_null()) { + return ApproxClosure(s); + } + + return tc; + +} + + + + + +} // namespace |