1 import scipy 2 import scipy.linalg 3 import math 4 5 """ 6 7 A code to simplify calculation of BKL billiard systems based on 8 three-dimensional coset models. 9 10 By Daniel H. Wesley 11 D.H.Wesley at damtp.cam.ac.uk 12 13 If this code is used in your research, please cite: 14 15 "Coxeter group structure of cosmological billiards on compact spatial 16 manifolds" Marc Henneaux, Daniel Persson, Daniel H. Wesley (to appear) 17 18 """ 19 20 21 class Billiard: 22 23 def __init__(self, d, forms, dil=True ): 24 """ 25 d: spatial dimensions 26 forms: a list of tuples (p, lamb, label) 27 dil: True if dilaton, False if no dilaton 28 29 Given this information, the class constructs the gravitational, 30 symmetry, and p-form walls corresponding to this billiard. The 31 walls themselves are stored as 1D SciPy arrays for easy manip- 32 ulation. 33 34 The p-forms must be labelled to make things easier: 35 Labels are just strings that are carried along for the ride. 36 Ordering of the labels is important. For 37 p-forms, the letter "E" or "B" is prepended to the label given 38 depending on whether the electric or magnetic components are 39 being discussed. 40 41 All of the couplings and so on are assumed to be in the Einstein 42 frame. This allows the study of theories with and without dilatons 43 in a single framework. Note that only one dilaton is currently 44 supported. 45 46 The caller has to manually set the dominant walls. This can be 47 done by just setting which labelled walls one wants to have in 48 the dominant wall set. 49 """ 50 51 self.d = d 52 self.forms = forms 53 self.dil = dil 54 55 # This has to be done manually, since it isn't clear 56 # algorithmically how one selects the dominant walls. 57 self.domWallMatrix = None 58 59 # This is a dictionary holding the map between the label for 60 # and the wall itself. Newly created walls should register 61 # themselves here. 62 self.walls = { } 63 64 self.domWallLabels = [] 65 self.domWalls = [] 66 67 self.coweightMatrix = None 68 69 self.makeWalls() 70 71 def makeWalls(self): 72 """ 73 This function makes all of the wall types. 74 """ 75 76 self.makeSWalls() 77 self.makeEWalls() 78 self.makeBWalls() 79 self.makeGWalls() 80 81 82 def makeSWalls(self): 83 """ 84 Make the symmetry walls, starting from beta^2-beta^1 up to beta^d-beta^(d-1) 85 """ 86 87 # These go from w2 = \beta^2 - \beta^1 all the way 88 # to \beta^d - \beta^{d-1} 89 90 sWalls = [] 91 92 for j in range(2,self.d+1): 93 new_wall = self.makeSWall(j) 94 new_wall_label = self.makeSymName(j) 95 sWalls.append( new_wall ) 96 self.walls[ new_wall_label ] = new_wall 97 98 self.sWalls = sWalls 99 100 101 def makeSWall(self, j): 102 """ Make the symmetry wall with \beta^j - (etc) """ 103 104 if ( (j>self.d) or (j<2) ): 105 return None 106 107 if self.dil: 108 wall = [0.] # first component is always for the scalar 109 else: 110 wall = [] 111 112 niz = j-2 # digits in 1...(j-1) 113 if (niz > 0): 114 wall = wall + ( [0.] * niz ) 115 116 wall = wall + ( [-1., 1.] ) 117 118 nfz = self.d-j # digits in j ... d 119 if (nfz > 0): 120 wall = wall + ( [0.] * nfz ) 121 122 return scipy.array(wall) 123 124 125 def makeEWalls(self): 126 """ 127 Make the electric wall set 128 """ 129 130 eWalls = [] 131 132 for f in self.forms: 133 # We have to unpack the tuple, since makeEWall only 134 # cares about "p" and "d" here, while we care about the 135 # label as well. 136 137 new_wall = self.makeEWall( f[:2] ) 138 eWalls.append( new_wall ) 139 140 new_wall_label = "E" + f[2] 141 self.walls[ new_wall_label ] = new_wall 142 143 self.eWalls = eWalls 144 145 146 def makeEWall(self, ft): 147 """ 148 Make an individual electric wall. 149 The second argument is ft=(p,lamb) 150 """ 151 152 (p,lamb) = ft 153 154 if self.dil: 155 wall = [ 0.5*lamb ] 156 else: 157 wall = [] 158 159 if (p > 0): 160 wall = wall + ( [1.] * p ) 161 162 if (self.d-p > 0): 163 wall = wall + ( [0.] * (self.d-p) ) 164 165 return scipy.array(wall) 166 167 168 def makeBWalls(self): 169 170 bWalls = [] 171 172 for f in self.forms: 173 new_wall = self.makeBWall( f[:2] ) 174 bWalls.append( new_wall ) 175 176 new_wall_label = "B" + f[2] 177 self.walls[ new_wall_label ] = new_wall 178 179 self.bWalls = bWalls 180 181 182 def makeBWall(self, ft): 183 184 (p,lamb) = ft 185 186 if self.dil: 187 wall = [ -0.5*lamb ] 188 else: 189 wall = [] 190 191 if (self.d - p - 1 > 0): 192 wall = wall + [1.] * (self.d - p - 1) 193 194 wall = wall + [0.] * (p+1) 195 196 return scipy.array(wall) 197 198 199 def makeGWalls(self): 200 201 gwall = self.makeGWall() 202 self.gWalls = [ gwall ] 203 self.walls[ "G" ] = gwall 204 205 206 def makeGWall(self): 207 208 if self.dil: 209 wall = [ 0. ] 210 else: 211 wall = [] 212 213 wall = wall + [2.] 214 215 if ( self.d-3 > 0): 216 wall = wall + [1.]*(self.d-3) 217 218 wall = wall + [0.]*2 219 220 return scipy.array(wall) 221 222 223 def wallDot(self, w1, w2 ): 224 """ 225 Compute the inner product of two walls 226 """ 227 228 if self.dil: 229 dil1 = w1[0] 230 dil2 = w2[0] 231 x1 = w1[1:] 232 x2 = w2[1:] 233 else: 234 dil1 = 0. 235 dil2 = 0. 236 x1 = w1 237 x2 = w2 238 239 dot = x1.sum() * x2.sum() 240 dot = dot * -1./float(self.d-1.) 241 242 dot = dot + (x1*x2).sum() 243 dot = dot + dil1*dil2 244 245 return dot 246 247 248 def wallNorm(self, w): 249 return self.wallDot(w,w) 250 251 252 def getNonSymWalls(self): 253 """ 254 Return a list of labels for walls that do not correspond to 255 symmetry walls. 256 257 Right now this is just implemented by checking the first character 258 of the wall label. Maybe in the future there will be a more 259 sophisticated way of doing this. 260 """ 261 262 return filter( lambda s : s[0] != 'S', self.walls.keys() ) 263 264 265 def setDomWalls(self, domWallLabels ): 266 """ 267 domWallLabels: a list of wall labels (strings) 268 269 This sets which walls one wants in the dominant wall set. 270 One simply chooses which (non-symmetry) walls are desired. 271 The symmetry walls are automatically included. 272 273 In the end this function also calculates the coweights 274 and their norms. 275 """ 276 277 # Implementation note: the symmetry walls are found by just 278 # looking at the list of self.sWalls 279 280 # Now we have to copy over the desired walls without losing track 281 # of the labels. The wrinkle is that to form the dominant wall array 282 # we have to change the arrays to lists, which we can then mold into 283 # an array. 284 285 # Implementation note: these have to be lists, because we need to keep 286 # ordering information about them! 287 288 self.domWallLabels = [] 289 self.domWalls = [] 290 291 # First add the requested walls 292 293 # This seems perverse, but the idea is that the caller has requested that 294 # the walls in domWallLabels be properly added to self.domWallLables 295 for wl in domWallLabels: 296 self.domWallLabels.append(wl) 297 self.domWalls.append( self.walls[ wl ] ) 298 299 # Now add on the symmetry walls 300 301 for j in range(len(self.sWalls)): 302 # Can't reverse the lookup in a dict 303 self.domWallLabels.append( self.makeSymName(j+2) ) 304 self.domWalls.append( self.sWalls[j] ) 305 306 # Now the dominant walls are all set. Make the wall matrix 307 308 tmp = [] 309 for w in self.domWalls: 310 tmp.append( w.tolist() ) 311 312 self.domWallMatrix = scipy.asarray(tmp) 313 314 # Finally, to keep everything straight, we have to also calculate 315 # the coweights so all is synchronized. 316 317 self.calcCoweights() 318 319 # And we have to compute the Cartan matrix. 320 # Potentially, this is the transpose of the Cartan matrix 321 322 A_mtrx = [] 323 for dw1 in self.domWalls: 324 A_row = [] 325 for dw2 in self.domWalls: 326 A_row = A_row + [ 2. * self.wallDot( dw1, dw2 )\ 327 / self.wallNorm( dw1 ) ] 328 A_mtrx.append( A_row ) 329 330 self.cartanMatrix = scipy.asarray(A_mtrx) 331 self.cartanMatrixEigenvalues = scipy.linalg.eigvals(self.cartanMatrix) 332 self.cartanMatrixEigenvalues = self.cartanMatrixEigenvalues.real 333 334 335 def calcCoweights(self): 336 337 (size, size1) = self.domWallMatrix.shape 338 339 if (size != size1): 340 print "Billiard.calcCoweights: matrix not square" 341 self.coweights = None 342 self.coweightNorms = None 343 return 344 345 tmp = scipy.linalg.inv(self.domWallMatrix) 346 self.coweightMatrix = tmp.transpose() 347 348 # Now we have the coweights. At this point there is just the 349 # task of unpacking the coweights and computing their norms. 350 351 # By symmetry, let's create a dictionary called coweights that 352 # holds these labels. 353 354 # By construction, the domWallLabels are exactly those of the 355 # coweights. So we can just re-use them. 356 357 self.coweights = {} 358 self.coweightNorms = {} 359 360 for n in range(size): 361 cw = self.coweightMatrix[n] 362 363 self.coweights[ self.domWallLabels[n] ] = cw 364 self.coweightNorms[ self.domWallLabels[n] ] = \ 365 self.coweightDot(cw,cw) 366 367 368 def coweightDot(self, cw1, cw2): 369 """ 370 Calculate the dot product of two coweights. 371 """ 372 373 if self.dil: 374 dil1 = cw1[0] 375 dil2 = cw2[0] 376 x1 = cw1[1:] 377 x2 = cw2[1:] 378 else: 379 dil1 = 0. 380 dil2 = 0. 381 x1 = cw1 382 x2 = cw2 383 384 dot = -1. * x1.sum() * x2.sum() 385 dot = dot + (x1*x2).sum() 386 dot = dot + dil1*dil2 387 388 return dot 389 390 def isChaotic(self): 391 """ 392 Return whether the theory is chaotic or not. 393 """ 394 395 # in some cases coweights are not defined 396 ct = self.countCoweightTypes() 397 398 if not ct: 399 return None 400 401 (nT, nN, nS) = self.countCoweightTypes() 402 403 if ( (nT>0) and (nS>0) ): 404 return False 405 406 return True 407 408 409 def isNonChaotic(self): 410 411 if self.isChaotic == None: 412 return None 413 414 return (not self.isChaotic() ) 415 416 417 def countCoweightTypes(self): 418 """ 419 Return the number of timelike, null, spacelike coweights. 420 421 This assumes you've already set the dominant walls, of course! 422 """ 423 424 # coweightNorms have failed (error checking) 425 if not self.coweightNorms: 426 return None 427 428 numTimelike = 0 429 numNull = 0 430 numSpacelike = 0 431 432 tol = 1.e-6 433 434 for n in self.coweightNorms.values(): 435 436 if n > tol: 437 numSpacelike += 1 438 elif (n+tol) > 0.: 439 numNull += 1 440 else: 441 numTimelike += 1 442 443 return (numTimelike, numNull, numSpacelike) 444 445 def __str__(self): 446 s = "Original model " + self.__class__.__name__ + "\n" 447 s = s + "Dominant walls " + str(self.domWallLabels) + "\n" 448 s = s + "Coweights (T,N,S) " + str(self.countCoweightTypes()) + "\n" 449 s = s + "Chaotic? " + str(self.isChaotic()) + "\n" 450 s = s + "\n" 451 s = s + self.dynkinString() 452 return s 453 454 def printDynkin(self): 455 print 456 print self.dynkinString() 457 458 def dynkinString(self): 459 """ 460 Print a Dynkin-ish diagram. 461 462 Basically, the cheat is that we always know that the symmetry 463 walls form the backbone. So just print out a list of the symm 464 walls, together with the walls that have a nonzero inner product, 465 and the number of lines connecting them. 466 467 Something like --- =>= etc. 468 469 Option to only print the dominant ones, perhaps. 470 """ 471 472 s = "" 473 474 nonSymsLabels = [e for e in self.domWallLabels if e[0] != 'S' ] 475 476 # Keep track of the nodes that we've printed 477 isPrinted = {} 478 for nsl in nonSymsLabels: 479 isPrinted[nsl] = False 480 481 for j in range(2,self.d+1): 482 symLabel = self.makeSymName(j) 483 484 # If we're not the first, then we must have a symmetry node 485 # already above us, which we should connect with the gravity line 486 if (j != 2): 487 s = s + "\n |" 488 489 # Do something clever to only loop through the non-symmetry 490 # walls. Then we must define a function that gives the number 491 # of Dynkin links between two nodes according to the usual 492 # rules of Dynkin diagrams. 493 494 if ( j != 2): 495 s = s + "\n" 496 497 s = s + " %3s o" % symLabel 498 499 # Now print all of the non-symmetry walls that have nonzero IP 500 # with this wall. 501 502 for nsLabel in nonSymsLabels: 503 d = self.wallDot( self.walls[symLabel], self.walls[nsLabel] ) 504 nsNorm = self.wallNorm( self.walls[nsLabel] ) 505 if (round(d) != 0): 506 s = s + self.makeDynkinConnect(2., nsNorm, d) + "o" 507 s = s + " %s" % nsLabel 508 # mark it printed 509 isPrinted[nsLabel] = True 510 511 # print s 512 #s = s + "\n" 513 514 # There should also be a check to report any additional linkages 515 # that are not just symmetry wall connected with another wall 516 517 for j in range(len(nonSymsLabels)): 518 for k in range(j+1,len(nonSymsLabels)): 519 w1 = self.walls[nonSymsLabels[j]] 520 w2 = self.walls[nonSymsLabels[k]] 521 d = self.wallDot( w1, w2 ) 522 if (round(d) != 0): 523 s = s + "\n\n%5s o%so %s" % (nonSymsLabels[j],\ 524 self.makeDynkinConnect( self.wallNorm(w1), \ 525 self.wallNorm(w2), d ),\ 526 nonSymsLabels[k]) 527 isPrinted[nonSymsLabels[j]]=True 528 isPrinted[nonSymsLabels[k]]=True 529 530 # Finally, there may be nodes that haven't beeen printed anywhere! 531 # Also I want to avoid a terminating carriage return. 532 for nsl in nonSymsLabels: 533 if not isPrinted[nsl]: 534 s = s + "\n\n o %s" % nsl 535 536 return s 537 538 539 def makeDynkinConnect(self, norm1, norm2, dot): 540 541 # New line algorithm that avoids some rare issues 542 543 iN1 = int(round(norm1)) 544 iN2 = int(round(norm2)) 545 546 norm = norm1 547 548 if norm2 < norm1: 549 norm = norm2 550 551 iLines = int( round( -2. * dot / norm ) ) 552 553 if iLines == 1: 554 line = "-" 555 elif iLines == 2: 556 line = "=" 557 elif iLines == 3: 558 line = "3" 559 elif iLines == 4: 560 line = "4" 561 else: 562 line = "?" 563 564 if (dot>1e-3): 565 line = "+" 566 567 if (iN1 > iN2): 568 mid = ">" 569 elif (iN1 < iN2): 570 mid = "<" 571 else: 572 mid = line 573 574 return line+mid+line 575 576 577 def makeSymName(self, j): 578 return "S" + str(j) 579 580 581 582 hetBill = Billiard(9, [ (1,-1./math.sqrt(2.),"F2"), (2,-1.*math.sqrt(2.),"H3") ] ) 583 584 585 class Compact: 586 """ 587 Defines a compactification of a wall system. 588 589 This class does very little computation, and really just defines an interface for 590 setting the zero betti numbers. 591 """ 592 593 def __init__(self, d): 594 self.d=d 595 self.betti=[1]*(self.d+1) 596 597 598 def setZeroBetti(self, zeroB): 599 """ 600 Sets a given list of Betti numbers to zero. 601 Enforces the duality. 602 603 This should be called in any descendendant classes 604 since it helps with error-checking. 605 """ 606 607 self.betti = [1] * (self.d+1) 608 #print "Compact.setZeroBetti" 609 for zb in zeroB: 610 self.betti[zb] = 0 611 # Enforce the duality 612 self.betti[self.d-zb] = 0 613 614 def getBettis(self): 615 return self.betti 616 617 def _prepCompactRules(self, billiard): 618 """ 619 Intented to be a helper function. In each class that implements the 620 Compact interface, there is some kind of decision structure that decides 621 which walls are dominant based on 1. the compactification deletions, and 622 2. some kind of dominance heirarchy. 623 624 This function does the beginning bit of this process. It creates some 625 dictionaries, keyed on the non-symmetry walls. dom keeps track of whether 626 a wall is dominant. ext keeps track of whether it exists. 627 628 This function then takes care of all of the compactification rules, and 629 passes a tuple to the caller with (wks,ext,dom) where wks are the wall 630 names. 631 632 To do this, it needs to know about the Billiard object so that it can get 633 the ps. 634 635 This function is considered private because it assumes implementation 636 details of the other stuff. 637 """ 638 639 dom = {} # whether the wall is dominant (untouched here except to init) 640 ext = {} # whether the wall exists 641 sbn = {} # to which betti is this wall sensitive? 642 wks = billiard.getNonSymWalls() 643 644 # to which betti number is a given form sensitive? 645 for (p,lamb,nom) in billiard.forms: 646 sbn[ "E"+nom ] = p 647 sbn[ "B"+nom ] = p+1 648 649 # gravity walls are not symmetry walls 650 sbn[ "G" ] = -1 651 652 for wk in wks: 653 dom[wk] = True 654 ext[wk] = True 655 656 # Now remove walls that are excluded by compactification rules 657 658 bs = self.betti 659 660 for wk in wks: 661 if not self.betti[sbn[wk]]: 662 ext[wk] = False 663 ext["G"] = True 664 665 return (wks,ext,dom) 666 667 def _postCompactRules(self, billiard, ext, dom): 668 """ 669 After the dominance heirarchy logic, set the dominant walls. 670 """ 671 672 dwl = [] 673 for w in dom.keys(): 674 if dom[w] and ext[w]: 675 dwl.append(w) 676 677 #print "Compact.__postCompactRules: setting to dom ",dwl 678 679 billiard.setDomWalls(dwl) 680 681 682 683 class cosetA(Billiard,Compact): 684 """ 685 686 The billiard based on the An groups. This yields a wall system defined 687 by An^^ = AE_{n+2}. Compactification does not affect this at all. 688 689 """ 690 691 def __init__(self, n): 692 self.n = n 693 Billiard.__init__(self, n+2, (), False ) 694 Compact.__init__(self, n+2) 695 self.setDomWalls( [ "G" ] ) 696 697 def setZeroBetti(self, zeroB): 698 Compact.setZeroBetti(self, zeroB) 699 print "bill.cosetAn.setZeroBetti has no effect" 700 701 702 class cosetB(Billiard,Compact): 703 """ 704 705 The billiard for the Bn groups. The Bn coset in D=3 oxidizes to 706 D=n+2, unless n=3 in which case it oxidizes to D=6. 707 708 one obtains the following forms: 709 710 p lamb name 711 712 1 a sqrt(2)/2 F 713 2 a sqrt(2) G 714 715 where a^2 = 8/n. (So far as I know the sign does not matter here) 716 717 When n=3 in one obtains 718 719 2 0 (no dilaton) G which is self-dual 720 721 """ 722 723 def __init__(self, n): 724 self.n = n 725 sqrt=scipy.sqrt 726 lamb1 = sqrt(8./float(n)) * sqrt(2.) / 2. 727 lamb2 = sqrt(8./float(n)) * sqrt(2.) 728 729 if (n != 3): 730 Billiard.__init__( self, n+1, [ (1, lamb1, "F2"), \ 731 (2, lamb2, "G3") ] ) 732 Compact.__init__(self, n+1) 733 self.setDomWalls( [ "EF2", "BG3" ] ) 734 else: 735 # In this case, the maximal oxidation is to a higher dimension. 736 Billiard.__init__( self, 5, [ (2, 0., "G3") ], False ) 737 Compact.__init__( self, 5) 738 self.setDomWalls( [ "BG3" ] ) 739 740 def setZeroBetti(self, zeroB): 741 Compact.setZeroBetti(self,zeroB) 742 743 #print "bill.cosetB.setZeroBetti ", zeroB 744 745 if (self.n==3): 746 return self.setZeroBettiSpecial() 747 748 (wkl,ext,dom) = self._prepCompactRules(self) 749 750 # Construction rules 751 752 # Example: if EF2 and BG3 exist, then BF2 is not dominant. 753 if ext['EF2'] and ext['BG3']: 754 dom['BF2']=False 755 756 if ext['EF2']: 757 dom['EG3']=False 758 759 if ext['BG3'] and ext['EF2']: 760 dom['G']=False 761 762 if ext['G'] and ext['BG3']: 763 dom['BF2']=False 764 765 if ext['EG3'] and ext['BG3']: 766 dom['G']=False 767 768 if ext['EF2'] and ext['BF2']: 769 dom['G']=False 770 771 self._postCompactRules(self,ext,dom) 772 773 774 def setZeroBettiSpecial(self): 775 """ 776 In this case, the oxidation can proceed further, to D=6 777 where there is no dilaton. 778 779 Oddly, here the electric and magnetic walls are the same. 780 The reason here is that G3 is self-dual. 781 """ 782 783 print "bill.cosetB.setZeroBettiSpecial" 784 785 (wkl,ext,dom) = self._prepCompactRules(self) 786 787 # The dominance heirarchy 788 789 if ext['BG3']: 790 dom['G'] = False 791 792 # The convention we take is that BG3 is the wall. 793 794 dom['EG3'] = False 795 796 self._postCompactRules(self, ext, dom) 797 798 799 class cosetC(Billiard,Compact): 800 """ 801 The billiard for the Cn groups. The Cn cosets do not oxidize beyond D=4. 802 Also, there are many dilatons. Therefore they are kind of a special case 803 that has to be handled carefully. 804 805 Fortunately this will be easy because of 806 the way in which Python handles inhertiance! 807 """ 808 809 def __init__(self, n): 810 self.d = 3 811 self.n = n 812 813 # Here I differ from usual practice 814 self.dil = n-1 815 816 # This has to be done manually, since it isn't clear 817 # algorithmically how one selects the dominant walls. 818 self.domWallMatrix = None 819 820 # This is a dictionary holding the map between the label for 821 # and the wall itself. Newly created walls should register 822 # themselves here. 823 self.walls = { } 824 825 self.domWallLabels = [] 826 self.domWalls = [] 827 self.coweightMatrix = None 828 829 self.makeWalls() 830 831 dw = [] 832 for wk in self.walls.keys(): 833 if wk != "G" and wk[0] != "S": 834 dw.append(wk) 835 self.setDomWalls( dw ) 836 837 def _padWall(self, walla ): 838 """ 839 Pad the wall created by another function to include all the 840 dilatons 841 """ 842 843 walll = walla.tolist() 844 walll = [0.] * (self.dil-1) + walll 845 return scipy.array(walll) 846 847 848 def makeSWall(self,j): 849 """ 850 The regular version does all right, but only has one dilaton. 851 """ 852 return self._padWall( Billiard.makeSWall(self,j) ) 853 854 def makeGWall(self): 855 return self._padWall( Billiard.makeGWall(self) ) 856 857 858 def setZeroBetti(self, zeroB): 859 """ 860 Again, a departure from normalcy 861 """ 862 863 Compact.setZeroBetti(self, zeroB) 864 865 b1zero = False 866 867 for b in zeroB: 868 if b == 1 or b == 2: 869 b1zero = True 870 871 if b1zero: 872 dw = [] 873 for wk in self.walls.keys(): 874 if wk[0] != "S" and wk[0] != "B": 875 dw.append(wk) 876 self.setDomWalls( dw ) 877 878 def makeEWalls(self): 879 """ 880 Thuis function needs to make both the walls and their labels. I'm 881 going to cheat here, because all of the relevant walls here are either 882 dominant or don't exist. These are all axion electric walls. So: 883 """ 884 885 x = math.sqrt(2.)/2. 886 887 self.eWalls = [] 888 889 # A unique wall 890 curr_wall = [0.] * (self.d + self.n - 1) 891 curr_wall[0] = math.sqrt(2.) 892 self.eWalls.append(curr_wall) 893 self.walls[ "E%d0" % self.n ] = scipy.asarray(curr_wall) 894 895 for j in range(2,self.n): 896 curr_wall = [ 0. ] * (self.d + self.n - 1) 897 curr_wall[self.n -j] = x 898 curr_wall[self.n-1-j] = -1. * x 899 self.eWalls.append( curr_wall ) 900 label = "EX%d0" % j 901 self.walls[ label ] = scipy.asarray(curr_wall) 902 903 def makeBWalls(self): 904 # A unique wall 905 curr_wall = [0.] * (self.d + self.n - 1) 906 curr_wall[self.n-2] = -1. * math.sqrt(2.)/2. 907 curr_wall[self.n-1] = 1. 908 self.bWalls = [ curr_wall ] 909 self.walls[ "BA1" ] = scipy.asarray(curr_wall) 910 911 def wallDot(self, w1, w2 ): 912 # Just remove everything but the last dilaton entry. There are n-1 913 # dilatons, so this means 914 n = self.n 915 d = self.d 916 ndils = self.dil 917 918 dils1 = w1[:ndils] 919 dils2 = w2[:ndils] 920 bets1 = w1[ndils:] 921 bets2 = w2[ndils:] 922 923 dot = sum( dils1*dils2 ) 924 dot += -1. * bets1.sum()*bets2.sum()/float(self.d-1) 925 dot += (bets1*bets2).sum() 926 927 return dot 928 929 def coweightDot(self, cw1, cw2): 930 """ 931 Calculate the dot product of two coweights. 932 """ 933 934 # Just remove everything but the last dilaton entry. There are n-1 935 # dilatons, so this means 936 n = self.n 937 d = self.d 938 ndils = self.dil 939 940 dils1 = cw1[:ndils] 941 dils2 = cw2[:ndils] 942 bets1 = cw1[ndils:] 943 bets2 = cw2[ndils:] 944 945 dot = (dils1*dils2).sum() 946 dot = dot -1. * bets1.sum() * bets2.sum() 947 dot = dot + (bets1*bets2).sum() 948 949 return dot 950 951 952 class cosetD(Billiard,Compact): 953 """ 954 955 The billiard for the Dn groups. These oxidize to D=n+2 dimensions where 956 one obtains the following form fields. 957 958 p lamb name 959 960 2 a sqrt(2) H 961 962 where a^2=8/n 963 964 """ 965 966 def __init__(self, n): 967 self.n = n 968 sqrt = scipy.sqrt 969 lamb = sqrt(8./float(n)) * sqrt(2.) 970 971 Billiard.__init__( self, n+1, [ (2, lamb, "H3") ], True ) 972 Compact.__init__( self, n+1 ) 973 974 self.setDomWalls( [ "EH3", "BH3" ] ) 975 976 977 def setZeroBetti(self, zeroB): 978 Compact.setZeroBetti(self, zeroB) 979 980 #print "bill.cosetD.setZeroBetti: ",zeroB 981 982 (wkl,ext,dom) = self._prepCompactRules(self) 983 984 # Dominance heirarchy 985 986 if ext["BH3"] and ext["EH3"]: 987 dom["G"] = False 988 989 self._postCompactRules(self,ext,dom) 990 991 992 class cosetE6(Billiard,Compact): 993 994 def __init__(self): 995 996 root2 = scipy.sqrt(2.) 997 998 Billiard.__init__(self, 7, [ (0, 2.*root2, "X1"), \ 999 (3, -1.*root2, "G4" ) ], True ) 1000 Compact.__init__(self, 7 ) 1001 1002 self.setDomWalls( [ "EX1", "EG4" ] ) 1003 1004 1005 def setZeroBetti( self, zeroB ): 1006 Compact.setZeroBetti( self, zeroB ) 1007 1008 #print "bill.cosetE6.setZeroBetti: ", zeroB 1009 1010 (wkl, ext, dom) = self._prepCompactRules(self) 1011 1012 # all bs nonzero 1013 if ext["EX1"] and ext["EG4"]: 1014 dom["BX1"] = False 1015 dom["BG4"] = False 1016 dom["G"] = False 1017 1018 self._postCompactRules(self, ext, dom) 1019 1020 1021 class cosetE7(Billiard, Compact): 1022 1023 def __init__(self): 1024 lamb = 2. * math.sqrt(2./7.) 1025 Billiard.__init__( self, 8, \ 1026 [ (1, -2.*lamb, "F2"), (3, lamb, "G4") ], True ) 1027 Compact.__init__( self, 8 ) 1028 self.setDomWalls( [ "EF2", "EG4" ] ) 1029 1030 def setZeroBetti( self, zeroB ): 1031 Compact.setZeroBetti( self, zeroB ) 1032 #print "bill.cosetE7.setZeroBetti: ", zeroB 1033 (wkl, ext, dom) = self._prepCompactRules(self) 1034 1035 # all bs nonzero 1036 if ext["EF2"] and ext["EG4"]: 1037 dom["BF2"] = False 1038 dom["BG4"] = False 1039 dom["G"] = False 1040 1041 if ext["EG4"] and ext["BG4"]: 1042 dom["BF2"] = False 1043 dom["G"] = False 1044 1045 if ext["EF2"] and ext["BF2"]: 1046 dom["G"] = False 1047 1048 self._postCompactRules(self, ext, dom) 1049 1050 1051 class cosetE8( Billiard, Compact ): 1052 1053 def __init__(self): 1054 Billiard.__init__( self, 10, [ ( 3, 0., "G4" ) ], False ) 1055 Compact.__init__( self, 10 ) 1056 self.setDomWalls( [ "EG4" ] ) 1057 1058 def setZeroBetti( self, zeroB ): 1059 Compact.setZeroBetti( self, zeroB ) 1060 ( wkl, ext, dom ) = self._prepCompactRules( self ) 1061 1062 if ext[ "EG4" ]: 1063 dom[ "BG4" ] = False 1064 1065 if ext["EG4"] and ext["BG4"]: 1066 dom["G"] = False 1067 1068 self._postCompactRules( self, ext, dom ) 1069 1070 1071 class cosetF4( Billiard, Compact ): 1072 1073 def __init__(self): 1074 Billiard.__init__( self, 5, [ (0, 2., "X1"), (1, 1., "F+2"), \ 1075 (1, -1., "F-2" ), (2, -2., "H3"), \ 1076 (2, 0., "G3" ) ], True ) 1077 Compact.__init__(self, 5) 1078 self.setDomWalls( [ "EX1", "EF-2" ] ) 1079 1080 def setZeroBetti(self, zeroB): 1081 Compact.setZeroBetti(self,zeroB) 1082 1083 #print "bill.cosetF4.setZeroBetti: ", zeroB 1084 1085 (wkl,ext,dom) = self._prepCompactRules(self) 1086 1087 if ext["EX1"] and ext["EH3"]: 1088 dom["EG3"] = False 1089 1090 # alls bs nonzero 1091 if ext["EX1"] and ext["EF-2"]: 1092 dom["EF+2"] = False 1093 dom["EH3"] = False 1094 dom["BX1"] = False 1095 dom["BF+2"] = False 1096 dom["BF-2"] = False 1097 dom["BH3"] = False 1098 dom["BG3"] = False 1099 dom["G"] = False 1100 1101 # b1=0, killing EF+2,EF-2,BX1 1102 if ext["EX1"] and ext["EH3"]: 1103 dom["EG3"] = False 1104 dom["BX1"] = False 1105 dom["BF+2"] = False 1106 dom["BF-2"] = False 1107 dom["BH3"] = False 1108 dom["BG3"] = False 1109 dom["G"] = False 1110 1111 1112 1113 self._postCompactRules(self, ext, dom) 1114 1115 1116 class cosetG2(Billiard,Compact): 1117 1118 def __init__(self): 1119 Billiard.__init__(self, 4, [ (1, 0., "F2") ], False ) 1120 Compact.__init__(self, 4) 1121 self.setDomWalls( [ "EF2" ] ) 1122 1123 def setZeroBetti(self): 1124 return 1125 1126 1127 1128 def test_run(): 1129 b5=cosetE6() 1130 1131 zblists = [ [], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3] ] 1132 1133 print "-" * 79 1134 for lzb in zblists: 1135 b5.setZeroBetti( lzb ) 1136 print "Coweights (T,N,S): ", b5.countCoweightTypes() 1137 print "Chaotic: ", b5.isChaotic() 1138 b5.printDynkin() 1139 print "-" * 79 1140 1141 def all( cos ): 1142 1143 zblists = [ [], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3] ] 1144 1145 print "-" * 79 1146 for lzb in zblists: 1147 cos.setZeroBetti( lzb ) 1148 print "Zero Betti: ", lzb 1149 print "Coweights (T,N,S): ", cos.countCoweightTypes() 1150 print "Chaotic: ", cos.isChaotic() 1151 cos.printDynkin() 1152 print "-" * 79 1153 1154 def test_print(): 1155 e6 = cosetE6() 1156 print e6 1157 1158 def chaosTable( c, zb ): 1159 for n in range(2,12): 1160 b = c(n) 1161 b.setZeroBetti(zb) 1162 print "%2d %s" % ( n, str(b.isChaotic()) ) 1163 1164 if __name__ == "__main__": 1165 cos = [ cosetB(8), cosetD(8), cosetF4(), cosetE6(), cosetE7() ] 1166 zblist_3 = [ [], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3] ] 1167 1168 zblist_4 = [] + zblist_3 1169 for zb in zblist_3: 1170 zblist_4.append( zb + [4] ) 1171 #zblist_4.append( [] ) 1172 1173 for c in cos: 1174 print "="*79 1175 1176 max_p = -1 1177 for (p,l,s) in c.forms: 1178 if p > max_p: 1179 max_p = p 1180 1181 zblists = zblist_3 1182 1183 if max_p > 2: 1184 zblists = zblist_4 1185 1186 for zb in zblists: 1187 print "-"*79 1188 print "Zero Betti: ", str(zb) 1189 c.setZeroBetti(zb) 1190 print c 1191 1192 1193 1194