Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members

/home/r0/dav/atlas.dir/atlas3/sources/gkmod/kgb.cpp

Go to the documentation of this file.
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

Generated on Wed Mar 26 16:49:33 2008 for atlas by  doxygen 1.3.9.1