PermLib
|
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