PermLib
type_recognition.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 TYPE_RECOGNITION_H_
00033 #define TYPE_RECOGNITION_H_
00034 
00035 #include <permlib/prime_helper.h>
00036 #include <permlib/transversal/schreier_tree_transversal.h>
00037 #include <permlib/generator/bsgs_generator.h>
00038 #include <permlib/construct/schreier_sims_construction.h>
00039 #include <permlib/transversal/orbit_set.h>
00040 #include <permlib/test/giant_test.h>
00041 #include <permlib/test/group_type.h>
00042 #include <permlib/test/primitivity_sgs_test.h>
00043 #include <permlib/test/primitivity_test.h>
00044 #include <permlib/permlib_api.h>
00045 
00046 #include <boost/shared_ptr.hpp>
00047 #include <boost/math/common_factor_rt.hpp>
00048 #include <iostream>
00049 
00050 
00051 namespace permlib {
00052 
00054 
00058 template<class PERM, class TRANSVERSAL = SchreierTreeTransversal<PERM> >
00059 class TypeRecognition {
00060 public:
00062         typedef boost::shared_ptr<BSGS<PERM, TRANSVERSAL> > PermutationGroupPtr;
00063         
00069         template<class InputIterator>
00070         TypeRecognition(unsigned int n, InputIterator genBegin, InputIterator genEnd);
00071         
00075         TypeRecognition(const PermutationGroupPtr& bsgs);
00076         
00078         GroupType* analyze() { return analyze(true); }
00080         PermutationGroupPtr bsgs() const { return m_bsgs; }
00081         
00083 
00089         GroupType* largeSymmetricDiagonalSubgroup(std::vector<dom_int>& orbitCharacteristic) const;
00090 private:
00091         const unsigned int m_n;
00092         std::list<typename PERM::ptr> m_generators;
00093         const PermutationGroupPtr m_externalBSGS;
00094         PermutationGroupPtr m_bsgs;
00095         
00099         GroupType* analyze(bool allowRecursiveCalls);
00100         
00102 
00105         GroupType* checkOrbitActions();
00106         
00108         static const unsigned int m_bsgsComputationDegreeLimit = 50;
00109 };
00110 
00111 template<class PERM, class TRANSVERSAL>
00112 template<class InputIterator>
00113 TypeRecognition<PERM,TRANSVERSAL>::TypeRecognition(unsigned int n, InputIterator genBegin, InputIterator genEnd) 
00114         : m_n(n), m_generators(genBegin, genEnd)
00115 { }
00116 
00117 template<class PERM, class TRANSVERSAL>
00118 TypeRecognition<PERM,TRANSVERSAL>::TypeRecognition(const PermutationGroupPtr& bsgs_) 
00119         : m_n(bsgs_->n), m_generators(bsgs_->S.begin(), bsgs_->S.end()), m_externalBSGS(bsgs_), m_bsgs(m_externalBSGS)
00120 { }
00121 
00122 
00123 template<class PERM, class TRANSVERSAL>
00124 GroupType* TypeRecognition<PERM,TRANSVERSAL>::analyze(bool allowRecursiveCalls) {
00125         if (m_n < m_bsgsComputationDegreeLimit && ! m_bsgs) {
00126                 SchreierSimsConstruction<PERM,TRANSVERSAL> ssc(m_n);
00127                 m_bsgs = PermutationGroupPtr(new BSGS<PERM, TRANSVERSAL>(ssc.construct(m_generators.begin(), m_generators.end())));
00128         }
00129 
00130         // error probability for randomized algorithms
00131         const double eps = 1e-3;
00132         
00133         if (m_bsgs) {
00134                 const unsigned int groupIntOrder = m_bsgs->template order<unsigned int>();
00135                 
00136                 if (groupIntOrder == 1)
00137                         return new TrivialGroupType(m_n);
00138 
00139                 if (groupIntOrder == 2)
00140                         return new SymmetricGroupType(groupIntOrder, m_n);
00141                 
00142                 //
00143                 // check for cyclic groups
00144                 //
00145                 if (PrimeHelper::isPrimeNumber(groupIntOrder, false)) {
00146                         BOOST_ASSERT( m_n % groupIntOrder == 0 );
00147                         return new CyclicGroupType(groupIntOrder, m_n);
00148                 }
00149                 
00150                 if (groupIntOrder == 4) {
00151                         // distinguish between C_4 and the Klein four group
00152                         BSGSGenerator<TRANSVERSAL> bsgsGen(m_bsgs->U);
00153                         while (bsgsGen.hasNext()) {
00154                                 PERM el = bsgsGen.next();
00155                                 if (el.order() == 4)
00156                                         return new CyclicGroupType(4, m_n);
00157                         }
00158                         return new DirectProductGroupType(new SymmetricGroupType(2,2), new SymmetricGroupType(2,2), m_n);
00159                 }
00160                 
00161                 //
00162                 // check for symmetric groups, natural action
00163                 //
00164                 unsigned int symmetricGroupCandidateDegree = 0;
00165                 
00166                 std::vector<unsigned int> transversalSizes(m_bsgs->U.size());
00167                 for (unsigned int i = 0; i < m_bsgs->U.size(); ++i) {
00168                         transversalSizes[i] = m_bsgs->U[i].size();
00169                 }
00170                 std::sort(transversalSizes.begin(), transversalSizes.end());
00171                 
00172                 // test whether group order is a factorial of some natural number
00173                 
00174                 unsigned int expectedFactor = 2;
00175                 for (std::vector<unsigned int>::const_iterator it = transversalSizes.begin(); it != transversalSizes.end(); ++it) {
00176                         if (*it == 1)
00177                                 continue;
00178                         if (*it == expectedFactor)
00179                                 ++expectedFactor;
00180                         else {
00181                                 expectedFactor = 0;
00182                                 break;
00183                         }
00184                 }
00185                 
00186                 if (expectedFactor > 0)
00187                         symmetricGroupCandidateDegree = expectedFactor - 1;
00188                 
00189                 if (symmetricGroupCandidateDegree == m_n)
00190                         return new SymmetricGroupType(symmetricGroupCandidateDegree, m_n);
00191                 
00192                 // check for wreath products of symmetric groups if group is transitive
00193                 if (m_bsgs->U[0].size() == m_n) {
00194                         std::list<typename PERM::ptr> S1;
00195                         PointwiseStabilizerPredicate<PERM> stabPred(m_bsgs->B[0]);
00196                         BOOST_FOREACH(const typename PERM::ptr& p, m_bsgs->S) {
00197                                 if (stabPred(p))
00198                                         S1.push_back(p);
00199                         }
00200                         PrimitivitySGSTest<TRANSVERSAL> primitivityTest(m_bsgs->S.begin(), m_bsgs->S.end(), m_bsgs->B[0], S1.begin(), S1.end(), m_bsgs->U[0]);
00201                         std::vector<dom_int> minimalBlock;
00202                         bool groupIsPrimitive = primitivityTest.blockOfImprimitivity(&minimalBlock); 
00203                         if ( ! groupIsPrimitive ) {
00204                                 // TODO: avoid integer overflow in order computation
00205                                 unsigned int degreeG = minimalBlock.size();
00206                                 unsigned int degreeH = m_n / degreeG;
00207                                 if (m_n % degreeG == 0) {
00208                                         boost::uint64_t wreathSize = 1;
00209                                         // group order must equal   factorial(deg G)^(deg H) * factorial(deg H)
00210                                         for (unsigned int i = 1; i <= degreeH; ++i) {
00211                                                 for (unsigned int j = 2; j <= degreeG; ++j) {
00212                                                         wreathSize *= j;
00213                                                 }
00214                                                 wreathSize *= i;
00215                                         }
00216                                         if (wreathSize == m_bsgs->order())
00217                                                 return new WreathSymmetricGroupType(degreeG, degreeH, m_n);
00218                                 }
00219                         } else {
00220                                 GiantTest<PERM> giantTest;
00221                                 GiantTestBase::GiantGroupType giant = giantTest.determineGiantType(eps, 
00222                                         m_n, m_bsgs->S.begin(), m_bsgs->S.end(), true);
00223                                 if (giant == GiantTestBase::Symmetric)
00224                                         return new SymmetricGroupType(m_n, m_n);
00225                                 else if (giant == GiantTestBase::Alternating)
00226                                         return new AlternatingGroupType(m_n, m_n);
00227                         }
00228                 }
00229                 
00230                 
00231                 if (allowRecursiveCalls) {
00232                         // check for multiple linked copies of S_k
00233                         // TODO: check for inner direct products of S_k \times S_l
00234                         GroupType* t = checkOrbitActions();
00235                         if (t) {
00236                                 return t;
00237                         }
00238                 }
00239         } // end if m_bsgs
00240         else // else if ! m_bsgs
00241         {
00242                 GiantTest<PERM> giantTest;
00243                 GiantTestBase::GiantGroupType giant = giantTest.determineGiantType(eps, 
00244                         m_n, m_generators.begin(), m_generators.end());
00245                 if (giant == GiantTestBase::Symmetric)
00246                         return new SymmetricGroupType(m_n, m_n);
00247                 else if (giant == GiantTestBase::Alternating)
00248                         return new AlternatingGroupType(m_n, m_n);
00249                 
00250                 if (allowRecursiveCalls) {
00251                         GroupType* t = checkOrbitActions();
00252                         if (t)
00253                                 return t;
00254                 }
00255         }
00256         
00257         if (m_bsgs)
00258                 return new AnonymousGroupType<>(m_n, m_bsgs->order());
00259         return new AnonymousGroupType<>(m_n);
00260 }
00261 
00262 template<class PERM, class TRANSVERSAL>
00263 GroupType* TypeRecognition<PERM,TRANSVERSAL>::checkOrbitActions() {
00264         if (m_generators.empty())
00265                 return NULL;
00266         
00267         typedef typename PERM::ptr PERMptr;
00268         boost::dynamic_bitset<> worked(m_n);
00269         GroupType* candidateType = 0;
00270         unsigned int groupSupport = m_n;
00271         
00272         for (unsigned int i = 0; i < m_n; ++i) {
00273                 if (worked[i])
00274                         continue;
00275                 worked.set(i, true);
00276                 OrbitSet<PERM, dom_int> orbit;
00277                 orbit.orbit(i, m_generators, typename Transversal<PERM>::TrivialAction());
00278                 for (typename OrbitSet<PERM, dom_int>::const_iterator it = orbit.begin(); it != orbit.end(); ++it) {
00279                         worked.set(*it, true);
00280                 }
00281                 // ignore fix points
00282                 if (orbit.size() == 1) {
00283                         --groupSupport;
00284                         continue;
00285                 }
00286                 
00287                 // restrict group to this orbit
00288                 std::list<PERMptr> orbitGenerators;
00289                 BOOST_FOREACH(const PERMptr& p, m_generators) {
00290                         orbitGenerators.push_back(PERMptr(p->project(orbit.size(), orbit.begin(), orbit.end())));
00291                 }
00292                 TypeRecognition<PERM, TRANSVERSAL> orbitTypeRecognition(orbit.size(), orbitGenerators.begin(), orbitGenerators.end());
00293                 GroupType* orbitType = orbitTypeRecognition.analyze(false);
00294                 if ( ! candidateType )
00295                         candidateType = orbitType;
00296                 else {
00297                         const bool isEqualType = candidateType->equals(orbitType);
00298                         delete orbitType;
00299                         if ( ! isEqualType ) 
00300                                 return NULL;
00301                 }
00302         }
00303         if (candidateType)
00304                 candidateType->setNonNaturalAction(groupSupport);
00305         return candidateType;
00306 }
00307 
00308 template<typename PERM>
00309 struct BlockVectorAction {
00310         std::vector<dom_int> operator()(const PERM& p, const std::vector<dom_int>& v) const {
00311                 std::vector<dom_int> ret(v.size());
00312                 for (unsigned int i = 0; i < v.size(); ++i)
00313                         ret[i] = p / v[i];
00314                 std::sort(ret.begin(), ret.end());
00315                 return ret;
00316         }
00317 };
00318 
00319 template<class PERM, class TRANSVERSAL>
00320 GroupType* TypeRecognition<PERM,TRANSVERSAL>::largeSymmetricDiagonalSubgroup(std::vector<dom_int>& orbitCharacteristic) const {
00321         if (m_generators.empty())
00322                 return NULL;
00323         
00324         typedef boost::shared_ptr<OrbitSet<PERM, dom_int> > OrbitPtr;
00325         typedef typename PERM::ptr PERMptr;
00326 
00327         orbitCharacteristic.clear();
00328         orbitCharacteristic.resize(m_n);
00329         unsigned int orbitCharacteristicCounter = 0;
00330         
00331         std::list<OrbitPtr> orbits;
00332         boost::dynamic_bitset<> worked(m_n);
00333         for (unsigned int i = 0; i < m_n; ++i) {
00334                 if (worked[i])
00335                         continue;
00336                 worked.set(i, true);
00337                 OrbitPtr orbit(new OrbitSet<PERM, dom_int>());
00338                 orbit->orbit(i, m_generators, typename Transversal<PERM>::TrivialAction());
00339                 for (typename OrbitSet<PERM, dom_int>::const_iterator it = orbit->begin(); it != orbit->end(); ++it) {
00340                         worked.set(*it, true);
00341                 }
00342                 orbits.push_back(orbit);
00343         }
00344         
00345         unsigned long int orbitGCD = orbits.front()->size();
00346         BOOST_FOREACH(const OrbitPtr& orbit, orbits) {
00347                 orbitGCD = boost::math::gcd(orbitGCD, orbit->size());
00348         }
00349         
00350         GroupType* lastType = 0;
00351         BOOST_FOREACH(const OrbitPtr& orbit, orbits) {
00352                 GroupType* orbitType = 0;
00353                 // restrict group to this orbit
00354                 std::list<PERMptr> orbitGenerators;
00355                 // copy orbit elements into vector because we need a fixed order to recover from 'project'ion
00356                 std::vector<dom_int> orbitV(orbit->begin(), orbit->end());
00357                 BOOST_FOREACH(const PERMptr& p, m_generators) {
00358                         orbitGenerators.push_back(PERMptr(p->project(orbit->size(), orbitV.begin(), orbitV.end())));
00359                         //TODO: filter identities
00360                 }
00361                 PermutationGroupPtr orbitGroup = construct(orbit->size(), orbitGenerators.begin(), orbitGenerators.end());
00362                 
00363                 // compute all minimal blocks
00364                 PrimitivityTest<PERM> primitivityTest(orbit->size(), orbitGenerators.begin(), orbitGenerators.end());
00365                 std::list<std::vector<dom_int> > blocks;
00366                 bool groupIsPrimitive = primitivityTest.allBlocks(&blocks);
00367                 
00368                 if (!groupIsPrimitive) {
00369                         BOOST_FOREACH(const std::vector<dom_int>& b, blocks) {
00370                                 // if group is transitive (#orbits == 1), then we do not need the gcd condition
00371                                 if (orbits.size() != 1 && b.size() % orbitGCD != 0)
00372                                         continue;
00373                                 
00374                                 // look at group action within block b
00375                                 // 1. compute stabilizer of b
00376                                 // 2. project action of stabilizer onto b
00377                                 // 3. check whether induced action is a symmetric group 
00378                                 PermutationGroupPtr stab = setStabilizer(*orbitGroup, b.begin(), b.end());
00379                                 std::list<PERMptr> suborbitGenerators;
00380                                 BOOST_FOREACH(const PERMptr& p, stab->S) {
00381                                         suborbitGenerators.push_back(PERMptr(p->project(b.size(), b.begin(), b.end())));
00382                                 }
00383                                 TypeRecognition<PERM, TRANSVERSAL> suborbitRecognition(b.size(), suborbitGenerators.begin(), suborbitGenerators.end());
00384                                 GroupType* subType = suborbitRecognition.analyze(false);
00385                                 if (subType->type() == GroupType::Named && subType->isNaturalAction()) {
00386                                         NamedGroupType* namedType = reinterpret_cast<NamedGroupType*>(subType);
00387                                         if (strcmp(namedType->name(), "S") == 0) {
00388                                                 orbitType = subType;
00389                                         }
00390                                 }
00391                                 if (orbitType) {
00392                                         OrbitSet<PERM, std::vector<dom_int> > blockOrbit;
00393                                         blockOrbit.orbit(b, orbitGenerators, BlockVectorAction<PERM>());
00394                                         BOOST_FOREACH(const std::vector<dom_int>& block, std::make_pair(blockOrbit.begin(), blockOrbit.end())) {
00395                                                 BOOST_FOREACH(const dom_int j, block) {
00396                                                         orbitCharacteristic[orbitV[j]] = orbitCharacteristicCounter;
00397                                                 }
00398                                                 ++orbitCharacteristicCounter;
00399                                         }
00400                                         break;
00401                                 } else {
00402                                         delete subType;
00403                                 }
00404                         }
00405                 } else {
00406                         TypeRecognition<PERM, TRANSVERSAL> suborbitRecognition(orbit->size(), orbitGenerators.begin(), orbitGenerators.end());
00407                         orbitType = suborbitRecognition.analyze(false);
00408                         BOOST_FOREACH(const dom_int& o, orbitV) {
00409                                 orbitCharacteristic[o] = orbitCharacteristicCounter;
00410                         }
00411                         ++orbitCharacteristicCounter;
00412                 }
00413                 if (!orbitType) {
00414                         delete lastType;
00415                         return NULL;
00416                 }
00417                 
00418                 if (!lastType) {
00419                         lastType = orbitType;
00420                 } else { 
00421                         if (!lastType->equals(orbitType)) {
00422                                 delete orbitType;
00423                                 delete lastType;
00424                                 return NULL;
00425                         }
00426                         delete orbitType;
00427                 }
00428         }
00429         
00430         if (lastType)
00431                 lastType->setNonNaturalAction(m_n);
00432         
00433         return lastType;
00434 }
00435 
00436 }
00437 
00438 #endif
00439