PermLib
orbit_lex_min_search.h
00001 // ---------------------------------------------------------------------------
00002 //
00003 //  This file is part of PermLib.
00004 //
00005 // Copyright (c) 2009-2011 Thomas Rehn <thomas@carmen76.de>
00006 // All rights reserved.
00007 //
00008 // Redistribution and use in source and binary forms, with or without
00009 // modification, are permitted provided that the following conditions
00010 // are met:
00011 // 1. Redistributions of source code must retain the above copyright
00012 //    notice, this list of conditions and the following disclaimer.
00013 // 2. Redistributions in binary form must reproduce the above copyright
00014 //    notice, this list of conditions and the following disclaimer in the
00015 //    documentation and/or other materials provided with the distribution.
00016 // 3. The name of the author may not be used to endorse or promote products
00017 //    derived from this software without specific prior written permission.
00018 //
00019 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00020 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00021 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00022 // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00023 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00024 // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00025 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00026 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00028 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 //
00030 // ---------------------------------------------------------------------------
00031 
00032 #ifndef ORBIT_LEX_MIN_SEARCH_H_
00033 #define ORBIT_LEX_MIN_SEARCH_H_
00034 
00035 #include <permlib/predicate/pointwise_stabilizer_predicate.h>
00036 #include <permlib/change/conjugating_base_change.h>
00037 #include <permlib/change/random_base_transpose.h>
00038 #include <permlib/search/dset.h>
00039 
00040 #include <vector>
00041 #include <limits>
00042 #include <boost/dynamic_bitset.hpp>
00043 
00044 namespace permlib {
00045 
00047 
00051 template<class BSGSIN>
00052 class OrbitLexMinSearch {
00053 public:
00055 
00060         OrbitLexMinSearch(const BSGSIN& bsgs, bool abortSearchIfSmallerFound = false)
00061                 : m_bsgs(bsgs), m_cbc(bsgs), m_dsetAction(bsgs.n), m_orb(m_bsgs.n), m_orbVector(m_bsgs.n, 0),
00062                   m_orbVectorIndex(0), m_abortSearchIfSmallerFound(abortSearchIfSmallerFound) {}
00063 
00065 
00070         dset lexMin(const dset& element, const BSGSIN* stabilizer = NULL);
00071 
00073 
00078         static bool isLexSmaller(const dset& a, const dset& b);
00079 
00080 private:
00081         BSGSIN m_bsgs;
00082         const BSGSIN* m_bsgsStabilizer;
00083         typedef typename BSGSIN::PERMtype PERM;
00084         typedef std::vector<boost::shared_ptr<PERM> > PERMvec;
00085 
00086         struct Candidate {
00087                 dset D;
00088                 dset J;
00089 
00090                 Candidate(dset D_) : D(D_), J(D_.size()) {}
00091                 Candidate(dset D_, dset J_) : D(D_), J(J_) {}
00092 
00093                 void print(const char* prefix) const {
00094                         std::cout << prefix <<  ".J = " << J << "  ; " << prefix << ".D = " << D << std::endl;
00095                 }
00096         };
00097 
00098         typedef Candidate* CandidatePtr;
00099 
00100         ConjugatingBaseChange<PERM, typename BSGSIN::TRANStype, RandomBaseTranspose<PERM, typename BSGSIN::TRANStype> > m_cbc;
00101         DSetAction<PERM> m_dsetAction;
00102 
00103         bool lexMin(unsigned int i, unsigned int k, const dset& element, const BSGSIN* stabilizer, const std::list<CandidatePtr>& candidates, std::list<CandidatePtr>& candidatesNext, dset& M_i, std::list<unsigned long>& base, PERMvec& S_i);
00105         unsigned long orbMin(unsigned long element, const PERMvec& generators);
00106 
00108 
00113         dset* orbRepresentatives(dset element, const std::list<typename PERM::ptr>& generators);
00114 
00115         // temporary variables for the orbMin calculation
00116         dset m_orb;
00117         std::vector<unsigned long> m_orbVector;
00118         unsigned int m_orbVectorIndex;
00119         const bool m_abortSearchIfSmallerFound;
00120 };
00121 
00122 
00123 template<class BSGSIN>
00124 inline dset OrbitLexMinSearch<BSGSIN>::lexMin(const dset& element, const BSGSIN* stabilizer) {
00125         if (element.count() == element.size())
00126                 return element;
00127         if (element.count() == 0)
00128                 return element;
00129         CandidatePtr c0(new Candidate(element));
00130 
00131         std::list<CandidatePtr> candList0, candList1;
00132         std::list<CandidatePtr>* cand0 = &candList0;
00133         std::list<CandidatePtr>* cand1 = &candList1;
00134 
00135         cand0->push_back(c0);
00136         dset M_i(element.size());
00137         std::list<unsigned long> base;
00138         PERMvec S_i;
00139         S_i.reserve(m_bsgs.S.size());
00140 
00141         for (unsigned int i = 0; i < element.count(); ++i) {
00142                 if (lexMin(i, element.count(), element, stabilizer, *cand0, *cand1, M_i, base, S_i))
00143                         break;
00144                 std::swap(cand0, cand1);
00145         }
00146         std::for_each(candList0.begin(), candList0.end(), delete_object());
00147         std::for_each(candList1.begin(), candList1.end(), delete_object());
00148 
00149         return M_i;
00150 }
00151 
00152 template<class BSGSIN>
00153 inline bool OrbitLexMinSearch<BSGSIN>::lexMin(unsigned int i, unsigned int k, const dset& element, const BSGSIN* stabilizer, const std::list<CandidatePtr>& candidates, std::list<CandidatePtr>& candidatesNext, dset& M_i, std::list<unsigned long>& base, PERMvec& S_i) {
00154         PERMLIB_DEBUG(std::cout << "### START " << i << " with #" << candidates.size() << std::endl;)
00155 
00156         // if current stabilizer in the stabilizer chain is trivial we may
00157         // choose the minimal candidate and abort the search
00158         bool allOne = true;
00159         for (unsigned int j = i; j < m_bsgs.B.size(); ++j) {
00160                 if (m_bsgs.U[j].size() > 1) {
00161                         allOne = false;
00162                         break;
00163                 }
00164         }
00165         if (allOne) {
00166                 M_i = candidates.front()->D;
00167                 BOOST_FOREACH(const CandidatePtr& R, candidates) {
00168                         if (isLexSmaller(R->D, M_i)) {
00169                                 M_i = R->D;
00170                         }
00171                 }
00172                 return true;
00173         }
00174 
00175         unsigned int m = m_bsgs.n + 1;
00176         S_i.clear();
00177         PointwiseStabilizerPredicate<PERM> stab_i(m_bsgs.B.begin(), m_bsgs.B.begin() + i);
00178         std::copy_if(m_bsgs.S.begin(), m_bsgs.S.end(), std::back_inserter(S_i), stab_i);
00179         const unsigned long UNDEFINED_ORBIT = std::numeric_limits<unsigned long>::max();
00180         std::vector<unsigned long> orbitCache(m_bsgs.n, UNDEFINED_ORBIT);
00181         std::list<CandidatePtr> pass;
00182 
00183         BOOST_FOREACH(const CandidatePtr& R, candidates) {
00184                 unsigned long m_R = m;
00185                 if (m_abortSearchIfSmallerFound && isLexSmaller(R->D, element)) {
00186                         BOOST_ASSERT(R->D.count() == element.count());
00187                         M_i = R->D;
00188                         return true;
00189                 }
00190                 for (unsigned long j = 0; j < R->D.size(); ++j) {
00191                         if (R->J[j] || !R->D[j])
00192                                 continue;
00193 
00194                         unsigned long val = orbitCache[j];
00195                         if (val == UNDEFINED_ORBIT) {
00196                                 val = orbMin(j, S_i);
00197                                 orbitCache[j] = val;
00198                         }
00199                         if (m_R > val)
00200                                 m_R = val;
00201                 }
00202 
00203                 if (m_R < m) {
00204                         m = m_R;
00205                         pass.clear();
00206                         pass.push_back(R);
00207                 } else if (m_R == m) {
00208                         pass.push_back(R);
00209                 }
00210         }
00211 
00212         PERMLIB_DEBUG(std::cout << " found m = " << m << std::endl;)
00213         M_i.set(m, 1);
00214         if (i == k-1)
00215                 return true;
00216 
00217         base.push_back(m);
00218         m_cbc.change(m_bsgs, base.begin(), base.end());
00219 
00220         std::for_each(candidatesNext.begin(), candidatesNext.end(), delete_object());
00221         candidatesNext.clear();
00222 
00223         PERM* UNDEFINED_TRANSVERSAL = reinterpret_cast<PERM*>(1L);
00224         std::vector<PERM*> transversalCache(m_bsgs.n);
00225         BOOST_FOREACH(PERM*& p, transversalCache) {
00226                 p = UNDEFINED_TRANSVERSAL;
00227         }
00228         BOOST_FOREACH(const CandidatePtr& R, pass) {
00229                 for (unsigned long j = 0; j < R->D.size(); ++j) {
00230                         if (!R->D[j])
00231                                 continue;
00232 
00233                         PERM* perm = transversalCache[j];
00234                         if (perm == UNDEFINED_TRANSVERSAL) {
00235                                 perm = m_bsgs.U[i].at(j);
00236                                 if (perm) {
00237                                         perm->invertInplace();
00238                                 }
00239                                 transversalCache[j] = perm;
00240                         }
00241 
00242                         if (!perm)
00243                                 continue;
00244 
00245                         CandidatePtr c(new Candidate(R->D, R->J));
00246                         m_dsetAction.apply(*perm, R->D, c->D);
00247                         c->J.set(m);
00248                         candidatesNext.push_back(c);
00249                 }
00250         }
00251 
00252         BOOST_FOREACH(PERM* p, transversalCache) {
00253                 if (p != UNDEFINED_TRANSVERSAL)
00254                         delete p;
00255         }
00256         return false;
00257 }
00258 
00259 template<class BSGSIN>
00260 inline unsigned long OrbitLexMinSearch<BSGSIN>::orbMin(unsigned long element, const PERMvec& generators) {
00261         if (element == 0)
00262                 return 0;
00263 
00264         unsigned long minElement = element;
00265         m_orb.reset();
00266         m_orb.set(element, 1);
00267         m_orbVectorIndex = 0;
00268         m_orbVector[m_orbVectorIndex++] = element;
00269 
00270         for (unsigned int i = 0; i < m_orbVectorIndex; ++i) {
00271                 const unsigned long &alpha = m_orbVector[i];
00272                 BOOST_FOREACH(const typename PERM::ptr& p, generators) {
00273                         unsigned long alpha_p = *p / alpha;
00274                         if (alpha_p == 0)
00275                                 return 0;
00276                         if (!m_orb[alpha_p]) {
00277                                 m_orbVector[m_orbVectorIndex++] = alpha_p;
00278                                 m_orb.set(alpha_p);
00279                                 if (alpha_p < minElement)
00280                                         minElement = alpha_p;
00281                         }
00282                 }
00283         }
00284 
00285         return minElement;
00286 }
00287 
00288 
00289 template<class BSGSIN>
00290 inline dset* OrbitLexMinSearch<BSGSIN>::orbRepresentatives(dset element, const std::list<typename PERM::ptr>& generators) {
00291         dset* ret = new dset(element.size());
00292 
00293         for (unsigned int j = 0; j < element.size(); ++j) {
00294                 if (!element[j])
00295                         continue;
00296 
00297                 m_orb.set();
00298                 m_orb.set(j, 0);
00299                 m_orbVectorIndex = 0;
00300                 m_orbVector[m_orbVectorIndex++] = j;
00301                 for (unsigned int i = 0; i < m_orbVectorIndex; ++i) {
00302                         const unsigned long &alpha = m_orbVector[i];
00303                         BOOST_FOREACH(const typename PERM::ptr& p, generators) {
00304                                 unsigned long alpha_p = *p / alpha;
00305                                 if (m_orb[alpha_p]) {
00306                                         m_orbVector[m_orbVectorIndex++] = alpha_p;
00307                                         m_orb.reset(alpha_p);
00308                                 }
00309                         }
00310                 }
00311 
00312                 element &= m_orb;
00313                 ret->set(j);
00314         }
00315 
00316         return ret;
00317 }
00318 
00319 
00320 template<class BSGSIN>
00321 inline bool OrbitLexMinSearch<BSGSIN>::isLexSmaller(const dset& a, const dset& b) {
00322                 dset::size_type i = a.find_first(), j = b.find_first();
00323                 while (i != dset::npos && j != dset::npos) {
00324                         if (i < j)
00325                                 return true;
00326                         if (i > j)
00327                                 return false;
00328                         i = a.find_next(i);
00329                         j = b.find_next(j);
00330                 }
00331                 return false;
00332         }
00333 
00334 } // ns permlib
00335 
00336 #endif // ORBIT_LEX_MIN_SEARCH_H_