00001 /*! 00002 \file 00003 \brief Implementation of the class KGB representing orbits of K on G/B. 00004 00005 This module contains code for the construction of a block in the 00006 one-sided parameter set (in other words, the subset of the one-sided 00007 parameter set corresponding to a single real form.) As explained in 00008 my Palo Alto III notes, this is equivalent to parametrizing the set 00009 K\\G/B of (K,B)-orbits in G; hence the provocative title. 00010 */ 00011 /* 00012 This is kgb.cpp 00013 00014 Copyright (C) 2004,2005 Fokko du Cloux 00015 Copyright (C) 2007 Marc van Leeuwen 00016 part of the Atlas of Reductive Lie Groups 00017 00018 See file main.cpp for full copyright notice 00019 */ 00020 00021 #include "kgb.h" 00022 00023 #include <cassert> 00024 #include <map> 00025 #include <memory> 00026 #include <set> 00027 #include <stdexcept> 00028 00029 #include <iostream> 00030 00031 #include "bitmap.h" 00032 #include "bruhat.h" 00033 #include "cartanset.h" 00034 #include "cartanclass.h" 00035 #include "complexredgp.h" 00036 #include "error.h" 00037 #include "gradings.h" 00038 #include "hashtable.h" 00039 #include "latticetypes.h" 00040 #include "realredgp.h" 00041 #include "rootdata.h" 00042 #include "tits.h" 00043 #include "tori.h" 00044 #include "weyl.h" 00045 00046 /* 00047 This module contains code for the construction of a block in the 00048 one-sided parameter set (in other words, the subset of the one-sided 00049 parameter set corresponding to a single real form.) As explained in 00050 my Palo Alto III notes, this is equivalent to parametrizing the set 00051 K\\G/B of (K,B)-orbits in G; hence the provocative title. 00052 00053 */ 00054 00055 namespace atlas { 00056 00057 // namespace { 00058 namespace kgb { 00059 namespace { 00060 00061 /* 00062 Local functions 00063 */ 00064 00065 gradings::Grading 00066 square_class_grading_offset(const complexredgp::ComplexReductiveGroup& G, 00067 cartanclass::square_class csc); 00068 gradings::Grading 00069 grading_offset_for(const realredgp::RealReductiveGroup& GR); 00070 00071 void makeHasse(std::vector<set::SetEltList>&, const KGB&); 00072 00073 00074 00075 /* 00076 Some small additional classes 00077 */ 00078 00079 /*! 00080 \brief A |FiberData| object associates to each twisted involution a subspace 00081 describing how corresponding Tits elements should be normalized. 00082 00083 It also records the Cartan class that each twisted involution belongs to. 00084 */ 00085 class FiberData 00086 { 00087 const tits::TitsGroup& Tits; 00088 weyl::TI_Entry::Pooltype pool; 00089 hashtable::HashTable<weyl::TI_Entry,unsigned int> hash_table; 00090 std::vector<latticetypes::SmallSubspace> data; 00091 std::vector<unsigned int> Cartan_class; 00092 public: 00093 FiberData(const complexredgp::ComplexReductiveGroup& G, 00094 const bitmap::BitMap& Cartan_classes); 00095 00096 void reduce(tits::TitsElt& a) const; 00097 00098 size_t cartanClass(const weyl::TwistedInvolution& tw) const 00099 { 00100 return Cartan_class[hash_table.find(tw)]; 00101 } 00102 00103 private: // the space actually stored need not be exposed 00104 const latticetypes::SmallSubspace& mod_space(const tits::TitsElt& a) const 00105 { 00106 size_t i=hash_table.find(a.tw()); 00107 assert(i!=hash_table.empty); 00108 return data[i]; 00109 } 00110 }; // class FiberData 00111 00112 00113 00114 00115 /* 00116 The |KGBHelp| class, a helper class for |KGB| 00117 00118 */ 00119 00120 /* The helper class stores the basic data for KGB elements that will be 00121 exported to the |KGB| class, but also additional data that is used during 00122 generation but is not retained in the final representation; notably for 00123 each KGB element the Tits group element with which it is associated (the 00124 correspondence between the two depends on fairly arbitrary choices, whence 00125 retaining the Tits group element after generation is not really useful). 00126 Also stored is a table |d_fiberData| recording per-twisted-involution data. 00127 Upon exportation the numbering of the elements will be changed, and all 00128 internally used indices modified to reflect the renumbering. 00129 */ 00130 00131 00132 class KGBHelp 00133 { 00134 const size_t d_rank; 00135 const tits::TitsGroup& d_titsGroup; 00136 00137 // data that will be exported (in permuted form) to the KGB class 00138 std::vector<KGBEltList> d_cross; 00139 std::vector<KGBEltList> d_cayley; 00140 std::vector<KGBInfo> d_info; 00141 00142 // other data 00143 00144 /*!\brief List of Tits elements parametrizing KGB orbits. 00145 00146 Accessed usually via the hash table |d_tits| (and |d_tits[i]| is |d_pool[i]|) 00147 */ 00148 tits::TE_Entry::Pooltype d_pool; 00149 hashtable::HashTable<tits::TE_Entry,KGBElt> d_tits; 00150 00151 // some values stored for easy access during the KGB generation: 00152 const complexredgp::ComplexReductiveGroup& d_G; 00153 const cartanclass::Fiber& d_fundf; // fundamental fiber 00154 // strong real form representative (if fixed; may remain unset and unused) 00155 const cartanclass::StrongRealFormRep d_srf; 00156 // all compact imaginary roots for basic form (may remain unset and unused) 00157 const rootdata::RootSet d_base_compact; 00158 00159 /*! \brief 00160 Flags the noncompact imaginary roots for the basic strong involution, 00161 among the simple roots for G. 00162 00163 This is an important parameter in the KGB generation. It depends on an 00164 implicitly chosen "basic strong involution" in the fundamental fiber, 00165 i.e., one of the form \f$x_0\delta\f$ with \f$x_0\in H\f$. The element 00166 $x_0$ is determined, up a factor in $Z(G)$, by the evaluations of simple 00167 roots at it; these are given by \f$\alpha_i(x_0)=(-1)^{g_i}\f$ for all 00168 $i$, where $g_i$ denotes |d_gradingOffset[i]|. When \f$\alpha_i\f$ is 00169 imaginary for \f$\delta\f$, the value $g_i$ is fixed by the choice of a 00170 $W_{im}$ orbit representative in the real form; otherwise (\f$\alpha_i\f$ 00171 is complex for \f$\delta\f$) we choose $g_i=0$, which choice can be 00172 accommodated by $H$-conjugation of \f$x_0\delta\f$. Thanks to the latter 00173 convention, we are able to compute, using |d_gradingOffset|, gradings at 00174 simple roots that are imaginary in the fiber given by \emph{any} twisted 00175 involution, even if those roots were complex in the fundamental fiber. 00176 */ 00177 gradings::Grading d_gradingOffset; 00178 00179 //! Permits reducing each Tits group element modulo its fiber denominator 00180 FiberData d_fiberData; 00181 00182 public: 00183 00184 // constructors and destructors 00185 KGBHelp(realredgp::RealReductiveGroup&); 00186 KGBHelp(realredgp::RealReductiveGroup& GR, 00187 const bitmap::BitMap& Cartan_classes); 00188 00189 ~KGBHelp() {}; 00190 00191 // public manipulators and accessor: 00192 00193 //!\brief fill the KGB set and return it 00194 KGBHelp& fill(); 00195 00196 //!\brief deliver values to fields of a |KGB| object under construction. 00197 size_t export_tables(std::vector<KGBEltList>& cross, 00198 std::vector<KGBEltList>& cayley, 00199 weyl::TwistedInvolutionList& twisted, 00200 std::vector<KGBInfo>& info, 00201 bool traditional) const; 00202 00203 // though used only from within |export_tables|, this method must be public 00204 bool comp(KGBElt x,KGBElt y) const 00205 { 00206 if (d_info[x].length!=d_info[y].length) 00207 return d_info[x].length<d_info[y].length; 00208 unsigned long lx=weylGroup().length(d_pool[x].w()); 00209 unsigned long ly=weylGroup().length(d_pool[y].w()); 00210 return lx!=ly ? lx<ly : d_pool[x].w()<d_pool[y].w(); 00211 } 00212 00213 // accessors (private) 00214 private: 00215 void cayleyTransform(tits::TitsElt& a, size_t s) const { 00216 titsGroup().sigma_mult(s,a); // set |a| to $\sigma_s.a$ 00217 } 00218 00219 const tits::TitsGroup& titsGroup() const { 00220 return d_titsGroup; 00221 } 00222 00223 const weyl::WeylGroup& weylGroup() const { 00224 return d_titsGroup.weylGroup(); 00225 } 00226 00227 size_t twist(size_t s) const { 00228 return d_titsGroup.twist(s); 00229 } 00230 00231 /* The amazingly simple way to compute the grading a simple roots, for any 00232 twisted involution for which they are imaginary. Let $w$ be the twisted 00233 involution associated to the Tits element |a| (i.e., its Weyl group part) 00234 and let $\alpha=\alpha_s$ be a simple root, with image $\beta=\alpha_t$ 00235 under the distinguished involution $\delta$ (so $t=twist(s)$, and $s=t$ 00236 if $\alpha$ is imaginary for $\delta$). The precondition is that $s$ is 00237 imaginary for $w$ which means that $s.w.t=w$ in $W$ and $l(s.w)>l(w)$, so 00238 that $s.w=w.t$ are both reduced. This means that this equation lifts to 00239 canonical Weyl elements in the Tits group, so in that group conjugation 00240 by $\sigma_w$ sends $\sigma_t$ to $\sigma_s$ (rather than its inverse). 00241 Therefore in $G$, the action of $Ad(\sigma_w.\delta)$ is the identity on 00242 the $SL(2)$ or $PSL(2)$ subgroup containing $\sigma_s$ whose Lie algebra 00243 contains $X_\alpha$ and $X_alpha$, and so those roots are fixed under the 00244 adjoint action of $\sigma_w.\delta$. Then the adjoint action of the 00245 strong involution $x.\sigma_w.x_0.\delta$ (which |a| represents) on 00246 $X_\alpha$ gives $(Ad(x).Ad(\sigma_w).Ad(x_0))(X_\beta) 00247 =\alpha(x)\beta(x_0)X_\alpha$. So the grading $g$ to be computed here 00248 should satisfy $(-1)^g=\alpha(x)\beta(x_0)$, or 00249 $g=<\alpha,x>+<beta,x_0>$ in $\Z/2\Z$. The first term is computed by 00250 |scalarProduct|, while the second term is the grading of $x_0.\delta$ on 00251 the root $X_\beta=X_\alpha$ if it is imaginary for $\delta$ (i.e., 00252 $s=t$), or $0$ if $X_\alpha$ and $X_beta$ are complex (thanks to the 00253 choice of $x_0$); either way, $<beta,x_0>$ equals |d_gradingOffset[s]|. 00254 00255 Note that by using the a left torus factor |x| rather than a right 00256 factor, we need not refer to $\beta$, i.e., |simpleRoot(twist(s))| below. 00257 */ 00258 00259 // grading of KGB element represented by |a| at simple root |s| 00260 bool simple_grading(const tits::TitsElt& a, size_t s) const 00261 { 00262 return d_gradingOffset[s] 00263 ^bitvector::scalarProduct(titsGroup().left_torus_part(a), 00264 titsGroup().simpleRoot(s)); 00265 } 00266 00267 // whether arbitrary root |n| is compact for |x| in fundamental fiber 00268 bool is_compact(const tits::TorusPart& x, rootdata::RootNbr n) const; 00269 00270 // general case of grading of a KGB element at an imaginary root 00271 bool grading(tits::TitsElt a, rootdata::RootNbr n) const; 00272 00273 /* Apart from the grading methods, this is another place where we use 00274 |d_gradingOffset|. The task is to compute the conjugate of the strong 00275 involution $a.x_0.\delta$ by $\sigma_s$ or by $\sigma_s^{-1}=\sigma_s^3$. 00276 Note that since conjugation by $\sigma_s^2=m_s$ is not a trivial 00277 operation at the Tits group level, there is a difference between these 00278 operations, and they do not define an involution, but the difference can 00279 be seen to disappear into the modular reduction that is systematically 00280 applied to torus parts of Tits group elements; therefore this operation 00281 will define an involution at the level of the KGB structure. In fact we 00282 shall prefer to implement conjugation by $\sigma_s^{-1}$. 00283 00284 For the twisted involution (the Weyl group part of |a|) this operation is 00285 just twisted conjugation by |s|. For the Tits group part, twisted 00286 conjugation by $\sigma_s^{-1}$ is not the whole story; in addition there 00287 is a contribution from interchanging $\sigma_t$ and $x_0$, where 00288 $t=twist(s)$. Since $\sigma_t$ represents $t$ in the Weyl group, one has 00289 $Ad(\sigma_t)(x_0)=(x_0)=t(x_0)$, so we should add $<\alpha_t,x_0>m_t$ 00290 at the right to the torus part of |a|. By our choice of $x_0$, this 00291 scalar product can be nonzero only if $s=t$, and is equal to 00292 |d_gradingOffset[s]|; moreover the value of $m_s$ in the coordinates of 00293 the torus part is available as |d_titsGroup.simpleCoroot(s)|. By the 00294 remark above about conjugating by $m_s$, the contribution may ba added 00295 into the left instead of the right torus part, which is done below. 00296 */ 00297 00298 // operation defining cross action of simple roots 00299 void basedTwistedConjugate(tits::TitsElt& a, size_t s) const { 00300 titsGroup().inverseTwistedConjugate(a,s); 00301 if (d_gradingOffset[s]) // this implies |titsGroup().twist(s)==s| 00302 d_titsGroup.left_add(d_titsGroup.simpleCoroot(s),a); 00303 } 00304 00305 // various methods that provide a starting KGB element for any Cartan class 00306 tits::TitsElt naive_seed(realform::RealForm rf, size_t cn) const; 00307 tits::TitsElt grading_seed(realform::RealForm rf, size_t cn) const; 00308 tits::TitsElt backtrack_seed(realform::RealForm rf, size_t cn) const; 00309 00310 00311 // private manipulators 00312 void cross_extend(KGBElt parent); 00313 void cayleyExtend(KGBElt parent); 00314 00315 }; // class KGBHelp 00316 00317 00318 00319 00320 /* The following auxiliary class provides a comparison object for calling 00321 standard search and sorting routines for twisted involutions. It serves as 00322 specification of the comparison that is used in sorting the KGB structure, 00323 but in fact it is so expensive for repeated use (since |W.involutionLength| 00324 has to recompute its result each time) that its use has been discontinued. 00325 In Fokko's code it was used by |tauPacket|, and formed the main bottleneck 00326 for the block construction; now the hash table |d_tau| togther with the 00327 table |first_of_tau| provide a much faster way to implement |tauPacket|. 00328 */ 00329 class InvolutionCompare { 00330 private: 00331 const weyl::WeylGroup& W; 00332 public: 00333 explicit InvolutionCompare(const weyl::WeylGroup& w) : W(w) {} 00334 00335 // one should have a < b iff 00336 // (a) involutionLength(a) < involutionLength(b) or 00337 // (b) involutionLengths are equal and length(a) < length (b) or 00338 // (c) both lengths are equal and a < b 00339 bool operator() 00340 (const weyl::TwistedInvolution& a, const weyl::TwistedInvolution& b) const 00341 { 00342 if (W.involutionLength(a) != W.involutionLength(b)) 00343 return W.involutionLength(a) < W.involutionLength(b) ; 00344 else if (W.length(a.w()) != W.length(b.w())) 00345 return W.length(a.w()) < W.length(b.w()); 00346 else 00347 return a < b; 00348 } 00349 }; // class InvolutionCompare 00350 00351 /* For sorting the KGB elements on exportation from the helper class, we use 00352 the |comp| method of the helper class, which effectively applies the 00353 comparison of |InvolutionCompare| to the Weyl group parts of the stored 00354 Tits group elements, but using their stored length value rather than 00355 calling |WeylGroup::involutionLength|, for much improved speed. The class 00356 below serves to wrap that |comp| method into a proper comparison object. 00357 */ 00358 class IndexCompare 00359 { 00360 private: 00361 const KGBHelp& kgb; 00362 public: 00363 explicit IndexCompare(const KGBHelp& h) : kgb(h) {} 00364 00365 // here |unsigned long| arguments, as |setutils::Permutation| contains such 00366 bool operator() (unsigned long i, unsigned long j) const { 00367 return kgb.comp(i,j); 00368 } 00369 }; // class IndexCompare 00370 00371 } // namespace 00372 } // namespace kgb 00373 00374 00375 /***************************************************************************** 00376 00377 Chapter I -- The KGB class, public methods 00378 00379 ******************************************************************************/ 00380 00381 namespace kgb { 00382 00383 /*! 00384 \brief Construct the KGB data structure for the given real form, 00385 but if any Cartan classes are specified, only for those classes 00386 00387 This really handles two cases (the standard construction and a specialised 00388 on for a small set of Cartan classes) in one. The reason that they are 00389 combined into a single constructor is that this allows a single constructor 00390 of a containing class to choose between the two methods (a constructor 00391 method must use a fixed constructor for each of its subobjects, so having 00392 multiple constructors here would force the same for every containing class). 00393 */ 00394 KGB::KGB(realredgp::RealReductiveGroup& GR, 00395 const bitmap::BitMap& Cartan_classes) 00396 : d_rank(GR.semisimpleRank()) 00397 , d_cross() 00398 , d_cayley() 00399 , d_inverseCayley() 00400 , d_info() 00401 , d_involution() 00402 , first_of_tau() 00403 , d_pool(), d_tau(d_pool) 00404 , d_bruhat(NULL) 00405 , d_weylGroup(&GR.weylGroup()) 00406 { 00407 bool traditional= Cartan_classes.size()==0; // whether traditional generation 00408 size_t size = 00409 (traditional ? KGBHelp(GR) : KGBHelp(GR,Cartan_classes)) 00410 .fill() 00411 .export_tables(d_cross,d_cayley,d_involution,d_info,traditional); 00412 00413 // check that the size is right 00414 assert(size == (traditional ? GR.kgbSize() 00415 : GR.complexGroup().cartanClasses().KGB_size 00416 (GR.realForm(),Cartan_classes) 00417 )); 00418 00419 // reserve one more that the number of twisted involutions present 00420 first_of_tau.reserve((traditional ? GR.numInvolutions() 00421 :GR.complexGroup().numInvolutions(Cartan_classes) 00422 )+1); 00423 00424 /* Since elements are sorted by twisted involution, collecting those is easy. 00425 By using the hash table |d_tau|, it gets initialised in the proper order 00426 */ 00427 { // insert twisted involutions in order into hash table |d_tau| 00428 for (KGBElt x=0; x<size; ++x) 00429 { 00430 unsigned int old = d_tau.size(); 00431 if (d_tau.match(d_involution[x])==old) // a new twisted involution 00432 first_of_tau.push_back(x); // record first |x| for each |tau| 00433 } 00434 first_of_tau.push_back(size); // put final sentinel 00435 } 00436 00437 // Finally compute inverse Cayley tables 00438 00439 d_inverseCayley.resize 00440 (d_rank,KGBEltPairList(size,std::make_pair(UndefKGB,UndefKGB))); 00441 00442 for (KGBElt x=0; x<size; ++x) 00443 for (weyl::Generator s=0; s < d_rank; ++s) 00444 if (cayley(s,x)!=UndefKGB) 00445 { 00446 KGBEltPair& target=d_inverseCayley[s][cayley(s,x)]; 00447 if (target.first==UndefKGB) target.first=x; 00448 else target.second=x; 00449 } 00450 } 00451 00452 /******** copy, assignment and swap ******************************************/ 00453 00454 00455 /******** accessors **********************************************************/ 00456 00457 00458 /*! 00459 \brief Returns the range of |KGBElt| values |z| with |involution(z)==w|. 00460 00461 It is possible to return a range of |KGBElt|, since these elements have 00462 been sorted by considering the associated twisted involution first. 00463 00464 The fields |d_tau| and |first_of_tau| were added to |KGB| in order to make 00465 this method, central to the block construction, as fast as possible. 00466 */ 00467 KGBEltPair KGB::tauPacket(const weyl::TwistedInvolution& w) const 00468 { 00469 unsigned int i=d_tau.find(w); 00470 if (i==d_tau.empty) 00471 return KGBEltPair(0,0); 00472 return KGBEltPair(first_of_tau[i],first_of_tau[i+1]); 00473 } 00474 00475 /* 00476 We have left the original, much less efficient code, below for comparison. 00477 An intermediate possibility is to first make a call to |std::equal_range| to 00478 find the range where the stored |d_length| values match the involution 00479 length of |w|, and then refine the search with a comparison object that does 00480 not inspect involution lengths. This takes most of the sting out of the 00481 inefficiency of |InvolutionCompare|, but we have not (also) kept that code. 00482 */ 00483 00484 #if 0 00485 /* 00486 A comment to the original code given below: [MvL: It was tempting to call 00487 |std::equal_range| with type |ctr_iterator::CounterIterator<KGBElt>| 00488 arguments, representing indices into |d_involution|, to find the range of 00489 such indices that compare equal to |w| under (the now removed method) 00490 |KGB::compare|. I even believe this is why Fokko defined |CounterIterator| 00491 in the first place. The idea is doomed however, by the requirement that we 00492 supply to |std::equal_range| as third argument a value of the same type as 00493 comparison key. This means we would first have to supply an index |i| such 00494 that |d_involution[i]==w|; such an |i| need not exist, and even if it does, 00495 locating it is almost as hard as the task of |tauPacket| in the first place. 00496 Therefore the class |InvolutionCompare| needs to be used instead.] 00497 */ 00498 { 00499 using namespace weyl; 00500 00501 typedef std::vector<TwistedInvolution>::const_iterator TI_it; 00502 00503 InvolutionCompare comp(weylGroup()); 00504 00505 std::pair<TI_it,TI_it> range = 00506 std::equal_range(d_involution.begin(),d_involution.end(),w,comp); 00507 00508 KGBElt first = range.first - d_involution.begin(); 00509 KGBElt last = range.second - d_involution.begin(); 00510 00511 return std::make_pair(first,last); 00512 } 00513 #endif 00514 00515 /******** manipulators *******************************************************/ 00516 00517 /*! 00518 \brief Constructs the BruhatOrder. 00519 00520 NOTE: may throw a MemoryOverflow error. Commit-or-rollback is guaranteed. 00521 */ 00522 void KGB::fillBruhat() 00523 00524 { 00525 using namespace bruhat; 00526 using namespace set; 00527 00528 if (d_state.test(BruhatConstructed)) // work was already done 00529 return; 00530 00531 try { 00532 std::vector<SetEltList> hd; makeHasse(hd,*this); 00533 BruhatOrder* bp = new BruhatOrder(hd); // may throw here 00534 00535 // commit 00536 delete d_bruhat; // this is overly careful: it must be NULL 00537 d_bruhat = bp; 00538 d_state.set(BruhatConstructed); 00539 } 00540 catch (std::bad_alloc) { // transform failed allocation into MemoryOverflow 00541 throw error::MemoryOverflow(); 00542 } 00543 } 00544 00545 } // namespace kgb 00546 00547 00548 00549 00550 00551 /***************************************************************************** 00552 00553 Chapter II -- The auxiliary classes, methods 00554 00555 ******************************************************************************/ 00556 00557 namespace kgb { 00558 namespace { 00559 00560 00561 /* II a. |FiberData| */ 00562 00563 /* 00564 The |FiberData| constructor computes a subspace for each twisted involution 00565 $tw$ (representing an involution $\tau$ of $H$ and an involution 00566 $q=tw.\delta$ of the weight lattice $X$) that occurs for $GR$. The subspace 00567 of $X^\vee/2X^\vee$ that will be stored for $tw$ is the image $I$ of the 00568 $-1$ eigenspace $X^\vee_-$ of $q^t$. When a Tits group element of the form 00569 $x.\sigma_w$ occurs in the KGB construction, only the coset the left torus 00570 part $x$ modulo $I$ matters, will after computation be systematically 00571 normalised by reducing the left torus part $x$ modulo $I$. 00572 00573 The image $I$ corresponds to the subset of elements of order 2 in $H$ that 00574 are of the form $h*\tau(h)^{-1}$. It is also what one divides by to get the 00575 fiber group of the real Cartan associated to $H$ and $\tau$. Note however 00576 that it is not true that such $t\in X^\vee/2X^\vee$ will always lie in the 00577 image of $X^\vee_+ + X^\vee_-$ that is used to form the fiber group. 00578 00579 The Cartan class associated to $tw$ is also recorded. 00580 00581 This constructor depends on the real form only via the set |Cartan_classes| 00582 that determines (limits) the set of twisted involutions to be considered. 00583 */ 00584 FiberData::FiberData(const complexredgp::ComplexReductiveGroup& G, 00585 const bitmap::BitMap& Cartan_classes) 00586 : Tits(G.titsGroup()) 00587 , pool() 00588 , hash_table(pool) 00589 , data() 00590 , Cartan_class() 00591 { 00592 { // dimension |data| and |Cartan_class| 00593 size_t n_inv=G.numInvolutions(Cartan_classes); 00594 data.reserve(n_inv); Cartan_class.reserve(n_inv); 00595 } 00596 00597 const rootdata::RootDatum& rd=G.rootDatum(); 00598 latticetypes::BinaryMap delta(G.distinguished().transposed()); 00599 00600 std::vector<latticetypes::BinaryMap> refl(G.semisimpleRank()); 00601 for (weyl::Generator s=0; s<refl.size(); ++s) 00602 { 00603 // get endomorphism of weight lattice $X$ given by generator $s$ 00604 latticetypes::LatticeMatrix r = rd.rootReflection(rd.simpleRootNbr(s)); 00605 00606 // reflection map is induced vector space endomorphism of $X^* / 2X^*$ 00607 refl[s] = latticetypes::BinaryMap(r.transposed()); 00608 } 00609 00610 for (bitmap::BitMap::iterator it=Cartan_classes.begin(); it(); ++it) 00611 { 00612 size_t cn=*it; 00613 size_t i = hash_table.match(G.twistedInvolution(cn)); 00614 assert(i==data.size()); // this twisted involution should be new 00615 00616 { // store data for canonical twisted involution |i| of Cartan class |cn| 00617 using namespace latticetypes; 00618 LatticeMatrix qtr= G.cartan(cn).involution().transposed(); 00619 data.push_back(SmallSubspace(SmallBitVectorList(tori::minusBasis(qtr)), 00620 G.rank())); // compute subspace $I$ 00621 Cartan_class.push_back(cn); // record number of Cartan class 00622 } 00623 00624 00625 // now generate all non-canonical twisted involutions for this Cartan class 00626 for ( ; i<data.size(); ++i) // |data.size()| increases during the loop 00627 for (size_t s=0; s<G.semisimpleRank(); ++s) 00628 { 00629 weyl::TwistedInvolution stw=G.weylGroup().twistedConjugated(pool[i],s); 00630 if (hash_table.match(stw)==data.size()) // then |stw| is new 00631 { 00632 data.push_back(data[i]); // start with copy of source subspace 00633 data.back().apply(refl[s]); // modify according to cross action used 00634 Cartan_class.push_back(cn); // record number of Cartan class 00635 } 00636 } 00637 } 00638 00639 // check that the number of generated elements is as predicted 00640 assert(data.size()==G.numInvolutions(Cartan_classes)); 00641 assert(Cartan_class.size()==G.numInvolutions(Cartan_classes)); 00642 } 00643 00644 00645 void FiberData::reduce(tits::TitsElt& a) const 00646 { 00647 a=tits::TitsElt(Tits,mod_space(a).mod_image(Tits.left_torus_part(a)),a.w()); 00648 } 00649 00650 00651 /* II b. The main helper class |KGBHelp| */ 00652 00653 /* 00654 The actual KGB contruction takes place below. During the construction, the 00655 elements are represented as Tits group elements. The links in the KGB 00656 structure are realised by |KGBHelp::basedTwistedConjugate| for the cross 00657 actions and by |KGBHelp::cayleyTransform| for Cayley transforms. After each 00658 of these, the result is subject to |d_fiberdata.reduce| to normalise the 00659 representation of a KGB element. 00660 */ 00661 00662 /*! \brief 00663 This constructor sets |gradingOffset| for |GR|, and a trival initial value. 00664 00665 So we choose a different grading offset for each real form, but always start 00666 at the same Tits element. For an approach where different real forms can 00667 share a grading offset and thus make their KGB sets mesh together, see the 00668 next constructor. About the current (more or less) constructor Fokko said: 00669 ``from the datum of the real form (or more precisely, from the corresponding 00670 fundamental grading) we recover the basic cocycle that transforms the whole 00671 construction into a Tits group computation''. By the basic (1-)cocycle he 00672 seems to have meant the map from \f$W\f$ to the \f$W\f$-module \f$Y/2Y\f$ 00673 defined by the mentioned adjoint fiber element as image of the identity, and 00674 extended by the natural grading shifts for the simple reflections (only the 00675 image of the identity was actually computed). 00676 00677 Currently the fields |d_srf| and |d_base_compact| are only (possibly) used 00678 by the other constructor, and so are left just default-constructed here. 00679 */ 00680 00681 KGBHelp::KGBHelp(realredgp::RealReductiveGroup& GR) 00682 : d_rank(GR.semisimpleRank()) 00683 , d_titsGroup(GR.titsGroup()) 00684 00685 , d_cross(d_rank) 00686 , d_cayley(d_rank) 00687 , d_info() 00688 00689 , d_pool(), d_tits(d_pool) 00690 00691 , d_G(GR.complexGroup()) 00692 , d_fundf(d_G.fundamental()) 00693 00694 , d_srf() // d_fundf.strongRepresentative(GR.realForm()) 00695 , d_base_compact() // d_fundf.compactRoots(d_fundf.class_base(d_srf.second)) 00696 00697 // only the final fields depend on the real form of |GR| 00698 , d_gradingOffset(grading_offset_for(GR)) 00699 , d_fiberData(d_G,GR.cartanSet()) 00700 { 00701 KGBElt size = GR.kgbSize(); 00702 00703 d_pool.reserve(size); 00704 d_info.reserve(size); 00705 00706 // set up cross and cayley tables with undefined values 00707 for (size_t j = 0; j < d_rank; ++j) 00708 { 00709 d_cross[j].resize(size,UndefKGB); 00710 d_cayley[j].resize(size,0); // leave unset (set by |cayleyExtend|) 00711 } 00712 00713 // set identity Tits element as seed of the KGB construction 00714 d_tits.match(tits::TitsElt(d_titsGroup)); 00715 00716 // store its length and Cartan class (the latter is in fact always 0) 00717 d_info.push_back(KGBInfo(0,d_fiberData.cartanClass(d_tits[0].tw()))); 00718 } 00719 00720 00721 /*! 00722 \brief The helper constructor with a given set of Cartan classes initializes 00723 the lists with an element for each minimal Cartan class. 00724 00725 The base grading is set up to correspond to (the chosen adjoint fiber 00726 element in the fundamental Cartan for) the central square class of this 00727 real form, which is done by the call to |square_class_grading_offset|. 00728 00729 The initial element then represents the place within its central square class 00730 of one chosen strong real form |srf| lying over this weak real form; it has 00731 the identity twisted involution, and a torus factor obtained by lifting the 00732 representative fiber group element |srf.first| via the |fromBasis| method of 00733 the fiber group back to the ``coweight lattice modulo 2'' \f$Y/2Y\f$. 00734 00735 00736 Here we actually look up the strong real form in order to get a proper 00737 initial Tits group element associated to this Cartan 00738 */ 00739 KGBHelp::KGBHelp(realredgp::RealReductiveGroup& GR, 00740 const bitmap::BitMap& Cartan_classes) 00741 : d_rank(GR.semisimpleRank()) 00742 , d_titsGroup(GR.titsGroup()) 00743 00744 , d_cross(d_rank) 00745 , d_cayley(d_rank) 00746 , d_info() 00747 00748 , d_pool() // no elements a priori 00749 , d_tits(d_pool) 00750 00751 , d_G(GR.complexGroup()) 00752 , d_fundf(d_G.fundamental()) 00753 00754 // only the final fields depend on the real form of |GR| 00755 , d_srf(d_fundf.strongRepresentative(GR.realForm())) 00756 , d_base_compact(d_fundf.compactRoots(d_fundf.class_base(d_srf.second))) 00757 , d_gradingOffset(square_class_grading_offset(d_G,d_srf.second)) 00758 , d_fiberData(d_G,Cartan_classes) 00759 { 00760 const cartanset::CartanClassSet& ccs=d_G.cartanClasses(); 00761 realform::RealForm rf=GR.realForm(); 00762 KGBElt size = ccs.KGB_size(rf,Cartan_classes); 00763 00764 assert(d_srf.second==d_fundf.central_square_class(rf)); 00765 00766 d_pool.reserve(size); 00767 d_info.reserve(size); 00768 00769 // set up cross and cayley tables with undefined values 00770 for (size_t j = 0; j < d_rank; ++j) { 00771 d_cross[j].resize(size,UndefKGB); 00772 d_cayley[j].resize(size,0); // leave undefined (set by |cayleyExtend|) 00773 } 00774 00775 set::SetEltList m=ccs.ordering().minima(Cartan_classes); 00776 for (size_t i=0; i<m.size(); ++i) 00777 { 00778 tits::TitsElt a=grading_seed(rf,m[i]); 00779 00780 // now add KGB element for the reduced Tits group element 00781 #ifdef DEBUG 00782 size_t k=d_tits.match(a); 00783 assert(k==d_info.size()); // this KGB element should be new 00784 #else 00785 d_tits.match(a); // enter new KGB element into the tables 00786 #endif 00787 00788 // add additional infomation (length,Cartan class) for this KGB element 00789 d_info.push_back(KGBInfo(GR.weylGroup().involutionLength(a.tw()),m[i])); 00790 } 00791 } 00792 00793 /******** public methods ****************************************************/ 00794 00795 /*! 00796 \brief Constructs the full orbit set. 00797 00798 Precondition: the object is in the initial state, the one it is put in by 00799 the call to its constructor (at least one seed element is present). 00800 00801 Algorithm: The idea is just to start out from the given element, 00802 and then to saturate through cross actions and Cayley transforms. 00803 00804 It is important that for each element the cross actions are defined before 00805 the Cayley transforms is tried, because the status information set by the 00806 former is used by the latter. 00807 */ 00808 KGBHelp& KGBHelp::fill() 00809 { 00810 for (KGBElt j = 0; j < d_pool.size(); ++j) { 00811 // these calls will usually enlarge |d_pool.size()|; 00812 cross_extend(j); 00813 cayleyExtend(j); 00814 } 00815 return *this; 00816 } 00817 00818 size_t KGBHelp::export_tables(std::vector<KGBEltList>& cross, 00819 std::vector<KGBEltList>& cayley, 00820 weyl::TwistedInvolutionList& twisted, 00821 std::vector<KGBInfo>& info, 00822 bool traditional) const 00823 { 00824 size_t size=d_info.size(); 00825 00826 // sort (partially) 00827 setutils::Permutation a(size,1); 00828 IndexCompare comp(*this); 00829 if (traditional) 00830 std::sort(a.begin(),a.end(),comp); 00831 else 00832 std::stable_sort(a.begin(),a.end(),comp); // better, faster, more reliable 00833 00834 // export the cross and cayley maps, permuting each constituent list 00835 cross.clear(); cayley.clear(); 00836 cross.reserve(d_rank); cayley.reserve(d_rank); 00837 setutils::Permutation ai(a,-1); // compute inverse of |a| 00838 for (size_t s = 0; s < d_rank; ++s) { 00839 cross.push_back(a.pull_back(ai.renumber(d_cross[s]))); 00840 cayley.push_back(a.pull_back(ai.renumber(d_cayley[s],UndefKGB))); 00841 } 00842 00843 // export the other lists 00844 twisted.clear(); twisted.reserve(size); 00845 for (KGBElt x=0; x<size; ++x) 00846 twisted.push_back(d_pool[a[x]].tw()); // strip torus part off Tits elements 00847 00848 00849 a.pull_back(d_info).swap(info); 00850 00851 return size; 00852 } 00853 00854 /******** accessors (private) ***********************************************/ 00855 00856 /* The following methods are only used only from within the second 00857 constructor, when the fields |d_srf| and |d_base_compact| have been set. */ 00858 00859 /* The reasoning given for |simple_grading| fails for non-simple roots, so we 00860 cannot easily compute the grading at arbitrary imaginary roots even in the 00861 fundamental fiber, using only |d_gradingOffset|. However, |d_base_compact| 00862 knows the full grading by $x_0.\delta$; we can still use a scalar product. 00863 */ 00864 bool KGBHelp::is_compact(const tits::TorusPart& x, rootdata::RootNbr n) const 00865 { 00866 latticetypes::SmallBitVector rn(d_G.rootDatum().root(n)); 00867 return d_base_compact.isMember(n) ^ bitvector::scalarProduct(x,rn); 00868 } 00869 00870 /* To compute the grading at arbitrary imaginary roots at an arbitrary fiber, 00871 no simple closed formula seems avaiable. So we revert to appying a set of 00872 cross actions to convert the question into one about a simple root. 00873 */ 00874 bool KGBHelp::grading(tits::TitsElt a, rootdata::RootNbr n) const 00875 { 00876 const rootdata::RootDatum& rd=d_G.rootDatum(); 00877 if (not rd.isPosRoot(n)) 00878 n=rd.rootMinus(n); 00879 00880 assert(rd.isPosRoot(n)); 00881 size_t i; // declare outside loop to allow inspection of final value 00882 do 00883 for (i=0; i<rd.semisimpleRank(); ++i) 00884 if (rd.scalarProduct(rd.simpleRootNbr(i),n)>0) 00885 if (n==rd.simpleRootNbr(i)) 00886 return simple_grading(a,i); 00887 else 00888 { 00889 rd.rootReflect(n,i); 00890 basedTwistedConjugate(a,i); 00891 break; 00892 } 00893 while (i!=rd.semisimpleRank()); // i.e., until no change occurs any more 00894 00895 assert(false); // root |n| cannot become negative without becoming simple 00896 return false; 00897 } 00898 00899 /* The functions below can be used to compute an initial value for a (partial) 00900 KGB construction for a weak real form |rf| starting at Cartan class |cn|, 00901 using various methods with various degrees of success. They should be used 00902 only from the |KGBHelp| constructor with a |Cartan_classes| argument. 00903 */ 00904 00905 /* The method |naive_seed| attempts to get an initial Tits group element by 00906 extracting the necessary information fom the |Fiber| object associated to 00907 the Cartan class |cn|, and lifting that information from the level of the 00908 fiber group back to the level of torus parts. But as the name indicates, 00909 the result is not always good; the main case where it works reliably is for 00910 the fundamental Cartan (|cn==0|). The circumstance that makes it useless in 00911 other fibers is that it fails to account for the different grading choices 00912 involved in identifying the (weak) real form in the fiber and in the KGB 00913 construction, so that there is no guarantee that the lifted element will 00914 appear to belong to the correct real form. In fact the computed Tits 00915 element |a| might not even give a strong involution $a.x_0.\delta$ at all. 00916 00917 The main reason for leaving this (unused) method in the code is that it 00918 provides an alternative method (if it were called from the second |KGBHelp| 00919 constructor) to choosing the identity Tits element as starting point in the 00920 fundamental fiber, and that it illustrates at least how lifting of 00921 information from the fiber group should be done. 00922 */ 00923 tits::TitsElt KGBHelp::naive_seed(realform::RealForm rf, size_t cn) const 00924 { 00925 // locate fiber, weak and strong real forms, and check central square class 00926 const cartanclass::Fiber& f=d_G.cartan(cn).fiber(); 00927 const cartanset::CartanClassSet& ccs=d_G.cartanClasses(); 00928 cartanclass::adjoint_fiber_orbit wrf = ccs.real_form_part(rf,cn); 00929 cartanclass::StrongRealFormRep srf=f.strongRepresentative(wrf); 00930 assert(srf.second==f.central_square_class(wrf)); 00931 00932 // now lift strong real form from fiber group to a torus part in |result| 00933 latticetypes::SmallBitVector v(bitset::RankFlags(srf.first),f.fiberRank()); 00934 tits::TorusPart x = f.fiberGroup().fromBasis(v); 00935 tits::TitsElt result(d_titsGroup,x); 00936 00937 // right-multiply this torus part by canonical twisted involution for |cn| 00938 const weyl::TwistedInvolution& tw=ccs.twistedInvolution(cn); 00939 d_titsGroup.mult(result,tits::TitsElt(d_titsGroup,tw)); 00940 d_fiberData.reduce(result); 00941 return result; 00942 } 00943 00944 /* The method |grading_seed| attempts to correct the shortcomings of 00945 |naive_seed| by insisting on obtaining an element exhibiting a grading that 00946 corresponds to the real form |rf|. Thus no element is actually recovered 00947 from any fiber group, but rather a set of equations for the torus part is 00948 set up and solved. Giving the right grading, one hopes that the Tits 00949 element automatically defines a strong involution, but we do not check this 00950 below (in fact we do not know how such a check should be performed). 00951 00952 Although the system of equations is highly underdetermined, it might 00953 suffice to fix a correct torus part modulo the subspace by which these 00954 torus parts are systematically reduced, and modulo torus parts that lie in 00955 $Z(G)$ (which cannot be detected by any grading of roots, but which for the 00956 same reason have no effect on the construction); in any case the seed 00957 produced here seems to work for partial KGB constructions that are useful 00958 or the construction of (small) blocks. In the more general case that there 00959 can be more than one minimal Cartan class in the set demanded, this method 00960 would risk giving non-coherent seeds in different classes, since nothing 00961 guarantees that the choices made will belong to the same strong real form; 00962 if this happens, too much elements will be generated Cartan classes that 00963 lie above more than one minimal Cartan class. 00964 */ 00965 tits::TitsElt KGBHelp::grading_seed(realform::RealForm rf, size_t cn) const 00966 { 00967 // locate root datum, fiber, and weak real form 00968 const rootdata::RootDatum& rd=d_G.rootDatum(); 00969 const cartanclass::Fiber& f=d_G.cartan(cn).fiber(); 00970 const cartanset::CartanClassSet& ccs=d_G.cartanClasses(); 00971 cartanclass::adjoint_fiber_orbit wrf = ccs.real_form_part(rf,cn); 00972 00973 // get an element lying over the canonical twisted involution for |cn| 00974 tits::TitsElt a(d_titsGroup,ccs.twistedInvolution(cn)); // trial element 00975 00976 // get the grading of the imaginary root system given by the element |a| 00977 gradings::Grading base_grading; 00978 for (size_t i=0; i<f.imaginaryRank(); ++i) 00979 base_grading.set(i,grading(a,f.simpleImaginary(i))); 00980 00981 // get the grading of the same system given by chosen representative of |wrf| 00982 gradings::Grading form_grading = f.grading(f.weakReal().classRep(wrf)); 00983 00984 /* set up equations with simple imaginray roots as left hand sides, and with 00985 the the bits of |base_grading-form_grading| as right hand side. 00986 */ 00987 latticetypes::BinaryEquationList eqns(f.imaginaryRank()); 00988 for (size_t i = 0; i < eqns.size(); ++i) 00989 { 00990 latticetypes::BinaryEquation& equation = eqns[i]; 00991 equation = latticetypes::BinaryEquation(rd.root(f.simpleImaginary(i))); 00992 equation.pushBack((base_grading^form_grading)[i]); 00993 } 00994 00995 // solve, and tack a solution |x| to the left of |a|. 00996 tits::TorusPart x(d_G.rank()); 00997 #ifdef DEBUG 00998 bool success=bitvector::firstSolution(x,eqns); 00999 assert(success); 01000 #else 01001 bitvector::firstSolution(x,eqns); 01002 #endif 01003 01004 titsGroup().left_add(x,a); // form element $x.\sigma_w$ 01005 01006 d_fiberData.reduce(a); 01007 01008 // double-check that we have found an element that gives the desired grading 01009 for (size_t i=0; i<f.imaginaryRank(); ++i) 01010 assert(grading(a,f.simpleImaginary(i))==form_grading[i]); 01011 return a; 01012 } 01013 01014 /* In this final and most elaborate seeding function, which is also the most 01015 reliable one, we stoop down to simulating the KGB construction back from 01016 the fundamental fiber to the one for which we try to find a seed, and to 01017 try all the representatives in the fundamental fiber of the strong real 01018 form, until finding one that, along the chosen path of cross actions and 01019 Cayley transforms, proves to be suited for every necessary Cayley transform 01020 (making the simple root involved noncompact). 01021 */ 01022 tits::TitsElt KGBHelp::backtrack_seed(realform::RealForm rf, size_t cn) const 01023 { 01024 const rootdata::RootDatum& rd=d_G.rootDatum(); 01025 const weyl::WeylGroup& W= d_G.weylGroup(); 01026 01027 const cartanset::CartanClassSet& ccs=d_G.cartanClasses(); 01028 const weyl::TwistedInvolution& tw=ccs.twistedInvolution(cn); 01029 01030 rootdata::RootList Cayley; 01031 weyl::WeylWord cross; 01032 cartanset::cayley_and_cross_part(Cayley,cross,tw,rd,W); 01033 01034 /* at this point we can get from the fundamental fiber to |tw| by first 01035 applying cross actions according to |cross|, and then applying Cayley 01036 transforms in the strongly orthogonal set |Cayley|. 01037 */ 01038 01039 // transform strong orthogonal set |Cayley| back to distinguished involution 01040 for (size_t i=0; i<Cayley.size(); ++i) 01041 for (size_t j=cross.size(); j-->0; ) 01042 rd.rootReflect(Cayley[i],cross[j]); 01043 01044 /* at this point we can get from the fundamental fiber to |tw| by first 01045 applying Cayley transforms in the strongly orthogonal set |Cayley|, and 01046 then applying cross actions according to |cross| 01047 */ 01048 01049 /* Now find an element in the chosen strong real form at the fundamental 01050 fiber, that has noncompact grading on all the roots of |Cayley| (which 01051 are imaginary for $\delta$) 01052 */ 01053 tits::TitsElt result(d_titsGroup); 01054 01055 partition::Partition srp = d_fundf.strongReal(d_srf.second); 01056 for (unsigned long x=0; x<srp.size(); ++x) 01057 if (srp(x)==srp(d_srf.first)) 01058 { 01059 latticetypes::SmallBitVector v 01060 (static_cast<bitset::RankFlags>(x),d_fundf.fiberRank()); 01061 tits::TorusPart t =d_fundf.fiberGroup().fromBasis(v); 01062 for (size_t i=0; i<Cayley.size(); ++i) 01063 if (is_compact(t,Cayley[i])) 01064 goto again; // none of the |Cayley[i]| should be compact 01065 01066 // if we get here, |t| is OK as torus part 01067 result = tits::TitsElt(d_titsGroup,weyl::WeylElt(),t); 01068 goto found; 01069 again: {} 01070 } 01071 assert(false); // getting here means none of the orbit elements is in order 01072 01073 found: 01074 01075 /* Now we must apply the Cayley transforms and cross actions to |result|. 01076 However, Cayley transforms by non-simple roots are not implemented, and 01077 so we reorder the operations as in |W.involution_expr(tw)|, which gives 01078 the same cross actions, but interspersed with simple Cayley transforms. 01079 */ 01080 01081 // transform |result| via Cayley transforms and cross actions 01082 std::vector<signed char> dec=W.involution_expr(tw); 01083 for (size_t j=dec.size(); j-->0; ) 01084 if (dec[j]>=0) 01085 { 01086 assert(simple_grading(result,dec[j])); // simple root must be noncompact 01087 cayleyTransform(result,dec[j]); 01088 } 01089 else 01090 basedTwistedConjugate(result,~dec[j]); 01091 01092 assert(result.tw()==tw); 01093 d_fiberData.reduce(result); 01094 01095 return result; 01096 } 01097 01098 /******** manipulators *******************************************************/ 01099 01100 /*! 01101 \brief Tries to enlarge the parameter set by cross-actions, without 01102 supposing that elements are generated by increasing order of twisted 01103 involution length 01104 01105 Precondition: |parent| is the index into |d_tits| of the parameter we are 01106 extending from. 01107 */ 01108 void KGBHelp::cross_extend(KGBElt parent) 01109 { 01110 const tits::TitsElt current = d_tits[parent]; 01111 01112 // try to get new elements by cross-action 01113 for (size_t s = 0; s < d_rank; ++s) { 01114 // see if link was already filled 01115 if (d_cross[s][parent] != UndefKGB) 01116 continue; 01117 01118 // twisted-conjugate (using base grading) |current| by |s| 01119 tits::TitsElt a = current; basedTwistedConjugate(a,s); 01120 01121 /* Check that this operation is its own inverse 01122 { 01123 tits::TitsElt b=a; basedTwistedConjugate(b,s); 01124 d_fiberData.reduce(b); 01125 assert(b==current); 01126 } 01127 */ 01128 01129 /* Check that result is independent of representative: 01130 { 01131 latticetypes::SmallBitVectorList b = 01132 d_fiberData.mod_space(current).basis(); 01133 for (size_t i=0; i<b.size(); ++i) 01134 { 01135 TitsElt ai=current; ai+=b[i]; basedTwistedConjugate(ai,s); 01136 assert(ai.tw()==a.tw()); 01137 d_fiberData.reduce(ai); 01138 assert(ai.t()==a.t()); 01139 } 01140 } 01141 */ 01142 01143 // find the Tits element 01144 d_fiberData.reduce(a); 01145 01146 /* test whether action was (more or less) as computed in cartanclass.cpp 01147 if (a.tw()!=current.tw()) 01148 { weyl::Generator t = titsGroup().twist(s); 01149 tits::TorusPart x= current.t(); 01150 titsGroup().reflect(x,t); 01151 if (not d_gradingOffset.test(s)) 01152 x += titsGroup().simpleCoroot(t); 01153 tits::TitsElt b(weylGroup().twistedConjugated(current.w(),s),x); 01154 d_fiberData.reduce(b); 01155 assert(a==b); 01156 } 01157 */ 01158 01159 // involution length changes by 0 for twisted commutation, or else by +/-1 01160 int lc= 01161 a.tw()==current.tw() ? 0 : weylGroup().length_change(s,current.w()); 01162 01163 // now find the Tits element 01164 KGBElt child = d_tits.match(a); 01165 if (child==d_info.size()) // add a new Tits element 01166 d_info.push_back(KGBInfo(d_info[parent].length+lc, 01167 d_fiberData.cartanClass(a.tw()) 01168 )); 01169 01170 // set cross links for |parent| and for |child| (whether new or old) 01171 d_cross[s][parent] = child; 01172 d_cross[s][child] = parent; 01173 01174 if (lc!=0) // then set complex status, and whether ascent or descent 01175 { 01176 d_info[parent].status.set(s,gradings::Status::Complex); 01177 d_info[child].status.set(s,gradings::Status::Complex); 01178 d_info[parent].desc.set(s,lc<0); 01179 d_info[child].desc.set(s,lc>0); 01180 } 01181 else if (weylGroup().hasDescent(s,current.w())) // real 01182 { 01183 d_info[parent].status.set(s,gradings::Status::Real); // |child==parent| 01184 d_info[parent].desc.set(s,true); // real roots are always descents 01185 } 01186 else// imaginary 01187 { 01188 d_info[parent].status.set_imaginary(s,simple_grading(current,s)); 01189 d_info[parent].desc.set(s,false); // imaginary roots are never descents 01190 if (child!=parent) // give child same status (which will be noncompact) 01191 { 01192 d_info[child].status.set(s,d_info[parent].status[s]); 01193 d_info[child].desc.set(s,false); 01194 } 01195 } 01196 } 01197 } 01198 01199 01200 /*! 01201 \brief Tries to enlarge the parameter set by Cayley transforms from |parent|. 01202 01203 Precondition: |parent| is a |KGBElt| whose |status| field in |d_info| has 01204 been set to the proper value. 01205 01206 Calling this function also sets the fields |d_cayley[s][parent]| either to 01207 the appropriate value or to |UndefKGB| (it was 0, which is neither of those) 01208 01209 It is assumed that the it is an invariant of the |KGBHelp| structure that 01210 all downward links are already filled in. 01211 */ 01212 void KGBHelp::cayleyExtend(KGBElt parent) 01213 { 01214 const tits::TitsElt current = d_tits[parent]; 01215 01216 // try to get new elements by Cayley transform 01217 for (size_t s = 0; s < d_rank; ++s) { 01218 01219 gradings::Status::Value v = d_info[parent].status[s]; 01220 if (v != gradings::Status::ImaginaryNoncompact) { 01221 d_cayley[s][parent] = UndefKGB; 01222 continue; 01223 } 01224 01225 // cayley-transform |current| by $\sigma_s$ 01226 tits::TitsElt a = current; cayleyTransform(a,s); 01227 assert(titsGroup().length(a)>titsGroup().length(current)); // should go up 01228 01229 01230 // now look up the correspondingly reduced Tits element 01231 d_fiberData.reduce(a); // subspace has grown, so mod out new supspace 01232 KGBElt x = d_tits.match(a); 01233 if (x==d_info.size()) // add a new Tits element 01234 d_info.push_back(KGBInfo(d_info[parent].length+1, // length goes up 01235 d_fiberData.cartanClass(a.tw()) 01236 )); 01237 // add new cayley link 01238 d_cayley[s][parent] = x; 01239 } 01240 } 01241 01242 01243 01244 } // namespace 01245 } // namespace kgb 01246 01247 /***************************************************************************** 01248 01249 Chapter III -- Functions local to kgb.cpp 01250 01251 ******************************************************************************/ 01252 01253 namespace kgb { 01254 namespace { 01255 01256 /*! \brief 01257 Returns the grading offset (on simple roots) adapted to |G|. This flags the 01258 simple roots that are noncompact imaginary at the fundamental Cartan in G. 01259 01260 Algorithm: the variable |rset| is first made to flag, among the imaginary 01261 roots of the fundamental Cartan, those that are noncompact for the chosen 01262 representative (in the adjoint fiber) of the real form of |G|. The result is 01263 formed by extracting only the information concerning the presence of the 01264 \emph{simple} roots in |rset|. 01265 */ 01266 gradings::Grading grading_offset_for(const realredgp::RealReductiveGroup& G) 01267 { 01268 const rootdata::RootDatum& rd = G.rootDatum(); 01269 rootdata::RootSet rset= G.noncompactRoots(); 01270 01271 return cartanclass::restrictGrading(rset,rd.simpleRootList()); 01272 } 01273 01274 /*! 01275 \brief Returns the grading offset for the base real form of |csc| 01276 01277 Precondition: |csc| specifies a square class (coset in adjoint fiber group) 01278 */ 01279 gradings::Grading 01280 square_class_grading_offset(const complexredgp::ComplexReductiveGroup& G, 01281 cartanclass::square_class csc) 01282 { 01283 const cartanclass::Fiber& f=G.fundamental(); 01284 rootdata::RootSet rset = f.noncompactRoots(f.class_base(csc)); 01285 01286 return cartanclass::restrictGrading(rset,G.rootDatum().simpleRootList()); 01287 } 01288 01289 void makeHasse(std::vector<set::SetEltList>& hd, const KGB& kgb) 01290 01291 /*! 01292 \brief Puts in hd the hasse diagram data for the Bruhat ordering on KGB. 01293 01294 Explanation: this is the closure ordering of orbits. We use the 01295 algorithm from Richardson and Springer. 01296 */ 01297 01298 { 01299 using namespace gradings; 01300 using namespace poset; 01301 using namespace set; 01302 01303 hd.resize(kgb.size()); 01304 01305 for (KGBElt x = 0; x < kgb.size(); ++x) { 01306 01307 const Descent& d = kgb.descent(x); 01308 if (d.none()) // element is minimal in bruhat order 01309 continue; 01310 01311 size_t s = d.firstBit(); 01312 KGBElt sx; 01313 01314 if (kgb.status(s,x) == Status::Complex) { // s is complex 01315 sx = kgb.cross(s,x); 01316 hd[x].push_back(sx); 01317 } else { // s is real for x 01318 KGBEltPair sxp = kgb.inverseCayley(s,x); 01319 hd[x].push_back(sxp.first); 01320 if (sxp.second != UndefKGB) // s is real type I for x 01321 hd[x].push_back(sxp.second); 01322 sx = sxp.first; 01323 } 01324 01325 for (size_t j = 0; j < hd[sx].size(); ++j) { 01326 KGBElt z = hd[sx][j]; 01327 if (kgb.isAscent(s,z)) { 01328 if (kgb.status(s,z) == Status::ImaginaryNoncompact) 01329 hd[x].push_back(kgb.cayley(s,z)); 01330 else // s is complex for z 01331 hd[x].push_back(kgb.cross(s,z)); 01332 } 01333 } 01334 01335 std::sort(hd[x].begin(),hd[x].end()); 01336 } 01337 01338 } 01339 01340 } // namespace 01341 } // namespace kgb 01342 01343 } // namespace atlas
1.3.9.1