/* Cadabra: a field-theory motivated computer algebra system. Copyright (C) 2001-2014 Kasper Peeters This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* length vector normal combinations: one element, value=total length. normal permutations: n elements, each equal to 1. */ #pragma once #include #include #include #include #include #include namespace combin { typedef std::vector range_t; typedef std::vector range_vector_t; typedef std::vector weights_t; unsigned long factorial(unsigned int x); /// sum of elements long vector_sum(const std::vector&); /// product of elements unsigned long vector_prod(const std::vector&); /// product of factorials of elements unsigned long vector_prod_fact(const std::vector&); bool operator==(const std::vector&, const std::vector&); /// compute a hash value for a vector of unsigned ints long hash(const std::vector&); template class combinations_base { public: combinations_base(); combinations_base(const std::vector&); virtual ~combinations_base(); void permute(long start=-1, long end=-1); virtual void clear(); virtual void clear_results(); unsigned int sum_of_sublengths() const; void set_unit_sublengths(); unsigned int multiplier(const std::vector&) const; unsigned int total_permutations() const; // including the ones not stored enum weight_cond { weight_equals, weight_less, weight_greater }; unsigned int block_length; std::vector sublengths; range_vector_t input_asym; std::vector original; bool multiple_pick; std::vector weights; std::vector max_weights; std::vector weight_conditions; unsigned int sub_problem_blocksize; // when non-zero, do permutations within protected: virtual void vector_generated(const std::vector&)=0; virtual bool entry_accepted(unsigned int current) const; std::vector temparr; long start_, end_, vector_generated_called_; std::vector current_weight; private: bool is_allowed_by_weight_constraints(unsigned int i); bool final_weight_constraints_check() const; void update_weights(unsigned int i); void restore_weights(unsigned int i); void nextstep(unsigned int current, unsigned int fromalgehad, unsigned int groupindex, std::vector algehad); }; template class combinations : public combinations_base { public: typedef typename std::vector > permuted_sets_t; typedef typename permuted_sets_t::const_iterator const_iterator; combinations(); combinations(const std::vector&); virtual ~combinations(); virtual void clear(); virtual void clear_results(); const std::vector& operator[](unsigned int) const; int ordersign(unsigned int) const; unsigned int size() const; unsigned int multiplier(unsigned int) const; protected: virtual void vector_generated(const std::vector&); private: permuted_sets_t storage; }; template class symmetriser; template class symm_helper : public combinations_base { public: symm_helper(symmetriser&); virtual void clear(); int current_multiplicity; protected: bool first_one; symmetriser& owner_; virtual void vector_generated(const std::vector&); }; template class symm_val_helper : public combinations_base { public: symm_val_helper(symmetriser&); virtual void clear(); int current_multiplicity; protected: bool first_one; symmetriser& owner_; virtual void vector_generated(const std::vector&); }; template class symmetriser { public: symmetriser(); void apply_symmetry(long start=-1, long end=-1); std::vector original; unsigned int block_length; std::vector permute_blocks; // offset in unit elements! (not in blocks) std::vector value_permute; int permutation_sign; std::vector sublengths; // refers to position within permute_blocks range_vector_t input_asym; // as in combinations_base range_vector_t sublengths_scattered; // sublengths, in original, but not connected. /// Convert vectors of values to vectors of locations in the original /// (mainly useful to create input_asym for permutation by value). range_t values_to_locations(const std::vector& values) const; const std::vector& operator[](unsigned int) const; int signature(unsigned int) const; void set_multiplicity(unsigned int pos, int val); unsigned int size() const; void clear(); /// Collect equal entries, and adjust the multiplier field accordingly. void collect(); void remove_multiplicity_zero(); friend class symm_helper; friend class symm_val_helper; private: symm_helper sh_; symm_val_helper svh_; unsigned int current_; std::vector > originals; std::vector multiplicity; }; int determine_intersection_ranges(const range_vector_t& prod, const range_vector_t& indv, range_vector_t& target); template int ordersign(iterator1 b1, iterator1 e1, iterator2 b2, iterator2 e2, int stepsize=1); template int ordersign(iterator1 b1, iterator1 e1); template T fact(T x); template std::ostream& operator<<(std::ostream& str, const symmetriser& sym); /* I assume PI consists of the integers 1 to N. It can be done with O(N) comparisons and transpositions of integers in the list. sign:= 1; for i from 1 to N do while PI[i] <> i do interchange PI[i] and PI[PI[i]]; sign:= -sign od od */ template int ordersign(iterator1 b1, iterator1 e1, iterator2 b2, iterator2 e2, int stepsize) { int sign=1; std::vector crossedoff(std::distance(b1,e1),false); while(b1!=e1) { int otherpos=0; iterator2 it=b2; while(it!=e2) { if( (*it)==(*b1) && crossedoff[otherpos]==false) { crossedoff[otherpos]=true; break; } else { if(!crossedoff[otherpos]) sign=-sign; } it+=stepsize; ++otherpos; } b1+=stepsize; } return sign; } //template //int ordersign(iterator1 b1, iterator1 e1, iterator2 b2, iterator2 e2, comparator cmp, int stepsize) // { // int sign=1; // std::vector crossedoff(std::distance(b1,e1),false); // while(b1!=e1) { // int otherpos=0; // iterator2 it=b2; // while(it!=e2) { // if(cmp((*it), (*b1)) && crossedoff[otherpos]==false) { // crossedoff[otherpos]=true; // break; // } // else { // if(!crossedoff[otherpos]) // sign=-sign; // } // it+=stepsize; // ++otherpos; // } // b1+=stepsize; // } // return sign; // } template int ordersign(iterator1 b1, iterator1 e1) { std::vector fil; for(int k=0; k T fact(T x) { T ret=1; assert(x>=0); while(x!=0) { ret*=x--; } return ret; } // Implementations template combinations_base::combinations_base() : block_length(1), multiple_pick(false), sub_problem_blocksize(0) { } template combinations_base::combinations_base(const std::vector& oa) : block_length(1), original(oa), multiple_pick(false), sub_problem_blocksize(0) { } template combinations::combinations() : combinations_base() { } template combinations::combinations(const std::vector& oa) : combinations_base(oa) { } template combinations_base::~combinations_base() { } template combinations::~combinations() { } template void combinations::vector_generated(const std::vector& toadd) { ++this->vector_generated_called_; if((this->start_==-1 || this->vector_generated_called_ >= this->start_) && (this->end_==-1 || this->vector_generated_called_ < this->end_)) { std::vector newone(toadd.size()*this->block_length); for(unsigned int i=0; iblock_length; ++bl) newone[i*this->block_length+bl]=this->original[toadd[i]*this->block_length+bl]; storage.push_back(newone); } } template bool combinations_base::entry_accepted(unsigned int) const { return true; } template void combinations_base::permute(long start, long end) { start_=start; end_=end; vector_generated_called_=-1; // Initialise weight handling. current_weight.clear(); current_weight.resize(weights.size(), 0); for(unsigned int i=0; i0) { if(weight_conditions.size()==0) weight_conditions.resize(weights.size(), weight_equals); else assert(weight_conditions.size()==weights.size()); } else assert(weight_conditions.size()==0); // Sublength handling. assert(sublengths.size()!=0); unsigned int len=sum_of_sublengths(); // Consistency checks. assert(original.size()%block_length==0); if(!multiple_pick) assert(len*block_length<=original.size()); for(unsigned int i=0; iinput_asym.size(); ++i) std::sort(this->input_asym[i].begin(), this->input_asym[i].end()); temparr=std::vector(len/* *block_length*/); std::vector algehad(original.size()/block_length,false); nextstep(0,0,0,algehad); } template void combinations_base::clear() { block_length=1; sublengths.clear(); this->input_asym.clear(); original.clear(); weights.clear(); max_weights.clear(); weight_conditions.clear(); sub_problem_blocksize=0; temparr.clear(); current_weight.clear(); } template void combinations_base::clear_results() { temparr.clear(); } template void combinations::clear() { storage.clear(); combinations_base::clear(); } template void combinations::clear_results() { storage.clear(); combinations_base::clear_results(); } template const std::vector& combinations::operator[](unsigned int i) const { assert(i unsigned int combinations::size() const { return storage.size(); } template unsigned int combinations_base::sum_of_sublengths() const { unsigned int ret=0; for(unsigned int i=0; i unsigned int combinations_base::total_permutations() const { return vector_generated_called_+1; } template void combinations_base::set_unit_sublengths() { sublengths.clear(); for(unsigned int i=0; i int combinations::ordersign(unsigned int num) const { assert(numblock_length); } template unsigned int combinations::multiplier(unsigned int num) const { return combinations_base::multiplier(this->storage[num]); } template unsigned int combinations_base::multiplier(const std::vector& stor) const { unsigned long numerator=1; for(unsigned int i=0; iinput_asym.size(); ++i) numerator*=fact(this->input_asym[i].size()); unsigned long denominator=1; for(unsigned int i=0; iinput_asym.size(); ++i) { // for each input asym, and for each output asym, count // the number of overlap elements. unsigned int current=0; for(unsigned int k=0; ksublengths.size(); ++k) { if(this->sublengths[k]>1) { unsigned int overlap=0; for(unsigned int slc=0; slcsublengths[k]; ++slc) { for(unsigned int j=0; jinput_asym[i].size(); ++j) { unsigned int index=0; while(!(stor[current]==this->original[index])) ++index; if(index==this->input_asym[i][j]) ++overlap; } ++current; } if(overlap>0) denominator*=fact(overlap); // FIXME: for each overlap thus found, divide out by a factor // due to the fact that output asym ranges can overlap. // well, that's not right either. } else ++current; } } return numerator/denominator; } template bool combinations_base::is_allowed_by_weight_constraints(unsigned int i) { if(weights.size()==0) return true; for(unsigned int cn=0; cn= max_weights[cn]) return false; } return true; } template bool combinations_base::final_weight_constraints_check() const { for(unsigned int cn=0; cn void combinations_base::update_weights(unsigned int i) { if(weights.size()==0) return; for(unsigned int cn=0; cn void combinations_base::restore_weights(unsigned int i) { if(weights.size()==0) return; for(unsigned int cn=0; cn void combinations_base::nextstep(unsigned int current, unsigned int lowest_in_group, unsigned int groupindex, std::vector algehad) { unsigned int grouplen=0; for(unsigned int i=0; i<=groupindex; ++i) grouplen+=sublengths[i]; if(current==grouplen) { // group is filled ++groupindex; if(groupindex==sublengths.size()) { if(final_weight_constraints_check()) vector_generated(temparr); return; } lowest_in_group=0; } unsigned int starti=0, endi=original.size()/block_length; if(sub_problem_blocksize>0) { starti=current-current%sub_problem_blocksize; endi=starti+sub_problem_blocksize; } for(unsigned int i=starti; iinput_asym.size(); ++k) { for(unsigned int kk=0; kkinput_asym[k].size(); ++kk) { if(i==this->input_asym[k][kk]) { unsigned int k2=kk; while(k2!=0) { --k2; if(!algehad[this->input_asym[k][k2]]) { // std::cout << "discarding " << std::endl; discard=true; break; } } } } if(discard) break; } } else discard=true; if(!discard) if(i+1>lowest_in_group) { algehad[i]=true; update_weights(i); temparr[current]=i; // for(unsigned bl=0; bl symmetriser::symmetriser() : block_length(1), permutation_sign(1), sh_(*this), svh_(*this) { } template void symmetriser::clear() { original.clear(); block_length=1; permute_blocks.clear(); value_permute.clear(); permutation_sign=1; sublengths.clear(); input_asym.clear(); sublengths_scattered.clear(); originals.clear(); multiplicity.clear(); } template void symmetriser::collect() { std::cout << "collecting" << std::endl; // Fill the hash map: entries which are equal have to sit in the same // bin, but there may be other entries in that bin which still have to // be separated. std::multimap hashmap; for(unsigned int i=0; i(hash(originals[i]), i)); // Collect equal vectors. std::multimap::iterator it=hashmap.begin(), thisbin1, thisbin2, tmpit; while(it!=hashmap.end()) { long current_hash=it->first; thisbin1=it; while(thisbin1!=hashmap.end() && thisbin1->first==current_hash) { thisbin2=thisbin1; ++thisbin2; while(thisbin2!=hashmap.end() && thisbin2->first==current_hash) { if(originals[(*thisbin1).second]==originals[(*thisbin2).second]) { multiplicity[(*thisbin1).second]+=multiplicity[(*thisbin2).second]; multiplicity[(*thisbin2).second]=0; tmpit=thisbin2; ++tmpit; hashmap.erase(thisbin2); thisbin2=tmpit; } else ++thisbin2; } ++thisbin1; } it=thisbin1; } remove_multiplicity_zero(); } template void symmetriser::remove_multiplicity_zero() { std::vector > new_originals; std::vector new_multiplicity; for(unsigned int k=0; k void symmetriser::apply_symmetry(long start, long end) { unsigned int current_length=originals.size(); if(current_length==0) { originals.push_back(original); multiplicity.push_back(1); current_length=1; } // Some options are mutually exclusive. assert(permute_blocks.size()>0 || value_permute.size()>0); assert(sublengths.size()==0 || sublengths_scattered.size()==0); if(permute_blocks.size()==0) { // permute by value assert(value_permute.size()!=0); if(input_asym.size()==0 && sublengths_scattered.size()==0) { // When permuting by value, we can do the permutation once, // and then figure out (see vector_generated of symm_val_helper), // for each permutation which is already stored in the symmetriser, // how the objects are moved. current_=current_length; svh_.clear(); svh_.original=value_permute; svh_.input_asym.clear(); svh_.sublengths=sublengths; svh_.current_multiplicity=combin::vector_prod_fact(sublengths); if(svh_.sublengths.size()==0) svh_.set_unit_sublengths(); svh_.permute(start, end); // Since we do not divide by the number of permutations, we need // to adjust the multiplicity of all the originals. // for(unsigned int i=0; i my_permute_blocks; // Determine the location of the values. for(unsigned int k=0; k0) { // Re-order my_permute_blocks in such a way that the objects which sit // in one sublength_scattered range are consecutive. This does not make // any difference for the sign. sh_.sublengths.clear(); std::vector reordered_permute_blocks; for(unsigned int m=0; m::iterator it=my_permute_blocks.begin(); while(it!=my_permute_blocks.end()) { if((*it)==sublengths_scattered[m][mm]) { // std::cout << " found " << std::endl; reordered_permute_blocks.push_back(*it); my_permute_blocks.erase(it); ++overlap; break; } ++it; } // std::cout << std::endl; } if(overlap>0) sh_.sublengths.push_back(overlap); } std::vector::iterator it=my_permute_blocks.begin(); while(it!=my_permute_blocks.end()) { reordered_permute_blocks.push_back(*it); // std::cout << "adding one" << std::endl; sh_.sublengths.push_back(1); ++it; } my_permute_blocks=reordered_permute_blocks; // std::cout << "handled sublengths" << std::endl; } // Put to-be-permuted data in originals. for(unsigned int k=0; k0) { // Make a proper input_asym which refers to object locations // in the permute blocks array, rather than in the original // array. for(unsigned int k=0; k1) { subprob_input_asym.push_back(newrange); sh_.current_multiplicity*=fact(newrange.size()); } } } if(sh_.sublengths.size()==0) sh_.set_unit_sublengths(); sh_.current_multiplicity*=combin::vector_prod_fact(sh_.sublengths); // debugging // std::cout << "my_permute_blocks: "; // for(unsigned int ii=0; ii0) { // for(unsigned int k=0; k0); // When permuting by location, we have to apply the permutation // algorithm separately to each and every permutation which is // already stored in the symmetriser. for(unsigned int i=0; i const std::vector& symmetriser::operator[](unsigned int i) const { assert(i unsigned int symmetriser::size() const { return originals.size(); } template range_t symmetriser::values_to_locations(const std::vector& values) const { range_t ret; for(unsigned int i=0; i symm_val_helper::symm_val_helper(symmetriser& tt) : current_multiplicity(1), first_one(true), owner_(tt) { } template void symm_val_helper::clear() { first_one=true; combinations_base::clear(); } template void symm_val_helper::vector_generated(const std::vector& vec) { ++this->vector_generated_called_; if(first_one) { first_one=false; } else { if((this->start_==-1 || this->vector_generated_called_ >= this->start_) && (this->end_==-1 || this->vector_generated_called_ < this->end_)) { // Since we permuted by value, we can do this permutation in one // shot on all previously generated sets. for(unsigned int i=0; ioriginal[j]) { owner_.originals[loc][k]=this->original[vec[j]]; break; } } } } } } } template symm_helper::symm_helper(symmetriser& tt) : current_multiplicity(1), first_one(true), owner_(tt) { } template void symm_helper::clear() { first_one=true; combinations_base::clear(); } template int symmetriser::signature(unsigned int i) const { assert(i void symmetriser::set_multiplicity(unsigned int i, int val) { assert(i void symm_helper::vector_generated(const std::vector& vec) { ++this->vector_generated_called_; if(first_one) { first_one=false; } else { if((this->start_==-1 || this->vector_generated_called_ >= this->start_) && (this->end_==-1 || this->vector_generated_called_ < this->end_)) { // std::cout << "produced "; // for(unsigned int m=0; m std::ostream& operator<<(std::ostream& str, const symmetriser& sym) { for(unsigned int i=0; i