diff options
author | Tuowen Zhao <ztuowen@gmail.com> | 2016-09-18 15:45:13 +0000 |
---|---|---|
committer | Tuowen Zhao <ztuowen@gmail.com> | 2016-09-18 15:45:13 +0000 |
commit | 2fce43d484e4148ae858f410d51dcd9951d34374 (patch) | |
tree | 80c204799cd38349b3bb209d4d37962b11aa6222 /omegalib/omega/src | |
parent | f433eae7a1408cca20f3b72fb4c136d9b62de3b8 (diff) | |
download | chill-2fce43d484e4148ae858f410d51dcd9951d34374.tar.gz chill-2fce43d484e4148ae858f410d51dcd9951d34374.tar.bz2 chill-2fce43d484e4148ae858f410d51dcd9951d34374.zip |
remove include & rename
Diffstat (limited to 'omegalib/omega/src')
38 files changed, 24271 insertions, 0 deletions
diff --git a/omegalib/omega/src/RelBody.cc b/omegalib/omega/src/RelBody.cc new file mode 100644 index 0000000..825b153 --- /dev/null +++ b/omegalib/omega/src/RelBody.cc @@ -0,0 +1,906 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + class Rel_Body, internal Relation representation + + Notes: + + History: + 11/26/09 Remove unecessary mandatary checking for set or relation, + treat them uniformly for easy coding, by Chun Chen +*****************************************************************************/ + +#include <basic/util.h> +#include <omega/RelBody.h> +#include <omega/Relation.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/omega_i.h> +#include <assert.h> + +namespace omega { + +Rel_Body null_rel; +bool Rel_Body::is_null() const { + return(this == &null_rel); +} + + +int Rel_Body::max_ufs_arity() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_ufs_arity_of_set() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + if ((*v)->function_of() == Set_Tuple) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_ufs_arity_of_in() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + if ((*v)->function_of() == Input_Tuple) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_ufs_arity_of_out() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + if ((*v)->function_of() == Output_Tuple) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +int Rel_Body::max_shared_ufs_arity() { + int ma = 0, a; + for (Variable_ID_Iterator v(*global_decls()); v; v++) + for (Variable_ID_Iterator v2(*global_decls()); v2; v2++) + if (*v != *v2 + && (*v)->get_global_var() == (*v2)->get_global_var() + && (*v)->function_of() != (*v2)->function_of()) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + +// +// Input and output variables. +// +void Rel_Body::name_input_var(int nth, Const_String S) { + // assert(1 <= nth && nth <= number_input && (!is_set() || skip_set_checks > 0)); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_input) + throw std::invalid_argument("invalid input var number"); + In_Names[nth] = S; +} + +void Rel_Body::name_output_var(int nth, Const_String S) { + // assert(1<= nth && nth <= number_output && (!is_set() || skip_set_checks > 0)); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_output) + throw std::invalid_argument("invalid output var number"); + Out_Names[nth] = S; +} + +void Rel_Body::name_set_var(int nth, Const_String S) { + if (number_output != 0) + throw std::runtime_error("relation is not a set"); + name_input_var(nth, S); +} + +int Rel_Body::n_inp() const { + // assert(!is_null() && (!is_set()||skip_set_checks>0)); + if (is_null()) + return 0; + else + return number_input; +} + +int Rel_Body::n_out() const { + // assert(!is_null() && (!is_set()||skip_set_checks>0)); + if (is_null()) + return 0; + else + return number_output; +} + +int Rel_Body::n_set() const { + if (number_output != 0) + throw std::runtime_error("relation is not a set"); + return n_inp(); +} + +Variable_ID Rel_Body::input_var(int nth) { + // assert(!is_null()); + // assert(!is_set() || skip_set_checks>0); + // assert(1 <= nth && nth <= number_input); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_input) + throw std::invalid_argument("invalid input var number"); + input_vars[nth]->base_name = In_Names[nth]; + return input_vars[nth]; +} + +Variable_ID Rel_Body::output_var(int nth) { + // assert(!is_null()); + // assert(!is_set() || skip_set_checks>0); + // assert(1<= nth && nth <= number_output); + if (is_null()) + throw std::invalid_argument("empty relation"); + if (nth < 1 || nth > number_output) + throw std::invalid_argument("invalid output var number"); + output_vars[nth]->base_name = Out_Names[nth]; + return output_vars[nth]; +} + +Variable_ID Rel_Body::set_var(int nth) { + if (number_output != 0) + throw std::runtime_error("relation is not a set"); + return input_var(nth); +} + +// +// Add the AND node to the relation. +// Useful for adding restraints. +// +F_And *Rel_Body::and_with_and() { + assert(!is_null()); + if (is_simplified()) + DNF_to_formula(); + relation()->finalized = false; + Formula *f = rm_formula(); + F_And *a = add_and(); + a->add_child(f); + return a; +} + +// +// Add constraint to relation at the upper level. +// +EQ_Handle Rel_Body::and_with_EQ() { + assert(!is_null()); + if (is_simplified()) + DNF_to_formula(); + assert(! is_shared()); // The relation has been split. + relation()->finalized = false; + return find_available_conjunct()->add_EQ(); +} + +EQ_Handle Rel_Body::and_with_EQ(const Constraint_Handle &initial) { + assert(!is_null()); + assert(initial.relation()->is_simplified()); + EQ_Handle H = and_with_EQ(); + copy_constraint(H, initial); + return H; +} + +GEQ_Handle Rel_Body::and_with_GEQ() { + assert(!is_null()); + if (is_simplified()) + DNF_to_formula(); + assert(! is_shared()); // The relation has been split. + relation()->finalized = false; // We are giving out a handle. + // We should evantually implement finalization + // of subtrees, so the existing formula cannot + // be modified. + return find_available_conjunct()->add_GEQ(); +} + +GEQ_Handle Rel_Body::and_with_GEQ(const Constraint_Handle &initial) { + assert(!is_null()); + assert(initial.relation()->is_simplified()); + GEQ_Handle H = and_with_GEQ(); + copy_constraint(H, initial); + return H; +} + + + +Conjunct *Rel_Body::find_available_conjunct() { + Conjunct *c; + assert(!is_null()); + + if (children().empty()) { + c = add_conjunct(); + } + else { + assert(children().length() == 1); + Formula *kid = children().front(); // RelBodies have only one child + c = kid->find_available_conjunct(); + if (c==NULL) { + remove_child(kid); + F_And *a = add_and(); + a->add_child(kid); + c = a->add_conjunct(); + } + } + return c; +} + +void Rel_Body::finalize() { + assert(!is_null()); + if (!is_finalized()) + assert(! is_shared()); // no other pointers into here + finalized = true; + if (! children().empty()) + children().front()->finalize(); // Can have at most one child +} + +// Null Rel_Body +// This is the only rel_body constructor that has ref_count initialized to 1; +// That's because it's used to construct the global Rel_Body "null_rel". +// Unfortunately because we don't know in what order global constructors will +// be called, we could create a global relation with the default relation +// constructor (which would set the null_rel ref count to 1), and *then* +// call Rel_Body::Rel_Body(), which would set it back to 0, leaving a relation +// that points to a rel_body with it's ref_count set to 0! So this is done as +// a special case, in which the ref_count is always 1. +Rel_Body::Rel_Body(): + Formula(0, this), + ref_count(1), + status(under_construction), + number_input(0), number_output(0), + In_Names(0), Out_Names(0), + simplified_DNF(NULL), + r_conjs(0), + finalized(true), + _is_set(false) { +} + + +Rel_Body::Rel_Body(int n_input, int n_output): + Formula(0, this), + ref_count(0), + status(under_construction), + number_input(n_input), number_output(n_output), + In_Names(n_input), Out_Names(n_output), + simplified_DNF(NULL), + r_conjs(0), + finalized(false) { + if (n_output == 0) + _is_set = true; + else + _is_set = false; + if(pres_debug) { + fprintf(DebugFile, "+++ Create Rel_Body::Rel_Body(%d, %d) = 0x%p +++\n", + n_input, n_output, this); + } + int i; + for(i=1; i<=number_input; i++) { + In_Names[i] = Const_String(); + } + for(i=1; i<=number_output; i++) { + Out_Names[i] = Const_String(); + } +} + +// Rel_Body::Rel_Body(Rel_Body *r): +// Formula(0, this), +// ref_count(0), +// status(r->status), +// number_input(r->number_input), number_output(r->number_output), +// In_Names(r->number_input), Out_Names(r->number_output), +// simplified_DNF(NULL), +// r_conjs(r->r_conjs), +// finalized(r->finalized), +// _is_set(r->_is_set) { +// if(pres_debug) { +// fprintf(DebugFile, "+++ Copy Rel_Body::Rel_Body(Rel_Body * 0x%p) = 0x%p +++\n", r, this); +// prefix_print(DebugFile); +// } + +// int i; +// for(i=1;i<=r->number_input;i++) In_Names[i] = r->In_Names[i]; +// for(i=1;i<=r->number_output;i++) Out_Names[i] = r->Out_Names[i]; +// copy_var_decls(Symbolic,r->Symbolic); + +// if(!r->children().empty() && r->simplified_DNF==NULL) { +// Formula *f = r->formula()->copy(this,this); +// f->remap(); +// children().append(f); +// } +// else if(r->children().empty() && r->simplified_DNF!=NULL) { +// simplified_DNF = r->simplified_DNF->copy(this); +// simplified_DNF->remap(); +// } +// else { // copy NULL relation +// } + +// reset_remap_field(r->Symbolic); +// } + +Rel_Body *Rel_Body::clone() { + Rel_Body *b = new Rel_Body(); + + b->ref_count = 0; + b->status = status; + b->number_input = number_input; + b->number_output = number_output; + b->r_conjs = r_conjs; + b->finalized = finalized; + b->_is_set = _is_set; + + b->In_Names = Tuple<Const_String>(number_input); + b->Out_Names = Tuple<Const_String>(number_output); + for(int i = 1; i <= number_input; i++) + b->In_Names[i] = In_Names[i]; + for(int i = 1; i <= number_output; i++) + b->Out_Names[i] = Out_Names[i]; + + copy_var_decls(b->Symbolic, Symbolic); + + if(!children().empty() && simplified_DNF==NULL) { + Formula *f = formula()->copy(b, b); + f->remap(); + b->children().append(f); + } + else if(children().empty() && simplified_DNF!=NULL) { + b->simplified_DNF = simplified_DNF->copy(b); + b->simplified_DNF->remap(); + } + else { // copy NULL relation + } + + reset_remap_field(Symbolic); + return b; +} + + +Rel_Body::Rel_Body(Rel_Body *r, Conjunct *c): + Formula(0, this), + ref_count(0), + status(uncompressed), + number_input(r->number_input), number_output(r->number_output), + In_Names(r->number_input), Out_Names(r->number_output), + r_conjs(0), + finalized(r->finalized), + _is_set(r->_is_set) { + if(pres_debug) { + fprintf(DebugFile, "+++ Copy Rel_Body::Rel_Body(Rel_Body * 0x%p, Conjunct * 0x%p) = 0x%p +++\n",r,c,this); + } + + int i; + for(i=1;i<=r->number_input;i++) In_Names[i] = r->In_Names[i]; + for(i=1;i<=r->number_output;i++) Out_Names[i] = r->Out_Names[i]; + copy_var_decls(Symbolic,r->Symbolic); + + // assert that r has as many variables as c requires, or that c is from r + assert(r == c->relation()); + assert(r->simplified_DNF != NULL); + simplified_DNF = new DNF; + simplified_DNF->add_conjunct(c->copy_conj_diff_relation(this,this)); + single_conjunct()->remap(); + + reset_remap_field(r->Symbolic); +} + +Rel_Body::~Rel_Body() { + if(pres_debug) { + fprintf(DebugFile, "+++ Destroy Rel_Body::~Rel_Body() 0x%p +++\n", this); + } + free_var_decls(Symbolic); + if(simplified_DNF != NULL) { + delete simplified_DNF; + } +} + +// +// Take a relation that has been simplified and convert it +// back to formula form. +// +void Rel_Body::DNF_to_formula() { + assert(!is_null()); + if (simplified_DNF != NULL) { + simplified_DNF->DNF_to_formula(this); + simplified_DNF = NULL; + status = under_construction; + } +} + +bool Rel_Body::can_add_child() { + assert(this != &null_rel); + return n_children() < 1; +} + + + +// ******************** +// Simplify functions +// ******************** + + +extern int s_rdt_constrs; + + +// +// Simplify a given relation. +// Store the resulting DNF in the relation, clean out the formula. +// +void Rel_Body::simplify(int rdt_conjs, int rdt_constrs) { + if(simplified_DNF == NULL) { + finalized = true; + if(children().empty()) { + simplified_DNF = new DNF; + } + else { + if(pres_debug) { + if(DebugFile==NULL) { + DebugFile = fopen("test.out", "w"); + if(DebugFile==NULL) + fprintf(stderr, "Can not open file test.out\n"); + } + } + + assert(children().length()==1); + if(pres_debug) { + fprintf(DebugFile, "=== %p Rel_Body::simplify(%d, %d) Input tree (%d) ===\n", this,rdt_conjs,rdt_constrs,r_conjs); + prefix_print(DebugFile); + } + verify_tree(); + + beautify(); + verify_tree(); + + rearrange(); + verify_tree(); + + beautify(); + verify_tree(); + + s_rdt_constrs = rdt_constrs; + if(pres_debug) { + fprintf(DebugFile, "\n=== In simplify, before DNFize ===\n"); + prefix_print(DebugFile); + } + DNFize(); + if(pres_debug) { + fprintf(DebugFile, "\n=== In simplify, after DNFize ===\n"); + prefix_print(DebugFile); + } + verify_tree(); + + + simplified_DNF->rm_redundant_inexact_conjs(); + verify_tree(); + + if (rdt_conjs > 0 && !simplified_DNF->is_definitely_false() && simplified_DNF->length() > 1) { + simplified_DNF->rm_redundant_conjs(rdt_conjs-1); + verify_tree(); + } + + if(pres_debug) { + fprintf(DebugFile, "\n=== Resulting Relation ===\n"); + prefix_print(DebugFile); + } + } + } + else { + /* Reprocess DNF to get rid of redundant stuff */ + + if (rdt_constrs < 0) return; + simplified_DNF->rm_redundant_inexact_conjs(); + + if (rdt_conjs > r_conjs) { + if(pres_debug) + fprintf(DebugFile,"=== Rel_Body::simplify() redundant CONJUNCTS ===\n"); + simplified_DNF->rm_redundant_conjs(rdt_conjs-1); + } + if (rdt_constrs > 0 ) { + if(pres_debug) + fprintf(DebugFile,"=== Rel_Body::simplify() redundant CONSTR-S ===\n"); + s_rdt_constrs = rdt_constrs; + simplified_DNF->simplify(); + } + } + + r_conjs = rdt_conjs; + + for(DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + D.curr()->set_relation(this); + D.curr()->set_parent(this); + } +} + + +// ****************** +// Query functions +// ****************** + + +// +// Check if relation has a single conjunct formula and return this conjunct. +// +Conjunct *Rel_Body::single_conjunct() { + simplify(); + return simplified_DNF->single_conjunct(); +} + +bool Rel_Body::has_single_conjunct() { + simplify(); + return simplified_DNF->has_single_conjunct(); +} + +// +// Remove and return first conjunct +// +Conjunct *Rel_Body::rm_first_conjunct() { + simplify(); + return simplified_DNF->rm_first_conjunct(); +} + + +void Rel_Body::query_difference(Variable_ID v1, Variable_ID v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed) { + simplify(); + + coef_t _lb, _ub; + int first = 1; + bool _g; + lowerBound = negInfinity; // default values if no DNF's + upperBound = posInfinity; + guaranteed = 0; + + for (DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + (*D)->query_difference(v1, v2, _lb, _ub, _g); + if (first) { + lowerBound = _lb; + upperBound = _ub; + guaranteed = _g; + first = 0; + } + else { + guaranteed = guaranteed && _g; + lowerBound = min(lowerBound, _lb); + upperBound = max(upperBound, _ub); + } + } +} + + +void Rel_Body::query_variable_bounds(Variable_ID v, coef_t &lowerBound, coef_t &upperBound) { + simplify(); + + coef_t _lb, _ub; + int first = 1; + lowerBound = negInfinity; // default values if no DNF's + upperBound = posInfinity; + + for (DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + (*D)->query_variable_bounds(v, _lb, _ub); + if (first) { + lowerBound = _lb; + upperBound = _ub; + first = 0; + } + else { + lowerBound = min(lowerBound, _lb); + upperBound = max(upperBound, _ub); + } + } +} + +coef_t Rel_Body::query_variable_mod(Variable_ID v, coef_t factor) { + simplify(); + + bool first = true; + coef_t result; + + for (DNF_Iterator D(simplified_DNF); D.live(); D.next()) { + coef_t t = (*D)->query_variable_mod(v, factor); + if (t == posInfinity) + return posInfinity; + + if (first) { + result = t; + first = false; + } + else { + if (result != t) + return posInfinity; + } + } + + return result; +} + + + +// +// Simplify formula if needed and return the resulting DNF. +// +DNF* Rel_Body::query_DNF() { + return(query_DNF(false,false)); +} + +DNF* Rel_Body::query_DNF(int rdt_conjs, int rdt_constrs) { + simplify(rdt_conjs, rdt_constrs); + return(simplified_DNF); +} + +// +// Other formula queries. +// + +// Interpret UNKNOWN as true, then check satisfiability +// i.e., check if the formula simplifies to FALSE, since the library +// will never say that if the *known* constraints are unsatisfiable by +// themselves. +bool Rel_Body::is_upper_bound_satisfiable() { + int tmp = s_rdt_constrs; + s_rdt_constrs = -1; + simplify(); + s_rdt_constrs = tmp; + return(!simplified_DNF->is_definitely_false()); +} + +// Interpret UNKNOWN as false, then check satisfiability +// i.e., check if there exist any exact conjuncts in the solution +bool Rel_Body::is_lower_bound_satisfiable() { + int tmp = s_rdt_constrs; + s_rdt_constrs = -1; + simplify(); + s_rdt_constrs = tmp; + for(DNF_Iterator d(simplified_DNF); d; d++) + if((*d)->is_exact()) return true; + return false; +} + +bool Rel_Body::is_satisfiable() { + assert(is_lower_bound_satisfiable() == is_upper_bound_satisfiable()); + return is_upper_bound_satisfiable(); +} + +// Check if we can easily determine if the formula evaluates to true. +bool Rel_Body::is_obvious_tautology() { + int tmp = s_rdt_constrs; + s_rdt_constrs = 0; + simplify(); + s_rdt_constrs = tmp; + return(simplified_DNF->is_definitely_true()); +} + +// Expensive check to determine if the formula evaluates to true. +bool Rel_Body::is_definite_tautology() { + if(is_obvious_tautology()) return true; + Relation l = Lower_Bound(Relation(*this,1)); + return !(Complement(l).is_upper_bound_satisfiable()); +} + +bool Rel_Body::is_unknown() { + simplify(); + return(has_single_conjunct() && single_conjunct()->is_unknown()); +} + +// +// Get accuracy status of the relation +// + +Rel_Unknown_Uses Rel_Body::unknown_uses() { + if (!is_simplified()) + simplify(); + + Rel_Unknown_Uses local_status=0; + int n_conj=0; + + for (DNF_Iterator c(simplified_DNF); c; c++) { + n_conj++; + if ((*c)->is_exact()) + local_status |= no_u; + else if ((*c)->is_unknown()) + local_status |= or_u; + else + local_status |= and_u; + } + + if (n_conj == 0) { + assert(local_status == 0); + local_status = no_u; + } + assert(local_status); +#if ! defined NDEBUG + Rel_Unknown_Uses impossible = (and_u | or_u); + assert( (local_status & impossible) != impossible); +#endif + + return local_status; +} + +void Rel_Body::interpret_unknown_as_false() { + simplify(); + simplified_DNF->remove_inexact_conj(); +} + +void Rel_Body::interpret_unknown_as_true() { + simplify(); + for(DNF_Iterator d(simplified_DNF); d; d++) + (*d)->interpret_unknown_as_true(); +} + + +void Rel_Body::reverse_leading_dir_info() { + if (is_simplified()) { + for (DNF_Iterator c(simplified_DNF); c; c++) + (*c)->reverse_leading_dir_info(); + } + else { + assert(!simplified_DNF); + assert(children().size() == 1); + children().front()->reverse_leading_dir_info(); + } +} + +// +// Rel_Body::DNFize just DNF-izes its child node and calls verify +// + +DNF* Rel_Body::DNFize() { +#if defined(INCLUDE_COMPRESSION) + assert(!this->is_compressed()); +#endif + if (! simplified_DNF) { + simplified_DNF = children().remove_front()->DNFize(); + + int mua = max_shared_ufs_arity(); + if (mua > 0) { + if (pres_debug) { + fprintf(DebugFile, "\n=== In DNFize, before LCDNF ===\n"); + prefix_print(DebugFile); + } + + simplified_DNF->make_level_carried_to(mua); + } + + if(pres_debug) { + fprintf(DebugFile, "\n=== In DNFize, before verify ===\n"); + prefix_print(DebugFile); + } + + simplified_DNF->simplify(); + } + + assert(children().length() == 0); + + return simplified_DNF; +} + +void Rel_Body::make_level_carried_to(int level) { + if (!simplified_DNF) { + DNFize(); + } + + assert(simplified_DNF && children().empty()); + + simplified_DNF->make_level_carried_to(level); +} + +// +// if direction==0, move all conjuncts with >= level leading 0's to return +// else move all conjuncts with level-1 0's followed by +// the appropriate signed difference to returned Relation +// + +Relation Rel_Body::extract_dnf_by_carried_level(int level, int direction) { + if (!simplified_DNF) { + DNFize(); + } + + assert(simplified_DNF && children().empty()); + + simplified_DNF->make_level_carried_to(level); + + Relation extracted(n_inp(), n_out()); + extracted.copy_names(*this); + assert(extracted.rel_body->children().empty()); + assert(extracted.rel_body->simplified_DNF == NULL); + extracted.rel_body->simplified_DNF = new DNF; + extracted.rel_body->Symbolic = Symbolic; + + DNF *remaining = new DNF; + Conjunct *curr; + + for (curr = simplified_DNF->rm_first_conjunct(); + curr; + curr = simplified_DNF->rm_first_conjunct()) { + assert(curr->guaranteed_leading_0s >= level || curr->guaranteed_leading_0s == curr->possible_leading_0s); + assert(curr->possible_leading_0s >= 0); + + curr->assert_leading_info(); + + if ((direction == 0 && curr->guaranteed_leading_0s >= level) || + (curr->guaranteed_leading_0s == level-1 && + curr->leading_dir_valid_and_known() && + curr->leading_dir * direction > 0)) { + extracted.rel_body->simplified_DNF->add_conjunct(curr); + } + else { + remaining->add_conjunct(curr); + } + } + delete simplified_DNF; + simplified_DNF = remaining; + +#if ! defined NDEBUG + for (DNF_Iterator rc(simplified_DNF); rc; rc++) + (*rc)->assert_leading_info(); + + for (DNF_Iterator ec(extracted.rel_body->simplified_DNF); ec; ec++) + (*ec)->assert_leading_info(); +#endif + + finalize(); + extracted.finalize(); + return extracted; +} + +//Compress/uncompress functions + +bool Rel_Body::is_compressed() { +#if defined(INCLUDE_COMPRESSION) + if(is_simplified()) { + for(DNF_Iterator p(simplified_DNF); p.live(); p.next()) { + if(p.curr()->is_compressed()) + return true; + } + } + return false; +#else + return true; // This allows is_compressed assertions to work +#endif +} + +void Rel_Body::compress() { +#if !defined(INCLUDE_COMPRESSION) + return; +#else + if (status == compressed) + return; + if (pres_debug) + fprintf(DebugFile,">>> Compressing relation %p\n",this); + simplify(); + for(DNF_Iterator p(simplified_DNF); p.live(); p.next()) { + p.curr()->compress(); + status = compressed; + } +#endif +} + +void Rel_Body::uncompress() { +#if !defined(INCLUDE_COMPRESSION) + return; +#else + if (pres_debug) + fprintf(DebugFile,"<<< Uncompressing relation %p\n",this); + assert(is_simplified()); + for(DNF_Iterator p(simplified_DNF); p.live(); p.next()) { + p.curr()->uncompress(); + status = uncompressed; + } +#endif +} + +} diff --git a/omegalib/omega/src/RelVar.cc b/omegalib/omega/src/RelVar.cc new file mode 100644 index 0000000..d9b977c --- /dev/null +++ b/omegalib/omega/src/RelVar.cc @@ -0,0 +1,71 @@ +#include <omega/RelBody.h> +#include <omega/omega_i.h> + +namespace omega { + +Variable_ID Rel_Body::get_local(const Variable_ID v) { + Global_Var_ID g; + if (v->kind() == Global_Var) { + g = v->get_global_var(); + if (g->arity()) return get_local(g,v->function_of()); + return get_local(g); + } + if (is_set()) return set_var(v->get_position()); + if (v->kind() == Input_Var) return input_var(v->get_position()); + if (v->kind() == Output_Var) return output_var(v->get_position()); + assert(0 && "Can only get local for variable with global scope"); + exit(1); + return 0; +} + +// +// Find or declare global variable. +// If the VarID does not exist, it is created. Otherwise it's returned. +// Note that this version now works only for 0-ary functions. +// +Variable_ID Rel_Body::get_local(const Global_Var_ID G) { + assert(G->arity() == 0); + for(Variable_Iterator i(Symbolic); i; i++) + if ((*i)->get_global_var() == G) + return (*i); + + Variable_ID v = G->get_local(); + Symbolic.append(v); + return v; +} + + +Variable_ID Rel_Body::get_local(const Global_Var_ID G, Argument_Tuple of) { + assert(G->arity() == 0 || of == Input_Tuple || of == Output_Tuple); + + for(Variable_Iterator i = Symbolic; i; i++) + if ((*i)->get_global_var() == G && (G->arity() == 0 || + of == (*i)->function_of())) + return (*i); + + Variable_ID V = G->get_local(of); + Symbolic.append(V); + return V; +} + + +bool Rel_Body::has_local(const Global_Var_ID G) { + assert(G->arity() == 0); + for(Variable_Iterator i = Symbolic; i; i++) + if ((*i)->get_global_var() == G) + return true; + return false; +} + + +bool Rel_Body::has_local(const Global_Var_ID G, Argument_Tuple of) { + assert(G->arity() == 0 || of == Input_Tuple || of == Output_Tuple); + + for(Variable_Iterator i = Symbolic; i; i++) + if ((*i)->get_global_var() == G && (G->arity() == 0 || + of == (*i)->function_of())) + return true; + return false; +} + +} // namespace diff --git a/omegalib/omega/src/Relation.cc b/omegalib/omega/src/Relation.cc new file mode 100644 index 0000000..1cca43a --- /dev/null +++ b/omegalib/omega/src/Relation.cc @@ -0,0 +1,279 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + class Relation + + Notes: + + History: +*****************************************************************************/ + +#include <omega/Relation.h> +#include <omega/Relations.h> +#include <omega/pres_dnf.h> +#include <omega/pres_conj.h> +#include <omega/Rel_map.h> +#include <omega/omega_i.h> +#include <omega/omega_core/debugging.h> + +namespace omega { + +// copy function for Relation, will be removed in the future +// in favor of correct C++ copy constructor and const paramater passing +Relation copy(const Relation &t) { + Relation r = t; + return r; +} + +// +// Create null relation. +// +Relation::Relation() : rel_body(&null_rel) { + rel_body->ref_count = 1; +} + +Relation Relation::Null() { + return Relation(); +} + +bool Relation::is_null() const { + return(rel_body == &null_rel); +} + + +// +// Create a relation. Its will be built later. +// +Relation::Relation(int n_input, int n_output) { + rel_body = new Rel_Body(n_input,n_output); + rel_body->ref_count = 1; +} + +Relation::Relation(Rel_Body &r, int) { + rel_body = &r; + r.ref_count++; +} + +Relation Relation::Empty(const Relation &R) { + if (R.is_set()) return Relation(R.n_set()); + else return Relation(R.n_inp(),R.n_out()); +} + +// +// Create relation which is FALSE or TRUE. +// + +Relation Relation::True(const Relation &R) { + if (R.is_set()) return True(R.n_set()); + else return True(R.n_inp(),R.n_out()); +} + +Relation Relation::False(const Relation &R) { + if (R.is_set()) return False(R.n_set()); + else return False(R.n_inp(),R.n_out()); +} + +Relation Relation::Unknown(const Relation &R) { + if (R.is_set()) return Unknown(R.n_set()); + else return Unknown(R.n_inp(), R.n_out()); +} + + +Relation Relation::True(int setvars) { + Relation R(setvars); + R.add_and(); + R.finalize(); + return R; +} + +Relation Relation::True (int in, int out) { + Relation R(in,out); + R.add_and(); + R.finalize(); + return R; +} + +Relation Relation::False (int setvars) { + Relation R(setvars); + R.add_or(); + R.finalize(); + return R; +} + +Relation Relation::False (int in, int out) { + Relation R(in,out); + R.add_or(); + R.finalize(); + return R; +} + + +Relation Relation::Unknown (int setvars) { + Relation R(setvars); + R.add_and(); + R.finalize(); + R.simplify(); + Conjunct * c= R.single_conjunct(); + c->make_inexact(); + return R; +} + +Relation Relation::Unknown (int in, int out) { + Relation R(in,out); + R.add_and(); + R.finalize(); + R.simplify(); + Conjunct * c= R.single_conjunct(); + c->make_inexact(); + return R; +} + + +// +// Copy a relation. +// +Relation::Relation(const Relation &r) { +#if defined(INCLUDE_COMPRESSION) + assert(!r.is_compressed()); +#endif + if (r.is_finalized()) { + rel_body = r.rel_body; + rel_body->ref_count++; + } else { + assert(! r.rel_body->is_shared()); + // rel_body = new Rel_Body(r.rel_body); + rel_body = r.rel_body->clone(); + rel_body->ref_count = 1; + } +} + +// +// Copy relation r and replace formula in it with conjunct c. +// Wayne (TM) function. +// +Relation::Relation(const Relation &r, Conjunct *c) { + rel_body = new Rel_Body(r.rel_body, c); + rel_body->ref_count = 1; +} + + +// +// Assign a relation r to this relation. +// +Relation &Relation::operator=(const Relation &r) { +#if defined(INCLUDE_COMPRESSION) + assert (!r.is_compressed()); +#endif + + /* === Destroy this === */ + assert(rel_body->ref_count >= 1); + if(rel_body!=&null_rel && --(rel_body->ref_count)==0) { + delete rel_body; + } + + /* === Copy r to this === */ + if (r.is_finalized()) { + rel_body = r.rel_body; + rel_body->ref_count++; + } else { + assert(! r.rel_body->is_shared()); + // rel_body = new Rel_Body(r.rel_body); + rel_body = r.rel_body->clone(); + rel_body->ref_count = 1; + } + return *this; +} + +void Relation::copy_names(Rel_Body &r) { + int t; + for(t = 1; t <= r.n_inp(); t++) + name_input_var(t,r.input_var(t)->base_name); + for(t = 1; t <= r.n_out(); t++) + name_output_var(t,r.output_var(t)->base_name); +} + + +// Like makeSet (see Relations.c), but won't invert the relation -- +// fails if it has output instead of input variables. Called in Relation +// functions just after a MapRel, so that we know there are no outputs anyway. + +void Relation::markAsSet() { + assert(!is_null()); + assert(is_set() || (n_inp() >= 0 && n_out() == 0)); + if (!is_set()) split(); // split if we'll modify this + rel_body->_is_set = true; + invalidate_leading_info(); +} + +void Relation::markAsRelation() { + assert(!is_null()); + if (is_set()) split(); // split if we'll modify this + rel_body->_is_set = false; +} + + +Relation::~Relation() { + assert(rel_body->ref_count >= 1); + assert(this->is_null() == (rel_body == &null_rel)); + if(rel_body!=&null_rel && --(rel_body->ref_count)==0) { + if (rel_body == &null_rel) abort(); + delete rel_body; + } +} + + + +// +// One of the representatives using the body wants to be changed. +// Create a separate body for this rep not to damage other reps. +// Return address of the body. Old rep point to new body. +// +Rel_Body *Relation::split() { + assert(rel_body != &null_rel && "Error: Attempt to modify a null relation"); + assert (rel_body->ref_count >= 1); + if(!(rel_body==&null_rel || rel_body->ref_count==1)) { + if(pres_debug) { + fprintf(DebugFile, "+++ SPLIT relation +++\n"); + } + // Rel_Body *new_body = new Rel_Body(rel_body); + Rel_Body *new_body = rel_body->clone(); + new_body->ref_count = 1; + rel_body->ref_count--; + rel_body = new_body; + if(pres_debug>=2) { + fprintf(DebugFile, " copying 0x%p to give 0x%p\n", this, rel_body); + } + } + return (rel_body); +} + + +void Relation::dimensions(int & ndim_all, int &ndim_domain) { + ndim_all = ndim_domain = 0; + int a,d; + simplify(2,2); + for (DNF_Iterator s(query_DNF()); s.live(); s.next()) { + s.curr()->calculate_dimensions(*this, a, d); + if (a > ndim_all) ndim_all = a; + if (d > ndim_domain) ndim_domain = d; + } +} + +// Make a set: assert that it had only input or output variables, make it +// it have only input, set a flag. Called from domain, range, and difference, +// as well as functions that require a set as input. +void Relation::makeSet() { + assert(!is_null()); + // Assert that it is a set... + assert((n_inp() == 0 && n_out() >= 0) || (n_inp() >= 0 && n_out() == 0)); + + if ((n_inp() == 0 && n_out() != 0) || !is_set()) split(); // split if we'll modify this + if (n_inp() == 0 && n_out() != 0) //Inverse the relation + Inverse(*this); // Modifies "this"; also returns this but we ignore it + rel_body->_is_set = true; +} + +} // namespace diff --git a/omegalib/omega/src/Relations.cc b/omegalib/omega/src/Relations.cc new file mode 100644 index 0000000..d7dbe86 --- /dev/null +++ b/omegalib/omega/src/Relations.cc @@ -0,0 +1,2882 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Integer set and relation operations. + + Notes: + + History: + 04/22/09 merge_rels, Chun Chen +*****************************************************************************/ + +#include <omega/Relation.h> +#include <omega/Rel_map.h> +#include <omega/pres_tree.h> +#include <omega/pres_dnf.h> +#include <omega/pres_conj.h> +#include <omega/hull.h> +#include <basic/Tuple.h> +#include <basic/Map.h> +#include <basic/util.h> +#include <omega/omega_i.h> +#if defined STUDY_EVACUATIONS +#include <omega/evac.h> +#endif +#include <assert.h> + +namespace omega { + +#define CHECK_MAYBE_SUBSET 1 + +int relation_debug=0; + +namespace { + int leave_pufs_untouched = 0; + Variable_ID_Tuple exists_ids; + List<int> exists_numbers; + F_And * and_below_exists; +} + +/* The following allows us to avoid warnings about passing + temporaries as non-const references. This is useful but + has suddenly become illegal. */ + +Relation consume_and_regurgitate(NOT_CONST Relation &R) { + if(!R.is_null()) + ((Relation &) R).finalize(); + Relation S = (Relation &) R; + (Relation &) R = Relation::Null(); + return S; +} + + +// +// r1 Union r2. +// align the input tuples (if any) for F and G +// align the output tuples (if any) for F and G +// match named variables in F and G +// formula is f | g +// +Relation Union(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r2; + else if (r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + // assert(!r1.is_null() && !r2.is_null()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + return MapAndCombineRel2(r1, r2, Mapping::Identity(in, out), + Mapping::Identity(in,out), Comb_Or); +} + + +// +// F intersection G +// align the input tuples (if any) for F and G +// align the output tuples (if any) for F and G +// match named variables in F and G +// formula is f & g +// +Relation Intersection(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r2; + else if (r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + // assert(!r1.is_null() && !r2.is_null()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + return MapAndCombineRel2(r1, r2, Mapping::Identity(in,out), + Mapping::Identity(in,out), Comb_And); +} + + +// +// F \ G (the relation F restricted to domain G) +// align the input tuples for F and G +// match named variables in F and G +// formula is f & g +// +Relation Restrict_Domain(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r1; + else if (r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_set()) + throw std::invalid_argument("relation arity does not match"); + + // assert(!r1.is_null() && !r2.is_null()); + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_set()); + // assert(r2.is_set()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + int i; + Mapping m2(r2.n_set()); + for(i=1; i<=r2.n_set(); i++) m2.set_map_set(i, Input_Var,i); + + // skip_set_checks++; + assert(r2.query_guaranteed_leading_0s() == -1 && + r2.query_possible_leading_0s() == -1); + // skip_set_checks--; + + Relation result = MapAndCombineRel2(r1, r2, Mapping::Identity(in,out), + m2, Comb_And); + // FERD -- update leading 0's - the may close up? + //result.invalidate_leading_info(); // could do better + return result; +} + +// +// +// F / G (the relation F restricted to range G) +// align the output tuples for F and G +// match named variables in F and G +// formula is f & g +// +Relation Restrict_Range(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null()) + return r1; + else if (r2.is_null()) + return r1; + if (r1.n_out() != r2.n_set()) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(r1.n_out() == r2.n_set()); + // assert(r2.is_set()); + // assert(!r1.is_null() && !r2.is_null()); + int in = r1.n_inp(), out = r1.n_out(); + // skip_set_checks--; + + int i; + Mapping m2(r2.n_set()); + for(i=1; i<=r2.n_set(); i++) m2.set_map_set(i, Output_Var,i); + + // skip_set_checks++; + assert(r2.query_guaranteed_leading_0s() == -1 && + r2.query_possible_leading_0s() == -1); + // skip_set_checks--; + + Relation result = MapAndCombineRel2(r1, r2, Mapping::Identity(in, out), + m2, Comb_And); + // FERD -- update leading 0's - the may close up? + // result.invalidate_leading_info(); // could do better + return result; +} + + +// +// Add input variable to relation. +// +Relation Extend_Domain(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend domain on null relation"); + + // assert(!R.is_null() && (skip_set_checks || !R.is_set())); + // assert(!R.is_null()); + Rel_Body *r = R.split(); + r->In_Names.append(Const_String()); + r->number_input++; + assert(!r->is_null()); + + if (r->number_input <= r->number_output) + R.invalidate_leading_info(r->number_input); + + return R; +} + +// +// Add more input variables to relation. +// +Relation Extend_Domain(NOT_CONST Relation &S, int more) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend domain on null relation"); + + // assert(!R.is_null()); + R.split(); + for (int i=1; i<=more; i++) R = Extend_Domain(R); + return R; +} + + +// +// Add output variable to relation. +// +Relation Extend_Range(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend range on null relation"); + + // assert(!R.is_null() && !R.is_set()); + // assert(!R.is_null()); + Rel_Body *r = R.split(); + r->Out_Names.append(Const_String()); + r->number_output++; + assert(!r->is_null()); + + if (r->number_output <= r->number_input) + R.invalidate_leading_info(r->number_output); + + return R; +} + +// +// Add more output variables to relation. +// +Relation Extend_Range(NOT_CONST Relation &S, int more) { + Relation R = consume_and_regurgitate(S); + + // assert(!R.is_null()); + R.split(); + for (int i=1; i<=more; i++) R = Extend_Range(R); + return R; +} + + +// +// Add set variable to set. +// +Relation Extend_Set(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + throw std::invalid_argument("cannot extend set on null relation"); + if (R.n_out() > 0) + throw std::invalid_argument("relation must be a set"); + + // assert(!R.is_null() && R.is_set()); + Rel_Body *r = R.split(); + r->In_Names.append(Const_String()); + r->number_input++; + assert(!r->is_null()); + return R; +} + +// +// Add more variables to set +// +Relation Extend_Set(NOT_CONST Relation &S, int more) { + Relation R = consume_and_regurgitate(S); + R.split(); + for (int i=1; i<=more; i++) R = Extend_Set(R); + return R; +} + + + +// +// Domain and Range. +// Make output (input) variables wildcards and simplify. +// Move all UFS's to have have the remaining tuple as an argument, +// and maprel will move them to the set tuple +// RESET all leading 0's +// +Relation Domain(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + // assert(!S.is_null()); + // assert(!r.is_set()); + // skip_set_checks++; + int i; + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Set_Var,i); + for(i=1; i<=r.n_out(); i++) m1.set_map_out(i, Exists_Var,i); + // skip_set_checks--; + + int a = r.max_ufs_arity_of_out(); + if (a > 0) { + // UFS's must evacuate from the output tuple + Variable_ID_Tuple remapped; + + r.simplify(); + DNF *d = r.split()->DNFize(); + d->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= "a" leading 0s + // What a gross way to do this. Ferd + + for (DNF_Iterator conj(d); conj; conj++) { +#if defined STUDY_EVACUATIONS + study_evacuation(*conj, out_to_in, a); +#endif + + int cL0 = (*conj)->guaranteed_leading_0s; + + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Output_Tuple) { + if (cL0 >= f->arity()) { + (*func)->remap = r.get_local(f, Input_Tuple); + } + else { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + } + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + + (*conj)->guaranteed_leading_0s = (*conj)->possible_leading_0s = -1; + (*conj)->leading_dir = 0; + } + } + + MapRel1(r, m1, Comb_Id); // this invalidates leading0s + assert(r.is_set() || m1.n_in() == 0); // MapRel can't tell to make a set + r.markAsSet(); // if there were no inputs. + + // skip_set_checks++; + assert(r.query_guaranteed_leading_0s() == -1 && r.query_possible_leading_0s() == -1); + // skip_set_checks--; + + return r; +} + + +Relation Range(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + //assert(!r.is_null()); + // skip_set_checks++; + + int i; + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Exists_Var,i); + for(i=1; i<=r.n_out(); i++) m1.set_map_out(i, Set_Var,i); + // skip_set_checks--; + + int a = r.max_ufs_arity_of_in(); + if (a > 0) { + // UFS's must evacuate from the input tuple + Variable_ID_Tuple remapped; + + r.simplify(); + DNF *d = r.split()->DNFize(); + d->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= "a" leading 0s + // What a gross way to do this. Ferd + + for (DNF_Iterator conj(d); conj; conj++) { +#if defined STUDY_EVACUATIONS + study_evacuation(*conj, in_to_out, a); +#endif + + int cL0 = (*conj)->guaranteed_leading_0s; + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Input_Tuple) { + if (cL0 >= f->arity()) { + (*func)->remap = r.get_local(f, Output_Tuple); + } + else { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + } + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + + (*conj)->guaranteed_leading_0s = (*conj)->possible_leading_0s = -1; + (*conj)->leading_dir = 0; + } + } + + MapRel1(r, m1, Comb_Id); // this invalidates leading0s + assert(r.is_set() || m1.n_out() == 0); // MapRel can't tell to make a set + r.markAsSet(); // if there were no outputs. + + // skip_set_checks++; + assert(r.query_guaranteed_leading_0s() == -1 && r.query_possible_leading_0s() == -1); + // skip_set_checks--; + + return r; +} + + +// +// Cross Product. Give two sets, A and B, create a relation whose +// domain is A and whose range is B. +// +Relation Cross_Product(NOT_CONST Relation &input_A, + NOT_CONST Relation &input_B) { + Relation A = consume_and_regurgitate(input_A); + Relation B = consume_and_regurgitate(input_B); + if (A.is_null() || B.is_null()) + throw std::invalid_argument("null relation"); + if (!A.is_set() || !B.is_set()) + throw std::invalid_argument("cross product must be on two set"); + + // assert(A.is_set()); + // assert(B.is_set()); + + // skip_set_checks++; + assert(A.query_guaranteed_leading_0s() == -1 && + A.query_possible_leading_0s() == -1); + assert(B.query_guaranteed_leading_0s() == -1 && + B.query_possible_leading_0s() == -1); + // skip_set_checks--; + + Mapping mA(A.n_set()); + Mapping mB(B.n_set()); + int i; + for(i = 1; i <= B.n_set(); i++) mB.set_map_set(i, Output_Var,i); + for(i = 1; i <= A.n_set(); i++) mA.set_map_set(i, Input_Var,i); + return MapAndCombineRel2(A, B, mA, mB, Comb_And); +} + + +// +// inverse F +// reverse the input and output tuples +// +Relation Inverse(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + // assert(!r.is_null()); + // assert(!r.is_set()); + int i; + + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Output_Var,i); + for(i=1; i<=r.n_out(); i++) m1.set_map_out(i, Input_Var,i); + + MapRel1(r, m1, Comb_Id, -1, -1, false); + + r.reverse_leading_dir_info(); + + return r; +} + +Relation After(NOT_CONST Relation &input_S, + int carried_by, int new_output,int dir) { + Relation S = consume_and_regurgitate(input_S); + assert(!S.is_null()); + assert(!S.is_set()); + int i; + Relation r(*S.split(),42); + + int a = r.max_ufs_arity_of_out(); + int preserved_positions = min(carried_by-1,new_output); + if (a >= preserved_positions) { + // UFS's must evacuate from the output tuple + Variable_ID_Tuple remapped; + + r.simplify(); + DNF *d = r.split()->DNFize(); + d->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= "a" leading 0s + // What a gross way to do this. Ferd + + for (DNF_Iterator conj(d); conj; conj++) { + int cL0 = (*conj)->guaranteed_leading_0s; + + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > preserved_positions + && (*func)->function_of()==Output_Tuple) { + if (cL0 >= f->arity()) { + (*func)->remap = r.get_local(f, Input_Tuple); + } + else { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + } + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + + (*conj)->guaranteed_leading_0s = + (*conj)->possible_leading_0s = -1; + (*conj)->leading_dir = 0; + } + } + + Mapping m1(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m1.set_map_in (i, Input_Var,i); + if (carried_by > new_output) { + int preserve = min(new_output,r.n_out()); + for(i=1; i<=preserve; i++) m1.set_map_out(i, Output_Var,i); + for(i=preserve+1; i<=r.n_out(); i++) m1.set_map_out(i, Exists_Var,-1); + MapRel1(r, m1, Comb_Id, -1, -1, true); + if (new_output > preserve) + r = Extend_Range(r,new_output-r.n_out()); + return r; + } + + for(i=1; i<carried_by; i++) m1.set_map_out(i, Output_Var,i); + m1.set_map_out(carried_by, Exists_Var,1); + for(i=carried_by+1; i<=r.n_out(); i++) m1.set_map_out(i, Exists_Var,-1); + + MapRel1(r, m1, Comb_Id, -1, -1, true,false); + + Rel_Body *body = r.split(); + body->Out_Names.append(Const_String()); + body->number_output++; + assert(body->n_out() <= input_vars.size()); + + + GEQ_Handle h = and_below_exists->add_GEQ(0); + assert(carried_by < 128); + h.update_coef(exists_ids[1],-dir); + h.update_coef(r.output_var(carried_by),dir); + h.update_const(-1); + h.finalize(); + r.finalize(); + if (new_output > r.n_out()) + r = Extend_Range(r,new_output-r.n_out()); + return r; +} + +// +// Identity. +// +Relation Identity(int n_inp) { + Relation rr(n_inp, n_inp); + F_And *f = rr.add_and(); + for(int i=1; i<=n_inp; i++) { + EQ_Handle e = f->add_EQ(); + e.update_coef(rr.input_var(i), -1); + e.update_coef(rr.output_var(i), 1); + e.finalize(); + } + rr.finalize(); + assert(!rr.is_null()); + return rr; +} + +Relation Identity(NOT_CONST Relation &input_r) { + Relation r = consume_and_regurgitate(input_r); + return Restrict_Domain(Identity(r.n_set()),r); +} + +// +// Deltas(F) +// Return a set such that the ith variable is old Out_i - In_i +// Delta variables are created as input variables. +// Then input and output variables are projected out. +// +Relation Deltas(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + // skip_set_checks++; + assert(R.n_inp()==R.n_out()); + int in = R.n_inp(); + // skip_set_checks--; + return Deltas(R,in); +} + +Relation Deltas(NOT_CONST Relation &S, int eq_no) { + Relation R = consume_and_regurgitate(S); + // skip_set_checks++; + assert(!R.is_null()); + assert(eq_no<=R.n_inp()); + assert(eq_no<=R.n_out()); + // R.split(); + + int no_inp = R.n_inp(); + int no_out = R.n_out(); + + if(relation_debug) { + fprintf(DebugFile,"Computing Deltas:\n"); + R.prefix_print(DebugFile); + } + int a = R.max_ufs_arity(); + if (a > 0) { + Variable_ID_Tuple remapped; + + // UFS's must evacuate from all tuples - we need to go to DNF + // to enumerate the variables, I think... + R.simplify(); + if(relation_debug) { + fprintf(DebugFile,"Relation simplified:\n"); + R.prefix_print(DebugFile); + } + DNF *d = R.split()->DNFize(); + + for (DNF_Iterator conj(d); conj; conj++) { + for (Variable_ID_Iterator func((*conj)->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0) { + (*func)->remap = (*conj)->declare(); + (*conj)->make_inexact(); + remapped.append(*func); + } + } + (*conj)->remap(); + reset_remap_field(remapped); + remapped.clear(); + } + } + + R = Extend_Domain(R, eq_no); // add eq_no Delta vars + Mapping M(no_inp+eq_no, no_out); + int i; + for(i=1; i<=eq_no; i++) { // Set up Deltas equalities + EQ_Handle E = R.and_with_EQ(); + /* delta_i - w_i + r_i = 0 */ + E.update_coef(R.input_var(i), 1); + E.update_coef(R.output_var(i), -1); + E.update_coef(R.input_var(no_inp+i), 1); + E.finalize(); + M.set_map(Input_Var, no_inp+i, Set_Var, i); // Result will be a set + } + for(i=1; i<=no_inp; i++) { // project out input variables + M.set_map(Input_Var, i, Exists_Var, i); + } + for(i=1; i<=no_out; i++) { // project out output variables + M.set_map(Output_Var, i, Exists_Var, no_inp+i); + } + MapRel1(R, M, Comb_Id, eq_no, 0); + + if(relation_debug) { + fprintf(DebugFile,"Computing deltas:\n"); + R.prefix_print(DebugFile); + }; + R.finalize(); + assert(R.is_set()); // Should be since we map things to Set_Var + assert(R.n_set() == eq_no); + // skip_set_checks--; + return R; +} + + + + +Relation DeltasToRelation(NOT_CONST Relation &D, int n_inputs, int n_outputs) { + Relation R = consume_and_regurgitate(D); + + // skip_set_checks++; + assert(!R.is_null()); + R.markAsRelation(); + int common = R.n_inp(); + assert(common <= n_inputs); + assert(common <= n_outputs); + R.split(); + + if (R.max_ufs_arity() > 0) { + assert(R.max_ufs_arity() == 0 && + "'Deltas' not ready for UFS yet"); // FERD + fprintf(stderr, "'Deltas' not ready for UFS yet"); + exit(1); + } + + R = Extend_Domain(R, n_inputs); + R = Extend_Range(R, n_outputs); + Mapping M(common+n_inputs, n_outputs); + int i; + for(i=1; i<=common; i++) { // Set up Deltas equalities + EQ_Handle E = R.and_with_EQ(); + /* delta_i - w_i + r_i = 0 */ + E.update_coef(R.input_var(i), 1); + E.update_coef(R.output_var(i), -1); + E.update_coef(R.input_var(common+i), 1); + E.finalize(); + M.set_map(Input_Var, i, Exists_Var, i); // Result will be a set + } + for(i=1; i<=n_inputs; i++) { // project out input variables + M.set_map(Input_Var, common+i, Input_Var, i); + } + for(i=1; i<=n_outputs; i++) { // project out output variables + M.set_map(Output_Var, i, Output_Var, i); + } + MapRel1(R, M, Comb_Id, n_inputs, n_outputs); + + if(relation_debug) { + fprintf(DebugFile,"Computed DeltasToRelation:\n"); + R.prefix_print(DebugFile); + } + R.finalize(); + assert(!R.is_set()); + // skip_set_checks--; + return R; +} + + + +Relation Join(NOT_CONST Relation &G, NOT_CONST Relation &F) { + return Composition(F, G); +} + +bool prepare_relations_for_composition(Relation &r1,Relation &r2) { + assert(!r2.is_null() && !r1.is_null()); + + if(r2.is_set()) { + int a1 = r1.max_ufs_arity_of_in(), a2 = r2.max_ufs_arity_of_set(); + + if (a1 == 0 && a2 == 0) + return true; + else { + assert(0 && "Can't compose relation and set with function symbols"); + fprintf(stderr, "Can't compose relation and set with function symbols"); + exit(1); + return false; // make compiler shut up + } + } + + assert(r2.n_out() == r1.n_inp()); + + int zeros = max(r1.query_guaranteed_leading_0s(), + r2.query_guaranteed_leading_0s()); + return (zeros >= r1.max_ufs_arity_of_in() + && zeros >= r2.max_ufs_arity_of_out()); +} + +// +// Composition(F, G) = F o G, where F o G (x) = F(G(x)) +// That is, if F = { [i] -> [j] : ... } +// and G = { [x] -> [y] : ... } +// then Composition(F, G) = { [x] -> [j] : ... } +// +// align the output tuple for G and the input tuple for F, +// these become existensially quantified variables +// use the output tuple from F and the input tuple from G for the result +// match named variables in G and F +// formula is g & f +// +// If there are function symbols of arity > 0, we call special case +// code to handle them. This is not set up for the r2.is_set case yet. +// + +Relation Composition(NOT_CONST Relation &input_r1, NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + assert(!r2.is_null() && !r1.is_null()); + + if(r2.is_set()) { + int a1 = r1.max_ufs_arity_of_in(), a2 = r2.max_ufs_arity_of_set(); + if (r2.n_set() != r1.n_inp()) { + fprintf(stderr,"Illegal composition/application, arities don't match\n"); + fprintf(stderr,"Trying to compute r1(r2)\n"); + fprintf(stderr,"arity of r2 must match input arity of r1\n"); + fprintf(stderr,"r1: "); + r1.print_with_subs(stderr); + fprintf(stderr,"r2: "); + r2.print_with_subs(stderr); + fprintf(stderr,"\n"); + assert(r2.n_set() == r1.n_inp()); + exit(1); + } + // skip_set_checks++; + int i; + if (a1 == 0 && a2 == 0) { + int x = r1.n_out(); + Mapping m1(r1.n_inp(), r1.n_out()); + for(i=1; i<=r1.n_out(); i++) m1.set_map_out(i, Set_Var,i); + for(i=1; i<=r1.n_inp(); i++) m1.set_map_in (i, Exists_Var,i); + Mapping m2(r2.n_set()); + for(i=1; i<=r2.n_set(); i++) m2.set_map_set(i, Exists_Var,i); + Relation R3 = MapAndCombineRel2(r2, r1, m2, m1, Comb_And); + // skip_set_checks--; + if (x == 0) + R3.markAsSet(); + return R3; + } + else { + assert(0 && + "Can't compose relation and set with function symbols"); + fprintf(stderr, + "Can't compose relation and set with function symbols"); + exit(1); + return Identity(0); // make compiler shut up + } + } + + if (r2.n_out() != r1.n_inp()) { + fprintf(stderr,"Illegal composition, arities don't match\n"); + fprintf(stderr,"Trying to compute r1 compose r2\n"); + fprintf(stderr,"Output arity of r2 must match input arity of r1\n"); + fprintf(stderr,"r1: "); + r1.print_with_subs(stderr); + fprintf(stderr,"r2: "); + r2.print_with_subs(stderr); + fprintf(stderr,"\n"); + assert(r2.n_out() == r1.n_inp()); + exit(1); + } + + int a1 = r1.max_ufs_arity_of_in(), a2 = r2.max_ufs_arity_of_out(); + + if (a1 == 0 && a2 == 0 && 0 /* FERD - leading 0's go wrong here */ ) { + // If no real UFS's, we can just use the general code: + int i; + Mapping m1(r1.n_inp(), r1.n_out()); + for(i=1; i<=r1.n_inp(); i++) m1.set_map_in (i, Exists_Var,i); + for(i=1; i<=r1.n_out(); i++) m1.set_map_out(i, Output_Var,i); + Mapping m2(r2.n_inp(), r2.n_out()); + for(i=1; i<=r2.n_inp(); i++) m2.set_map_in (i, Input_Var,i); + for(i=1; i<=r2.n_out(); i++) m2.set_map_out(i, Exists_Var,i); + + return MapAndCombineRel2(r2, r1, m2, m1, Comb_And); + } + else { + Relation result(r2.n_inp(), r1.n_out()); + int mid_size = r2.n_out(); + int i; + for(i =1; i<=r2.n_inp(); i++) + result.name_input_var(i,r2.input_var(i)->base_name); + for(i =1; i<=r1.n_out(); i++) + result.name_output_var(i,r1.output_var(i)->base_name); + + r1.simplify(); + r2.simplify(); + + Rel_Body *b1 = r1.split(), *b2 = r2.split(); + + if (b1 == b2) { + assert(0 && "Compose: not ready to handle b1 == b2 yet."); + fprintf(stderr, "Compose: not ready to handle b1 == b2 yet.\n"); + exit(1); + } + + DNF *d1 = b1->DNFize(); + DNF *d2 = b2->DNFize(); + + d1->count_leading_0s(); + d2->count_leading_0s(); + // Any conjucts with leading_0s == -1 must have >= max_arity leading 0s + // What a gross way to do this. Ferd + + F_Exists *exists = result.add_exists(); + Section<Variable_ID> middle_tuple = exists->declare_tuple(mid_size); + Map<Global_Var_ID, Variable_ID> lost_functions((Variable_ID)0); + + F_Or *result_conjs = exists->add_or(); + + for (DNF_Iterator conj1(d1); conj1; conj1++) + for (DNF_Iterator conj2(d2); conj2; conj2++) { + // combine conj1 and conj2: + // conj2's in becomes result's in; conj1's out becomes out + // conj2's out and conj1's in get merged and exist. quant. + // conj2's f(in) and conj1's f(out) become f(in) and f(out) + // conj2's f(out) and conj1's f(in) get merged, evacuate: + // if conj1 has f.arity leading 0s, they become f(out), + // if conj2 has f.arity leading 0s, they become f(in) + // if neither has enough 0s, they become a wildcard + // and the result is inexact + // old wildcards stay wildcards + +#if defined STUDY_EVACUATIONS + study_evacuation(*conj1, *conj2, max(a1, a2)); +#endif + + Conjunct *copy1, *copy2; + copy2 = (*conj2)->copy_conj_same_relation(); + copy1 = (*conj1)->copy_conj_same_relation(); + + Variable_ID_Tuple remapped; + + int c1L0 = copy1->guaranteed_leading_0s; + int c2L0 = copy2->guaranteed_leading_0s; + + int inexact = 0; + + // get rid of conj2's f(out) + { + for (Variable_ID_Iterator func(copy2->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Output_Tuple) { + if (c2L0 >= f->arity()) { + (*func)->remap = r2.get_local(f, Input_Tuple); + remapped.append(*func); + } + else if (c1L0 >= f->arity()) { + // f->remap = copy1->get_local(f, Output_Tuple); + // this should work with the current impl. + // SHOULD BE A NO-OP? + assert((*func)==r1.get_local(f,Output_Tuple)); + } + else { + Variable_ID f_quantified = lost_functions[f]; + if (!f_quantified) { + f_quantified = exists->declare(); + lost_functions[f] = f_quantified; + } + inexact = 1; + (*func)->remap = f_quantified; + remapped.append(*func); + } + } + } + } + + // remap copy2's out + for (i=1; i<=mid_size; i++) { + r2.output_var(i)->remap = middle_tuple[i]; + } + + // do remapping for conj2, then reset everything so + // we can go on with conj1 + + copy2->remap(); + reset_remap_field(remapped); + reset_remap_field(output_vars,mid_size); + + + remapped.clear(); + + // get rid of conj1's f(in) + { + for (Variable_ID_Iterator func(copy1->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0 && (*func)->function_of()==Input_Tuple) { + if (c1L0 >= f->arity()) { + (*func)->remap = r1.get_local(f,Output_Tuple); + remapped.append(*func); + } + else if (c2L0 >= f->arity()) { + // f->remap = copy2->get_local(f, Input_Tuple); + // this should work with the current impl. + // SHOULD BE A NO-OP? + assert((*func)==r2.get_local(f,Input_Tuple)); + } + else { + Variable_ID f_quantified = lost_functions[f]; + if (!f_quantified) { + f_quantified = exists->declare(); + lost_functions[f] = f_quantified; + } + inexact = 1; + (*func)->remap = f_quantified; + remapped.append(*func); + } + } + } + } + + // merge copy1's in with the already remapped copy2's out + for (i=1; i<=mid_size; i++) { + r1.input_var(i)->remap = middle_tuple[i]; + } + + copy1->remap(); + reset_remap_field(remapped); + reset_remap_field(input_vars,mid_size); + + Conjunct *conj3 = merge_conjs(copy1, copy2, MERGE_COMPOSE, exists->relation()); + result_conjs->add_child(conj3); + delete copy1; + delete copy2; + + // make sure all variables used in the conjunct + // are listed in the "result" relation + + for (Variable_ID_Iterator func(conj3->mappedVars); func; func++) + if ((*func)->kind() == Global_Var) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() > 0) + result.get_local(f, (*func)->function_of()); + else + result.get_local(f); + } + + if (inexact) + conj3->make_inexact(); + } + + // result.simplify(2, 4); // can't really do that now, will cause failure in chill + result.finalize(); + r1 = r2 = Relation(); + return result; + } +} + + + +bool Is_Obvious_Subset(NOT_CONST Relation &input_r1, NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + + assert(!r1.is_null() && !r2.is_null()); + Rel_Body *rr1 = r1.split(); + Rel_Body *rr2 = r2.split(); + rr1->simplify(); + rr2->simplify(); + use_ugly_names++; + + remap_DNF_vars(rr2, rr1); + + for(DNF_Iterator pd1(rr1->query_DNF()); pd1.live(); pd1.next()) { + Conjunct *conj1 = pd1.curr(); + int found = false; + for(DNF_Iterator pd2(rr2->query_DNF()); pd2.live(); pd2.next()) { + Conjunct *conj2 = pd2.curr(); + if (!conj2->is_exact()) continue; + + Conjunct *cgist = merge_conjs(conj1, conj2, MERGE_GIST, conj2->relation()); +#ifndef NDEBUG + cgist->setup_names(); +#endif + if (cgist->redSimplifyProblem(2, 0) == noRed) { + delete cgist; + found = true; + break; + } + delete cgist; + } + if (! found) { + use_ugly_names--; + r1 = r2 = Relation(); + return false; + } + } + use_ugly_names--; + r1 = r2 = Relation(); + return true; +} /* Is_Obvious_Subset */ + + +bool do_subset_check(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2); + +// do_subset_check really implements Must_Be_Subset anyway (due to +// correct handling of inexactness in the negation code), but +// still take upper and lower bounds here +bool Must_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2) { + Relation s1 = Upper_Bound(consume_and_regurgitate(r1)); + Relation s2 = Lower_Bound(consume_and_regurgitate(r2)); + return do_subset_check(s1,s2); +} + +bool Might_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2) { + Relation s1 = Lower_Bound(consume_and_regurgitate(r1)); + Relation s2 = Upper_Bound(consume_and_regurgitate(r2)); + return do_subset_check(s1,s2); +} + +bool May_Be_Subset(NOT_CONST Relation &r1, NOT_CONST Relation &r2){ + return Might_Be_Subset(r1,r2); +} + + + + +// +// F Must_Be_Subset G +// Test that (f => g) === (~f | g) is a Tautology +// or that (f & ~g) is unsatisfiable: +// align the input tuples (if any) for F and G +// align the output tuples (if any) for F and G +// Special case: if r2 has a single conjunct then use HasRedQeuations. +// + +bool do_subset_check(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null() || r2.is_null()) + throw std::invalid_argument("null relation"); + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + // assert(!r1.is_null() && !r2.is_null()); + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + // skip_set_checks--; + r1.simplify(1,0); + r2.simplify(2,2); + Rel_Body *rr1 = r1.split(); + + if(relation_debug) { + fprintf(DebugFile, "\n$$$ Must_Be_Subset IN $$$\n"); + } + + bool c = true; + + // Check each conjunct separately + for(DNF_Iterator pd(rr1->query_DNF()); c && pd.live(); ) { + Relation tmp(r1,pd.curr()); + pd.next(); +#ifndef CHECK_MAYBE_SUBSET + if (pd.live()) + c = !Difference(tmp,copy(r2)).is_upper_bound_satisfiable(); + else + c = !Difference(tmp,r2).is_upper_bound_satisfiable(); +#else + Relation d=Difference(copy(tmp), copy(r2)); + c=!d.is_upper_bound_satisfiable(); + if (!c && !d.is_exact()) { // negation-induced inexactness + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + if (OMEGA_WHINGE) { + fprintf(DebugFile,"\n===== r1 is maybe a Must_Be_Subset of r2 ========\n"); + fprintf(DebugFile,"-------> r1:\n"); + tmp.print_with_subs(DebugFile); + fprintf(DebugFile,"-------> r2:\n"); + r2.print_with_subs(DebugFile); + fprintf(DebugFile,"-------> r1-r2:\n"); + d.print_with_subs(DebugFile); + } + } +#endif + } + + if(relation_debug) { + fprintf(DebugFile, "$$$ Must_Be_Subset OUT $$$\n"); + } + r1 = r2 = Relation(); + return c; +} + + +// +// F minus G +// +Relation Difference(NOT_CONST Relation &input_r1, + NOT_CONST Relation &input_r2) { + Relation r1 = consume_and_regurgitate(input_r1); + Relation r2 = consume_and_regurgitate(input_r2); + if (r1.is_null() || r2.is_null()) + return r1; + if (r1.n_inp() != r2.n_inp() || r1.n_out() != r2.n_out()) + throw std::invalid_argument("relation arity does not match"); + + //assert(!r1.is_null() && !r2.is_null()); + // skip_set_checks++; + // assert(r1.n_inp() == r2.n_inp()); + // assert(r1.n_out() == r2.n_out()); + + int i; + Mapping m1(r1.n_inp(), r1.n_out()); + for(i=1; i<=r1.n_inp(); i++) m1.set_map_in (i, Input_Var,i); + for(i=1; i<=r1.n_out(); i++) m1.set_map_out(i, Output_Var,i); + Mapping m2(r2.n_inp(), r2.n_out()); + for(i=1; i<=r2.n_inp(); i++) m2.set_map_in (i, Input_Var,i); + for(i=1; i<=r2.n_out(); i++) m2.set_map_out(i, Output_Var,i); + // skip_set_checks--; + + return MapAndCombineRel2(r1, r2, m1, m2, Comb_AndNot); +} + +// +// complement F +// not F +// +Relation Complement(NOT_CONST Relation &S) { + Relation r = consume_and_regurgitate(S); + if (r.is_null()) + return r; + + // assert(!r.is_null()); + // skip_set_checks++; + int i; + Mapping m(r.n_inp(), r.n_out()); + for(i=1; i<=r.n_inp(); i++) m.set_map_in (i, Input_Var,i); + for(i=1; i<=r.n_out(); i++) m.set_map_out(i, Output_Var,i); + // skip_set_checks--; + + MapRel1(r, m, Comb_AndNot, -1, -1, false); + return r; +} + + +// +// Compute (gist r1 given r2). +// Currently we assume that r2 has only one conjunct. +// r2 may have zero input and output OR may have # in/out vars equal to r1. +// +Relation GistSingleConjunct(NOT_CONST Relation &input_R1, + NOT_CONST Relation &input_R2, int effort) { + Relation R1 = consume_and_regurgitate(input_R1); + Relation R2 = consume_and_regurgitate(input_R2); + + // skip_set_checks++; + assert(!R1.is_null() && !R2.is_null()); + assert((R1.n_inp() == R2.n_inp() && R1.n_out() == R2.n_out()) || + (R2.n_inp() == 0 && R2.n_out() == 0)); + R1.simplify(); + R2.simplify(); + Rel_Body *r1 = R1.split(); + Rel_Body *r2 = R2.split(); + + if(relation_debug) { + fprintf(DebugFile, "\n### GIST computation start ### [\n"); + R1.prefix_print(DebugFile); + R2.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } + + +// The merged conjunct has to have the variables of either r1 or r2, but +// not both. Use r1's, since it'll be cheaper to remap r2's single conj. + remap_DNF_vars(r2, r1); + assert(r2->is_upper_bound_satisfiable() && "Gist: second operand is FALSE"); + // skip_set_checks--; + + Conjunct *known = r2->single_conjunct(); + assert(known != NULL && "Gist: second operand has more than 1 conjunct"); + + DNF *new_dnf = new DNF(); + for(DNF_Iterator pd(r1->simplified_DNF); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + Conjunct *cgist = merge_conjs(known, conj, MERGE_GIST, conj->relation()); // Uses r1's vars + cgist->set_relation(r1); // Thinks it's part of r1 now, for var. purposes + if(simplify_conj(cgist, true, effort+1, EQ_RED)) { + /* Throw out black constraints, turn red constraints into black */ + cgist->rm_color_constrs(); + if(cgist->is_true()) { + delete new_dnf; + delete cgist; + // skip_set_checks++; + Relation retval = Relation::True(r1->n_inp(), r2->n_out()); + // retval.finalize(); + retval.simplify(); + if(R1.is_set() && R2.is_set()) retval.markAsSet(); + // skip_set_checks--; + return retval; + } + else { + // since modular equations might be changed, simplify again! + simplify_conj(cgist, true, effort+1, EQ_BLACK); + + new_dnf->add_conjunct(cgist); + } + } + } + delete r1->simplified_DNF; + r1->simplified_DNF = new_dnf; + assert(!r1->is_null()); + R1.finalize(); + if(relation_debug) { + fprintf(DebugFile, "] ### GIST computation end ###\n"); + R1.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } + return(R1); +} + + +// +// Compute gist r1 given r2. r2 can have multiple conjuncts, +// return result is always simplified. +// +Relation Gist(NOT_CONST Relation &input_R1, + NOT_CONST Relation &input_R2, int effort) { + Relation R1 = consume_and_regurgitate(input_R1); + Relation R2 = consume_and_regurgitate(input_R2); + if (R1.is_null()) + return R1; + // change the Gist semantics to allow r2 be null -- by chun 07/30/2007 + if (R2.is_null()) { + R1.simplify(); + return R1; + } + if (!(R1.n_inp() == 0 && R2.n_out() == 0) && + (R1.n_inp() != R2.n_inp() || R1.n_out() != R2.n_out())) + throw std::invalid_argument("relation arity does not match"); + + // skip_set_checks++; + // assert(!R1.is_null()); + // assert(R2.is_null() || + // (R1.n_inp() == R2.n_inp() && R1.n_out() == R2.n_out()) || + // (R2.n_inp() == 0 && R2.n_out() == 0)); + // skip_set_checks--; + R2.simplify(); + + if(relation_debug) { + fprintf(DebugFile, "\n### multi-GIST computation start ### [\n"); + R1.prefix_print(DebugFile); + R2.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } + + if (!R2.is_upper_bound_satisfiable()) + return Relation::True(R1); + if (R2.is_obvious_tautology()) { + R1.simplify(); + return R1; + } + R1.simplify(); + + if (!Intersection(copy(R1), copy(R2)).is_upper_bound_satisfiable()) + return Relation::False(R1); + + int nconj1=0; + for (DNF_Iterator di(R1.simplified_DNF()); di.live(); di.next()) + nconj1++; + int nconj2=0; + for (DNF_Iterator di2(R2.simplified_DNF()); di2.live(); di2.next()) + nconj2++; + + { + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + if (OMEGA_WHINGE && (nconj1 + nconj2 > 50)) { + fprintf(DebugFile,"WOW!!!! - Gist (%d conjuncts, %d conjuncts)!!!\n", + nconj1,nconj2); + fprintf(DebugFile,"Base:\n"); + R1.prefix_print(DebugFile); + fprintf(DebugFile,"Context:\n"); + R2.prefix_print(DebugFile); + } + } + + if (nconj2==1) + return GistSingleConjunct(R1,R2, effort); + else { + R1.simplify(0,1); + R2.simplify(0,1); + Relation G = Relation::True(R1); + for (DNF_Iterator di2(R2.simplified_DNF()); di2.live(); di2.next()) { + Conjunct * c2 = di2.curr(); + Relation G2 = Relation::False(R1); + for (DNF_Iterator di1(R1.simplified_DNF()); di1.live(); di1.next()) { + Conjunct * c1 = di1.curr(); + Relation G1=GistSingleConjunct(Relation(R1,c1), Relation(R2,c2),effort); + + if (G1.is_obvious_tautology()) { + G2 = G1; + break; + } + else if (!G1.is_upper_bound_satisfiable() || !G1.is_exact()) { + if(relation_debug) { + fprintf(DebugFile, "gist A given B is unsatisfiable\n"); + fprintf(DebugFile, "A:\n"); + Relation(R1,c1).prefix_print(DebugFile); + fprintf(DebugFile, "B:\n"); + Relation(R2,c2).prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + } + //G1 = Relation(R1,c1); + return R1; + } + else if(0 && G1.is_exact() && !Must_Be_Subset(Relation(R1,c1),copy(G1))) { + fprintf(DebugFile,"Unexpected non-Must_Be_Subset gist result!\n"); + fprintf(DebugFile,"base: \n"); + Relation(R1,c1).prefix_print(DebugFile); + fprintf(DebugFile,"context: \n"); + Relation(R2,c2).prefix_print(DebugFile); + fprintf(DebugFile,"result: \n"); + G1.prefix_print(DebugFile); + fprintf(DebugFile,"base not subseteq result: \n"); + assert(!G1.is_exact() || Must_Be_Subset(Relation(R1,c1),copy(G1))); + } + G2=Union(G2,G1); + } + G2.simplify(0,1); + G = Intersection(G,G2); + G.simplify(0,1); + if(relation_debug) { + fprintf(DebugFile, "result so far is:\n"); + G.prefix_print(DebugFile); + } + } + + if(relation_debug) { + fprintf(DebugFile, "\n### end multi-GIST computation ### ]\n"); + fprintf(DebugFile, "G is:\n"); + G.prefix_print(DebugFile); + fprintf(DebugFile, "### ###\n"); + } +#if ! defined NDEBUG + Relation S1 = Intersection(copy(R1), copy(R2)); + Relation S2 = Intersection(copy(G), copy(R2)); + + + if(relation_debug) { + fprintf(DebugFile, "\n---->[Checking validity of the GIST result\n"); + fprintf(DebugFile, "for G=gist R1 given R2:\n"); + fprintf(DebugFile, "R1 intersect R2 is:\n"); + S1.print_with_subs(DebugFile); + fprintf(DebugFile, "\nG intersect R2 is:\n"); + S2.print_with_subs(DebugFile); + fprintf(DebugFile, "---->]\n"); + } + assert (!S1.is_exact() || !S2.is_exact() || (Must_Be_Subset(copy(S1),copy(S2)) && Must_Be_Subset(copy(S2),copy(S1)))); +#endif + return G; + } +} + + +// Project away all input and output variables. +Relation Project_On_Sym(NOT_CONST Relation &S, + NOT_CONST Relation &input_context) { + Relation R = consume_and_regurgitate(S); + Relation context = consume_and_regurgitate(input_context); + int i; + + // skip_set_checks++; + leave_pufs_untouched++; + int in_arity = R.max_ufs_arity_of_in(); + int out_arity = R.max_ufs_arity_of_out(); + assert(!R.is_null()); + R.split(); + + int no_inp = R.n_inp(); + int no_out = R.n_out(); + Mapping M(no_inp, no_out); + + for(i=1; i<=no_inp; i++) { // project out input variables + M.set_map(Input_Var, i, Exists_Var, i); + } + for(i=1; i<=no_out; i++) { // project out output variables + M.set_map(Output_Var, i, Exists_Var, no_inp+i); + } + MapRel1(R, M, Comb_Id, 0, 0); + + R.finalize(); + if (in_arity) R = Extend_Domain(R,in_arity); + if (out_arity) R = Extend_Range(R,out_arity); + + int d = min(in_arity,out_arity); + if (d && !context.is_null()) { + int g = min(d,context.query_guaranteed_leading_0s()); + int p = min(d,context.query_possible_leading_0s()); + int dir = context.query_leading_dir(); + R.enforce_leading_info(g,p,dir); + } + + leave_pufs_untouched--; + // skip_set_checks--; + if(relation_debug) { + fprintf(DebugFile,"\nProjecting onto symbolic (%d,%d):\n",in_arity,out_arity); + R.prefix_print(DebugFile); + } + return R; +} + + +// +// Project out global variable g from relation r +// +Relation Project(NOT_CONST Relation &S, Global_Var_ID g) { + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + + skip_finalization_check++; + + Rel_Body *r = R.split(); + r->DNF_to_formula(); + Formula *f = r->rm_formula(); + F_Exists *ex = r->add_exists(); + ex->add_child(f); + + if (g->arity() == 0) { + assert(R.has_local(g) && "Project: Relation doesn't contain variable to be projected"); + Variable_ID v = R.get_local(g); + + bool rmd = rm_variable(r->Symbolic,v); + assert(rmd && "Project: Variable to be projected doesn't exist"); + + v->remap = ex->declare(v->base_name); + f->remap(); + v->remap = v; + } + else { + assert((R.has_local(g, Input_Tuple) || R.has_local(g, Output_Tuple)) && "Project: Relation doesn't contain variable to be projected"); + + if (R.has_local(g, Input_Tuple)) { + Variable_ID v = R.get_local(g, Input_Tuple); + + bool rmd = rm_variable(r->Symbolic,v); + assert(rmd && "Project: Variable to be projected doesn't exist"); + + v->remap = ex->declare(v->base_name); + f->remap(); + v->remap = v; + } + if (R.has_local(g, Output_Tuple)) { + Variable_ID v = R.get_local(g, Output_Tuple); + + bool rmd = rm_variable(r->Symbolic,v); + assert(rmd && "Project: Variable to be projected doesn't exist"); + + v->remap = ex->declare(v->base_name); + f->remap(); + v->remap = v; + } + } + + skip_finalization_check--; + + R.finalize(); + return R; +} + + +// +// Project all symbolic variables from relation r +// +Relation Project_Sym(NOT_CONST Relation &S) { + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + + Rel_Body *r = R.split(); + r->DNF_to_formula(); + + Formula *f = r->rm_formula(); + + skip_finalization_check++; + F_Exists *ex = r->add_exists(); + for(Variable_ID_Iterator R_Sym(r->Symbolic); R_Sym; R_Sym++) { + Variable_ID v = *R_Sym; + v->remap = ex->declare(v->base_name); + } + ex->add_child(f); + skip_finalization_check--; + + f->remap(); + + reset_remap_field(r->Symbolic); + r->Symbolic.clear(); + + R.finalize(); + return R; +} + +// +// Project specified variables, leaving those variables with no constraints. +// +Relation Project(NOT_CONST Relation &S, Sequence<Variable_ID> &s) { + // This is difficult to do with mappings. This cheats, since it is + // much easier and more straightforward. + + Relation R = consume_and_regurgitate(S); + assert(!R.is_null()); + + Rel_Body *r = R.split(); + r->DNF_to_formula(); + Formula *f = r->rm_formula(); + bool need_symbolic_clear = false; + + skip_finalization_check++; + F_Exists *ex = r->add_exists(); + for(int i = 1; i <= s.size(); i++) { + if (s[i]->kind() == Global_Var) + need_symbolic_clear = true; + s[i]->remap = ex->declare(s[i]->base_name); + } + ex->add_child(f); + skip_finalization_check--; + + f->remap(); + + reset_remap_field(s); + if (need_symbolic_clear) + r->Symbolic.clear(); + + R.finalize(); + return R; +} + +Relation Project(NOT_CONST Relation &S, int pos, Var_Kind vkind) { + Variable_ID v = 0; // shut the compiler up + switch (vkind) { + case Input_Var: + v = input_vars[pos]; + break; + case Output_Var: + v = output_vars[pos]; + break; + // case Set_Var: + // v = set_vars[pos]; + // break; + default: + assert(0); + } + + return Project(S, v); +} + +Relation Project(NOT_CONST Relation &S, Variable_ID v) { + Tuple<Variable_ID> s; + s.append(v); + return Project(S, s); +} + +// +// Variables in DNF of map_rel reference declarations of map_rel (or not). +// remap_DNF_vars makes them to reference declarations of ref_rel. +// Ref_rel can get new global variable declarations in the process. +// +void remap_DNF_vars(Rel_Body *map_rel, Rel_Body *ref_rel) { + // skip_set_checks++; + assert (map_rel->simplified_DNF); + assert (ref_rel->simplified_DNF); + + // skip_set_checks++; + + for(DNF_Iterator pd(map_rel->simplified_DNF); pd.live(); pd.next()) { + Conjunct *cc = pd.curr(); + Variable_ID_Tuple &mvars = cc->mappedVars; + for(Variable_Iterator mvarsIter=mvars; mvarsIter; mvarsIter++) { + Variable_ID v = *mvarsIter; + switch(v->kind()) { + case Input_Var: + assert(ref_rel->n_inp() >= v->get_position()); + break; + case Output_Var: + assert(ref_rel->n_out() >= v->get_position()); + break; + case Global_Var: + // The assignment is a noop, but tells ref_rel that the global may be + // used inside it, which is required. + *mvarsIter = ref_rel->get_local(v->get_global_var(),v->function_of()); + break; + case Wildcard_Var: + break; + default: + assert(0 && "bad variable kind"); + } + } + } + // skip_set_checks--; +} + + +Relation projectOntoJust(Relation R, Variable_ID v) { + // skip_set_checks++; + + int ivars = R.n_inp(), ovars = R.n_out(); + int ex_ivars= 0, ex_ovars = 0; + + assert(v->kind() == Input_Var || v->kind() == Output_Var); + if (v->kind() == Input_Var) { + ex_ivars = 1; + R = Extend_Domain(R,1); + } + else { + ex_ovars = 1; + R = Extend_Range(R,1); + } + + // Project everything except v + Mapping m(ivars+ex_ivars,ovars+ex_ovars); + int j; + for(j = 1; j <=ivars+ex_ivars; j++) m.set_map_in(j, Exists_Var, j); + for(j = 1; j <=ovars+ex_ovars; j++) m.set_map_out(j, Exists_Var, j+ivars+ex_ivars); + m.set_map(v->kind(), v->get_position(), v->kind(), v->get_position()); + + MapRel1(R, m, Comb_Id,-1,-1); + R.finalize(); + // skip_set_checks--; + return R; +} + +//static +//void copyEQtoGEQ(GEQ_Handle &g, const EQ_Handle &e, bool negate) { +//extern void copy_constraint(Constraint_Handle H, Constraint_Handle initial); +// copy_constraint(g, e); +//} + + +Relation EQs_to_GEQs(NOT_CONST Relation &S, bool excludeStrides) { + Relation R = consume_and_regurgitate(S); + assert(R.is_simplified()); + use_ugly_names++; + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) + s.curr()->convertEQstoGEQs(excludeStrides); + use_ugly_names--; + return R; +} + + +// Tuple to find values for is input+output +Relation Symbolic_Solution(NOT_CONST Relation &R) { + Relation S = consume_and_regurgitate(R); + Tuple<Variable_ID> vee; + // skip_set_checks++; + int i; + for(i = 1; i <= S.n_inp(); i++) vee.append(input_var(i)); + for(i = 1; i <= S.n_out(); i++) vee.append(output_var(i)); + // skip_set_checks--; + + return Solution(S, vee); +} + + +// Tuple to find values for is given as arg, plus input and output +Relation Symbolic_Solution(NOT_CONST Relation &R, Sequence<Variable_ID> &for_these){ + Relation S = consume_and_regurgitate(R); + Tuple<Variable_ID> vee; + // skip_set_checks++; + int i; + for(Any_Iterator<Variable_ID> it(for_these); it; it++) + vee.append(*it); + for(i = 1; i <= S.n_inp(); i++) vee.append(input_var(i)); + for(i = 1; i <= S.n_out(); i++) vee.append(output_var(i)); + // skip_set_checks--; + + return Solution(S, vee); +} + + +// Tuple to find values for is input+output+global_decls +Relation Sample_Solution(NOT_CONST Relation &R) { + Relation S = consume_and_regurgitate(R); + + Tuple<Variable_ID> vee; + + // skip_set_checks++; + int i; + for(i = 1; i <= S.global_decls()->size(); i++) + vee.append((*S.global_decls())[i]); + for(i = 1; i <= S.n_inp(); i++) vee.append(input_var(i)); + for(i = 1; i <= S.n_out(); i++) vee.append(output_var(i)); + // skip_set_checks--; + + return Solution(S,vee); +} + + +// Tuple to find values is given as arg +Relation Solution(NOT_CONST Relation &S, Sequence<Variable_ID> &for_these ) { + Relation R = consume_and_regurgitate(S); + if (R.is_null()) + return R; + + //assert(!R.is_null()); + + if(!R.is_upper_bound_satisfiable()) { + return Relation::False(R); + } + + bool inexactAnswer=false; + if(R.is_inexact()) { + if(R.is_lower_bound_satisfiable()) + R = Lower_Bound(R); // a solution to LB is a solution to the relation + else { + // A solution to the UB may not be a solution to the relation: + // There may be a solution which satisfies all known constraints, but + // we have no way of knowing if it satisifies the unknown constraints. + inexactAnswer = true; + R = Upper_Bound(R); + } + } + + Sequence<Variable_ID> &vee = for_these; + for (DNF_Iterator di(R.query_DNF()); di; di++) { + Relation current(R, *di); + int i; + for(i = vee.size()-1; i >= 0; i--) { + bool some_constraints = false, one_stride = false; + + int current_var = vee.size()-i; + Section<Variable_ID> s(&vee,current_var+1,i); + + // Query variable in vee[current_var] + Relation projected = Project(copy(current), s); + + retry_solution: + assert(projected.has_single_conjunct()); + DNF_Iterator one = projected.query_DNF(); + + // Look for candidate EQ's + EQ_Handle stride; + EQ_Iterator ei(*one); + for(; ei; ei++) { + if((*ei).get_coef(vee[current_var]) != 0) { + if(!Constr_Vars_Iter(*ei,true).live()) { // no wildcards + some_constraints = true; + // Add this constraint to the current as an EQ + current.and_with_EQ(*ei); + break; + } + else { + one_stride = !one_stride && !some_constraints; + stride = *ei; + } + } + } + if(ei) + continue; // Found an EQ, skip to next variable + else if (one_stride && !some_constraints) { + // if unconstrained except for a stride, pick stride as value + Constr_Vars_Iter cvi(stride,true); + assert(cvi.live()); + cvi++; + if(!cvi) { // Just one existentially quantified variable + Relation current_copy = current; + EQ_Handle eh = current_copy.and_with_EQ(); + for(Constr_Vars_Iter si = stride; si; si++) + if((*si).var->kind() != Wildcard_Var){ + // pick "0" for wildcard, don't set its coef + eh.update_coef((*si).var, (*si).coef); + } + eh.update_const(stride.get_const()); + if(current_copy.is_upper_bound_satisfiable()){ + current = current_copy; + continue; // skip to next var + } + } + some_constraints = true; // count the stride as a constraint + } + + // Can we convert a GEQ? + GEQ_Iterator gi(*one); + for(; gi; gi++) { + if((*gi).get_coef(vee[current_var]) != 0) { + some_constraints = true; + if(!Constr_Vars_Iter(*gi,true).live()) { // no wildcards + Relation current_copy = current; + // Add this constraint to the current as an EQ & test + current_copy.and_with_EQ(*gi); + if (current_copy.is_upper_bound_satisfiable()) { + current = current_copy; + break; + } + } + } + } + if (gi) continue; // Turned a GEQ into EQ, skip to next + + // Remove wildcards, try try again + Relation approx = Approximate(copy(projected)); + assert(approx.has_single_conjunct()); + DNF_Iterator d2 = approx.query_DNF(); + + EQ_Iterator ei2(*d2); + for(; ei2; ei2++) { + if((*ei2).get_coef(vee[current_var]) != 0) { + some_constraints = true; + assert(!Constr_Vars_Iter(*ei2,true).live()); // no wildcards + Relation current_copy = current; + // Add this constraint to the current as an EQ & test + current_copy.and_with_EQ(*ei2); + if (current_copy.is_upper_bound_satisfiable()) { + current = current_copy; + break; + } + } + } + if(ei2) continue; // Found an EQ, skip to next variable + + GEQ_Iterator gi2(*d2); + for(; gi2; gi2++) { + if((*gi2).get_coef(vee[current_var]) != 0) { + some_constraints = true; + assert(!Constr_Vars_Iter(*gi2,true).live()); // no wildcards + Relation current_copy = current; + // Add this constraint to the current as an EQ & test + current_copy.and_with_EQ(*gi2); + if (current_copy.is_upper_bound_satisfiable()) { + current = current_copy; + break; + } + } + } + if(gi2) continue; + + if(!some_constraints) { // No constraints on this variable were found + EQ_Handle e = current.and_with_EQ(); + e.update_const(-42); // Be creative + e.update_coef(vee[current_var], 1); + continue; + } + else { // What to do? Find a wildcard to discard + Variable_ID wild = NULL; + + for (GEQ_Iterator gi(*one); gi; gi++) + if ((*gi).get_coef(vee[current_var]) != 0 && (*gi).has_wildcards()) { + Constr_Vars_Iter cvi(*gi, true); + wild = (*cvi).var; + break; + } + if (wild == NULL) + for (EQ_Iterator ei(*one); ei; ei++) + if ((*ei).get_coef(vee[current_var]) != 0 && (*ei).has_wildcards()) { + Constr_Vars_Iter cvi(*ei, true); + wild = (*cvi).var; + break; + } + + if (wild != NULL) { + // skip_set_checks++; + + Relation R2; + { + Tuple<Relation> r(1); + r[1] = projected; + Tuple<std::map<Variable_ID, std::pair<Var_Kind, int> > > mapping(1); + mapping[1][wild] = std::make_pair(vee[current_var]->kind(), vee[current_var]->get_position()); + mapping[1][vee[current_var]] = std::make_pair(Exists_Var, 1); + Tuple<bool> inverse(1); + inverse[1] = false; + R2 = merge_rels(r, mapping, inverse, Comb_And); + } + + Variable_ID R2_v; + switch (vee[current_var]->kind()) { + // case Set_Var: + case Input_Var: { + int pos = vee[current_var]->get_position(); + R2_v = R2.input_var(pos); + break; + } + case Output_Var: { + int pos = vee[current_var]->get_position(); + R2_v = R2.output_var(pos); + break; + } + case Global_Var: { + Global_Var_ID g = vee[current_var]->get_global_var(); + if (g->arity() == 0) + R2_v = R2.get_local(g); + else + R2_v = R2.get_local(g, vee[current_var]->function_of()); + } + default: + assert(0); + } + + Relation S2; + { + Tuple<Variable_ID> vee; + vee.append(R2_v); + S2 = Solution(R2, vee); + } + + Variable_ID S2_v; + switch (vee[current_var]->kind()) { + // case Set_Var: + case Input_Var: { + int pos = vee[current_var]->get_position(); + S2_v = S2.input_var(pos); + break; + } + case Output_Var: { + int pos = vee[current_var]->get_position(); + S2_v = S2.output_var(pos); + break; + } + case Global_Var: { + Global_Var_ID g = vee[current_var]->get_global_var(); + if (g->arity() == 0) + S2_v = S2.get_local(g); + else + S2_v = S2.get_local(g, vee[current_var]->function_of()); + } + default: + assert(0); + } + + Relation R3; + { + Tuple<Relation> r(2); + r[1] = projected; + r[2] = S2; + Tuple<std::map<Variable_ID, std::pair<Var_Kind, int> > > mapping(2); + mapping[1][wild] = std::make_pair(Exists_Var, 1); + mapping[2][S2_v] = std::make_pair(Exists_Var, 1); + Tuple<bool> inverse(2); + inverse[1] = inverse[2] = false; + R3 = merge_rels(r, mapping, inverse, Comb_And); + } + + // skip_set_checks--; + + if (R3.is_upper_bound_satisfiable()) { + projected = R3; + goto retry_solution; + } + } + } + + // If we get here, we failed to find a suitable constraint for + // this variable at this conjunct, look for another conjunct. + break; + } + + if (i < 0) { // solution found + if(inexactAnswer) + current.and_with_and()->add_unknown(); + current.finalize(); + return current; + } + } + + // No solution found for any conjunct, we bail out. + fprintf(stderr,"Couldn't find suitable constraint for variable\n"); + return Relation::Unknown(R); +} + + +Relation Approximate(NOT_CONST Relation &input_R, bool strides_allowed) { + Relation R = consume_and_regurgitate(input_R); + if (R.is_null()) + return R; + + // assert(!R.is_null()); + Rel_Body *r = R.split(); + + // approximate can be used to remove lambda variables from farkas, + // so be careful not to invoke simplification process for integers. + r->simplify(-1,-1); + + if (pres_debug) { + fprintf(DebugFile,"Computing approximation "); + if (strides_allowed) fprintf(DebugFile,"with strides allowed "); + fprintf(DebugFile,"[ \n"); + r->prefix_print(DebugFile); + } + + use_ugly_names++; + for (DNF_Iterator pd(r->simplified_DNF); pd.live(); ) { + Conjunct *C = pd.curr(); + pd.next(); + + for(int i = 0; i < C->problem->nGEQs; i++) + C->problem->GEQs[i].touched = 1; + + C->reorder(); + if(C->problem->simplifyApproximate(strides_allowed)==0) { + r->simplified_DNF->rm_conjunct(C); + delete C; + } + else { + C->simplifyProblem(1,0,1); + + free_var_decls(C->myLocals); C->myLocals.clear(); + + Problem *p = C->problem; + Variable_ID_Tuple new_mapped(0); // This is expanded by "append" + for (int i = 1; i <= p->safeVars; i++) { + // what is now in column i used to be in column p->var[i] + Variable_ID v = C->mappedVars[p->var[i]]; + assert (v->kind() != Wildcard_Var); + new_mapped.append(v); + } + assert(strides_allowed || C->problem->nVars == C->problem->safeVars); + C->mappedVars = new_mapped; + for (int i = p->safeVars+1; i <= p->nVars; i++) { + Variable_ID v = C->declare(); + C->mappedVars.append(v); + } + + + // reset var and forwarding address if desired. + p->variablesInitialized = 0; + for(int i = 1; i < C->problem->nVars; i++) + C->problem->var[i] = C->problem->forwardingAddress[i] = i; + } + } + + if (pres_debug) + fprintf(DebugFile,"] done Computing approximation\n"); + use_ugly_names--; + return R; +} + + +Relation Lower_Bound(NOT_CONST Relation &r) { + Relation s = consume_and_regurgitate(r); + s.interpret_unknown_as_false(); + return s; +} + + +Relation Upper_Bound(NOT_CONST Relation &r) { + Relation s = consume_and_regurgitate(r); + s.interpret_unknown_as_true(); + return s; +} + + +bool operator==(const Relation &, const Relation &) { + assert(0 && "You rilly, rilly don't want to do this.\n"); + abort(); + return false; +} + + +namespace { // supporting stuff for MapRel1 and MapAndCombine2 + // Determine if a mapping requires an f_exists node + bool has_existentials(const Mapping &m) { + for(int i=1;i<=m.n_in(); i++) + if (m.get_map_in_kind(i) == Exists_Var) return true; + for(int j=1;j<=m.n_out(); j++) + if (m.get_map_out_kind(j) == Exists_Var) return true; + return false; + } + + void get_relation_arity_from_one_mapping(const Mapping &m1, + int &in_req, int &out_req) { + int j, i; + in_req = 0; out_req = 0; + for(i = 1; i <= m1.n_in(); i++) { + j = m1.get_map_in_pos(i); + switch(m1.get_map_in_kind(i)) { + case Input_Var: in_req = max(in_req, j); break; + // case Set_Var: in_req = max(in_req, j); break; + case Output_Var: out_req = max(out_req, j); break; + default: break; + } + } + for(i = 1; i <= m1.n_out(); i++) { + j = m1.get_map_out_pos(i); + switch(m1.get_map_out_kind(i)) { + case Input_Var: in_req = max(in_req, j); break; + // case Set_Var: in_req = max(in_req, j); break; + case Output_Var: out_req = max(out_req, j); break; + default: break; + } + } + } + + // Scan mappings to see how many input and output variables they require. + void get_relation_arity_from_mappings(const Mapping &m1, + const Mapping &m2, + int &in_req, int &out_req) { + int inreq1, inreq2, outreq1, outreq2; + get_relation_arity_from_one_mapping(m1, inreq1, outreq1); + get_relation_arity_from_one_mapping(m2, inreq2, outreq2); + in_req = max(inreq1, inreq2); + out_req = max(outreq1, outreq2); + } +} + + +// +// Build lists of variables that need to be replaced in the given +// Formula. Declare globals in new relation. Then call +// map_vars to do the replacements. +// +// Obnoxiously many arguments here: +// Relation arguments contain declarations of symbolic and in/out vars. +// F_Exists argument is where needed existentially quant. vars can be decl. +// +// Mapping specifies how in/out vars are mapped +// Two lists are required to be able to map in/out variables from the first +// and second relations to the same existentially quantified variable. +// +void align(Rel_Body *originalr, Rel_Body *newr, F_Exists *fe, + Formula *f, const Mapping &mapping, bool &newrIsSet, + List<int> &seen_exists, Variable_ID_Tuple &seen_exists_ids) { + int i, cur_ex = 0; // initialize cur_ex to shut up the compiler + + f->set_relation(newr); // Might not need to do this anymore, if bugs were fixed + int input_remapped = 0; + int output_remapped = 0; + int sym_remapped = 0; + // skip_set_checks++; + + Variable_ID new_var; + Const_String new_name; + int new_pos; + + // MAP old input variables by setting their remap fields + for(i = 1; i <= originalr->n_inp(); i++) { + Variable_ID this_var = originalr->input_var(i), New_E; + Const_String this_name = originalr->In_Names[i]; + + switch (mapping.get_map_in_kind(i)) { + case Input_Var: + // case Set_Var: + // if (mapping.get_map_in_kind(i) == Set_Var) + // newrIsSet = true; // Don't mark it just yet; we still need to + // // refer to its "input" vars internally + + // assert((newrIsSet && mapping.get_map_in_kind(i) == Set_Var) + // || ((!newrIsSet &&mapping.get_map_in_kind(i) == Input_Var))); + + new_pos = mapping.get_map_in_pos(i); + new_var = newr->input_var(new_pos); + if (this_var != new_var) { + input_remapped = 1; + this_var->remap = new_var; + } + new_name = newr->In_Names[new_pos]; + if (!this_name.null()) { // should we name this? + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_input_var(new_pos, Const_String()); + } + else + newr->name_input_var(new_pos, this_name); + } + break; + case Output_Var: + assert(!newr->is_set()); + input_remapped = 1; + new_pos = mapping.get_map_in_pos(i); + this_var->remap = new_var = newr->output_var(new_pos); + new_name = newr->Out_Names[new_pos]; + if (!this_name.null()) { + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_output_var(new_pos, Const_String()); + } + else + newr->name_output_var(new_pos, this_name); + } + break; + case Exists_Var: + input_remapped = 1; + // check if we have declared it, use that if so. + // create it if not. + if (mapping.get_map_in_pos(i) <= 0 || + (cur_ex = seen_exists.index(mapping.get_map_in_pos(i))) == 0){ + if (!this_name.null()) + New_E = fe->declare(this_name); + else + New_E = fe->declare(); + this_var->remap = New_E; + if (mapping.get_map_in_pos(i) > 0) { + seen_exists.append(mapping.get_map_in_pos(i)); + seen_exists_ids.append(New_E); + } + } + else { + this_var->remap = new_var = seen_exists_ids[cur_ex]; + if (!this_name.null()) { // Have we already assigned a name? + if (!new_var->base_name.null()) { + if (new_var->base_name != this_name) + new_var->base_name = Const_String(); + } + else { + new_var->base_name = this_name; + assert(!this_name.null()); + } + } + } + break; + default: + assert(0 && "Unsupported var type in MapRel2"); + break; + } + } + + // MAP old output variables. + for(i = 1; i <= originalr->n_out(); i++) { + Variable_ID this_var = originalr->output_var(i), New_E; + Const_String this_name = originalr->Out_Names[i]; + + switch (mapping.get_map_out_kind(i)) { + case Input_Var: + // case Set_Var: + // if (mapping.get_map_out_kind(i) == Set_Var) + // newrIsSet = true; // Don't mark it just yet; we still need to refer to its "input" vars internally + + // assert((newrIsSet && mapping.get_map_out_kind(i) == Set_Var) + // ||((!newrIsSet &&mapping.get_map_out_kind(i) == Input_Var))); + + output_remapped = 1; + new_pos = mapping.get_map_out_pos(i); + this_var->remap = new_var = newr->input_var(new_pos); + new_name = newr->In_Names[new_pos]; + if (!this_name.null()) { + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_input_var(new_pos, Const_String()); + } + else + newr->name_input_var(new_pos, this_name); + } + break; + case Output_Var: + assert(!newr->is_set()); + new_pos = mapping.get_map_out_pos(i); + new_var = newr->output_var(new_pos); + if (new_var != this_var) { + output_remapped = 1; + this_var->remap = new_var; + } + new_name = newr->Out_Names[new_pos]; + if (!this_name.null()) { + if (!new_name.null()) { // already named, anonymize + if (new_name != this_name) + newr->name_output_var(new_pos, Const_String()); + } + else + newr->name_output_var(new_pos, this_name); + } + break; + case Exists_Var: + // check if we have declared it, create it if not. + output_remapped = 1; + if (mapping.get_map_out_pos(i) <= 0 || + (cur_ex = seen_exists.index(mapping.get_map_out_pos(i))) == 0) { // Declare it. + New_E = fe->declare(this_name); + this_var->remap = New_E; + if (mapping.get_map_out_pos(i) > 0) { + seen_exists.append(mapping.get_map_out_pos(i)); + seen_exists_ids.append(New_E); + } + } + else { + this_var->remap = new_var = seen_exists_ids[cur_ex]; + if (!this_name.null()) { + if (!new_var->base_name.null()) { + if (new_var->base_name != this_name) + new_var->base_name = Const_String(); + } + else { + new_var->base_name = this_name; + } + } + } + break; + default: + assert(0 &&"Unsupported var type in MapRel2"); + break; + } + } + + Variable_ID_Tuple *oldSym = originalr->global_decls(); + for(i=1; i<=(*oldSym).size(); i++) { + Variable_ID v = (*oldSym)[i]; + assert(v->kind()==Global_Var); + if (v->get_global_var()->arity() > 0) { + Argument_Tuple new_of = v->function_of(); + if (!leave_pufs_untouched) + new_of = mapping.get_tuple_fate(new_of, v->get_global_var()->arity()); + if (new_of == Unknown_Tuple) { + // hopefully v is not really used + // if we get here, f should have been in DNF, + // now an OR node with conjuncts below + // we just need to check that no conjunct uses v +#if ! defined NDEBUG + if (f->node_type() == Op_Conjunct) { + assert(f->really_conjunct()->mappedVars.index(v)==0 + && "v unused"); + } +#if 0 + else { + // assert(f->node_type() == Op_Or); + for (List_Iterator<Formula *> conj(f->children()); conj; conj++) { + assert((*conj)->really_conjunct()->mappedVars.index(v)==0 + && "v unused"); + } + } +#endif +#endif + // since its not really used, don't bother adding it to + // the the global_vars list of the new relation + continue; + } + if (v->function_of() != new_of) { + Variable_ID new_v=newr->get_local(v->get_global_var(),new_of); + assert(v != new_v); + v->remap = new_v; + sym_remapped = 1; + } + else { + // add symbolic to symbolic list +#if ! defined NDEBUG + Variable_ID new_v = +#endif + newr->get_local(v->get_global_var(), v->function_of()); +#if ! defined NDEBUG + assert(v == new_v); +#endif + } + } + else { + // add symbolic to symbolic list +#if ! defined NDEBUG + Variable_ID new_v = +#endif + newr->get_local(v->get_global_var()); +#if ! defined NDEBUG + assert(v == new_v); +#endif + } + } + + if (sym_remapped || input_remapped || output_remapped) { + f->remap(); + + // If 2 vars mapped to same variable, combine them + //There's a column to combine only when there are two equal remap fields. + Tuple<Variable_ID> vt(0); + bool combine = false; + Tuple_Iterator<Variable_ID> t(input_vars); + for(i=1; !combine && i<=originalr->n_inp(); t++, i++) + if (vt.index((*t)->remap)) + combine = true; + else + vt.append((*t)->remap); + Tuple_Iterator<Variable_ID> t2(output_vars); + for(i=1; !combine && i <= originalr->n_out(); t2++, i++) + if (vt.index((*t2)->remap)) + combine = true; + else + vt.append((*t2)->remap); + if (combine) f->combine_columns(); + + if (sym_remapped) + reset_remap_field(originalr->Symbolic); + if (input_remapped) + reset_remap_field(input_vars,originalr->n_inp()); + if (output_remapped) + reset_remap_field(output_vars,originalr->n_out()); + } + + // skip_set_checks--; + +#ifndef NDEBUG + if (fe) + foreach(v,Variable_ID,fe->myLocals,assert(v == v->remap)); +#endif +} + + +// MapRel1, MapAndCombineRel2 can be replaced by merge_rels +void MapRel1(Relation &R, const Mapping &map, Combine_Type ctype, + int number_input, int number_output, + bool invalidate_resulting_leading_info, + bool finalize) { +#if defined(INCLUDE_COMPRESSION) + assert(!R.is_compressed()); +#endif + assert(!R.is_null()); + + Relation inputRel = R; + R = Relation(); + Rel_Body *inputRelBody = inputRel.split(); + + int in_req=0, out_req=0; + get_relation_arity_from_one_mapping(map, in_req, out_req); + + R = Relation(number_input == -1 ? in_req : number_input, + number_output == -1 ? out_req : number_output); + + Rel_Body *outputRelBody = R.split(); + + inputRelBody->DNF_to_formula(); + Formula *f1 = inputRelBody->rm_formula(); + + F_Exists *fe; + Formula *f; + if (has_existentials(map)) { + f = fe = outputRelBody->add_exists(); + } + else { + fe = NULL; + f = outputRelBody; + } + and_below_exists = NULL; + if (finalize) and_below_exists = NULL; + else f = and_below_exists = f->add_and(); + if(ctype == Comb_AndNot) { + f = f->add_not(); + } + f->add_child(f1); + + exists_ids.clear(); + exists_numbers.clear(); + + bool returnAsSet=false; + align(inputRelBody, outputRelBody, fe, f1, map, returnAsSet, + exists_numbers, exists_ids); + + if (returnAsSet || + (inputRelBody->is_set() && outputRelBody->n_out() == 0)) { + R.markAsSet(); + R.invalidate_leading_info(); // nonsensical for a set + } + + if (finalize) R.finalize(); + inputRel = Relation(); + if (invalidate_resulting_leading_info) + R.invalidate_leading_info(); +} + + +Relation MapAndCombineRel2(Relation &R1, Relation &R2, const Mapping &mapping1, + const Mapping &mapping2, Combine_Type ctype, + int number_input, int number_output) { +#if defined(INCLUDE_COMPRESSION) + assert(!R1.is_compressed()); + assert(!R2.is_compressed()); +#endif + assert(!R1.is_null() && !R2.is_null()); + Rel_Body *r1 = R1.split(); + Rel_Body *r2 = R2.split(); + + int in_req, out_req; // Create the new relation + get_relation_arity_from_mappings(mapping1, mapping2, in_req, out_req); + Relation R3(number_input == -1 ? in_req : number_input, + number_output == -1 ? out_req : number_output); + Rel_Body *r3 = R3.split(); // This is just to get the pointer, it's cheap + + /* permit the add_{exists,and} below, reset after they are done.*/ + skip_finalization_check++; + + F_Exists *fe = NULL; + Formula *f; + if (has_existentials(mapping1) || has_existentials(mapping2)) { + fe = r3->add_exists(); + f = fe; + } + else { + f = r3; + } + + r1->DNF_to_formula(); + Formula *f1 = r1->rm_formula(); + r2->DNF_to_formula(); + Formula *f2 = r2->rm_formula(); + + // align: change r1 vars to r3 vars in formula f1 via map mapping1, + // declaring needed exists vars in F_Exists *fe + // Also maps symbolic variables appropriately, sets relation ptrs in f1. + // In order to map variables of both relations to the same variables, + // we keep a list of new existentially quantified vars between calls. + // returnAsSet means mark r3 as set before return. Don't mark it yet, + // because internally we need to refer to "input_vars" of a set, and that + // would blow assertions. + + bool returnAsSet=false; + exists_ids.clear(); + exists_numbers.clear(); + align(r1, r3, fe, f1, mapping1, returnAsSet, exists_numbers, exists_ids); + // align: change r2 vars to r3 vars in formula f2 via map mapping2 + align(r2, r3, fe, f2, mapping2, returnAsSet, exists_numbers, exists_ids); + + switch (ctype) { + case Comb_Or: + if(f1->node_type() == Op_Or) { + f->add_child(f1); + f = f1; + } + else { + f = f->add_or(); + f->add_child(f1); + } + break; + case Comb_And: + case Comb_AndNot: + if(f1->node_type() == Op_And) { + f->add_child(f1); + f = f1; + } + else { + f = f->add_and(); + f->add_child(f1); + } + break; + default: + assert(0 && "Invalid combine type in MapAndCombineRel2"); + } + + Formula *c2; + if (ctype==Comb_AndNot) { + c2 = f->add_not(); + } + else { + c2 = f; + } + c2->add_child(f2); + + skip_finalization_check--; /* Set this back for return */ + R3.finalize(); + + if (returnAsSet || + (R1.is_set() && R2.is_set() && R3.n_inp() >= 0 && R3.n_out() == 0)){ + R3.markAsSet(); + R3.invalidate_leading_info(); + } + R1 = Relation(); + R2 = Relation(); + return R3; +} + + +// +// Scramble each relation's variables and merge these relations +// together. Support variable mapping to and from existentials. +// Unspecified variables in mapping are mapped to themselves by +// default. It intends to replace MapRel1 and MapAndCombineRel2 +// functions (the time saved by grafting formula tree might be +// neglegible when compared to the simplification cost). +// +Relation merge_rels(Tuple<Relation> &R, const Tuple<std::map<Variable_ID, std::pair<Var_Kind, int> > > &mapping, const Tuple<bool> &inverse, Combine_Type ctype, int number_input, int number_output) { + const int m = R.size(); + assert(mapping.size() == m && inverse.size() == m); + // skip_set_checks++; + + // if new relation's arity is not given, calculate it on demand + if (number_input == -1) { + number_input = 0; + for (int i = 1; i <= m; i++) { + for (int j = R[i].n_inp(); j >= 1; j--) { + Variable_ID v = R[i].input_var(j); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + number_input = j; + break; + } + } + + for (std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator j = mapping[i].begin(); j != mapping[i].end(); j++) { + if ((*j).second.first == Input_Var || (*j).second.first == Set_Var) + number_input = max(number_input, (*j).second.second); + } + } + } + + if (number_output == -1) { + number_output = 0; + for (int i = 1; i <= m; i++) { + for (int j = R[i].n_out(); j >= 1; j--) { + Variable_ID v = R[i].output_var(j); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + number_output = j; + break; + } + } + for (std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator j = mapping[i].begin(); j != mapping[i].end(); j++) { + if ((*j).second.first == Output_Var) + number_output = max(number_output, (*j).second.second); + } + } + } + + Relation R2(number_input, number_output); + F_Exists *fe = R2.add_exists(); + Formula *f_root; + switch (ctype) { + case Comb_And: + f_root = fe->add_and(); + break; + case Comb_Or: + f_root = fe->add_or(); + break; + default: + assert(0); // unsupported merge type + } + + std::map<int, Variable_ID> seen_exists_by_num; + std::map<Variable_ID, Variable_ID> seen_exists_by_id; + + for (int i = 1; i <= m; i++) { + F_Or *fo; + if (inverse[i]) + fo = f_root->add_not()->add_or(); + else + fo = f_root->add_or(); + + for (DNF_Iterator di(R[i].query_DNF()); di; di++) { + F_And *f = fo->add_and(); + + for (GEQ_Iterator gi(*di); gi; gi++) { + GEQ_Handle h = f->add_GEQ(); + for (Constr_Vars_Iter cvi(*gi); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + switch (v->kind()) { + // case Set_Var: + case Input_Var: { + int pos = v->get_position(); + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = v->get_position(); + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + std::map<Variable_ID, Variable_ID>::iterator p2 = seen_exists_by_id.find(cvi.curr_var()); + Variable_ID e; + if (p2 == seen_exists_by_id.end()) { + e = fe->declare(); + seen_exists_by_id[cvi.curr_var()] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = R2.get_local(g); + else + v2 = R2.get_local(g, v->function_of()); + h.update_coef(v2, cvi.curr_coef()); + break; + } + default: + assert(0); // shouldn't happen if input relations are simplified + } + } + else { + switch ((*p).second.first) { + // case Set_Var: + case Input_Var: { + int pos = (*p).second.second; + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = (*p).second.second; + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + int pos = (*p).second.second; + std::map<int, Variable_ID>::iterator p2 = seen_exists_by_num.find(pos); + Variable_ID e; + if (p2 == seen_exists_by_num.end()) { + e = fe->declare(); + seen_exists_by_num[pos] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + default: + assert(0); // mapped to unsupported variable type + } + } + } + h.update_const((*gi).get_const()); + } + + for (EQ_Iterator ei(*di); ei; ei++) { + EQ_Handle h = f->add_EQ(); + for (Constr_Vars_Iter cvi(*ei); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + std::map<Variable_ID, std::pair<Var_Kind, int> >::const_iterator p = mapping[i].find(v); + if (p == mapping[i].end()) { + switch (v->kind()) { + // case Set_Var: + case Input_Var: { + int pos = v->get_position(); + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = v->get_position(); + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + std::map<Variable_ID, Variable_ID>::iterator p2 = seen_exists_by_id.find(v); + Variable_ID e; + if (p2 == seen_exists_by_id.end()) { + e = fe->declare(); + seen_exists_by_id[v] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = v->get_global_var(); + Variable_ID v2; + if (g->arity() == 0) + v2 = R2.get_local(g); + else + v2 = R2.get_local(g, v->function_of()); + h.update_coef(v2, cvi.curr_coef()); + break; + } + default: + assert(0); // shouldn't happen if input relations are simplified + } + } + else { + switch ((*p).second.first) { + // case Set_Var: + case Input_Var: { + int pos = (*p).second.second; + h.update_coef(R2.input_var(pos), cvi.curr_coef()); + break; + } + case Output_Var: { + int pos = (*p).second.second; + h.update_coef(R2.output_var(pos), cvi.curr_coef()); + break; + } + case Exists_Var: + case Wildcard_Var: { + int pos = (*p).second.second; + std::map<int, Variable_ID>::iterator p2 = seen_exists_by_num.find(pos); + Variable_ID e; + if (p2 == seen_exists_by_num.end()) { + e = fe->declare(); + seen_exists_by_num[pos] = e; + } + else + e = (*p2).second; + h.update_coef(e, cvi.curr_coef()); + break; + } + default: + assert(0); // mapped to unsupported variable type + } + } + } + h.update_const((*ei).get_const()); + } + } + } + + // skip_set_checks--; + + if (number_output == 0) { + R2.markAsSet(); + // R2.invalidate_leading_info(); + } + + return R2; +} + +} // namespace diff --git a/omegalib/omega/src/basic/ConstString.cc b/omegalib/omega/src/basic/ConstString.cc new file mode 100644 index 0000000..7d2ec1e --- /dev/null +++ b/omegalib/omega/src/basic/ConstString.cc @@ -0,0 +1,134 @@ +#include <basic/ConstString.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <string> +#include <string.h> + +/* static const int CS_HashTable_Size = 1000; */ +/* static ConstStringRep *hashTable[CS_HashTable_Size] = {0}; */ + +namespace omega { + +const int CS_HashTable_Size = 1000; +class CS_HashTable { +public: + ConstStringRep *p[CS_HashTable_Size]; + CS_HashTable(); + ~CS_HashTable(); +}; + +namespace { + CS_HashTable hashTable; +} + +CS_HashTable::CS_HashTable() { + for (int i = 0; i < CS_HashTable_Size; i++) + p[i] = NULL; + } + +CS_HashTable::~CS_HashTable() { + for (int i = 0; i < CS_HashTable_Size; i++) { + ConstStringRep *t = p[i]; + while (t != NULL) { + ConstStringRep *tt = t->nextInBucket; + delete []t->name; + delete t; + t = tt; + } + } +} + +Const_String::Const_String() { + rep = 0; +} + +void Const_String::buildRep(const char* t) { + int hash = 0; + const char *s = t; + while (*s != '\0') + hash = hash*33 + *s++; + int hashBucket = hash % CS_HashTable_Size; + if (hashBucket < 0) hashBucket += CS_HashTable_Size; + assert(0 <= hashBucket && hashBucket < CS_HashTable_Size); + ConstStringRep **q = &(hashTable.p[hashBucket]); + ConstStringRep *p = *q; + while (p != 0) { + if (strcmp(p->name,t) == 0) break; + q = &p->nextInBucket; + p = *q; + } + if (p!= 0) rep = p; + else { + rep = new ConstStringRep(t); + *q = rep; + } +} + +Const_String::Const_String(const char * t) { + buildRep(t); +} + +Const_String::Const_String(const std::string &s) { + buildRep(s.c_str()); +} + +Const_String::operator const char*() const { + if (!rep) return 0; + return rep->name; +} + +Const_String::operator std::string() const { + if (!rep) return std::string(""); + return std::string(rep->name); +} + +int Const_String::operator++(int) { + return rep->count++; +} + +int Const_String::operator++() { + return ++rep->count; +} + +int Const_String:: operator--(int) { + return rep->count--; +} + +int Const_String:: operator--() { + return --rep->count; +} + +int operator ==(const Const_String &x, const Const_String &y) { + return x.rep == y.rep; +} + +int operator !=(const Const_String &x, const Const_String &y) { + return x.rep != y.rep; +} + +int operator <(const Const_String &x, const Const_String &y) { + return (strcmp(x.rep->name,y.rep->name) < 0); +} + +int operator >(const Const_String &x, const Const_String &y) { + return (strcmp(x.rep->name,y.rep->name) > 0); +} + +Const_String:: operator int() const { + return rep != 0; +} + +int Const_String::null() const { + return rep == 0; +} + +ConstStringRep:: ConstStringRep(const char *t) { + count = 0; + nextInBucket = 0; + char *s = new char[1+strlen(t)]; + strcpy(s,t); + name = s; +} + +} // namespace diff --git a/omegalib/omega/src/basic/Link.cc b/omegalib/omega/src/basic/Link.cc new file mode 100644 index 0000000..50b9441 --- /dev/null +++ b/omegalib/omega/src/basic/Link.cc @@ -0,0 +1,41 @@ +#include <basic/Link.h> + +namespace omega { + +#if ListElementFreeList + static List_Element<void*> *_kludgy_List_Element_free_list_pointer; +// we rely on the fact that that is initialized to 0 before any +// constructor-based initialization that could call List_Element::new. + + void *kludgy_List_Element_new(size_t size) + { + void *mem; + if (size == sizeof(List_Element<void*>) && + _kludgy_List_Element_free_list_pointer) + { + List_Element<void*> *it = _kludgy_List_Element_free_list_pointer; + _kludgy_List_Element_free_list_pointer = it->tail; + mem = it; + } + else + mem = ::operator new(size); + + return mem; + } + + void kludgy_List_Element_delete(void *ptr, size_t size) + { + if (ptr) + if (size == sizeof(List_Element<void*>)) + { + List_Element<void*> *it = (List_Element<void*> *) ptr; + it->tail = _kludgy_List_Element_free_list_pointer; + _kludgy_List_Element_free_list_pointer = it; + } + else + ::operator delete(ptr); + } + +#endif + +} // namespace diff --git a/omegalib/omega/src/closure.cc b/omegalib/omega/src/closure.cc new file mode 100644 index 0000000..416a3e7 --- /dev/null +++ b/omegalib/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 diff --git a/omegalib/omega/src/evac.cc b/omegalib/omega/src/evac.cc new file mode 100644 index 0000000..ff872c9 --- /dev/null +++ b/omegalib/omega/src/evac.cc @@ -0,0 +1,339 @@ +#if defined STUDY_EVACUATIONS + +#include <omega/Relations.h> +#include <omega/pres_conj.h> +#include <omega/evac.h> +#include <omega/omega_core/debugging.h> +#include <omega/omega_core/oc_i.h> + +namespace omega { + +int evac_debug = 0; + +char *evac_names[] = { "trivial", + "offset", + "subseq", + "off_sub", +// "perm.", + "affine", + "nasty" }; + +int single_evacs[evac_nasty+1]; +int double_evacs[evac_nasty+1][evac_nasty+1]; + +/* + * We're going to try to describe the equalities among a set of variables + * We want to perform some substitutions to ensure that we don't miss + * v_1 = v_2 due to its expression as v_1 = v_3 && v_2 = v_3 + * We therefore try to substitute out all variables that we don't care + * about (e.g., v_3 in the above example). + */ + +static bool try_to_sub(Problem *p, int col) { + int e, i; + + if (!p->variablesInitialized) { + p->initializeVariables(); + } + + assert(col <= p->nVars); + assert(!inApproximateMode); + + for(e=0;e<p->nEQs;e++) + if (p->EQs[e].coef[col] == 1 || p->EQs[e].coef[col] == -1) { + int var = p->var[col]; + p->doElimination(e, col); + if (col != p->nVars + 1) + p->forwardingAddress[p->var[p->nVars+1]] = col; + assert(p->SUBs[p->nSUBs-1].key = var); + p->forwardingAddress[var] = -p->nSUBs; + break; + } + + if (e == p->nEQs) + return false; + + for (int c=0;c<=p->nVars;c++) { + assert(p->EQs[e].coef[c] == 0); + } + + p->nEQs--; + if (e < p->nEQs) eqnncpy(&p->EQs[e], &p->EQs[p->nEQs], p->nVars); + + for (i = 0; i < p->nSUBs; i++) { + assert(p->forwardingAddress[p->SUBs[i].key] == -i - 1); + } + + return true; +} + + +// should be static, but must be a friend +bool check_subseq_n(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity, int n, bool allow_offset) { + // check each position v to see if from[v] == to[v+n] (+ offset) + + assert(max_arity + n <= n_to); + + for (int v = 1; v <= max_arity; v++){ + // first, get rid of possible interlopers: + int col; + Conjunct *d = c->copy_conj_same_relation(); + for (int tv = 1; tv <= n_to; tv++) + if (tv != v+n) + if ((col = d->find_column(evac_to[tv])) > 0) + try_to_sub(d->problem, col); + for (int fv = 1; fv <= n_from; fv++) + if (fv != v) + if ((col = d->find_column(evac_from[fv])) > 0) + try_to_sub(d->problem, col); + + int c_to = d->find_column(evac_to[v+n]); + int c_from = d->find_column(evac_from[v]); + assert(c_to > 0); + assert(c_from > 0); + assert(c_to != c_from); + + // now, just look for an equality c_to = c_from + offset + + bool found_needed_eq = false; + + for (int e = 0; e < d->problem->nEQs; e++) { + if (d->problem->EQs[e].coef[c_from] != 0) { + for (int k = allow_offset?1:0; k < d->problem->nVars; k++) + if (k!=c_to && k!=c_from && d->problem->EQs[e].coef[k]!=0) + break; // this EQ is not what we need + if (k == d->problem->nVars) { // this EQ is what we need + found_needed_eq = true; + break; + } + } + } + + delete d; + + if (!found_needed_eq) + return false; // no EQ did what we need + } + + return true; +} + +void assert_subbed_syms(Conjunct *c) { + int v, col; + + // where possible, symbolic constants must have been subbed out + for (v = 1; v <= c->relation()->global_decls()->length(); v++) + if ((col = c->find_column((*c->relation()->global_decls())[v]))>0) + assert(!try_to_sub(c->problem, col)); +} + + +static bool check_offset(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert_subbed_syms(c); + + return check_subseq_n(c,evac_from,evac_to,n_from,n_to,max_arity,0,true); +} + +static bool check_subseq(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert_subbed_syms(c); + + for (int i = 0; i <= n_to - max_arity; i++) + if (check_subseq_n(c,evac_from,evac_to,n_from,n_to,max_arity,i,false)) + return true; + + return false; +} + +static bool check_offset_subseq(Conjunct *c, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert_subbed_syms(c); + + for (int i = 0; i <= n_to - max_arity; i++) + if (check_subseq_n(c,evac_from,evac_to,n_from,n_to,max_arity,i,true)) + return true; + + return false; +} + +bool check_affine(Conjunct *d, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + int v, col; + Conjunct *c = d->copy_conj_same_relation(); + assert_subbed_syms(c); + + // try to find substitutions for all evac_to variables + for (v = 1; v <= max_arity; v++) + if ((col = c->find_column(evac_to[v])) > 0) + try_to_sub(c->problem, col); + + // any that didn't have substitutions, aren't affine + for (v = 1; v <= max_arity; v++) + if (c->find_column(evac_to[v]) >= 0) { + delete c; + return false; + } + + // FERD - disallow symbolic constants? + delete c; + return true; +} + + +evac study(Conjunct *C, Sequence<Variable_ID> &evac_from, Sequence<Variable_ID> &evac_to, int n_from, int n_to, int max_arity) { + assert(max_arity > 0); + assert(max_arity <= C->relation()->n_inp()); + assert(max_arity <= C->relation()->n_out()); + + assert((&evac_from == &input_vars && &evac_to == &output_vars) || + (&evac_from == &output_vars && &evac_to == &input_vars)); + + evac ret = evac_nasty; + + if (C->query_guaranteed_leading_0s() >= max_arity) + ret = evac_trivial; + else { + Conjunct *c = C->copy_conj_same_relation(); + assert(c->relation() == C->relation()); + + if (evac_debug >= 3) { + fprintf(DebugFile, "About to study %s evacuation for conjunct\n", + &evac_from == &input_vars ? "In-->Out" : "Out-->In"); + use_ugly_names++; + C->prefix_print(DebugFile); + use_ugly_names--; + } + + bool sat = simplify_conj(c, true, 4, black); + assert(sat); // else c is deleted + + int v, col; + + // Substitute out all possible symbolic constants + assert(c->problem->nSUBs == 0); + for (v = 1; v <= c->relation()->global_decls()->length(); v++) + if ((col = c->find_column((*c->relation()->global_decls())[v]))>0) + try_to_sub(c->problem, col); + + if (check_offset(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_offset; + else if (check_subseq(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_subseq; + else if (check_offset_subseq(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_offset_subseq; + else if (check_affine(c, evac_from, evac_to, n_from, n_to, max_arity)) + ret = evac_affine; + + delete c; + } + + if (evac_debug >= 2) { + if ((evac_debug == 2 && ret != evac_trivial && ret != evac_nasty)) { + fprintf(DebugFile, "Studied %s evacuation for conjunct\n", + &evac_from == &input_vars ? "In-->Out" : "Out-->In"); + use_ugly_names++; + C->prefix_print(DebugFile); + use_ugly_names--; + } + + fprintf(DebugFile, "Saw evacuation type %s\n", evac_names[ret]); + } + + return ret; +} + + +void study_evacuation(Conjunct *C, which_way dir, int max_arity) { + if (evac_debug > 0) { + assert(max_arity >= 0); + + if (max_arity > 0) + if (dir == in_to_out) { + assert(max_arity <= C->relation()->n_inp()); + if (max_arity <= C->relation()->n_out()) + single_evacs[study(C, input_vars, output_vars, + C->relation()->n_inp(), + C->relation()->n_out(), + max_arity)]++; + } + else { + assert(max_arity <= C->relation()->n_out()); + if (max_arity <= C->relation()->n_inp()) + single_evacs[study(C, output_vars, input_vars, + C->relation()->n_out(), + C->relation()->n_inp(), + max_arity)]++; + } + } +} + +void study_evacuation(Conjunct *C1, Conjunct *C2, int max_arity) { + if (evac_debug > 0) { + assert(max_arity >= 0); + assert(max_arity <= C1->relation()->n_inp()); + assert(C2->relation()->n_out() == C1->relation()->n_inp()); + + if (max_arity > 0) + if (max_arity <= C1->relation()->n_out() && + max_arity <= C2->relation()->n_inp()) { + double_evacs[study(C1, input_vars, output_vars, + C1->relation()->n_inp(), + C1->relation()->n_out(), + max_arity)] + [study(C2, output_vars, input_vars, + C2->relation()->n_out(), + C2->relation()->n_inp(), + max_arity)]++; + } + else if (max_arity <= C1->relation()->n_out()) { + single_evacs[study(C1, input_vars, output_vars, + C1->relation()->n_inp(), + C1->relation()->n_out(), + max_arity)]++; + } + else if (max_arity <= C2->relation()->n_inp()) { + single_evacs[study(C2, output_vars, input_vars, + C2->relation()->n_out(), + C2->relation()->n_inp(), + max_arity)]++; + } + } +} + +class Evac_info_printer { +public: + ~Evac_info_printer(); +}; + +Evac_info_printer::~Evac_info_printer() { + if (evac_debug > 0) { + int i, j; + + fprintf(DebugFile, "\n"); + + fprintf(DebugFile, "SINGLE"); + for (i = 0; i <= evac_nasty; i++) + fprintf(DebugFile, "\t%s", evac_names[i]); + fprintf(DebugFile, "\n"); + + for (i = 0; i <= evac_nasty; i++) + fprintf(DebugFile, "\t%d", single_evacs[i]); + fprintf(DebugFile, "\n\n"); + + + fprintf(DebugFile, "DOUBLE"); + for (i = 0; i <= evac_nasty; i++) + fprintf(DebugFile, "\t%s", evac_names[i]); + fprintf(DebugFile, "\n"); + + for (i = 0; i <= evac_nasty; i++) { + fprintf(DebugFile, "%s\t", evac_names[i]); + for (j = 0; j <= evac_nasty; j++) + fprintf(DebugFile, "%d\t", double_evacs[i][j]); + fprintf(DebugFile, "\n"); + } + } +} + +static Evac_info_printer print_stats_at_exit; + +} // namespace + +#endif diff --git a/omegalib/omega/src/farkas.cc b/omegalib/omega/src/farkas.cc new file mode 100644 index 0000000..1b3ef87 --- /dev/null +++ b/omegalib/omega/src/farkas.cc @@ -0,0 +1,480 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + convert to dual cone for manipulation. + + Notes: + + History: +*****************************************************************************/ + +#include <basic/Bag.h> +#include <basic/Map.h> +#include <omega.h> +#include <omega/farkas.h> + +namespace omega { + +static Global_Var_Decl constant_term("constantTerm"); + +// class Constant_Term { +// public: +// Global_Var_Decl *p; + +// Constant_Term(); +// ~Constant_Term(); +// }; + +// namespace { +// Constant_Term constant_term; +// } + +// Constant_Term::Constant_Term() { +// p = new Global_Var_Decl("constantTerm"); +// } + +// Constant_Term::~Constant_Term() { +// delete p; +// } + +// Global_Var_ID coefficient_of_constant_term = constant_term.p; + +Global_Var_ID coefficient_of_constant_term = &constant_term; + +extern int inApproximateMode; + +int farkas_debug = 0; + +coef_t farkasDifficulty; + +//***************************************************************************** +// +// forall x1,..,xn s.t. a10 + a11 x1 + ... + a1n xn >= 0 and +// ... +// am0 + am1 x1 + ... + amn xn >= 0 +// +// b0 + b1 x1 + ... + bn xn >= 0 +// +// iff +// +// exists lambda_0,...,lambda_m >= 0 s.t. +// forall x1,..,xn +// lambda_0 + +// lambda_1 ( a10 + a11 x1 + ... + a1n xn) + +// ... +// lambda_m ( am0 + am1 x1 + ... + amn xn) = +// +// b0 + b1 x1 + ... + bn xn +// +// iff +// +// exists lambda_0,...,lambda_m >= 0 s.t. +// lambda_0 + sum_i ( lambda_i a_i0 ) = b_0 +// for j in 1..n +// sum_i ( a_ij lambda_i ) = b_j +// +// iff +// +// exists lambda0,...,lambda_m s.t. +// lambda1,...,lambda_m >= 0 +// lambda0 >= 0 +// lambda_0 = b_0 - sum_i ( lambda_i a_i0 ) +// for j in 1..n +// sum_i ( a_ij lambda_i ) = b_j +// iff +// +// exists lambda1,...,lambda_m s.t. +// lambda1,...,lambda_m >= 0 +// b_0 - sum_i ( lambda_i a_i0 ) >= 0 +// for j in 1..n +// sum_i ( a_ij lambda_i ) = b_j +// +// a_ij come from relation rel +// +// x_1,...,x_n are input and output variables from rel. +// +// b_0,...,b_m are input and output arrays of coef_vars +// +//***************************************************************************** + + +// Given a Relation/Set R +// Compute A,B,C such that +// Ax+By + C >= 0 is true for all x,y in R +// iff [A,B] : constantTerm=C is in AffineClosure(R) +// Note: constantTerm is a special global variable +// If constantTerm appears in the incoming relation +// then set it's coefficient to be 1 in the result + + +// # For example, given +// R := {[i,j] : 1 <= i <= 10 && 1 <= j <= n}; +// # the farkas closure of R is: +// # ac := approximate {[i,j] : exists (lambda0, lambda1,lambda2,lambda3,lambda4 : +// # 0 <= lambda1,lambda2,lambda3,lambda4 +// # && constantTerm - (-lambda1+ 10 lambda2 - lambda3) >= 0 +// # && i = lambda1-lambda2 +// # && j = lambda3-lambda4 +// # && n = lambda4)}; +// # +// # ac; +// +// {[i,j]: 0 <= n && 0 <= n+constantTerm+i+j +// && 0 <= n+constantTerm+10i+j && 0 <= n+j} +// +// The ConvexCombination of ac is: +//# +//# approximate {[i,j] : exists (lambda1,lambda2,lambda3,lambda4 : +//# 0 <= lambda1,lambda2,lambda3,lambda4 +//# && 1 = lambda2+lambda3 +//# && i = lambda2+10lambda3 +//# && j = lambda2+lambda3+lambda4 +//# && n = lambda1+lambda2+lambda3+lambda4 +//# )}; +// +//{[i,j]: 1 <= i <= 10 && 1 <= j <= n} +// + +static void handleVariable(Relation &farkas, Conjunct * conj, + F_And* and_node, + Map<GEQ_Handle, Variable_ID> &gMap, + Map<EQ_Handle, Variable_ID> &eMap, + Variable_ID v) { + use_ugly_names++; + if (farkas_debug > 1) { + fprintf(DebugFile,"Building equality for %s\n", v->name().c_str()); + } + + EQ_Handle e = and_node->add_EQ(); + + for (GEQ_Iterator g = conj->GEQs(); g.live(); g.next()) + if (gMap(*g) != (Variable_ID) 0) { + coef_t c = (*g).get_coef(v); + if (c != 0) { + e.update_coef(gMap(*g), c); + } + } + + for (EQ_Iterator eq = conj->EQs(); eq.live(); eq.next()) + if (eMap(*eq) != (Variable_ID) 0) { + coef_t c = (*eq).get_coef(v); + if (c != 0) { + e.update_coef(eMap(*eq), c); + } + } + + if ((v)->kind() == Global_Var && + (v)->get_global_var() == coefficient_of_constant_term) + e.update_const(-1); + else + e.update_coef(farkas.get_local(v), -1); + + e.finalize(); + if (farkas_debug > 1) { + fprintf(DebugFile,"Constraint is %s\n", e.print_to_string().c_str()); + } + use_ugly_names--; +} + + +Relation Farkas(NOT_CONST Relation &input_R, Farkas_Type op, bool early_bailout) { + assert(!input_R.is_null()); + int saved_use_ugly_names = use_ugly_names; + + use_ugly_names++; + farkasDifficulty = 0; + + Relation R = consume_and_regurgitate(input_R); + + if (op == Basic_Farkas || op == Decoupled_Farkas) { + R.simplify(2, 4); + R = Approximate(R, false); + } + + Relation result = Relation::False(R); + + if (farkas_debug) { + fprintf(DebugFile,"Computing farka of: [\n"); + R.prefix_print(DebugFile); + } + + Variable_ID_Tuple vars; + for (Variable_ID_Iterator v(*R.global_decls()); v; v++) vars.append(*v); + if (R.is_set()) + for(int i=1; i <= R.n_set(); i++) vars.append(R.set_var(i)); + else { + int i; + for(i=1; i <= R.n_inp(); i++) vars.append(R.input_var(i)); + for(i=1; i <= R.n_out(); i++) vars.append(R.output_var(i)); + } + + Set<Variable_ID> empty; + Variable_ID_Tuple owners; + Map<Variable_ID, Set<Variable_ID> > connectedVariables(empty); + + if (op == Decoupled_Farkas) { + for (Variable_ID_Iterator v(*R.global_decls()); v; v++) + if ((*v)->kind() == Global_Var) { + Global_Var_ID g = (*v)->get_global_var(); + if (g->arity() > 0) { + if (R.is_set()) + for(int i=1; i <= g->arity(); i++) + (*v)->UF_union(R.set_var(i)); + else if ((*v)->function_of() == Input_Tuple) + for(int i=1; i <= g->arity(); i++) + (*v)->UF_union(R.input_var(i)); + else + for(int i=1; i <= g->arity(); i++) + (*v)->UF_union(R.output_var(i)); + } + } + + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) { + for (Variable_ID_Iterator v1(*(*s)->variables()); v1; v1++) { + for (EQ_Iterator eq = (*s)->EQs(); eq.live(); eq.next()) + if ((*eq).get_coef(*v1)) + for (Variable_ID_Iterator v2(*(*s)->variables()); v2; v2++) + if ((*eq).get_coef(*v2)) + (*v1)->UF_union(*v2); + for (GEQ_Iterator g = (*s)->GEQs(); g.live(); g.next()) + if ((*g).get_coef(*v1)) + for (Variable_ID_Iterator v2(*(*s)->variables()); v2; v2++) + if ((*g).get_coef(*v2)) + (*v1)->UF_union(*v2); + } + } + for (Variable_ID_Iterator v3(vars); v3.live(); v3.next()) + connectedVariables[(*v3)->UF_owner()] |= *v3; + + foreach_map(v,Variable_ID,s,Set<Variable_ID>,connectedVariables, + owners.append(v); + if (farkas_debug) { + fprintf(DebugFile,"%s:",v->char_name()); + foreach(v2,Variable_ID,s, + fprintf(DebugFile," %s",v2->char_name()); + ); + fprintf(DebugFile,"\n"); + } + ); + } + + Variable_ID_Iterator varGroup(owners); + int lambda_cnt = 1; + + Relation partialResult; + bool firstGroup = true; + try { + while ((op == Decoupled_Farkas && varGroup.live()) + || (op != Decoupled_Farkas && firstGroup)) { + + if (farkas_debug && op == Decoupled_Farkas) { + fprintf(DebugFile,"[Computing decoupled farkas for:"); + foreach(v2,Variable_ID,connectedVariables(varGroup.curr()), + fprintf(DebugFile," %s",v2->char_name()); + ); + fprintf(DebugFile,"\n"); + } + firstGroup = false; + partialResult = Relation::True(R); + coef_t difficulty = 0; + for (DNF_Iterator s(R.query_DNF()); s.live(); s.next()) { + int nz; + coef_t m,sum; + (*s)->difficulty(nz,m,sum); + difficulty = max((coef_t) nz,2*nz+2*m+sum); + if (farkas_debug) { + fprintf(DebugFile,"Computing farka of conjunct: \n"); + (*s)->prefix_print(DebugFile); + fprintf(DebugFile,"Difficulty is " coef_fmt "(%d," coef_fmt "," coef_fmt ")\n", difficulty,nz,m,sum); + } + if (early_bailout && difficulty >= 500) { + farkasDifficulty = difficulty; + if (farkas_debug) { + fprintf(DebugFile,"Too ugly, returning dull result\n"); + } + use_ugly_names--; + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else return Relation::True(partialResult); + } + Relation farkas = Relation::Empty(R); + farkas.copy_names(R); + F_Exists* exist = farkas.add_exists(); + F_And* and_node = exist->add_and(); + Map<GEQ_Handle, Variable_ID> gMap((Variable_ID)0); + Map<EQ_Handle, Variable_ID> eMap((Variable_ID)0); + for (EQ_Iterator eq = (*s)->EQs(); eq.live(); eq.next()) { + if (op == Decoupled_Farkas) { + bool ShouldConsider = true; + for (Variable_ID_Iterator v(*(*s)->variables()); v; v++) { + if ((*eq).get_coef(*v) != 0 + && (*v)->UF_owner() != varGroup.curr()) { + ShouldConsider = false; + break; + } + } + if (!ShouldConsider) continue; + } + char s[10]; + sprintf(s, "lambda%d", lambda_cnt++); + eMap[*eq] = exist->declare(s); + assert(op == Basic_Farkas || op == Decoupled_Farkas + || (*eq).get_const() == 0); + } + for (GEQ_Iterator g = (*s)->GEQs(); g.live(); g.next()) { + if (op == Decoupled_Farkas) { + bool ShouldConsider = true; + for (Variable_ID_Iterator v(*(*s)->variables()); v; v++) { + if ((*g).get_coef(*v) != 0 + && (*v)->UF_owner() != varGroup.curr()) { + ShouldConsider = false; + break; + } + } + if (!ShouldConsider) continue; + } + char s[10]; + sprintf(s, "lambda%d", lambda_cnt++); + Variable_ID lambda = exist->declare(s); + GEQ_Handle positive; + switch(op) { + case Positive_Combination_Farkas: + case Convex_Combination_Farkas: + case Basic_Farkas: + case Decoupled_Farkas: + positive = and_node->add_GEQ(); + positive.update_coef(lambda, 1); + positive.finalize(); + break; + case Linear_Combination_Farkas: + case Affine_Combination_Farkas: + break; + } + gMap[*g] = lambda; + assert(op == Basic_Farkas || op == Decoupled_Farkas || (*g).get_const() == 0); + } + + for (Variable_ID_Iterator v(vars); v; v++) { + assert((*v)->kind() != Wildcard_Var); + if ((*v)->kind() == Global_Var + && (*v)->get_global_var() == coefficient_of_constant_term) { + assert(op != Basic_Farkas && op != Decoupled_Farkas); + if (op == Linear_Combination_Farkas) continue; + if (op == Positive_Combination_Farkas) continue; + } + if (op == Decoupled_Farkas && (*v)->UF_owner() != varGroup.curr()) { + EQ_Handle e = and_node->add_EQ(); + e.update_coef(farkas.get_local(*v),-1); + continue; + } + handleVariable(farkas, *s, and_node, gMap,eMap, *v); + } + + if (op == Basic_Farkas || op == Decoupled_Farkas) { + GEQ_Handle e = and_node->add_GEQ(); + e.update_coef(farkas.get_local(coefficient_of_constant_term),1); + for (GEQ_Iterator g = s.curr()->GEQs(); g.live(); g.next()) + if (gMap(*g) != (Variable_ID) 0) + e.update_coef( gMap(*g),-(*g).get_const()); + for (EQ_Iterator eq = s.curr()->EQs(); eq.live(); eq.next()) + if (eMap(*eq) != (Variable_ID) 0) + e.update_coef(eMap(*eq),-(*eq).get_const()); + e.finalize(); + } + + // lambda variables are not integers, so disable integer problem solving, + // we just mark it as simplified. + farkas.simplify(-1, -1); + + farkas.single_conjunct()->difficulty(nz,m,sum); + difficulty = max((coef_t) nz,2*nz+2*m+sum); + if (farkas_debug) { + fprintf(DebugFile,"farka has difficulty " coef_fmt "(%d," coef_fmt "," coef_fmt "):\n", difficulty,nz,m,sum); + farkas.prefix_print(DebugFile); + } + if (early_bailout && difficulty >= 500) { + farkasDifficulty = difficulty; + if (farkas_debug) { + fprintf(DebugFile,"Too ugly, returning dull result\n"); + } + use_ugly_names--; + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else return Relation::True(partialResult); + } + farkas = Approximate(farkas); + if (farkas_debug) { + fprintf(DebugFile,"simplified:\n"); + farkas.prefix_print(DebugFile); + } + partialResult = Approximate(Intersection(partialResult,farkas)); + if (farkas_debug) { + fprintf(DebugFile,"combined:\n"); + partialResult.prefix_print(DebugFile); + } + if (partialResult.has_single_conjunct()) { + partialResult.single_conjunct()->difficulty(nz,m,sum); + difficulty = max((coef_t) nz,2*nz+2*m+sum); + } + else + difficulty = 1000; + if (early_bailout && difficulty >= 500) { + farkasDifficulty = difficulty; + if (farkas_debug) { + fprintf(DebugFile,"Too ugly, returning dull result\n"); + } + use_ugly_names--; + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else return Relation::True(partialResult); + } + } + farkasDifficulty += difficulty; + + if (farkas_debug) { + fprintf(DebugFile,"] done computing farkas\n"); + partialResult.prefix_print(DebugFile); + } + + if (op == Decoupled_Farkas) { + result = Union(result,partialResult); + varGroup.next(); + } + } + } + catch (const std::overflow_error &e) { + // clear global variables + inApproximateMode = 0; + use_ugly_names = saved_use_ugly_names; + + if (early_bailout) { + if (farkasDifficulty < 1000) + farkasDifficulty = 1000; + // return dull result + if (op == Basic_Farkas || op == Decoupled_Farkas) + return Relation::False(partialResult); + else + return Relation::True(partialResult); + } + else + throw std::overflow_error("farkas too ugly"); + } + + if (1 || op == Decoupled_Farkas) { + foreach(v,Variable_ID,vars, reset_remap_field(v)); + } + use_ugly_names--; + if (op == Decoupled_Farkas) { + if (farkas_debug) { + fprintf(DebugFile,"] decoupled result:\n"); + result.prefix_print(DebugFile); + } + return result; + } + return partialResult; +} + +} // namespace diff --git a/omegalib/omega/src/hull.cc b/omegalib/omega/src/hull.cc new file mode 100644 index 0000000..f1b0601 --- /dev/null +++ b/omegalib/omega/src/hull.cc @@ -0,0 +1,1489 @@ +/***************************************************************************** + 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 <omega.h> +#include <omega/farkas.h> +#include <omega/hull.h> +#include <basic/Bag.h> +#include <basic/Map.h> +#include <basic/omega_error.h> +#include <list> +#include <vector> +#include <set> + +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<Variable_ID> 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<Relation> Rs(1); + Rs[1] = R; + return QuickHull(Rs); +} + + +// This function is deprecated!!! +Relation QuickHull(Tuple<Relation> &Rs) { + assert(!Rs.empty()); + + // if (Rs.size() == 1) return Rs[1]; + + Tuple<Relation> 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<Relation> &Rs, Tuple<int> &active) { +// assert(Rs.size() == active.size() && Rs.size() > 0); + +// Tuple<Relation> 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<Relation> &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<Relation> &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<bool> 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<Relation> 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<Relation> &Rs, + Tuple<int> &validMask, + int effort, + bool stridesAllowed, + NOT_CONST Relation &knownHull) { + // Use relation of index i only when validMask[i] != 0 + Tuple<Relation> 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<Relation> 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<std::pair<Relation, Relation> >::iterator pos; + coef_t lb; + coef_t ub; + bool change; + coef_t modulo; + Interval(std::list<std::pair<Relation, Relation> >::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<Interval> &intervals, coef_t modulo, std::list<std::pair<Relation, Relation> > &Rs, std::list<std::pair<Relation, Relation> >::iterator orig) { + // normalize intervals + for (std::list<Interval>::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<Interval>::iterator p = intervals.begin(); + while (p != intervals.end()) { + std::list<Interval>::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_Interval> modulo_intervals; + coef_t max_distance = modulo/2; + for (std::list<Interval>::iterator p = intervals.begin(); p != intervals.end(); p++) { + if ((*p).lb >= max_distance) + break; + + coef_t size = (*p).ub - (*p).lb; + + std::list<Interval>::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<Interval>::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<Modulo_Interval>::iterator p2 = modulo_intervals.begin(); + while (p2 != modulo_intervals.end()) { + std::list<Modulo_Interval>::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<Modulo_Interval>::iterator i = modulo_intervals.begin(); i != modulo_intervals.end(); i++) { + std::vector<Relation *> candidates; + int num_replaced = 0; + for (std::list<Interval>::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<Interval>::iterator replaced_one = intervals.end(); + for (std::list<Interval>::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<std::pair<Relation, Relation> > 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<std::pair<Relation, Relation> >::iterator i = Rs.begin(); + while (i != Rs.end()) { + // find regions with identical hole conditions to merge + { + std::list<std::pair<Relation, Relation> >::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<std::list<std::pair<Relation, Relation> >::iterator> s; + for (std::list<std::pair<Relation, Relation> >::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<Variable_ID> 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<Interval> intervals; + intervals.push_back(Interval(i, lb, (*gei).get_const())); + + for (std::list<std::list<std::pair<Relation, Relation> >::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<Interval>::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<Interval>::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<std::pair<Relation, Relation> >::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<std::string> 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<std::string> 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<std::pair<coef_t, coef_t> > bounds1(R.n_inp()); + std::vector<std::pair<coef_t, coef_t> > bounds2(R.n_out()); + { + Relation t = Project_Sym(copy(r)); + t.simplify(); + for (int i = 1; i <= R.n_inp(); i++) { + Tuple<Variable_ID> 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<Variable_ID> 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<Variable_ID> 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<Variable_ID> 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 diff --git a/omegalib/omega/src/hull_legacy.cc b/omegalib/omega/src/hull_legacy.cc new file mode 100755 index 0000000..a59d34f --- /dev/null +++ b/omegalib/omega/src/hull_legacy.cc @@ -0,0 +1,1484 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Legacy hull calculations' implementation. + + Notes: + + History: + 06/15/09 ConvexRepresentation, Chun Chen + 11/25/09 RectHull, Chun Chen +*****************************************************************************/ + +#include <omega.h> +#include <omega/farkas.h> +#include <omega/hull.h> +#include <basic/Bag.h> +#include <basic/omega_error.h> +#include <list> +#include <vector> +#include <set> + +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<Variable_ID> 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<Relation> Rs(1); + Rs[1] = R; + return QuickHull(Rs); +} + + +// This function is deprecated!!! +Relation QuickHull(Tuple<Relation> &Rs) { + assert(!Rs.empty()); + + // if (Rs.size() == 1) return Rs[1]; + + Tuple<Relation> 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<Relation> &Rs, Tuple<int> &active) { +// assert(Rs.size() == active.size() && Rs.size() > 0); + +// Tuple<Relation> 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<Relation> &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<Relation> &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<bool> 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<Relation> 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<Relation> &Rs, + const std::vector<bool> &validMask, + int effort, + bool stridesAllowed, + NOT_CONST Relation &knownHull) { + // Use relation of index i only when validMask[i] != 0 + Tuple<Relation> Rs2; + for(int i = 1; i <= Rs.size(); i++) { + if (validMask[i-1]) { + 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<Relation> 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<std::pair<Relation, Relation> >::iterator pos; + coef_t lb; + coef_t ub; + bool change; + coef_t modulo; + Interval(std::list<std::pair<Relation, Relation> >::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<Interval> &intervals, coef_t modulo, std::list<std::pair<Relation, Relation> > &Rs, std::list<std::pair<Relation, Relation> >::iterator orig) { + // normalize intervals + for (std::list<Interval>::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<Interval>::iterator p = intervals.begin(); + while (p != intervals.end()) { + std::list<Interval>::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_Interval> modulo_intervals; + coef_t max_distance = modulo/2; + for (std::list<Interval>::iterator p = intervals.begin(); p != intervals.end(); p++) { + if ((*p).lb >= max_distance) + break; + + coef_t size = (*p).ub - (*p).lb; + + std::list<Interval>::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<Interval>::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<Modulo_Interval>::iterator p2 = modulo_intervals.begin(); + while (p2 != modulo_intervals.end()) { + std::list<Modulo_Interval>::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<Modulo_Interval>::iterator i = modulo_intervals.begin(); i != modulo_intervals.end(); i++) { + std::vector<Relation *> candidates; + int num_replaced = 0; + for (std::list<Interval>::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<Interval>::iterator replaced_one = intervals.end(); + for (std::list<Interval>::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<std::pair<Relation, Relation> > 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<std::pair<Relation, Relation> >::iterator i = Rs.begin(); + while (i != Rs.end()) { + // find regions with identical hole conditions to merge + { + std::list<std::pair<Relation, Relation> >::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 { + r = ConvexHull(Union(copy((*i).first), copy((*j).first))); + } + catch (const std::overflow_error &e) { + r = SimpleHull(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 = SimpleHull(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<std::list<std::pair<Relation, Relation> >::iterator> s; + for (std::list<std::pair<Relation, Relation> >::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<Variable_ID> 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<Interval> intervals; + intervals.push_back(Interval(i, lb, (*gei).get_const())); + + for (std::list<std::list<std::pair<Relation, Relation> >::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<Interval>::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<Interval>::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<std::pair<Relation, Relation> >::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<std::string> 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<std::string> 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<std::pair<coef_t, coef_t> > bounds1(R.n_inp()); + std::vector<std::pair<coef_t, coef_t> > bounds2(R.n_out()); + { + Relation t = Project_Sym(copy(r)); + t.simplify(); + for (int i = 1; i <= R.n_inp(); i++) { + Tuple<Variable_ID> 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<Variable_ID> 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<Variable_ID> 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<Variable_ID> 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; +} + +} + diff --git a/omegalib/omega/src/hull_simple.cc b/omegalib/omega/src/hull_simple.cc new file mode 100755 index 0000000..62dcb26 --- /dev/null +++ b/omegalib/omega/src/hull_simple.cc @@ -0,0 +1,1013 @@ +/***************************************************************************** + Copyright (C) 2011 Chun Chen + All Rights Reserved. + + Purpose: + Hull approximation including lattice and irregular constraints that + involves wildcards. + + Notes: + + History: + 03/12/11 Created by Chun Chen + *****************************************************************************/ + +#include <assert.h> +#include <omega.h> +#include <basic/BoolSet.h> +#include <vector> +#include <list> +#include <set> +#include <string> +#include <algorithm> + +namespace omega { + +Relation SimpleHull(const Relation &R, bool allow_stride_constraint, + bool allow_irregular_constraint) { + std::vector<Relation> Rs; + Rs.push_back(R); + return SimpleHull(Rs, allow_stride_constraint, allow_irregular_constraint); +} + +Relation SimpleHull(const std::vector<Relation> &Rs, + bool allow_stride_constraint, bool allow_irregular_constraint) { + // check for sanity of parameters + if (Rs.size() == 0) + return Relation::False(0); + int num_dim = -1; + int first_non_null; + for (int i = 0; i < Rs.size(); i++) { + if (Rs[i].is_null()) + continue; + + if (num_dim == -1) { + num_dim = Rs[i].n_inp(); + first_non_null = i; + } + + if (Rs[i].n_inp() != num_dim) + throw std::invalid_argument( + "relations for hull must have same dimension"); + if (Rs[i].n_out() != 0) + throw std::invalid_argument( + "hull calculation must be set relation"); + } + + // convert to a list of relations each with a single conjunct + std::vector<Relation> l_Rs; + for (int i = 0; i < Rs.size(); i++) { + if (Rs[i].is_null()) + continue; + + Relation r = copy(Rs[i]); + + //r.simplify(2, 4); + r.simplify(); + DNF_Iterator c(r.query_DNF()); + int top = l_Rs.size(); + while (c.live()) { + Relation r2 = Relation(r, c.curr()); + + // quick elimination of redundant conjuncts + bool already_included = false; + for (int j = 0; j < top; j++) + if (Must_Be_Subset(copy(r2), copy(l_Rs[j]))) { + already_included = true; + break; + } else if (Must_Be_Subset(copy(l_Rs[j]), copy(r2))) { + l_Rs.erase(l_Rs.begin() + j); + top--; + break; + } + + if (!already_included) + l_Rs.push_back(r2); + c++; + } + } + + // shortcut for simple case + if (l_Rs.size() == 0) { + if (num_dim == -1) + return Relation::False(0); + else { + Relation r = Relation::False(num_dim); + r.copy_names(Rs[first_non_null]); + r.setup_names(); + return r; + } + } else if (l_Rs.size() == 1) { + if (allow_stride_constraint && allow_irregular_constraint) { + l_Rs[0].copy_names(Rs[first_non_null]); + l_Rs[0].setup_names(); + return l_Rs[0]; + } else if (!allow_stride_constraint && !allow_irregular_constraint) { + l_Rs[0] = Approximate(l_Rs[0]); + l_Rs[0].copy_names(Rs[first_non_null]); + l_Rs[0].setup_names(); + return l_Rs[0]; + } + } + + Relation hull = Relation::True(num_dim); + + // lattice union approximation + if (allow_stride_constraint) { + std::vector<std::vector<std::pair<EQ_Handle, BoolSet<> > > > strides( + l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) + for (EQ_Iterator e = l_Rs[i].single_conjunct()->EQs(); e; e++) + if ((*e).has_wildcards()) { + int num_wildcard = 0; + BoolSet<> affected(num_dim); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) { + if (cvi.curr_var()->kind() == Wildcard_Var) + num_wildcard++; + else if (cvi.curr_var()->kind() == Input_Var) + affected.set(cvi.curr_var()->get_position() - 1); + } + if (num_wildcard == 1) + strides[i].push_back(std::make_pair(*e, affected)); + } + + for (int i = 0; i < strides[0].size(); i++) { + coef_t c = + abs( + strides[0][i].first.get_coef( + Constr_Vars_Iter(strides[0][i].first, true).curr_var())); + coef_t old_c = c; + bool is_stride = true; + for (int j = 1; j < l_Rs.size(); j++) { + std::list<coef_t> candidates; + for (int k = 0; k < strides[j].size(); k++) + if (!(strides[0][i].second & strides[j][k].second).empty()) { + coef_t t = gcd(c, + abs( + strides[j][k].first.get_coef( + Constr_Vars_Iter( + strides[j][k].first, + true).curr_var()))); + if (t != 1) { + std::list<coef_t>::iterator p = candidates.begin(); + while (p != candidates.end() && *p > t) + ++p; + if (p == candidates.end() || *p != t) + candidates.insert(p, t); + + t = gcd(t, abs(strides[0][i].first.get_const())); + t = gcd(t, abs(strides[j][k].first.get_const())); + if (t != 1) { + std::list<coef_t>::iterator p = + candidates.begin(); + while (p != candidates.end() && *p > t) + ++p; + if (p == candidates.end() || *p != t) + candidates.insert(p, t); + } + } + } + + bool found_matched_stride = false; + for (std::list<coef_t>::iterator k = candidates.begin(); + k != candidates.end(); k++) { + Relation r = Relation::True(num_dim); + EQ_Handle h = r.and_with_EQ(strides[0][i].first); + h.update_coef(Constr_Vars_Iter(h, true).curr_var(), + -old_c + *k); + r.simplify(); + if (Must_Be_Subset(copy(l_Rs[j]), copy(r))) { + c = *k; + found_matched_stride = true; + break; + } + } + + if (!found_matched_stride) { + is_stride = false; + break; + } + } + + if (is_stride) { + Relation r = Relation::True(num_dim); + EQ_Handle h = r.and_with_EQ(strides[0][i].first); + h.update_coef(Constr_Vars_Iter(h, true).curr_var(), -old_c + c); + r.simplify(); + hull = Intersection(hull, r); + } + } + } + + // consider some special wildcard constraints + if (allow_irregular_constraint) { + std::vector< + std::vector< + std::pair<Variable_ID, std::map<Variable_ID, coef_t> > > > ranges( + l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) { + std::vector<std::pair<GEQ_Handle, std::map<Variable_ID, coef_t> > > geqs_ub; + std::vector<std::pair<GEQ_Handle, std::map<Variable_ID, coef_t> > > geqs_lb; + for (GEQ_Iterator e = l_Rs[i].single_conjunct()->GEQs(); e; e++) + if ((*e).has_wildcards()) { + int num_wildcard = 0; + std::map<Variable_ID, coef_t> formula; + int direction; + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) { + Variable_ID v = cvi.curr_var(); + switch (v->kind()) { + case Wildcard_Var: + num_wildcard++; + if (cvi.curr_coef() > 0) + direction = true; + else + direction = false; + break; + case Input_Var: + case Global_Var: + formula[cvi.curr_var()] = cvi.curr_coef(); + break; + default: + assert(false); + } + } + if (num_wildcard == 1) { + if (direction) { + for (std::map<Variable_ID, coef_t>::iterator j = + formula.begin(); j != formula.end(); j++) + j->second = -j->second; + geqs_ub.push_back(std::make_pair(*e, formula)); + } else + geqs_lb.push_back(std::make_pair(*e, formula)); + } + } + for (int j = 0; j < geqs_lb.size(); j++) { + Variable_ID v = + Constr_Vars_Iter(geqs_lb[j].first, true).curr_var(); + for (int k = 0; k < geqs_ub.size(); k++) + if (v == Constr_Vars_Iter(geqs_ub[k].first, true).curr_var() + && geqs_lb[j].second == geqs_ub[k].second) + ranges[i].push_back( + std::make_pair(v, geqs_lb[j].second)); + } + } + + // find compatible wildcard match + // TODO: evaluate to find the best match, also avoid mapping two wildcards + // in a single conjunct to one variable (incorrect) + std::vector<std::vector<int> > all_match; + for (int i = 0; i < ranges[0].size(); i++) { + std::vector<int> match(l_Rs.size(), -1); + match[0] = i; + for (int j = 1; j < l_Rs.size(); j++) { + for (int k = 0; k < ranges[j].size(); k++) + if (ranges[0][i].second == ranges[j][k].second) { + match[j] = k; + break; + } + if (match[j] == -1) + break; + } + if (match[l_Rs.size() - 1] != -1) + all_match.push_back(match); + } + + // map compatible wildcards to input variables + std::vector<Relation> ll_Rs(l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) { + Relation r(num_dim + all_match.size()); + F_Exists *f_exists = r.add_and()->add_exists(); + F_And *f_root = f_exists->add_and(); + std::map<Variable_ID, Variable_ID> wc_map; + for (int j = 0; j < all_match.size(); j++) + wc_map[ranges[i][all_match[j][i]].first] = r.set_var(j + 1); + + for (EQ_Iterator e(l_Rs[i].single_conjunct()->EQs()); e; e++) + if (!(*e).has_wildcards()) { + EQ_Handle h = f_root->add_EQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + all_match.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = cvi.curr_var()->get_global_var(); + Variable_ID v; + if (g->arity() == 0) + v = r.get_local(g); + else + v = r.get_local(g, + cvi.curr_var()->function_of()); + h.update_coef(v, cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + for (GEQ_Iterator e(l_Rs[i].single_conjunct()->GEQs()); e; e++) { + GEQ_Handle h = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + all_match.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + Global_Var_ID g = cvi.curr_var()->get_global_var(); + Variable_ID v; + if (g->arity() == 0) + v = r.get_local(g); + else + v = r.get_local(g, cvi.curr_var()->function_of()); + h.update_coef(v, cvi.curr_coef()); + break; + } + case Wildcard_Var: { + std::map<Variable_ID, Variable_ID>::iterator p = + wc_map.find(cvi.curr_var()); + Variable_ID v; + if (p == wc_map.end()) { + v = f_exists->declare(); + wc_map[cvi.curr_var()] = v; + } else + v = p->second; + h.update_coef(v, cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + r.simplify(); + ll_Rs[i] = r; + } + + // now use SimpleHull on regular bounds only + Relation result = SimpleHull(ll_Rs, false, false); + + // convert imaginary input variables back to wildcards + Relation mapping(num_dim + all_match.size(), num_dim); + F_And *f_root = mapping.add_and(); + for (int i = 0; i < num_dim; i++) { + EQ_Handle h = f_root->add_EQ(); + h.update_coef(mapping.input_var(all_match.size() + i + 1), 1); + h.update_coef(mapping.output_var(i + 1), -1); + } + result = Range(Restrict_Domain(mapping, result)); + + hull = Intersection(hull, result); + hull.simplify(); + hull.copy_names(Rs[first_non_null]); + hull.setup_names(); + return hull; + } + + // check regular bounds + if (l_Rs.size() == 1) { + hull = Intersection(hull, Approximate(copy(l_Rs[0]))); + } else { + for (int i = 0; i < l_Rs.size(); i++) { + l_Rs[i] = Approximate(l_Rs[i]); + l_Rs[i].simplify(2, 4); + } + + // check global variables + // TODO: global variable function_of() is not considered for now + std::map<Global_Var_ID, int> globals; + for (int i = 0; i < l_Rs.size(); i++) + for (Constraint_Iterator e( + l_Rs[i].single_conjunct()->constraints()); e; e++) + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + if (cvi.curr_var()->kind() == Global_Var) + globals[cvi.curr_var()->get_global_var()] = -1; + + if (globals.size() > 0) { + int count = 1; + for (std::map<Global_Var_ID, int>::iterator i = globals.begin(); + i != globals.end(); i++) + i->second = count++; + + std::vector<Relation> ll_Rs(l_Rs.size()); + for (int i = 0; i < l_Rs.size(); i++) { + Relation r(num_dim + globals.size()); + F_And *f_root = r.add_and(); + for (EQ_Iterator e(l_Rs[i].single_conjunct()->EQs()); e; e++) { + EQ_Handle h = f_root->add_EQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + globals.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + h.update_coef( + r.set_var( + globals[cvi.curr_var()->get_global_var()]), + cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + for (GEQ_Iterator e(l_Rs[i].single_conjunct()->GEQs()); e; + e++) { + GEQ_Handle h = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + h.update_coef( + r.set_var( + cvi.curr_var()->get_position() + + globals.size()), + cvi.curr_coef()); + break; + } + case Global_Var: { + h.update_coef( + r.set_var( + globals[cvi.curr_var()->get_global_var()]), + cvi.curr_coef()); + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + ll_Rs[i] = r; + } + + Relation result = SimpleHull(ll_Rs, false, false); + + std::map<int, Global_Var_ID> globals_reverse; + for (std::map<Global_Var_ID, int>::iterator i = globals.begin(); + i != globals.end(); i++) + globals_reverse[i->second] = i->first; + + Relation r(num_dim); + F_And *f_root = r.add_and(); + for (EQ_Iterator e(result.single_conjunct()->EQs()); e; e++) { + EQ_Handle h = f_root->add_EQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + int pos = cvi.curr_var()->get_position(); + if (pos > globals_reverse.size()) + h.update_coef( + r.set_var(pos - globals_reverse.size()), + cvi.curr_coef()); + else { + Global_Var_ID g = globals_reverse[pos]; + h.update_coef(r.get_local(g), cvi.curr_coef()); + } + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + for (GEQ_Iterator e(result.single_conjunct()->GEQs()); e; e++) { + GEQ_Handle h = f_root->add_GEQ(); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + switch (cvi.curr_var()->kind()) { + case Input_Var: { + int pos = cvi.curr_var()->get_position(); + if (pos > globals_reverse.size()) + h.update_coef( + r.set_var(pos - globals_reverse.size()), + cvi.curr_coef()); + else { + Global_Var_ID g = globals_reverse[pos]; + h.update_coef(r.get_local(g), cvi.curr_coef()); + } + break; + } + default: + assert(false); + } + h.update_const((*e).get_const()); + } + + hull = Intersection(hull, r); + hull.simplify(); + hull.copy_names(Rs[first_non_null]); + hull.setup_names(); + return hull; + } else { + std::vector<std::vector<Relation> > projected(num_dim + 1, + std::vector<Relation>(l_Rs.size())); + for (int i = 0; i < l_Rs.size(); i++) { + projected[num_dim][i] = copy(l_Rs[i]); + for (int j = num_dim - 1; j >= 0; j--) { + projected[j][i] = Project(copy(projected[j + 1][i]), + projected[j + 1][i].input_var(j + 1)); + projected[j][i].simplify(2, 4); + } + } + + std::vector<bool> has_lb(num_dim, false); + std::vector<bool> has_ub(num_dim, false); + for (int i = 0; i < num_dim; i++) { + bool skip_lb = false; + bool skip_ub = false; + std::vector<Relation> bound(l_Rs.size()); + for (int j = 0; j < l_Rs.size(); j++) { + bound[j] = Gist(copy(projected[i + 1][j]), + copy(projected[i][j]), 1); + bound[j] = Approximate(bound[j]); + bound[j] = EQs_to_GEQs(bound[j]); + + bool has_lb_not_in_hull = false; + bool has_ub_not_in_hull = false; + for (GEQ_Iterator e = bound[j].single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef(bound[j].input_var(i + 1)); + if (!skip_lb && coef > 0) { + Relation r = Relation::True(bound[j].n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (j != 0 && l_Rs.size() > 2 + && Must_Be_Subset(copy(hull), copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); k++) + if (k != j + && !Must_Be_Subset(copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + has_lb[i] = true; + } else + has_lb_not_in_hull = true; + } else if (!skip_ub && coef < 0) { + Relation r = Relation::True(bound[j].n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (j != 0 && l_Rs.size() > 2 + && Must_Be_Subset(copy(hull), copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); k++) + if (k != j + && !Must_Be_Subset(copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + has_ub[i] = true; + } else + has_ub_not_in_hull = true; + } + } + + if (!has_lb_not_in_hull) + skip_lb = true; + if (!has_ub_not_in_hull) + skip_ub = true; + if (skip_lb && skip_ub) + break; + } + + // no ready lower bound, approximate it + bool got_rect_lb = false; + if (!skip_lb) { + for (int j = 0; j < l_Rs.size(); j++) { + std::set<BoolSet<> > S; + for (GEQ_Iterator e = + bound[j].single_conjunct()->GEQs(); e; e++) { + coef_t coef = (*e).get_coef( + bound[j].input_var(i + 1)); + if (coef > 0) { + BoolSet<> s(i); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + if ((*cvi).var->kind() == Input_Var + && (*cvi).var->get_position() - 1 + != i) { + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef < 0 + && has_lb[(*cvi).var->get_position() + - 1])) + s.set( + (*cvi).var->get_position() + - 1); + else { + for (GEQ_Iterator e2 = + bound[j].single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + s.set( + (*cvi).var->get_position() + - 1); + break; + } + } + } + + if (s.num_elem() > 0) + S.insert(s); + } + } + + if (S.size() > 0) { + BoolSet<> s(i); + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) + s |= *k; + for (int k = 0; k < i; k++) + if (s.get(k)) { + BoolSet<> t(i); + t.set(k); + S.insert(t); + } + + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) { + + bool do_again = false; + std::set<int> vars; + int round_trip = 0; + do { + Relation r = copy(projected[i + 1][j]); + + if (!do_again) { + for (int kk = 0; kk < i; kk++) + if ((*k).get(kk)) { + r = Project(r, + r.input_var(kk + 1)); + vars.insert(kk + 1); + } + } else { + for (std::set<int>::iterator vars_it = + vars.begin(); + vars_it != vars.end(); + vars_it++) + if (*vars_it < i + 1) + r = Project(r, + r.input_var(*vars_it)); + } + + r.simplify(2, 4); + Relation r2 = Project(copy(r), + r.input_var(i + 1)); + Relation b = Gist(copy(r), copy(r2), 1); + // Relation c = Project(copy(r),r.input_var(4) ); + + // c.simplify(2,4); + // Relation d = Project(copy(c), r.input_var(i+1)); + // Relation e = Gist(copy(c), copy(d), 1); + + b = Approximate(b); + b = EQs_to_GEQs(b); + + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + if (coef > 0) { + Relation r = Relation::True( + b.n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (Must_Be_Subset(copy(hull), + copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); + k++) + if (k != j + && !Must_Be_Subset( + copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + got_rect_lb = true; + } + } + } + do_again = false; + if (!got_rect_lb) { + bool found = false; + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + + if (coef > 0) { + for (Constr_Vars_Iter cvi(*e); + cvi; cvi++) + if ((*cvi).var->kind() + == Input_Var + && (*cvi).var->get_position() + - 1 != i) { + + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef + < 0 + && has_lb[(*cvi).var->get_position() + - 1])) { + vars.insert( + (*cvi).var->get_position()); + found = true; + } else { + for (GEQ_Iterator e2 = + b.single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef + > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + vars.insert( + (*cvi).var->get_position()); + found = + true; + break; + } + } + + } + + } + if (found) + break; + + } + if (found && (round_trip < i)) + do_again = true; + + } + round_trip++; + } while (do_again); + } + + if (got_rect_lb) + break; + } + } + } + + // no ready upper bound, approximate it + bool got_rect_ub = false; + if (!skip_ub) { + for (int j = 0; j < l_Rs.size(); j++) { + std::set<BoolSet<> > S; + for (GEQ_Iterator e = + bound[j].single_conjunct()->GEQs(); e; e++) { + coef_t coef = (*e).get_coef( + bound[j].input_var(i + 1)); + if (coef < 0) { + BoolSet<> s(i); + for (Constr_Vars_Iter cvi(*e); cvi; cvi++) + if ((*cvi).var->kind() == Input_Var + && (*cvi).var->get_position() - 1 + != i) { + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef < 0 + && has_lb[(*cvi).var->get_position() + - 1])) + s.set( + (*cvi).var->get_position() + - 1); + else { + for (GEQ_Iterator e2 = + bound[j].single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + s.set( + (*cvi).var->get_position() + - 1); + break; + } + } + } + + if (s.num_elem() > 0) + S.insert(s); + } + } + + if (S.size() > 0) { + BoolSet<> s(i); + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) + s |= *k; + for (int k = 0; k < i; k++) + if (s.get(k)) { + BoolSet<> t(i); + t.set(k); + S.insert(t); + } + + for (std::set<BoolSet<> >::iterator k = S.begin(); + k != S.end(); k++) { + + bool do_again = false; + std::set<int> vars; + int round_trip = 0; + do { + + Relation r = copy(projected[i + 1][j]); + + if (!do_again) { + for (int kk = 0; kk < i; kk++) + if ((*k).get(kk)) { + r = Project(r, + r.input_var(kk + 1)); + vars.insert(kk + 1); + } + } else { + for (std::set<int>::iterator vars_it = + vars.begin(); + vars_it != vars.end(); + vars_it++) + if (*vars_it < i + 1) + r = Project(r, + r.input_var(*vars_it)); + } + + r.simplify(2, 4); + Relation r2 = Project(copy(r), + r.input_var(i + 1)); + // r2.simplify(2,4); + Relation b = Gist(r, r2, 1); + b = Approximate(b); + b = EQs_to_GEQs(b); + + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + if (coef < 0) { + Relation r = Relation::True( + b.n_inp()); + r.and_with_GEQ(*e); + r.simplify(); + + if (Must_Be_Subset(copy(hull), + copy(r))) + continue; + + bool belong_to_hull = true; + for (int k = 0; k < l_Rs.size(); + k++) + if (k != j + && !Must_Be_Subset( + copy(l_Rs[k]), + copy(r))) { + belong_to_hull = false; + break; + } + if (belong_to_hull) { + hull.and_with_GEQ(*e); + got_rect_ub = true; + } + } + } + do_again = false; + if (!got_rect_ub) { + bool found = false; + for (GEQ_Iterator e = + b.single_conjunct()->GEQs(); e; + e++) { + coef_t coef = (*e).get_coef( + b.input_var(i + 1)); + if (coef < 0) { + for (Constr_Vars_Iter cvi(*e); + cvi; cvi++) + if ((*cvi).var->kind() + == Input_Var + && (*cvi).var->get_position() + - 1 != i) { + + if (((*cvi).coef > 0 + && has_ub[(*cvi).var->get_position() + - 1]) + || ((*cvi).coef + < 0 + && has_lb[(*cvi).var->get_position() + - 1])) { + vars.insert( + (*cvi).var->get_position()); + found = true; + } else { + for (GEQ_Iterator e2 = + b.single_conjunct()->GEQs(); + e2; e2++) + if (e2 != e + && (((*cvi).coef + > 0 + && (*e2).get_coef( + (*cvi).var) + < 0) + || ((*cvi).coef + < 0 + && (*e2).get_coef( + (*cvi).var) + > 0))) { + vars.insert( + (*cvi).var->get_position()); + found = + true; + break; + } + } + + } + } + if (found) + break; + } + if (found && (round_trip < i)) + do_again = true; + + } + round_trip++; + } while (do_again); + } + + if (got_rect_ub) + break; + } + } + } + } + } + } + + hull.simplify(); + hull.copy_names(Rs[first_non_null]); + hull.setup_names(); + return hull; +} + +} + diff --git a/omegalib/omega/src/omega_core/oc.cc b/omegalib/omega/src/omega_core/oc.cc new file mode 100644 index 0000000..0dc9b49 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc.cc @@ -0,0 +1,754 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Simplify a problem. + + Notes: + + History: + 12/10/06 Improved gist function, by Chun Chen. +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> + +namespace omega { + +eqn SUBs[maxVars+1]; +Memory redMemory[maxVars+1]; + +int Problem::reduceProblem() { + int result; + checkVars(nVars+1); + assert(omegaInitialized); + if (nVars > nEQs + 3 * safeVars) + freeEliminations(safeVars); + + check(); + if (!mayBeRed && nSUBs == 0 && safeVars == 0) { + result = solve(OC_SOLVE_UNKNOWN); + nGEQs = 0; + nEQs = 0; + nSUBs = 0; + nMemories = 0; + if (!result) { + int e = newEQ(); + assert(e == 0); + eqnnzero(&EQs[0], nVars); + EQs[0].color = EQ_BLACK; + EQs[0].coef[0] = 1; + } + check(); + return result; + } + return solve(OC_SOLVE_SIMPLIFY); +} + + +int Problem::simplifyProblem(int verify, int subs, int redundantElimination) { + checkVars(nVars+1); + assert(omegaInitialized); + setInternals(); + check(); + if (!reduceProblem()) goto returnFalse; + if (verify) { + addingOuterEqualities++; + int r = verifyProblem(); + addingOuterEqualities--; + if (!r) goto returnFalse; + if (nEQs) { // found some equality constraints during verification + int numRed = 0; + if (mayBeRed) + for (int e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) numRed++; + if (mayBeRed && nVars == safeVars && numRed == 1) + nEQs = 0; // discard them + else if (!reduceProblem()) { + assert(0 && "Added equality constraint to verified problem generates false"); + } + } + } + if (redundantElimination) { + if (redundantElimination > 1) { + if (!expensiveEqualityCheck()) goto returnFalse; + } + if (!quickKill(0)) goto returnFalse; + if (redundantElimination > 1) { + if (!expensiveKill()) goto returnFalse; + } + } + resurrectSubs(); + if (redundantElimination) + simplifyStrideConstraints(); + if (redundantElimination > 2 && safeVars < nVars) { + if (!quickKill(0)) goto returnFalse; + return simplifyProblem(verify, subs, redundantElimination-2); + } + setExternals(); + assert(nMemories == 0); + return (1); + +returnFalse: + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_BLACK; + EQs[neweq].coef[0] = 1; + nMemories = 0; + return 0; +} + +int Problem::simplifyApproximate(bool strides_allowed) { + int result; + checkVars(nVars+1); + assert(inApproximateMode == 0); + + inApproximateMode = 1; + inStridesAllowedMode = strides_allowed; + if (TRACE) + fprintf(outputFile, "Entering Approximate Mode [\n"); + + assert(omegaInitialized); + result = simplifyProblem(0,0,0); + + while (result && !strides_allowed && nVars > safeVars) { + int e; + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[nVars]) deleteGEQ(e); + for (e = nEQs - 1; e >= 0; e--) + if (EQs[e].coef[nVars]) deleteEQ(e); + nVars--; + result = simplifyProblem(0,0,0); + } + + if (TRACE) + fprintf(outputFile, "] Leaving Approximate Mode\n"); + + assert(inApproximateMode == 1); + inApproximateMode=0; + inStridesAllowedMode = 0; + + assert(nMemories == 0); + return (result); +} + + + + +/* + * Return 1 if red equations constrain the set of possible + * solutions. We assume that there are solutions to the black + * equations by themselves, so if there is no solution to the combined + * problem, we return 1. + */ + +#ifdef GIST_CHECK +Problem full_answer, context; +Problem redProblem; +#endif + +redCheck Problem::redSimplifyProblem(int effort, int computeGist) { + int result; + int e; + + checkVars(nVars+1); + assert(mayBeRed >= 0); + mayBeRed++; + + assert(omegaInitialized); + if (TRACE) { + fprintf(outputFile, "Checking for red equations:\n"); + printProblem(); + } + setInternals(); + +#ifdef GIST_CHECK + int r1,r2; + if (TRACE) + fprintf(outputFile,"Set-up for gist invariant checking[\n"); + redProblem = *this; + redProblem.check(); + full_answer = *this; + full_answer.check(); + full_answer.turnRedBlack(); + full_answer.check(); + r1 = full_answer.simplifyProblem(1,0,1); + full_answer.check(); + if (DBUG) fprintf(outputFile,"Simplifying context [\n"); + context = *this; + context.check(); + context.deleteRed(); + context.check(); + r2 = context.simplifyProblem(1,0,1); + context.check(); + if (DBUG) fprintf(outputFile,"] Simplifying context\n"); + + if (!r2 && TRACE) fprintf(outputFile, "WARNING: Gist context is false!\n"); + if (TRACE) + fprintf(outputFile,"] Set-up for gist invariant checking done\n"); +#endif + + // Save known integer modular equations, -- by chun 12/10/2006 + eqn ModularEQs[nEQs]; + int nModularEQs = 0; + int old_nVars = nVars; + for (int e = 0; e < nEQs; e++) + if (EQs[e].color != EQ_RED) + for (int i = safeVars+1; i <= nVars; i++) + if (EQs[e].coef[i] != 0) { + eqnncpy(&(ModularEQs[nModularEQs++]), &(EQs[e]), nVars); + break; + } + + + if (solveEQ() == false) { + if (TRACE) + fprintf(outputFile, "Gist is FALSE\n"); + if (computeGist) { + nMemories = 0; + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_RED; + EQs[neweq].coef[0] = 1; + } + mayBeRed--; + return redFalse; + } + + if (!computeGist && nMemories) + return redConstraints; + if (normalize() == normalize_false) { + if (TRACE) + fprintf(outputFile, "Gist is FALSE\n"); + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nMemories = 0; + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_RED; + EQs[neweq].coef[0] = 1; + } + mayBeRed--; + return redFalse; + } + + result = 0; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result = 1; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color == EQ_RED) result = 1; + if (nMemories) result = 1; + if (!result) { + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nMemories = 0; + nEQs = 0; + } + mayBeRed--; + return noRed; + } + + result = simplifyProblem(effort?1:0,1,0); +#ifdef GIST_CHECK + if (!r1 && TRACE && result) + fprintf(outputFile, "Gist is False but not detected\n"); +#endif + if (!result) { + if (TRACE) + fprintf(outputFile, "Gist is FALSE\n"); + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nEQs = 0; + int neweq = newEQ(); + assert(neweq == 0); + nMemories = 0; + eqnnzero(&EQs[neweq], nVars); + EQs[neweq].color = EQ_RED; + EQs[neweq].coef[0] = 1; + } + mayBeRed--; + return redFalse; + } + + freeRedEliminations(); + + result = 0; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result = 1; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color == EQ_RED) result = 1; + if (nMemories) result = 1; + if (!result) { + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nMemories = 0; + nEQs = 0; + } + mayBeRed--; + return noRed; + } + + if (effort && (computeGist || !nMemories)) { + if (TRACE) + fprintf(outputFile, "*** Doing potentially expensive elimination tests for red equations [\n"); + quickRedKill(computeGist); + checkGistInvariant(); + result = nMemories; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result++; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color == EQ_RED) result++; + if (result && effort > 1 && (computeGist || !nMemories)) { + expensiveRedKill(); + result = nMemories; + for (e = nGEQs-1; e >= 0; e--) if (GEQs[e].color == EQ_RED) result++; + for (e = nEQs-1; e >= 0; e--) if (EQs[e].color == EQ_RED) result++; + } + + if (!result) { + if (TRACE) + fprintf(outputFile, "]******************** Redudant Red Equations eliminated!!\n"); + if (computeGist) { + nGEQs = 0; + nEQs = 0; + resurrectSubs(); + nGEQs = 0; + nMemories = 0; + nEQs = 0; + } + mayBeRed--; + return noRed; + } + + if (TRACE) fprintf(outputFile, "]******************** Red Equations remain\n"); + if (DEBUG) printProblem(); + } + + if (computeGist) { + resurrectSubs(); + cleanoutWildcards(); + + + // Restore saved modular equations into EQs without affecting the problem + if (nEQs+nModularEQs > allocEQs) { + allocEQs = padEQs(allocEQs, nEQs+nModularEQs); + eqn *new_eqs = new eqn[allocEQs]; + for (int e = 0; e < nEQs; e++) + eqnncpy(&(new_eqs[e]), &(EQs[e]), nVars); + delete[] EQs; + EQs= new_eqs; + } + + for (int e = 0; e < nModularEQs; e++) { + eqnncpy(&(EQs[nEQs+e]), &(ModularEQs[e]), old_nVars); + EQs[nEQs+e].color = EQ_RED; + Tuple<coef_t> t(safeVars); + for (int i = 1; i <= safeVars; i++) + t[i] = ModularEQs[e].coef[var[i]]; + for (int i = 1; i <= safeVars; i++) + EQs[nEQs+e].coef[i] = t[i]; + } + + + // Now simplify modular equations using Chinese remainder theorem -- by chun 12/10/2006 + for (int e = 0; e < nEQs; e++) + if (EQs[e].color == EQ_RED) { + int wild_pos = -1; + for (int i = safeVars+1; i <= nVars; i++) + if (EQs[e].coef[i] != 0) { + wild_pos = i; + break; + } + + if (wild_pos == -1) + continue; + + for (int e2 = e+1; e2 < nEQs+nModularEQs; e2++) + if (EQs[e2].color == EQ_RED) { + int wild_pos2 = -1; + for (int i = safeVars+1; i <= ((e2<nEQs)?nVars:old_nVars); i++) + if (EQs[e2].coef[i] != 0) { + wild_pos2 = i; + break; + } + + if (wild_pos2 == -1) + continue; + + coef_t g = gcd(abs(EQs[e].coef[wild_pos]), abs(EQs[e2].coef[wild_pos2])); + coef_t g2 = 1; + coef_t g3; + EQs[e].color = EQs[e2].color = EQ_BLACK; + while ((g3 = factor(g)) != 1) { + coef_t gg = g2 * g3; + g = g/g3; + + bool match = true; + coef_t c = EQs[e].coef[0]; + coef_t c2 = EQs[e2].coef[0]; + bool change_sign = false; + for (int i = 1; i <= safeVars; i++) { + coef_t coef = int_mod_hat(EQs[e].coef[i], gg); + coef_t coef2 = int_mod_hat(EQs[e2].coef[i], gg); + + if (coef == 0 && coef2 == 0) + continue; + + if (change_sign && coef == -coef2) + continue; + + if (!change_sign) { + if (coef == coef2) + continue; + else if (coef == -coef2) { + change_sign = true; + continue; + } + } + + if (coef != 0) { + coef_t t = query_variable_mod(i, gg/gcd(abs(coef), gg), EQ_RED, nModularEQs, old_nVars); + if (t == posInfinity) { + match = false; + break; + } + + c += coef * t; + } + if (coef2 != 0) { + coef_t t = query_variable_mod(i, gg/gcd(abs(coef2), gg), EQ_RED, nModularEQs, old_nVars); + if (t == posInfinity) { + match = false; + break; + } + + c2 += coef2 * t; + } + } + if ((change_sign && int_mod_hat(c, gg) != -int_mod_hat(c2, gg)) || + (!change_sign && int_mod_hat(c, gg) != int_mod_hat(c2, gg))) + match = false; + + if (match) + g2 = gg; + } + EQs[e].color = EQs[e2].color = EQ_RED; + + if (g2 == 1) + continue; + + if (g2 == abs(EQs[e].coef[wild_pos])) { + EQs[e].color = EQ_BLACK; + break; + } + else if (e2 < nEQs && g2 == abs(EQs[e2].coef[wild_pos2])) + EQs[e2].color = EQ_BLACK; + else { + coef_t g4 = abs(EQs[e].coef[wild_pos])/g2; + while (lcm(g2, g4) != abs(EQs[e].coef[wild_pos])) { + assert(lcm(g2, g4) < abs(EQs[e].coef[wild_pos])); + g4 *= abs(EQs[e].coef[wild_pos])/lcm(g2, g4); + } + + for (int i = 0; i <= safeVars; i++) + EQs[e].coef[i] = int_mod_hat(EQs[e].coef[i], g4); + EQs[e].coef[wild_pos] = (EQs[e].coef[wild_pos]>0?1:-1)*g4; + } + } + } + + deleteBlack(); + } + + setExternals(); + mayBeRed--; + assert(nMemories == 0); + return redConstraints; +} + + +void Problem::convertEQstoGEQs(bool excludeStrides) { + int i; + int e; + if (DBUG) + fprintf(outputFile, "Converting all EQs to GEQs\n"); + simplifyProblem(0,0,0); + for(e=0;e<nEQs;e++) { + bool isStride = 0; + int e2 = newGEQ(); + if (excludeStrides) + for(i = safeVars+1; i <= nVars; i++) + isStride = isStride || (EQs[e].coef[i] != 0); + if (isStride) continue; + eqnncpy(&GEQs[e2], &EQs[e], nVars); + GEQs[e2].touched = 1; + e2 = newGEQ(); + eqnncpy(&GEQs[e2], &EQs[e], nVars); + GEQs[e2].touched = 1; + for (i = 0; i <= nVars; i++) + GEQs[e2].coef[i] = -GEQs[e2].coef[i]; + } + // If we have eliminated all EQs, can set nEQs to 0 + // If some strides are left, we don't know the position of them in the EQs + // array, so decreasing nEQs might remove wrong EQs -- we just leave them + // all in. (could sort the EQs to move strides to the front, but too hard.) + if (!excludeStrides) nEQs=0; + if (DBUG) + printProblem(); +} + + +void Problem::convertEQtoGEQs(int eq) { + int i; + if (DBUG) + fprintf(outputFile, "Converting EQ %d to GEQs\n",eq); + int e2 = newGEQ(); + eqnncpy(&GEQs[e2], &EQs[eq], nVars); + GEQs[e2].touched = 1; + e2 = newGEQ(); + eqnncpy(&GEQs[e2], &EQs[eq], nVars); + GEQs[e2].touched = 1; + for (i = 0; i <= nVars; i++) + GEQs[e2].coef[i] = -GEQs[e2].coef[i]; + if (DBUG) + printProblem(); +} + + +/* + * Calculate value of variable modulo integer from problem's equation + * set plus additional saved modular equations embedded in the same + * EQs array (hinted by nModularEQs) if available. If there is no + * solution, return posInfinity. + */ +coef_t Problem::query_variable_mod(int v, coef_t factor, int color, int nModularEQs, int nModularVars) const { + if (safeVars < v) + return posInfinity; + + Tuple<bool> working_on(safeVars); + for (int i = 1; i <= safeVars; i++) + working_on[i] = false; + + return query_variable_mod(v, factor, color, nModularEQs, nModularVars, working_on); +} + +coef_t Problem::query_variable_mod(int v, coef_t factor, int color, int nModularEQs, int nModularVars, Tuple<bool> &working_on) const { + working_on[v] = true; + + for (int e = 0; e < nEQs+nModularEQs; e++) + if (EQs[e].color == color) { + coef_t coef = int_mod_hat(EQs[e].coef[v], factor); + if (abs(coef) != 1) + continue; + + bool wild_factored = true; + for (int i = safeVars+1; i <= ((e<nEQs)?nVars:nModularVars); i++) + if (int_mod_hat(EQs[e].coef[i], factor) != 0) { + wild_factored = false; + break; + } + if (!wild_factored) + continue; + + coef_t result = 0; + for (int i = 1; i <= safeVars; i++) + if (i != v) { + coef_t p = int_mod_hat(EQs[e].coef[i], factor); + + if (p == 0) + continue; + + if (working_on[i] == true) { + result = posInfinity; + break; + } + + coef_t q = query_variable_mod(i, factor, color, nModularEQs, nModularVars, working_on); + if (q == posInfinity) { + result = posInfinity; + break; + } + result += p*q; + } + + if (result != posInfinity) { + result += EQs[e].coef[0]; + if (coef == 1) + result = -result; + working_on[v] = false; + + return int_mod_hat(result, factor); + } + } + + working_on[v] = false; + return posInfinity; +} + + + +#ifdef GIST_CHECK +enum compareAnswer {apparentlyEqual, mightNotBeEqual, NotEqual}; + +static compareAnswer checkEquiv(Problem *p1, Problem *p2) { + int r1,r2; + + p1->check(); + p2->check(); + p1->resurrectSubs(); + p2->resurrectSubs(); + p1->check(); + p2->check(); + p1->putVariablesInStandardOrder(); + p2->putVariablesInStandardOrder(); + p1->check(); + p2->check(); + p1->ordered_elimination(0); + p2->ordered_elimination(0); + p1->check(); + p2->check(); + r1 = p1->simplifyProblem(1,1,0); + r2 = p2->simplifyProblem(1,1,0); + p1->check(); + p2->check(); + + if (!r1 || !r2) { + if (r1 == r2) return apparentlyEqual; + return NotEqual; + } + if (p1->nVars != p2->nVars + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs + || p1->checkSum() != p2->checkSum()) { + r1 = p1->simplifyProblem(0,1,1); + r2 = p2->simplifyProblem(0,1,1); + assert(r1 && r2); + p1->check(); + p2->check(); + if (p1->nVars != p2->nVars + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs + || p1->checkSum() != p2->checkSum()) { + r1 = p1->simplifyProblem(0,1,2); + r2 = p2->simplifyProblem(0,1,2); + p1->check(); + p2->check(); + assert(r1 && r2); + if (p1->nVars != p2->nVars + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs + || p1->checkSum() != p2->checkSum()) { + p1->check(); + p2->check(); + p1->resurrectSubs(); + p2->resurrectSubs(); + p1->check(); + p2->check(); + p1->putVariablesInStandardOrder(); + p2->putVariablesInStandardOrder(); + p1->check(); + p2->check(); + p1->ordered_elimination(0); + p2->ordered_elimination(0); + p1->check(); + p2->check(); + r1 = p1->simplifyProblem(1,1,0); + r2 = p2->simplifyProblem(1,1,0); + p1->check(); + p2->check(); + } + } + } + + if (p1->nVars != p2->nVars + || p1->nSUBs != p2->nSUBs + || p1->nGEQs != p2->nGEQs + || p1->nSUBs != p2->nSUBs) return NotEqual; + if (p1->checkSum() != p2->checkSum()) return mightNotBeEqual; + return apparentlyEqual; +} +#endif + +void Problem::checkGistInvariant() const { +#ifdef GIST_CHECK + Problem new_answer; + int r; + + check(); + fullAnswer.check(); + context.check(); + + if (safeVars < nVars) { + if (DBUG) { + fprintf(outputFile,"Can't check gist invariant due to wildcards\n"); + printProblem(); + } + return; + } + if (DBUG) { + fprintf(outputFile,"Checking gist invariant on: [\n"); + printProblem(); + } + + new_answer = *this; + new_answer->resurrectSubs(); + new_answer->cleanoutWildcards(); + if (DEBUG) { + fprintf(outputFile,"which is: \n"); + printProblem(); + } + deleteBlack(&new_answer); + turnRedBlack(&new_answer); + if (DEBUG) { + fprintf(outputFile,"Black version of answer: \n"); + printProblem(&new_answer); + } + problem_merge(&new_answer,&context); + + r = checkEquiv(&full_answer,&new_answer); + if (r != apparentlyEqual) { + fprintf(outputFile,"GIST INVARIANT REQUIRES MANUAL CHECK:[\n"); + fprintf(outputFile,"Original problem:\n"); + printProblem(&redProblem); + + fprintf(outputFile,"Context:\n"); + printProblem(&context); + + fprintf(outputFile,"Computed gist:\n"); + printProblem(); + + fprintf(outputFile,"Combined answer:\n"); + printProblem(&full_answer); + + fprintf(outputFile,"Context && red constraints:\n"); + printProblem(&new_answer); + fprintf(outputFile,"]\n"); + } + + if (DBUG) { + fprintf(outputFile,"] Done checking gist invariant on\n"); + } +#endif +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_eq.cc b/omegalib/omega/src/omega_core/oc_eq.cc new file mode 100644 index 0000000..dc595ea --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_eq.cc @@ -0,0 +1,653 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Solve equalities. + + Notes: + + History: + *****************************************************************************/ + +#include <omega/omega_core/oc_i.h> + +namespace omega { + +void Problem::simplifyStrideConstraints() { + int e, e2, i; + if (DBUG) + fprintf(outputFile, "Checking for stride constraints\n"); + for (i = safeVars + 1; i <= nVars; i++) { + if (DBUG) + fprintf(outputFile, "checking %s\n", variable(i)); + for (e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i]) + break; + if (e >= nGEQs) { + if (DBUG) + fprintf(outputFile, "%s passed GEQ test\n", variable(i)); + e2 = -1; + for (e = 0; e < nEQs; e++) + if (EQs[e].coef[i]) { + if (e2 == -1) + e2 = e; + else { + e2 = -1; + break; + } + } + if (e2 >= 0) { + if (DBUG) { + fprintf(outputFile, "Found stride constraint: "); + printEQ(&EQs[e2]); + fprintf(outputFile, "\n"); + } + /* Is a stride constraint */ + coef_t g = abs(EQs[e2].coef[i]); + assert(g>0); + int j; + for (j = 0; j <= nVars; j++) + if (i != j) + EQs[e2].coef[j] = int_mod_hat(EQs[e2].coef[j], g); + } + } + } +} + +void Problem::doMod(coef_t factor, int e, int j) { + /* Solve e = factor alpha for x_j and substitute */ + int k; + eqn eq; + coef_t nFactor; + + int alpha; + + // if (j > safeVars) alpha = j; + // else + if (EQs[e].color) { + rememberRedConstraint(&EQs[e], redEQ, 0); + EQs[e].color = EQ_BLACK; + } + alpha = addNewUnprotectedWildcard(); + eqnncpy(&eq, &EQs[e], nVars); + newVar = alpha; + + if (DEBUG) { + fprintf(outputFile, "doing moding: "); + fprintf(outputFile, "Solve "); + printTerm(&eq, 1); + fprintf(outputFile, " = " coef_fmt " %s for %s and substitute\n", + factor, variable(alpha), variable(j)); + } + for (k = nVars; k >= 0; k--) + eq.coef[k] = int_mod_hat(eq.coef[k], factor); + nFactor = eq.coef[j]; + assert(nFactor == 1 || nFactor == -1); + eq.coef[alpha] = factor / nFactor; + if (DEBUG) { + fprintf(outputFile, "adjusted: "); + fprintf(outputFile, "Solve "); + printTerm(&eq, 1); + fprintf(outputFile, " = 0 for %s and substitute\n", variable(j)); + } + + eq.coef[j] = 0; + substitute(&eq, j, nFactor); + newVar = -1; + deleteVariable(j); + for (k = nVars; k >= 0; k--) { + assert(EQs[e].coef[k] % factor == 0); + EQs[e].coef[k] = EQs[e].coef[k] / factor; + } + if (DEBUG) { + fprintf(outputFile, "Mod-ing and normalizing produces:\n"); + printProblem(); + } +} + +void Problem::substitute(eqn *sub, int i, coef_t c) { + int e, j; + coef_t k; + int recordSubstitution = (i <= safeVars && var[i] >= 0); + + redType clr; + if (sub->color) + clr = redEQ; + else + clr = notRed; + + assert(c == 1 || c == -1); + + if (DBUG || doTrace) { + if (sub->color) + fprintf(outputFile, "RED SUBSTITUTION\n"); + fprintf(outputFile, "substituting using %s := ", variable(i)); + printTerm(sub, -c); + fprintf(outputFile, "\n"); + printVars(); + } +#ifndef NDEBUG + if (i > safeVars && clr) { + bool unsafeSub = false; + for (e = nEQs - 1; e >= 0; e--) + if (!(EQs[e].color || !EQs[e].coef[i])) + unsafeSub = true; + for (e = nGEQs - 1; e >= 0; e--) + if (!(GEQs[e].color || !GEQs[e].coef[i])) + unsafeSub = true; + for (e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i]) + unsafeSub = true; + if (unsafeSub) { + fprintf(outputFile, "UNSAFE RED SUBSTITUTION\n"); + fprintf(outputFile, "substituting using %s := ", variable(i)); + printTerm(sub, -c); + fprintf(outputFile, "\n"); + printProblem(); + assert(0 && "UNSAFE RED SUBSTITUTION"); + } + } +#endif + + for (e = nEQs - 1; e >= 0; e--) { + eqn *eq; + eq = &(EQs[e]); + k = eq->coef[i]; + if (k != 0) { + k = check_mul(k, c); // Should be k = k/c, but same effect since abs(c) == 1 + eq->coef[i] = 0; + for (j = nVars; j >= 0; j--) { + eq->coef[j] -= check_mul(sub->coef[j], k); + } + } + if (DEBUG) { + printEQ(eq); + fprintf(outputFile, "\n"); + } + } + for (e = nGEQs - 1; e >= 0; e--) { + int zero; + eqn *eq; + eq = &(GEQs[e]); + k = eq->coef[i]; + if (k != 0) { + k = check_mul(k, c); // Should be k = k/c, but same effect since abs(c) == 1 + eq->touched = true; + eq->coef[i] = 0; + zero = 1; + for (j = nVars; j >= 0; j--) { + eq->coef[j] -= check_mul(sub->coef[j], k); + if (j > 0 && eq->coef[j]) + zero = 0; + } + if (zero && clr != notRed && !eq->color) { + coef_t z = int_div(eq->coef[0], abs(k)); + if (DBUG || doTrace) { + fprintf(outputFile, + "Black inequality matches red substitution\n"); + if (z < 0) + fprintf(outputFile, "System is infeasible\n"); + else if (z > 0) + fprintf(outputFile, "Black inequality is redundant\n"); + else { + fprintf(outputFile, + "Black constraint partially implies red equality\n"); + if (k < 0) { + fprintf(outputFile, "Black constraints tell us "); + assert(sub->coef[i] == 0); + sub->coef[i] = c; + printTerm(sub, 1); + sub->coef[i] = 0; + fprintf(outputFile, "<= 0\n"); + } else { + fprintf(outputFile, "Black constraints tell us "); + assert(sub->coef[i] == 0); + sub->coef[i] = c; + printTerm(sub, 1); + sub->coef[i] = 0; + fprintf(outputFile, " >= 0\n"); + } + } + } + if (z == 0) { + if (k < 0) { + if (clr == redEQ) + clr = redGEQ; + else if (clr == redLEQ) + clr = notRed; + } else { + if (clr == redEQ) + clr = redLEQ; + else if (clr == redGEQ) + clr = notRed; + } + } + + } + } + if (DEBUG) { + printGEQ(eq); + fprintf(outputFile, "\n"); + } + } + if (i <= safeVars && clr) { + assert(sub->coef[i] == 0); + sub->coef[i] = c; + rememberRedConstraint(sub, clr, 0); + sub->coef[i] = 0; + } + + if (recordSubstitution) { + int s = nSUBs++; + int kk; + eqn *eq = &(SUBs[s]); + for (kk = nVars; kk >= 0; kk--) + eq->coef[kk] = check_mul(-c, (sub->coef[kk])); + eq->key = var[i]; + if (DEBUG) { + fprintf(outputFile, "Recording substition as: "); + printSubstitution(s); + fprintf(outputFile, "\n"); + } + } + if (DEBUG) { + fprintf(outputFile, "Ready to update subs\n"); + if (sub->color) + fprintf(outputFile, "RED SUBSTITUTION\n"); + fprintf(outputFile, "substituting using %s := ", variable(i)); + printTerm(sub, -c); + fprintf(outputFile, "\n"); + printVars(); + } + + for (e = nSUBs - 1; e >= 0; e--) { + eqn *eq = &(SUBs[e]); + k = eq->coef[i]; + if (k != 0) { + k = check_mul(k, c); // Should be k = k/c, but same effect since abs(c) == 1 + eq->coef[i] = 0; + for (j = nVars; j >= 0; j--) { + eq->coef[j] -= check_mul(sub->coef[j], k); + } + } + if (DEBUG) { + fprintf(outputFile, "updated sub (" coef_fmt "): ", c); + printSubstitution(e); + fprintf(outputFile, "\n"); + } + } + + if (DEBUG) { + fprintf(outputFile, "---\n\n"); + printProblem(); + fprintf(outputFile, "===\n\n"); + } +} + + +void Problem::doElimination(int e, int i) { + if (DBUG || doTrace) + fprintf(outputFile, "eliminating variable %s\n", variable(i)); + + eqn sub; + eqnncpy(&sub, &EQs[e], nVars); + coef_t c = sub.coef[i]; + sub.coef[i] = 0; + + if (c == 1 || c == -1) { + substitute(&sub, i, c); + } else { + coef_t a = abs(c); + if (TRACE) + fprintf(outputFile, + "performing non-exact elimination, c = " coef_fmt "\n", c); + if (DBUG) + printProblem(); + assert(inApproximateMode); + + for (int e2 = nEQs - 1; e2 >= 0; e2--) { + eqn *eq = &(EQs[e2]); + coef_t k = eq->coef[i]; + if (k != 0) { + coef_t l = lcm(abs(k), a); + coef_t scale1 = l / abs(k); + for (int j = nVars; j >= 0; j--) + eq->coef[j] = check_mul(eq->coef[j], scale1); + eq->coef[i] = 0; + coef_t scale2 = l / c; + if (k < 0) + scale2 = -scale2; + for (int j = nVars; j >= 0; j--) + eq->coef[j] -= check_mul(sub.coef[j], scale2); + eq->color |= sub.color; + } + } + for (int e2 = nGEQs - 1; e2 >= 0; e2--) { + eqn *eq = &(GEQs[e2]); + coef_t k = eq->coef[i]; + if (k != 0) { + coef_t l = lcm(abs(k), a); + coef_t scale1 = l / abs(k); + for (int j = nVars; j >= 0; j--) + eq->coef[j] = check_mul(eq->coef[j], scale1); + eq->coef[i] = 0; + coef_t scale2 = l / c; + if (k < 0) + scale2 = -scale2; + for (int j = nVars; j >= 0; j--) + eq->coef[j] -= check_mul(sub.coef[j], scale2); + eq->color |= sub.color; + eq->touched = 1; + } + } + for (int e2 = nSUBs - 1; e2 >= 0; e2--) + if (SUBs[e2].coef[i]) { + eqn *eq = &(EQs[e2]); + assert(0); + // We can't handle this since we can't multiply + // the coefficient of the left-hand side + assert(!sub.color); + for (int j = nVars; j >= 0; j--) + eq->coef[j] = check_mul(eq->coef[j], a); + coef_t k = eq->coef[i]; + eq->coef[i] = 0; + for (int j = nVars; j >= 0; j--) + eq->coef[j] -= check_mul(sub.coef[j], k / c); + } + } + deleteVariable(i); +} + +int Problem::solveEQ() { + check(); + + // Reorder equations according to complexity. + { + int delay[nEQs]; + + for (int e = 0; e < nEQs; e++) { + delay[e] = 0; + if (EQs[e].color) + delay[e] += 8; + int nonunitWildCards = 0; + int unitWildCards = 0; + for (int i = nVars; i > safeVars; i--) + if (EQs[e].coef[i]) { + if (EQs[e].coef[i] == 1 || EQs[e].coef[i] == -1) + unitWildCards++; + else + nonunitWildCards++; + } + int unit = 0; + int nonUnit = 0; + for (int i = safeVars; i > 0; i--) + if (EQs[e].coef[i]) { + if (EQs[e].coef[i] == 1 || EQs[e].coef[i] == -1) + unit++; + else + nonUnit++; + } + if (unitWildCards == 1 && nonunitWildCards == 0) + delay[e] += 0; + else if (unitWildCards >= 1 && nonunitWildCards == 0) + delay[e] += 1; + else if (inApproximateMode && nonunitWildCards > 0) + delay[e] += 2; + else if (unit == 1 && nonUnit == 0 && nonunitWildCards == 0) + delay[e] += 3; + else if (unit > 1 && nonUnit == 0 && nonunitWildCards == 0) + delay[e] += 4; + else if (unit >= 1 && nonunitWildCards <= 1) + delay[e] += 5; + else + delay[e] += 6; + } + + for (int e = 0; e < nEQs; e++) { + int e2, slowest; + slowest = e; + for (e2 = e + 1; e2 < nEQs; e2++) + if (delay[e2] > delay[slowest]) + slowest = e2; + if (slowest != e) { + int tmp = delay[slowest]; + delay[slowest] = delay[e]; + delay[e] = tmp; + eqn eq; + eqnncpy(&eq, &EQs[slowest], nVars); + eqnncpy(&EQs[slowest], &EQs[e], nVars); + eqnncpy(&EQs[e], &eq, nVars); + } + } + } + + // Eliminate all equations. + while (nEQs != 0) { + int e = nEQs - 1; + eqn *eq = &(EQs[e]); + coef_t g, g2; + + assert(mayBeRed || !eq->color); + + check(); + + // get gcd of coefficients of all unprotected variables + g2 = 0; + for (int i = nVars; i > safeVars; i--) + if (eq->coef[i] != 0) { + g2 = gcd(abs(eq->coef[i]), g2); + if (g2 == 1) + break; + } + + // get gcd of coefficients of all variables + g = g2; + if (g != 1) + for (int i = safeVars; i >= 1; i--) + if (eq->coef[i] != 0) { + g = gcd(abs(eq->coef[i]), g); + if (g == 1) + break; + } + + // approximate mode bypass integer modular test; in Farkas(), + // existential variable lambda's are rational numbers. + if (inApproximateMode && g2 != 0) + g = gcd(abs(eq->coef[0]), g); + + // simple test to see if the equation is satisfiable + if (g == 0) { + if (eq->coef[0] != 0) { + return (false); + } else { + nEQs--; + continue; + } + } else if (abs(eq->coef[0]) % g != 0) { + return (false); + } + + // set gcd of all coefficients to 1 + if (g != 1) { + for (int i = nVars; i >= 0; i--) + eq->coef[i] /= g; + g2 = g2 / g; + } + + // exact elimination of unit coefficient variable + if (g2 != 0) { // for constraint with unprotected variable + int i; + for (i = nVars; i > safeVars; i--) + if (abs(eq->coef[i]) == 1) + break; + if (i > safeVars) { + nEQs--; + doElimination(e, i); + continue; + } + } else { // for constraint without unprotected variable + + // pick the unit coefficient variable with complex inequalites + // to eliminate, this will make inequalities tighter. e.g. + // {[t4,t6,t10]:exists (alpha: 0<=t6<=3 && t10=4alpha+t6 && + // 64t4<=t10<=64t4+15)} + int unit_var; + int cost = -1; + + for (int i = safeVars; i > 0; i--) + + if (abs(eq->coef[i]) == 1) { + int cur_cost = 0; + for (int j = 0; j < nGEQs; j++) + if (GEQs[j].coef[i] != 0) { + for (int k = safeVars; k > 0; k--) + if (GEQs[j].coef[k] != 0) { + if (abs(GEQs[j].coef[k]) != 1){ + + cur_cost += 3; + + } + else + cur_cost += 1; + } + } + + if (cur_cost > cost) { + cost = cur_cost; + unit_var = i; + } + + } + + if (cost != -1) { + nEQs--; + doElimination(e, unit_var); + continue; + } + + + } + + // check if there is an unprotected variable as wildcard + if (g2 > 0) { + int pos = 0; + coef_t g3; + for (int k = nVars; k > safeVars; k--) + if (eq->coef[k] != 0) { + int e2; + for (e2 = e - 1; e2 >= 0; e2--) + if (EQs[e2].coef[k]) + break; + if (e2 >= 0) + continue; + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[k]) + break; + if (e2 >= 0) + continue; + for (e2 = nSUBs - 1; e2 >= 0; e2--) + if (SUBs[e2].coef[k]) + break; + if (e2 >= 0) + continue; + + if (pos == 0) { + g3 = abs(eq->coef[k]); + pos = k; + } else { + if (abs(eq->coef[k]) < g3) { + g3 = abs(eq->coef[k]); + pos = k; + } + } + } + + if (pos != 0) { + bool change = false; + for (int k2 = nVars; k2 >= 0; k2--) + if (k2 != pos && eq->coef[k2] != 0) { + coef_t t = int_mod_hat(eq->coef[k2], g3); + if (t != eq->coef[k2]) { + eq->coef[k2] = t; + change = true; + } + } + + // strength reduced, try this equation again + if (change) { + // nameWildcard(pos); + continue; + } + } + } + + // insert new stride constraint + if (g2 > 1 && !(inApproximateMode && !inStridesAllowedMode)) { + int newvar = addNewProtectedWildcard(); + int neweq = newEQ(); + assert(neweq == e+1); + // we were working on highest-numbered EQ + eqnnzero(&EQs[neweq], nVars); + eqnncpy(&EQs[neweq], eq, safeVars); + + for (int k = nVars; k >= 0; k--) { + EQs[neweq].coef[k] = int_mod_hat(EQs[neweq].coef[k], g2); + } + if (EQs[e].color) + rememberRedConstraint(&EQs[neweq], redStride, g2); + EQs[neweq].coef[newvar] = g2; + EQs[neweq].color = EQ_BLACK; + continue; + } + + // inexact elimination of unprotected variable + if (g2 > 0 && inApproximateMode) { + int pos = 0; + for (int k = nVars; k > safeVars; k--) + if (eq->coef[k] != 0) { + pos = k; + break; + } + assert(pos > safeVars); + + // special handling for wildcard used in breaking down + // diophantine equation + if (abs(eq->coef[pos]) > 1) { + int e2; + for (e2 = nSUBs - 1; e2 >= 0; e2--) + if (SUBs[e2].coef[pos]) + break; + if (e2 >= 0) { + protectWildcard(pos); + continue; + } + } + + nEQs--; + doElimination(e, pos); + continue; + } + + // now solve linear diophantine equation using least remainder + // algorithm + { + coef_t factor = (posInfinity); // was MAXINT + int pos = 0; + for (int k = nVars; k > (g2 > 0 ? safeVars : 0); k--) + if (eq->coef[k] != 0 && factor > abs(eq->coef[k]) + 1) { + factor = abs(eq->coef[k]) + 1; + pos = k; + } + assert(pos > (g2>0?safeVars:0)); + doMod(factor, e, pos); + continue; + } + } + + assert(nEQs == 0); + return (OC_SOLVE_UNKNOWN); +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_exp_kill.cc b/omegalib/omega/src/omega_core/oc_exp_kill.cc new file mode 100644 index 0000000..fdb2718 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_exp_kill.cc @@ -0,0 +1,297 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Expensive inequality elimination. + + Notes: + + History: + 03/31/09 Use BoolSet, Chun Chen +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <basic/BoolSet.h> +#include <vector> + +namespace omega { + +int Problem::expensiveKill() { + int e; + if (TRACE) fprintf(outputFile,"Performing expensive kill tests: [\n"); + if (DBUG) printProblem(); + Problem tmpProblem; + int oldTrace = trace; + int constraintsRemoved = 0; + + trace = 0; + conservative++; + + for (e = nGEQs - 1; e >= 0; e--) + if (!GEQs[e].essential) { + if (DBUG) { + fprintf(outputFile, "checking equation %d to see if it is redundant: ", e); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + } + tmpProblem = *this; + tmpProblem.negateGEQ(e); + tmpProblem.varsOfInterest = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.safeVars = 0; + tmpProblem.variablesFreed = 0; + tmpProblem.isTemporary = true; + + if (!tmpProblem.solve(false)) { + if (DBUG) + fprintf(outputFile, "redundant!\n"); + constraintsRemoved++; + deleteGEQ(e); + } + } + + if (constraintsRemoved) { + if (TRACE) fprintf(outputFile,"%d Constraints removed!!\n",constraintsRemoved); + } + + trace = oldTrace; + conservative--; + if (TRACE) fprintf(outputFile,"] expensive kill tests done\n"); + return 1; +} + +int Problem::expensiveRedKill() { + int e; + if (TRACE) fprintf(outputFile,"Performing expensive red kill tests: [\n"); + Problem tmpProblem; + int oldTrace = trace; + int constraintsRemoved = 0; + + trace = 0; + conservative++; + + for (e = nGEQs - 1; e >= 0; e--) + if (!GEQs[e].essential && GEQs[e].color) { + if (DEBUG) { + fprintf(outputFile, "checking equation %d to see if it is redundant: ", e); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + } + tmpProblem = *this; + tmpProblem.negateGEQ(e); + tmpProblem.varsOfInterest = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.safeVars = 0; + tmpProblem.variablesFreed = 0; + tmpProblem.isTemporary = true; + tmpProblem.turnRedBlack(); + if (!tmpProblem.solve(false)) { + constraintsRemoved++; + deleteGEQ(e); + } + } + + if (constraintsRemoved) { + if (TRACE) fprintf(outputFile,"%d Constraints removed!!\n",constraintsRemoved); + } + + trace = oldTrace; + conservative--; + if (TRACE) fprintf(outputFile,"] expensive red kill tests done\n"); + return 1; +} + + +int Problem::expensiveEqualityCheck() { + int e; + return 1; + if (TRACE) fprintf(outputFile,"Performing expensive equality tests: [\n"); + Problem tmpProblem; + int oldTrace = trace; + int equalitiesFound = 0; + + trace = 0; + conservative++; + + for (e = nGEQs - 1; e >= 0; e--) { + if (DEBUG) { + fprintf(outputFile, "checking equation %d to see if it is an equality: ", e); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + } + tmpProblem = *this; + tmpProblem.GEQs[e].coef[0]--; + tmpProblem.varsOfInterest = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.safeVars = 0; + tmpProblem.variablesFreed = 0; + tmpProblem.isTemporary = true; + if (!tmpProblem.solve(false)) { + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + equalitiesFound++; + addingEqualityConstraint(neweq); + } + } + if (equalitiesFound) { + if (TRACE) fprintf(outputFile,"%d Equalities found!!\n",equalitiesFound); + } + + trace = oldTrace; + conservative--; + if (equalitiesFound) { + if (!solveEQ()) return 0; + if (!normalize()) return 0; + } + if (TRACE) fprintf(outputFile,"] expensive equality tests done\n"); + return 1; +} + + +void Problem::quickRedKill(int computeGist) { + if (DBUG) { + fprintf(outputFile, "in quickRedKill: [\n"); + printProblem(); + } + + noteEssential(0); + int moreToDo = chainKill(1,0); + +#ifdef NDEBUG + if (!moreToDo) { + if (DBUG) fprintf(outputFile, "] quickRedKill\n"); + return; + } +#endif + + int isDead[nGEQs]; + int deadCount = 0; + std::vector<BoolSet<> > P(nGEQs, BoolSet<>(nVars)), Z(nGEQs, BoolSet<>(nVars)), N(nGEQs, BoolSet<>(nVars)); + BoolSet<> PP, PZ, PN; /* possible Positives, possible zeros & possible negatives */ + BoolSet<> MZ; /* must zeros */ + + int equationsToKill = 0; + for (int e = nGEQs - 1; e >= 0; e--) { + isDead[e] = 0; + if (GEQs[e].color && !GEQs[e].essential) equationsToKill++; + if (GEQs[e].color && GEQs[e].essential && !computeGist) + if (!moreToDo) { + if (DBUG) fprintf(outputFile, "] quickRedKill\n"); + return; + } + for (int i = nVars; i >= 1; i--) { + if (GEQs[e].coef[i] > 0) + P[e].set(i-1); + else if (GEQs[e].coef[i] < 0) + N[e].set(i-1); + else + Z[e].set(i-1); + } + } + + if (!equationsToKill) + if (!moreToDo) { + if (DBUG) fprintf(outputFile, "] quickRedKill\n"); + return; + } + + for (int e = nGEQs - 1; e > 0; e--) + if (!isDead[e]) + for (int e2 = e - 1; e2 >= 0; e2--) + if (!isDead[e2]) { + coef_t a = 0; + int i, j; + for (i = nVars; i > 1; i--) { + for (j = i - 1; j > 0; j--) { + a = (GEQs[e].coef[i] * GEQs[e2].coef[j] - GEQs[e2].coef[i] * GEQs[e].coef[j]); + if (a != 0) + goto foundPair; + } + } + continue; + + foundPair: + if (DEBUG) { + fprintf(outputFile, "found two equations to combine, i = %s, ", variable(i)); + fprintf(outputFile, "j = %s, alpha = " coef_fmt "\n", variable(j), a); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + } + + MZ = (Z[e] & Z[e2]); + PZ = MZ | (P[e] & N[e2]) | (N[e] & P[e2]); + PP = P[e] | P[e2]; + PN = N[e] | N[e2]; + + for (int e3 = nGEQs - 1; e3 >= 0; e3--) + if (e3 != e && e3 != e2 && GEQs[e3].color && !GEQs[e3].essential) { + coef_t alpha1, alpha2, alpha3; + + if (!PZ.imply(Z[e3]) || MZ.imply(~Z[e3])) continue; + if (!PP.imply(P[e3]) || !PN.imply(N[e3])) continue; + + if (a > 0) { + alpha1 = GEQs[e2].coef[j] * GEQs[e3].coef[i] - GEQs[e2].coef[i] * GEQs[e3].coef[j]; + alpha2 = -(GEQs[e].coef[j] * GEQs[e3].coef[i] - GEQs[e].coef[i] * GEQs[e3].coef[j]); + alpha3 = a; + } + else { + alpha1 = -(GEQs[e2].coef[j] * GEQs[e3].coef[i] - GEQs[e2].coef[i] * GEQs[e3].coef[j]); + alpha2 = -(-(GEQs[e].coef[j] * GEQs[e3].coef[i] - GEQs[e].coef[i] * GEQs[e3].coef[j])); + alpha3 = -a; + } + + if (alpha1 > 0 && alpha2 > 0) { + if (DEBUG) { + fprintf(outputFile, "alpha1 = " coef_fmt ", alpha2 = " coef_fmt "; comparing against: ", alpha1, alpha2); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n"); + } + coef_t c; + int k; + for (k = nVars; k >= 0; k--) { + c = alpha1 * GEQs[e].coef[k] + alpha2 * GEQs[e2].coef[k]; + if (DEBUG) { + if (k>0) + fprintf(outputFile, " %s: " coef_fmt ", " coef_fmt "\n", variable(k), c, alpha3 * GEQs[e3].coef[k]); + else fprintf(outputFile, " constant: " coef_fmt ", " coef_fmt "\n", c, alpha3 * GEQs[e3].coef[k]); + } + if (c != alpha3 * GEQs[e3].coef[k]) + break; + } + if (k < 0 || (k == 0 && c < alpha3 * (GEQs[e3].coef[k]+1))) { + if (DEBUG) { + deadCount++; + fprintf(outputFile, "red equation#%d is dead (%d dead so far, %d remain)\n", e3, deadCount, nGEQs - deadCount); + printGEQ(&(GEQs[e])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n"); + assert(moreToDo); + } + isDead[e3] = 1; + } + } + } + } + + for (int e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) + deleteGEQ(e); + + if (DBUG) { + fprintf(outputFile,"] quickRedKill\n"); + printProblem(); + } +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_global.cc b/omegalib/omega/src/omega_core/oc_global.cc new file mode 100644 index 0000000..17d8a0c --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_global.cc @@ -0,0 +1,45 @@ +#include <omega/omega_core/oc_i.h> + +namespace omega { + +const int Problem::min_alloc = 10; +const int Problem::first_alloc_pad = 5; + +int omega_core_debug = 0; // 3: full debugging info + +int maxEQs = 100; // original 35, increased by chun +int maxGEQs = 200; // original 70, increased by chun + +int newVar = -1; +int findingImplicitEqualities = 0; +int firstCheckForRedundantEquations = 0; +int doItAgain; +int conservative = 0; +FILE *outputFile = stderr; /* printProblem writes its output to this file */ +char wildName[200][20]; +int nextWildcard = 0; +int trace = 1; +int depth = 0; +int headerLevel; +int inApproximateMode = 0; +int inStridesAllowedMode = 0; +int addingOuterEqualities = 0; +int outerColor = 0; +int reduceWithSubs = 1; +int pleaseNoEqualitiesInSimplifiedProblems = 0; +Problem *originalProblem = noProblem; +int omegaInitialized = 0; +int mayBeRed = 0; + + +// Hash table is used to hash all inequalties for all problems. It +// persists across problems for quick problem merging in case. When +// the table is filled to 1/3 full, it is flushed and the filling +// process starts all over again. +int packing[maxVars]; +int hashVersion = 0; +eqn hashMaster[hashTableSize]; +int fastLookup[maxKeys*2]; +int nextKey; + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_print.cc b/omegalib/omega/src/omega_core/oc_print.cc new file mode 100644 index 0000000..7934713 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_print.cc @@ -0,0 +1,686 @@ +#include <omega/omega_core/oc_i.h> + +namespace omega { + +int print_in_code_gen_style = 0; + +void Problem::initializeVariables() const { + Problem *p = (Problem *)this; + int i; + assert(!p->variablesInitialized); + for (i = p->nVars; i >= 0; i--) + p->forwardingAddress[i] = p->var[i] = i; + p->variablesInitialized = 1; +} + + +std::string Problem::print_term_to_string(const eqn *e, int c) const { + std::string s=""; + int i; + int first; + int n = nVars; + int wentFirst = -1; + first = 1; + for (i = 1; i <= n; i++) + if (c * e->coef[i] > 0) { + first = 0; + wentFirst = i; + + if (c * e->coef[i] == 1) + s+= variable(i); + else { + s += to_string(c * e->coef[i]); + if (print_in_code_gen_style) s += "*"; + s += variable(i); + } + break; + } + for (i = 1; i <= n; i++) + if (i != wentFirst && c * e->coef[i] != 0) { + if (!first && c * e->coef[i] > 0) + s += "+"; + + first = 0; + + if (c * e->coef[i] == 1) + s += variable(i); + else if (c * e->coef[i] == -1) { + s += "-"; s += variable(i); + } + else { + s += to_string(c * e->coef[i]); + if (print_in_code_gen_style) s += "*"; + s += variable(i); + } + } + if (!first && c * e->coef[0] > 0) + s += "+"; + if (first || c * e->coef[0] != 0) + s += to_string(c * e->coef[0]); + return s; +} + + +void Problem::printTerm(const eqn * e, int c) const { + std::string s = print_term_to_string(e, c); + fprintf(outputFile, "%s", s.c_str()); +} + + +void Problem::printSub(int v) const { + std::string s = print_sub_to_string(v); + fprintf(outputFile, "%s", s.c_str()); +} + + +std::string Problem::print_sub_to_string(int v) const { + std::string s; + + if (v > 0) + s = variable(v); + else + s = print_term_to_string(&SUBs[-v-1], 1); + return s; +} + + +void Problem::clearSubs() { + nSUBs = 0; + nMemories = 0; +} + + +void Problem::printEqn(const eqn *e, int test, int extra) const { + char buf[maxVars * 12 + 180]; // original buf[maxVars * 12 + 80] + + sprintEqn(buf, e, test, extra); + fprintf(outputFile, "%s", buf); +} + + +std::string Problem::printEqnToString(const eqn *e, int test, int extra) const { + char buf[maxVars * 12 + 180]; // original buf[maxVars * 12 + 80] + sprintEqn(buf, e, test, extra); + return std::string(buf); +} + + +void Problem::sprintEqn(char *str, const eqn *e, int test, int extra) const { + int i; + int first; + int n = nVars + extra; + int isLT; + + isLT = test && e->coef[0] == -1; + if (isLT) + isLT = 1; +#if 0 + if (test) { + if (DEBUG && e->touched) { + sprintf(str, "!"); + while (*str) + str++; + } + else if (DBUG && !e->touched && e->key != 0) { + sprintf(str, "%d: ", e->key); + while (*str) + str++; + } + } +#endif + if (e->color) { + sprintf(str, "["); + while (*str) + str++; + } + first = 1; + for (i = isLT; i <= n; i++) + if (e->coef[i] < 0) { + if (!first) { + sprintf(str, "+"); + while (*str) + str++; + } + else + first = 0; + if (i == 0) { + sprintf(str, coef_fmt, -e->coef[i]); + while (*str) + str++; + } + else if (e->coef[i] == -1) { + sprintf(str, "%s", variable(i)); + while (*str) + str++; + } + else { + if (print_in_code_gen_style) + sprintf(str, coef_fmt "*%s", -e->coef[i], variable(i)); + else sprintf(str, coef_fmt "%s", -e->coef[i], variable(i)); + while (*str) + str++; + } + } + if (first) { + if (isLT) { + sprintf(str, "1"); + isLT = 0; + } + else + sprintf(str, "0"); + while (*str) + str++; + } + if (test == 0) { + if (print_in_code_gen_style) sprintf(str, " == "); + else sprintf(str, " = "); + while (*str) + str++; + } + else { + if (isLT) + sprintf(str, " < "); + else + sprintf(str, " <= "); + while (*str) + str++; + } + + first = 1; + for (i = 0; i <= n; i++) + if (e->coef[i] > 0) { + if (!first) { + sprintf(str, "+"); + while (*str) + str++; + } + else + first = 0; + if (i == 0) { + sprintf(str, coef_fmt , e->coef[i]); + while (*str) + str++; + } + else if (e->coef[i] == 1) { + sprintf(str, "%s", variable(i)); + while (*str) + str++; + } + else { + if (print_in_code_gen_style) + sprintf(str, coef_fmt "*%s", e->coef[i], variable(i)); + else + sprintf(str, coef_fmt "%s", e->coef[i], variable(i)); + while (*str) + str++; + } + } + if (first) { + sprintf(str, "0"); + while (*str) + str++; + } + if (e->color) { + sprintf(str, "]"); + while (*str) + str++; + } +} + + +void Problem::printSubstitution(int s) const { + const eqn * eq = &(SUBs[s]); + assert(eq->key > 0); + fprintf(outputFile, "%s := ", orgVariable(eq->key)); + printTerm(eq, 1); +} + + +void Problem::printVars(int /*debug*/) const { + int i; + fprintf(outputFile, "variables = "); + if (safeVars > 0) + fprintf(outputFile, "("); + for (i = 1; i <= nVars; i++) { + fprintf(outputFile, "%s", variable(i)); + if (i == safeVars) + fprintf(outputFile, ")"); + if (i < nVars) + fprintf(outputFile, ", "); + } + fprintf(outputFile, "\n"); + /* + fprintf(outputFile, "forward addresses = "); + if (safeVars > 0) + fprintf(outputFile, "("); + for (i = 1; i <= nVars; i++) + { + int v = forwardingAddress[i]; + if (v > 0) fprintf(outputFile, "%s", variable(i)); + else fprintf(outputFile, "*"); + if (i == safeVars) + fprintf(outputFile, ")"); + if (i < nVars) + fprintf(outputFile, ", "); + }; + fprintf(outputFile, "\n"); + */ +} + + +void printHeader() { + int i; + for(i=0; i<headerLevel; i++) { + fprintf(outputFile, ". "); + } +} + + +void Problem::printProblem(int debug) const { + int e; + + if (!variablesInitialized) + initializeVariables(); + if (debug) { + printHeader(); + fprintf(outputFile, "#vars = %d, #EQ's = %d, #GEQ's = %d, # SUB's = %d, ofInterest = %d\n", + nVars,nEQs,nGEQs,nSUBs,varsOfInterest); + printHeader(); + printVars(debug); + } + for (e = 0; e < nEQs; e++) { + printHeader(); + printEQ(&EQs[e]); + fprintf(outputFile, "\n"); + } + for (e = 0; e < nGEQs; e++) { + printHeader(); + printGEQ(&GEQs[e]); + fprintf(outputFile, "\n"); + } + for (e = 0; e < nSUBs; e++) { + printHeader(); + printSubstitution(e); + fprintf(outputFile, "\n"); + } + + for (e = 0; e < nMemories; e++) { + int i; + printHeader(); + switch(redMemory[e].kind) { + case notRed: + fprintf(outputFile,"notRed: "); + break; + case redGEQ: + fprintf(outputFile,"Red: 0 <= "); + break; + case redLEQ: + fprintf(outputFile,"Red ??: 0 >= "); + break; + case redEQ: + fprintf(outputFile,"Red: 0 == "); + break; + case redStride: + fprintf(outputFile,"Red stride " coef_fmt ": ", redMemory[e].stride); + break; + } + fprintf(outputFile," " coef_fmt, redMemory[e].constantTerm); + for(i=0;i< redMemory[e].length; i++) + if(redMemory[e].coef[i] >= 0) + fprintf(outputFile,"+" coef_fmt "%s", redMemory[e].coef[i], orgVariable(redMemory[e].var[i])); + else + fprintf(outputFile,"-" coef_fmt "%s", -redMemory[e].coef[i], orgVariable(redMemory[e].var[i])); + fprintf(outputFile, "\n"); + } + fflush(outputFile); + + CHECK_FOR_DUPLICATE_VARIABLE_NAMES; +} + + +void Problem::printRedEquations() const { + int e; + + if (!variablesInitialized) + initializeVariables(); + printVars(1); + for (e = 0; e < nEQs; e++) { + if (EQs[e].color == EQ_RED) { + printEQ(&EQs[e]); + fprintf(outputFile, "\n"); + } + } + for (e = 0; e < nGEQs; e++) { + if (GEQs[e].color == EQ_RED) { + printGEQ(&GEQs[e]); + fprintf(outputFile, "\n"); + } + } + for (e = 0; e < nSUBs; e++) { + if (SUBs[e].color) { + printSubstitution(e); + fprintf(outputFile, "\n"); + } + } + fflush(outputFile); +} + + +int Problem::prettyPrintProblem() const { + std::string s = prettyPrintProblemToString(); + fprintf(outputFile, "%s", s.c_str()); + fflush(outputFile); + return 0; +} + + +std::string Problem::prettyPrintProblemToString() const { + std::string s=""; + int e; + int v; + int live[maxmaxGEQs]; + int v1, v2, v3; + int t, change; + int stuffPrinted = 0; + const char *connector = " && "; + + typedef enum { + none, le, lt + } partialOrderType; + + partialOrderType po[maxVars][maxVars]; + int poE[maxVars][maxVars]; + int lastLinks[maxVars]; + int firstLinks[maxVars]; + int chainLength[maxVars]; + int chain[maxVars]; + int varCount[maxVars]; + int i, m, multiprint; + + + if (!variablesInitialized) + initializeVariables(); + + if (nVars > 0) { + for (e = 0; e < nEQs; e++) { + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + s += print_EQ_to_string(&EQs[e]); + } + + for (e = 0; e < nGEQs; e++) { + live[e] = true; + varCount[e] = 0; + for (v = 1; v <= nVars; v++) + if (GEQs[e].coef[v]) varCount[e]++; + } + + if (!print_in_code_gen_style) + while (1) { + for (v = 1; v <= nVars; v++) { + lastLinks[v] = firstLinks[v] = 0; + chainLength[v] = 0; + for (v2 = 1; v2 <= nVars; v2++) + po[v][v2] = none; + } + + for (e = 0; e < nGEQs; e++) + if (live[e] && varCount[e] <= 2) { + for (v = 1; v <= nVars; v++) { + if (GEQs[e].coef[v] == 1) + firstLinks[v]++; + else if (GEQs[e].coef[v] == -1) + lastLinks[v]++; + } + + v1 = nVars; + while (v1 > 0 && GEQs[e].coef[v1] == 0) + v1--; + v2 = v1 - 1; + while (v2 > 0 && GEQs[e].coef[v2] == 0) + v2--; + v3 = v2 - 1; + while (v3 > 0 && GEQs[e].coef[v3] == 0) + v3--; + + if (GEQs[e].coef[0] > 0 || GEQs[e].coef[0] < -1 + || v2 <= 0 || v3 > 0 + || GEQs[e].coef[v1] * GEQs[e].coef[v2] != -1) { + /* Not a partial order relation */ + + } + else { + if (GEQs[e].coef[v1] == 1) { + v3 = v2; + v2 = v1; + v1 = v3; + } + /* relation is v1 <= v2 or v1 < v2 */ + po[v1][v2] = ((GEQs[e].coef[0] == 0) ? le : lt); + poE[v1][v2] = e; + } + } + for (v = 1; v <= nVars; v++) + chainLength[v] = lastLinks[v]; + + /* + * printf("\n\nPartial order:\n"); printf(" "); for (v1 = 1; v1 <= nVars; v1++) + * printf("%7s",variable(v1)); printf("\n"); for (v1 = 1; v1 <= nVars; v1++) { printf("%6s: + * ",variable(v1)); for (v2 = 1; v2 <= nVars; v2++) switch (po[v1][v2]) { case none: printf(" "); + * break; case le: printf(" <= "); break; case lt: printf(" < "); break; } printf("\n"); } + */ + + + /* Just in case nVars <= 0 */ + change = false; + for (t = 0; t < nVars; t++) { + change = false; + for (v1 = 1; v1 <= nVars; v1++) + for (v2 = 1; v2 <= nVars; v2++) + if (po[v1][v2] != none && + chainLength[v1] <= chainLength[v2]) { + chainLength[v1] = chainLength[v2] + 1; + change = true; + } + } + + if (change) { + /* caught in cycle */ + +#if 0 + printf("\n\nPartial order:\n"); printf(" "); + for (v1 = 1; v1 <= nVars; v1++) printf("%7s",variable(v1)); printf("\n"); + for (v1 = 1; v1 <= nVars; v1++) { + printf("%6s: ",variable(v1)); + for (v2 = 1; v2 <= nVars; v2++) switch (po[v1][v2]) { + case none: printf(" "); break; + case le: printf(" <= "); break; + case lt: printf(" < "); break; + } + printf("\n"); + } + + printProblem(1); +#endif + break; + } + + for (v1 = 1; v1 <= nVars; v1++) + if (chainLength[v1] == 0) + firstLinks[v1] = 0; + + v = 1; + for (v1 = 2; v1 <= nVars; v1++) + if (chainLength[v1] + firstLinks[v1] > chainLength[v] + firstLinks[v]) + v = v1; + + if (chainLength[v] + firstLinks[v] == 0) + break; + + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + /* chain starts at v */ + /* print firstLinks */ + { + coef_t tmp; + int first; + first = 1; + for (e = 0; e < nGEQs; e++) + if (live[e] && GEQs[e].coef[v] == 1 && varCount[e] <= 2) { + if (!first) + s += ", "; + tmp = GEQs[e].coef[v]; + ((Problem *)this)-> + GEQs[e].coef[v] = 0; + s += print_term_to_string(&GEQs[e], -1); + ((Problem *)this)-> + GEQs[e].coef[v] = tmp; + live[e] = false; + first = 0; + } + if (!first) + s += " <= "; + } + + + /* find chain */ + chain[0] = v; + m = 1; + while (1) { + /* print chain */ + for (v2 = 1; v2 <= nVars; v2++) + if (po[v][v2] && chainLength[v] == 1 + chainLength[v2]) + break; + if (v2 > nVars) + break; + chain[m++] = v2; + v = v2; + } + + s += variable(chain[0]); + + multiprint = 0; + for (i = 1; i < m; i++) { + v = chain[i - 1]; + v2 = chain[i]; + if (po[v][v2] == le) + s += " <= "; + else + s += " < "; + s += variable(v2); + live[poE[v][v2]] = false; + if (!multiprint && i < m - 1) + for (v3 = 1; v3 <= nVars; v3++) { + if (v == v3 || v2 == v3) + continue; + if (po[v][v2] != po[v][v3]) + continue; + if (po[v2][chain[i + 1]] != po[v3][chain[i + 1]]) + continue; + s += ","; s += variable(v3); + live[poE[v][v3]] = false; + live[poE[v3][chain[i + 1]]] = false; + multiprint = 1; + } + else + multiprint = 0; + } + + v = chain[m - 1]; + /* print lastLinks */ + { + coef_t tmp; + int first; + first = 1; + for (e = 0; e < nGEQs; e++) + if (live[e] && GEQs[e].coef[v] == -1 && varCount[e] <= 2) { + if (!first) + s += ", "; + else + s += " <= "; + tmp = GEQs[e].coef[v]; + ((Problem *)this)-> + GEQs[e].coef[v] = 0; + s += print_term_to_string(&GEQs[e], 1); + ((Problem *)this)-> + GEQs[e].coef[v] = tmp; + live[e] = false; + first = 0; + } + } + } + + + for (e = 0; e < nGEQs; e++) + if (live[e]) { + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + s += print_GEQ_to_string(&GEQs[e]); + } + + for (e = 0; e < nSUBs; e++) { + const eqn * eq = &SUBs[e]; + if (stuffPrinted) + s += connector; + stuffPrinted = 1; + if (eq->key > 0) { + s += orgVariable(eq->key); s += " := "; + } + else { + s += "#"; s += to_string(eq->key); s += " := "; + } + s += print_term_to_string(eq, 1); + } + } + return s; +} + + +int Problem::prettyPrintRedEquations() const { + int e, stuffPrinted = 0; + const char *connector = " && "; + + if (!variablesInitialized) + initializeVariables(); + + for (e = 0; e < nEQs; e++) { + if (EQs[e].color == EQ_RED) { + if (stuffPrinted) + fprintf(outputFile, "%s", connector); + stuffPrinted = 1; + ((Problem *)this)-> + EQs[e].color = EQ_BLACK; + printEQ(&EQs[e]); + ((Problem *)this)-> + EQs[e].color = EQ_RED; + } + } + for (e = 0; e < nGEQs; e++) { + if (GEQs[e].color == EQ_RED) { + if (stuffPrinted) + fprintf(outputFile, "%s", connector); + stuffPrinted = 1; + ((Problem *)this)-> + GEQs[e].color = EQ_BLACK; + printGEQ(&GEQs[e]); + ((Problem *)this)-> + GEQs[e].color = EQ_RED; + } + } + for (e = 0; e < nSUBs; e++) { + if (SUBs[e].color) { + if (stuffPrinted) + fprintf(outputFile, "%s", connector); + stuffPrinted = 1; + printSubstitution(e); + } + } + fflush(outputFile); + + return 0; +} + +} diff --git a/omegalib/omega/src/omega_core/oc_problems.cc b/omegalib/omega/src/omega_core/oc_problems.cc new file mode 100644 index 0000000..8b6e04c --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_problems.cc @@ -0,0 +1,198 @@ +#include <omega/omega_core/oc_i.h> +#include <basic/omega_error.h> + +namespace omega { + +Problem::~Problem() { + delete[] EQs; + delete[] GEQs; +} + + +void check_number_EQs(int n) { + if (n < 0) { + fprintf(stderr,"ERROR: nEQs < 0??\n"); + exit(1); + } + + if (n > maxmaxEQs) { + // clear global variables + inApproximateMode = 0; + outerColor = 0; + + throw presburger_error("\nERROR:\n" + "An attempt was made to set the number of available equality constraints to " + to_string(n) + ".\n" + "The maximum number of equality constraints in a conjunction is " + to_string(maxmaxEQs) + ".\n" + "This limit can be changed by redefining maxmaxEQs in oc.h and recompiling.\n\n"); + + // fprintf(stderr, "\nERROR:\n"); + // fprintf(stderr, "An attempt was made to set the number of available equality constraints to %d.\n", n); + // fprintf(stderr, "The maximum number of equality constraints in a conjunction is %d.\n", maxmaxEQs); + // fprintf(stderr, "This limit can be changed by redefining maxmaxEQs in oc.h and recompiling.\n\n"); + // exit(2); + } +} + +void check_number_GEQs(int n) { + if (n < 0) { + fprintf(stderr,"ERROR: nGEQs < 0??\n"); + exit(1); + } + + if (n > maxmaxGEQs) { + // clear global variables + inApproximateMode = 0; + outerColor = 0; + + throw presburger_error("\nERROR:\n" + "An attempt was made to set the number of available inequality constraints to " + to_string(n) +".\n" + "The maximum number of inequality constraints in a conjunction is " + to_string(maxmaxGEQs) +".\n" + "This limit can be changed by redefining maxmaxGEQs in oc.h and recompiling.\n\n"); + + // fprintf(stderr, "\nERROR:\n"); + // fprintf(stderr, "An attempt was made to set the number of available inequality constraints to %d.\n", n); + // fprintf(stderr, "The maximum number of inequality constraints in a conjunction is %d.\n", maxmaxGEQs); + // fprintf(stderr, "This limit can be changed by redefining maxmaxGEQs in oc.h and recompiling.\n\n"); + // exit(2); + } +} + + +void check_number_EQs_GEQs(int e, int g) { + check_number_EQs(e); + check_number_GEQs(g); +} + + +Problem::Problem(int in_eqs, int in_geqs) { + check_number_EQs_GEQs(in_eqs, in_geqs); + allocEQs = padEQs(in_eqs); + allocGEQs = padGEQs(in_geqs); + assert(allocEQs > 0 && allocGEQs > 0); + EQs = new eqn[allocEQs]; + GEQs = new eqn[allocGEQs]; + nVars = 0; + hashVersion = omega::hashVersion; + variablesInitialized = 0; + variablesFreed = 0; + varsOfInterest = 0; + safeVars = 0; + nEQs = 0; + nGEQs = 0; + nSUBs = 0; + nMemories = 0; + isTemporary = false; +} + +Problem::Problem(const Problem & p2) { + allocEQs = padEQs(p2.nEQs); // Don't over-allocate; p2 might have too many! + allocGEQs = padGEQs(p2.nGEQs); + assert(allocEQs > 0 && allocGEQs > 0); + EQs = new eqn[allocEQs]; + GEQs = new eqn[allocGEQs]; + int e, i; + nVars = p2.nVars; + hashVersion = p2.hashVersion; + variablesInitialized = p2.variablesInitialized; + variablesFreed = p2.variablesFreed; + varsOfInterest = p2.varsOfInterest; + safeVars = p2.safeVars; + nEQs = p2.nEQs; + isTemporary = p2.isTemporary; + //nSUBs = 0; + for (e = p2.nEQs - 1; e >= 0; e--) + eqnncpy(&(EQs[e]), &(p2.EQs[e]), p2.nVars); + nGEQs = p2.nGEQs; + for (e = p2.nGEQs - 1; e >= 0; e--) + eqnncpy(&(GEQs[e]), &(p2.GEQs[e]), p2.nVars); + for (i = 0; i <= p2.nVars; i++) + var[i] = p2.var[i]; + for (i = 0; i <= maxVars; i++) + forwardingAddress[i] = p2.forwardingAddress[i]; + //nMemories = 0; + get_var_name = p2.get_var_name; + getVarNameArgs = p2.getVarNameArgs; +} + +Problem & Problem::operator=(const Problem & p2) { + if (this != &p2) { + if(allocEQs < p2.nEQs) { + delete[] EQs; + allocEQs = padEQs(p2.nEQs); + EQs = new eqn[allocEQs]; + } + if(allocGEQs < p2.nGEQs) { + delete[] GEQs; + allocGEQs = padGEQs(p2.nGEQs); + GEQs = new eqn[allocGEQs]; + } + int e, i; + nVars = p2.nVars; + hashVersion = p2.hashVersion; + variablesInitialized = p2.variablesInitialized; + variablesFreed = p2.variablesFreed; + varsOfInterest = p2.varsOfInterest; + safeVars = p2.safeVars; + nEQs = p2.nEQs; + isTemporary = p2.isTemporary; + //nSUBs = 0; + for (e = p2.nEQs - 1; e >= 0; e--) + eqnncpy(&(EQs[e]), &(p2.EQs[e]), p2.nVars); + nGEQs = p2.nGEQs; + for (e = p2.nGEQs - 1; e >= 0; e--) + eqnncpy(&(GEQs[e]), &(p2.GEQs[e]), p2.nVars); + for (i = 0; i <= p2.nVars; i++) + var[i] = p2.var[i]; + for (i = 0; i <= maxVars; i++) + forwardingAddress[i] = p2.forwardingAddress[i]; + //nMemories = 0; + get_var_name = p2.get_var_name; + getVarNameArgs = p2.getVarNameArgs; + } + return *this; +} + + +void Problem::zeroVariable(int i) { + int e; + for (e = nGEQs - 1; e >= 0; e--) GEQs[e].coef[i] = 0; + for (e = nEQs - 1; e >= 0; e--) EQs[e].coef[i] = 0; + for (e = nSUBs - 1; e >= 0; e--) SUBs[e].coef[i] = 0; +} + +/* Functions for allocating EQ's and GEQ's */ + +int Problem::newGEQ() { + if (++nGEQs > allocGEQs) { + check_number_GEQs(nGEQs); + allocGEQs = padGEQs(allocGEQs, nGEQs); + assert(allocGEQs >= nGEQs); + eqn *new_geqs = new eqn[allocGEQs]; + for (int e = nGEQs - 2; e >= 0; e--) + eqnncpy(&(new_geqs[e]), &(GEQs[e]), nVars); + delete[] GEQs; + GEQs = new_geqs; + } +// problem->GEQs[nGEQs-1].color = black; +// eqnnzero(&problem->GEQs[nGEQs-1],problem->nVars); + return nGEQs-1; +} + +int Problem::newEQ() { + if (++nEQs > allocEQs) { + check_number_EQs(nEQs); + allocEQs = padEQs(allocEQs, nEQs); + assert(allocEQs >= nEQs); + eqn *new_eqs = new eqn[allocEQs]; + for (int e = nEQs - 2; e >= 0; e--) + eqnncpy(&(new_eqs[e]), &(EQs[e]), nVars); + delete[] EQs; + EQs = new_eqs; + } +// Could do this here, but some calls to newEQ do a copy instead of a zero; +// problem->EQs[nEQs-1].color = black; +// eqnnzero(&problem->EQs[nEQs-1],problem->nVars); + return nEQs-1; +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_query.cc b/omegalib/omega/src/omega_core/oc_query.cc new file mode 100644 index 0000000..528b297 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_query.cc @@ -0,0 +1,478 @@ +#include <omega/omega_core/oc_i.h> + +namespace omega { + +void Problem::unprotectVariable( int v) { + int e, j, i; + coef_t t; + i = forwardingAddress[v]; + if (i < 0) { + i = -1 - i; + nSUBs--; + if (i < nSUBs) { + eqnncpy(&SUBs[i], &SUBs[nSUBs], nVars); + forwardingAddress[SUBs[i].key] = -i - 1; + } + } + else { + int bringToLife[maxVars]; + int comingBack = 0; + int e2; + for (e = nSUBs - 1; e >= 0; e--) + if ((bringToLife[e] = (SUBs[e].coef[i] != 0))) + comingBack++; + + for (e2 = nSUBs - 1; e2 >= 0; e2--) + if (bringToLife[e2]) { + + nVars++; + safeVars++; + if (safeVars < nVars) { + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].coef[nVars] = GEQs[e].coef[safeVars]; + GEQs[e].coef[safeVars] = 0; + } + for (e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[nVars] = EQs[e].coef[safeVars]; + EQs[e].coef[safeVars] = 0; + } + for (e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[nVars] = SUBs[e].coef[safeVars]; + SUBs[e].coef[safeVars] = 0; + } + var[nVars] = var[safeVars]; + forwardingAddress[var[nVars]] = nVars; + } + else { + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].coef[safeVars] = 0; + } + for (e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[safeVars] = 0; + } + for (e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[safeVars] = 0; + } + } + + var[safeVars] = SUBs[e2].key; + forwardingAddress[SUBs[e2].key] = safeVars; + + int neweq = newEQ(); + eqnncpy(&(EQs[neweq]), &(SUBs[e2]), nVars); + EQs[neweq].coef[safeVars] = -1; + if (e2 < nSUBs - 1) + eqnncpy(&(SUBs[e2]), &(SUBs[nSUBs - 1]), nVars); + nSUBs--; + } + + if (i < safeVars) { + j = safeVars; + for (e = nSUBs - 1; e >= 0; e--) { + t = SUBs[e].coef[j]; + SUBs[e].coef[j] = SUBs[e].coef[i]; + SUBs[e].coef[i] = t; + } + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[j] != GEQs[e].coef[i]) { + GEQs[e].touched = true; + t = GEQs[e].coef[j]; + GEQs[e].coef[j] = GEQs[e].coef[i]; + GEQs[e].coef[i] = t; + } + for (e = nEQs - 1; e >= 0; e--) { + t = EQs[e].coef[j]; + EQs[e].coef[j] = EQs[e].coef[i]; + EQs[e].coef[i] = t; + } + { + short t; + t = var[j]; + var[j] = var[i]; + var[i] = t; + } + forwardingAddress[var[i]] = i; + forwardingAddress[var[j]] = j; + } + safeVars--; + } + chainUnprotect(); +} + +void Problem::constrainVariableSign( int color, int i, int sign) { + int nV = nVars; + int e, k, j; + + k = forwardingAddress[i]; + if (k < 0) { + k = -1 - k; + + if (sign != 0) { + e = newGEQ(); + eqnncpy(&GEQs[e], &SUBs[k], nVars); + for (j = 0; j <= nV; j++) + GEQs[e].coef[j] *= sign; + GEQs[e].coef[0]--; + GEQs[e].touched = 1; + GEQs[e].color = color; + } + else { + e = newEQ(); + eqnncpy(&EQs[e], &SUBs[k], nVars); + EQs[e].color = color; + } + } + else if (sign != 0) { + e = newGEQ(); + eqnnzero(&GEQs[e], nVars); + GEQs[e].coef[k] = sign; + GEQs[e].coef[0] = -1; + GEQs[e].touched = 1; + GEQs[e].color = color; + } + else { + e = newEQ(); + eqnnzero(&EQs[e], nVars); + EQs[e].coef[k] = 1; + EQs[e].color = color; + } + /* + unprotectVariable(i); + return (simplifyProblem(0,1,0)); + */ +} + +void Problem::constrainVariableValue( int color, int i, int value) { + int e, k; + + k = forwardingAddress[i]; + if (k < 0) { + k = -1 - k; + + e = newEQ(); + eqnncpy(&EQs[e], &SUBs[k], nVars); + EQs[e].coef[0] -= value; + + } + else { + e = newEQ(); + eqnnzero(&EQs[e], nVars); + EQs[e].coef[k] = 1; + EQs[e].coef[0] = -value; + } + EQs[e].color = color; +} + +// Analyze v1-v2 +void Problem:: query_difference(int v1, int v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed) { + int nV = nVars; + int e,i,e2; + + coef_t lb1,ub1; + coef_t lb2,ub2; + assert(nSUBs == 0); + lowerBound = negInfinity; + lb1 = lb2 = negInfinity; + upperBound = posInfinity; + ub1 = ub2 = posInfinity; + guaranteed = true; + for (e = nEQs - 1; e >= 0; e--) { + if (EQs[e].coef[v1] == 0 && EQs[e].coef[v2] == 0) + continue; + for(i=nV;i>0;i--) + if (EQs[e].coef[i] && i!=v1 && i != v2) { + break; + } + if (i != 0) { + if (i > safeVars) { + // check to see if this variable appears anywhere else + for(e2 = nEQs-1; e2>=0;e2--) if (e != e2 && EQs[e2].coef[i]) break; + if (e2 < 0) + for(e2 = nGEQs-1; e2>=0;e2--) if (e != e2 && GEQs[e2].coef[i]) break; + if (e2 < 0) + for(e2 = nSUBs-1; e2>=0;e2--) if (e != e2 && SUBs[e2].coef[i]) break; + if (e2 >= 0) guaranteed = false; + } + else guaranteed = false; + continue; + } + if (EQs[e].coef[v1]*EQs[e].coef[v2] == -1) { + // found exact difference + coef_t d = - EQs[e].coef[v1] * EQs[e].coef[0]; + set_max(lowerBound, d); + set_min(upperBound, d); + guaranteed =true; + return; + } + else if (EQs[e].coef[v1] == 0) + lb2 = ub2 = -EQs[e].coef[0]/ EQs[e].coef[v2]; + else if (EQs[e].coef[v2] == 0) + lb1 = ub1 = -EQs[e].coef[0]/ EQs[e].coef[v1]; + else guaranteed = false; + } + + bool isDead[maxmaxGEQs]; + + for (e = nGEQs - 1; e >= 0; e--) isDead[e] = false; + int tryAgain = 1; + while (tryAgain) { + tryAgain = 0; + for (i = nVars; i > 0;i--) + if (i!= v1 && i != v2) { + for (e = nGEQs - 1; e >= 0; e--) + if (!isDead[e] && GEQs[e].coef[i]) + break; + if (e < 0) + e2 = e; + else if (GEQs[e].coef[i] > 0) { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDead[e2] && GEQs[e2].coef[i] < 0) + break; + } + else { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDead[e2] && GEQs[e2].coef[i] > 0) + break; + } + if (e2 < 0) { + int e3; + for (e3 = nSUBs - 1; e3 >= 0; e3--) + if (SUBs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + for (e3 = nEQs - 1; e3 >= 0; e3--) + if (EQs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + if (e >= 0) { + isDead[e] = true; + for (e--; e >= 0; e--) + if (GEQs[e].coef[i]) isDead[e] = true; + } + } + } + } + + for (e = nGEQs - 1; e >= 0; e--) + if (!isDead[e]) { + if (GEQs[e].coef[v1] == 0 && GEQs[e].coef[v2] == 0) + continue; + for(i=nV;i>0;i--) + if (GEQs[e].coef[i] && i!=v1 && i != v2) + break; + if (i != 0) { + guaranteed = false; + continue; + } + if (GEQs[e].coef[v1]*GEQs[e].coef[v2] == -1) { + // found relative difference + if (GEQs[e].coef[v1] == 1) { + // v1 - v2 + c >= 0 + set_max(lowerBound, - GEQs[e].coef[0]); + } + else { + // v2 - v1 + c >= 0 + // c >= v1-v2 + set_min(upperBound, GEQs[e].coef[0]); + } + } + else if (GEQs[e].coef[v1] == 0 && GEQs[e].coef[v2] > 0) + lb2 = -GEQs[e].coef[0]/ GEQs[e].coef[v2]; + else if (GEQs[e].coef[v1] == 0 && GEQs[e].coef[v2] < 0) + ub2 = -GEQs[e].coef[0]/ GEQs[e].coef[v2]; + else if (GEQs[e].coef[v2] == 0 && GEQs[e].coef[v1] > 0) + lb1 = -GEQs[e].coef[0]/ GEQs[e].coef[v1]; + else if (GEQs[e].coef[v2] == 0 && GEQs[e].coef[v1] < 0) + ub1 = -GEQs[e].coef[0]/ GEQs[e].coef[v1]; + else guaranteed = false; + } + + // ub1-lb2 >= v1-v2 >= lb1-ub2 + + if (negInfinity < lb2 && ub1 < posInfinity) set_min(upperBound, ub1-lb2); + if (negInfinity < lb1 && ub2 < posInfinity) set_max(lowerBound, lb1-ub2); + if (lowerBound >= upperBound) guaranteed = 1; +} + + +int Problem::queryVariable(int i, coef_t *lowerBound, coef_t *upperBound) { + int nV = nVars; + int e, j; + int isSimple; + int coupled = false; + for(j=1;j<=safeVars;j++) + if (var[j] > 0) + assert(forwardingAddress[var[j]] == j); + + assert(i > 0); + i = forwardingAddress[i]; + assert(i != 0); + + (*lowerBound) = negInfinity; + (*upperBound) = posInfinity; + + if (i < 0) { + int easy = true; + i = -i - 1; + for (j = 1; j <= nV; j++) + if (SUBs[i].coef[j] != 0) + easy = false; + if (easy) { + *upperBound = *lowerBound = SUBs[i].coef[0]; + return (false); + } + return (true); + } + + for (e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i] != 0) + coupled = true; + + for (e = nEQs - 1; e >= 0; e--) + if (EQs[e].coef[i] != 0) { + isSimple = true; + for (j = 1; j <= nV; j++) + if (i != j && EQs[e].coef[j] != 0) { + isSimple = false; + coupled = true; + break; + } + if (!isSimple) + continue; + else { + *lowerBound = *upperBound = -EQs[e].coef[i] * EQs[e].coef[0]; + return (false); + } + } + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i] != 0) { + if (GEQs[e].key == i) { + set_max(*lowerBound, -GEQs[e].coef[0]); + } + else if (GEQs[e].key == -i) { + set_min(*upperBound, GEQs[e].coef[0]); + } + else + coupled = true; + } + return (coupled); +} + +int Problem::query_variable_bounds(int i, coef_t *l, coef_t *u) { + int coupled; + *l = negInfinity; + *u = posInfinity; + coupled = queryVariable(i, l, u); + if (!coupled || (nVars == 1 && forwardingAddress[i] == 1)) + return 0; + if (abs(forwardingAddress[i]) == 1 && nVars + nSUBs == 2 && nEQs + nSUBs == 1) { + int couldBeZero; + queryCoupledVariable(i, l, u, &couldBeZero, negInfinity, posInfinity); + return 0; + } + return 1; +} + +void Problem::queryCoupledVariable(int i, coef_t *l, coef_t *u, int *couldBeZero, coef_t lowerBound, coef_t upperBound) { + int e; + coef_t b1, b2; + const eqn *eqn; + coef_t sign; + int v; + + if (abs(forwardingAddress[i]) != 1 || nVars + nSUBs != 2 || nEQs + nSUBs != 1) { + fprintf(outputFile, "queryCoupledVariablecalled with bad parameters\n"); + printProblem(); + exit(2); + } + + if (forwardingAddress[i] == -1) { + eqn = &SUBs[0]; + sign = 1; + v = 1; + } + else { + eqn = &EQs[0]; + sign = -eqn->coef[1]; + v = 2; + } + + /* Variable i is defined in terms of variable v */ + + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[v] != 0) { + if (GEQs[e].coef[v] == 1) { + set_max(lowerBound, -GEQs[e].coef[0]); + } + else { + set_min(upperBound, GEQs[e].coef[0]); + } + } + /* lowerBound and upperBound are bounds on the value of v */ + + if (lowerBound > upperBound) { + *l = posInfinity; + *u = negInfinity; + *couldBeZero = 0; + return; + } + if (lowerBound == negInfinity) { + if (eqn->coef[v] > 0) + b1 = sign * negInfinity; + else + b1 = -sign * negInfinity; + } + else + b1 = sign * (eqn->coef[0] + eqn->coef[v] * lowerBound); + if (upperBound == posInfinity) { + if (eqn->coef[v] > 0) + b2 = sign * posInfinity; + else + b2 = -sign * posInfinity; + } + else + b2 = sign * (eqn->coef[0] + eqn->coef[v] * upperBound); + + /* b1 and b2 are bounds on the value of i (don't know which is upper bound) */ + if (b1 <= b2) { + set_max(*l, b1); + set_min(*u, b2); + } + else { + set_max(*l, b2); + set_min(*u, b1); + } + *couldBeZero = *l <= 0 && 0 <= *u && int_mod(eqn->coef[0], abs(eqn->coef[v])) == 0; +} + + +int Problem::queryVariableSigns(int i, int dd_lt, int dd_eq, int dd_gt, coef_t lowerBound, coef_t upperBound, bool *distKnown, coef_t *dist) { + int result; + coef_t l, u; + int couldBeZero; + + l = negInfinity; + u = posInfinity; + + queryVariable(i, &l, &u); + queryCoupledVariable(i, &l, &u, &couldBeZero, lowerBound, upperBound); + result = 0; + if (l < 0) + result |= dd_gt; + if (u > 0) + result |= dd_lt; + if (couldBeZero) + result |= dd_eq; + if (l == u) { + *distKnown = 1; + *dist = l; + } + else { + *distKnown = 0; + } + return (result); +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_quick_kill.cc b/omegalib/omega/src/omega_core/oc_quick_kill.cc new file mode 100644 index 0000000..1b988d4 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_quick_kill.cc @@ -0,0 +1,775 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Quick inequality elimination. + + Notes: + + History: + 03/31/09 Use BoolSet, Chun Chen +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <vector> +#include <algorithm> +#include <basic/BoolSet.h> + +namespace omega { + +int Problem::combineToTighten() { + int effort = min(12+5*(nVars-safeVars),23); + + if (DBUG) { + fprintf(outputFile, "\nin combineToTighten (%d,%d):\n",effort,nGEQs); + printProblem(); + fprintf(outputFile, "\n"); + } + if (nGEQs > effort) { + if (TRACE) { + fprintf(outputFile, "too complicated to tighten\n"); + } + return 1; + } + + for(int e = 1; e < nGEQs; e++) { + for(int e2 = 0; e2 < e; e2++) { + coef_t g = 0; + + bool has_wildcard = false; + bool has_wildcard2 = false; + for (int i = nVars; i > safeVars; i--) { + coef_t a = GEQs[e].coef[i]; + coef_t b = GEQs[e2].coef[i]; + g = gcd(g, abs(a+b)); + if (a != 0) + has_wildcard = true; + if (b != 0) + has_wildcard2 = true; + } + + coef_t c, c2; + if ((has_wildcard && !has_wildcard2) || (!has_wildcard && has_wildcard2)) + c = 0; + else + c = -1; + for (int i = safeVars; i >= 1; i--) { + coef_t a = GEQs[e].coef[i]; + coef_t b = GEQs[e2].coef[i]; + if (a != 0 || b != 0) { + g = gcd(g, abs(a+b)); + + if (c < 0) { + if (g == 1) + break; + } + else if ((a>0 && b<0) || (a<0 && b>0)) { + if (c == 0) { + try { + coef_t prod = lcm(abs(a), abs(b)); + c = prod/abs(a); + c2 = prod/abs(b); + } + catch (std::overflow_error) { + c = -1; + } + } + else { + if (c*a+c2*b != 0) + c = -1; + } + } + else { + c = -1; + } + } + } + + bool done_unit_combine = false; + if (g > 1 && (GEQs[e].coef[0] + GEQs[e2].coef[0]) % g != 0) { + int e3 = newGEQ(); + for(int i = nVars; i >= 1; i--) { + GEQs[e3].coef[i] = (GEQs[e].coef[i] + GEQs[e2].coef[i])/g; + } + GEQs[e3].coef[0] = int_div(GEQs[e].coef[0] + GEQs[e2].coef[0], g); + GEQs[e3].color = GEQs[e].color || GEQs[e2].color; + GEQs[e3].touched = 1; + if (DBUG) { + fprintf(outputFile, "Combined "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n and "); + printGEQ(&GEQs[e2]); + fprintf(outputFile,"\n to get #%d: ",e3); + printGEQ(&GEQs[e3]); + fprintf(outputFile,"\n\n"); + } + + done_unit_combine = true; + if (nGEQs > effort+5 || nGEQs > maxmaxGEQs-10) goto doneCombining; + } + + if (c > 0 && !(c == 1 && c2 == 1 && done_unit_combine)) { + bool still_has_wildcard = false; + coef_t p[nVars-safeVars]; + for (int i = nVars; i > safeVars; i--) { + p[i-safeVars-1] = c * GEQs[e].coef[i] + c2 * GEQs[e2].coef[i]; + if (p[i-safeVars-1] != 0) + still_has_wildcard = true; + } + if (still_has_wildcard) { + int e3 = newGEQ(); + for(int i = nVars; i > safeVars; i--) + GEQs[e3].coef[i] = p[i-safeVars-1]; + for (int i = safeVars; i > 0; i--) + GEQs[e3].coef[i] = 0; + GEQs[e3].coef[0] = c * GEQs[e].coef[0] + c2 * GEQs[e2].coef[0]; + GEQs[e3].color = GEQs[e].color || GEQs[e2].color; + GEQs[e3].touched = 1; + if (DBUG) { + fprintf(outputFile, "Additionally combined "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n and "); + printGEQ(&GEQs[e2]); + fprintf(outputFile,"\n to get #%d: ",e3); + printGEQ(&GEQs[e3]); + fprintf(outputFile,"\n\n"); + } + + if (nGEQs > effort+5 || nGEQs > maxmaxGEQs-10) goto doneCombining; + } + } + } + } + +doneCombining: + if (normalize() == normalize_false) return 0; + while (nEQs) { + if (!solveEQ()) return 0; + if (normalize() == normalize_false) return 0; + } + return 1; +} + + +void Problem::noteEssential(int onlyWildcards) { + for (int e = nGEQs - 1; e >= 0; e--) { + GEQs[e].essential = 0; + GEQs[e].varCount = 0; + } + if (onlyWildcards) { + for (int e = nGEQs - 1; e >= 0; e--) { + GEQs[e].essential = 1; + for (int i = nVars; i > safeVars; i--) + if (GEQs[e].coef[i] < -1 || GEQs[e].coef[i] > 1) { + GEQs[e].essential = 0; + break; + } + } + } + for (int i = nVars; i >= 1; i--) { + int onlyLB = -1; + int onlyUB = -1; + for (int e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i] > 0) { + GEQs[e].varCount ++; + if (onlyLB == -1) onlyLB = e; + else onlyLB = -2; + } + else if (GEQs[e].coef[i] < 0) { + GEQs[e].varCount ++; + if (onlyUB == -1) onlyUB = e; + else onlyUB = -2; + } + if (onlyUB >= 0) { + if (DBUG) { + fprintf(outputFile,"only UB: "); + printGEQ(&GEQs[onlyUB]); + fprintf(outputFile,"\n"); + } + GEQs[onlyUB].essential = 1; + } + if (onlyLB >= 0) { + if (DBUG) { + fprintf(outputFile,"only LB: "); + printGEQ(&GEQs[onlyLB]); + fprintf(outputFile,"\n"); + } + GEQs[onlyLB].essential = 1; + } + } + for (int e = nGEQs - 1; e >= 0; e--) + if (!GEQs[e].essential && GEQs[e].varCount > 1) { + int i1,i2,i3; + for (i1 = nVars; i1 >= 1; i1--) if (GEQs[e].coef[i1]) break; + for (i2 = i1-1; i2 >= 1; i2--) if (GEQs[e].coef[i2]) break; + for (i3 = i2-1; i3 >= 1; i3--) if (GEQs[e].coef[i3]) break; + assert(i2 >= 1); + int e2; + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (e!=e2) { + coef_t crossProduct; + crossProduct = GEQs[e].coef[i1]*GEQs[e2].coef[i1]; + crossProduct += GEQs[e].coef[i2]*GEQs[e2].coef[i2]; + for (int i = i3; i >= 1; i--) + if (GEQs[e2].coef[i]) + crossProduct += GEQs[e].coef[i]*GEQs[e2].coef[i]; + if (crossProduct > 0) { + if (DBUG) fprintf(outputFile,"Cross product of %d and %d is " coef_fmt "\n", e, e2, crossProduct); + break; + } + } + if (e2 < 0) GEQs[e].essential = 1; + } + if (DBUG) { + fprintf(outputFile,"Computed essential equations\n"); + fprintf(outputFile,"essential equations:\n"); + for (int e = 0; e < nGEQs; e++) + if (GEQs[e].essential) { + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + fprintf(outputFile,"potentially redundant equations:\n"); + for (int e = 0; e < nGEQs; e++) + if (!GEQs[e].essential) { + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + } +} + + +int Problem::findDifference(int e, int &v1, int &v2) { + // if 1 returned, eqn E is of form v1 -coef >= v2 + for(v1=1;v1<=nVars;v1++) + if (GEQs[e].coef[v1]) break; + for(v2=v1+1;v2<=nVars;v2++) + if (GEQs[e].coef[v2]) break; + if (v2 > nVars) { + if (GEQs[e].coef[v1] == -1) { + v2 = v1; + v1 = 0; + return 1; + } + if (GEQs[e].coef[v1] == 1) { + v2 = 0; + return 1; + } + return 0; + } + if (GEQs[e].coef[v1] * GEQs[e].coef[v2] != -1) return 0; + if (GEQs[e].coef[v1] < 0) std::swap(v1,v2); + return 1; +} + + +namespace { + struct succListStruct { + int num; + int notEssential; + int var[maxVars]; + coef_t diff[maxVars]; + int eqn[maxVars]; + }; +} + + +int Problem::chainKill(int color, int onlyWildcards) { + int v1,v2,e; + int essentialPred[maxVars]; + int redundant[maxmaxGEQs]; + int inChain[maxVars]; + int goodStartingPoint[maxVars]; + int tryToEliminate[maxmaxGEQs]; + int triedDoubleKill = 0; + + succListStruct succ[maxVars]; + +restart: + + int anyToKill = 0; + int anyKilled = 0; + int canHandle = 0; + + for(v1=0;v1<=nVars;v1++) { + succ[v1].num = 0; + succ[v1].notEssential = 0; + goodStartingPoint[v1] = 0; + inChain[v1] = -1; + essentialPred[v1] = 0; + } + + int essentialEquations = 0; + for (e = 0; e < nGEQs; e++) { + redundant[e] = 0; + tryToEliminate[e] = !GEQs[e].essential; + if (GEQs[e].essential) essentialEquations++; + if (color && !GEQs[e].color) tryToEliminate[e] = 0; + } + if (essentialEquations == nGEQs) return 0; + if (2*essentialEquations < nVars) return 1; + + for (e = 0; e < nGEQs; e++) + if (tryToEliminate[e] && GEQs[e].varCount <= 2 && findDifference(e,v1,v2)) { + assert(v1 == 0 || GEQs[e].coef[v1] == 1); + assert(v2 == 0 || GEQs[e].coef[v2] == -1); + succ[v2].notEssential++; + int s = succ[v2].num++; + succ[v2].eqn[s] = e; + succ[v2].var[s] = v1; + succ[v2].diff[s] = -GEQs[e].coef[0]; + goodStartingPoint[v2] = 1; + anyToKill++; + canHandle++; + } + if (!anyToKill) { + return canHandle < nGEQs; + } + for (e = 0; e < nGEQs; e++) + if (!tryToEliminate[e] && GEQs[e].varCount <= 2 && findDifference(e,v1,v2)) { + assert(v1 == 0 || GEQs[e].coef[v1] == 1); + assert(v2 == 0 || GEQs[e].coef[v2] == -1); + int s = succ[v2].num++; + essentialPred[v1]++; + succ[v2].eqn[s] = e; + succ[v2].var[s] = v1; + succ[v2].diff[s] = -GEQs[e].coef[0]; + canHandle++; + } + + + if (DBUG) { + int s; + fprintf(outputFile,"In chainkill: [\n"); + for(v1 = 0;v1<=nVars;v1++) { + fprintf(outputFile,"#%d <= %s: ",essentialPred[v1],variable(v1)); + for(s=0;s<succ[v1].notEssential;s++) + fprintf(outputFile," %s(" coef_fmt ") ",variable(succ[v1].var[s]), succ[v1].diff[s]); + for(;s<succ[v1].num;s++) + fprintf(outputFile," %s[" coef_fmt "] ",variable(succ[v1].var[s]), succ[v1].diff[s]); + fprintf(outputFile,"\n"); + } + } + + for(;v1<=nVars;v1++) + if (succ[v1].num == 1 && succ[v1].notEssential == 1) { + succ[v1].notEssential--; + essentialPred[succ[v1].var[succ[v1].notEssential]]++; + } + + if (DBUG) fprintf(outputFile,"Trying quick double kill:\n"); + int s1a,s1b,s2; + int v3; + for(v1 = 0;v1<=nVars;v1++) + for(s1a=0;s1a<succ[v1].notEssential;s1a++) { + v3 = succ[v1].var[s1a]; + for(s1b=0;s1b<succ[v1].num;s1b++) + if (s1a != s1b) { + v2 = succ[v1].var[s1b]; + for(s2=0;s2<succ[v2].num;s2++) + if (succ[v2].var[s2] == v3 && succ[v1].diff[s1b] + succ[v2].diff[s2] >= succ[v1].diff[s1a]) { + if (DBUG) { + fprintf(outputFile,"quick double kill: "); + printGEQ(&GEQs[succ[v1].eqn[s1a]]); + fprintf(outputFile,"\n"); + } + redundant[succ[v1].eqn[s1a]] = 1; + anyKilled++; + anyToKill--; + goto nextVictim; + } + } + nextVictim: v1 = v1; + } + if (anyKilled) { + for (e = nGEQs-1; e >= 0;e--) + if (redundant[e]) { + if (DBUG) { + fprintf(outputFile,"Deleting "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + deleteGEQ(e); + } + + if (!anyToKill) return canHandle < nGEQs; + noteEssential(onlyWildcards); + triedDoubleKill = 1; + goto restart; + } + + for(v1 = 0;v1<=nVars;v1++) + if (succ[v1].num == succ[v1].notEssential && succ[v1].notEssential > 0) { + succ[v1].notEssential--; + essentialPred[succ[v1].var[succ[v1].notEssential]]++; + } + + while (1) { + int chainLength; + int chain[maxVars]; + coef_t distance[maxVars]; + // pick a place to start + for(v1 = 0;v1<=nVars;v1++) + if (essentialPred[v1] == 0 && succ[v1].num > succ[v1].notEssential) + break; + if (v1 > nVars) + for(v1 = 0;v1<=nVars;v1++) + if (goodStartingPoint[v1] && succ[v1].num > succ[v1].notEssential) + break; + if (v1 > nVars) break; + + chainLength = 1; + chain[0] = v1; + distance[0] = 0; + inChain[v1] = 0; + int s; + + while (succ[v1].num > succ[v1].notEssential) { + s = succ[v1].num-1; + if (inChain[succ[v1].var[s]] >= 0) { + // Found cycle, don't do anything with them yet + break; + } + succ[v1].num = s; + + distance[chainLength]= distance[chainLength-1] + succ[v1].diff[s]; + v1 = chain[chainLength] = succ[v1].var[s]; + essentialPred[v1]--; + assert(essentialPred[v1] >= 0); + inChain[v1] = chainLength; + chainLength++; + } + + + int c; + if (DBUG) { + fprintf(outputFile,"Found chain: \n"); + for (c = 0; c < chainLength; c++) + fprintf(outputFile,"%s:" coef_fmt " ",variable(chain[c]), distance[c]); + fprintf(outputFile,"\n"); + } + + + for (c = 0; c < chainLength; c++) { + v1 = chain[c]; + for(s=0;s<succ[v1].notEssential;s++) { + if (DBUG) + fprintf(outputFile,"checking for %s + " coef_fmt " <= %s \n", variable(v1), succ[v1].diff[s], variable(succ[v1].var[s])); + if (inChain[succ[v1].var[s]] > c+1) { + if (DBUG) + fprintf(outputFile,"%s + " coef_fmt " <= %s is in chain\n", variable(v1), distance[inChain[succ[v1].var[s]]]- distance[c], variable(succ[v1].var[s])); + if ( distance[inChain[succ[v1].var[s]]]- distance[c] >= succ[v1].diff[s]) { + if (DBUG) + fprintf(outputFile,"%s + " coef_fmt " <= %s is redundant\n", variable(v1),succ[v1].diff[s], variable(succ[v1].var[s])); + redundant[succ[v1].eqn[s]] = 1; + } + } + } + } + for (c = 0; c < chainLength; c++) + inChain[chain[c]] = -1; + } + + for (e = nGEQs-1; e >= 0;e--) + if (redundant[e]) { + if (DBUG) { + fprintf(outputFile,"Deleting "); + printGEQ(&GEQs[e]); + fprintf(outputFile,"\n"); + } + deleteGEQ(e); + anyKilled = 1; + } + + if (anyKilled) noteEssential(onlyWildcards); + + if (anyKilled && DBUG) { + fprintf(outputFile,"\nResult:\n"); + printProblem(); + } + if (DBUG) { + fprintf(outputFile,"] end chainkill\n"); + printProblem(); + } + return canHandle < nGEQs; +} + + +namespace { + struct varCountStruct { + int e; + int safeVarCount; + int wildVarCount; + varCountStruct(int e_, int count1_, int count2_) { + e = e_; + safeVarCount = count1_; + wildVarCount = count2_; } + }; + bool operator<(const varCountStruct &a, const varCountStruct &b) { + if (a.wildVarCount < b.wildVarCount) + return true; + else if (a.wildVarCount > b.wildVarCount) + return false; + else + return a.safeVarCount < b.safeVarCount; + } +} + + +// +// Deduct redundant inequalities by combination of any two inequalities. +// Return value: 0 (no solution), +// 1 (nothing killed), +// 2 (some inequality killed). +// +int Problem::quickKill(int onlyWildcards, bool desperate) { + if (!onlyWildcards && !combineToTighten()) + return 0; + noteEssential(onlyWildcards); + int moreToDo = chainKill(0, onlyWildcards); + +#ifdef NDEBUG + if (!moreToDo) return 1; +#endif + + + if (!desperate && nGEQs > 256) { // original 60, increased by chun + if (TRACE) { + fprintf(outputFile, "%d inequalities are too complicated to quick kill\n", nGEQs); + } + return 1; + } + + if (DBUG) { + fprintf(outputFile, "in eliminate Redudant:\n"); + printProblem(); + } + + int isDead[nGEQs]; + std::vector<varCountStruct> killOrder; + std::vector<BoolSet<> > P(nGEQs, BoolSet<>(nVars)), Z(nGEQs, BoolSet<>(nVars)), N(nGEQs, BoolSet<>(nVars)); + BoolSet<> PP, PZ, PN; // possible Positives, possible zeros & possible negatives + + for (int e = nGEQs - 1; e >= 0; e--) { + isDead[e] = 0; + int safeVarCount = 0; + int wildVarCount = 0; + for (int i = nVars; i >= 1; i--) { + if (GEQs[e].coef[i] == 0) + Z[e].set(i-1); + else { + if (i > safeVars) + wildVarCount++; + else + safeVarCount++; + if (GEQs[e].coef[i] < 0) + N[e].set(i-1); + else + P[e].set(i-1); + } + } + + if (!GEQs[e].essential || wildVarCount > 0) + killOrder.push_back(varCountStruct(e, safeVarCount, wildVarCount)); + } + + sort(killOrder.begin(), killOrder.end()); + + if (DEBUG) { + fprintf(outputFile,"Prefered kill order:\n"); + for (int e3I = killOrder.size()-1; e3I >= 0; e3I--) { + fprintf(outputFile,"%2d: ",nGEQs-1-e3I); + printGEQ(&GEQs[killOrder[e3I].e]); + fprintf(outputFile,"\n"); + } + } + + int e3U = killOrder.size()-1; + while (e3U >= 0) { + // each round of elimination is for inequalities of same complexity and rounds are at descending complexity order + int e3L = e3U-1; + for(; e3L >= 0; e3L--) + if (killOrder[e3L].safeVarCount+killOrder[e3L].wildVarCount != killOrder[e3U].safeVarCount + killOrder[e3U].wildVarCount) + break; + + // check if e3 can be eliminated from combination of e1 and e2 + for (int e1 = 0; e1 < nGEQs; e1++) + if (!isDead[e1]) + for (int e2 = e1+1; e2 < nGEQs; e2++) + if (!isDead[e2]) { + coef_t alpha = 0; + int p, q; + for (p = nVars; p > 1; p--) + for (q = p - 1; q > 0; q--) { + try { + alpha = check_mul(GEQs[e1].coef[p], GEQs[e2].coef[q]) - check_mul(GEQs[e2].coef[p], GEQs[e1].coef[q]); + } + catch (std::overflow_error) { + continue; + } + if (alpha != 0) + goto foundPQ; + } + continue; + + foundPQ: + PZ = (Z[e1] & Z[e2]) | (P[e1] & N[e2]) | (N[e1] & P[e2]); + PP = P[e1] | P[e2]; + PN = N[e1] | N[e2]; + if (DEBUG) { + fprintf(outputFile,"Considering combination of "); + printGEQ(&(GEQs[e1])); + fprintf(outputFile," and "); + printGEQ(&(GEQs[e2])); + fprintf(outputFile,"\n"); + } + + for (int e3I = e3U; e3I > e3L; e3I--) { + int e3 = killOrder[e3I].e; + if (!isDead[e3] && e3 != e1 && e3 != e2) + try { + coef_t alpha1, alpha2, alpha3; + + if (!PZ.imply(Z[e3])) + goto nextE3; + + alpha1 = check_mul(GEQs[e2].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e2].coef[p], GEQs[e3].coef[q]); + alpha2 = -(check_mul(GEQs[e1].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e1].coef[p], GEQs[e3].coef[q])); + alpha3 = alpha; + + if (alpha1 < 0) { + alpha1 = -alpha1; + alpha2 = -alpha2; + alpha3 = -alpha3; + } + if (alpha1 == 0 || alpha2 <= 0) + goto nextE3; + + { + coef_t g = gcd(gcd(alpha1, alpha2), abs(alpha3)); + alpha1 /= g; + alpha2 /= g; + alpha3 /= g; + } + + if (DEBUG) { + fprintf(outputFile, coef_fmt "e1 + " coef_fmt "e2 = " coef_fmt "e3: ",alpha1,alpha2,alpha3); + printGEQ(&(GEQs[e3])); + fprintf(outputFile,"\n"); + } + + if (alpha3 > 0) { // trying to prove e3 is redundant + if (!GEQs[e3].color && (GEQs[e1].color || GEQs[e2].color)) { + goto nextE3; + } + if (!PP.imply(P[e3]) | !PN.imply(N[e3])) + goto nextE3; + + // verify alpha1*v1+alpha2*v2 = alpha3*v3 + for (int k = nVars; k >= 1; k--) + if (check_mul(alpha3, GEQs[e3].coef[k]) != check_mul(alpha1, GEQs[e1].coef[k]) + check_mul(alpha2, GEQs[e2].coef[k])) + goto nextE3; + + coef_t c = check_mul(alpha1, GEQs[e1].coef[0]) + check_mul(alpha2, GEQs[e2].coef[0]); + if (c < check_mul(alpha3, (GEQs[e3].coef[0] + 1))) { + if (DBUG) { + fprintf(outputFile, "found redundant inequality\n"); + fprintf(outputFile, "alpha1, alpha2, alpha3 = " coef_fmt "," coef_fmt "," coef_fmt "\n", alpha1, alpha2, alpha3); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n=> "); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n\n"); + assert(moreToDo); + } + + isDead[e3] = 1; + } + } + else { // trying to prove e3 <= 0 or e3 = 0 + if (!PN.imply(P[e3]) | !PP.imply(N[e3])) + goto nextE3; + + // verify alpha1*v1+alpha2*v2 = alpha3*v3 + for (int k = nVars; k >= 1; k--) + if (check_mul(alpha3, GEQs[e3].coef[k]) != check_mul(alpha1, GEQs[e1].coef[k]) + check_mul(alpha2, GEQs[e2].coef[k])) + goto nextE3; + + if (DEBUG) { + fprintf(outputFile,"All but constant term checked\n"); + } + coef_t c = check_mul(alpha1, GEQs[e1].coef[0]) + check_mul(alpha2, GEQs[e2].coef[0]); + if (DEBUG) { + fprintf(outputFile,"All but constant term checked\n"); + fprintf(outputFile,"Constant term is " coef_fmt " vs " coef_fmt "\n", + alpha3*GEQs[e3].coef[0], + alpha3*(GEQs[e3].coef[0]-1)); + } + if (c < check_mul(alpha3, (GEQs[e3].coef[0]))) { + // we just proved e3 < 0, so no solutions exist + if (DBUG) { + fprintf(outputFile, "found implied over tight inequality\n"); + fprintf(outputFile, "alpha1, alpha2, alpha3 = " coef_fmt "," coef_fmt "," coef_fmt "\n", alpha1, alpha2, -alpha3); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n=> not "); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n\n"); + } + return 0; + } + else if (!GEQs[e3].color && (GEQs[e1].color || GEQs[e2].color)) { + goto nextE3; + } + else if (c < check_mul(alpha3, (GEQs[e3].coef[0] - 1))) { + // we just proved e3 <= 0, so e3 = 0 + if (DBUG) { + fprintf(outputFile, "found implied tight inequality\n"); + fprintf(outputFile, "alpha1, alpha2, alpha3 = " coef_fmt "," coef_fmt "," coef_fmt "\n", alpha1, alpha2, -alpha3); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n=> inverse "); + printGEQ(&(GEQs[e3])); + fprintf(outputFile, "\n\n"); + } + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e3], nVars); + addingEqualityConstraint(neweq); + isDead[e3] = 1; + } + } + nextE3:; + } + catch (std::overflow_error) { + continue; + } + } + } + + e3U = e3L; + } + + bool anything_killed = false; + for (int e = nGEQs - 1; e >= 0; e--) { + if (isDead[e]) { + anything_killed = true; + deleteGEQ(e); + } + } + + if (DBUG) { + fprintf(outputFile,"\nResult:\n"); + printProblem(); + } + + if (anything_killed) + return 2; + else + return 1; +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_simple.cc b/omegalib/omega/src/omega_core/oc_simple.cc new file mode 100644 index 0000000..0e492db --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_simple.cc @@ -0,0 +1,1373 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Support functions for solving a problem. + + Notes: + + History: + 10/13/08 Complete back substitution process, Chun Chen. + 05/28/09 Extend normalize process to handle redundancy involving + wilddcards, Chun Chen +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <basic/BoolSet.h> +#include <algorithm> +#include <vector> + +namespace omega { + +int checkIfSingleVar(eqn* e, int i) { + for (; i > 0; i--) + if (e->coef[i]) { + i--; + break; + } + for (; i > 0; i--) + if (e->coef[i]) + break; + return (i == 0); +} + + +int singleVarGEQ(eqn* e) { + return !e->touched && e->key != 0 && -maxVars <= e->key && e->key <= maxVars; +} + + +void checkVars(int nVars) { + if (nVars > maxVars) { + fprintf(stderr, "\nERROR:\n"); + fprintf(stderr, "An attempt was made to create a conjunction with %d variables.\n", nVars); + fprintf(stderr, "The current limit on variables in a single conjunction is %d.\n", maxVars); + fprintf(stderr, "This limit can be changed by changing the #define of maxVars in oc.h.\n\n"); + exit(2); + } +} + + +void Problem::difficulty(int &numberNZs, coef_t &maxMinAbsCoef, coef_t &sumMinAbsCoef) const { + numberNZs=0; + maxMinAbsCoef=0; + sumMinAbsCoef=0; + for (int e = 0; e < nGEQs; e++) { + coef_t maxCoef = 0; + for(int i = 1;i <= nVars;i++) + if (GEQs[e].coef[i]!=0) { + coef_t a = abs(GEQs[e].coef[i]); + maxCoef = max(maxCoef,a); + numberNZs++; + } + coef_t nextCoef = 0; + for(int i = 1;i <= nVars;i++) + if (GEQs[e].coef[i]!=0) { + coef_t a = abs(GEQs[e].coef[i]); + if (a < maxCoef) nextCoef = max(nextCoef,a); + else if (a == maxCoef) maxCoef = 0x7fffffff; + } + maxMinAbsCoef = max(maxMinAbsCoef,nextCoef); + sumMinAbsCoef += nextCoef; + } + + for (int e = 0; e < nEQs; e++) { + coef_t maxCoef = 0; + for(int i = 1;i <= nVars;i++) + if (EQs[e].coef[i]!=0) { + coef_t a = abs(EQs[e].coef[i]); + maxCoef = max(maxCoef,a); + numberNZs++; + } + coef_t nextCoef = 0; + for(int i = 1;i <= nVars;i++) + if (EQs[e].coef[i]!=0) { + coef_t a = abs(EQs[e].coef[i]); + if (a < maxCoef) nextCoef = max(nextCoef,a); + else if (a == maxCoef) maxCoef = 0x7fffffff; + } + maxMinAbsCoef = max(maxMinAbsCoef,nextCoef); + sumMinAbsCoef += nextCoef; + } +} + +int Problem::countRedGEQs() const { + int result = 0; + for (int e = 0; e < nGEQs; e++) + if (GEQs[e].color == EQ_RED) result++; + return result; +} + +int Problem::countRedEQs() const { + int result = 0; + for (int e = 0; e < nEQs; e++) + if (EQs[e].color == EQ_RED) result++; + return result; +} + +int Problem::countRedEquations() const { + int result = 0; + for (int e = 0; e < nEQs; e++) + if (EQs[e].color == EQ_RED) { + int i; + for (i = nVars; i > 0; i--) if (EQs[e].coef[i]) break; + if (i == 0 && EQs[e].coef[0] != 0) return 0; + else result+=2; + } + for (int e = 0; e < nGEQs; e++) + if (GEQs[e].color == EQ_RED) result+=1; + for (int e = 0; e < nMemories; e++) + switch(redMemory[e].kind ) { + case redEQ: + case redStride: + e++; + case redLEQ: + case redGEQ: + e++; + case notRed: + ; /* avoid warning about notRed not handled */ + } + return result; +} + +void Problem::deleteBlack() { + int RedVar[maxVars]; + for(int i = safeVars+1;i <= nVars;i++) RedVar[i] = 0; + + assert(nSUBs == 0); + + for (int e = nEQs-1; e >= 0; e--) + if (EQs[e].color != EQ_RED) { + eqnncpy(&EQs[e],&EQs[nEQs-1], nVars); + nEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (EQs[e].coef[i]) RedVar[i] = 1; + + for (int e = nGEQs-1; e >= 0; e--) + if (GEQs[e].color != EQ_RED) { + eqnncpy(&GEQs[e],&GEQs[nGEQs-1], nVars); + nGEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (GEQs[e].coef[i]) RedVar[i] = 1; + + assert(nSUBs == 0); + + for(int i = nVars; i > safeVars;i--) { + if (!RedVar[i]) deleteVariable(i); + } +} + + +void Problem::deleteRed() { + int BlackVar[maxVars]; + for(int i = safeVars+1;i <= nVars;i++) BlackVar[i] = 0; + + assert(nSUBs == 0); + for (int e = nEQs-1; e >=0; e--) + if (EQs[e].color) { + eqnncpy(&EQs[e],&EQs[nEQs-1], nVars); + nEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (EQs[e].coef[i]) BlackVar[i] = 1; + + for (int e = nGEQs-1; e >=0; e--) + if (GEQs[e].color) { + eqnncpy(&GEQs[e],&GEQs[nGEQs-1], nVars); + nGEQs--; + } + else + for(int i = safeVars+1;i <= nVars;i++) + if (GEQs[e].coef[i]) BlackVar[i] = 1; + + assert(nSUBs == 0); + + for(int i = nVars; i> safeVars;i--) { + if (!BlackVar[i]) deleteVariable(i); + } +} + + +void Problem::turnRedBlack() { + for (int e = nEQs-1; e >= 0; e--) EQs[e].color = 0; + for (int e = nGEQs-1; e >= 0; e--) GEQs[e].color = 0; +} + + +void Problem::useWildNames() { + for(int i = safeVars+1; i <= nVars; i++) nameWildcard(i); +} + + +void negateCoefficients(eqn* eqn, int nVars) { + for (int i = nVars; i >= 0; i--) + eqn-> coef[i] = -eqn->coef[i]; + eqn->touched = true; +} + + +void Problem::negateGEQ(int e) { + negateCoefficients(&GEQs[e],nVars); + GEQs[e].coef[0]--; +} + + +void Problem:: deleteVariable(int i) { + if (i < safeVars) { + int j = safeVars; + for (int e = nGEQs - 1; e >= 0; e--) { + GEQs[e].touched = true; + GEQs[e].coef[i] = GEQs[e].coef[j]; + GEQs[e].coef[j] = GEQs[e].coef[nVars]; + } + for (int e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[i] = EQs[e].coef[j]; + EQs[e].coef[j] = EQs[e].coef[nVars]; + } + for (int e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[i] = SUBs[e].coef[j]; + SUBs[e].coef[j] = SUBs[e].coef[nVars]; + } + var[i] = var[j]; + var[j] = var[nVars]; + } + else if (i < nVars) { + for (int e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[nVars]) { + GEQs[e].coef[i] = GEQs[e].coef[nVars]; + GEQs[e].touched = true; + } + for (int e = nEQs - 1; e >= 0; e--) + EQs[e].coef[i] = EQs[e].coef[nVars]; + for (int e = nSUBs - 1; e >= 0; e--) + SUBs[e].coef[i] = SUBs[e].coef[nVars]; + var[i] = var[nVars]; + } + if (i <= safeVars) + safeVars--; + nVars--; +} + + +void Problem::setInternals() { + if (!variablesInitialized) { + initializeVariables(); + } + + var[0] = 0; + nextWildcard = 0; + for(int i = 1;i <= nVars;i++) + if (var[i] < 0) + var[i] = --nextWildcard; + + assert(nextWildcard >= -maxWildcards); + + CHECK_FOR_DUPLICATE_VARIABLE_NAMES; + + int v = nSUBs; + for(int i = 1;i <= safeVars;i++) if (var[i] > 0) v++; + varsOfInterest = v; + + if (nextKey * 3 > maxKeys) { + omega::hashVersion++; + nextKey = maxVars + 1; + for (int e = nGEQs - 1; e >= 0; e--) + GEQs[e].touched = true; + for (int i = 0; i < hashTableSize; i++) + hashMaster[i].touched = -1; + hashVersion = omega::hashVersion; + } + else if (hashVersion != omega::hashVersion) { + for (int e = nGEQs - 1; e >= 0; e--) + GEQs[e].touched = true; + hashVersion = omega::hashVersion; + } +} + + +void Problem::setExternals() { + for (int i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + for (int i = 0; i < nSUBs; i++) + forwardingAddress[SUBs[i].key] = -i - 1; +} + + +void setOutputFile(FILE * file) { + /* sets the file to which printProblem should send its output to "file" */ + + outputFile = file; +} + + +void setPrintLevel(int level) { + /* Sets the nber of points printed before constraints in printProblem */ + headerLevel = level; +} + + +void Problem::putVariablesInStandardOrder() { + for(int i = 1;i <= safeVars;i++) { + int b = i; + for(int j=i+1;j<=safeVars;j++) { + if (var[b] < var[j]) b = j; + } + if (b != i) swapVars(i,b); + } +} + + +void Problem::nameWildcard(int i) { + int j; + do { + --nextWildcard; + if (nextWildcard < -maxWildcards) + nextWildcard = -1; + var[i] = nextWildcard; + for(j = nVars; j > 0;j--) if (i!=j && var[j] == nextWildcard) break; + } while (j != 0); +} + + +int Problem::protectWildcard(int i) { + assert (i > safeVars); + if (i != safeVars+1) swapVars(i,safeVars+1); + safeVars++; + nameWildcard(safeVars); + return safeVars; +} + + +int Problem::addNewProtectedWildcard() { + int i = ++safeVars; + nVars++; + if (nVars != i) { + for (int e = nGEQs - 1; e >= 0; e--) { + if (GEQs[e].coef[i] != 0) + GEQs[e].touched = true; + GEQs[e].coef[nVars] = GEQs[e].coef[i]; + } + for (int e = nEQs - 1; e >= 0; e--) { + EQs[e].coef[nVars] = EQs[e].coef[i]; + } + for (int e = nSUBs - 1; e >= 0; e--) { + SUBs[e].coef[nVars] = SUBs[e].coef[i]; + } + var[nVars] = var[i]; + } + for (int e = nGEQs - 1; e >= 0; e--) + GEQs[e].coef[i] = 0; + for (int e = nEQs - 1; e >= 0; e--) + EQs[e].coef[i] = 0; + for (int e = nSUBs - 1; e >= 0; e--) + SUBs[e].coef[i] = 0; + nameWildcard(i); + return (i); +} + + +int Problem::addNewUnprotectedWildcard() { + int i = ++nVars; + for (int e = nGEQs - 1; e >= 0; e--) GEQs[e].coef[i] = 0; + for (int e = nEQs - 1; e >= 0; e--) EQs[e].coef[i] = 0; + for (int e = nSUBs - 1; e >= 0; e--) SUBs[e].coef[i] = 0; + nameWildcard(i); + return i; +} + + +void Problem::cleanoutWildcards() { + bool renormalize = false; + + // substituting wildcard equality + for (int e = nEQs-1; e >= 0; e--) { + for (int i = nVars; i >= safeVars+1; i--) + if (EQs[e].coef[i] != 0) { + coef_t c = EQs[e].coef[i]; + coef_t a = abs(c); + + bool preserveThisConstraint = true; + for (int e2 = nEQs-1; e2 >= 0; e2--) + if (e2 != e && EQs[e2].coef[i] != 0 && EQs[e2].color >= EQs[e].color) { + preserveThisConstraint = preserveThisConstraint && (gcd(a,abs(EQs[e2].coef[i])) != 1); + coef_t k = lcm(a, abs(EQs[e2].coef[i])); + coef_t coef1 = (EQs[e2].coef[i]>0?1:-1) * k / c; + coef_t coef2 = k / abs(EQs[e2].coef[i]); + for (int j = nVars; j >= 0; j--) + EQs[e2].coef[j] = EQs[e2].coef[j] * coef2 - EQs[e].coef[j] * coef1; + + coef_t g = 0; + for (int j = nVars; j >= 0; j--) { + g = gcd(abs(EQs[e2].coef[j]), g); + if (g == 1) + break; + } + if (g != 0 && g != 1) + for (int j = nVars; j >= 0; j--) + EQs[e2].coef[j] /= g; + } + + for (int e2 = nGEQs-1; e2 >= 0; e2--) + if (GEQs[e2].coef[i] != 0 && GEQs[e2].color >= EQs[e].color) { + coef_t k = lcm(a, abs(GEQs[e2].coef[i])); + coef_t coef1 = (GEQs[e2].coef[i]>0?1:-1) * k / c; + coef_t coef2 = k / abs(GEQs[e2].coef[i]); + for (int j = nVars; j >= 0; j--) + GEQs[e2].coef[j] = GEQs[e2].coef[j] * coef2 - EQs[e].coef[j] * coef1; + + GEQs[e2].touched = 1; + renormalize = true; + } + + for (int e2 = nSUBs-1; e2 >= 0; e2--) + if (SUBs[e2].coef[i] != 0 && SUBs[e2].color >= EQs[e].color) { + coef_t k = lcm(a, abs(SUBs[e2].coef[i])); + coef_t coef1 = (SUBs[e2].coef[i]>0?1:-1) * k / c; + coef_t coef2 = k / abs(SUBs[e2].coef[i]); + for (int j = nVars; j >= 0; j--) + SUBs[e2].coef[j] = SUBs[e2].coef[j] * coef2 - EQs[e].coef[j] * coef1; + + coef_t g = 0; + for (int j = nVars; j >= 0; j--) { + g = gcd(abs(SUBs[e2].coef[j]), g); + if (g == 1) + break; + } + if (g != 0 && g != 1) + for (int j = nVars; j >= 0; j--) + SUBs[e2].coef[j] /= g; + } + + // remove redundent wildcard equality + if (!preserveThisConstraint) { + if (e < nEQs-1) + eqnncpy (&EQs[e], &EQs[nEQs-1], nVars); + nEQs--; + deleteVariable(i); + } + + break; + } + } + + // remove multi-wildcard equality in approximation mode + if (inApproximateMode) + for (int e = nEQs-1; e >= 0; e--) + for (int i = nVars; i >= safeVars+1; i--) + if (EQs[e].coef[i] != 0) { + int j = i-1; + for (; j >= safeVars+1; j--) + if (EQs[e].coef[j] != 0) + break; + + if (j != safeVars) { + if (e < nEQs-1) + eqnncpy (&EQs[e], &EQs[nEQs-1], nVars); + nEQs--; + } + + break; + } + + if (renormalize) + normalize(); +} + + +void Problem:: check() const { +#ifndef NDEBUG + int v = nSUBs; + checkVars(nVars+1); + for(int i = 1; i <= safeVars; i++) if (var[i] > 0) v++; + assert(v == varsOfInterest); + for(int e = 0; e < nGEQs; e++) assert(GEQs[e].touched || GEQs[e].key != 0); + if(!mayBeRed) { + for(int e = 0; e < nEQs; e++) assert(!EQs[e].color); + for(int e = 0; e < nGEQs; e++) assert(!GEQs[e].color); + } + else + for(int i = safeVars+1; i <= nVars; i++) { + int isBlack = 0; + int isRed = 0; + for(int e = 0; e < nEQs; e++) + if (EQs[e].coef[i]) { + if (EQs[e].color) isRed = 1; + else isBlack = 1; + } + for(int e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i]) { + if (GEQs[e].color) isRed = 1; + else isBlack = 1; + } + if (isBlack && isRed && 0) { + fprintf(outputFile,"Mixed Red and Black variable:\n"); + printProblem(); + } + } +#endif +} + + +void Problem::rememberRedConstraint(eqn *e, redType type, coef_t stride) { + // Check if this is really a stride constraint + if (type == redEQ && newVar == nVars && e->coef[newVar]) { + type = redStride; + stride = e->coef[newVar]; + } + // else for(int i = safeVars+1; i <= nVars; i++) assert(!e->coef[i]); // outdated -- by chun 10/30/2008 + + assert(type != notRed); + assert(type == redStride || stride == 0); + + if (TRACE) { + fprintf(outputFile,"being asked to remember red constraint:\n"); + switch(type) { + case notRed: fprintf(outputFile,"notRed: "); + break; + case redGEQ: fprintf(outputFile,"Red: 0 <= "); + break; + case redLEQ: fprintf(outputFile,"Red: 0 >= "); + break; + case redEQ: fprintf(outputFile,"Red: 0 == "); + break; + case redStride: fprintf(outputFile,"Red stride " coef_fmt ": ",stride); + break; + } + printTerm(e,1); + fprintf(outputFile,"\n"); + printProblem(); + fprintf(outputFile,"----\n"); + } + + // Convert redLEQ to redGEQ + eqn mem; + eqnncpy(&mem,e, nVars); + e = &mem; + if (type == redLEQ) { + for(int i = 0; i <= safeVars; i++) + e->coef[i] = -e->coef[i]; + type = redGEQ; + } + + // Prepare coefficient array for red constraint + bool has_wildcard = false; + coef_t coef[varsOfInterest-nextWildcard+1]; + for (int i = 0; i <= varsOfInterest-nextWildcard; i++) + coef[i] = 0; + for (int i = 0; i <= safeVars; i++) { + if (var[i] < 0) { + if (e->coef[i] != 0) { + coef[varsOfInterest-var[i]] = e->coef[i]; + has_wildcard = true; + } + } + else + coef[var[i]] = e->coef[i]; + } + + // Sophisticated back substituion for wildcards, use Gaussian elimination + // as a fallback if no simple equations available. -- by chun 10/13/2008 + if (has_wildcard) { + // Find substitutions involving wildcard + coef_t *repl_subs[nSUBs]; + int num_wild_in_repl_subs[nSUBs]; + int num_repl_subs = 0; + for (int i = 0; i < nSUBs; i++) { + int t = 0; + for (int j = 1; j <= safeVars; j++) { + if (var[j] < 0 && SUBs[i].coef[j] != 0) + t++; + } + if (t > 0) { + repl_subs[num_repl_subs] = new coef_t[varsOfInterest-nextWildcard+1]; + for (int j = 0; j <= varsOfInterest-nextWildcard; j++) + repl_subs[num_repl_subs][j] = 0; + + for (int k = 0; k <= safeVars; k++) + repl_subs[num_repl_subs][(var[k]<0)?varsOfInterest-var[k]:var[k]] = SUBs[i].coef[k]; + repl_subs[num_repl_subs][SUBs[i].key] = -1; + num_wild_in_repl_subs[num_repl_subs] = t; + num_repl_subs++; + } + } + + int wild_solved[-nextWildcard+1]; + bool has_unsolved = false; + for (int i = 1; i <= -nextWildcard; i++) { + int minimum_wild = 0; + int pos; + for (int j = 0; j < num_repl_subs; j++) + if (repl_subs[j][varsOfInterest+i] != 0 && (minimum_wild == 0 || num_wild_in_repl_subs[j] < minimum_wild)) { + minimum_wild = num_wild_in_repl_subs[j]; + pos = j; + } + + if (minimum_wild == 0) { + wild_solved[i] = -1; + if (coef[varsOfInterest+i] != 0) { + fprintf(outputFile,"No feasible back substitutions available\n"); + printProblem(); + exit(1); + } + } + else if (minimum_wild == 1) + wild_solved[i] = pos; + else { + wild_solved[i] = -1; + if (coef[varsOfInterest+i] != 0) + has_unsolved = true; + } + } + + // Gaussian elimination + while (has_unsolved) { + for (int i = 0; i < num_repl_subs; i++) + if (num_wild_in_repl_subs[i] > 1) { + for (int j = 1; j <= -nextWildcard; j++) { + if (repl_subs[i][varsOfInterest+j] != 0 && wild_solved[j] >= 0) { + int s = wild_solved[j]; + coef_t l = lcm(abs(repl_subs[i][varsOfInterest+j]), abs(repl_subs[s][varsOfInterest+j])); + coef_t scale_1 = l/abs(repl_subs[i][varsOfInterest+j]); + coef_t scale_2 = l/abs(repl_subs[s][varsOfInterest+j]); + int sign = ((repl_subs[i][varsOfInterest+j]>0)?1:-1) * ((repl_subs[s][varsOfInterest+j]>0)?1:-1); + for (int k = 0; k <= varsOfInterest-nextWildcard; k++) + repl_subs[i][k] = scale_1*repl_subs[i][k] - sign*scale_2*repl_subs[s][k]; + num_wild_in_repl_subs[i]--; + } + } + + if (num_wild_in_repl_subs[i] == 1) { + for (int j = 1; j <= -nextWildcard; j++) + if (repl_subs[i][varsOfInterest+j] != 0) { + assert(wild_solved[j]==-1); + wild_solved[j] = i; + break; + } + } + else if (num_wild_in_repl_subs[i] > 1) { + int pos = 0; + for (int j = 1; j <= -nextWildcard; j++) + if (repl_subs[i][varsOfInterest+j] != 0) { + pos = j; + break; + } + assert(pos > 0); + + for (int j = i+1; j < num_repl_subs; j++) + if (repl_subs[j][varsOfInterest+pos] != 0) { + coef_t l = lcm(abs(repl_subs[i][varsOfInterest+pos]), abs(repl_subs[j][varsOfInterest+pos])); + coef_t scale_1 = l/abs(repl_subs[i][varsOfInterest+pos]); + coef_t scale_2 = l/abs(repl_subs[j][varsOfInterest+pos]); + int sign = ((repl_subs[i][varsOfInterest+pos]>0)?1:-1) * ((repl_subs[j][varsOfInterest+pos]>0)?1:-1); + for (int k = 0; k <= varsOfInterest-nextWildcard; k++) + repl_subs[j][k] = scale_2*repl_subs[j][k] - sign*scale_1*repl_subs[i][k]; + + num_wild_in_repl_subs[j] = 0; + int first_wild = 0; + for (int k = 1; k <= -nextWildcard; k++) + if (repl_subs[j][varsOfInterest+k] != 0) { + num_wild_in_repl_subs[j]++; + first_wild = k; + } + + if (num_wild_in_repl_subs[j] == 1) { + if (wild_solved[first_wild] < 0) + wild_solved[first_wild] = j; + } + } + } + } + + has_unsolved = false; + for (int i = 1; i <= -nextWildcard; i++) + if (coef[varsOfInterest+i] != 0 && wild_solved[i] < 0) { + has_unsolved = true; + break; + } + } + + // Substitute all widecards in the red constraint + for (int i = 1; i <= -nextWildcard; i++) { + if (coef[varsOfInterest+i] != 0) { + int s = wild_solved[i]; + assert(s >= 0); + + coef_t l = lcm(abs(coef[varsOfInterest+i]), abs(repl_subs[s][varsOfInterest+i])); + coef_t scale_1 = l/abs(coef[varsOfInterest+i]); + coef_t scale_2 = l/abs(repl_subs[s][varsOfInterest+i]); + int sign = ((coef[varsOfInterest+i]>0)?1:-1) * ((repl_subs[s][varsOfInterest+i]>0)?1:-1); + for (int j = 0; j <= varsOfInterest-nextWildcard; j++) + coef[j] = scale_1*coef[j] - sign*scale_2*repl_subs[s][j]; + + if (scale_1 != 1) + stride *= scale_1; + } + } + + for (int i = 0; i < num_repl_subs; i++) + delete []repl_subs[i]; + } + + // Ready to insert into redMemory + int m = nMemories++; + redMemory[m].length = 0; + redMemory[m].kind = type; + redMemory[m].constantTerm = coef[0]; + for(int i = 1; i <= varsOfInterest; i++) + if (coef[i]) { + int j = redMemory[m].length++; + redMemory[m].coef[j] = coef[i]; + redMemory[m].var[j] = i; + } + if (type == redStride) redMemory[m].stride = stride; + if (DBUG) { + fprintf(outputFile,"Red constraint remembered\n"); + printProblem(); + } +} + +void Problem::recallRedMemories() { + if (nMemories) { + if (TRACE) { + fprintf(outputFile,"Recalling red memories\n"); + printProblem(); + } + + eqn* e = 0; + for(int m = 0; m < nMemories; m++) { + switch(redMemory[m].kind) { + case redGEQ: + { + int temporary_eqn = newGEQ(); + e = &GEQs[temporary_eqn]; + eqnnzero(e, nVars); + e->touched = 1; + break; + } + case redEQ: + { + int temporary_eqn = newEQ(); + e = &EQs[temporary_eqn]; + eqnnzero(e, nVars); + break; + } + case redStride: + { + int temporary_eqn = newEQ(); + e = &EQs[temporary_eqn]; + eqnnzero(e, nVars); + int i = addNewUnprotectedWildcard(); + e->coef[i] = -redMemory[m].stride; + break; + } + default: + assert(0); + } + e->color = EQ_RED; + e->coef[0] = redMemory[m].constantTerm; + for(int i = 0; i < redMemory[m].length; i++) { + int v = redMemory[m].var[i]; + assert(var[forwardingAddress[v]] == v); + e->coef[forwardingAddress[v]] = redMemory[m].coef[i]; + } + } + + nMemories = 0; + if (TRACE) { + fprintf(outputFile,"Red memories recalled\n"); + printProblem(); + } + } +} + +void Problem::swapVars(int i, int j) { + if (DEBUG) { + use_ugly_names++; + fprintf(outputFile, "Swapping %d and %d\n", i, j); + printProblem(); + use_ugly_names--; + } + std::swap(var[i], var[j]); + for (int e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i] != GEQs[e].coef[j]) { + GEQs[e].touched = true; + coef_t t = GEQs[e].coef[i]; + GEQs[e].coef[i] = GEQs[e].coef[j]; + GEQs[e].coef[j] = t; + } + for (int e = nEQs - 1; e >= 0; e--) + if (EQs[e].coef[i] != EQs[e].coef[j]) { + coef_t t = EQs[e].coef[i]; + EQs[e].coef[i] = EQs[e].coef[j]; + EQs[e].coef[j] = t; + } + for (int e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i] != SUBs[e].coef[j]) { + coef_t t = SUBs[e].coef[i]; + SUBs[e].coef[i] = SUBs[e].coef[j]; + SUBs[e].coef[j] = t; + } + if (DEBUG) { + use_ugly_names++; + fprintf(outputFile, "Swapping complete \n"); + printProblem(); + fprintf(outputFile, "\n"); + use_ugly_names--; + } +} + +void Problem::addingEqualityConstraint(int e) { + if (addingOuterEqualities && originalProblem != noProblem && + originalProblem != this && !conservative) { + int e2 = originalProblem->newEQ(); + if (TRACE) + fprintf(outputFile, "adding equality constraint %d to outer problem\n", e2); + eqnnzero(&originalProblem->EQs[e2], originalProblem->nVars); + for (int i = nVars; i >= 1; i--) { + int j; + for (j = originalProblem->nVars; j >= 1; j--) + if (originalProblem->var[j] == var[i]) + break; + if (j <= 0 || (outerColor && j > originalProblem->safeVars)) { + if (DBUG) + fprintf(outputFile, "retracting\n"); + originalProblem->nEQs--; + return; + } + originalProblem->EQs[e2].coef[j] = EQs[e].coef[i]; + } + originalProblem->EQs[e2].coef[0] = EQs[e].coef[0]; + + originalProblem->EQs[e2].color = outerColor; + if (DBUG) + originalProblem->printProblem(); + } +} + + +// Initialize hash codes for inequalities, remove obvious redundancy. +// Case 1: +// a1*x1+a2*x2+...>=c (1) +// a1*x2+a2*x2+...>=c' (2) +// if c>=c' then (2) is redundant, and vice versa. +// +// case 2: +// a1*x1+a2*x2+...>=c (1) +// a1*x1+a2*x2+...<=c' (2) +// if c=c' then add equality of (1) or (2), +// if c>c' then no solution. +// +// Finally it calls extended normalize process which handles +// wildcards in redundacy removal. +normalizeReturnType Problem::normalize() { + int i, j; + bool coupledSubscripts = false; + + check(); + + for (int e = 0; e < nGEQs; e++) { + if (!GEQs[e].touched) { + if (!singleVarGEQ(&GEQs[e])) + coupledSubscripts = true; + } + else { // normalize e + coef_t g; + int topVar; + int i0; + coef_t hashCode; + + { + int *p = &packing[0]; + for (int k = 1; k <= nVars; k++) + if (GEQs[e].coef[k]) { + *(p++) = k; + } + topVar = (p - &packing[0]) - 1; + } + + if (topVar == -1) { + if (GEQs[e].coef[0] < 0) { + // e has no solution + return (normalize_false); + } + deleteGEQ(e); + e--; + continue; + } + else if (topVar == 0) { + int singleVar = packing[0]; + g = GEQs[e].coef[singleVar]; + if (g > 0) { + GEQs[e].coef[singleVar] = 1; + GEQs[e].key = singleVar; + } + else { + g = -g; + GEQs[e].coef[singleVar] = -1; + GEQs[e].key = -singleVar; + } + if (g > 1) + GEQs[e].coef[0] = int_div(GEQs[e].coef[0], g); + } + else { + coupledSubscripts = true; + i0 = topVar; + i = packing[i0--]; + g = GEQs[e].coef[i]; + hashCode = g * (i + 3); + if (g < 0) + g = -g; + for (; i0 >= 0; i0--) { + coef_t x; + i = packing[i0]; + x = GEQs[e].coef[i]; + hashCode = hashCode * keyMult * (i + 3) + x; + if (x < 0) + x = -x; + if (x == 1) { + g = 1; + i0--; + break; + } + else + g = gcd(x, g); + } + for (; i0 >= 0; i0--) { + coef_t x; + i = packing[i0]; + x = GEQs[e].coef[i]; + hashCode = hashCode * keyMult * (i + 3) + x; + } + if (g > 1) { + GEQs[e].coef[0] = int_div(GEQs[e].coef[0], g); + i0 = topVar; + i = packing[i0--]; + GEQs[e].coef[i] = GEQs[e].coef[i] / g; + hashCode = GEQs[e].coef[i] * (i + 3); + for (; i0 >= 0; i0--) { + i = packing[i0]; + GEQs[e].coef[i] = GEQs[e].coef[i] / g; + hashCode = hashCode * keyMult * (i + 3) + GEQs[e].coef[i]; + } + } + + { + coef_t g2 = abs(hashCode); // get e's hash code + j = static_cast<int>(g2 % static_cast<coef_t>(hashTableSize)); + assert (g2 % (coef_t) hashTableSize == j); + while (1) { + eqn *proto = &(hashMaster[j]); + if (proto->touched == g2) { + if (proto->coef[0] == topVar) { + if (hashCode >= 0) + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + if (GEQs[e].coef[i] != proto->coef[i]) + break; + } + else + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + if (GEQs[e].coef[i] != -proto->coef[i]) + break; + } + + if (i0 < 0) { + if (hashCode >= 0) + GEQs[e].key = proto->key; + else + GEQs[e].key = -proto->key; + break; + } + } + } + else if (proto->touched < 0) { //insert e into the empty entry in hash table + eqnnzero(proto, nVars); + if (hashCode >= 0) + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + proto->coef[i] = GEQs[e].coef[i]; + } + else + for (i0 = topVar; i0 >= 0; i0--) { + i = packing[i0]; + proto->coef[i] = -GEQs[e].coef[i]; + } + proto->coef[0] = topVar; + proto->touched = g2; + proto->key = nextKey++; + + if (proto->key > maxKeys) { + fprintf(outputFile, "too many hash keys generated \n"); + fflush(outputFile); + exit(2); + } + if (hashCode >= 0) + GEQs[e].key = proto->key; + else + GEQs[e].key = -proto->key; + break; + } + j = (j + 1) % hashTableSize; + } + } + } + } + + GEQs[e].touched = false; + + { + int eKey = GEQs[e].key; + int e2; + if (e > 0) { + e2 = fastLookup[maxKeys - eKey]; + if (e2 >= 0 && e2 < e && GEQs[e2].key == -eKey) { + // confirm it is indeed a match -- by chun 10/29/2008 + int k; + for (k = nVars; k >= 1; k--) + if (GEQs[e2].coef[k] != -GEQs[e].coef[k]) + break; + + if (k == 0) { + if (GEQs[e2].coef[0] < -GEQs[e].coef[0]) { + // there is no solution from e and e2 + return (normalize_false); + } + else if (GEQs[e2].coef[0] == -GEQs[e].coef[0]) { + // reduce e and e2 to an equation + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + EQs[neweq].color = GEQs[e].color || GEQs[e2].color; + addingEqualityConstraint(neweq); + } + } + } + + e2 = fastLookup[maxKeys + eKey]; + if (e2 >= 0 && e2 < e && GEQs[e2].key == eKey) { + // confirm it is indeed a match -- by chun 10/29/2008 + int k; + for (k = nVars; k >= 1; k--) + if (GEQs[e2].coef[k] != GEQs[e].coef[k]) + break; + + if (k == 0) { + if (GEQs[e2].coef[0] > GEQs[e].coef[0] || + (GEQs[e2].coef[0] == GEQs[e].coef[0] && GEQs[e2].color)) { + // e2 is redundant + GEQs[e2].coef[0] = GEQs[e].coef[0]; + GEQs[e2].color = GEQs[e].color; + deleteGEQ(e); + e--; + continue; + } + else { + // e is redundant + deleteGEQ(e); + e--; + continue; + } + } + } + } + fastLookup[maxKeys + eKey] = e; + } + } + + // bypass entended normalization for temporary problem + if (!isTemporary && !inApproximateMode) + normalize_ext(); + + return coupledSubscripts ? normalize_coupled : normalize_uncoupled; +} + +// +// Extended normalize process, remove redundancy involving wildcards. +// e.g. +// exists alpha, beta: +// v1+8*alpha<=v2<=15+8*alpha (1) +// v1+8*beta<=v2<=15+8*beta (2) +// if there are no other inequalities involving alpha or beta, +// then either (1) or (2) is redundant. Such case can't be simplified +// by fourier-motzkin algorithm due to special meanings of existentials. +// +void Problem::normalize_ext() { + std::vector<BoolSet<> > disjoint_wildcards(nVars-safeVars, BoolSet<>(nVars-safeVars)); + std::vector<BoolSet<> > wildcards_in_inequality(nVars-safeVars, BoolSet<>(nGEQs)); + for (int i = 0; i < nVars-safeVars; i++) { + disjoint_wildcards[i].set(i); + } + + // create disjoint wildcard sets according to inequalities + for (int e = 0; e < nGEQs; e++) { + std::vector<BoolSet<> >::iterator first_set = disjoint_wildcards.end(); + for (int i = 0; i < nVars-safeVars; i++) + if (GEQs[e].coef[i+safeVars+1] != 0) { + wildcards_in_inequality[i].set(e); + + std::vector<BoolSet<> >::iterator cur_set = disjoint_wildcards.end(); + for (std::vector<BoolSet<> >::iterator j = disjoint_wildcards.begin(); j != disjoint_wildcards.end(); j++) + if ((*j).get(i)) { + cur_set = j; + break; + } + assert(cur_set!=disjoint_wildcards.end()); + if (first_set == disjoint_wildcards.end()) + first_set = cur_set; + else if (first_set != cur_set) { + *first_set |= *cur_set; + disjoint_wildcards.erase(cur_set); + } + } + } + + // do not consider wildcards appearing in equalities + for (int e = 0; e < nEQs; e++) + for (int i = 0; i < nVars-safeVars; i++) + if (EQs[e].coef[i+safeVars+1] != 0) { + for (std::vector<BoolSet<> >::iterator j = disjoint_wildcards.begin(); j != disjoint_wildcards.end(); j++) + if ((*j).get(i)) { + disjoint_wildcards.erase(j); + break; + } + } + + // create disjoint inequality sets + std::vector<BoolSet<> > disjoint_inequalities(disjoint_wildcards.size()); + for (size_t i = 0; i < disjoint_wildcards.size(); i++) + for (int j = 0; j < nVars-safeVars; j++) + if (disjoint_wildcards[i].get(j)) + disjoint_inequalities[i] |= wildcards_in_inequality[j]; + + // hash the inequality again, this time separate wildcard variables from + // regular variables + coef_t hash_safe[nGEQs]; + coef_t hash_wild[nGEQs]; + for (int e = 0; e < nGEQs; e++) { + coef_t hashCode = 0; + for (int i = 1; i <= safeVars; i++) + if (GEQs[e].coef[i] != 0) + hashCode = hashCode * keyMult * (i+3) + GEQs[e].coef[i]; + hash_safe[e] = hashCode; + + hashCode = 0; + for (int i = safeVars+1; i <= nVars; i++) + if (GEQs[e].coef[i] != 0) + hashCode = hashCode * keyMult + GEQs[e].coef[i]; + hash_wild[e] = hashCode; + } + + // sort hash keys for each disjoint set + std::vector<std::vector<std::pair<int, std::pair<coef_t, coef_t> > > > disjoint_hash(disjoint_inequalities.size()); + for (size_t i = 0; i < disjoint_inequalities.size(); i++) + for (int e = 0; e < nGEQs; e++) + if (disjoint_inequalities[i].get(e)) { + std::vector<std::pair<int, std::pair<coef_t, coef_t> > >::iterator j = disjoint_hash[i].begin(); + for (; j != disjoint_hash[i].end(); j++) + if ((hash_safe[e] > (*j).second.first) || + (hash_safe[e] == (*j).second.first && hash_wild[e] > (*j).second.second)) + break; + disjoint_hash[i].insert(j, std::make_pair(e, std::make_pair(hash_safe[e], hash_wild[e]))); + } + + // test wildcard equivalance + std::vector<bool> is_dead(nGEQs, false); + for (size_t i = 0; i < disjoint_wildcards.size(); i++) { + if (disjoint_inequalities[i].num_elem() == 0) + continue; + + for (size_t j = i+1; j < disjoint_wildcards.size(); j++) { + if (disjoint_wildcards[i].num_elem() != disjoint_wildcards[j].num_elem() || + disjoint_hash[i].size() != disjoint_hash[j].size()) + continue; + + bool match = true; + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + if (disjoint_hash[i][k].second != disjoint_hash[j][k].second) { + match = false; + break; + } + } + if (!match) + continue; + + // confirm same coefficients for regular variables + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + for (int p = 1; p <= safeVars; p++) + if (GEQs[disjoint_hash[i][k].first].coef[p] != GEQs[disjoint_hash[j][k].first].coef[p]) { + match = false; + break; + } + if (!match) + break; + } + if (!match) + continue; + + // now try combinatory wildcard matching + std::vector<int> wild_map(nVars-safeVars, -1); + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + int e1 = disjoint_hash[i][k].first; + int e2 = disjoint_hash[j][k].first; + + for (int p = 0; p < nVars-safeVars; p++) + if (GEQs[e1].coef[p+safeVars+1] != 0) { + if (wild_map[p] == -1) { + for (int q = 0; q < nVars-safeVars; q++) + if (wild_map[q] == -1 && + GEQs[e2].coef[q+safeVars+1] == GEQs[e1].coef[p+safeVars+1]) { + wild_map[p] = q; + wild_map[q] = p; + break; + } + if (wild_map[p] == -1) { + match = false; + break; + } + } + else if (GEQs[e2].coef[wild_map[p]+safeVars+1] != GEQs[e1].coef[p+safeVars+1]) { + match = false; + break; + } + } + if (!match) + break; + + for (int p = 0; p < nVars-safeVars; p++) + if (GEQs[e2].coef[p+safeVars+1] != 0 && + (wild_map[p] == -1 || GEQs[e2].coef[p+safeVars+1] != GEQs[e1].coef[wild_map[p]+safeVars+1])) { + match = false; + break; + } + if (!match) + break; + } + if (!match) + continue; + + // check constants + int dir = 0; + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + if (GEQs[disjoint_hash[i][k].first].coef[0] > GEQs[disjoint_hash[j][k].first].coef[0]) { + if (dir == 0) + dir = 1; + else if (dir == -1) { + match = false; + break; + } + } + else if (GEQs[disjoint_hash[i][k].first].coef[0] < GEQs[disjoint_hash[j][k].first].coef[0]) { + if (dir == 0) + dir = -1; + else if (dir == 1) { + match = false; + break; + } + } + } + if (!match) + continue; + + // check redness + int red_dir = 0; + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + if (GEQs[disjoint_hash[i][k].first].color > GEQs[disjoint_hash[j][k].first].color) { + if (red_dir == 0) + red_dir = 1; + else if (red_dir == -1) { + match = false; + break; + } + } + else if (GEQs[disjoint_hash[i][k].first].color < GEQs[disjoint_hash[j][k].first].color) { + if (red_dir == 0) + red_dir = -1; + else if (red_dir == 1) { + match = false; + break; + } + } + } + if (!match) + continue; + + // remove redundant inequalities + if (dir == 1 || (dir == 0 && red_dir == 1)) { + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + GEQs[disjoint_hash[i][k].first].coef[0] = GEQs[disjoint_hash[j][k].first].coef[0]; + GEQs[disjoint_hash[i][k].first].color = GEQs[disjoint_hash[j][k].first].color; + is_dead[disjoint_hash[j][k].first] = true; + } + } + else { + for (size_t k = 0; k < disjoint_hash[i].size(); k++) { + is_dead[disjoint_hash[j][k].first] = true; + } + } + } + } + + // eliminate dead inequalities + for (int e = nGEQs-1; e >= 0; e--) + if (is_dead[e]) { + deleteGEQ(e); + } +} + + +void initializeOmega(void) { + if (omegaInitialized) + return; + +// assert(sizeof(eqn)==sizeof(int)*(headerWords)+sizeof(coef_t)*(1+maxVars)); + nextWildcard = 0; + nextKey = maxVars + 1; + for (int i = 0; i < hashTableSize; i++) + hashMaster[i].touched = -1; + + sprintf(wildName[1], "__alpha"); + sprintf(wildName[2], "__beta"); + sprintf(wildName[3], "__gamma"); + sprintf(wildName[4], "__delta"); + sprintf(wildName[5], "__tau"); + sprintf(wildName[6], "__sigma"); + sprintf(wildName[7], "__chi"); + sprintf(wildName[8], "__omega"); + sprintf(wildName[9], "__pi"); + sprintf(wildName[10], "__ni"); + sprintf(wildName[11], "__Alpha"); + sprintf(wildName[12], "__Beta"); + sprintf(wildName[13], "__Gamma"); + sprintf(wildName[14], "__Delta"); + sprintf(wildName[15], "__Tau"); + sprintf(wildName[16], "__Sigma"); + sprintf(wildName[17], "__Chi"); + sprintf(wildName[18], "__Omega"); + sprintf(wildName[19], "__Pi"); + + omegaInitialized = 1; +} + +// +// This is experimental (I would say, clinical) fact: +// If the code below is removed then simplifyProblem cycles. +// +class brainDammage { +public: + brainDammage(); +}; + +brainDammage::brainDammage() { + initializeOmega(); +} + +static brainDammage Podgorny; + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_solve.cc b/omegalib/omega/src/omega_core/oc_solve.cc new file mode 100644 index 0000000..c25b6d0 --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_solve.cc @@ -0,0 +1,1378 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Solve ineqalities. + + Notes: + + History: +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> + +namespace omega { + +static int solveDepth = 0; +#define maxDead maxmaxGEQs + +int Problem::solve(int desiredResult) { + assert(omegaInitialized); + int result; + + checkVars(nVars+1); + assert(nVars >= safeVars); + if (desiredResult != OC_SOLVE_SIMPLIFY) + safeVars = 0; + + solveDepth++; + if (solveDepth > 50) { + fprintf(outputFile, "Solve depth = %d, inApprox = %d, aborting\n", solveDepth, inApproximateMode); + printProblem(); + fflush(outputFile); + + if (solveDepth > 60) + exit(2); + } + + check(); + do { + doItAgain = 0; + check(); + if (solveEQ() == false) { + solveDepth--; + return (false); + } + check(); + if (!nGEQs) { + result = true; + nVars = safeVars; + break; + } + else + result = solveGEQ(desiredResult); + check(); + } + while (doItAgain && desiredResult == OC_SOLVE_SIMPLIFY); + solveDepth--; + + return (result); +} + + +// Supporting functions of solveGEQ +int Problem::smoothWeirdEquations() { + int e1, e2, e3, p, q, k; + coef_t alpha, alpha1, alpha2, alpha3; + coef_t c; + int v; + int result = 0; + + for (e1 = nGEQs - 1; e1 >= 0; e1--) + if (!GEQs[e1].color) { + coef_t g = 999999; + for (v = nVars; v >= 1; v--) + if (GEQs[e1].coef[v] != 0 && abs(GEQs[e1].coef[v]) < g) + g = abs(GEQs[e1].coef[v]); + if (g > 20) { + e3 = newGEQ(); /* Create a scratch GEQ,not part of the prob.*/ + nGEQs--; + for (v = nVars; v >= 1; v--) + GEQs[e3].coef[v] = int_div(6 * GEQs[e1].coef[v] + g / 2, g); + GEQs[e3].color = EQ_BLACK; + GEQs[e3].touched = 1; + GEQs[e3].coef[0] = 9997; + if (DBUG) { + fprintf(outputFile, "Checking to see if we can derive: "); + printGEQ(&GEQs[e3]); + fprintf(outputFile, "\n from: "); + printGEQ(&GEQs[e1]); + fprintf(outputFile, "\n"); + } + + + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (e1 != e2 && !GEQs[e2].color) { + for (p = nVars; p > 1; p--) { + for (q = p - 1; q > 0; q--) { + alpha = check_mul(GEQs[e1].coef[p], GEQs[e2].coef[q]) - check_mul(GEQs[e2].coef[p], GEQs[e1].coef[q]); + if (alpha != 0) + goto foundPQ; + } + } + continue; + + foundPQ: + + alpha1 = check_mul(GEQs[e2].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e2].coef[p], GEQs[e3].coef[q]); + alpha2 = -(check_mul(GEQs[e1].coef[q], GEQs[e3].coef[p]) - check_mul(GEQs[e1].coef[p], GEQs[e3].coef[q])); + alpha3 = alpha; + + if (alpha1 * alpha2 <= 0) + continue; + if (alpha1 < 0) { + alpha1 = -alpha1; + alpha2 = -alpha2; + alpha3 = -alpha3; + } + if (alpha3 > 0) { + /* Trying to prove e3 is redundant */ + + /* verify alpha1*v1+alpha2*v2 = alpha3*v3 */ + for (k = nVars; k >= 1; k--) + if (check_mul(alpha3, GEQs[e3].coef[k]) + != check_mul(alpha1, GEQs[e1].coef[k]) + check_mul(alpha2, GEQs[e2].coef[k])) + goto nextE2; + + c = check_mul(alpha1, GEQs[e1].coef[0]) + check_mul(alpha2, GEQs[e2].coef[0]); + if (c < check_mul(alpha3, (GEQs[e3].coef[0] + 1))) + GEQs[e3].coef[0] = int_div(c, alpha3); + + } + nextE2:; + } + if (GEQs[e3].coef[0] < 9997) { + result++; +#if !defined NDEBUG + int e4 = +#endif + newGEQ(); +#if !defined NDEBUG + assert(e3 == e4); +#endif + if (DBUG) { + fprintf(outputFile, "Smoothing wierd equations; adding:\n"); + printGEQ(&GEQs[e3]); + fprintf(outputFile, "\nto:\n"); + printProblem(); + fprintf(outputFile, "\n\n"); + } + } + } + } + return (result); +} + + +void Problem::analyzeElimination( + int &v, + int &darkConstraints, + int &darkShadowFeasible, + int &unit, + coef_t ¶llelSplinters, + coef_t &disjointSplinters, + coef_t &lbSplinters, + coef_t &ubSplinters, + int ¶llelLB) { + + parallelSplinters = (posInfinity); // was MAXINT + disjointSplinters = 0; + lbSplinters = 0; + ubSplinters = 0; + + darkConstraints = 0; + darkShadowFeasible = 1; + coef_t maxUBc = 0; + coef_t maxLBc = 0; + int e,e2; + unit = 0; + int exact = 1; + + for (e = nGEQs - 1; e >= 0; e--) { + coef_t c = GEQs[e].coef[v]; + + if (c < 0) { + coef_t Lc, Uc, g, diff, grey; + + set_max(maxUBc, -c); + Uc = -c; + for (e2 = nGEQs - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[v] > 0) { + Lc = GEQs[e2].coef[v]; + g = 0; + grey = (Lc - 1) * (Uc - 1); + + for (int j = nVars; j >= 1; j--) { + coef_t diff = check_mul(Lc, GEQs[e].coef[j]) + check_mul(Uc, GEQs[e2].coef[j]); + if (diff < 0) diff = -diff; + g = gcd(g, diff); + if (g == 1) + break; + } + diff = check_mul(Lc, GEQs[e].coef[0]) + check_mul(Uc, GEQs[e2].coef[0]); + if (g == 0) { + if (diff < 0) { + /* Real shadow must be true */ + /* otherwise we would have found it during */ + /* check for opposing constraints */ + fprintf(outputFile, "Found conflicting constraints "); + printGEQ(&GEQs[e]); + fprintf(outputFile," and "); + printGEQ(&GEQs[e2]); + fprintf(outputFile,"\nin\n"); + printProblem(); + assert(diff >= 0); + } + if (diff < grey) { + darkShadowFeasible = 0; + if (parallelSplinters > diff+1) { + parallelSplinters = diff + 1; + parallelLB = e2; + } + } + else {/* dark shadow is true, don't need to worry about this constraint pair */ + } + } + else { + coef_t splinters= int_div(diff, g) - int_div(diff - grey, g); + if (splinters) exact = 0; + disjointSplinters += splinters; + if (g > 1) unit++; + darkConstraints++; + } + } + } + else if (c > 0) { + set_max(maxLBc, c); + } /* else + darkConstraints++; */ + } + + if (darkShadowFeasible) { + disjointSplinters++; + ubSplinters++; + lbSplinters++; + } + else disjointSplinters = (posInfinity); // was MAXINT + + + if (!darkShadowFeasible || !exact) + for (e = nGEQs - 1; e >= 0; e--) { + coef_t c = GEQs[e].coef[v]; + if (c < -1) { + c = -c; + ubSplinters += 1+(check_mul(c, maxLBc) - c - maxLBc) / maxLBc; + } + else if (c > 1) { + lbSplinters += 1+ (check_mul(c, maxUBc) - c - maxUBc) / maxUBc; + } + } + + if (DEBUG) { + fprintf(outputFile,"analyzing elimination of %s(%d)\n",variable(v),v); + if (darkShadowFeasible) + fprintf(outputFile," # dark constraints = %d\n", darkConstraints); + else + fprintf(outputFile," dark shadow obviously unfeasible\n"); + + fprintf(outputFile," " coef_fmt " LB splinters\n", lbSplinters); + fprintf(outputFile," " coef_fmt " UB splinters\n", ubSplinters); + if (disjointSplinters != (posInfinity)) + fprintf(outputFile," " coef_fmt " disjoint splinters\n", disjointSplinters); + if (parallelSplinters != (posInfinity)) + fprintf(outputFile," " coef_fmt " parallel splinters\n", parallelSplinters); + fprintf(outputFile, "\n"); + fprintf(outputFile," %3d unit score \n", unit); + } +} + + +void Problem::partialElimination() { + if (DBUG) { + fprintf(outputFile, "Performing Partial elimination\n"); + printProblem(); + } + int fv; + if (0) + fv = 0; + else + fv = safeVars; + bool somethingHappened = false; + for (int i = nVars; i > fv; i--) { + bool isDead[maxmaxGEQs]; + int e; + for (e = nGEQs-1; e >= 0; e--) isDead[e] = false; + int deadEqns[maxDead]; + int numDead = 0; + for (int e1 = nGEQs-1; e1 >= 0; e1--) + if (abs(GEQs[e1].coef[i]) == 1) { + bool isGood = true; + for (int e2 = nGEQs-1; e2 >= 0; e2--) + if (check_mul(GEQs[e2].coef[i], GEQs[e1].coef[i]) < 0) + if (GEQs[e1].key != -GEQs[e2].key) { + coef_t Uc = abs(GEQs[e2].coef[i]); + for (int k = nVars; k > fv; k--) + if (GEQs[e2].coef[k] + check_mul(GEQs[e1].coef[k], Uc) != 0) + isGood = false; + } + if (isGood) { + somethingHappened = true; + for (int e2 = nGEQs-1; e2 >= 0; e2--) + if (check_mul(GEQs[e2].coef[i], GEQs[e1].coef[i]) < 0) { + if (GEQs[e1].key != -GEQs[e2].key) { + coef_t Uc = abs(GEQs[e2].coef[i]); + int new_eqn; + if (numDead == 0) { + new_eqn = newGEQ(); + } + else { + new_eqn = deadEqns[--numDead]; + } + isDead[new_eqn] = false; + if (DBUG) { + fprintf(outputFile,"Eliminating constraint on %s\n", variable(i)); + fprintf(outputFile, "e1 = %d, e2 = %d, gen = %d\n", e1, e2, new_eqn); + printGEQ(&(GEQs[e1])); + fprintf(outputFile, "\n"); + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + } + + for (int k = nVars; k >= 0; k--) + GEQs[new_eqn].coef[k] = GEQs[e2].coef[k] + check_mul(GEQs[e1].coef[k], Uc); + GEQs[new_eqn].touched = true; + GEQs[new_eqn].color = GEQs[e2].color | GEQs[e1].color; + if (DBUG) { + fprintf(outputFile, "give "); + printGEQ(&(GEQs[new_eqn])); + fprintf(outputFile, "\n"); + } + assert(GEQs[new_eqn].coef[i] == 0); + } + } + deadEqns[numDead++] = e1; + isDead[e1] = true; + if (DEBUG) + fprintf(outputFile, "Killed %d\n", e1); + } + } + for (e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) { + deleteGEQ(e); + } + } + if (somethingHappened && DBUG) { + fprintf(outputFile, "Result of Partial elimination\n"); + printProblem(); + } +} + + +int Problem:: solveGEQ(int desiredResult) { + int i, j, k, e; + int fv; + int result; + int coupledSubscripts; + int eliminateAgain; + int smoothed = 0; + int triedEliminatingRedundant = 0; + j = 0; + + if (desiredResult != OC_SOLVE_SIMPLIFY) { + nSUBs = 0; + nMemories = 0; + safeVars = 0; + varsOfInterest = 0; + } + +solveGEQstart: + while (1) { + assert(desiredResult == OC_SOLVE_SIMPLIFY || nSUBs == 0); + check_number_GEQs(nGEQs); + + if (DEBUG) { + fprintf(outputFile, "\nSolveGEQ(%d,%d):\n", desiredResult, pleaseNoEqualitiesInSimplifiedProblems); + printProblem(); + fprintf(outputFile, "\n"); + } + +#ifndef NDEBUG + for(e=0;e<nSUBs;e++) + for(i=safeVars+1;i<=nVars;i++) + assert(!SUBs[e].coef[i]); +#endif + + check(); + + if (nVars == 1) { + int uColor = EQ_BLACK; + int lColor = EQ_BLACK; + coef_t upperBound = posInfinity; + coef_t lowerBound = negInfinity; + for (e = nGEQs - 1; e >= 0; e--) { + coef_t a = GEQs[e].coef[1]; + coef_t c = GEQs[e].coef[0]; + /* our equation is ax + c >= 0, or ax >= -c, or c >= -ax */ + if (a == 0) { + if (c < 0) { + if (TRACE) + fprintf(outputFile, "equations have no solution (G)\n"); + return (false); + } + } + else if (a > 0) { + if (a != 1) + c = int_div(c, a); + if (lowerBound < -c || (lowerBound == -c && !isRed(&GEQs[e]))) { + lowerBound = -c; + lColor = GEQs[e].color; + } + } + else { + if (a != -1) + c = int_div(c, -a); + if (upperBound > c || (upperBound == c && !isRed(&GEQs[e]))) { + upperBound = c; + uColor = GEQs[e].color; + } + } + } + if (DEBUG) + fprintf(outputFile, "upper bound = " coef_fmt "\n", upperBound); + if (DEBUG) + fprintf(outputFile, "lower bound = " coef_fmt "\n", lowerBound); + if (lowerBound > upperBound) { + if (TRACE) + fprintf(outputFile, "equations have no solution (H)\n"); + return (false); + } + if (desiredResult == OC_SOLVE_SIMPLIFY) { + nGEQs = 0; + if (safeVars == 1) { + if (lowerBound == upperBound && !uColor && !lColor) { + int e = newEQ(); + assert(e == 0); + EQs[e].coef[0] = -lowerBound; + EQs[e].coef[1] = 1; + EQs[e].color = lColor | uColor; + return (solve(desiredResult)); + } + else { + if (lowerBound > negInfinity) { + int e = newGEQ(); + assert(e == 0); + GEQs[e].coef[0] = -lowerBound; + GEQs[e].coef[1] = 1; + GEQs[e].key = 1; + GEQs[e].color = lColor; + GEQs[e].touched = 0; + } + if (upperBound < posInfinity) { + int e = newGEQ(); + GEQs[e].coef[0] = upperBound; + GEQs[e].coef[1] = -1; + GEQs[e].key = -1; + GEQs[e].color = uColor; + GEQs[e].touched = 0; + } + } + } + else + nVars = 0; + return (true); + } + if (originalProblem != noProblem && !lColor && !uColor && !conservative && lowerBound == upperBound) { + int e = newEQ(); + assert(e == 0); + EQs[e].coef[0] = -lowerBound; + EQs[e].coef[1] = 1; + EQs[e].color = EQ_BLACK; + addingEqualityConstraint(0); + } + return (true); + } + + if (!variablesFreed) { + variablesFreed = 1; + if (desiredResult != OC_SOLVE_SIMPLIFY) + freeEliminations(0); + else + freeEliminations(safeVars); + if (nVars == 1) + continue; + } + + + switch (normalize()) { + case normalize_false: + return (false); + break; + case normalize_coupled: + coupledSubscripts = true; + break; + case normalize_uncoupled: + coupledSubscripts = false; + break; + default: + coupledSubscripts = false; + assert(0 && "impossible case in SolveGEQ"); + } + + + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) { + fprintf(outputFile, "\nafter normalization:\n"); + printProblem(); + fprintf(outputFile, "\n"); + for(e=0;e<nGEQs;e++) assert(!GEQs[e].touched); + fprintf(outputFile, "eliminating variable using fourier-motzkin elimination\n"); + } + + // eliminating variable using fourier-motzkin elimination + do { + eliminateAgain = 0; + + if (nEQs > 0) + return (solve(desiredResult)); + + if (!coupledSubscripts) { + if (safeVars == 0) + nGEQs = 0; + else + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].key > safeVars || -safeVars > GEQs[e].key) + deleteGEQ(e); + nVars = safeVars; + return (true); + } + + if (desiredResult != OC_SOLVE_SIMPLIFY) + fv = 0; + else + fv = safeVars; + + if (nVars == 0 || nGEQs == 0) { + nGEQs = 0; + if (desiredResult == OC_SOLVE_SIMPLIFY) + nVars = safeVars; + return (true); + } + if (desiredResult == OC_SOLVE_SIMPLIFY && nVars == safeVars) { + return (true); + } + + + if (nGEQs+6 > maxGEQs || nGEQs > 2 * nVars * nVars + 4 * nVars + 10) { + if (TRACE) + fprintf(outputFile, "TOO MANY EQUATIONS; %d equations, %d variables, ELIMINATING REDUNDANT ONES\n", nGEQs, nVars); + if (!quickKill(0,true)) + return 0; + if (nEQs > 0) + return (solve(desiredResult)); + if (TRACE) + fprintf(outputFile, "END ELIMINATION OF REDUNDANT EQUATIONS\n"); + if (DBUG) printProblem(); + } + + + { + int darkConstraints, darkShadowFeasible, unit, parallelLB; + coef_t parallelSplinters, disjointSplinters, lbSplinters, ubSplinters, splinters; + coef_t bestScore, score; + int bestVar; + int exact; + int Ue,Le; + + if (desiredResult != OC_SOLVE_SIMPLIFY) fv = 0; + else fv = safeVars; + + if (DEBUG) { + fprintf(outputFile,"Considering elimination possibilities[ \n"); + printProblem(); + } + + analyzeGEQstart: + try { + bestScore = posInfinity; + bestVar = -1; + for (i = nVars; i != fv; i--) { + analyzeElimination(i, darkConstraints, darkShadowFeasible, unit, parallelSplinters, disjointSplinters, lbSplinters, ubSplinters, parallelLB); + + score = min(min(parallelSplinters,disjointSplinters), + min(lbSplinters,ubSplinters)); + exact = score == 1; + score = 10000*(score-1) + darkConstraints; + if (score >= posInfinity) // too big the score + score = posInfinity - 1; + score -= 3*unit; + + if (score < bestScore) { + bestScore = score; + bestVar = i; + if (i > 4 && score < nGEQs) break; + } + } + assert(bestVar>=0); + exact = bestScore < 10000; + i = bestVar; + assert(i<=nVars); + analyzeElimination(i, darkConstraints, darkShadowFeasible, unit, parallelSplinters, disjointSplinters, lbSplinters, ubSplinters, parallelLB); + if (DEBUG) { + fprintf(outputFile,"] Choose to eliminate %s \n",variable(i)); + } + splinters = lbSplinters; + if (splinters <= parallelSplinters) + parallelSplinters = posInfinity; + else splinters = parallelSplinters; + if (disjointSplinters == 1) splinters = 1; + exact = splinters == 1; + if (inApproximateMode) exact = 1; + } + catch (std::overflow_error) { + int result = quickKill(0, true); + if (result == 0) + return 0; + else if (result == 1) + return true; + else { + if (nEQs > 0) + return (solve(desiredResult)); + triedEliminatingRedundant = 1; + goto analyzeGEQstart; + } + } + + if (!triedEliminatingRedundant && darkConstraints > maxGEQs) { + if (TRACE) + fprintf(outputFile, "Elimination will create TOO MANY EQUATIONS; %d equations, %d variables, %d new constraints, ELIMINATING REDUNDANT ONES\n", nGEQs, nVars,darkConstraints); + if (!quickKill(0)) + return 0; + if (nEQs > 0) + return (solve(desiredResult)); + if (TRACE) + fprintf(outputFile, "END ELIMINATION OF REDUNDANT EQUATIONS\n"); + if (DBUG) printProblem(); + + triedEliminatingRedundant = 1; + eliminateAgain = 1; + continue; + } + + if (!exact && !triedEliminatingRedundant && + safeVars > 0 && desiredResult == OC_SOLVE_SIMPLIFY) { + if (TRACE) + fprintf(outputFile, "Trying to produce exact elimination by finding redundant constraints [\n"); + if (!quickKill(1)) return 0; + if (TRACE) + fprintf(outputFile, "]\n"); + triedEliminatingRedundant = 1; + eliminateAgain = 1; + continue; + } + triedEliminatingRedundant = 0; + + if (desiredResult == OC_SOLVE_SIMPLIFY && !exact) { + partialElimination(); + switch (normalize()) { + case normalize_false: + return (false); + break; + case normalize_coupled: + case normalize_uncoupled: + break; + } + if (nEQs) return solveEQ(); + if (DBUG) fprintf(outputFile,"Stopping short due to non-exact elimination\n"); + return (true); + } + + if ( desiredResult == OC_SOLVE_SIMPLIFY && darkConstraints > maxGEQs) { + if (DBUG) fprintf(outputFile,"Stopping short due to overflow of GEQs: %d\n", darkConstraints); + return (true); + } + + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) { + fprintf(outputFile, "going to eliminate %s, (%d)\n", variable(i), i); + if (DEBUG) + printProblem(); + fprintf(outputFile, "score = " coef_fmt "/" coef_fmt "\n", bestScore,splinters); + } + + if (!exact && desiredResult == OC_SOLVE_SIMPLIFY && parallelSplinters == splinters) { + return parallelSplinter(parallelLB, parallelSplinters, desiredResult); + } + + // smoothed = 0; // what a bug!!! -- by chun 6/10/2008 + + if (i != nVars) { + j = nVars; + swapVars(i,j); + + i = j; + } + else if (DEBUG) { + printVars(); + fprintf(outputFile, "No swap needed before eliminating %s(%d/%d)\n",variable(i),i,nVars); + for(j=1;j<=i;j++) fprintf(outputFile,"var #%d = %s(%x)\n",j,variable(j),var[j]); + printProblem(); + } + nVars--; + + if (exact) { + if (nVars == 1) { + coef_t upperBound = posInfinity; + coef_t lowerBound = negInfinity; + int ub_color = 0; + int lb_color = 0; + coef_t constantTerm, coefficient; + int topEqn = nGEQs - 1; + coef_t Lc; + for (Le = topEqn; Le >= 0; Le--) + if ((Lc = GEQs[Le].coef[i]) == 0) { + if (GEQs[Le].coef[1] == 1) { + constantTerm = -GEQs[Le].coef[0]; + if (constantTerm > lowerBound || (constantTerm == lowerBound && !isRed(&GEQs[Le]))) { + lowerBound = constantTerm; + lb_color = GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Le].color == EQ_BLACK) + fprintf(outputFile, " :::=> %s >= " coef_fmt "\n", variable(1), constantTerm); + else + fprintf(outputFile, " :::=> [%s >= " coef_fmt "]\n", variable(1), constantTerm); + } + } + else { + constantTerm = GEQs[Le].coef[0]; + if (constantTerm < upperBound || (constantTerm == upperBound && !isRed(&GEQs[Le]))) { + upperBound = constantTerm; + ub_color = GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Le].color == EQ_BLACK) + fprintf(outputFile, " :::=> %s <= " coef_fmt "\n", variable(1), constantTerm); + else + fprintf(outputFile, " :::=> [%s <= " coef_fmt "]\n", variable(1), constantTerm); + } + } + } + else if (Lc > 0) { + for (Ue = topEqn; Ue >= 0; Ue--) + if (GEQs[Ue].coef[i] < 0) { + if (GEQs[Le].key != -GEQs[Ue].key) { + coef_t Uc = -GEQs[Ue].coef[i]; + coefficient = check_mul(GEQs[Ue].coef[1], Lc) + check_mul(GEQs[Le].coef[1], Uc); + constantTerm = check_mul(GEQs[Ue].coef[0], Lc) + check_mul(GEQs[Le].coef[0], Uc); + if (DEBUG) { + printGEQextra(&(GEQs[Ue])); + fprintf(outputFile, "\n"); + printGEQextra(&(GEQs[Le])); + fprintf(outputFile, "\n"); + } + if (coefficient > 0) { + constantTerm = -(int_div(constantTerm, coefficient)); + /* assert(black == 0) */ + if (constantTerm > lowerBound || + (constantTerm == lowerBound && + (desiredResult != OC_SOLVE_SIMPLIFY || (GEQs[Ue].color == EQ_BLACK && GEQs[Le].color == EQ_BLACK)))) { + lowerBound = constantTerm; + lb_color = GEQs[Ue].color || GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Ue].color || GEQs[Le].color) + fprintf(outputFile, " ::=> [%s >= " coef_fmt "]\n", variable(1), constantTerm); + else + fprintf(outputFile, " ::=> %s >= " coef_fmt "\n", variable(1), constantTerm); + } + } + else if (coefficient < 0) { + constantTerm = (int_div(constantTerm, -coefficient)); + if (constantTerm < upperBound || + (constantTerm == upperBound && GEQs[Ue].color == EQ_BLACK && GEQs[Le].color == EQ_BLACK)) { + upperBound = constantTerm; + ub_color = GEQs[Ue].color || GEQs[Le].color; + } + if (DEBUG) { + if (GEQs[Ue].color || GEQs[Le].color) + fprintf(outputFile, " ::=> [%s <= " coef_fmt "]\n", variable(1), constantTerm); + else + fprintf(outputFile, " ::=> %s <= " coef_fmt "\n", variable(1), constantTerm); + } + } + } + } + } + nGEQs = 0; + if (DEBUG) + fprintf(outputFile, " therefore, %c" coef_fmt " <= %c%s%c <= " coef_fmt "%c\n", lb_color ? '[' : ' ', lowerBound, (lb_color && !ub_color) ? ']' : ' ', variable(1), (!lb_color && ub_color) ? '[' : ' ', upperBound, ub_color ? ']' : ' '); + if (lowerBound > upperBound) + return (false); + + if (upperBound == lowerBound) { + int e = newEQ(); + assert(e == 0); + EQs[e].coef[1] = -1; + EQs[e].coef[0] = upperBound; + EQs[e].color = ub_color | lb_color; + addingEqualityConstraint(0); + } + else if (safeVars == 1) { + if (upperBound != posInfinity) { + int e = newGEQ(); + assert(e == 0); + GEQs[e].coef[1] = -1; + GEQs[e].coef[0] = upperBound; + GEQs[e].color = ub_color; + GEQs[e].key = -1; + GEQs[e].touched = 0; + } + if (lowerBound != negInfinity) { + int e = newGEQ(); + GEQs[e].coef[1] = 1; + GEQs[e].coef[0] = -lowerBound; + GEQs[e].color = lb_color; + GEQs[e].key = 1; + GEQs[e].touched = 0; + } + } + if (safeVars == 0) + nVars = 0; + return (true); + } + eliminateAgain = 1; + + { + int deadEqns[maxDead]; + int numDead = 0; + int topEqn = nGEQs - 1; + int lowerBoundCount = 0; + for (Le = topEqn; Le >= 0; Le--) + if (GEQs[Le].coef[i] > 0) + lowerBoundCount++; + if (DEBUG) + fprintf(outputFile, "lower bound count = %d\n", lowerBoundCount); + if (lowerBoundCount == 0) { + if (desiredResult != OC_SOLVE_SIMPLIFY) fv = 0; + else fv = safeVars; + nVars++; + freeEliminations(fv); + continue; + } + for (Le = topEqn; Le >= 0; Le--) + if (GEQs[Le].coef[i] > 0) { + coef_t Lc = GEQs[Le].coef[i]; + for (Ue = topEqn; Ue >= 0; Ue--) + if (GEQs[Ue].coef[i] < 0) { + if (GEQs[Le].key != -GEQs[Ue].key) { + coef_t Uc = -GEQs[Ue].coef[i]; + int e2; + if (numDead == 0) { + /*( Big kludge warning ) */ + /* this code is still using location nVars+1 */ + /* but newGEQ, if it reallocates, only copies*/ + /* locations up to nVars. This fixes that. */ + nVars++; + e2 = newGEQ(); + nVars--; + } + else { + e2 = deadEqns[--numDead]; + } + if (DEBUG) + fprintf(outputFile, "Le = %d, Ue = %d, gen = %d\n", Le, Ue, e2); + if (DEBUG) { + printGEQextra(&(GEQs[Le])); + fprintf(outputFile, "\n"); + printGEQextra(&(GEQs[Ue])); + fprintf(outputFile, "\n"); + } + eliminateAgain = 0; + coef_t g = gcd(Lc,Uc); + coef_t Lc_over_g = Lc/g; + coef_t Uc_over_g = Uc/g; + + for (k = nVars; k >= 0; k--) + GEQs[e2].coef[k] = + check_mul(GEQs[Ue].coef[k], Lc_over_g) + check_mul(GEQs[Le].coef[k], Uc_over_g); + + GEQs[e2].coef[nVars + 1] = 0; + GEQs[e2].touched = true; + GEQs[e2].color = GEQs[Ue].color | GEQs[Le].color; + + if (DEBUG) { + printGEQ(&(GEQs[e2])); + fprintf(outputFile, "\n"); + } + } + if (lowerBoundCount == 1) { + deadEqns[numDead++] = Ue; + if (DEBUG) + fprintf(outputFile, "Killed %d\n", Ue); + } + } + lowerBoundCount--; + deadEqns[numDead++] = Le; + if (DEBUG) + fprintf(outputFile, "Killed %d\n", Le); + } + + { + int isDead[maxmaxGEQs]; + for (e = nGEQs - 1; e >= 0; e--) + isDead[e] = false; + while (numDead > 0) { + e = deadEqns[--numDead]; + isDead[e] = true; + } + for (e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) { + nVars++; + deleteGEQ(e); + nVars--; + } + } + continue; + } + } + else { + Problem *rS, *iS; + + rS = new Problem; + iS = new Problem; + + iS->nVars = rS->nVars = nVars; // do this immed.; in case of reallocation, we + // need to know how much to copy + rS->get_var_name = get_var_name; + rS->getVarNameArgs = getVarNameArgs; + iS->get_var_name = get_var_name; + iS->getVarNameArgs = getVarNameArgs; + + for (e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i] == 0) { + int re2 = rS->newGEQ(); + int ie2 = iS->newGEQ(); + eqnncpy(&(rS->GEQs[re2]), &GEQs[e], nVars); + eqnncpy(&(iS->GEQs[ie2]), &GEQs[e], nVars); + if (DEBUG) { + int t; + fprintf(outputFile, "Copying (%d, " coef_fmt "): ", i, GEQs[e].coef[i]); + printGEQextra(&GEQs[e]); + fprintf(outputFile, "\n"); + for (t = 0; t <= nVars + 1; t++) + fprintf(outputFile, coef_fmt " ", GEQs[e].coef[t]); + fprintf(outputFile, "\n"); + } + } + for (Le = nGEQs - 1; Le >= 0; Le--) + if (GEQs[Le].coef[i] > 0) { + coef_t Lc = GEQs[Le].coef[i]; + for (Ue = nGEQs - 1; Ue >= 0; Ue--) + if (GEQs[Ue].coef[i] < 0) + if (GEQs[Le].key != -GEQs[Ue].key) { + coef_t Uc = -GEQs[Ue].coef[i]; + coef_t g = gcd(Lc,Uc); + coef_t Lc_over_g = Lc/g; + coef_t Uc_over_g = Uc/g; + int re2 = rS->newGEQ(); + int ie2 = iS->newGEQ(); + rS->GEQs[re2].touched = iS->GEQs[ie2].touched = true; + if (DEBUG) { + fprintf(outputFile, "---\n"); + fprintf(outputFile, "Le(Lc) = %d(" coef_fmt "), Ue(Uc) = %d(" coef_fmt "), gen = %d\n", Le, Lc, Ue, Uc, ie2); + printGEQextra(&GEQs[Le]); + fprintf(outputFile, "\n"); + printGEQextra(&GEQs[Ue]); + fprintf(outputFile, "\n"); + } + + if (Uc == Lc) { + for (k = nVars; k >= 0; k--) + iS->GEQs[ie2].coef[k] = rS->GEQs[re2].coef[k] = + GEQs[Ue].coef[k] + GEQs[Le].coef[k]; + iS->GEQs[ie2].coef[0] -= (Uc - 1); + } + else { + for (k = nVars; k >= 0; k--) + iS->GEQs[ie2].coef[k] = rS->GEQs[re2].coef[k] = + check_mul(GEQs[Ue].coef[k], Lc_over_g) + check_mul(GEQs[Le].coef[k], Uc_over_g); + iS->GEQs[ie2].coef[0] -= check_mul(Uc_over_g-1, Lc_over_g-1); + } + + iS->GEQs[ie2].color = rS->GEQs[re2].color + = GEQs[Ue].color || GEQs[Le].color; + + if (DEBUG) { + printGEQ(&(rS->GEQs[re2])); + fprintf(outputFile, "\n"); + } + // ie2 = iS->newGEQ(); + // re2 = rS->newGEQ(); + } + + } + iS->variablesInitialized = rS->variablesInitialized = 1; + iS->nEQs = rS->nEQs = 0; + assert(desiredResult != OC_SOLVE_SIMPLIFY); + assert(nSUBs == 0); + iS->nSUBs = rS->nSUBs = nSUBs; + iS->safeVars = rS->safeVars = safeVars; + int t; + for (t = nVars; t >= 0; t--) + rS->var[t] = var[t]; + for (t = nVars; t >= 0; t--) + iS->var[t] = var[t]; + nVars++; + if (desiredResult != true) { + int t = trace; + if (TRACE) + fprintf(outputFile, "\nreal solution(%d):\n", depth); + depth++; + trace = 0; + if (originalProblem == noProblem) { + originalProblem = this; + result = rS->solveGEQ(false); + originalProblem = noProblem; + } + else + result = rS->solveGEQ(false); + trace = t; + depth--; + if (result == false) { + delete rS; + delete iS; + return (result); + } + + if (nEQs > 0) { + /* An equality constraint must have been found */ + delete rS; + delete iS; + return (solve(desiredResult)); + } + } + if (desiredResult != false) { + if (darkShadowFeasible) { + if (TRACE) + fprintf(outputFile, "\ninteger solution(%d):\n", depth); + depth++; + conservative++; + result = iS->solveGEQ(desiredResult); + conservative--; + depth--; + if (result != false) { + delete rS; + delete iS; + return (result); + } + } + if (TRACE) + fprintf(outputFile, "have to do exact analysis\n"); + + { + coef_t worstLowerBoundConstant=1; + int lowerBounds = 0; + int lowerBound[maxmaxGEQs]; + int smallest; + int t; + conservative++; + for (e = 0; e < nGEQs; e++) + if (GEQs[e].coef[i] < -1) { + set_max(worstLowerBoundConstant, + -GEQs[e].coef[i]); + } + else if (GEQs[e].coef[i] > 1) + lowerBound[lowerBounds++] = e; + /* sort array */ + for (j = 0; j < lowerBounds; j++) { + smallest = j; + for (k = j + 1; k < lowerBounds; k++) + if (GEQs[lowerBound[smallest]].coef[i] > GEQs[lowerBound[k]].coef[i]) + smallest = k; + t = lowerBound[smallest]; + lowerBound[smallest] = lowerBound[j]; + lowerBound[j] = t; + } + if (DEBUG) { + fprintf(outputFile, "lower bound coeeficients = "); + for (j = 0; j < lowerBounds; j++) + fprintf(outputFile, " " coef_fmt, GEQs[lowerBound[j]].coef[i]); + fprintf(outputFile, "\n"); + } + + + for (j = 0; j < lowerBounds; j++) { + coef_t maxIncr; + coef_t c; + e = lowerBound[j]; + maxIncr = (check_mul(GEQs[e].coef[i]-1, worstLowerBoundConstant-1) - 1) / worstLowerBoundConstant; + + /* maxIncr += 2; */ + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) { + fprintf(outputFile, "for equation "); + printGEQ(&GEQs[e]); + fprintf(outputFile, "\ntry decrements from 0 to " coef_fmt "\n", maxIncr); + printProblem(); + } + if (maxIncr > 50) { + if (!smoothed && smoothWeirdEquations()) { + conservative--; + delete rS; + delete iS; + smoothed = 1; + goto solveGEQstart; + } + } + int neweq = newEQ(); + assert(neweq == 0); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + /* + * if (GEQs[e].color) fprintf(outputFile,"warning: adding black equality constraint + * based on red inequality\n"); + */ + EQs[neweq].color = EQ_BLACK; + eqnnzero(&GEQs[e], nVars); + GEQs[e].touched = true; + for (c = maxIncr; c >= 0; c--) { + if (DBUG) + fprintf(outputFile, "trying next decrement of " coef_fmt "\n", maxIncr - c); + if (DBUG) + printProblem(); + *rS = *this; + if (DEBUG) + rS->printProblem(); + result = rS->solve(desiredResult); + if (result == true) { + delete rS; + delete iS; + conservative--; + return (true); + } + EQs[0].coef[0]--; + } + if (j + 1 < lowerBounds) { + nEQs = 0; + eqnncpy(&GEQs[e], &EQs[0], nVars); + GEQs[e].touched = 1; + GEQs[e].color = EQ_BLACK; + *rS = *this; + if (DEBUG) + fprintf(outputFile, "exhausted lower bound, checking if still feasible "); + result = rS->solve(false); + if (result == false) + break; + } + } + if ((doTrace && desiredResult == OC_SOLVE_SIMPLIFY) || DBUG) + fprintf(outputFile, "fall-off the end\n"); + delete rS; + delete iS; + + conservative--; + return (false); + } + } + delete rS; + delete iS; + } + return (OC_SOLVE_UNKNOWN); + } + } + while (eliminateAgain); + } +} + + +int Problem::parallelSplinter(int e, int diff, int desiredResult) { + Problem *tmpProblem; + int i; + if (DBUG) { + fprintf(outputFile, "Using parallel splintering\n"); + printProblem(); + } + tmpProblem = new Problem; + int neweq = newEQ(); + assert(neweq == 0); + eqnncpy(&EQs[0], &GEQs[e], nVars); + for (i = 0; i <= diff; i++) { + *tmpProblem = * this; + tmpProblem->isTemporary = true; + if (DBUG) { + fprintf(outputFile, "Splinter # %i\n", i); + printProblem(); + } + if (tmpProblem->solve(desiredResult)) { + delete tmpProblem; + return true; + } + EQs[0].coef[0]--; + } + delete tmpProblem; + return false; +} + + +int Problem::verifyProblem() { + int result; + int e; + int areRed; + check(); + Problem tmpProblem(*this); + tmpProblem.varsOfInterest = 0; + tmpProblem.safeVars = 0; + tmpProblem.nSUBs = 0; + tmpProblem.nMemories = 0; + tmpProblem.isTemporary = true; + areRed = 0; + if (mayBeRed) { + for(e=0; e<nEQs; e++) if (EQs[e].color) areRed = 1; + for(e=0; e<nGEQs; e++) if (GEQs[e].color) areRed = 1; + if (areRed) tmpProblem.turnRedBlack(); + } + originalProblem = this; + assert(!outerColor); + outerColor = areRed; + if (TRACE) { + fprintf(outputFile, "verifying problem: [\n"); + printProblem(); + } + tmpProblem.check(); + tmpProblem.freeEliminations(0); + result = tmpProblem.solve(OC_SOLVE_UNKNOWN); + originalProblem = noProblem; + outerColor = 0; + if (TRACE) { + if (result) + fprintf(outputFile, "] verified problem\n"); + else + fprintf(outputFile, "] disproved problem\n"); + printProblem(); + } + check(); + return result; +} + + +void Problem:: freeEliminations(int fv) { + int tryAgain = 1; + int i, e, e2; + while (tryAgain) { + tryAgain = 0; + for (i = nVars; i > fv; i--) { + for (e = nGEQs - 1; e >= 0; e--) + if (GEQs[e].coef[i]) + break; + if (e < 0) + e2 = e; + else if (GEQs[e].coef[i] > 0) { + for (e2 = e - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[i] < 0) + break; + } + else { + for (e2 = e - 1; e2 >= 0; e2--) + if (GEQs[e2].coef[i] > 0) + break; + } + if (e2 < 0) { + int e3; + for (e3 = nSUBs - 1; e3 >= 0; e3--) + if (SUBs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + for (e3 = nEQs - 1; e3 >= 0; e3--) + if (EQs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + if (DBUG) + fprintf(outputFile, "a free elimination of %s (%d)\n", variable(i),e); + if (e >= 0) { + deleteGEQ(e); + for (e--; e >= 0; e--) + if (GEQs[e].coef[i]) { + deleteGEQ(e); + } + tryAgain = (i < nVars); + } + deleteVariable(i); + } + } + } + + if (DEBUG) { + fprintf(outputFile, "\nafter free eliminations:\n"); + printProblem(); + fprintf(outputFile, "\n"); + } +} + + +void Problem::freeRedEliminations() { + int tryAgain = 1; + int i, e, e2; + int isRedVar[maxVars]; + int isDeadVar[maxVars]; + int isDeadGEQ[maxmaxGEQs]; + for (i = nVars; i > 0; i--) { + isRedVar[i] = 0; + isDeadVar[i] = 0; + } + for (e = nGEQs - 1; e >= 0; e--) { + isDeadGEQ[e] = 0; + if (GEQs[e].color) + for (i = nVars; i > 0; i--) + if (GEQs[e].coef[i] != 0) + isRedVar[i] = 1; + } + + while (tryAgain) { + tryAgain = 0; + for (i = nVars; i > 0; i--) + if (!isRedVar[i] && !isDeadVar[i]) { + for (e = nGEQs - 1; e >= 0; e--) + if (!isDeadGEQ[e] && GEQs[e].coef[i]) + break; + if (e < 0) + e2 = e; + else if (GEQs[e].coef[i] > 0) { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDeadGEQ[e2] && GEQs[e2].coef[i] < 0) + break; + } + else { + for (e2 = e - 1; e2 >= 0; e2--) + if (!isDeadGEQ[e2] && GEQs[e2].coef[i] > 0) + break; + } + if (e2 < 0) { + int e3; + for (e3 = nSUBs - 1; e3 >= 0; e3--) + if (SUBs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + for (e3 = nEQs - 1; e3 >= 0; e3--) + if (EQs[e3].coef[i]) + break; + if (e3 >= 0) + continue; + if (DBUG) + fprintf(outputFile, "a free red elimination of %s\n", variable(i)); + for (; e >= 0; e--) + if (GEQs[e].coef[i]) + isDeadGEQ[e] = 1; + tryAgain = 1; + isDeadVar[i] = 1; + } + } + } + + for (e = nGEQs - 1; e >= 0; e--) + if (isDeadGEQ[e]) + deleteGEQ(e); + + for (i = nVars; i > safeVars; i--) + if (isDeadVar[i]) + deleteVariable(i); + + + if (DEBUG) { + fprintf(outputFile, "\nafter free red eliminations:\n"); + printProblem(); + fprintf(outputFile, "\n"); + } +} + +} // namespace diff --git a/omegalib/omega/src/omega_core/oc_util.cc b/omegalib/omega/src/omega_core/oc_util.cc new file mode 100644 index 0000000..a7d21be --- /dev/null +++ b/omegalib/omega/src/omega_core/oc_util.cc @@ -0,0 +1,327 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + + Notes: + + History: +*****************************************************************************/ + +#include <omega/omega_core/oc_i.h> +#include <algorithm> + +namespace omega { + +void Problem:: problem_merge(Problem &p2) { + int newLocation[maxVars]; + int i,e2; + + resurrectSubs(); + p2.resurrectSubs(); + setExternals(); + p2.setExternals(); + + assert(safeVars == p2.safeVars); + if(DBUG) { + fprintf(outputFile,"Merging:\n"); + printProblem(); + fprintf(outputFile,"and\n"); + p2.printProblem(); + } + for(i=1; i<= p2.safeVars; i++) { + assert(p2.var[i] > 0) ; + newLocation[i] = forwardingAddress[p2.var[i]]; + } + for(; i<= p2.nVars; i++) { + int j = ++(nVars); + newLocation[i] = j; + zeroVariable(j); + var[j] = -1; + } + newLocation[0] = 0; + + for (e2 = p2.nEQs - 1; e2 >= 0; e2--) { + int e1 = newEQ(); + eqnnzero(&(EQs[e1]), nVars); + for(i=0;i<=p2.nVars;i++) + EQs[e1].coef[newLocation[i]] = p2.EQs[e2].coef[i]; + } + for (e2 = p2.nGEQs - 1; e2 >= 0; e2--) { + int e1 = newGEQ(); + eqnnzero(&(GEQs[e1]), nVars); + GEQs[e1].touched = 1; + for(i=0;i<=p2.nVars;i++) + GEQs[e1].coef[newLocation[i]] = p2.GEQs[e2].coef[i]; + } + int w = -1; + for (i = 1; i <= nVars; i++) + if (var[i] < 0) var[i] = w--; + if(DBUG) { + fprintf(outputFile,"to get:\n"); + printProblem(); + } +} + + + +void Problem::chainUnprotect() { + int i, e; + int unprotect[maxVars]; + int any = 0; + for (i = 1; i <= safeVars; i++) { + unprotect[i] = (var[i] < 0); + for (e = nSUBs - 1; e >= 0; e--) + if (SUBs[e].coef[i]) + unprotect[i] = 0; + } + for (i = 1; i <= safeVars; i++) if (unprotect[i]) any=1; + if (!any) return; + + if (DBUG) { + fprintf(outputFile, "Doing chain reaction unprotection\n"); + printProblem(); + for (i = 1; i <= safeVars; i++) + if (unprotect[i]) + fprintf(outputFile, "unprotecting %s\n", variable(i)); + } + for (i = 1; i <= safeVars; i++) + if (unprotect[i]) { + /* wild card */ + if (i < safeVars) { + int j = safeVars; + std::swap(var[i], var[j]); + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].touched = 1; + std::swap(GEQs[e].coef[i], GEQs[e].coef[j]); + } + for (e = nEQs - 1; e >= 0; e--) + std::swap(EQs[e].coef[i], EQs[e].coef[j]); + for (e = nSUBs - 1; e >= 0; e--) + std::swap(SUBs[e].coef[i], SUBs[e].coef[j]); + std::swap(unprotect[i], unprotect[j]); + i--; + } + safeVars--; + } + if (DBUG) { + fprintf(outputFile, "After chain reactions\n"); + printProblem(); + } +} + +void Problem::resurrectSubs() { + if (nSUBs > 0 && !pleaseNoEqualitiesInSimplifiedProblems) { + int i, e, n, m,mbr; + mbr = 0; + for (e = nGEQs - 1; e >= 0; e--) if (GEQs[e].color) mbr=1; + for (e = nEQs - 1; e >= 0; e--) if (EQs[e].color) mbr=1; + if (nMemories) mbr = 1; + + assert(!mbr || mayBeRed); + + if (DBUG) { + fprintf(outputFile, "problem reduced, bringing variables back to life\n"); + if(mbr && !mayBeRed) fprintf(outputFile, "Red equations we don't expect\n"); + printProblem(); + } + if (DBUG && nEQs > 0) + fprintf(outputFile,"This is wierd: problem has equalities\n"); + + for (i = 1; i <= safeVars; i++) + if (var[i] < 0) { + /* wild card */ + if (i < safeVars) { + int j = safeVars; + std::swap(var[i], var[j]); + for (e = nGEQs - 1; e >= 0; e--) { + GEQs[e].touched = 1; + std::swap(GEQs[e].coef[i], GEQs[e].coef[j]); + } + for (e = nEQs - 1; e >= 0; e--) + std::swap(EQs[e].coef[i], EQs[e].coef[j]); + for (e = nSUBs - 1; e >= 0; e--) + std::swap(SUBs[e].coef[i], SUBs[e].coef[j]); + i--; + } + safeVars--; + } + + m = nSUBs; + n = nVars; + if (n < safeVars + m) + n = safeVars + m; + for (e = nGEQs - 1; e >= 0; e--) { + if (singleVarGEQ(&GEQs[e])) { + i = abs(GEQs[e].key); + if (i >= safeVars + 1) + GEQs[e].key += (GEQs[e].key > 0 ? m : -m); + } + else { + GEQs[e].touched = true; + GEQs[e].key = 0; + } + } + for (i = nVars; i >= safeVars + 1; i--) { + var[i + m] = var[i]; + for (e = nGEQs - 1; e >= 0; e--) + GEQs[e].coef[i + m] = GEQs[e].coef[i]; + for (e = nEQs - 1; e >= 0; e--) + EQs[e].coef[i + m] = EQs[e].coef[i]; + for (e = nSUBs - 1; e >= 0; e--) + SUBs[e].coef[i + m] = SUBs[e].coef[i]; + } + for (i = safeVars + m; i >= safeVars + 1; i--) { + for (e = nGEQs - 1; e >= 0; e--) GEQs[e].coef[i] = 0; + for (e = nEQs - 1; e >= 0; e--) EQs[e].coef[i] = 0; + for (e = nSUBs - 1; e >= 0; e--) SUBs[e].coef[i] = 0; + } + nVars += m; + safeVars += m; + for (e = nSUBs - 1; e >= 0; e--) + var[safeVars -m + 1 + e] = SUBs[e].key; + for (i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + if (DBUG) { + fprintf(outputFile,"Ready to wake substitutions\n"); + printProblem(); + } + for (e = nSUBs - 1; e >= 0; e--) { + int neweq = newEQ(); + eqnncpy(&(EQs[neweq]), &(SUBs[e]), nVars); + EQs[neweq].coef[safeVars -m + 1 + e] = -1; + EQs[neweq].color = EQ_BLACK; + if (DBUG) { + fprintf(outputFile, "brought back: "); + printEQ(&EQs[neweq]); + fprintf(outputFile, "\n"); + } + } + nSUBs = 0; + + if (DBUG) { + fprintf(outputFile, "variables brought back to life\n"); + printProblem(); + } + } + + coalesce(); + recallRedMemories(); + cleanoutWildcards(); +} + + +void Problem::reverseProtectedVariables() { + int v1,v2,e,i; + coef_t t; + for (v1 = 1; v1 <= safeVars; v1++) { + v2 = safeVars+1-v1; + if (v2>=v1) break; + for(e=0;e<nEQs;e++) { + t = EQs[e].coef[v1]; + EQs[e].coef[v1] = EQs[e].coef[v2]; + EQs[e].coef[v2] = t; + } + for(e=0;e<nGEQs;e++) { + t = GEQs[e].coef[v1]; + GEQs[e].coef[v1] = GEQs[e].coef[v2]; + GEQs[e].coef[v2] = t; + GEQs[e].touched = 1; + } + for(e=0;e<nSUBs;e++) { + t = SUBs[e].coef[v1]; + SUBs[e].coef[v1] = SUBs[e].coef[v2]; + SUBs[e].coef[v2] = t; + } + } + + for (i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + for (i = 0; i < nSUBs; i++) + forwardingAddress[SUBs[i].key] = -i - 1; +} + +void Problem::ordered_elimination(int symbolic) { + int i,j,e; + int isDead[maxmaxGEQs]; + for(e=0;e<nEQs;e++) isDead[e] = 0; + + if (!variablesInitialized) { + initializeVariables(); + } + + for(i=nVars;i>symbolic;i--) + for(e=0;e<nEQs;e++) + if (EQs[e].coef[i] == 1 || EQs[e].coef[i] == -1) { + for(j=nVars;j>i;j--) if (EQs[e].coef[j]) break; + if (i==j) { + doElimination(e, i); + isDead[e] = 1; + break; + } + } + + for(e=nEQs-1;e>=0;e--) + if (isDead[e]) { + nEQs--; + if (e < nEQs) eqnncpy(&EQs[e], &EQs[nEQs], nVars); + } + + for (i = 1; i <= safeVars; i++) + forwardingAddress[var[i]] = i; + for (i = 0; i < nSUBs; i++) + forwardingAddress[SUBs[i].key] = -i - 1; +} + + +coef_t Problem::checkSum() const { + coef_t cs; + int e; + cs = 0; + for(e=0;e<nGEQs;e++) { + coef_t c = GEQs[e].coef[0]; + cs += c*c*c; + } + return cs; +} + + +void Problem::coalesce() { + int e, e2, colors; + int isDead[maxmaxGEQs]; + int foundSomething = 0; + + + colors = 0; + for (e = 0; e < nGEQs; e++) + if (GEQs[e].color) + colors++; + if (colors < 2) + return; + for (e = 0; e < nGEQs; e++) + isDead[e] = 0; + for (e = 0; e < nGEQs; e++) + if (!GEQs[e].touched) + for (e2 = e + 1; e2 < nGEQs; e2++) + if (!GEQs[e2].touched && GEQs[e].key == -GEQs[e2].key + && GEQs[e].coef[0] == -GEQs[e2].coef[0]) { + int neweq = newEQ(); + eqnncpy(&EQs[neweq], &GEQs[e], nVars); + EQs[neweq].color = GEQs[e].color || GEQs[e2].color; + foundSomething++; + isDead[e] = 1; + isDead[e2] = 1; + } + for (e = nGEQs - 1; e >= 0; e--) + if (isDead[e]) { + deleteGEQ(e); + } + if (DEBUG && foundSomething) { + fprintf(outputFile, "Coalesced GEQs into %d EQ's:\n", foundSomething); + printProblem(); + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_beaut.cc b/omegalib/omega/src/pres_beaut.cc new file mode 100644 index 0000000..c23962a --- /dev/null +++ b/omegalib/omega/src/pres_beaut.cc @@ -0,0 +1,235 @@ +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +///////////////////////// +// // +// Beautify functions // +// // +///////////////////////// + + +namespace omega { + +// +// f & true = f +// f | false = f +// f1 & f2 & ... & fn = Conjunct(f1,f2,...,fn) +// + +void Rel_Body::beautify() { + assert(children().length()==1); + set_parent(NULL,this); + + skip_finalization_check++; + formula()->beautify(); + Formula *child = formula(); + if(child->node_type()==Op_And && child->children().empty()) { + remove_child(child); + delete child; + add_conjunct(); + } + skip_finalization_check--; + + if(pres_debug) { + fprintf(DebugFile, "\n=== Beautified TREE ===\n"); + prefix_print(DebugFile); + } + assert(children().length()==1); +} + +void Formula::beautify() { + // copy list of children, as they may be removed as we work + List<Formula*> kiddies = myChildren; + + for(List_Iterator<Formula*> c(kiddies); c; c++) + (*c)->beautify(); +} + +void F_Exists::beautify() { + Formula::beautify(); + assert(children().length()==1); + + if(myLocals.empty()) { + // exists( : ***) + parent().remove_child(this); + parent().add_child(children().remove_front()); + delete this; + } + else if (children()[1]->node_type() == Op_And && children()[1]->children().empty()) { + // exists(*** : TRUE) --chun 6/4/2008 + parent().remove_child(this); + parent().add_child(children().remove_front()); + delete this; + } + else { + Formula *child = children().front(); + if(child->node_type() == Op_Conjunct || child->node_type() == Op_Exists) { + child->push_exists(myLocals); + parent().remove_child(this); + parent().add_child(child); + children().remove_front(); + delete this; + } + } +} + +void F_Forall::beautify() { + Formula::beautify(); + assert(children().length()==1); + + if(myLocals.empty()) { + parent().remove_child(this); + parent().add_child(children().remove_front()); + delete this; + } +} + + +// +// The Pix-free versions of beautify for And and Or are a bit +// less efficient than the previous code, as we keep moving +// things from one list to another, but they do not depend on +// knowing that a Pix is valid after the list is updated, and +// they can always be optimized later if necessary. +// + +void F_Or::beautify() { + Formula::beautify(); + + List<Formula*> uglies, beauties; + uglies.join(children()); assert(children().empty()); +#if ! defined NDEBUG + foreach(c,Formula*,uglies,c->set_parent(0)); +#endif + + while(!uglies.empty()) { + Formula *f = uglies.remove_front(); + if(f->node_type()==Op_And && f->children().empty() ) { + // smth | true = true + foreach(c,Formula*,uglies,delete c); + foreach(c,Formula*,beauties,delete c); + parent().remove_child(this); + parent().add_and(); + delete f; + delete this; + return; + } + else if(f->node_type()==Op_Or) { + // OR(f[1-m], OR(c[1-n])) = OR(f[1-m], c[1-n]) +#if ! defined NDEBUG + foreach(c,Formula*,f->children(),c->set_parent(0)); +#endif + uglies.join(f->children()); + delete f; + } + else + beauties.prepend(f); + } + + if(beauties.length()==1) { + beauties.front()->set_parent(&parent()); + parent().remove_child(this); + parent().add_child(beauties.remove_front()); + delete this; + } + else { + foreach(c,Formula*,beauties,(c->set_parent(this), + c->set_relation(relation()))); + assert(children().empty()); + children().join(beauties); + } +} + +void F_And::beautify() { + Formula::beautify(); + + Conjunct *conj = NULL; + + List<Formula*> uglies, beauties; + uglies.join(children()); assert(children().empty()); +#if ! defined NDEBUG + foreach(c,Formula*,uglies,c->set_parent(0)); +#endif + + while(!uglies.empty()) { + Formula *f = uglies.remove_front(); + if (f->node_type() == Op_Conjunct) { + if(conj==NULL) + conj = f->really_conjunct(); + else { + Conjunct *conj1 = merge_conjs(conj, f->really_conjunct(), MERGE_REGULAR); + delete f; + delete conj; + conj = conj1; + } + } + else if(f->node_type()==Op_Or && f->children().empty()) { + // smth & false = false + foreach(c,Formula*,uglies,delete c); + foreach(c,Formula*,beauties,delete c); + parent().remove_child(this); + parent().add_or(); + delete f; + delete conj; + delete this; + return; + } + else if(f->node_type()==Op_And) { + // AND(f[1-m], AND(c[1-n])) = AND(f[1-m], c[1-n]) +#if ! defined NDEBUG + foreach(c,Formula*,f->children(),c->set_parent(0)); +#endif + uglies.join(f->children()); + delete f; + } + else + beauties.prepend(f); + } + + if(conj!=NULL) + beauties.prepend(conj); + + if(beauties.length()==1) { + beauties.front()->set_parent(&parent()); + parent().remove_child(this); + parent().add_child(beauties.remove_front()); + delete this; + } + else { + foreach(c,Formula*,beauties,(c->set_parent(this), + c->set_relation(relation()))); + assert(children().empty()); + children().join(beauties); + } +} + +void F_Not::beautify() { + Formula::beautify(); + assert(children().length()==1); + Formula *child = children().front(); + + if(child->node_type()==Op_And && child->children().empty()) { + // Not TRUE = FALSE + parent().remove_child(this); + parent().add_or(); + delete this; + } + else if (child->node_type()==Op_Or && child->children().empty()) { + // Not FALSE = TRUE + parent().remove_child(this); + parent().add_and(); + delete this; + } +} + +void Conjunct::beautify() { + if(is_true()) { + parent().remove_child(this); + parent().add_and(); + delete this; + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_cnstr.cc b/omegalib/omega/src/pres_cnstr.cc new file mode 100644 index 0000000..a8ebd15 --- /dev/null +++ b/omegalib/omega/src/pres_cnstr.cc @@ -0,0 +1,450 @@ +#include <omega/pres_cnstr.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +Constraint_Handle::Constraint_Handle(Conjunct *_c, eqn **_eqns, int _e): + c(_c), eqns(_eqns), e(_e) { +} + +GEQ_Handle::GEQ_Handle(Conjunct *_c, int _e): + Constraint_Handle(_c,&(_c->problem->GEQs),_e) { +} + +bool Constraint_Handle::is_const(Variable_ID v) { + bool is_const=true; + for(Constr_Vars_Iter cvi(*this, false); cvi && is_const; cvi++) + is_const = ((*cvi).coef == 0 || ((*cvi).var == v && (*cvi).coef !=0)); + return is_const; +} + +bool Constraint_Handle::is_const_except_for_global(Variable_ID v){ + bool is_const=true; + for(Constr_Vars_Iter cvi(*this, false); cvi && is_const; cvi++) + if((*cvi).var->kind() != Global_Var) + is_const = ((*cvi).coef == 0 || ((*cvi).var == v && (*cvi).coef !=0)); + return is_const; +} + +bool EQ_Handle::operator==(const Constraint_Handle &that) { + Constraint_Handle &e1=*this; + const Constraint_Handle &e2=that; + 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(); +} + +bool GEQ_Handle::operator==(const Constraint_Handle &that) { + Constraint_Handle &e1=*this; + const Constraint_Handle &e2=that; + for(Constr_Vars_Iter cvi(e1, false); cvi; cvi++) { + coef_t c1 = (*cvi).coef; + coef_t c2 = e2.get_coef((*cvi).var); + if (c1 != c2) return false; + } + { + for(Constr_Vars_Iter cvi(e2, false); cvi; cvi++) { + coef_t c1 = e1.get_coef((*cvi).var); + coef_t c2 = (*cvi).coef; + if (c1 != c2) return false; + } + } + return e1.get_const() == e2.get_const(); +} + + + + +void GEQ_Handle::negate() { + assert(! this->relation()->is_simplified()); + int i; + for(i=1; i<=c->problem->nVars; i++) { + (*eqns)[e].coef[i] = -(*eqns)[e].coef[i]; + } + (*eqns)[e].coef[0] = -(*eqns)[e].coef[0]-1; +} + +bool Constraint_Handle::has_wildcards() const { + Constr_Vars_Iter C(*this, true); + if (C.live()) { + assert(C.curr_var()->kind() == Wildcard_Var); + assert(C.curr_coef() != 0); + return 1; + } + return 0; +} + +int Constraint_Handle::max_tuple_pos() const { + int m = 0; + for( Constr_Vars_Iter C(*this, false); C.live() ; C.next()) { + switch (C.curr_var()->kind()) { + case Input_Var: + case Output_Var: { + int pos = C.curr_var()->get_position(); + if (m < pos) m = pos; + break; + } + default: + ; + } + } + return m; +} + +int Constraint_Handle::min_tuple_pos() const { + int m = 0; + for( Constr_Vars_Iter C(*this, false); C.live() ; C.next()) { + switch (C.curr_var()->kind()) { + case Input_Var: + case Output_Var: { + int pos = C.curr_var()->get_position(); + if (m == 0 || m > pos) m = pos; + break; + } + default: + ; + } + } + return m; +} + + +EQ_Handle::EQ_Handle(Conjunct *_c, int _e): Constraint_Handle(_c,&(_c->problem->EQs),_e) { +} + +// +// Update functions. +// +void Constraint_Handle::update_coef(Variable_ID D, coef_t I) { + assert(! this->relation()->is_simplified()); + assert(D != 0); + // The next two assertions are somewhat high-cost. +#if !defined(NDEBUG) + // skip_set_checks++; + assert((D->kind() != Input_Var || D->get_position() <= this->relation()->n_inp())); + assert((D->kind() != Output_Var || D->get_position() <= this->relation()->n_out())); + // skip_set_checks--; +#endif + int col = c->get_column(D); + (*eqns)[e].coef[col] += I; +} + +void Constraint_Handle::update_const(coef_t I) { + assert(! this->relation()->is_simplified()); + (*eqns)[e].coef[0] += I; +} + + +// update a coefficient of a variable that already exists in mappedvars + +void Constraint_Handle::update_coef_during_simplify(Variable_ID D, coef_t I) { + assert(D != 0); + int col = c->get_column(D); + (*eqns)[e].coef[col] += I; +} + +void Constraint_Handle::update_const_during_simplify(coef_t I) { + (*eqns)[e].coef[0] += I; +} + +// +// Get functions. +// + +coef_t Constraint_Handle::get_coef(Variable_ID v) const { + assert(this->relation()->is_simplified()); + assert(v != 0); + int col = c->find_column(v); + if(col == 0) { + return 0; + } + else { + return (*eqns)[e].coef[col]; + } +} + +coef_t Constraint_Handle::get_coef_during_simplify(Variable_ID v) const { + assert(v != 0); + int col = c->find_column(v); + if(col == 0) { + return 0; + } + else { + return (*eqns)[e].coef[col]; + } +} + +coef_t Constraint_Handle::get_const() const { + assert(this->relation()->is_simplified()); + return((*eqns)[e].coef[0]); +} + +coef_t Constraint_Handle::get_const_during_simplify() const { + return((*eqns)[e].coef[0]); +} + +Variable_ID Constraint_Handle::get_local(const Global_Var_ID G) { + return relation()->get_local(G); +} + +Variable_ID Constraint_Handle::get_local(const Global_Var_ID G, Argument_Tuple of) { + return relation()->get_local(G, of); +} + +void Constraint_Handle::finalize() { + c->n_open_constraints--; +} + +void Constraint_Handle::multiply(int multiplier) { + int i; + assert(! this->relation()->is_simplified()); + for(i=1; i<=c->problem->nVars; i++) { + (*eqns)[e].coef[i] = (*eqns)[e].coef[i] * multiplier; + } + (*eqns)[e].coef[0] = (*eqns)[e].coef[0] * multiplier; +} + +Rel_Body *Constraint_Handle::relation() const { + return c->relation(); +} + + +// +// Variables of constraint iterator. +// +Constr_Vars_Iter::Constr_Vars_Iter(const Constraint_Handle &ch, bool _wild_only): eqns(ch.eqns), e(ch.e), prob(ch.c->problem), vars(ch.c->mappedVars), wild_only(_wild_only) { + assert(vars.size() == prob->nVars); + for(current=1; current<=prob->nVars; current++) { + if((*eqns)[e].coef[current]!=0 && + (!wild_only || vars[current]->kind()==Wildcard_Var)) + return; + } +} + +int Constr_Vars_Iter::live() const { + return (current<=prob->nVars); +} + + +void Constr_Vars_Iter::operator++() { this->operator++(0); } + +void Constr_Vars_Iter::operator++(int) { + for(current++ ; current <=prob->nVars; current++) + if((*eqns)[e].coef[current]!=0 && + (!wild_only || vars[current]->kind()==Wildcard_Var)) + return; +} + + +Variable_ID Constr_Vars_Iter::curr_var() const { + assert(current <= prob->nVars); + return vars[current]; +} + +coef_t Constr_Vars_Iter::curr_coef() const { + assert(current <= prob->nVars); + return (*eqns)[e].coef[current]; +} + +Variable_Info Constr_Vars_Iter::operator*() const { + assert(current <= prob->nVars); + return Variable_Info(vars[current],(*eqns)[e].coef[current]); +} + +// +// Constraint iterator. +// +Constraint_Iterator Conjunct::constraints() { + return Constraint_Iterator(this); +} + +Constraint_Iterator::Constraint_Iterator(Conjunct *_c): c(_c), current(0), + last(c->problem->nGEQs-1), eqns(&(c->problem->GEQs)) { + if(!this->live()) (*this)++; // switch to EQ's if no GEQs +} + +int Constraint_Iterator::live() const { + return current <=last; +} + +void Constraint_Iterator::operator++() { + this->operator++(0); +} + +void Constraint_Iterator::operator++(int) { + if(++current > last) + if(eqns == &(c->problem->GEQs)) { // Switch to EQs + eqns = &(c->problem->EQs); + current = 0; + last = c->problem->nEQs-1; + } +} + +Constraint_Handle Constraint_Iterator::operator*() { + assert((c && eqns && current <= last) && "Constraint_Iterator::operator*: bad call"); + return Constraint_Handle(c,eqns,current); +} + +Constraint_Handle Constraint_Iterator::operator*() const { + assert((c && eqns && current <= last) && "Constraint_Iterator::operator*: bad call"); + return Constraint_Handle(c,eqns,current); +} + + +// +// EQ iterator. +// +EQ_Iterator Conjunct::EQs() { + return EQ_Iterator(this); +} + +EQ_Iterator::EQ_Iterator(Conjunct *_c) : c(_c) { + last = c->problem->nEQs-1; + current = 0; +} + +int EQ_Iterator::live() const { + return current <= last; +} + +void EQ_Iterator::operator++() { + this->operator++(0); +} + +void EQ_Iterator::operator++(int) { + current++; +} + +EQ_Handle EQ_Iterator::operator*() { + assert((c && current <= last) && "EQ_Iterator::operator*: bad call"); + return EQ_Handle(c,current); +} + +EQ_Handle EQ_Iterator::operator*() const { + assert((c && current <= last) && "EQ_Iterator::operator*: bad call"); + return EQ_Handle(c,current); +} + + +// +// GEQ iterator. +// +GEQ_Iterator Conjunct::GEQs() { + return GEQ_Iterator(this); +} + +GEQ_Iterator::GEQ_Iterator(Conjunct *_c) : c(_c) { + last = c->problem->nGEQs-1; + current = 0; +} + +int GEQ_Iterator::live() const { + return current <= last; +} + +void GEQ_Iterator::operator++() { + this->operator++(0); +} + +void GEQ_Iterator::operator++(int) { + current++; +} + + +GEQ_Handle GEQ_Iterator::operator*() { + assert((c && current <= last) && "GEQ_Iterator::operator*: bad call"); + return GEQ_Handle(c,current); +} + +GEQ_Handle GEQ_Iterator::operator*() const { + assert((c && current <= last) && "GEQ_Iterator::operator*: bad call"); + return GEQ_Handle(c,current); +} + + +void copy_constraint(Constraint_Handle H, const Constraint_Handle initial) { + // skip_set_checks++; +// assert(H.relation()->n_inp() == initial.relation()->n_inp()); +// assert(H.relation()->n_out() == initial.relation()->n_out()); + + H.update_const_during_simplify(initial.get_const_during_simplify()); + if (H.relation() == initial.relation()) { + for( Constr_Vars_Iter C(initial, false); C.live() ; C.next()) { + assert(C.curr_var()->kind()!= Forall_Var && + C.curr_var()->kind()!= Exists_Var); + if (C.curr_var()->kind()!= Wildcard_Var) + H.update_coef_during_simplify(C.curr_var(), C.curr_coef()); + else + // Must add a new wildcard, + // since they can't be used outside local Conjunct + H.update_coef_during_simplify(H.c->declare(), C.curr_coef()); + } + } + else { + Rel_Body *this_rel = H.relation(); + for( Constr_Vars_Iter C(initial, false); C.live() ; C.next()) { + switch (C.curr_var()->kind()) { + case Forall_Var: + case Exists_Var: + assert(0 && "Can't copy quantified constraints across relations"); + break; + case Wildcard_Var: + // for each wildcard var we see, create a new wildcard + // will lead to lots of wildcards, but Wayne likes it + // that way + { + H.update_coef_during_simplify(H.c->declare(), C.curr_coef()); + break; + } + case Input_Var: //use variable_ID of corresponding position + { + int pos = C.curr_var()->get_position(); + assert(this_rel->n_inp() >= pos); + Variable_ID V = this_rel->input_var(pos); + H.update_coef_during_simplify(V, C.curr_coef()); + break; + } + case Output_Var: //use variable_ID of corresponding position + { + int pos = C.curr_var()->get_position(); + assert(this_rel->n_out() >= pos); + Variable_ID V = this_rel->output_var(pos); + H.update_coef_during_simplify(V, C.curr_coef()); + break; + } + + case Global_Var: // get this Global's Var_ID in this relation + { + Variable_ID V; + Global_Var_ID G = C.curr_var()->get_global_var(); + if (G->arity() == 0) + V = this_rel->get_local(G); + else + V = this_rel->get_local(G,C.curr_var()->function_of()); + H.update_coef_during_simplify(V, C.curr_coef()); + break; + } + default: + assert(0 && "copy_constraint: variable of impossible type"); + } + } + } + // skip_set_checks--; +} + +} // namespace diff --git a/omegalib/omega/src/pres_col.cc b/omegalib/omega/src/pres_col.cc new file mode 100644 index 0000000..1569116 --- /dev/null +++ b/omegalib/omega/src/pres_col.cc @@ -0,0 +1,104 @@ +#include <omega/pres_conj.h> +#include <omega/RelBody.h> +#include <omega/omega_i.h> + +namespace omega { + +// +// Copy column fr_col of problem fp +// to column to_col of problem tp. +// Displacement for constraints in tp are start_EQ and start_GEQ. +// +void copy_column(Problem *tp, int to_col, + Problem *fp, int fr_col, + int start_EQ, int start_GEQ) { + checkVars(to_col); + assert(start_EQ + fp->nEQs <= tp->allocEQs); + assert(start_GEQ + fp->nGEQs <= tp->allocGEQs); + + int i; + for(i=0; i<fp->nEQs; i++) { + tp->EQs[i+start_EQ].coef[to_col] = fp->EQs[i].coef[fr_col]; + } + for(i=0; i<fp->nGEQs; i++) { + tp->GEQs[i+start_GEQ].coef[to_col] = fp->GEQs[i].coef[fr_col]; + } +} + +// +// Zero column to_col of problem tp. +// Displacement for constraints in to_conj are start_EQ and start_GEQ. +// Number of constraints to zero are no_EQ and no_GEQ. +// +void zero_column(Problem *tp, int to_col, + int start_EQ, int start_GEQ, + int no_EQs, int no_GEQs) { + assert(start_EQ + no_EQs <= tp->allocEQs); + assert(start_GEQ + no_GEQs <= tp->allocGEQs); + + int i; + for(i=0; i<no_EQs; i++) { + tp->EQs[i+start_EQ].coef[to_col] = 0; + } + for(i=0; i<no_GEQs; i++) { + tp->GEQs[i+start_GEQ].coef[to_col] = 0; + } +} + +// +// return column for D in conjunct +// +int Conjunct::get_column(Variable_ID D) { + int col = find_column(D); + if (col == 0) // if it does not already have a column assigned + col = map_to_column(D); + assert(col > 0); // Not substituted + return col; +} + +// +// Find column in conjunct. +// +int Conjunct::find_column(Variable_ID D) { + assert(D != 0); + int column = mappedVars.index(D); + + // If it has been through the omega core (variablesInitialized), + // and it exists in the problem, check to see if it has been forwarded. + // This will likely only be the case if substitutions have been done; + // that won't arise in user code, only in print_with_subs and the + // Substitutions class. + if (problem->variablesInitialized && column > 0) { + assert(problem->forwardingAddress[column] != 0); + column = problem->forwardingAddress[column]; + } + assert (column <= problem->nVars); + return column; +} + +// +// Create new column in conjunct. +// +int Conjunct::map_to_column(Variable_ID D) { + assert(D != 0); + // This heavy-duty assertion says that if you are trying to use a global + // var's local representative in a relation, that you have first told the + // relation that you are using it here. PUFS requires that we know + // all the function symbols that might be used in a relation. + // If one wanted to be less strict, one could just tell the relation + // that the global variable was being used. + assert(D->kind() != Global_Var || + (relation()->has_local(D->get_global_var(), D->function_of()) + && "Attempt to update global var without a local variable ID")); + + cols_ordered = false; // extremely important + checkVars(problem->nVars+2); + int col = ++problem->nVars; + mappedVars.append(D); + problem->forwardingAddress[col] = col; + problem->var[col] = col; + zero_column(problem, col, 0, 0, problem->nEQs, problem->nGEQs); + return col; +} + +} // namespace diff --git a/omegalib/omega/src/pres_conj.cc b/omegalib/omega/src/pres_conj.cc new file mode 100644 index 0000000..f3f458d --- /dev/null +++ b/omegalib/omega/src/pres_conj.cc @@ -0,0 +1,1460 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + class Conjunct. + + Notes: + + History: +*****************************************************************************/ + +#include <omega/omega_core/oc.h> +#include <omega/pres_conj.h> +#include <omega/pres_cmpr.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> +#include <set> + +namespace omega { + +int NR_CONJUNCTS, MAX_CONJUNCTS; + +/* + * Make a new wildcard variable, return WC number. + * Should be static to this file, but must be a friend of conjunct. + */ +int new_WC(Conjunct *nc, Problem *) { + Variable_ID wc = nc->declare(); + int wc_no = nc->map_to_column(wc); + return(wc_no); +} + + +const char* get_var_name(unsigned int col, void * void_conj) { +#if defined PRINT_COLUMN_NUMBERS + static char scum[512]; +#endif + Conjunct *c = (Conjunct *) void_conj; + if (col == 0) + return 0; + Variable_ID v = c->mappedVars[col]; + assert(v!=0); +#if defined PRINT_COLUMN_NUMBERS + strcpy(scum, v->char_name()); + sprintf(scum + strlen(scum), "(%d)", col); + return scum; +#endif + return v->char_name(); +} + +void Conjunct::promise_that_ub_solutions_exist(Relation &R) { +#ifndef NDEBUG + Relation verify=Relation(R, this); + assert(verify.is_upper_bound_satisfiable()); +#endif + verified = true; +} + + +int Conjunct::max_ufs_arity_of_set() { + int ma = 0, a; + for (Variable_ID_Iterator v(mappedVars); v; v++) + if ((*v)->kind() == Global_Var && (*v)->function_of() == Set_Tuple + && query_variable_used(*v)) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + + +int Conjunct::max_ufs_arity_of_in() { + int ma = 0, a; + for (Variable_ID_Iterator v(mappedVars); v; v++) + if ((*v)->kind() == Global_Var && (*v)->function_of() == Input_Tuple + && query_variable_used(*v)) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + + +int Conjunct::max_ufs_arity_of_out() { + int ma = 0, a; + for (Variable_ID_Iterator v(mappedVars); v; v++) + if ((*v)->kind() == Global_Var && (*v)->function_of() == Output_Tuple + && query_variable_used(*v)) { + a = (*v)->get_global_var()->arity(); + if (a > ma) + ma = a; + } + return ma; +} + + +bool Conjunct::is_unknown() const { + assert(problem || comp_problem); + return !exact && ((problem && problem->nEQs==0 && problem->nGEQs==0) || + (comp_problem && comp_problem->no_constraints())); +} + + +/* + * Remove black constraints from the problem. + * Make all the remaining red constraints black. + */ +void Conjunct::rm_color_constrs() { + int geqs = 0, eqs = 0; + + possible_leading_0s = -1; + guaranteed_leading_0s = -1; + leading_dir = 0; + + for(int i=0; i<problem->nGEQs; i++) { + if(problem->GEQs[i].color) { + if(geqs!=i) + eqnncpy(&problem->GEQs[geqs], &problem->GEQs[i], problem->nVars); + problem->GEQs[geqs].color = EQ_BLACK; + geqs++; + } + } + problem->nGEQs = geqs; + + for(int i=0; i<problem->nEQs; i++) { + if(problem->EQs[i].color) { + if(eqs!=i) + eqnncpy(&problem->EQs[eqs], &problem->EQs[i], problem->nVars); + problem->EQs[eqs].color = EQ_BLACK; + eqs++; + } + } + problem->nEQs = eqs; +} + + + +// +// Conjunct constructors. +// +Conjunct::Conjunct() : + F_Declaration(NULL, NULL), + mappedVars(0), + n_open_constraints(0), + cols_ordered(false), + simplified(false), + verified(false), + guaranteed_leading_0s(-1), + possible_leading_0s(-1), + leading_dir(0), + exact(true), + r_constrs(0) { + NR_CONJUNCTS++; + if (NR_CONJUNCTS>MAX_CONJUNCTS) MAX_CONJUNCTS = NR_CONJUNCTS; + problem = new Problem; + comp_problem = NULL; + problem->get_var_name = get_var_name; + problem->getVarNameArgs = (void *) this; +} + + +Conjunct::Conjunct(Formula *f, Rel_Body *r) : + F_Declaration(f,r), + mappedVars(0), + n_open_constraints(0), + cols_ordered(false), + simplified(false), + verified(false), + guaranteed_leading_0s(-1), + possible_leading_0s(-1), + leading_dir(0), + exact(true), + r_constrs(0) { + NR_CONJUNCTS++; + if (NR_CONJUNCTS>MAX_CONJUNCTS) MAX_CONJUNCTS = NR_CONJUNCTS; + problem = new Problem; + comp_problem = NULL; + problem->get_var_name = get_var_name; + problem->getVarNameArgs = (void *) this ; +} + +void internal_copy_conjunct(Conjunct* to, Conjunct* fr); + +// +// Copy Conjunct. +// +Conjunct* Conjunct::copy_conj_diff_relation(Formula *parent, Rel_Body *rel_body) { + Conjunct *new_conj; + if(problem && comp_problem==NULL) { + new_conj = new Conjunct(parent, rel_body); + internal_copy_conjunct(new_conj, this); + } + else if (problem==NULL && comp_problem) { + /* copy compressed conjunct */ + assert(0 && "copy compressed conjunct"); + new_conj = 0; + } + else { + assert(0 && "problem == NULL"); + new_conj = 0; + } + return new_conj; +} + + +void internal_copy_conjunct(Conjunct* to, Conjunct* fr) { + copy_conj_header(to, fr); + + // + // We repeat part of what is done by copy_conj_header(to, fr) by + // calling Problem::operator=(const Problem &). + // copy_conj_header should go away, but there is some code that still needs + // it in negate_conj. + // + to->r_constrs = fr->r_constrs; + to->simplified = fr->simplified; + to->verified = fr->verified; + to->guaranteed_leading_0s = fr->guaranteed_leading_0s; + to->possible_leading_0s = fr->possible_leading_0s; + to->leading_dir = fr->leading_dir; + // the following duplicates some work of the "copy_conj_header" brain damage + *to->problem = *fr->problem; + to->problem->getVarNameArgs = (void *)to; // important +} + + +// +// Copy Conjunct variable declarations +// and problem parameters but not problem itself. +// +void copy_conj_header(Conjunct* to, Conjunct* fr) { + free_var_decls(to->myLocals); to->myLocals.clear(); + + copy_var_decls(to->myLocals, fr->myLocals); + to->mappedVars = fr->mappedVars; + to->remap(); + reset_remap_field(fr->myLocals); + + to->cols_ordered = fr->cols_ordered; + to->r_constrs = fr->r_constrs; + to->simplified = fr->simplified; + to->verified = fr->verified; + to->guaranteed_leading_0s = fr->guaranteed_leading_0s; + to->possible_leading_0s = fr->possible_leading_0s; + to->leading_dir = fr->leading_dir; + to->n_open_constraints = fr->n_open_constraints; + to->exact=fr->exact; + + Problem *fp = fr->problem; + Problem *tp = to->problem; + tp->nVars = fp->nVars; + tp->safeVars = fp->safeVars; + tp->variablesInitialized = fp->variablesInitialized; + tp->variablesFreed = fp->variablesFreed; + for(int i=1; i<=maxVars; i++) { // only need nVars of var + tp->forwardingAddress[i] = fp->forwardingAddress[i]; + tp->var[i] = fp->var[i]; + } + to->problem->get_var_name = get_var_name; + to->problem->getVarNameArgs = (void *)to ; +} + + +void Conjunct::reverse_leading_dir_info() { + leading_dir *= -1; +} + + +void Conjunct::enforce_leading_info(int guaranteed, int possible, int dir) { + skip_finalization_check++; + guaranteed_leading_0s = guaranteed; + + int d = min(relation()->n_inp(),relation()->n_out()); + + assert(0 <= guaranteed); + assert(guaranteed <= possible); + assert(possible <= d); + + for(int i = 1; i <= guaranteed; i++) { + EQ_Handle e = add_EQ(); + e.update_coef_during_simplify(input_var(i), -1); + e.update_coef_during_simplify(output_var(i), 1); + e.finalize(); + } + + + if (guaranteed == possible && guaranteed >= 0 && possible+1 <= d && dir) { + GEQ_Handle g = add_GEQ(); + if (dir > 0) { + g.update_coef_during_simplify(input_var(possible+1), -1); + g.update_coef_during_simplify(output_var(possible+1), 1); + } + else { + g.update_coef_during_simplify(input_var(possible+1), 1); + g.update_coef_during_simplify(output_var(possible+1), -1); + } + g.update_const_during_simplify(-1); + g.finalize(); + possible_leading_0s = possible; + leading_dir = dir; + } + else { + possible_leading_0s = d; + leading_dir = 0; + } + + skip_finalization_check--; +#if ! defined NDEBUG + assert_leading_info(); +#endif +} + + +void Conjunct::invalidate_leading_info(int changed) { + if (changed == -1) { + guaranteed_leading_0s = possible_leading_0s = -1; + leading_dir = 0; + } + else { + int d = min(relation()->n_inp(), relation()->n_out()); + assert(1 <= changed && changed <= d); + if (possible_leading_0s == changed -1) { + possible_leading_0s = d; + } + guaranteed_leading_0s = min(guaranteed_leading_0s,changed-1); + } +#if ! defined NDEBUG + assert_leading_info(); +#endif +} + + +int Conjunct::leading_dir_valid_and_known() { + if (relation()->is_set()) { + return 0; + } + // if we know leading dir, we can rule out extra possible 0's + assert(leading_dir == 0 || + possible_leading_0s == guaranteed_leading_0s); + + return (possible_leading_0s == guaranteed_leading_0s && + possible_leading_0s >= 0 && + possible_leading_0s < min(relation()->n_inp(),relation()->n_out()) + && leading_dir); +} + + +#if ! defined NDEBUG +void Conjunct::assert_leading_info() { + if (relation()->is_set()) { + return; + } + + int d = min(relation()->n_inp(), relation()->n_out()); + + if ( guaranteed_leading_0s == -1 + && guaranteed_leading_0s == possible_leading_0s) + assert(leading_dir == 0); + + if(leading_dir != 0 && + possible_leading_0s != guaranteed_leading_0s) { + use_ugly_names++; + prefix_print(DebugFile); + use_ugly_names--; + } + + assert(leading_dir == 0 || possible_leading_0s == guaranteed_leading_0s); + + assert(possible_leading_0s <= d && guaranteed_leading_0s <= d); + + assert(possible_leading_0s == -1 || guaranteed_leading_0s <= possible_leading_0s); + + // check that there must be "guaranteed_leading_0s" 0s + int carried_level; + for (carried_level = 1; carried_level <= guaranteed_leading_0s; carried_level++) { + Variable_ID in = input_var(carried_level), + out = output_var(carried_level); + coef_t lb, ub; + bool guar; + query_difference(out, in, lb, ub, guar); + if (lb != 0 && ub != 0) { + // probably "query_difference" is just approximate + // add the negation of leading_dir and assert that + // the result is unsatisfiable; + // add in > out (in-out-1>=0) and assert unsatisfiable. + + Conjunct *test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + skip_finalization_check++; + + GEQ_Handle g = test->add_GEQ(); + g.update_coef_during_simplify(in, -1); + g.update_coef_during_simplify(out, 1); + g.update_const_during_simplify(-1); + g.finalize(); + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + g = test->add_GEQ(); + g.update_coef_during_simplify(in, 1); + g.update_coef_during_simplify(out, -1); + g.update_const_during_simplify(-1); + g.finalize(); + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + skip_finalization_check--; + } + } + + carried_level = possible_leading_0s+1; + + // check that there can't be another + if (guaranteed_leading_0s == possible_leading_0s + && possible_leading_0s >= 0 && + carried_level <= min(relation()->n_inp(), relation()->n_out())) { + Variable_ID in = input_var(carried_level), + out = output_var(carried_level); + coef_t lb, ub; + bool guar; + query_difference(out, in, lb, ub, guar); + if (lb <= 0 && ub >= 0) { + // probably "query_difference" is just approximate + // add a 0 and see if its satisfiable + + Conjunct *test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + skip_finalization_check++; + + EQ_Handle e = test->add_EQ(); + e.update_coef_during_simplify(in, -1); + e.update_coef_during_simplify(out, 1); + e.finalize(); + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + skip_finalization_check--; + } + } + + // check leading direction info + if (leading_dir_valid_and_known()) { + Variable_ID in = input_var(guaranteed_leading_0s+1), + out = output_var(guaranteed_leading_0s+1); + coef_t lb, ub; + bool guar; + query_difference(out, in, lb, ub, guar); + if ((leading_dir < 0 && ub >= 0) || + (leading_dir > 0 && lb <= 0)) { + // probably "query_difference" is just approximate + // add the negation of leading_dir and assert that + // the result is unsatisfiable; + // eg for leading_dir = +1 (in must be < out), + // add in >= out (in-out>=0) and assert unsatisfiable. + + Conjunct *test = copy_conj_same_relation(); + test->problem->turnRedBlack(); + skip_finalization_check++; + + GEQ_Handle g = test->add_GEQ(); + g.update_coef_during_simplify(in, leading_dir); + g.update_coef_during_simplify(out, -leading_dir); + g.finalize(); + + assert(!simplify_conj(test, true, 0, 0)); + // test was deleted by simplify_conj, as it was FALSE + + skip_finalization_check--; + } + } +} +#endif + + +Variable_ID Conjunct::declare(Const_String s) { + return do_declare(s, Wildcard_Var); +} + +Variable_ID Conjunct::declare() { + return do_declare(Const_String(), Wildcard_Var); +} + +Variable_ID Conjunct::declare(Variable_ID v) { + return do_declare(v->base_name, Wildcard_Var); +} + +Conjunct* Conjunct::really_conjunct() { + return this; +} + + +Variable_ID_Tuple* Conjunct::variables() { + return &mappedVars; +} + +Stride_Handle Conjunct::add_stride(int step, int preserves_level) { + assert_not_finalized(); + Variable_ID wild = declare(); + int c; + c = problem->newEQ(); + simplified = false; + verified = false; + if (! preserves_level) { + if (leading_dir == 0) + possible_leading_0s = -1; + // otherwise we must still have leading_dir, and thus no more 0's + } + problem->EQs[c].color = EQ_BLACK; + eqnnzero(&problem->EQs[c],problem->nVars); + n_open_constraints++; + EQ_Handle h = EQ_Handle(this, c); + h.update_coef(wild,step); + return h; +} + +// This should only be used to copy constraints from simplified relations, +// i.e. there are no quantified variables in c except wildcards. +EQ_Handle Conjunct::add_EQ(const Constraint_Handle &c, int /*preserves_level, currently unused*/) { + EQ_Handle e = add_EQ(); + copy_constraint(e,c); + return e; +} + + +EQ_Handle Conjunct::add_EQ(int preserves_level) { + assert_not_finalized(); + int c; + c = problem->newEQ(); + simplified = false; + verified = false; + if (!preserves_level) { + if (leading_dir == 0) + possible_leading_0s = -1; + // otherwise we must still have leading_dir, and thus no more 0's + } + problem->EQs[c].color = EQ_BLACK; + eqnnzero(&problem->EQs[c],problem->nVars); + n_open_constraints++; + return EQ_Handle(this, c); +} + + +// This should only be used to copy constraints from simplified relations, +// i.e. there are no quantified variables in c except wildcards. +GEQ_Handle Conjunct::add_GEQ(const Constraint_Handle &c, int /*preserves_level, currently unused */) { + GEQ_Handle g = add_GEQ(); + copy_constraint(g,c); + return g; +} + + +GEQ_Handle Conjunct::add_GEQ(int preserves_level) { + assert_not_finalized(); + int c; + c = problem->newGEQ(); + simplified = false; + verified = false; + if (!preserves_level) { + if (leading_dir == 0) + possible_leading_0s = -1; + // otherwise we must still have leading_dir, and thus no more 0's + } + problem->GEQs[c].color = EQ_BLACK; + eqnnzero(&problem->GEQs[c],problem->nVars); + n_open_constraints++; + return GEQ_Handle(this, c); +} + + +Conjunct *Conjunct::find_available_conjunct() { + return this; +} + + +bool Conjunct::can_add_child() { + return false; +} + + +void Conjunct::combine_columns() { + int nvars = mappedVars.size(),i,j,k; + + for(i=1; i<=nvars; i++) + for(j=i+1; j<=nvars; j++) { + // If they are the same, copy into the higher numbered column. + // That way we won't have problems with already-merged columns later + assert(i != j); + if(mappedVars[i] == mappedVars[j]) { + if (pres_debug) + fprintf(DebugFile, "combine_col:Actually combined %d,%d\n", + j,i); + for(k=0; k<problem->nEQs; k++) + problem->EQs[k].coef[j] += problem->EQs[k].coef[i]; + for(k=0; k<problem->nGEQs; k++) + problem->GEQs[k].coef[j] += problem->GEQs[k].coef[i]; + zero_column(problem, i, 0, 0, problem->nEQs, problem->nGEQs); + // Create a wildcard w/no constraints. temporary measure, + // so we don't have to shuffle columns + Variable_ID zero_var = declare(); + mappedVars[i] = zero_var; + break; + } + } +} + + +void Conjunct::finalize() { +// Debugging version of finalize; copy the conjunct and free the old one, +// so that purify will catch accesses to finalized constraints +// assert(n_open_constraints == 0); +// Conjunct *C = this->copy(); +// parent().replace_child(this, C); +// delete this; +} + +Conjunct::~Conjunct() { + NR_CONJUNCTS--; + delete problem; + delete comp_problem; +} + + +// +// Cost = # of terms in DNF when negated +// or CantBeNegated if too bad (i.e. bad wildcards) +// or AvoidNegating if it would be inexact +// +// Also check pres_legal_negations -- +// If set to any_negation, just return the number +// If set to one_geq_or_stride, return CantBeNegated if c > 1 +// If set to one_geq_or_eq, return CantBeNegated if not a single geq or eq +// + +int Conjunct::cost() { + int c; + int i; + int wc_no; + int wc_j = 0; // initialize to shut up the compiler + + // cost 1 per GEQ, and if 1 GEQ has wildcards, +2 for each of them + + c = problem->nGEQs; + for(i=0; i<problem->nGEQs; i++) { + wc_no = 0; + for(int j=1; j<=problem->nVars; j++) if(problem->GEQs[i].coef[j]!=0) { + Variable_ID v = mappedVars[j]; + if(v->kind()==Wildcard_Var) { + wc_no++; + c+=2; + wc_j = j; + } + } + if (wc_no > 1) return CantBeNegated; + } + + for(i=0; i<problem->nEQs; i++) { + wc_no = 0; + for(int j=1; j<=problem->nVars; j++) if(problem->EQs[i].coef[j]!=0) { + Variable_ID v = mappedVars[j]; + if(v->kind()==Wildcard_Var) { + wc_no++; + wc_j = j; + } + } + + if (wc_no == 0) // no wildcards + c+=2; + else if (wc_no == 1) { // one wildcard - maybe we can negate it + int i2; + for(i2=0; i2<problem->nEQs; i2++) + if(i != i2 && problem->EQs[i2].coef[wc_j]!=0) break; + if (i2 >= problem->nEQs) // Stride constraint + c++; + else // We are not ready to handle this + return CantBeNegated; + } + else // Multiple wildcards + return CantBeNegated; + } + if (!exact) return AvoidNegating; + + if (pres_legal_negations == any_negation) { + return c; + } + else { + // single GEQ ok either way as long as no wildcards + // (we might be able to handle wildcards, but I haven't thought about it) + if (problem->nEQs==0 && problem->nGEQs<=1) { + if (c>1) { // the GEQ had a wildcard -- I'm not ready to go here. + if (pres_debug > 0) { + fprintf(DebugFile, + "Refusing to negate a GEQ with wildcard(s)" + " under restricted_negation; " + "It may be possible to fix this.\n"); + } + return CantBeNegated; + } + return c; + } + else if (problem->nEQs==1 && problem->nGEQs==0) { + assert(c == 1 || c == 2); + + if (pres_legal_negations == one_geq_or_stride) { + if (c == 1) + return c; // stride constraint is ok + else { + if (pres_debug > 0) { + fprintf(DebugFile, "Refusing to negate a non-stride EQ under current pres_legal_negations.\n"); + } + return CantBeNegated; + } + } + else { + assert(pres_legal_negations == one_geq_or_eq); + return c; + } + } + else { + if (pres_debug > 0) { + fprintf(DebugFile, "Refusing to negate multiple constraints under current pres_legal_negations.\n"); + } + return CantBeNegated; + } + } +} + + +// +// Merge CONJ1 & CONJ2 -> CONJ. +// Action: MERGE_REGULAR or MERGE_COMPOSE: regular merge. +// MERGE_GIST make constraints from conj2 red, i.e. +// Gist Conj2 given Conj1 (T.S. comment). +// Reorder columns as we go. +// Merge the columns for identical variables. +// We assume we know nothing about the ordering of conj1, conj2. +// +// Does not consume its arguments +// +// Optional 4th argument gives the relation for the result - if +// null, conj1 and conj2 must have the same relation, which will +// be used for the result +// +// The only members of conj1 and conj2 that are used are: problem, +// mappedVars and declare(), and the leading_0s/leading_dir members +// and exact. +// +// NOTE: variables that are shared between conjuncts are necessarily +// declared above, not here; so we can simply create columns for the +// locals of each conj after doing the protected vars. +// +Conjunct* merge_conjs(Conjunct* conj1, Conjunct* conj2, + Merge_Action action, Rel_Body *body) { + // body must be set unless both conjuncts are from the same relation + assert(body || conj1->relation() == conj2->relation()); + + if (body == conj1->relation() && body == conj2->relation()) + body = 0; // we test this later to see if there is a new body + + Conjunct *conj3 = new Conjunct(NULL, body ? body : conj2->relation()); + Problem *p1 = conj1->problem; + Problem *p2 = conj2->problem; + Problem *p3 = conj3->problem; + int i; + + if (action != MERGE_COMPOSE) { + conj1->assert_leading_info(); + conj2->assert_leading_info(); + } + + if(pres_debug>=2) { + use_ugly_names++; + fprintf(DebugFile, ">>> Merge conjuncts: Merging%s:\n", + (action == MERGE_GIST ? " for gist" : + (action == MERGE_COMPOSE ? " for composition" : ""))); + conj1->prefix_print(DebugFile); + conj2->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + use_ugly_names--; + } + + + + switch(action) { + case MERGE_REGULAR: + case MERGE_COMPOSE: + conj3->exact=conj1->exact && conj2->exact; + break; + case MERGE_GIST: + conj3->exact=conj2->exact; + break; + } + + if (action == MERGE_COMPOSE) { + conj3->guaranteed_leading_0s=min(conj1->guaranteed_leading_0s, + conj2->guaranteed_leading_0s); + conj3->possible_leading_0s=min((unsigned int) conj1->possible_leading_0s, + (unsigned int) conj2->possible_leading_0s); + + assert( conj3->guaranteed_leading_0s <= conj3->possible_leading_0s); + + // investigate leading_dir - not well tested code + if (conj1->guaranteed_leading_0s<0 || conj2->guaranteed_leading_0s<0) { + conj3->leading_dir = 0; + } + else if (conj1->guaranteed_leading_0s == conj2->guaranteed_leading_0s) + if (conj1->leading_dir == conj2->leading_dir) + conj3->leading_dir = conj1->leading_dir; + else + conj3->leading_dir = 0; + else if (conj1->guaranteed_leading_0s < conj2->guaranteed_leading_0s) { + conj3->leading_dir = conj1->leading_dir; + } + else { // (conj1->guaranteed_leading_0s > conj2->guaranteed_leading_0s) + conj3->leading_dir = conj2->leading_dir; + } + + if (conj3->leading_dir == 0) + conj3->possible_leading_0s = min(conj3->relation()->n_inp(), + conj3->relation()->n_out()); + + assert(conj3->guaranteed_leading_0s <= conj3->possible_leading_0s); + assert(conj3->guaranteed_leading_0s == conj3->possible_leading_0s + || !conj3->leading_dir); + } + else if (!body) { // if body is set, who knows what leading 0's mean? + assert(action == MERGE_REGULAR || action == MERGE_GIST); + + int feasable = 1; + + int redAndBlackGuarLeadingZeros = max(conj1->guaranteed_leading_0s, + conj2->guaranteed_leading_0s); + if (action == MERGE_REGULAR) + conj3->guaranteed_leading_0s= redAndBlackGuarLeadingZeros; + else conj3->guaranteed_leading_0s=conj1->guaranteed_leading_0s; + + conj3->possible_leading_0s=min((unsigned)conj1->possible_leading_0s, + (unsigned)conj2->possible_leading_0s); + if (conj3->possible_leading_0s < redAndBlackGuarLeadingZeros) + feasable = 0; + else if (conj3->guaranteed_leading_0s == -1 + || conj3->possible_leading_0s > redAndBlackGuarLeadingZeros) + conj3->leading_dir = 0; + else { + if (conj1->guaranteed_leading_0s == conj2->guaranteed_leading_0s) + if (!conj1->leading_dir_valid_and_known()) + conj3->leading_dir = conj2->leading_dir; + else if (!conj2->leading_dir_valid_and_known()) + conj3->leading_dir = conj1->leading_dir; + else if (conj1->leading_dir * conj2->leading_dir > 0) + conj3->leading_dir = conj1->leading_dir; // 1,2 same dir + else + feasable = 0; // 1 and 2 go in opposite directions + else if (conj3->possible_leading_0s != conj3->guaranteed_leading_0s) + conj3->leading_dir = 0; + else if (conj1->guaranteed_leading_0s<conj2->guaranteed_leading_0s) { + assert(!conj1->leading_dir_valid_and_known()); + conj3->leading_dir = conj2->leading_dir; + } + else { + assert(!conj2->leading_dir_valid_and_known()); + conj3->leading_dir = conj1->leading_dir; + } + } + + if (!feasable) { + if(pres_debug>=2) + fprintf(DebugFile, ">>> Merge conjuncts: quick check proves FALSE.\n"); + + // return 0 = 1 + + int e = p3->newEQ(); + p3->EQs[e].color = EQ_BLACK; + p3->EQs[e].touched = 1; + p3->EQs[e].key = 0; + p3->EQs[e].coef[0] = 1; + + // Make sure these don't blow later assertions + conj3->possible_leading_0s = conj3->guaranteed_leading_0s = -1; + conj3->leading_dir = 0; + + return conj3; + } + } + else { // provided "body" argument but not composing, leading 0s meaningless + conj3->guaranteed_leading_0s = conj3->possible_leading_0s = -1; + conj3->leading_dir = 0; + } + + // initialize omega stuff + + for(i=0; i<p1->nGEQs+p2->nGEQs; i++) { + int e = p3->newGEQ(); + assert(e == i); + p3->GEQs[e].color = EQ_BLACK; + p3->GEQs[e].touched = 1; + p3->GEQs[e].key = 0; + } + for(i=0; i<p1->nEQs+p2->nEQs; i++) { + int e = p3->newEQ(); + assert(e == i); + p3->EQs[e].color = EQ_BLACK; + p3->EQs[e].touched = 1; + p3->EQs[e].key = 0; + } + + assert(p3->nGEQs == p1->nGEQs + p2->nGEQs); + assert(p3->nEQs == p1->nEQs + p2->nEQs); + + // flag constraints from second constraint as red, if necessary + if (action == MERGE_GIST) { + for(i=0; i<p2->nEQs; i++) { + p3->EQs[i+p1->nEQs].color = EQ_RED; + } + for(i=0; i<p2->nGEQs; i++) { + p3->GEQs[i+p1->nGEQs].color = EQ_RED; + } + } + + // copy constant column + copy_column(p3, 0, p1, 0, 0, 0); + copy_column(p3, 0, p2, 0, p1->nEQs, p1->nGEQs); + + // copy protected variables column from conj1 + int new_col = 1; + Variable_Iterator VI(conj1->mappedVars); + for(i=1; VI; VI++, i++) { + Variable_ID v = *VI; + if(v->kind() != Wildcard_Var) { + conj3->mappedVars.append(v); + int fr_ix = i; + copy_column(p3, new_col, p1, fr_ix, 0, 0); + zero_column(p3, new_col, p1->nEQs, p1->nGEQs, + p2->nEQs, p2->nGEQs); + new_col++; + } + } + + // copy protected variables column from conj2, + // checking if conj3 already has this variable from conj1 + for(i=1; i <= conj2->mappedVars.size(); i++) { + Variable_ID v = conj2->mappedVars[i]; + if(v->kind() != Wildcard_Var) { + int to_ix = conj3->mappedVars.index(v); + int fr_ix = i; + if(to_ix > 0) { + // use old column + copy_column(p3, to_ix, p2, fr_ix, p1->nEQs, p1->nGEQs); + } + else { + // create new column + conj3->mappedVars.append(v); + zero_column(p3, new_col, 0, 0, p1->nEQs, p1->nGEQs); + copy_column(p3, new_col, p2, fr_ix, p1->nEQs, p1->nGEQs); + new_col++; + } + } + } + + p3->safeVars = new_col-1; + + // copy wildcards from conj1 + for(i=1; i <= conj1->mappedVars.size(); i++) { + Variable_ID v = conj1->mappedVars[i]; + if(v->kind() == Wildcard_Var) { + Variable_ID nv = conj3->declare(v); + conj3->mappedVars.append(nv); + int fr_ix = i; + copy_column(p3, new_col, p1, fr_ix, 0, 0); + zero_column(p3, new_col, p1->nEQs, p1->nGEQs, + p2->nEQs, p2->nGEQs); + new_col++; + } + } + + // copy wildcards from conj2 + for(i=1; i <= conj2->mappedVars.size(); i++) { + Variable_ID v = conj2->mappedVars[i]; + if(v->kind() == Wildcard_Var) { + Variable_ID nv = conj3->declare(v); + conj3->mappedVars.append(nv); + int fr_ix = i; + zero_column(p3, new_col, 0, 0, p1->nEQs, p1->nGEQs); + copy_column(p3, new_col, p2, fr_ix, p1->nEQs, p1->nGEQs); + new_col++; + } + } + + p3->nVars = new_col-1; + checkVars(p3->nVars); + p3->variablesInitialized = 1; + for(i=1; i<=p3->nVars; i++) + p3->var[i] = p3->forwardingAddress[i] = i; + + conj3->cols_ordered = true; + conj3->simplified = false; + conj3->verified = false; + + if(pres_debug>=2) { + use_ugly_names++; + fprintf(DebugFile, ">>> Merge conjuncts: result is:\n"); + conj3->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + use_ugly_names--; + } + + conj3->assert_leading_info(); + + return conj3; +} + + + + +// +// Reorder variables by swapping. +// cols_ordered is just a hint that thorough check needs to be done. +// Sets _safeVars. +// +void Conjunct::reorder() { + if(!cols_ordered) { + int var_no = mappedVars.size(); + int first_wild = 1; + int last_prot = var_no; + while(first_wild < last_prot) { + for(; first_wild<=var_no && mappedVars[first_wild]->kind()!=Wildcard_Var; + first_wild++) ; + for(; last_prot>=1 && mappedVars[last_prot]->kind()==Wildcard_Var; + last_prot--) ; + if(first_wild < last_prot) { + problem->swapVars(first_wild, last_prot); + problem->variablesInitialized = false; + Var_Decl *t = mappedVars[first_wild]; + mappedVars[first_wild] = mappedVars[last_prot]; + mappedVars[last_prot] = t; + if(pres_debug) { + fprintf(DebugFile, "<<<OrderConjCols>>>: swapped var-s %d and %d\n", first_wild, last_prot); + } + } + } + + int safe_vars; + for(safe_vars=0; + safe_vars<var_no && mappedVars[safe_vars+1]->kind()!=Wildcard_Var; + safe_vars++) ; + +#if ! defined NDEBUG + for(int s = safe_vars ; s<var_no ; s++ ) { + assert(mappedVars[s+1]->kind() == Wildcard_Var); + } +#endif + + problem->safeVars = safe_vars; + cols_ordered = true; + } +} + + + +// Wherever possible, move function symbols to input tuple. +// This ensures that if in == out, red F(in) = x is redundant +// with black F(out) = x + +void Conjunct::move_UFS_to_input() { + if (guaranteed_leading_0s > 0) { + std::set<Global_Var_ID> already_done; + int remapped = 0; + skip_finalization_check++; + Rel_Body *body = relation(); + + assert(body); + + for (Variable_ID_Iterator func(*body->global_decls()); func; func++) { + Global_Var_ID f = (*func)->get_global_var(); + if (f->arity() <= guaranteed_leading_0s) + if (already_done.find(f) == already_done.end() && + body->has_local(f, Input_Tuple) && + body->has_local(f, Output_Tuple)) { + already_done.insert(f); + + // equatE f(in) = f(out) + Variable_ID f_in = body->get_local(f, Input_Tuple); + Variable_ID f_out = body->get_local(f, Output_Tuple); + if (f_in != f_out) { + EQ_Handle e = add_EQ(1); + + e.update_coef_during_simplify(f_in, -1); + e.update_coef_during_simplify(f_out, 1); + + f_out->remap = f_in; + remapped = 1; + } + } + } + + if (remapped) { + remap(); + combine_columns(); + reset_remap_field(*body->global_decls()); + remapped = 0; + } + + skip_finalization_check--; + } +} + + + + + +// +// Simplify CONJ. +// Return TRUE if there are solutions, FALSE -- no solutions. +// +int simplify_conj(Conjunct* conj, int ver_sim, int simplificationEffort, int color) { + if (conj->verified + && simplificationEffort <= conj->r_constrs + && (conj->simplified || simplificationEffort < 0) + && !color) { + if(pres_debug) { + fprintf(DebugFile, "$$$ Redundant simplify_conj ignored (%d,%d,%d)\n",ver_sim,simplificationEffort,color); + conj->prefix_print(DebugFile); + } + return 1; + } + + if (simplificationEffort < 0) simplificationEffort = 0; + conj->move_UFS_to_input(); + conj->reorder(); + + Problem *p = conj->problem; + + use_ugly_names++; + + int i; + for(i=0; i<p->nGEQs; i++) { + p->GEQs[i].touched = 1; + } + for(i=0; i<p->nEQs; i++) { + p->EQs[i].touched = 1; + } + + if(pres_debug) { + fprintf(DebugFile, "$$$ simplify_conj (%d,%d,%d)[\n",ver_sim,simplificationEffort,color); + conj->prefix_print(DebugFile); + } + + assert(conj->cols_ordered); + + int ret_code; + assert(p == conj->problem); + if(!color) { + ret_code = conj->simplifyProblem(ver_sim && ! conj->verified,0,simplificationEffort); + } + else { + ret_code = conj->redSimplifyProblem(simplificationEffort,1); + ret_code = (ret_code==redFalse ? 0 : 1); + } + assert(p->nSUBs==0); + + if(ret_code == 0) { + if(pres_debug) + fprintf(DebugFile, "] $$$ simplify_conj : false\n\n"); + delete conj; + use_ugly_names--; + return(false); + } + + + // + // mappedVars is mapping from columns to Variable_IDs. + // Recompute mappedVars for problem returned from ip.c + // + Variable_ID_Tuple new_mapped(0); // This is expanded by "append" + for (i=1; i<=p->safeVars; i++) { + // what is now in column i used to be in column p->var[i] + Variable_ID v = conj->mappedVars[p->var[i]]; + assert(v->kind() != Wildcard_Var); + new_mapped.append(v); + } + + /* Redeclare all wildcards that weren't eliminated. */ + free_var_decls(conj->myLocals); conj->myLocals.clear(); + + conj->mappedVars = new_mapped; + for (i = p->safeVars+1; i<=p->nVars; i++) { + Variable_ID v = conj->declare(); + conj->mappedVars.append(v); + } + + // reset var and forwarding address if desired. + p->variablesInitialized = 1; + for(i=1; i<=conj->problem->nVars; i++) + conj->problem->var[i] = conj->problem->forwardingAddress[i] = i; + + if(pres_debug) { + fprintf(DebugFile, "] $$$ simplify_conj\n"); + conj->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + } + + + use_ugly_names--; + conj->simplified = true; + conj->setup_anonymous_wildcard_names(); + + return(true); +} + + +int Conjunct::rank() { + Conjunct *C = this->copy_conj_same_relation(); + C->reorder(); + C->ordered_elimination(C->relation()->global_decls()->size()); + int C_rank = 0; + for(Variable_Iterator vi = C->mappedVars; vi; vi++) + if(C->find_column(*vi) > 0) C_rank++; + delete C; + return C_rank; + +} + + +void Conjunct::query_difference(Variable_ID v1, Variable_ID v2, coef_t &lowerBound, coef_t &upperBound, bool &guaranteed) { + int c1 = get_column(v1); + int c2 = get_column(v2); + assert(c1 && c2); + problem->query_difference(c1, c2, lowerBound, upperBound, guaranteed); +} + + +void Conjunct::query_variable_bounds(Variable_ID v, coef_t &lowerBound, coef_t &upperBound) { + int c = get_column(v); + assert (c); + problem->query_variable_bounds(c, &lowerBound, &upperBound); +} + +coef_t Conjunct::query_variable_mod(Variable_ID v, coef_t factor) { + int c = get_column(v); + assert(c); + return problem->query_variable_mod(c, factor); +} + +bool Conjunct::query_variable_used(Variable_ID v) { + for (GEQ_Iterator g = GEQs(); g.live(); g.next()) { + if ((*g).get_coef(v)) return true; + } + for (EQ_Iterator e = EQs(); e.live(); e.next()) { + if ((*e).get_coef(v)) return true; + } + return false; +} + + +int Conjunct::simplifyProblem(int verify, int subs, int redundantElimination) { + if (verified) verify = 0; + int result = problem->simplifyProblem(verify, subs, redundantElimination); + if (result == false && !exact) + exact=true; + assert(!(verified && verify && result == false)); + if (verify && result) verified = true; + else if (!result) verified = false; + return result; +} + + +// not as confident about this one as the previous: +int Conjunct::redSimplifyProblem(int effort, int computeGist) { + redCheck result = problem->redSimplifyProblem(effort, computeGist); + if (result == redFalse && !exact) + exact=true; + return result; +} + + +// +// Add given list of wildcards S to this Conjunct. +// Clears argument. (That's very important, otherwise those var_id's get freed) +// Push_exists takes responsibility for reusing or deleting Var_ID's; +// here we reuse them. Must also empty out the Tuple when finished (joins). +void Conjunct::push_exists(Variable_ID_Tuple &S) { + for(Tuple_Iterator<Variable_ID> VI(S); VI; VI++) { + (*VI)->var_kind = Wildcard_Var; + } + myLocals.join(S); // Sets S to be empty. + cols_ordered = false; + simplified = false; +} + + +Conjunct *Formula::add_conjunct() { + assert_not_finalized(); + assert(can_add_child()); + Conjunct *f = new Conjunct(this, myRelation); + myChildren.append(f); + return f; +} + +// Compress/uncompress functions + +bool Conjunct::is_compressed() { + if(problem!=NULL && comp_problem==NULL) { + return false; + } + else if(problem==NULL && comp_problem!=NULL) { + return true; + } + else { + assert(0 && "Conjunct::is_compressed: bad conjunct"); + return false; + } +} + + +void Conjunct::compress() { + if(!is_compressed()) { // compress + comp_problem = new Comp_Problem(problem); + delete problem; + problem = NULL; + } +} + + +void Conjunct::uncompress() { + if(is_compressed()) { + problem = comp_problem->UncompressProblem(); + delete comp_problem; + comp_problem = NULL; + } +} + + +Comp_Problem::Comp_Problem(Problem *problem) : + _nVars(problem->nVars), + _safeVars(problem->safeVars), + _get_var_name(problem->get_var_name), + _getVarNameArgs(problem->getVarNameArgs), + eqs(&problem->EQs[0],problem->nEQs,problem->nVars), + geqs(&problem->GEQs[0],problem->nGEQs,problem->nVars) { +} + +Comp_Constraints::Comp_Constraints(eqn *constrs, int no_constrs, int no_vars) : + n_constrs(no_constrs), + n_vars(no_vars) { + coefs = new coef_t[(n_vars+1)*n_constrs]; + int e, v; + for(e=0; e<n_constrs; e++) { + for(v=0; v<=n_vars; v++) { + coefs[coef_index(e,v)] = constrs[e].coef[v]; + } + } +} + + +Comp_Constraints::~Comp_Constraints() { + delete coefs; +} + + +Problem *Comp_Problem::UncompressProblem() { + Problem *p = new Problem(eqs.n_constraints(), geqs.n_constraints()); + p->get_var_name = get_var_name; + p->getVarNameArgs = _getVarNameArgs; + p->nVars = _nVars; + p->safeVars = _safeVars; + for(int i=1; i<=p->nVars; i++) { + p->forwardingAddress[i] = i; + p->var[i] = i; + } + eqs.UncompressConstr(&p->EQs[0], p->nEQs); + geqs.UncompressConstr(&p->GEQs[0], p->nGEQs); + return p; +} + +void Comp_Constraints::UncompressConstr(eqn *constrs, short &pn_constrs) { + int e, v; + for(e=0; e<n_constrs; e++) { + eqnnzero(&constrs[e], 0); + for(v=0; v<=n_vars; v++) { + constrs[e].coef[v] = coefs[coef_index(e,v)]; + } + constrs[e].touched = 1; + } + pn_constrs = n_constrs; +} + + +void Conjunct::convertEQstoGEQs(bool excludeStrides) { + simplify_conj(this,true,1,EQ_BLACK); // don't remember why I want to comment this statement out with reason "will cause inconsistency between Conjunct::mappedVars and Problem::nVars", 06/09/2009 by chun + problem->convertEQstoGEQs(excludeStrides); +} + + +void Conjunct::calculate_dimensions(Relation &R, int &ndim_all, int &ndim_domain) { + + Conjunct * c = this; + Relation rc=Relation(R, c); + + if(relation_debug) { + fprintf(DebugFile,"{{{\nIn Conjunct::calculate_dimensions:\n"); + rc.prefix_print(DebugFile); + } + + rc=Approximate(rc); + Relation rd=rc; + + if(relation_debug) { + fprintf(DebugFile,"Conjunct::calculate_dimensions: Approximated as:\n"); + rc.prefix_print(DebugFile); + } + + // skip_set_checks++; + + Conjunct * rc_conj=rc.single_conjunct(); + ndim_all=rc.n_inp()+rc.n_out(); + ndim_all-=rc_conj->n_EQs(); + + rc = Project_On_Sym(rc); + rc.simplify(); + + if(relation_debug) { + fprintf(DebugFile, "Conjunct::calculate_dimensions: after project_on_sym\n"); + rc.prefix_print(DebugFile); + } + + int n_eq_sym = 1000; + for (DNF_Iterator s(rc.query_DNF()); s.live(); s.next()) + n_eq_sym = min(n_eq_sym, s.curr()->n_EQs()); + ndim_all+=n_eq_sym; + // skip_set_checks--; + + if (R.is_set()) + ndim_domain = ndim_all; + else { + /* get dimensions for the domain (broadcasting) */ + + rd=Domain(rd); + rd.simplify(); + + if(relation_debug) { + fprintf(DebugFile,"Domain is:\n"); + rd.prefix_print(DebugFile); + } + + rc_conj=rd.single_conjunct(); + ndim_domain=rd.n_set()-rc_conj->n_EQs()+n_eq_sym; + } + + if(relation_debug) { + fprintf(DebugFile,"n_eq_sym=%d \n",n_eq_sym); + fprintf(DebugFile,"Dimensions: all=%d domain=%d\n}}}\n", ndim_all,ndim_domain); + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_decl.cc b/omegalib/omega/src/pres_decl.cc new file mode 100644 index 0000000..f5ac312 --- /dev/null +++ b/omegalib/omega/src/pres_decl.cc @@ -0,0 +1,71 @@ +#include <omega/pres_decl.h> +#include <omega/omega_i.h> + +namespace omega { + +// +// Declare functions. +// +Variable_ID F_Declaration::do_declare(Const_String s, Var_Kind var_type) { + Variable_ID v; + assert(var_type != Global_Var); + if(!s.null()) { + v = new Var_Decl(s, var_type, 0); + } + else { + v = new Var_Decl(var_type, 0); + } + myLocals.append(v); + return v; +} + +Variable_ID F_Declaration::declare(Const_String) { + assert(0); // must be declared in forall, exists, or conjunct + return(NULL); +} + +Section<Variable_ID> F_Declaration::declare_tuple(int n) { + int first = myLocals.size()+1; + + for (int i=1 ; i<=n; i++) + declare(); + + return Section<Variable_ID>(&myLocals, first, n); +} + + +void F_Declaration::finalize() { + assert(n_children() == 1); + Formula::finalize(); +} + +bool F_Declaration::can_add_child() { + return n_children() < 1; +} + + +F_Declaration::F_Declaration(Formula *p, Rel_Body *r): + Formula(p,r), myLocals(0) { +} + +F_Declaration::F_Declaration(Formula *p, Rel_Body *r, Variable_ID_Tuple &S): + Formula(p,r), myLocals(S) { +} + +// +// Destruct declarative node. +// Delete variableID's themselves if they are not global. +// +F_Declaration::~F_Declaration() { + free_var_decls(myLocals); +} + +//Setup names for printing +void F_Declaration::setup_anonymous_wildcard_names() { + for(Tuple_Iterator<Variable_ID> VI(myLocals); VI; VI++) { + Variable_ID v = *VI; + if (v->base_name.null()) v->instance = wildCardInstanceNumber++; + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_dnf.cc b/omegalib/omega/src/pres_dnf.cc new file mode 100644 index 0000000..c9fd7e6 --- /dev/null +++ b/omegalib/omega/src/pres_dnf.cc @@ -0,0 +1,1416 @@ +/***************************************************************************** + Copyright (C) 1994-2000 the Omega Project Team + Copyright (C) 2005-2011 Chun Chen + All Rights Reserved. + + Purpose: + Functions for disjunctive normal form. + + Notes: + + History: +*****************************************************************************/ + +#include <basic/Bag.h> +#include <omega/pres_dnf.h> +#include <omega/pres_conj.h> +#include <omega/pres_tree.h> /* all DNFize functions are here */ +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +void DNF::remap() { + for(DNF_Iterator DI(this); DI.live(); DI.next()) { + Conjunct *C = DI.curr(); + C->remap(); + } +} + + +// +// DNF1 & DNF2 -> DNF. +// Free arguments. +// +DNF* DNF_and_DNF(DNF* dnf1, DNF* dnf2) { + DNF* new_dnf = new DNF; + for(DNF_Iterator p(dnf2); p.live(); p.next()) { + new_dnf->join_DNF(DNF_and_conj(dnf1, p.curr())); + } + delete dnf1; + delete dnf2; + if(new_dnf->length() > 1) { + new_dnf->simplify(); + } + + if(pres_debug) { + fprintf(DebugFile, "+++ DNF_and_DNF OUT +++\n"); + new_dnf->prefix_print(DebugFile); + } + return(new_dnf); +} + + +/* + * Remove redundant conjuncts from given DNF. + * If (C1 => C2), remove C1. + * C1 => C2 is TRUE: when problem where C1 is Black and C2 is Red + * Blk Red : has no red constraints. + * It means that C1 is a subset of C2 and therefore C1 is redundant. + * + * Exception: C1 => UNKNOWN - leave them as they are + */ +void DNF::rm_redundant_conjs(int effort) { + if(is_definitely_false() || has_single_conjunct()) + return; + + use_ugly_names++; + // skip_set_checks++; + + int count = 0; + for(DNF_Iterator p(this); p.live(); p.next()) count++; + + if(pres_debug) { + int i = 0; + fprintf(DebugFile, "@@@ rm_redundant_conjs IN @@@[\n"); + prefix_print(DebugFile); + for(DNF_Iterator p(this); p.live(); p.next()) + fprintf(DebugFile, "#%d = %p\n", ++i, p.curr()); + } + + DNF_Iterator pdnext; + DNF_Iterator pdel(this); + for(; pdel.live(); pdel=pdnext) { + pdnext = pdel; + pdnext.next(); + Conjunct *cdel = pdel.curr(); + int del_min_leading_zeros = cdel->query_guaranteed_leading_0s(); + int del_max_leading_zeros = cdel->query_possible_leading_0s(); + + for(DNF_Iterator p(this); p.live(); p.next()) { + Conjunct *c = p.curr(); + if(c != cdel) { + int c_min_leading_zeros = cdel->query_guaranteed_leading_0s(); + int c_max_leading_zeros = cdel->query_possible_leading_0s(); + if(pres_debug) + fprintf(DebugFile, "@@@ rm_redundant_conjs @%p => @%p[\n", cdel, c); + + if (c->is_inexact() && cdel->is_exact()) { + if (pres_debug) + fprintf(DebugFile, "]@@@ rm_redundant_conjs @@@ Exact Conj => Inexact Conj is not tested\n"); + } + else if (del_min_leading_zeros >=0 && c_min_leading_zeros >= 0 + && c_max_leading_zeros >= 0 && del_max_leading_zeros >=0 + && (del_min_leading_zeros > c_max_leading_zeros + || c_min_leading_zeros > del_max_leading_zeros)) { + if (1 || pres_debug) + fprintf(DebugFile, "]@@@ not redundant due to leading zero info\n"); + } + else { + Conjunct *cgist = merge_conjs(cdel, c, MERGE_GIST); + + if (!cgist->redSimplifyProblem(effort,0)) { + if(pres_debug) { + fprintf(DebugFile, "]@@@ rm_redundant_conjs @@@ IMPLICATION TRUE @%p\n", cdel); + cdel->prefix_print (DebugFile); + fprintf(DebugFile, "=>\n"); + c->prefix_print (DebugFile); + } + rm_conjunct(cdel); + delete cdel; + delete cgist; + break; + } + else { + if(pres_debug) { + fprintf(DebugFile, "]@@@ rm_redundant_conjs @@@ IMPLICATION FALSE @%p\n", cdel); + if(pres_debug > 1) + cgist->prefix_print(DebugFile); + } + delete cgist; + } + } + } + } + } + + if(pres_debug) { + fprintf(DebugFile, "]@@@ rm_redundant_conjs OUT @@@\n"); + prefix_print(DebugFile); + } + // skip_set_checks--; + use_ugly_names--; +} + + +/* Remove inexact conjuncts from given DNF if it contains UNKNOWN + * conjunct + */ + +void DNF::rm_redundant_inexact_conjs() { + if (is_definitely_false() || has_single_conjunct()) + return; + + bool has_unknown=false; + bool has_inexact=false; + + Conjunct * c_unknown = 0; // make compiler shut up + for (DNF_Iterator p(this); p.live(); p.next()) { + assert (p.curr()->problem!=NULL); + if (p.curr()->is_inexact()) { + if (p.curr()->is_unknown()) { + has_unknown=true; + c_unknown = p.curr(); + } + else + has_inexact=true; + } + } + + if (! has_unknown || ! has_inexact) + return; + + use_ugly_names++; + // skip_set_checks++; + + DNF_Iterator pdnext; + DNF_Iterator pdel(this); + + for (; pdel.live(); pdel=pdnext) { + pdnext = pdel; + pdnext.next(); + Conjunct * cdel=pdel.curr(); + if (cdel->is_inexact() && cdel!=c_unknown) { + rm_conjunct(cdel); + delete cdel; + } + } + + use_ugly_names--; + // skip_set_checks--; +} + + + +// +// DNF properties. +// +bool DNF::is_definitely_false() const { + return(conjList.empty()); +} + +bool DNF::is_definitely_true() const { + return(has_single_conjunct() && single_conjunct()->is_true()); +} + +int DNF::length() const { + return conjList.length(); +} + +Conjunct *DNF::single_conjunct() const { + assert(conjList.length()==1); + return(conjList.front()); +} + +bool DNF::has_single_conjunct() const { + return (conjList.length()==1); +} + +Conjunct *DNF::rm_first_conjunct() { + if(conjList.empty()) { + return NULL; + } + else { + return conjList.remove_front(); + } +} + + +// +// Convert DNF to Formula and add it root. +// Free this DNF. +// +void DNF::DNF_to_formula(Formula* root) { + Formula *new_or; + if (conjList.length()!=1) { + skip_finalization_check++; + new_or = root->add_or(); + skip_finalization_check--; + } + else { + new_or = root; + } + while(!conjList.empty()) { + Conjunct *conj = conjList.remove_front(); + new_or->add_child(conj); + } + delete this; +} + + +// +// DNF functions. +// +DNF::DNF() : conjList() { +} + +DNF::~DNF() { + // for(DNF_Iterator p(this); p.live(); p.next()) { + // if(p.curr() != NULL) + // delete p.curr(); + // } + for(List_Iterator<Conjunct *> i(conjList); i.live(); i.next()) + delete *i; +} + +// +// Copy DNF +// +DNF* DNF::copy(Rel_Body *rel_body) { + DNF *new_dnf = new DNF; + for(DNF_Iterator pd(this); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + if(conj) + new_dnf->add_conjunct(conj->copy_conj_diff_relation(rel_body,rel_body)); + } + return(new_dnf); +} + +// +// Add Conjunct to DNF +// +void DNF::add_conjunct(Conjunct* conj) { + conjList.append(conj); +} + +// +// Add DNF to DNF. +// The second DNF is reused. +// +void DNF::join_DNF(DNF* dnf) { + conjList.join(dnf->conjList); + delete dnf; +} + +// +// Remove conjunct from DNF. +// Conjunct itself is not deleted. +// +void DNF::rm_conjunct(Conjunct *c) { + if(conjList.front() == c) { + conjList.remove_front(); + } + else { + List_Iterator<Conjunct*> p, pp; + for(p=List_Iterator<Conjunct*> (conjList); p; p++) { + if((*p)==c) { + conjList.del_after(pp); + return; + } + pp = p; + } + assert(0 && "DNF::rm_conjunct: no such conjunct"); + } +} + + +// remove (but don't delete) all conjuncts + +void DNF::clear() { + conjList.clear(); +} + + +// +// DNF & CONJ -> new DNF. +// Don't touch arguments. +// +DNF* DNF_and_conj(DNF* dnf, Conjunct* conj) { + DNF* new_dnf = new DNF; + for(DNF_Iterator p(dnf); p.live(); p.next()) { + Conjunct* new_conj = merge_conjs(p.curr(), conj, MERGE_REGULAR); + new_dnf->add_conjunct(new_conj); + } + if(new_dnf->length() > 1) { + new_dnf->simplify(); + } + return(new_dnf); +} + +// +// Compute C0 and not (C1 or C2 or ... CN). +// Reuse/delete its arguments. +// +DNF* conj_and_not_dnf(Conjunct *positive_conjunct, DNF *neg_conjs, bool weak) { + DNF *ret_dnf = new DNF; + int recursive = 0; + use_ugly_names++; + + if(pres_debug) { + fprintf(DebugFile, "conj_and_not_dnf [\n"); + fprintf(DebugFile, "positive_conjunct:\n"); + positive_conjunct->prefix_print(DebugFile); + fprintf(DebugFile, "neg_conjs:\n"); + neg_conjs->prefix_print(DebugFile); + fprintf(DebugFile, "\n\n"); + } + + if (simplify_conj(positive_conjunct, true, false, EQ_BLACK) == false) { + positive_conjunct = NULL; + goto ReturnDNF; + } + + /* Compute gists of negative conjuncts given positive conjunct */ + + + int c0_updated; + c0_updated = true; + while(c0_updated) { + c0_updated = false; + for(DNF_Iterator p(neg_conjs); p.live(); p.next()) { + Conjunct *neg_conj = p.curr(); + if(neg_conj==NULL) continue; + if (!positive_conjunct->is_exact() + && !neg_conj->is_exact()) { + // C1 and unknown & ~(C2 and unknown) = C1 and unknown + delete neg_conj; + p.curr_set(NULL); + continue; + } + Conjunct *cgist = merge_conjs(positive_conjunct, neg_conj, MERGE_GIST); + if(simplify_conj(cgist, false, true, EQ_RED) == false) { + // C1 & ~FALSE = C1 + delete neg_conj; + p.curr_set(NULL); + } + else { + cgist->rm_color_constrs(); + if(cgist->is_true()) { + // C1 & ~TRUE = FALSE + delete cgist; + goto ReturnDNF; + } + else { + if(cgist->cost()==1) { // single inequality + DNF *neg_dnf = negate_conj(cgist); + delete cgist; + Conjunct *conj = + merge_conjs(positive_conjunct, neg_dnf->single_conjunct(), MERGE_REGULAR); + delete positive_conjunct; + delete neg_dnf; + positive_conjunct = conj; + delete neg_conj; + p.curr_set(NULL); + if(!simplify_conj(positive_conjunct, false, false, EQ_BLACK)) { + positive_conjunct = NULL; + goto ReturnDNF; + } + c0_updated = true; + } + else { + delete neg_conj; + p.curr_set(cgist); + } + } + } + } + } + + if(pres_debug) { + fprintf(DebugFile, "--- conj_and_not_dnf positive_conjunct NEW:\n"); + positive_conjunct->prefix_print(DebugFile); + fprintf(DebugFile, "--- conj_and_not_dnf neg_conjs GISTS:\n"); + neg_conjs->prefix_print(DebugFile); + fprintf(DebugFile, "--- conj_and_not_dnf ---\n\n"); + } + + /* Find minimal negative conjunct */ + { + Conjunct *min_conj = NULL; + int min_cost = INT_MAX; + DNF_Iterator min_p; + int live_count = 0; + for(DNF_Iterator q(neg_conjs); q.live(); q.next()) { + Conjunct *neg_conj = q.curr(); + if(neg_conj!=NULL) { + live_count++; + if(neg_conj->cost() < min_cost) { + min_conj = neg_conj; + min_cost = neg_conj->cost(); + min_p = q; + } + } + } + + /* Negate minimal conjunct, AND result with positive conjunct */ + if(weak || min_conj==NULL) { + ret_dnf->add_conjunct(positive_conjunct); + positive_conjunct = NULL; + } + else if (min_cost == CantBeNegated) { + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + if (OMEGA_WHINGE) { + fprintf(stderr, "Ignoring negative clause that can't be negated and generating inexact result\n"); + if (!pres_debug) fprintf(DebugFile, "Ignoring negative clause that can't be negated and generating inexact result\n"); + } + + positive_conjunct->make_inexact(); + ret_dnf->add_conjunct(positive_conjunct); + positive_conjunct = NULL; + if(pres_debug) + fprintf(DebugFile, "Ignoring negative clause that can't be negated and generating inexact upper bound\n"); + } + else { + DNF *neg_dnf = negate_conj(min_conj); + delete min_conj; + min_p.curr_set(NULL); + DNF *new_pos = DNF_and_conj(neg_dnf, positive_conjunct); + delete neg_dnf; + delete positive_conjunct; + positive_conjunct = NULL; + // new_dnf->rm_redundant_conjs(2); + if(live_count>1) { + recursive = 1; + for(DNF_Iterator pd(new_pos); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + ret_dnf->join_DNF(conj_and_not_dnf(conj, neg_conjs->copy(conj->relation()))); + pd.curr_set(NULL); + } + delete new_pos; + } + else { + ret_dnf->join_DNF(new_pos); + } + } + } + +ReturnDNF:; + delete positive_conjunct; + delete neg_conjs; + + //if (recursive) ret_dnf->rm_redundant_conjs(1); + + if(pres_debug) { + fprintf(DebugFile, "] conj_and_not_dnf RETURN:\n"); + ret_dnf->prefix_print(DebugFile); + fprintf(DebugFile, "\n\n"); + } + use_ugly_names--; + return ret_dnf; +} + +/* first some functions for manipulating oc "problems" */ + +static void EqnnZero(eqn *e, int s) { +// memset((char*)e, 0, (headerWords+1+s)*sizeof(int)); + e->key = 0; + e->touched = 0; + e->color = EQ_BLACK; + e->essential = 0; + e->varCount = 0; + for (int i = 0; i <= s; i++) + e->coef[i] = 0; +} + +/* + * Make a new black equation in a given problem + */ +static int NewEquation(Problem *p) { + int e = p->newEQ(); + EqnnZero(&p->EQs[e], p->nVars); + return e; +} + +/* + * Make a new black inequality in a given problem + */ +static int NewInequality(Problem *p) { + int g = p->newGEQ(); + EqnnZero(&p->GEQs[g], p->nVars); + return g; +} + +// +// ~CONJ -> DNF +// +DNF* negate_conj(Conjunct* conj) { + if(pres_debug) { + fprintf(DebugFile, "%%%%%% negate_conj IN %%%%%%\n"); + conj->prefix_print(DebugFile); + fprintf(DebugFile, "\n"); + } + + DNF* new_dnf = new DNF; + Problem *p = conj->problem; + int i, j,k; + + if (!conj->is_exact()) new_dnf->add_conjunct(conj->copy_conj_same_relation()); + + Conjunct* true_part = new Conjunct(NULL, conj->relation()); + Problem *tp = true_part->problem; + copy_conj_header(true_part, conj); + true_part->invalidate_leading_info(); + int *wildCard = new int[p->nGEQs]; + int *handleIt = new int[p->nVars+1]; + for(j=1; j<=p->nVars; j++) handleIt[j] = false; + + for(i=0; i<p->nGEQs; i++) { + wildCard[i] = 0; + for(j=1; j<=p->nVars; j++) { + Variable_ID v = conj->mappedVars[j]; + if(v->kind()==Wildcard_Var && p->GEQs[i].coef[j]!=0) { + assert(wildCard[i] == 0); + handleIt[j] = true; + if (p->GEQs[i].coef[j] > 0) wildCard[i] = j; + else wildCard[i] = -j; + } + } + } + + for(i=0; i<p->nGEQs; i++) if (wildCard[i] == 0) { + /* ~(ax + by + c >= 0) = (-ax -by -c-1 >= 0) */ + Conjunct* new_conj = true_part->copy_conj_same_relation(); + Problem *np = new_conj->problem; + new_conj->exact=true; + int n_e = NewInequality(np); + int t_e = NewInequality(tp); + np->GEQs[n_e].coef[0] = -p->GEQs[i].coef[0]-1; + tp->GEQs[t_e].coef[0] = p->GEQs[i].coef[0]; + for(j=1; j<=p->nVars; j++) { + Variable_ID v = conj->mappedVars[j]; + if(v->kind()==Wildcard_Var && p->GEQs[i].coef[j]!=0) { + assert(0 && "negate_conj: wildcard in inequality"); + } + np->GEQs[n_e].coef[j] = -p->GEQs[i].coef[j]; + tp->GEQs[t_e].coef[j] = p->GEQs[i].coef[j]; + + } + assert(j-1 == p->nVars); + assert(j-1 == conj->mappedVars.size()); + new_dnf->add_conjunct(new_conj); + } + + + for(i=0; i<p->nEQs; i++) { + int wc_no = 0; + int wc_j = 0; // make complier shut up + for(j=1; j<=p->nVars; j++) { + Variable_ID v = conj->mappedVars[j]; + if(v->kind()==Wildcard_Var && p->EQs[i].coef[j]!=0) { + wc_no++; + wc_j = j; + } + } + + if(wc_no!=0) { +#if ! defined NDEBUG + int i2; + assert(!handleIt[wc_j]); + for(i2=0; i2<p->nEQs; i2++) + if(i != i2 && p->EQs[i2].coef[wc_j] != 0) break; + assert(i2 >= p->nEQs); +#endif + assert(wc_no == 1 && "negate_conj: more than 1 wildcard in equality"); + + // === Negating equality with a wildcard for K>0 === + // ~(exists v st expr + K v + C = 0) = + // (exists v st 1 <= - expr - K v - C <= K-1) + + Conjunct *nc = true_part->copy_conj_same_relation(); + Problem *np = nc->problem; + nc->exact=true; + + // -K alpha = expr <==> K alpha = expr + if(p->EQs[i].coef[wc_j]<0) + p->EQs[i].coef[wc_j] = -p->EQs[i].coef[wc_j]; + + if(p->EQs[i].coef[wc_j]==2) { + // ~(exists v st expr +2v +C = 0) = + // (exists v st -expr -2v -C = 1) + // That is (expr +2v +C+1 = 0) + int e = NewEquation(np); + np->EQs[e].coef[0] = p->EQs[i].coef[0] +1; + for(j=1; j<=p->nVars; j++) { + np->EQs[e].coef[j] = p->EQs[i].coef[j]; + } + } + else { + // -expr -Kv -C-1 >= 0 + int e = NewInequality(np); + np->GEQs[e].coef[0] = -p->EQs[i].coef[0] -1; + for(j=1; j<=p->nVars; j++) { + np->GEQs[e].coef[j] = -p->EQs[i].coef[j]; + } + + // +expr +Kv +C+K-1 >= 0 + e = NewInequality(np); + np->GEQs[e].coef[0] = p->EQs[i].coef[0] +p->EQs[i].coef[wc_j] -1; + for(j=1; j<=p->nVars; j++) { + np->GEQs[e].coef[j] = p->EQs[i].coef[j]; + } + } + + new_dnf->add_conjunct(nc); + + } + else { + /* ~(ax + by + c = 0) = (-ax -by -c-1 >= 0) Or (ax + by + c -1 >= 0) */ + Conjunct *nc1 = true_part->copy_conj_same_relation(); + Conjunct *nc2 = true_part->copy_conj_same_relation(); + Problem* np1 = nc1->problem; + Problem* np2 = nc2->problem; + nc1->invalidate_leading_info(); + nc2->invalidate_leading_info(); + nc1->exact=true; + nc2->exact=true; + int n_e1 = NewInequality(np1); + int n_e2 = NewInequality(np2); + np1->GEQs[n_e1].coef[0] = -p->EQs[i].coef[0]-1; + np2->GEQs[n_e2].coef[0] = p->EQs[i].coef[0]-1; + for(j=1; j<=p->nVars; j++) { + coef_t coef = p->EQs[i].coef[j]; + np1->GEQs[n_e1].coef[j] = -coef; + np2->GEQs[n_e2].coef[j] = coef; + } + new_dnf->add_conjunct(nc1); + new_dnf->add_conjunct(nc2); + } + { + int e = NewEquation(tp); + tp->EQs[e].coef[0] = p->EQs[i].coef[0]; + for(j=1; j<=p->nVars; j++) + tp->EQs[e].coef[j] = p->EQs[i].coef[j]; + } + } + + for(j=1; j<=p->nVars; j++) + if (handleIt[j]) { + for(i=0; i<p->nGEQs; i++) + if (wildCard[i] == j) + for(k=0; k<p->nGEQs; k++) if (wildCard[k] == -j){ + // E_i <= c_i alpha + // c_k alpha <= E_k + // c_k E_i <= c_i c_k alpha <= c_i E_k + // c_k E_i <= c_i c_k floor (c_i E_k / c_i c_k) + // negating: + // c_k E_i > c_i c_k floor (c_i E_k / c_i c_k) + // c_k E_i > c_i c_k beta > c_i E_k - c_i c_k + // c_k E_i - 1 >= c_i c_k beta >= c_i E_k - c_i c_k + 1 + Conjunct* new_conj = true_part->copy_conj_same_relation(); + Problem *np = new_conj->problem; + coef_t c_k = - p->GEQs[k].coef[j]; + coef_t c_i = p->GEQs[i].coef[j]; + assert(c_k > 0); + assert(c_i > 0); + new_conj->exact=true; + int n_e = NewInequality(np); + // c_k E_i - 1 >= c_i c_k beta + int v; + for(v=0; v<=p->nVars; v++) { + np->GEQs[n_e].coef[v] = - c_k * p->GEQs[i].coef[v]; + } + np->GEQs[n_e].coef[j] = -c_i * c_k; + np->GEQs[n_e].coef[0]--; + + n_e = NewInequality(np); + // c_i c_k beta >= c_i E_k - c_i c_k + 1 + // c_i c_k beta + c_i c_k -1 >= c_i E_k + for(v=0; v<=p->nVars; v++) { + np->GEQs[n_e].coef[v] = - c_i * p->GEQs[k].coef[v]; + } + np->GEQs[n_e].coef[j] = c_i * c_k; + np->GEQs[n_e].coef[0] += c_i * c_k -1; + + new_dnf->add_conjunct(new_conj); + } + } + + if(pres_debug) { + fprintf(DebugFile, "%%%%%% negate_conj OUT %%%%%%\n"); + new_dnf->prefix_print(DebugFile); + } + delete true_part; + delete[] wildCard; + delete[] handleIt; + return(new_dnf); +} + + + + +/////////////////////////////////////////////////////// +// DNFize formula -- this is the real simplification // +// It also destroys the formula it simplifies // +/////////////////////////////////////////////////////// + + + +// +// Try to separate positive and negative clauses below the AND, +// letting us use the techniques described in Pugh & Wonnacott: +// "An Exact Method for Value-Based Dependence Analysis" +// + + +DNF* F_And::DNFize() { + Conjunct *positive_conjunct = NULL; + DNF *neg_conjs = new DNF; + List<DNF*> pos_dnfs; + List_Iterator<DNF*> pos_dnf_i; + DNF *new_dnf = new DNF; + int JustReturnDNF = 0; + + use_ugly_names++; + + if(pres_debug) { + fprintf(DebugFile, "\nF_And:: DNFize [\n"); + prefix_print(DebugFile); + } + + if(children().empty()) { + Conjunct * c=new Conjunct(NULL, relation()); + new_dnf->add_conjunct(c); + } + else { + while(!children().empty()) { + Formula* carg = children().remove_front(); + if(carg->node_type()==Op_Not) { + // DNF1 & ~DNF2 -> DNF + DNF *dnf = carg->children().remove_front()->DNFize(); + delete carg; + neg_conjs->join_DNF(dnf); // negative conjunct + } + else { + // DNF1 & DNF2 -> DNF + DNF *dnf = carg->DNFize(); + int dl = dnf->length(); + if(dl==0) { + // DNF & false -> false + delete this; + JustReturnDNF = 1; + break; + } + else if(dl==1) { + // positive conjunct + Conjunct *conj = dnf->rm_first_conjunct(); + delete dnf; + if(positive_conjunct==NULL) { + positive_conjunct = conj; + } + else { + Conjunct *new_conj = merge_conjs(positive_conjunct, conj, MERGE_REGULAR); + delete conj; + delete positive_conjunct; + positive_conjunct = new_conj; + } + } + else { + // positive DNF + pos_dnfs.append(dnf); + } + } + } + + if (!JustReturnDNF) { + Rel_Body * my_relation = relation(); + delete this; + + // If we have a positive_conjunct, it can serve as the 1st arg to + // conj_and_not_dnf. Otherwise, if pos_dnfs has one DNF, + // use each conjunct there for this purpose. + // Only pass "true" here if there is nothing else to try, + // as long as TRY_TO_AVOID_TRUE_AND_NOT_DNF is set. + // + // Perhaps we should even try to and multiple DNF's? + + if (!positive_conjunct && pos_dnfs.length() == 1) { + if(pres_debug) { + fprintf(DebugFile, "--- F_AND::DNFize() Single pos_dnf:\n"); + pos_dnfs[1]->prefix_print(DebugFile); + fprintf(DebugFile, "--- F_AND::DNFize() vs neg_conjs:\n"); + neg_conjs->prefix_print(DebugFile); + } + + DNF *real_neg_conjs = new DNF; + for (DNF_Iterator nc(neg_conjs); nc; nc++) { + if (simplify_conj((*nc), true, false, EQ_BLACK) != false) + real_neg_conjs->add_conjunct(*nc); + (*nc) = 0; + } + delete neg_conjs; + neg_conjs = real_neg_conjs; + + for(DNF_Iterator pc(pos_dnfs[1]); pc; pc++) { + new_dnf->join_DNF(conj_and_not_dnf((*pc), neg_conjs->copy((*pc)->relation()))); + (*pc) = 0; + } + } + else if(positive_conjunct==NULL && neg_conjs->is_definitely_false()) { + pos_dnf_i = List_Iterator<DNF*>(pos_dnfs); + delete new_dnf; + new_dnf = *pos_dnf_i; + *pos_dnf_i = NULL; + pos_dnf_i++; + for ( ; pos_dnf_i; pos_dnf_i++) { + DNF *pos_dnf = *pos_dnf_i; + new_dnf = DNF_and_DNF(new_dnf, pos_dnf); + *pos_dnf_i = NULL; + } + } + else { + if(positive_conjunct==NULL) { + static int OMEGA_WHINGE = -1; + if (OMEGA_WHINGE < 0) { + OMEGA_WHINGE = getenv("OMEGA_WHINGE") ? atoi(getenv("OMEGA_WHINGE")) : 0; + } + + if (pres_debug || OMEGA_WHINGE) { + fprintf(DebugFile, "Uh-oh: F_AND::DNFize() resorting to TRUE and not DNF\n"); + fprintf(DebugFile, "--- F_AND::DNFize() neg_conjs\n"); + neg_conjs->prefix_print(DebugFile); + fprintf(DebugFile, "--- F_AND::DNFize() pos_dnfs:\n"); + for (pos_dnf_i=List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) { + (*pos_dnf_i)->prefix_print(DebugFile); + fprintf(DebugFile,"---- --\n"); + } + } + if (OMEGA_WHINGE) { + fprintf(stderr, "Uh-oh: F_AND::DNFize() resorting to TRUE and not DNF\n"); + fprintf(stderr, "--- F_AND::DNFize() neg_conjs\n"); + neg_conjs->prefix_print(stderr); + fprintf(stderr, "--- F_AND::DNFize() pos_dnfs:\n"); + for (pos_dnf_i=List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) { + (*pos_dnf_i)->prefix_print(stderr); + fprintf(stderr,"---- --\n"); + } + } + positive_conjunct = new Conjunct(NULL, my_relation); + } + + if(!neg_conjs->is_definitely_false()) { + new_dnf->join_DNF(conj_and_not_dnf(positive_conjunct, neg_conjs)); + neg_conjs = NULL; + } + else { + new_dnf->add_conjunct(positive_conjunct); + } + positive_conjunct = NULL; + + // + // AND it with positive DNFs + // + if(pres_debug) { + fprintf(DebugFile, "--- F_AND::DNFize() pos_dnfs:\n"); + for (pos_dnf_i=List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) + (*pos_dnf_i)->prefix_print(DebugFile); + } + for (pos_dnf_i = List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) { + DNF *pos_dnf = *pos_dnf_i; + new_dnf = DNF_and_DNF(new_dnf, pos_dnf); + *pos_dnf_i = NULL; + } + } + } + } + + delete positive_conjunct; + delete neg_conjs; + for (pos_dnf_i = List_Iterator<DNF*>(pos_dnfs); pos_dnf_i; pos_dnf_i++) + delete *pos_dnf_i; + + if(pres_debug) { + fprintf(DebugFile, "] F_AND::DNFize() OUT \n"); + new_dnf->prefix_print(DebugFile); + } + + use_ugly_names--; + + return new_dnf; +} + +// +// ~ dnf = true ^ ~ dnf, so just call conj_and_not_dnf +// + +DNF* F_Not::DNFize() { + Conjunct *positive_conjunct = new Conjunct(NULL, relation()); + DNF *neg_conjs = children().remove_front()->DNFize(); + delete this; + DNF *new_dnf = conj_and_not_dnf(positive_conjunct, neg_conjs); + + if(pres_debug) { + fprintf(DebugFile, "=== F_NOT::DNFize() OUT ===\n"); + new_dnf->prefix_print(DebugFile); + } + return new_dnf; +} + + +// +// or is almost in DNF already: +// + +DNF* F_Or::DNFize() { + DNF* new_dnf = new DNF; + bool empty_or=true; + + while(!children().empty()) { + DNF* c_dnf = children().remove_front()->DNFize(); + new_dnf->join_DNF(c_dnf); + empty_or=false; + } + + + delete this; + + if(pres_debug) { + fprintf(DebugFile, "=== F_OR::DNFize() OUT ===\n"); + new_dnf->prefix_print(DebugFile); + } + return(new_dnf); +} + + +// +// exists x : (c1 v c2 v ...) --> (exists x : c1) v (exists x : c2) v ... +// + +DNF* F_Exists::DNFize() { + DNF *dnf = children().remove_front()->DNFize(); + + for (DNF_Iterator pd(dnf); pd.live(); pd.next()) { + Conjunct *conj = pd.curr(); + + // can simply call localize_vars for DNF with a single conjunct + Variable_ID_Tuple locals_copy(myLocals.size()); + copy_var_decls(locals_copy, myLocals); + conj->push_exists(locals_copy); + conj->remap(); + reset_remap_field(myLocals); + + conj->r_constrs = 0; + conj->simplified = false; // who knows + conj->cols_ordered = false; + } + delete this; + + if(pres_debug) { + fprintf(DebugFile, "=== F_EXISTS::DNFize() OUT ===\n"); + dnf->prefix_print(DebugFile); + } + return(dnf); +} + + +// +// Single conjunct is already in DNF. +// + +DNF* Conjunct::DNFize() { + assert(!is_compressed()); + DNF *results = new DNF; + + if (is_true()) { + simplified = true; + verified = true; + results->add_conjunct(this); + } + else { + results->add_conjunct(this); + } + + return results; +} + + +// +// Foralls should have been removed before we get to DNFize +// + +DNF* F_Forall::DNFize() { + assert(0); + return(NULL); +} + +void DNF::count_leading_0s() { + if (conjList.empty()) + return; + + for (DNF_Iterator conj(this); conj; conj++) { + (*conj)->count_leading_0s(); + } +} + + +// return x s.t. forall conjuncts c, c has >= x leading 0s +// if set, always returns -1; arg tells you if it's a set or relation. + +int DNF::query_guaranteed_leading_0s(int what_to_return_for_empty_dnf) { + count_leading_0s(); + int result = what_to_return_for_empty_dnf; // if set, -1; if rel, 0 + bool first = true; + + for (DNF_Iterator conj(this); conj; conj++) { + int tmp = (*conj)->query_guaranteed_leading_0s(); + assert(tmp >= 0 || ((*conj)->relation()->is_set() && tmp == -1)); + if (first || tmp < result) result = tmp; + first = false; + } + + return result; +} + +// return x s.t. forall conjuncts c, c has <= x leading 0s +// if no conjuncts, return the argument + +int DNF::query_possible_leading_0s(int n_input_and_output) { + count_leading_0s(); + int result = n_input_and_output; + bool first = true; + + for (DNF_Iterator conj(this); conj; conj++) { + int tmp = (*conj)->query_possible_leading_0s(); + assert(tmp >= 0 || (tmp == -1 && (*conj)->relation()->is_set())); + if (first || tmp > result) result = tmp; + first = false; + } + + return result; +} + + +// return 0 if we don't know, or +-1 if we do + +int DNF::query_leading_dir() { + count_leading_0s(); + int result = 0; + bool first = true; + + for (DNF_Iterator conj(this); conj; conj++) { + int glz = (*conj)->query_guaranteed_leading_0s(); + int plz = (*conj)->query_possible_leading_0s(); + int rlz = 0; // shut the compiler up + if (glz != plz) + return 0; + + if (first) { + rlz = glz; + result = (*conj)->query_leading_dir(); + first = false; + } + else + if (glz != rlz || result != (*conj)->query_leading_dir()) + return 0; + } + + return result; +} + +void Conjunct::count_leading_0s() { + Rel_Body *body = relation(); + int max_depth = min(body->n_inp(), body->n_out()); + if(body->is_set()) { + assert(guaranteed_leading_0s == -1 && possible_leading_0s == -1); +// guaranteed_leading_0s = possible_leading_0s = -1; + leading_dir = 0; + return; + } + + +#if ! defined NDEBUG + assert_leading_info(); +#endif + if (guaranteed_leading_0s < 0) { + int L; + for (L=1; L <= max_depth; L++) { + Variable_ID in = body->input_var(L), out = body->output_var(L); + coef_t min, max; + bool guaranteed; + + query_difference(out, in, min, max, guaranteed); + if (min < 0 || max > 0) { + if (min > 0 || max < 0) { // we know guaranteed & possible + guaranteed_leading_0s = possible_leading_0s = L-1; + if (min > 0) // We know its 0,..,0,+ + leading_dir = 1; + else // We know its 0,..,0,- + leading_dir = -1; + return; + } + break; + } + } + guaranteed_leading_0s = L-1; + for ( ; L <= max_depth; L++) { + Variable_ID in = body->input_var(L), + out = body->output_var(L); + coef_t min, max; + bool guaranteed; + + query_difference(out, in, min, max, guaranteed); + + if (min > 0 || max < 0) break; + } + possible_leading_0s = L-1; + } +#if ! defined NDEBUG + assert_leading_info(); +#endif +} + +// +// add level-carried DNF form out to level "level" +// + + +void DNF::make_level_carried_to(int level) { + count_leading_0s(); + Rel_Body *body = 0; // make compiler shut up + if (length() > 0 && !(body = conjList.front()->relation())->is_set()) { + // LCDNF makes no sense otherwise + Relation tmp; +#ifndef NDEBUG + tmp = Relation(*body,42); +#endif + + DNF *newstuff = new DNF; + int shared_depth = min(body->n_inp(), body->n_out()); + int split_to = level >= 0 ? min(shared_depth,level) : shared_depth; + + skip_finalization_check++; + EQ_Handle e; + + for (DNF_Iterator conj(this); conj; conj++) { + assert(body = (*conj)->relation()); + int leading_eqs; + + bool is_guaranteed = (*conj)->verified; + + for (leading_eqs=1; leading_eqs <= split_to; leading_eqs++) { + Variable_ID in = body->input_var(leading_eqs), + out = body->output_var(leading_eqs); + coef_t min, max; + bool guaranteed; + + if (leading_eqs > (*conj)->possible_leading_0s && + (*conj)->leading_dir_valid_and_known()) { + leading_eqs--; + break; + } + + if (leading_eqs > (*conj)->guaranteed_leading_0s) { + (*conj)->query_difference(out, in, min, max, guaranteed); + if (min > 0 || max < 0) guaranteed = true; +// fprintf(DebugFile,"Make level carried, %d <= diff%d <= %d (%d):\n", +// min,leading_eqs,max,guaranteed); +// use_ugly_names++; +// (*conj)->prefix_print(DebugFile); +// use_ugly_names--; + if (!guaranteed) is_guaranteed = false; + bool generateLTClause = min < 0; + bool generateGTClause = max > 0; + bool retainEQClause = (leading_eqs <= (*conj)->possible_leading_0s && + min <= 0 && max >= 0); + if (!(generateLTClause || generateGTClause || retainEQClause)) { + // conjunct is infeasible + if (pres_debug) { + fprintf(DebugFile, "Conjunct discovered to be infeasible during make_level_carried_to(%d):\n", level); + (*conj)->prefix_print(DebugFile); + } +#if ! defined NDEBUG + Conjunct *cpy = (*conj)->copy_conj_same_relation(); + assert(!simplify_conj(cpy, true, 32767, 0)); +#endif + } + + if (generateLTClause) { + Conjunct *lt; + if (!generateGTClause && !retainEQClause) + lt = *conj; + else + lt = (*conj)->copy_conj_same_relation(); + if (max >= 0) { + GEQ_Handle l = lt->add_GEQ(); // out<in ==> in-out-1>=0 + l.update_coef_during_simplify(in, 1); + l.update_coef_during_simplify(out, -1); + l.update_const_during_simplify(-1); + } + lt->guaranteed_leading_0s + = lt->possible_leading_0s = leading_eqs-1; + lt->leading_dir = -1; + if (is_guaranteed) { + /* + fprintf(DebugFile,"Promising solutions to: %d <= diff%d <= %d (%d):\n", + min,leading_eqs,max,guaranteed); + use_ugly_names++; + lt->prefix_print(DebugFile); + use_ugly_names--; + */ + lt->promise_that_ub_solutions_exist(tmp); + } + else if (0) { + fprintf(DebugFile,"Can't guaranteed solutions to:\n"); + use_ugly_names++; + lt->prefix_print(DebugFile); + use_ugly_names--; + } + if (generateGTClause || retainEQClause) + newstuff->add_conjunct(lt); + } + + if (generateGTClause) { + Conjunct *gt; + if (retainEQClause) gt = (*conj)->copy_conj_same_relation(); + else gt = *conj; + if (min <= 0) { + GEQ_Handle g = gt->add_GEQ(); // out>in ==> out-in-1>=0 + g.update_coef_during_simplify(in, -1); + g.update_coef_during_simplify(out, 1); + g.update_const_during_simplify(-1); + } + gt->guaranteed_leading_0s = + gt->possible_leading_0s = leading_eqs-1; + gt->leading_dir = 1; + if (is_guaranteed) { + /* + fprintf(DebugFile,"Promising solutions to: %d <= diff%d <= %d (%d):\n", + min,leading_eqs,max,guaranteed); + use_ugly_names++; + gt->prefix_print(DebugFile); + use_ugly_names--; + */ + gt->promise_that_ub_solutions_exist(tmp); + } + else if (0) { + fprintf(DebugFile,"Can't guaranteed solutions to:\n"); + use_ugly_names++; + gt->prefix_print(DebugFile); + use_ugly_names--; + } + if (retainEQClause) newstuff->add_conjunct(gt); + } + + if (retainEQClause) { + assert(min <= 0 && 0 <= max); + + if (min < 0 || max > 0) { + e = (*conj)->add_EQ(1); + e.update_coef_during_simplify(in, -1); + e.update_coef_during_simplify(out, 1); + } + + assert((*conj)->guaranteed_leading_0s == -1 + || leading_eqs > (*conj)->guaranteed_leading_0s); + assert((*conj)->possible_leading_0s == -1 + || leading_eqs <= (*conj)->possible_leading_0s); + + (*conj)->guaranteed_leading_0s = leading_eqs; + } + else break; + } + + { + Set<Global_Var_ID> already_done; + int remapped = 0; + + assert((*conj)->guaranteed_leading_0s == -1 + || leading_eqs <= (*conj)->guaranteed_leading_0s); + + for (Variable_ID_Iterator func(*body->global_decls()); func; func++) { + Global_Var_ID f = (*func)->get_global_var(); + if (!already_done.contains(f) && + body->has_local(f, Input_Tuple) && + body->has_local(f, Output_Tuple) && + f->arity() == leading_eqs) { + already_done.insert(f); + + // add f(in) = f(out), project one away + e = (*conj)->add_EQ(1); + Variable_ID f_in =body->get_local(f,Input_Tuple); + Variable_ID f_out =body->get_local(f,Output_Tuple); + + e.update_coef_during_simplify(f_in, -1); + e.update_coef_during_simplify(f_out, 1); + + f_out->remap = f_in; + remapped = 1; + is_guaranteed = false; + } + } + + if (remapped) { + (*conj)->remap(); + (*conj)->combine_columns(); + reset_remap_field(*body->global_decls()); + remapped = 0; + } + } + } + if (is_guaranteed) + (*conj)->promise_that_ub_solutions_exist(tmp); + else if (0) { + fprintf(DebugFile,"Can't guaranteed solutions to:\n"); + use_ugly_names++; + (*conj)->prefix_print(DebugFile); + use_ugly_names--; + } + } + + skip_finalization_check--; + join_DNF(newstuff); + } + +#if ! defined NDEBUG + for (DNF_Iterator c(this); c; c++) + (*c)->assert_leading_info(); +#endif + + simplify(); +} + +void DNF::remove_inexact_conj() { + bool found_inexact=false; + + do { + bool first=true; + found_inexact=false; + DNF_Iterator c_prev; + for (DNF_Iterator c(this); c; c++) { + if (!(*c)->is_exact()) { // remove it from the list + found_inexact=true; + delete (*c); + if (first) + conjList.del_front(); + else + conjList.del_after(c_prev); + break; + } + else { + first=false; + c_prev=c; + } + } + } + while (found_inexact); +} + + +int s_rdt_constrs; + +// +// Simplify all conjuncts in a DNF +// +void DNF::simplify() { + for (DNF_Iterator pd(this); pd.live(); ) { + Conjunct *conj = pd.curr(); + pd.next(); + if(s_rdt_constrs >= 0 && !simplify_conj(conj, true, s_rdt_constrs, EQ_BLACK)) { + rm_conjunct(conj); + } + } +} + +} // namespace diff --git a/omegalib/omega/src/pres_form.cc b/omegalib/omega/src/pres_form.cc new file mode 100644 index 0000000..82b710b --- /dev/null +++ b/omegalib/omega/src/pres_form.cc @@ -0,0 +1,147 @@ +#include <omega/pres_form.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +// +// Children and parents. +// +void Formula::remove_child(Formula *kid) { + assert(&kid->parent() == this); + if (myChildren.front() == kid) + myChildren.del_front(); + else { + List_Iterator<Formula*> j,k; + for(j=List_Iterator<Formula*>(myChildren); *j != kid && j; k=j, j++) + ; + + if (k) + myChildren.del_after(k); + else + assert(0 && "Child to be removed not found in child list"); + } +} + + +void Formula::add_child(Formula *kid) { + assert(can_add_child()); + myChildren.append(kid); + kid->myParent = this; + kid->myRelation = this->relation(); +} + +void Formula::replace_child(Formula *child, Formula* new_child) { + assert(&child->parent() == this); + for(List_Iterator<Formula *> LI(myChildren); LI; LI++) + if(*LI == child) { + *LI = new_child; + new_child->myParent = this; + new_child->myRelation = this->relation(); + break; + } +} + +void Formula::set_parent(Formula *parent, Rel_Body *reln) { + myParent = parent; + myRelation = reln; + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->set_parent(this,reln); +} + +// +// Function that sets myRelation pointers in a tree. +// +void Formula::set_relation(Rel_Body *r) { + myRelation = r; + for(List_Iterator<Formula *> FI(myChildren); FI; FI++) + (*FI)->set_relation(r); +} + + +// +// Function that descends to conjuncts to merge columns +// +void Formula::combine_columns() { + foreach(child,Formula *,myChildren,child->combine_columns()); +} + + +void Formula::finalize() { + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->finalize(); +} + +bool Formula::can_add_child() { + return true; +} + + + +Conjunct *Formula::really_conjunct() { + assert(0 && "really_conjunct() called on something that wasn't"); + return NULL; +} + +Formula::Formula(Formula *p, Rel_Body *r): myParent(p), myRelation(r) { +} + + +void Formula::verify_tree() { // should be const +#if ! defined NDEBUG + Any_Iterator<Formula*> c = myChildren.any_iterator(); + for (; c; c++) { + assert((*c)->myParent==this); + assert((*c)->myRelation==this->myRelation); + (*c)->verify_tree(); + } +#endif +} + +Formula *Formula::copy(Formula *, Rel_Body *) { + assert(0); + return NULL; +} + +Formula::~Formula() { + for(List_Iterator<Formula*> c(myChildren); c; c++) { + delete *c; + } + myChildren.clear(); +} + +void Formula::assert_not_finalized() { + if (!skip_finalization_check) { + assert(! relation()->is_finalized()); + assert(! relation()->is_shared()); + } +} + +void Formula::reverse_leading_dir_info() { + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->reverse_leading_dir_info(); +} + +void Formula::invalidate_leading_info(int changed) { + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->invalidate_leading_info(changed); +} + +void Formula::enforce_leading_info(int guaranteed, int possible, int dir) { + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->enforce_leading_info(guaranteed, possible, dir); +} + +// +// Push_exists functions. +// Push exists takes responsibility for the Variable_ID's in the Tuple. +// It should: +// * Re-use them, or +// * Delete them. +void Formula::push_exists(Variable_ID_Tuple &) { + assert(0); +} + +} // namespace diff --git a/omegalib/omega/src/pres_gen.cc b/omegalib/omega/src/pres_gen.cc new file mode 100644 index 0000000..0f05d40 --- /dev/null +++ b/omegalib/omega/src/pres_gen.cc @@ -0,0 +1,45 @@ +#include <omega/pres_gen.h> + +namespace omega { + +int skip_finalization_check=0; +// int skip_set_checks=0; + +int pres_debug=0 ; +FILE *DebugFile=stderr; // This is the default; it's best to set it yourself. + +negation_control pres_legal_negations = any_negation; + +// +// I/O utility functions. +// +// void PresErrAssert(const char *t) { +// fprintf(stdout, "\nERROR: %s\n", t); +// if(pres_debug) { +// fprintf(DebugFile, "\nERROR: %s\n", t); +// } +// exit(1); +// } + + + +// +// Needed for gprof +// +#if defined PROFILE_MALLOCS +void* operator new(size_t n) { + void *result = malloc (n < 1 ? 1 : n); + if (result) + return result; + else { + write(2,"Virtual memory exceeded in new\n",32); + return 0; + } +} + +void operator delete (void* f) { + if (f) free(f); +} +#endif + +} // namespace diff --git a/omegalib/omega/src/pres_logic.cc b/omegalib/omega/src/pres_logic.cc new file mode 100644 index 0000000..8ee90f1 --- /dev/null +++ b/omegalib/omega/src/pres_logic.cc @@ -0,0 +1,226 @@ +#include <omega/pres_logic.h> +#include <omega/pres_conj.h> +#include <omega/pres_quant.h> +#include <omega/omega_i.h> + +namespace omega { + +GEQ_Handle F_And::add_GEQ(int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nGEQs >= maxGEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nGEQs < maxGEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();// FERD -- set level if preserved? + } + return pos_conj->add_GEQ(preserves_level); +} + + +EQ_Handle F_And::add_EQ(int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nEQs >= maxEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nEQs < maxEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();//FERD-set level info if preserved? + } + return pos_conj->add_EQ(preserves_level); +} + +Stride_Handle F_And::add_stride(int step, int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nEQs >= maxEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nEQs < maxEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct(); // FERD -set level if preserved? + } + return pos_conj->add_stride(step, preserves_level); +} + +GEQ_Handle F_And::add_GEQ(const Constraint_Handle &constraint, int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nGEQs >= maxGEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nGEQs < maxGEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();// FERD -- set level if preserved? + } + return pos_conj->add_GEQ(constraint, preserves_level); +} + + +EQ_Handle F_And::add_EQ(const Constraint_Handle &constraint, int preserves_level) { + assert_not_finalized(); + if (pos_conj == NULL || pos_conj->problem->nEQs >= maxEQs) { + pos_conj = NULL; + for(List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct && + ((*c)->really_conjunct())->problem->nEQs < maxEQs) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct();//FERD-set level info if preserved? + } + return pos_conj->add_EQ(constraint,preserves_level); +} + + +void F_And::add_unknown() { + assert_not_finalized(); + if (pos_conj == NULL) { + for (List_Iterator<Formula*> c(children()); c; c++) { + if ((*c)->node_type()==Op_Conjunct) { + pos_conj = (*c)->really_conjunct(); + break; + } + } + if(!pos_conj) pos_conj = add_conjunct(); // FERD - set level if preseved? + } + pos_conj->make_inexact(); +} + +Conjunct *F_Or::find_available_conjunct() { + return 0; +} + +Conjunct *F_Not::find_available_conjunct() { + return 0; +} + +Conjunct *F_And::find_available_conjunct() { + for(List_Iterator<Formula*> child(children()); child; child++) { + Conjunct *c = (*child)->find_available_conjunct(); + if (c) return c; + } + return 0; +} + + +void F_Not::finalize() { + assert(n_children() == 1); + Formula::finalize(); +} + +bool F_Not::can_add_child() { + return n_children() < 1; +} + +F_And *F_And::and_with() { + assert_not_finalized(); + assert(can_add_child()); + return this; +} + +F_And::F_And(Formula *p, Rel_Body *r): Formula(p,r), pos_conj(NULL) { +} + +F_Or::F_Or(Formula *p, Rel_Body *r): Formula(p,r){ +} + +F_Not::F_Not(Formula *p, Rel_Body *r): Formula(p,r){ +} + +Formula *F_And::copy(Formula *parent, Rel_Body *reln) { + F_And *f = new F_And(parent, reln); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + return f; +} + +Formula *F_Or::copy(Formula *parent, Rel_Body *reln) { + F_Or *f = new F_Or(parent, reln); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + return f; +} + +Formula *F_Not::copy(Formula *parent, Rel_Body *reln) { + F_Not *f = new F_Not(parent, reln); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + return f; +} + +// +// Create F_Exists nodes below this F_Or. +// Copy list S to each of the created nodes. +// Push_exists takes responsibility for reusing or deleting Var_ID's; +// here we delete them. Must also empty out the Tuple when finished. +void F_Or::push_exists(Variable_ID_Tuple &S) { + List<Formula*> mc; + mc.join(children()); + + while(!mc.empty()) { + Formula *f = mc.remove_front(); + F_Exists *e = add_exists(); + + copy_var_decls(e->myLocals, S); + f->remap(); + reset_remap_field(S); + + e->add_child(f); + } + // Since these are not reused, they have to be deleted + for(Tuple_Iterator<Variable_ID> VI(S); VI; VI++) { + assert((*VI)->kind() == Exists_Var); + delete *VI; + } + S.clear(); +} + +void F_Exists::push_exists(Variable_ID_Tuple &S) { + myLocals.join(S); +} + +F_Not *Formula::add_not() { + assert_not_finalized(); + assert(can_add_child()); + F_Not *f = new F_Not(this, myRelation); + myChildren.append(f); + return f; +} + +F_And *Formula::add_and() { + assert_not_finalized(); + assert(can_add_child()); + F_And *f = new F_And(this, myRelation); + myChildren.append(f); + return f; +} + +F_And *Formula::and_with() { + return add_and(); +} + +F_Or *Formula::add_or() { + assert_not_finalized(); + assert(can_add_child()); + F_Or *f = new F_Or(this, myRelation); + myChildren.append(f); + return f; +} + +} // namespace diff --git a/omegalib/omega/src/pres_print.cc b/omegalib/omega/src/pres_print.cc new file mode 100644 index 0000000..4f2cd0d --- /dev/null +++ b/omegalib/omega/src/pres_print.cc @@ -0,0 +1,908 @@ +#include <omega/pres_gen.h> +#include <omega/pres_var.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <basic/Bag.h> +#include <omega/omega_i.h> +#include <omega/omega_core/oc.h> + +namespace omega { + +//////////////////////////////////////// +// // +// Print functions. // +// // +//////////////////////////////////////// + +void Conjunct::reorder_for_print(bool reverseOrder, int first_pass_input, int first_pass_output, bool sort) { + Conjunct *C2 = copy_conj_same_relation(); + Variable_ID_Tuple newpos(0),wcvars(0),gvars(0); + +// We reorder the original Variable_ID's into the newpos list; later, we +// copy from their original column (using find_column) to the new one. + int n = mappedVars.size(); + int i; + // there may be more inp/outp vars than maxVars; must do dynamically + // skip_set_checks++; + Tuple<bool> input_used(myRelation->n_inp()); + Tuple<bool> output_used(myRelation->n_out()); + for(i=1; i<=myRelation->n_inp();i++) input_used[i] = false; + for(i=1; i<=myRelation->n_out();i++) output_used[i] = false; + for(i=1; i<=n;i++) { + if (mappedVars[i]->kind() == Input_Var) + input_used[mappedVars[i]->get_position()] = true; + else if (mappedVars[i]->kind() == Output_Var) + output_used[mappedVars[i]->get_position()] = true; + else if(mappedVars[i]->kind() == Global_Var) + gvars.append(mappedVars[i]); + } + + + if(sort) + for(i=1; i<=gvars.size();i++) + for(int j=1; j <= gvars.size(); j++) + if(gvars[j]->get_global_var()->base_name() + < gvars[j+1]->get_global_var()->base_name()) { + Variable_ID t = gvars[j]; gvars[j] = gvars[j+1]; gvars[j+1] = t; + } + + newpos.join(gvars); + + if(!reverseOrder) { + for(i=1; i<=min(myRelation->n_inp(),first_pass_input);i++) + if (input_used[i]) newpos.append(input_vars[i]); + for(i=1; i<=min(myRelation->n_out(),first_pass_output);i++) + if (output_used[i]) newpos.append(output_vars[i]); + for(i=max(1,first_pass_input+1); i<=myRelation->n_inp();i++) + if (input_used[i]) newpos.append(input_vars[i]); + for(i=max(1,first_pass_output+1); i<=myRelation->n_out();i++) + if (output_used[i]) newpos.append(output_vars[i]); + } + else { + for(i=1; i<=min(myRelation->n_out(),first_pass_output);i++) + if (output_used[i]) newpos.append(output_vars[i]); + for(i=1; i<=min(myRelation->n_inp(),first_pass_input);i++) + if (input_used[i]) newpos.append(input_vars[i]); + for(i=max(1,first_pass_output+1); i<=myRelation->n_out();i++) + if (output_used[i]) newpos.append(output_vars[i]); + for(i=max(1,first_pass_input+1); i<=myRelation->n_inp();i++) + if (input_used[i]) newpos.append(input_vars[i]); + } + + + for(i=1; i<=n;i++) + if (mappedVars[i]->kind() == Wildcard_Var) + wcvars.append(mappedVars[i]); + + if(sort) + for(i=1; i<=gvars.size();i++) + for(int j=1; j <= gvars.size(); j++) + if(gvars[j]->name() < gvars[j+1]->name()) { + Variable_ID t = gvars[j]; gvars[j] = gvars[j+1]; gvars[j+1] = t; + } + + newpos.join(wcvars); + + assert(problem->nVars == newpos.size()); // i.e. no other variable types + + // Copy coef columns into new order. Constant column is unchanged. + for(int e=0; e<problem->nGEQs; e++) problem->GEQs[e].touched = 1; + + for(i=1; i<=problem->nVars; i++) { + int col = find_column(newpos[i]); // Find column in original conj. + assert(col != 0); + copy_column(problem, i, // Copy it from orig. column in the copy. + C2->problem, col, 0, 0); + problem->var[i] = i; + } + for(i=1; i<=problem->nVars; i++) + problem->forwardingAddress[i] = i; + + mappedVars = newpos; + delete C2; + // skip_set_checks--; +} + +void Rel_Body::print_with_subs(FILE *output_file, bool printSym, bool newline) { + std::string s = this->print_with_subs_to_string(printSym, newline); + fprintf(output_file, "%s", s.c_str()); +} + +void Rel_Body::print_with_subs() { + this->print_with_subs(stdout, 0, 1); +} + +static std::string tryToPrintVarToStringWithDiv(Conjunct *C, Variable_ID v) { + std::string s; + bool seen = false; +// This assumes that there is one EQ involving v, that v cannot be +// substituted and hence has a non-unit coefficient. + for(EQ_Iterator e(C); e; e++) { + if ((*e).get_coef(v) != 0) { + assert(!seen); // This asserts just one EQ with v + coef_t v_coef = (*e).get_coef(v); + int v_sign = v_coef > 0 ? 1 : -1; + v_coef *= v_sign; + int sign_adj = -v_sign; + + s += "intDiv("; + bool first=true; + for(Constr_Vars_Iter i(*e,false); i; i++) { + if ((*i).var != v && (*i).coef != 0) { + coef_t this_coef = sign_adj*(*i).coef; + if(!first && this_coef > 0) + s+= "+"; + if (this_coef == 1) + s += (*i).var->name(); + else if (this_coef == -1) { + s += "-"; s += (*i).var->name(); + } + else { + s += to_string(this_coef) + "*" + (*i).var->name(); + } + first = false; + } + } + coef_t the_const = (*e).get_const()* sign_adj; + if (the_const > 0 && !first) + s+= "+"; + if (the_const != 0) + s += to_string(the_const); + s += "," + to_string(v_coef) + ")"; + seen = true; + } + } + return s; +} + + +// This function prints the output tuple of a relation with each one as a +// function of the input variables. +// This will fail or look goofy if the outputs are not affine functions of +// the inputs. +// BIG WARNING: Call this only from the codegen stuff, since it assumes things +// about coefficients +std::string Rel_Body::print_outputs_with_subs_to_string() { + // Rel_Body S(this),Q(this); + Rel_Body *S = this->clone(); + Rel_Body *Q = this->clone(); + S->setup_names(); + Conjunct *C = S->single_conjunct(); + Conjunct *D = Q->single_conjunct(); // ordered_elimination futzes with conj + std::string s; // = "["; + C->reorder_for_print(); + C->ordered_elimination(S->global_decls()->length()); + // Print output names with substitutions in them + for(int i=1; i<=S->n_out(); i++) { + std::string t; + int col = C->find_column(output_vars[i]); + if (col != 0) + t = C->print_sub_to_string(col); + if (col == 0 || t == output_vars[i]->name()) // no sub found + // Assume you can't get a unit coefficient on v, must use div + t = tryToPrintVarToStringWithDiv(D, output_vars[i]); + s += t; + if (i < S->n_out()) s += ","; + } + // s += "] "; + delete S; + delete Q; + return s; +} + +std::string Rel_Body::print_outputs_with_subs_to_string(int i) { + // Rel_Body S(this),Q(this); + Rel_Body *S = this->clone(); + Rel_Body *Q = this->clone(); + S->setup_names(); + Conjunct *C = S->single_conjunct(); + Conjunct *D = Q->single_conjunct(); // ordered_elimination futzes with conj + std::string s; + C->reorder_for_print(); + C->ordered_elimination(S->global_decls()->length()); + // Print output names with substitutions in them + { + std::string t; + int col = C->find_column(output_vars[i]); + if (col != 0) + t = C->print_sub_to_string(col); + if (col == 0 || t == output_vars[i]->name()) // no sub found? + t = tryToPrintVarToStringWithDiv(D, output_vars[i]); + // should check for failure + s += t; + } + delete S; + delete Q; + return s; +} + +std::string Rel_Body::print_with_subs_to_string(bool printSym, bool newline) { + std::string s=""; + + if (pres_debug) { + fprintf(DebugFile,"print_with_subs_to_string:\n"); + prefix_print(DebugFile); + } + + int anythingPrinted = 0; + + if (this->is_null()) { + s = "NULL Rel_Body\n"; + return s; + } + + // Rel_Body R(this); + Rel_Body *R = this->clone(); + bool firstRelation = true; + + for(DNF_Iterator DI(R->query_DNF()); DI.live(); DI.next()) { + Rel_Body S(R, DI.curr()); + Conjunct *C = S.single_conjunct(); + if(!simplify_conj(C, true, 4, EQ_BLACK)) continue; + + S.setup_names(); + + if(! firstRelation) { + s += " union"; + if (newline) s += "\n "; + } + else + firstRelation = false; + + anythingPrinted = 1; + + C->reorder_for_print(false,C->max_ufs_arity_of_in(), + C->max_ufs_arity_of_out()); + C->ordered_elimination(S.Symbolic.length() + +C->max_ufs_arity_of_in() + +C->max_ufs_arity_of_out()); +// assert(C->problem->variablesInitialized); + + if (pres_debug) S.prefix_print(DebugFile); + + /* Do actual printing of Conjunct C as a relation */ + s += "{"; + + if (!Symbolic.empty()) { + if (printSym) s += "Sym=["; + for (Variable_ID_Iterator Sym_I = S.Symbolic; Sym_I;) + { + if (printSym) + s += (*Sym_I)->name(); + Sym_I++; + if (printSym && Sym_I) s+= ","; + } + if (printSym) s += "] "; + } + + int i, col; + + if (S.number_input != 0) { + s += "["; + // Print input names with substitutions in them + for(i=1; i<=S.number_input; i++) { + col = C->find_column(input_vars[i]); + if (col != 0) + s += C->problem->print_sub_to_string(col); + else + s += input_vars[i]->name(); + if (i<S.number_input) s += ","; + } + s += "]"; + } + + if (! S.is_set()) + s += " -> "; + + if (S.number_output != 0) { + s += "["; + + // Print output names with substitutions in them + for(i=1; i<=S.number_output; i++) { + col = C->find_column(output_vars[i]); + if (col != 0) + s += C->problem->print_sub_to_string(col); + else + s += output_vars[i]->name(); + if (i < S.number_output) s += ","; + } + s += "] "; + } + + if (C->is_unknown()) { + if (S.number_input != 0 || S.number_output != 0) + s += ":"; + s += " UNKNOWN"; + } + else { + // Empty conj means TRUE, so don't print colon + if (C->problem->nEQs != 0 || C->problem->nGEQs != 0) { + C->problem->clearSubs(); + if (S.number_input != 0 || S.number_output != 0) + s += ":"; + s += " "; + s += C->print_to_string(false); + } + else { + if (S.number_input == 0 && S.number_output == 0) + s += " TRUE "; + } + } + + s += "}"; + } + + if (!anythingPrinted) { + R->setup_names(); + s = "{"; + s += R->print_variables_to_string(printSym); + if (R->number_input != 0 || R->number_output != 0) + s += " :"; + s += " FALSE }"; + if (newline) s += "\n"; + + delete R; + return s; + } + + if (newline) s += "\n"; + + delete R; + return s; +} + + +void print_var_addrs(std::string &s, Variable_ID v) { + if(pres_debug>=2) { + char ss[20]; + sprintf(ss, "(%p,%p)", v, v->remap); + s += ss; + } +} + +std::string Rel_Body::print_variables_to_string(bool printSym) { + std::string s=""; + + if (! Symbolic.empty()) { + if (printSym) s += " Sym=["; + for (Variable_ID_Iterator Sym_I(Symbolic); Sym_I; ) { + if (printSym) s += (*Sym_I)->name(); + print_var_addrs(s, *Sym_I); + Sym_I++; + if (printSym && Sym_I) s += ","; + } + if (printSym) s += "] "; + } + int i; + + if (number_input) { + s += "["; + for (i=1;i<=number_input;i++) { + s += input_vars[i]->name(); + print_var_addrs(s, input_vars[i]); + if(i < number_input) s += ","; + } + s += "] "; + } + + if (number_output) { + s += "-> ["; + for (i=1;i<=number_output;i++) { + s += output_vars[i]->name(); + print_var_addrs(s, output_vars[i]); + if(i < number_output) s += ","; + } + s += "] "; + } + + return s; +} + + +int F_Declaration::priority() { + return 3; +} + +int Formula::priority () { + return 0; +} + +int F_Or::priority() { + return 0; +} + +int F_And::priority() { + return 1; +} + +int Conjunct::priority() { + return 1; +} + +int F_Not::priority() { + return 2; +} + + +// +// print() functions +// +void Formula::print(FILE *output_file) { + if(myChildren.empty()) { + if(node_type()==Op_Relation || node_type()==Op_Or) + fprintf(output_file, "FALSE"); + else if(node_type()==Op_And) + fprintf(output_file, "TRUE"); + else { + assert(0); + } + } + + for(List_Iterator<Formula*> c(myChildren); c;) { + if (node_type() == Op_Exists || node_type() == Op_Forall + || (*c)->priority() < priority()) + fprintf(output_file, "( "); + (*c)->print(output_file); + if (node_type() == Op_Exists || node_type() == Op_Forall + || (*c)->priority() < priority()) + fprintf(output_file, " )"); + c++; + if(c.live()) + print_separator(output_file); + } +} + +std::string Rel_Body::print_formula_to_string() { + std::string s; + setup_names(); + for(DNF_Iterator DI(query_DNF()); DI.live();) { + + s += (*DI)->print_to_string(1); + DI.next(); + if (DI.live()) s += " && "; + if (pres_debug) fprintf(DebugFile,"Partial print to string: %s\n", + s.c_str()); + } + return s; +} + +void Rel_Body::print(FILE *output_file, bool printSym) { + if (this->is_null()) { + fprintf(output_file, "NULL Rel_Body\n"); + return; + } + + setup_names(); + + fprintf(output_file, "{"); + + std::string s = print_variables_to_string(printSym); + fprintf(output_file, "%s", s.c_str()); + + fprintf(output_file, ": "); + + if(simplified_DNF==NULL) { + Formula::print(output_file); + } + else { + assert(children().empty()); + simplified_DNF->print(output_file); + } + + fprintf(output_file, " }\n"); +} + +void Rel_Body::print() { + this->print(stdout); +} + +void F_Not::print(FILE *output_file) { + fprintf(output_file, " not "); + Formula::print(output_file); +} + +void F_And::print_separator(FILE *output_file) { + fprintf(output_file, " and "); +} + +void Conjunct::print(FILE *output_file) { + std::string s = print_to_string(true); + fprintf(output_file, "%s", s.c_str()); +} + +std::string Conjunct::print_to_string(int true_printed) { + std::string s=""; + + int num = myLocals.size(); + if(num) { + s += "exists ( "; + int i; + for (i = 1; i <= num; i++) { + Variable_ID v = myLocals[i]; + s += v->char_name(); + if(i < num) s += ","; + } + if (true_printed || !(is_true())) s += " : "; + } + + if(is_true()) { + s += true_printed ? "TRUE" : ""; + } + else { + if (is_unknown()) + s += "UNKNOWN"; + else { + s += problem->prettyPrintProblemToString(); + if (!exact) + s += " && UNKNOWN"; + } + } + + + if (num) s += ")"; + return s; +} + +void F_Or::print_separator(FILE *output_file) { + fprintf(output_file, " or "); +} + +void F_Declaration::print(FILE *output_file) { + std::string s=""; + for(Variable_ID_Iterator VI(myLocals); VI; ) { + s += (*VI)->name(); + VI++; + if(VI) s += ","; + } + fprintf(output_file, "( %s : ", s.c_str()); + Formula::print(output_file); + fprintf(output_file, ")"); +} + +void F_Forall::print(FILE *output_file) { + fprintf(output_file, "forall "); + F_Declaration::print(output_file); +} + +void F_Exists::print(FILE *output_file) { + fprintf(output_file, "exists "); + F_Declaration::print(output_file); +} + +void Formula::print_separator(FILE *) { + assert(0); // should never be called, it's only for derived classes +} + +// +// Setup names in formula. +// + +typedef Set<Global_Var_ID> g_set; +void Rel_Body::setup_names() { + int i; + + + if (use_ugly_names) return; + + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for 0x%p\n", this); + + for(i=1; i<=number_input; i++) { + input_vars[i]->base_name = In_Names[i]; + } + for(i=1; i<=number_output; i++) { + output_vars[i]->base_name = Out_Names[i]; + } + + g_set gbls; + + wildCardInstanceNumber = 0; + + for(i=1; i<= Symbolic.size(); i++) { + gbls.insert(Symbolic[i]->get_global_var()); + } + + foreach(g,Global_Var_ID,gbls, + g->instance = g->base_name()++); + + for(i=1; i<=number_input; i++) { + if (!input_vars[i]->base_name.null()) + input_vars[i]->instance = input_vars[i]->base_name++; + } + for(i=1; i<=number_output; i++) { + if (!output_vars[i]->base_name.null()) + output_vars[i]->instance = output_vars[i]->base_name++; + } + + if (simplified_DNF != NULL) // It is simplified + simplified_DNF->setup_names(); + else // not simplified + Formula::setup_names(); + + for(i=1; i<=number_output; i++) { + if (!output_vars[i]->base_name.null()) + output_vars[i]->base_name--; + } + for(i=1; i<=number_input; i++) { + if (!input_vars[i]->base_name.null()) + input_vars[i]->base_name--; + } + foreach(g,Global_Var_ID,gbls, g->base_name()--); +} + +void Formula::setup_names() { + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for formula 0x%p\n", this); + + for(List_Iterator<Formula*> c(myChildren); c; c++) + (*c)->setup_names(); +} + +void DNF::setup_names() { + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for DNF 0x%p\n", this); + + for(DNF_Iterator DI(this); DI.live(); DI.next()) + DI.curr()->setup_names(); +} + + +void F_Declaration::setup_names() { + if (pres_debug>=2) + fprintf(DebugFile,"Setting up names for Declaration 0x%p\n", this); + + // Allow re-use of wc names in other scopes + int savedWildCardInstanceNumber = wildCardInstanceNumber; + + for(Tuple_Iterator<Variable_ID> VI(myLocals); VI; VI++) { + Variable_ID v = *VI; + if (!v->base_name.null()) v->instance = v->base_name++; + else v->instance = wildCardInstanceNumber++; + } + + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->setup_names(); + + for(Tuple_Iterator<Variable_ID> VI2(myLocals); VI2; VI2++) { + Variable_ID v = *VI2; + if (!v->base_name.null()) v->base_name--; + } + wildCardInstanceNumber = savedWildCardInstanceNumber; +} + + +// +// Prefix_print functions. +// Print Formula Tree, used in debugging. +// +static int level = 0; + +void Formula::prefix_print(FILE *output_file, int debug) { + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->prefix_print(output_file, debug); + level--; +} + + +void Rel_Body::prefix_print() { + this->prefix_print(stdout, 1); +} + +void Rel_Body::prefix_print(FILE *output_file, int debug) { + int old_use_ugly_names = use_ugly_names; + use_ugly_names = 0; + + setup_names(); + + level = 0; + if(pres_debug>=2) fprintf(output_file, "(@%p)", this); + fprintf(output_file, is_set() ? "SET: " : "RELATION: "); + std::string s = print_variables_to_string(true); + fprintf(output_file, "%s\n", s.c_str()); + + if(simplified_DNF==NULL) { + Formula::prefix_print(output_file, debug); + } else { + assert(children().empty()); + simplified_DNF->prefix_print(output_file, debug, true); + } + fprintf(output_file, "\n"); + use_ugly_names = old_use_ugly_names; +} + + +void F_Forall::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "forall ["); + F_Declaration::prefix_print(output_file, debug); + for (Variable_ID_Iterator VI(myLocals); VI; VI++) + assert((*VI)->kind() == Forall_Var); + +} + +void F_Exists::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + if(pres_debug>=2) fprintf(output_file, "(@%p)", this); + fprintf(output_file, "exists ["); + F_Declaration::prefix_print(output_file, debug); + for (Variable_ID_Iterator VI(myLocals); VI; VI++) + assert((*VI)->kind() == Exists_Var); +} + +void F_Declaration::prefix_print(FILE *output_file, int debug) { + std::string s = ""; + for (Variable_ID_Iterator VI(myLocals); VI; ) { + s += (*VI)->name(); + print_var_addrs(s, *VI); + VI++; + if(VI) s += ","; + } + s += "]\n"; + fprintf(output_file, "%s", s.c_str()); + Formula::prefix_print(output_file, debug); +} + +void F_Or::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "or\n"); + Formula::prefix_print(output_file, debug); +} + +void F_And::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "and\n"); + Formula::prefix_print(output_file, debug); +} + +void F_Not::prefix_print(FILE *output_file, int debug) { + Formula::print_head(output_file); + fprintf(output_file, "not\n"); + Formula::prefix_print(output_file, debug); +} + +void Conjunct::prefix_print(FILE *output_file, int debug) { + static char dir_glyphs[] = { '-', '?', '+' }; + + if (debug) { + Formula::print_head(output_file); + if(pres_debug>=2) fprintf(output_file, "(@%p)", this); + fprintf(output_file, "%s CONJUNCT, ", exact ? "EXACT" : "INEXACT"); + if (simplified) fprintf(output_file, "simplified, "); + if (verified) fprintf(output_file, "verified, "); + if (possible_leading_0s != -1 && guaranteed_leading_0s != -1) + assert (guaranteed_leading_0s <= possible_leading_0s); + if (guaranteed_leading_0s != -1 && guaranteed_leading_0s == possible_leading_0s) + fprintf(output_file,"# leading 0's = %d,", possible_leading_0s); + else if (possible_leading_0s != -1 || guaranteed_leading_0s != -1) { + if (guaranteed_leading_0s != -1) + fprintf(output_file,"%d <= ",guaranteed_leading_0s); + fprintf(output_file,"#O's"); + if (possible_leading_0s != -1) + fprintf(output_file," <= %d",possible_leading_0s); + fprintf(output_file,", "); + } + if (dir_glyphs[leading_dir+1] != '?') + fprintf(output_file," first = %c, ", dir_glyphs[leading_dir+1]); + fprintf(output_file,"myLocals=["); + std::string s=""; + for (Variable_ID_Iterator VI(myLocals); VI; ) { + assert( (*VI)->kind() == Wildcard_Var); + s += (*VI)->name(); + print_var_addrs(s, *VI); + VI++; + if(VI) s += ","; + } + s += "] mappedVars=["; + for(Variable_ID_Iterator MVI(mappedVars); MVI; ) { + s += (*MVI)->name(); + print_var_addrs(s, *MVI); + MVI++; + if(MVI) s += ","; + } + fprintf(output_file, "%s]\n", s.c_str()); + } + else + level++; + + setOutputFile(output_file); + setPrintLevel(level+1); + problem->printProblem(debug); + setPrintLevel(0); + Formula::prefix_print(output_file, debug); +} + +void Formula::print_head(FILE *output_file) { + level++; + int i; + for(i=0; i<level; i++) { + fprintf(output_file, ". "); + } +} + +// +// Print DNF. +// +void DNF::print(FILE *out_file) { + DNF_Iterator p(this); + if (!p.live()) + fprintf(out_file, "FALSE"); + else + for(; p.live(); ) { + p.curr()->print(out_file); + p.next(); + if(p.live()) + fprintf(out_file, " or "); + } +} + +void DNF::prefix_print(FILE *out_file, int debug, bool parent_names_setup) { + wildCardInstanceNumber = 0; + Variable_ID_Tuple all_vars; + if(!use_ugly_names && !parent_names_setup) { + // We need to manually set up all of the input,output, and symbolic + // variables, since during simplification, a dnf's conjuncts may not + // be listed as part of a relation, or perhaps as part of different + // relations (?) (grr). + for(DNF_Iterator p0(this); p0.live(); p0.next()) { + for(Variable_Iterator vi((*p0)->mappedVars); vi; vi++) + if((*vi)->kind() != Wildcard_Var && all_vars.index(*vi) == 0) + all_vars.append(*vi); + (*p0)->setup_names(); + } + foreach(v,Variable_ID,all_vars, + if (!v->base_name.null()) v->instance = v->base_name++; + else v->instance = wildCardInstanceNumber++; + ); + } + + int i = 1; + level = 0; + for(DNF_Iterator p(this); p.live(); p.next(), i++) { + fprintf(out_file, "#%d ", i); + if(*p == NULL) + fprintf(out_file, "(NULL)\n"); + else + (*p)->prefix_print(out_file, debug); + } + + foreach(v,Variable_ID,all_vars,if (!v->base_name.null()) v->base_name--); + + fprintf(out_file, "\n"); +} + +std::string Constraint_Handle::print_to_string() const { + assert(0); + return "FOO"; +} + +std::string EQ_Handle::print_to_string() const { + relation()->setup_names(); + std::string s = c->print_EQ_to_string(e); + return s; +} + +std::string GEQ_Handle::print_to_string() const { + relation()->setup_names(); + std::string s = c->print_GEQ_to_string(e); + return s; +} + +std::string Constraint_Handle::print_term_to_string() const { + assert(0); + return "FOO"; +} + +std::string EQ_Handle::print_term_to_string() const { + relation()->setup_names(); + std::string s = c->print_EQ_term_to_string(e); + return s; +} + +std::string GEQ_Handle::print_term_to_string() const { + relation()->setup_names(); + std::string s = c->print_GEQ_term_to_string(e); + return s; +} + +} diff --git a/omegalib/omega/src/pres_quant.cc b/omegalib/omega/src/pres_quant.cc new file mode 100644 index 0000000..5483bad --- /dev/null +++ b/omegalib/omega/src/pres_quant.cc @@ -0,0 +1,95 @@ +#include <omega/pres_quant.h> +#include <omega/omega_i.h> + +namespace omega { + +F_Forall::F_Forall(Formula *p, Rel_Body *r): F_Declaration(p,r) { +} + +F_Exists::F_Exists(Formula *p, Rel_Body *r): F_Declaration(p,r) { +} + +F_Exists::F_Exists(Formula *p, Rel_Body *r, Variable_ID_Tuple &S): F_Declaration(p,r,S) { +} + + +Formula *F_Forall::copy(Formula *parent, Rel_Body *reln) { + F_Forall *f = new F_Forall(parent, reln); + copy_var_decls(f->myLocals, myLocals); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + reset_remap_field(myLocals); + return f; +} + +Formula *F_Exists::copy(Formula *parent, Rel_Body *reln) { + F_Exists *f = new F_Exists(parent, reln); + copy_var_decls(f->myLocals, myLocals); + for(List_Iterator<Formula*> c(children()); c; c++) + f->children().append((*c)->copy(f,reln)); + reset_remap_field(myLocals); + return f; +} + +Variable_ID F_Forall::declare(Const_String s) { + return do_declare(s, Forall_Var); +} + +Variable_ID F_Forall::declare() { + return do_declare(Const_String(), Forall_Var); +} + +Variable_ID F_Forall::declare(Variable_ID v) { + return do_declare(v->base_name, Forall_Var); +} + + +Variable_ID F_Exists::declare(Const_String s) { + return do_declare(s, Exists_Var); +} + +Variable_ID F_Exists::declare() { + return do_declare(Const_String(), Exists_Var); +} + +Variable_ID F_Exists::declare(Variable_ID v) { + return do_declare(v->base_name, Exists_Var); +} + +Conjunct *F_Forall::find_available_conjunct() { + return 0; +} + +Conjunct *F_Exists::find_available_conjunct() { + assert(children().length() == 1 || children().length() == 0); + if (children().length() == 0) + return 0; + else + return children().front()->find_available_conjunct(); +} + +F_Exists *Formula::add_exists() { + assert_not_finalized(); + assert(can_add_child()); + F_Exists *f = new F_Exists(this, myRelation); + myChildren.append(f); + return f; +} + +F_Exists *Formula::add_exists(Variable_ID_Tuple &S) { + assert_not_finalized(); + assert(can_add_child()); + F_Exists *f = new F_Exists(this, myRelation, S); + myChildren.append(f); + return f; +} + +F_Forall *Formula::add_forall() { + assert_not_finalized(); + assert(can_add_child()); + F_Forall *f = new F_Forall(this, myRelation); + myChildren.append(f); + return f; +} + +} // namespace diff --git a/omegalib/omega/src/pres_rear.cc b/omegalib/omega/src/pres_rear.cc new file mode 100644 index 0000000..508959d --- /dev/null +++ b/omegalib/omega/src/pres_rear.cc @@ -0,0 +1,131 @@ +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/Relation.h> +#include <omega/omega_i.h> + +namespace omega { + +///////////////////////// +// // +// Rearrange functions // +// // +///////////////////////// + +// +// Rules: +// ~ (f1 | f2 | ... | fn) = ~f1 & ~f2 & ... & fn +// ~ ~ f = f +// Forall v: f = ~ (Exists v: ~ f) +// Exists v: (f1 | ... | fn) = (Exists v: f1) | ... | (Exists v: fn) +// + +void Rel_Body::rearrange() { + assert(children().length()==1); + + skip_finalization_check++; + formula()->rearrange(); + skip_finalization_check--; + + if(pres_debug) { + fprintf(DebugFile, "\n=== Rearranged TREE ===\n"); + prefix_print(DebugFile); + } +} + +void Formula::rearrange() { + // copy list of children, as they may be removed as we work + List<Formula*> kiddies = myChildren; + + for(List_Iterator<Formula*> c(kiddies); c; c++) + (*c)->rearrange(); +} + +// +// Push nots down the tree until quantifier or conjunct, rearrange kids +// +void F_Not::rearrange() { + Formula *child = children().front(); + Formula *new_child, *f; + + switch(child->node_type()) { + case Op_Or: + parent().remove_child(this); + new_child = parent().add_and(); + while(!child->children().empty()) { + f = child->children().remove_front(); + F_Not *new_not = new_child->add_not(); + new_not->add_child(f); + } + delete this; + break; +//case Op_And: +// parent().remove_child(this); +// new_child = parent().add_or(); +// while(!child->myChildren.empty()) { +// f = child->myChildren.remove_front(); +// F_Not *new_not = new_child->add_not(); +// new_not->add_child(f); +// } +// delete this; +// break; + case Op_Not: + parent().remove_child(this); + f = child->children().remove_front(); + parent().add_child(f); + delete this; + f->rearrange(); + return; + default: + new_child = child; + break; + } + + new_child->rearrange(); +} + +// +// Convert a universal quantifier to "not exists not". +// Forall v: f = ~ (Exists v: ~ f) +// +void F_Forall::rearrange() { + Formula &p = parent(); + p.remove_child(this); + + F_Not *topnot = p.add_not(); + F_Exists *exist = topnot->add_exists(); + for (Variable_ID_Iterator VI(myLocals); VI; VI++) + (*VI)->set_kind(Exists_Var); + exist->myLocals.join(myLocals); + + F_Not *botnot = exist->add_not(); + Formula *f = children().remove_front(); + botnot->add_child(f); + + delete this; + + botnot->rearrange(); +} + +// +// Exists v: (f1 | ... | fn) = (Exists v: f1) | ... | (Exists v: fn) +// +void F_Exists::rearrange() { + Formula* child = children().front(); + switch(child->node_type()) { + case Op_Or: + case Op_Conjunct: + case Op_Exists: + child->push_exists(myLocals); + parent().remove_child(this); + parent().add_child(child); + children().remove_front(); + delete this; + break; + default: + break; + } + + child->rearrange(); +} + +} // namespace diff --git a/omegalib/omega/src/pres_subs.cc b/omegalib/omega/src/pres_subs.cc new file mode 100644 index 0000000..9854b09 --- /dev/null +++ b/omegalib/omega/src/pres_subs.cc @@ -0,0 +1,131 @@ +#include <omega/pres_subs.h> + +namespace omega { + +Substitutions::Substitutions(Relation &input_R, Conjunct *input_c) { + int i; + r = new Relation(input_R,input_c); + c = r->single_conjunct(); + c->reorder_for_print(); + c->ordered_elimination(r->global_decls()->length()); + int num_subs = c->problem->nSUBs; + subs = new eqn[num_subs]; + for(i = 0; i < num_subs; i++) + subs[i] = SUBs[i]; + subbed_vars.reallocate(num_subs); + /* Go through and categorize variables as: + 1) substituted, 2) not substituted, 3) wildcard + Safevars number of variables were not able to be substituted. + nVars number of total variables, including wildcards. + nSUBs is the number of substitutions. + nSUBs + nVars == the number of variables that went in. + Then reset var and forwardingAddress arrays in the problem, + so that they will correctly refer to the reconstructed + mappedVars. */ + Variable_ID_Tuple unsubbed_vars(c->problem->safeVars); + for(i = 1; i <= c->mappedVars.size(); i++) + if(c->mappedVars[i]->kind() != Wildcard_Var) { + int addr = c->problem->forwardingAddress[i]; + assert(addr == c->find_column(c->mappedVars[i]) && addr != 0); + if(addr < 0) { + assert(-addr <= subbed_vars.size()); + subbed_vars[-addr] = c->mappedVars[i]; + } + else { + assert(addr <= unsubbed_vars.size()); + unsubbed_vars[addr] = c->mappedVars[i]; + } + } + else { + // Here we don't redeclare wildcards, just re-use them. + unsubbed_vars.append(c->mappedVars[i]); + } + assert(unsubbed_vars.size() + subbed_vars.size() == c->mappedVars.size()); + c->mappedVars = unsubbed_vars; /* These are the variables that remain */ + + for(int col = 1; col <= c->problem->nVars; col++) { + c->problem->var[col] = col; + c->problem->forwardingAddress[col] = col; + } +} + +Substitutions::~Substitutions() { + delete [] subs; + delete r; +} + +bool Substitutions::substituted(Variable_ID v) { + return (subbed_vars.index(v) > 0); +} + +Sub_Handle Substitutions::get_sub(Variable_ID v) { + assert(substituted(v) && "No substitution for variable"); + return Sub_Handle(this,subbed_vars.index(v)-1,v); +} + +bool Substitutions::sub_involves(Variable_ID v, Var_Kind kind) { + assert(substituted(v)); + for(Constr_Vars_Iter i = get_sub(v); i; i++) + if ((*i).var->kind() == kind) + return true; + return false; +} + + +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + +int Sub_Iterator::live() const { + return current <= last; +} + +void Sub_Iterator::operator++() { this->operator++(0); } + +void Sub_Iterator::operator++(int) { + current++; +} + +Sub_Handle Sub_Iterator::operator*() { + assert(s && current <= last && "Sub_Iterator::operator*: bad call"); + return Sub_Handle(s,current,s->subbed_vars[current+1]); +} + +Sub_Handle Sub_Iterator::operator*() const { + assert(s && current <= last && "Sub_Iterator::operator*: bad call"); + return Sub_Handle(s,current,s->subbed_vars[current+1]); +} + + +//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + + +Sub_Handle::Sub_Handle(Substitutions *_s, int _e, Variable_ID _v) : +Constraint_Handle(_s->c,&(_s->subs),_e), v(_v) {} + +std::string Sub_Handle::print_to_string() const { + relation()->setup_names(); + return v->name() + " = " + this->print_term_to_string(); +} + +std::string Sub_Handle::print_term_to_string() const { + /* The horrible truth is that print_term_to_string is a member + function of Conjunct, (and then Problem below it) but uses + nothing from there but the names, so you can pass it a pointer to + an equation that isn't even part of the conjunct. */ + relation()->setup_names(); + return c->print_term_to_string(&((*eqns)[e])); +} + + +/* + String Sub_Handle::print_to_string() const { + return c->problem->print_EQ_to_string(&((*eqns)[e])); + } + + String Sub_Handle::print_term_to_string() const { + return c->print_term_to_string(&(*eqns[e])); + } +*/ + +} // namespace diff --git a/omegalib/omega/src/pres_var.cc b/omegalib/omega/src/pres_var.cc new file mode 100644 index 0000000..0ec406f --- /dev/null +++ b/omegalib/omega/src/pres_var.cc @@ -0,0 +1,459 @@ +#include <omega/pres_var.h> +#include <omega/pres_tree.h> +#include <omega/pres_conj.h> +#include <omega/omega_i.h> + +namespace omega { + +int wildCardInstanceNumber; + +const int Global_Input_Output_Tuple::initial_allocation = 10; + +// Declare named Variable +Var_Decl::Var_Decl(Const_String name, Var_Kind vkind, int pos): + base_name(name), + instance(999), + remap(this), + var_kind(vkind), + position(pos), + global_var(NULL), + of((Argument_Tuple) 0) { + assert(((vkind==Input_Var || vkind==Output_Var) && pos>0) || pos==0); +} + +// Declare unnamed variable +Var_Decl::Var_Decl(Var_Kind vkind, int pos): + instance(999), + remap(this), + var_kind(vkind), + position(pos), + global_var(NULL), + of((Argument_Tuple) 0) { + assert(((vkind==Input_Var || vkind==Output_Var) && pos>0) || pos==0); +} + +// Copy variable declaration +Var_Decl::Var_Decl(Variable_ID v): + base_name(v->base_name), + instance(v->instance), + remap(this), + var_kind(v->var_kind), + position(v->position), + global_var(v->global_var), + of(v->of) { +} + +Var_Decl::Var_Decl(Const_String name, Global_Var_ID v): + base_name(name), + instance(999), + remap(this), + var_kind(Global_Var), + position(0), + global_var(v), + of((Argument_Tuple) 0) { + assert(v->arity() == 0); +} + +Var_Decl::Var_Decl(Const_String name, Global_Var_ID v, Argument_Tuple function_of): + base_name(name), + instance(999), + remap(this), + var_kind(Global_Var), + position(0), + global_var(v), + of(function_of) { +} + +int Var_Decl::get_position() { + assert((var_kind == Input_Var || var_kind == Output_Var) && "Var_Decl::get_position: bad var_kind"); + return(position); +} + +Global_Var_ID Var_Decl::get_global_var() { + assert(var_kind == Global_Var && "Var_Decl::get_global_var: bad var_kind"); + return(global_var); +} + +Argument_Tuple Var_Decl::function_of() { + assert(var_kind == Global_Var); + return(of); +} + + +Omega_Var *Global_Var_Decl::really_omega_var() { + assert(0); + return(NULL); +} + +Coef_Var_Decl *Global_Var_Decl::really_coef_var() { + assert(0); + return(NULL); +} + +// +// Variable name. +// + +const int N_greek_letters = 19; + +char greek_letters[19][10] = { + "alpha" , "beta" , "gamma" , "delta" , "tau" , "sigma" , "chi" , + "omega" , "pi" , "ni" , "Alpha" , "Beta" , "Gamma" , "Delta" , + "Tau" , "Sigma" , "Chi" , "Omega" , "Pi" +}; + +const int numBuffers = 50; +const int bufferSize = 90; +char nameBuffers[numBuffers][bufferSize]; +int nextBuffer = 0; + +int use_ugly_names = 0; + +const char *Var_Decl::char_name() { + char *s = nameBuffers[nextBuffer++]; + char *start = s; + if (nextBuffer >= numBuffers) nextBuffer = 0; + int primes; + + if (use_ugly_names) + primes = 0; + else + primes = instance; + + switch(var_kind) { + case Input_Var: + if (!use_ugly_names && !base_name.null()) + sprintf(s,"%s",(const char *)base_name); + else { + primes = 0; + sprintf(s,"In_%d",position); + } + break; + case Output_Var: + if (!use_ugly_names && !base_name.null()) + sprintf(s,"%s",(const char *)base_name); + else { + primes = 0; + sprintf(s,"Out_%d",position); + } + break; + case Global_Var: + assert(!base_name.null()); + if (use_ugly_names) + sprintf(s,"%s@%p",(const char *)base_name, this); + else { + sprintf(s,"%s",(const char *)base_name); + primes = get_global_var()->instance; + } + break; + default: + if (use_ugly_names) { + if (!base_name.null()) + sprintf(s,"%s@%p",(const char *)base_name, this); + else + sprintf(s,"?@%p", this); + } + else if (!base_name.null()) sprintf(s,"%s",(const char *)base_name); + else { + assert(instance < 999); + sprintf(s,"%s", + greek_letters[instance % N_greek_letters]); + primes = instance/N_greek_letters; + } + break; + } + + while (*s) s++; + int i; + assert(primes < 999); + for(i=1; i<= primes; i++) *s++ = '\''; + *s = '\0'; + if (var_kind == Global_Var) { + int a = get_global_var()->arity(); + if (a) { + if (use_ugly_names) { + static const char *arg_names[4] = { "???", "In", "Out", "In == Out" }; + sprintf(s, "(%s[1#%d])", arg_names[function_of()], a); + } + else { + int f_of = function_of(); + assert(f_of == Input_Tuple || f_of == Output_Tuple); + *s++ = '('; + for(i = 1;i<=a;i++) { + if (f_of == Input_Tuple) + sprintf(s,"%s",(const char *)input_vars[i]->char_name()); + else + sprintf(s,"%s",(const char *)output_vars[i]->char_name()); + while (*s) s++; + if (i<a) *s++ = ','; + } + *s++ = ')'; + *s++ = 0; + } + } + } + + assert(s < start+bufferSize); + return start; +} + +std::string Var_Decl::name() { + return char_name(); +} + +// +// Copy variable declarations. +// +void copy_var_decls(Variable_ID_Tuple &new_vl, Variable_ID_Tuple &vl) { + if (new_vl.size() < vl.size()) { + new_vl.reallocate(vl.size()); + } + for(int p=1; p<=vl.size(); p++) { + Variable_ID v = vl[p]; + if(v->kind()==Global_Var) { + new_vl[p] = v; + v->remap = v; + } + else { + new_vl[p] = new Var_Decl(vl[p]); + v->remap = new_vl[p]; + } + } +} + +// +// Name a variable. +// +void Var_Decl::name_variable(char *newname) { + base_name = newname; +} + + +static Const_String coef_var_name(int i, int v) { + char s[100]; + sprintf(s, "c_%d_%d", i, v); + return Const_String(s); +} + + +// +// Global variables stuff. +// +Global_Var_Decl::Global_Var_Decl(Const_String baseName): + loc_rep1(baseName, this, Input_Tuple), + loc_rep2(baseName, this, Output_Tuple) { +} + +Global_Kind Global_Var_Decl::kind() const { + assert(0); + return Coef_Var; +} + +Coef_Var_Decl::Coef_Var_Decl(int id, int var): + Global_Var_Decl(coef_var_name(id, var)) { + i = id; + v = var; +} + +Coef_Var_Decl *Coef_Var_Decl::really_coef_var() { + return this; +} + +int Coef_Var_Decl::stmt() const { + return i; +} + +int Coef_Var_Decl::var() const { + return v; +} + +Global_Kind Coef_Var_Decl::kind() const { + return Coef_Var; +} + +Free_Var_Decl::Free_Var_Decl(Const_String name): + Global_Var_Decl(name), _arity(0) { +} + +Free_Var_Decl::Free_Var_Decl(Const_String name, int arity): + Global_Var_Decl(name), _arity(arity) { +} + +int Free_Var_Decl::arity() const { + return _arity; +} + +Global_Kind Free_Var_Decl::kind() const { + return Free_Var; +} + + +// +// Delete variable from variable list. Does not preserve order +// +bool rm_variable(Variable_ID_Tuple &vl, Variable_ID v) { + int n = vl.size(); + int i; + for(i = 1; i<n; i++) if (vl[i] == v) break; + if (i>n) return 0; + vl[i] = vl[n]; + vl.delete_last(); + return 1; +} + + +// +// Destroy variable declarations. +// +void free_var_decls(Variable_ID_Tuple &c) { + for(Variable_ID_Iterator p = c; p; p++) { + Variable_ID v = *p; + if(v->kind()!=Global_Var) { + delete v; + } + } +} + + +Variable_ID input_var(int nth) { + assert(1 <= nth && nth <= input_vars.size()); + return input_vars[nth]; +} + +Variable_ID output_var(int nth) { + assert(1<= nth && nth <= output_vars.size()); + return output_vars[nth]; +} + +Variable_ID set_var(int nth) { + assert(1 <= nth && set_vars.size()); + return input_vars[nth]; +} + + +// +// Remap mappedVars in all conjuncts of formula. +// Uses the remap field of the Var_Decl. +// +void Formula::remap() { + for(List_Iterator<Formula*> c(children()); c; c++) + (*c)->remap(); +} + +void Conjunct::remap() { + for(Variable_Iterator VI(mappedVars); VI; VI++) { + Variable_ID v = *VI; + *VI = v->remap; + } + cols_ordered = false; +} + +// Function to reset the remap field of a Variable_ID +void reset_remap_field(Variable_ID v) { + v->remap = v; +} + +// Function to reset the remap fields of a Variable_ID_Seq +void reset_remap_field(Sequence<Variable_ID> &T) { + for(Any_Iterator<Variable_ID> VI(T.any_iterator()); VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + } +} + +// Function to reset the remap fields of a Variable_ID_Tuple, +// more efficiently +void reset_remap_field(Variable_ID_Tuple &T) { + for(Variable_Iterator VI(T); VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + } +} + +void reset_remap_field(Sequence<Variable_ID> &T, int var_no) { + int i=1; + for(Any_Iterator<Variable_ID> VI(T.any_iterator()); i <= var_no && VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + i++; + } +} + +void reset_remap_field(Variable_ID_Tuple &T, int var_no) { + int i=1; + for(Variable_Iterator VI(T); i <= var_no && VI; VI++) { + Variable_ID v = *VI; + v->remap = v; + i++; + } +} + + +// Global input and output variable tuples. +// These Tuples must be initialized as more in/out vars are needed. +//Variable_ID_Tuple input_vars(0); +//Variable_ID_Tuple output_vars(0); +//Variable_ID_Tuple& set_vars = input_vars; +Global_Input_Output_Tuple input_vars(Input_Var); +Global_Input_Output_Tuple output_vars(Output_Var); +Global_Input_Output_Tuple &set_vars = input_vars; + +//////////////////////////////////////// +// // +// Variable and Declaration functions // +// // +//////////////////////////////////////// + +// +// Constructors and properties. +// + + +// Allocate ten variables initially. Most applications won't require more. +Global_Input_Output_Tuple::Global_Input_Output_Tuple(Var_Kind in_my_kind, int init): + my_kind(in_my_kind) { + for (int i=1; i<=(init == -1? initial_allocation: max(0,init)); i++) + this->append(new Var_Decl(Const_String(), my_kind, i)); +} + +Global_Input_Output_Tuple::~Global_Input_Output_Tuple() { + for (int i=1; i<=size(); i++) + delete data[i-1]; +} + +Variable_ID & Global_Input_Output_Tuple::operator[](int index) { + assert(index > 0); + while(size() < index) + this->append(new Var_Decl(Const_String(), my_kind, size()+1)); + return data[index-1]; +} + +const Variable_ID & Global_Input_Output_Tuple::operator[](int index) const { + assert(index > 0); + Global_Input_Output_Tuple *unconst_this = (Global_Input_Output_Tuple *) this; + while(size() < index) + unconst_this->append(new Var_Decl(Const_String(), my_kind, size()+1)); + return data[index-1]; +} + +Variable_ID Var_Decl::UF_owner() { + Variable_ID v = this; + while (v->remap != v) v = v->remap; + return v; +} + +void Var_Decl::UF_union(Variable_ID b) { + Variable_ID a = this; + while (a->remap != a) { + Variable_ID tmp = a->remap; + a->remap = tmp->remap; + a = tmp; + } + while (b->remap != a) { + Variable_ID tmp = b->remap; + b->remap = a; + b = tmp; + } +} + +} // namespace diff --git a/omegalib/omega/src/reach.cc b/omegalib/omega/src/reach.cc new file mode 100644 index 0000000..bde785c --- /dev/null +++ b/omegalib/omega/src/reach.cc @@ -0,0 +1,211 @@ +#include <omega.h> +#include <omega/Relations.h> +#include <basic/Dynamic_Array.h> +#include <omega/reach.h> + +namespace omega { + +typedef Dynamic_Array1<Relation> Rel_Array1; +typedef Dynamic_Array2<Relation> Rel_Array2; + +// This is from parallelism.c, modified + +static void closure_rel(Rel_Array2 &trans, int i, int k, int j) { + Relation tik; + + if (trans[k][k].is_upper_bound_satisfiable()) { + Relation tkk = TransitiveClosure(copy(trans[k][k])); + tkk.simplify(2,4); + tik = Composition(tkk, copy(trans[i][k])); + tik.simplify(2,4); + tik = Union(copy(trans[i][k]), tik); + tik.simplify(2,4); + } + else { + tik = trans[i][k]; + } + Relation fresh; + Relation tkj = trans[k][j]; + fresh = Composition(tkj, tik); + fresh.simplify(2,4); + trans[i][j] = Union(trans[i][j], fresh); + trans[i][j].simplify(2,4); + +#if 0 + fprintf(DebugFile, "%d -> %d -> %d\n", i, k, j); + trans[i][j].print_with_subs(DebugFile); +#endif +} + + +static void close_rels(Rel_Array2 &trans,int n_nodes) { + for (int k=1; k<=n_nodes; k++) + for (int i=1; i<=n_nodes; i++) + if (trans[i][k].is_upper_bound_satisfiable()) + for (int j=1; j<=n_nodes; j++) + if (trans[k][j].is_upper_bound_satisfiable()) + closure_rel(trans, i, k, j); +} + + +void dump_rels(Rel_Array2 &a, reachable_information *reachable_info) { + int i,j; + int n_nodes = reachable_info->node_names.size(); + for(i = 1; i <= n_nodes; i++) + for(j = 1; j <= n_nodes; j++) { + fprintf(stderr,"t[%s][%s] = ", + (reachable_info->node_names[i]).c_str(), + (reachable_info->node_names[j]).c_str()); + a[i][j].print_with_subs(stderr); + } +} + + +void dump_sets(Rel_Array1 &a, reachable_information *reachable_info) { + int i; + int n_nodes = reachable_info->node_names.size(); + for(i = 1; i <= n_nodes; i++) { + fprintf(stderr,"r[%s] = ", (reachable_info->node_names[i]).c_str()); + a[i].print_with_subs(stderr); + } +} + + + +Rel_Array1 *Reachable_Nodes(reachable_information *reachable_info) { + Tuple<std::string> &node_names = reachable_info->node_names; + Tuple<int> &arity = reachable_info->node_arity; + Rel_Array2 &transitions = reachable_info->transitions; + Rel_Array1 &start_nodes = reachable_info->start_nodes; + + int n_nodes = node_names.size(),i,j; + +#define DUMP_INITIAL 1 +#define DUMP_CLOSED 1 + + if(DUMP_INITIAL && relation_debug){ + fprintf(stderr,"Initially:\n"); + dump_rels(transitions, reachable_info); + } + + close_rels(transitions,n_nodes); + + if(DUMP_CLOSED && relation_debug) { + fprintf(stderr,"Closed:\n"); + dump_rels(transitions, reachable_info); + } + + Rel_Array1 *finalp = + new Rel_Array1("node"); + Rel_Array1 &final = *finalp; + final.resize(n_nodes+1); + for (i=1; i<=n_nodes; i++) + final[i] = Relation::False(arity[i]); + + for(i = 1; i <= n_nodes; i++) + for(j = 1; j <= n_nodes; j++) + if(start_nodes[i].is_upper_bound_satisfiable()) + final[j] = Union(final[j], + Composition(copy(transitions[i][j]), + copy(start_nodes[i]))); + return finalp; +} + +static void compute_initially_reachable(Rel_Array1 &r, + Rel_Array1 &start_nodes, + Rel_Array2 &, + Rel_Array2 &closed, + Rel_Array1 &end_nodes, + int n_nodes, Tuple<int> &arity){ + for(int n = 1; n <= n_nodes; n++) + r[n] = Relation::False(arity[n]); + + for(int i = 1; i <= n_nodes; i++) + for(int j = 1; j <= n_nodes; j++) + r[i] = Union(r[i], + Range(Restrict_Domain( + Restrict_Range(copy(closed[j][i]), + copy(end_nodes[i])), + copy(start_nodes[j])))); +} + + +static bool iterate(Rel_Array1 &r, Rel_Array2 &, Rel_Array2 &closed, + Rel_Array1 &, int n_nodes) { + bool changed; + + changed = false; + for(int j = 1; j <= n_nodes; j++) { + for(int i = 1; i <= n_nodes; i++) { + /* look for additional steps from interesting states */ + Relation new_rj = Range(Restrict_Domain(copy(closed[i][j]), + copy(r[i]))); + if(!Must_Be_Subset(copy(new_rj),copy(r[j]))) { + r[j] = Union(r[j],new_rj); + r[j].simplify(2,2); + changed = true; + } + } + } + return changed; +} + + + + +Rel_Array1 *I_Reachable_Nodes(reachable_information *reachable_info) { + bool changed; + + Tuple<std::string> &node_names = reachable_info->node_names; + int n_nodes = node_names.size(); + Tuple<int> &arity = reachable_info->node_arity; + Rel_Array2 &transitions = reachable_info->transitions; + Rel_Array1 &start_nodes = reachable_info->start_nodes; + Rel_Array2 closed("node number","node number"); + closed.resize(n_nodes+1,n_nodes+1); // abuse of dynamic arrays + Rel_Array1 *rp = new Rel_Array1("node number"); + Rel_Array1 &r = *rp; + r.resize(n_nodes+1); // abuse of dynamic arrays + + int i,j; + + Rel_Array1 end_nodes("Hi!"); + end_nodes.resize(n_nodes+1); // for future use + for(int n = 1; n <= n_nodes; n++) end_nodes[n] = Relation::True(arity[n]); + + for(j = 1; j <= n_nodes; j++) { + closed[j][j] = TransitiveClosure(copy(transitions[j][j])); + for(i = 1; i <= n_nodes; i++) + if (i != j) + closed[i][j] = transitions[i][j]; + } + + compute_initially_reachable(r,start_nodes,transitions,closed,end_nodes, + n_nodes,arity); + +#define DUMP_INITIAL 1 +#define DUMP_CLOSED 1 + + if(DUMP_INITIAL && relation_debug > 1) { + fprintf(stderr,"Closed:\n"); + dump_rels(closed, reachable_info); + } + if(DUMP_INITIAL && relation_debug) { + fprintf(stderr,"start nodes:\n"); + dump_sets(start_nodes, reachable_info); + fprintf(stderr,"Initially reachable:\n"); + dump_sets(r, reachable_info); + } + + changed = true; + int iterations = 0, max_iterations = 1000; + while(changed && iterations < max_iterations) { + changed = iterate(r,transitions,closed,start_nodes,n_nodes); + iterations++; + } + if(relation_debug) + fprintf(stdout,"[Iterations to convergence: %d]\n",iterations); + return rp; +} + +} // namespace |