Package madgraph :: Package core :: Module color_algebra
[hide private]
[frames] | no frames]

Source Code for Module madgraph.core.color_algebra

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15   
  16  """Classes and methods required for all calculations related to SU(N) color  
  17  algebra.""" 
  18   
  19  import array 
  20  import copy 
  21  import fractions 
  22  import itertools 
  23   
  24  #=============================================================================== 
  25  # ColorObject 
  26  #=============================================================================== 
27 -class ColorObject(array.array):
28 """Parent class for all color objects like T, Tr, f, d, ... Any new color 29 object MUST inherit from this class!""" 30
31 - def __new__(cls, *args):
32 """Create a new ColorObject, assuming an integer array""" 33 return super(ColorObject, cls).__new__(cls, 'i', args)
34
35 - def __reduce__(self):
36 """Special method needed to pickle color objects correctly""" 37 return (self.__class__, tuple([i for i in self]))
38
39 - def __str__(self):
40 """Returns a standard string representation.""" 41 42 return '%s(%s)' % (self.__class__.__name__, 43 ','.join([str(i) for i in self]))
44 45 __repr__ = __str__ 46
47 - def simplify(self):
48 """Simplification rules, to be overwritten for each new color object! 49 Should return a color factor or None if no simplification is possible""" 50 return None
51
52 - def pair_simplify(self, other):
53 """Pair simplification rules, to be overwritten for each new color 54 object! Should return a color factor or None if no simplification 55 is possible""" 56 return None
57
58 - def complex_conjugate(self):
59 """Complex conjugation. By default, the ordering of color index is 60 reversed. Can be overwritten for specific color objects like T,...""" 61 62 self.reverse() 63 return self
64
65 - def replace_indices(self, repl_dict):
66 """Replace current indices following the rules listed in the replacement 67 dictionary written as {old_index:new_index,...}. Deals correctly with 68 the replacement by allowing only one single replacement.""" 69 70 for i, index in enumerate(self): 71 try: 72 self[i] = repl_dict[index] 73 except KeyError: 74 continue
75
76 - def create_copy(self):
77 """Return a real copy of the current object.""" 78 return globals()[self.__class__.__name__](*self)
79 80 __copy__ = create_copy
81 82 83 #=============================================================================== 84 # Tr 85 #===============================================================================
86 -class Tr(ColorObject):
87 """The trace color object""" 88
89 - def simplify(self):
90 """Implement simple trace simplifications and cyclicity, and 91 Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c))""" 92 93 # Tr(a)=0 94 if len(self) == 1: 95 col_str = ColorString() 96 col_str.coeff = fractions.Fraction(0, 1) 97 return ColorFactor([col_str]) 98 99 # Tr()=Nc 100 if len(self) == 0: 101 col_str = ColorString() 102 col_str.Nc_power = 1 103 return ColorFactor([col_str]) 104 105 # Always order starting from smallest index 106 if self[0] != min(self): 107 pos = self.index(min(self)) 108 new = self[pos:] + self[:pos] 109 return ColorFactor([ColorString([Tr(*new)])]) 110 111 # Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c)) 112 for i1, index1 in enumerate(self): 113 for i2, index2 in enumerate(self[i1 + 1:]): 114 if index1 == index2: 115 a = self[:i1] 116 b = self[i1 + 1:i1 + i2 + 1] 117 c = self[i1 + i2 + 2:] 118 col_str1 = ColorString([Tr(*(a + c)), Tr(*b)]) 119 col_str2 = ColorString([Tr(*(a + b + c))]) 120 col_str1.coeff = fractions.Fraction(1, 2) 121 col_str2.coeff = fractions.Fraction(-1, 2) 122 col_str2.Nc_power = -1 123 return ColorFactor([col_str1, col_str2]) 124 125 return None
126
127 - def pair_simplify(self, col_obj):
128 """Implement Tr product simplification: 129 Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d)) and 130 Tr(a,x,b)T(c,x,d,i,j) = 1/2(T(c,b,a,d,i,j)-1/Nc Tr(a,b)T(c,d,i,j))""" 131 132 # Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d)) 133 if isinstance(col_obj, Tr): 134 for i1, index1 in enumerate(self): 135 for i2, index2 in enumerate(col_obj): 136 if index1 == index2: 137 a = self[:i1] 138 b = self[i1 + 1:] 139 c = col_obj[:i2] 140 d = col_obj[i2 + 1:] 141 col_str1 = ColorString([Tr(*(a + d + c + b))]) 142 col_str2 = ColorString([Tr(*(a + b)), Tr(*(c + d))]) 143 col_str1.coeff = fractions.Fraction(1, 2) 144 col_str2.coeff = fractions.Fraction(-1, 2) 145 col_str2.Nc_power = -1 146 return ColorFactor([col_str1, col_str2]) 147 148 # Tr(a,x,b)T(c,x,d,i,j) = 1/2(T(c,b,a,d,i,j)-1/Nc Tr(a,b)T(c,d,i,j)) 149 if isinstance(col_obj, T): 150 for i1, index1 in enumerate(self): 151 for i2, index2 in enumerate(col_obj[:-2]): 152 if index1 == index2: 153 a = self[:i1] 154 b = self[i1 + 1:] 155 c = col_obj[:i2] 156 d = col_obj[i2 + 1:-2] 157 ij = col_obj[-2:] 158 col_str1 = ColorString([T(*(c + b + a + d + ij))]) 159 col_str2 = ColorString([Tr(*(a + b)), T(*(c + d) + ij)]) 160 col_str1.coeff = fractions.Fraction(1, 2) 161 col_str2.coeff = fractions.Fraction(-1, 2) 162 col_str2.Nc_power = -1 163 return ColorFactor([col_str1, col_str2]) 164 165 return None
166 167 #=============================================================================== 168 # ColorOne 169 #===============================================================================
170 -class ColorOne(ColorObject):
171 """The one of the color object""" 172
173 - def __init__(self, *args):
174 """Check for no index""" 175 176 assert len(args) == 0 , "ColorOne objects must have no index!" 177 178 super(ColorOne, self).__init__()
179
180 - def simplify(self):
181 """""" 182 assert len(self)==0, "There is argument(s) in color object ColorOne." 183 col_str = ColorString() 184 col_str.coeff = fractions.Fraction(1, 1) 185 return ColorFactor([col_str])
186 187
188 - def pair_simplify(self, col_obj):
189 """Implement ColorOne product simplification""" 190 191 if any(isinstance(col_obj, c_type) for c_type in [Tr,T,f,d,ColorOne]): 192 col_str = ColorString([col_obj]) 193 return ColorFactor([col_str]) 194 return None
195 196 197 #=============================================================================== 198 # T 199 #===============================================================================
200 -class T(ColorObject):
201 """The T color object. Last two indices have a special meaning""" 202
203 - def __init__(self, *args):
204 """Check for at least two indices""" 205 206 assert len(args) > 1 , "T objects must have at least two indices!" 207 208 super(T, self).__init__()
209
210 - def simplify(self):
211 """Implement T(a,b,c,...,i,i) = Tr(a,b,c,...) and 212 T(a,x,b,x,c,i,j) = 1/2(T(a,c,i,j)Tr(b)-1/Nc T(a,b,c,i,j))""" 213 214 # T(a,b,c,...,i,i) = Tr(a,b,c,...) 215 if self[-2] == self[-1]: 216 return ColorFactor([ColorString([Tr(*self[:-2])])]) 217 218 # T(a,x,b,x,c,i,j) = 1/2(T(a,c,i,j)Tr(b)-1/Nc T(a,b,c,i,j)) 219 for i1, index1 in enumerate(self[:-2]): 220 for i2, index2 in enumerate(self[i1 + 1:-2]): 221 if index1 == index2: 222 a = self[:i1] 223 b = self[i1 + 1:i1 + i2 + 1] 224 c = self[i1 + i2 + 2:-2] 225 ij = self[-2:] 226 col_str1 = ColorString([T(*(a + c + ij)), Tr(*b)]) 227 col_str2 = ColorString([T(*(a + b + c + ij))]) 228 col_str1.coeff = fractions.Fraction(1, 2) 229 col_str2.coeff = fractions.Fraction(-1, 2) 230 col_str2.Nc_power = -1 231 return ColorFactor([col_str1, col_str2]) 232 233 return None
234
235 - def pair_simplify(self, col_obj):
236 """Implement T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k) 237 and T(a,x,b,i,j)T(c,x,d,k,l) = 1/2(T(a,d,i,l)T(c,b,k,j) 238 -1/Nc T(a,b,i,j)T(c,d,k,l)).""" 239 240 if isinstance(col_obj, T): 241 ij1 = self[-2:] 242 ij2 = col_obj[-2:] 243 244 # T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k) 245 if ij1[1] == ij2[0]: 246 return ColorFactor([ColorString([T(*(self[:-2] + \ 247 col_obj[:-2] + \ 248 array.array('i', [ij1[0], 249 ij2[1]])))])]) 250 251 # T(a,x,b,i,j)T(c,x,d,k,l) = 1/2(T(a,d,i,l)T(c,b,k,j) 252 # -1/Nc T(a,b,i,j)T(c,d,k,l)) 253 for i1, index1 in enumerate(self[:-2]): 254 for i2, index2 in enumerate(col_obj[:-2]): 255 if index1 == index2: 256 a = self[:i1] 257 b = self[i1 + 1:-2] 258 c = col_obj[:i2] 259 d = col_obj[i2 + 1:-2] 260 col_str1 = ColorString([T(*(a + d + \ 261 array.array('i', 262 [ij1[0], ij2[1]]))), 263 T(*(c + b + \ 264 array.array('i', 265 [ij2[0], ij1[1]])))]) 266 col_str2 = ColorString([T(*(a + b + \ 267 array.array('i', 268 [ij1[0], ij1[1]]))), 269 T(*(c + d + \ 270 array.array('i', 271 [ij2[0], ij2[1]])))]) 272 col_str1.coeff = fractions.Fraction(1, 2) 273 col_str2.coeff = fractions.Fraction(-1, 2) 274 col_str2.Nc_power = -1 275 return ColorFactor([col_str1, col_str2])
276
277 - def complex_conjugate(self):
278 """Complex conjugation. Overwritten here because the two last indices 279 should be treated differently""" 280 281 # T(a,b,c,i,j)* = T(c,b,a,j,i) 282 l1 = self[:-2] 283 l1.reverse() 284 l2 = self[-2:] 285 l2.reverse() 286 self[:] = l1 + l2 287 return self
288 289 #=============================================================================== 290 # f 291 #===============================================================================
292 -class f(ColorObject):
293 """The f color object""" 294
295 - def __init__(self, *args):
296 """Ensure f and d objects have strictly 3 indices""" 297 298 assert len(args) == 3, "f and d objects must have three indices!" 299 300 super(f, self).__init__()
301 302
303 - def simplify(self):
304 """Implement only the replacement rule 305 f(a,b,c)=-2ITr(a,b,c)+2ITr(c,b,a)""" 306 307 indices = self[:] 308 col_str1 = ColorString([Tr(*indices)]) 309 indices.reverse() 310 col_str2 = ColorString([Tr(*indices)]) 311 312 col_str1.coeff = fractions.Fraction(-2, 1) 313 col_str2.coeff = fractions.Fraction(2, 1) 314 315 col_str1.is_imaginary = True 316 col_str2.is_imaginary = True 317 318 return ColorFactor([col_str1, col_str2])
319 320 #=============================================================================== 321 # d 322 #===============================================================================
323 -class d(f):
324 """The d color object""" 325
326 - def simplify(self):
327 """Implement only the replacement rule 328 d(a,b,c)=2Tr(a,b,c)+2Tr(c,b,a)""" 329 330 indices = self[:] 331 col_str1 = ColorString([Tr(*indices)]) 332 indices.reverse() 333 col_str2 = ColorString([Tr(*indices)]) 334 335 col_str1.coeff = fractions.Fraction(2, 1) 336 col_str2.coeff = fractions.Fraction(2, 1) 337 338 return ColorFactor([col_str1, col_str2])
339 340 #=============================================================================== 341 # Epsilon and EpsilonBar, the totally antisymmetric tensors of three triplet 342 # (antitriplet) indices 343 #===============================================================================
344 -class Epsilon(ColorObject):
345 """Epsilon_ijk color object for three triplets""" 346
347 - def __init__(self, *args):
348 """Ensure e_ijk objects have strictly 3 indices""" 349 350 super(Epsilon, self).__init__() 351 assert len(args) == 3, "Epsilon objects must have three indices!"
352
353 - def pair_simplify(self, col_obj):
354 """Implement e_ijk ae_ilm = T(j,l)T(k,m) - T(j,m)T(k,l) and 355 e_ijk T(l,k) = e_ikl""" 356 357 # e_ijk ae_ilm = T(j,l)T(k,m) - T(j,m)T(k,l) 358 if isinstance(col_obj, EpsilonBar): 359 360 incommon = False 361 eps_indices = self[:] 362 aeps_indices = col_obj[:] 363 for i in self: 364 if i in col_obj: 365 incommon = True 366 com_index_eps = self.index(i) 367 com_index_aeps = col_obj.index(i) 368 369 if incommon: 370 eps_indices = self[com_index_eps:] + self[:com_index_eps] 371 aeps_indices = col_obj[com_index_aeps:] + col_obj[:com_index_aeps] 372 col_str1 = ColorString([T(eps_indices[1], aeps_indices[1]), 373 T(eps_indices[2], aeps_indices[2])]) 374 col_str2 = ColorString([T(eps_indices[1], aeps_indices[2]), 375 T(eps_indices[2], aeps_indices[1])]) 376 377 col_str2.coeff = fractions.Fraction(-1, 1) 378 379 return ColorFactor([col_str1, col_str2]) 380 381 # e_ijk T(l,k) = e_ikl 382 if isinstance(col_obj, T) and len(col_obj) == 2 and col_obj[1] in self: 383 384 com_index = self.index(col_obj[1]) 385 new_self = copy.copy(self) 386 new_self[com_index] = col_obj[0] 387 388 return ColorFactor([ColorString([new_self])])
389 390
391 - def complex_conjugate(self):
392 """Complex conjugation. Overwritten here because complex conjugation 393 interchange triplets and antitriplets.""" 394 395 return EpsilonBar(*self)
396 397
398 -class EpsilonBar(ColorObject):
399 """Epsilon_ijk color object for three antitriplets""" 400
401 - def __init__(self, *args):
402 """Ensure e_ijk objects have strictly 3 indices""" 403 404 super(EpsilonBar, self).__init__() 405 assert len(args) == 3, "EpsilonBar objects must have three indices!"
406
407 - def pair_simplify(self, col_obj):
408 """Implement ebar_ijk T(k,l) = e_ikl""" 409 410 # ebar_ijk T(k,l) = ebar_ijl 411 if isinstance(col_obj, T) and len(col_obj) == 2 and col_obj[0] in self: 412 413 com_index = self.index(col_obj[0]) 414 new_self = copy.copy(self) 415 new_self[com_index] = col_obj[1] 416 417 return ColorFactor([ColorString([new_self])])
418 419
420 - def complex_conjugate(self):
421 """Complex conjugation. Overwritten here because complex conjugation 422 interchange triplets and antitriplets.""" 423 424 return Epsilon(*self)
425 426 427 #=============================================================================== 428 # Color sextet objects: K6, K6Bar, T6 429 # Note that delta3 = T, delta6 = T6, delta8 = 2 Tr 430 # This 2 Tr is weird and should be check why it is not the expected 1/2. 431 #=============================================================================== 432
433 -class K6(ColorObject):
434 """K6, the symmetry clebsch coefficient, mapping into the symmetric 435 tensor.""" 436
437 - def __init__(self, *args):
438 """Ensure sextet color objects have strictly 3 indices""" 439 440 super(K6, self).__init__() 441 assert len(args) == 3, "sextet color objects must have three indices!"
442
443 - def pair_simplify(self, col_obj):
444 """Implement the replacement rules 445 K6(m,i,j)K6Bar(m,k,l) = 1/2(delta3(l,i)delta3(k,j) 446 + delta3(k,i)delta3(l,j)) 447 = 1/2(T(l,i)T(k,j) + T(k,i)T(l,j)) 448 K6(m,i,j)K6Bar(n,j,i) = delta6(m,n) 449 K6(m,i,j)K6Bar(n,i,j) = delta6(m,n) 450 delta3(i,j)K6(m,i,k) = K6(m,j,k) 451 delta3(i,k)K6(m,j,i) = K6(m,j,k).""" 452 453 if isinstance(col_obj, K6Bar): 454 455 m = self[0] 456 n = col_obj[0] 457 458 ij1 = self[-2:] 459 ij2 = col_obj[-2:] 460 461 # K6(m,i,j)K6Bar(m,k,l) = 1/2(T(l,i)T(k,j) 462 # + T(k,i)T(l,j) 463 if m == n: 464 col_str1 = ColorString([T(ij2[1], ij1[0]), 465 T(ij2[0], ij1[1])]) 466 col_str2 = ColorString([T(ij2[0], ij1[0]), 467 T(ij2[1], ij1[1])]) 468 col_str1.coeff = fractions.Fraction(1, 2) 469 col_str2.coeff = fractions.Fraction(1, 2) 470 471 return ColorFactor([col_str1, col_str2]) 472 473 # K6(m,i,j)K6Bar(n,j,i) = delta6(m,n) 474 if ij1[1] == ij2[0] and ij1[0] == ij2[1]: 475 return ColorFactor([ColorString([T6(m, n)])]) 476 477 # K6(m,i,j)K6Bar(n,i,j) = delta6(m,n) 478 if ij1[0] == ij2[0] and ij1[1] == ij2[1]: 479 return ColorFactor([ColorString([T6(m, n)])]) 480 481 if isinstance(col_obj, T) and len(col_obj) == 2: 482 # delta3(i,j)K6(m,i,k) = K6(m,j,k) 483 # delta3(i,k)K6(m,j,i) = K6(m,j,k) 484 if col_obj[0] in self[-2:]: 485 index1 = self[-2:].index(col_obj[0]) 486 return ColorFactor([ColorString([K6(self[0], 487 self[2-index1], 488 col_obj[1])])])
489
490 - def complex_conjugate(self):
491 """Complex conjugation. By default, the ordering of color index is 492 reversed. Can be overwritten for specific color objects like T,...""" 493 494 return K6Bar(*self)
495 496
497 -class K6Bar(ColorObject):
498 """K6Bar, the barred symmetry clebsch coefficient, mapping into the symmetric 499 tensor.""" 500
501 - def __init__(self, *args):
502 """Ensure sextet color objects have strictly 3 indices""" 503 504 super(K6Bar, self).__init__() 505 assert len(args) == 3, "sextet color objects must have three indices!"
506
507 - def pair_simplify(self, col_obj):
508 """Implement the replacement rules 509 delta3(i,j)K6Bar(m,j,k) = K6Bar(m,i,k) 510 delta3(k,j)K6Bar(m,i,j) = K6Bar(m,i,k).""" 511 512 if isinstance(col_obj, T) and len(col_obj) == 2: 513 # delta3(i,j)K6Bar(m,j,k) = K6Bar(m,i,k) 514 # delta3(k,j)K6Bar(m,i,j) = K6Bar(m,i,k) 515 if col_obj[1] in self[-2:]: 516 index1 = self[-2:].index(col_obj[1]) 517 return ColorFactor([ColorString([K6Bar(self[0], 518 self[2-index1], 519 col_obj[0])])])
520
521 - def complex_conjugate(self):
522 """Complex conjugation. By default, the ordering of color index is 523 reversed. Can be overwritten for specific color objects like T,...""" 524 525 return K6(*self)
526
527 -class T6(ColorObject):
528 """The T6 sextet trace color object.""" 529 530 new_index = 10000 531
532 - def __init__(self, *args):
533 """Check for exactly three indices""" 534 535 super(T6, self).__init__() 536 assert len(args) >= 2 and len(args) <= 3, \ 537 "T6 objects must have two or three indices!"
538
539 - def simplify(self):
540 """Implement delta6(i,i) = 1/2 Nc(Nc+1), 541 T6(a,i,j) = 2(K6(i,ii,jj)T(a,jj,kk)K6Bar(j,kk,ii))""" 542 543 # delta6(i,i) = Nc 544 if len(self) == 2 and self[0] == self[1]: 545 col_str1 = ColorString() 546 col_str1.Nc_power = 2 547 col_str1.coeff = fractions.Fraction(1, 2) 548 col_str2 = ColorString() 549 col_str2.Nc_power = 1 550 col_str2.coeff = fractions.Fraction(1, 2) 551 return ColorFactor([col_str1, col_str2]) 552 553 if len(self) == 2: 554 return 555 556 # Set new indices according to the Mathematica template 557 ii = T6.new_index 558 jj = ii + 1 559 kk = jj + 1 560 T6.new_index += 3 561 # Create the resulting color objects 562 col_string = ColorString([K6(self[1], ii, jj), 563 T(self[0], jj, kk), 564 K6Bar(self[2], kk, ii)]) 565 col_string.coeff = fractions.Fraction(2, 1) 566 return ColorFactor([col_string])
567
568 - def pair_simplify(self, col_obj):
569 """Implement the replacement rules 570 delta6(i,j)delta6(j,k) = delta6(i,k) 571 delta6(m,n)K6(n,i,j) = K6(m,i,j) 572 delta6(m,n)K6Bar(m,i,j) = K6Bar(n,i,j).""" 573 574 if len(self) == 3: 575 return 576 577 if isinstance(col_obj, T6) and len(col_obj) == 2: 578 #delta6(i,j)delta6(j,k) = delta6(i,k) 579 if col_obj[0] == self[1]: 580 return ColorFactor([ColorString([T6(self[0], 581 col_obj[1])])]) 582 583 if isinstance(col_obj, K6): 584 # delta6(m,n)K6(n,i,j) = K6(m,i,j) 585 if col_obj[0] == self[1]: 586 return ColorFactor([ColorString([K6(self[0], 587 col_obj[1], 588 col_obj[2])])]) 589 590 591 if isinstance(col_obj, K6Bar): 592 # delta6(m,n)K6Bar(m,i,j) = K6Bar(n,i,j).""" 593 if col_obj[0] == self[0]: 594 return ColorFactor([ColorString([K6Bar(self[1], 595 col_obj[1], 596 col_obj[2])])])
597 598 #=============================================================================== 599 # ColorString 600 #===============================================================================
601 -class ColorString(list):
602 """A list of ColorObjects with an implicit multiplication between, 603 together with a Fraction coefficient and a tag 604 to indicate if the coefficient is real or imaginary. ColorStrings can be 605 simplified, by simplifying their elements.""" 606 607 coeff = fractions.Fraction(1, 1) 608 is_imaginary = False 609 Nc_power = 0 610 canonical = None 611 immutable = None 612
613 - def __init__(self, init_list=[], 614 coeff=fractions.Fraction(1, 1), 615 is_imaginary=False, Nc_power=0):
616 """Overrides norm list constructor to implement easy modification 617 of coeff, is_imaginary and Nc_power""" 618 619 if init_list: 620 for obj in init_list: 621 assert type(obj) != array.array 622 self.extend(init_list) 623 self.coeff = coeff 624 self.is_imaginary = is_imaginary 625 self.Nc_power = Nc_power
626
627 - def __str__(self):
628 """Returns a standard string representation based on color object 629 representations""" 630 631 coeff_str = str(self.coeff) 632 if self.is_imaginary: 633 coeff_str += ' I' 634 if self.Nc_power > 0: 635 coeff_str += ' Nc^%i' % self.Nc_power 636 elif self.Nc_power < 0: 637 coeff_str += ' 1/Nc^%i' % abs(self.Nc_power) 638 return '%s %s' % (coeff_str, 639 ' '.join([str(col_obj) for col_obj in self]))
640 641 __repr__ = __str__ 642
643 - def product(self, other):
644 """Multiply self with other.""" 645 646 self.coeff = self.coeff * other.coeff 647 648 self.Nc_power = self.Nc_power + other.Nc_power 649 650 # Complex algebra 651 if self.is_imaginary and other.is_imaginary: 652 self.is_imaginary = False 653 self.coeff = -self.coeff 654 elif self.is_imaginary or other.is_imaginary: 655 self.is_imaginary = True 656 657 # Reset "canonical", so don't get wrong result from comparison 658 self.canonical = None 659 self.immutable = None 660 661 self.extend(other)
662
663 - def simplify(self):
664 """Simplify the current ColorString by applying simplify rules on 665 each element and building a new ColorFactor to return if necessary""" 666 667 # First, try to simplify element by element 668 for i1, col_obj1 in enumerate(self): 669 res = col_obj1.simplify() 670 # If a simplification possibility is found... 671 if res: 672 # Create a color factor to store the answer... 673 res_col_factor = ColorFactor() 674 # Obtained my multiplying the initial string minus the color 675 # object to simplify with all color strings in the result 676 for second_col_str in res: 677 first_col_str = copy.copy(self) 678 del first_col_str[i1] 679 first_col_str.product(second_col_str) 680 # This sort is necessary to ensure ordering of ColorObjects 681 # remains the same for comparison 682 first_col_str.sort() 683 res_col_factor.append(first_col_str) 684 685 return res_col_factor 686 687 # Second, try to simplify pairs 688 for i1, col_obj1 in enumerate(self): 689 690 for i2, col_obj2 in enumerate(self[i1 + 1:]): 691 res = col_obj1.pair_simplify(col_obj2) 692 # Try both pairing 693 if not res: 694 res = col_obj2.pair_simplify(col_obj1) 695 if res: 696 res_col_factor = ColorFactor() 697 for second_col_str in res: 698 first_col_str = copy.copy(self) 699 del first_col_str[i1] 700 del first_col_str[i1 + i2] 701 first_col_str.product(second_col_str) 702 first_col_str.sort() 703 res_col_factor.append(first_col_str) 704 return res_col_factor 705 706 return None
707
708 - def add(self, other):
709 """Add string other to current string. ONLY USE WITH SIMILAR STRINGS!""" 710 711 self.coeff = self.coeff + other.coeff
712
713 - def complex_conjugate(self):
714 """Returns the complex conjugate of the current color string""" 715 716 compl_conj_str = ColorString([], self.coeff, self.is_imaginary, 717 self.Nc_power) 718 for col_obj in self: 719 compl_conj_str.append(col_obj.complex_conjugate()) 720 if compl_conj_str.is_imaginary: 721 compl_conj_str.coeff = -compl_conj_str.coeff 722 723 return compl_conj_str
724
725 - def to_immutable(self):
726 """Returns an immutable object summarizing the color structure of the 727 current color string. Format is ((name1,indices1),...) where name is the 728 class name of the color object and indices a tuple corresponding to its 729 indices. An immutable object, in Python, is built on tuples, strings and 730 numbers, i.e. objects which cannot be modified. Their crucial property 731 is that they can be used as dictionary keys!""" 732 733 if self.immutable: 734 return self.immutable 735 736 ret_list = [(col_obj.__class__.__name__, tuple(col_obj)) \ 737 for col_obj in self] 738 739 if not ret_list and self.coeff: 740 ret_list=[("ColorOne",tuple([]))] 741 742 ret_list.sort() 743 self.immutable = tuple(ret_list) 744 745 return self.immutable
746
747 - def from_immutable(self, immutable_rep):
748 """Fill the current object with Color Objects created using an immutable 749 representation.""" 750 751 del self[:] 752 753 for col_tuple in immutable_rep: 754 self.append(globals()[col_tuple[0]](*col_tuple[1]))
755
756 - def replace_indices(self, repl_dict):
757 """Replace current indices following the rules listed in the replacement 758 dictionary written as {old_index:new_index,...}, does that for ALL 759 color objects.""" 760 761 map(lambda col_obj: col_obj.replace_indices(repl_dict), self)
762
763 - def create_copy(self):
764 """Returns a real copy of self, non trivial because bug in 765 copy.deepcopy""" 766 767 res = ColorString() 768 for col_obj in self: 769 assert type(col_obj) != array.array 770 res.append(col_obj.create_copy()) 771 res.coeff = self.coeff 772 res.is_imaginary = self.is_imaginary 773 res.Nc_power = self.Nc_power 774 775 return res
776 777 __copy__ = create_copy 778
779 - def set_Nc(self, Nc=3):
780 """Returns a tuple, with the first entry being the string coefficient 781 with Nc replaced (by default by 3), and the second one being True 782 or False if the coefficient is imaginary or not. Raise an error if there 783 are still non trivial color objects.""" 784 785 if self: 786 raise ValueError, \ 787 "String %s cannot be simplified to a number!" % str(self) 788 789 if self.Nc_power >= 0: 790 return (self.coeff * fractions.Fraction(\ 791 int(Nc ** self.Nc_power), 1), 792 self.is_imaginary) 793 else: 794 return (self.coeff * fractions.Fraction(\ 795 1, int(Nc ** abs(self.Nc_power))), 796 self.is_imaginary)
797
798 - def order_summation(self, immutable=None):
799 """Force a specific order for the summation indices 800 in case we have Clebsch Gordan coefficients K6's or K6Bar's 801 This is necessary to correctly recognize later on the equivalent 802 color strings (otherwise the color basis is degenerate) 803 The new ordering is as follow: 804 1. put K and KBar Clebsch Gordan coefficients at the end of the list of color factors 805 the other factors are re-arranged in the reversed order compared with immutable 806 2. rename the summation indices so that they are increasing (starting from 10000) 807 from left to right 808 3. finally, after the summation indices have been renamed, replace 809 K6(a,i,j) by K6(a,j,i) and K6Bar(a,i,j) by K6Bar(a,j,i) IF j>i 810 """ 811 812 if not immutable: 813 immutable = self.to_immutable() 814 815 # STEP 1: first scan to see whether there are some K's or KBar's, 816 # and put them at the en 817 immutable_order2=[] 818 go_further=0 819 for elem in immutable: 820 if elem[0]=="K6" or elem[0]=="K6Bar" : 821 immutable_order2.append(elem) 822 go_further=1 823 else: immutable_order2.insert(0,elem) 824 825 if go_further==0: return 826 827 # STEP 2: rename the summation indices so that they are increasing (starting from 10000) 828 # from left to right 829 replaced_indices = {} 830 curr_ind = 10000 831 return_list = [] 832 833 for elem in immutable_order2: 834 can_elem = [elem[0], []] 835 for index in elem[1]: 836 if index>9999: # consider only summation indices 837 try: 838 new_index = replaced_indices[index] 839 except KeyError: 840 new_index = curr_ind 841 curr_ind += 1 842 replaced_indices[index] = new_index 843 else: new_index=index 844 can_elem[1].append(new_index) 845 # STEP 3. replace K6(a,i,j) by K6(a,j,i) and K6Bar(a,i,j) by K6Bar(a,j,i) IF j>i 846 if (can_elem[0]=="K6" or can_elem[0]=="K6Bar"): 847 if can_elem[1][2]>can_elem[1][1]: can_elem[1]=[can_elem[1][0], can_elem[1][2], can_elem[1][1] ] 848 return_list.append((can_elem[0], tuple(can_elem[1]))) 849 return_list.sort() 850 851 self.from_immutable(return_list) 852 self.immutable=None # don't use the information self.immutable later on in the code, 853 # since the summation indices have been modified 854 return
855
856 - def to_canonical(self, immutable=None):
857 """Returns the canonical representation of the immutable representation 858 (i.e., first index is 1, ...). This allow for an easy comparison of 859 two color strings, i.e. independently of the actual index names (only 860 relative positions matter). Also returns the conversion dictionary. 861 If no immutable representation is given, use the one build from self.""" 862 863 if not immutable: 864 immutable = self.to_immutable() 865 866 if self.canonical: 867 return self.canonical 868 869 replaced_indices = {} 870 curr_ind = 1 871 return_list = [] 872 873 for elem in immutable: 874 can_elem = [elem[0], []] 875 for index in elem[1]: 876 try: 877 new_index = replaced_indices[index] 878 except KeyError: 879 new_index = curr_ind 880 curr_ind += 1 881 replaced_indices[index] = new_index 882 can_elem[1].append(new_index) 883 return_list.append((can_elem[0], tuple(can_elem[1]))) 884 885 return_list.sort() 886 887 self.canonical = (tuple(return_list), replaced_indices) 888 return self.canonical
889
890 - def __eq__(self, col_str):
891 """Check if two color strings are equivalent by checking if their 892 canonical representations and the coefficients are equal.""" 893 894 return self.coeff == col_str.coeff and \ 895 self.Nc_power == col_str.Nc_power and \ 896 self.is_imaginary == col_str.is_imaginary and \ 897 self.to_canonical() == col_str.to_canonical()
898
899 - def __ne__(self, col_str):
900 """Logical opposite of ea""" 901 902 return not self.__eq__(col_str)
903
904 - def is_similar(self, col_str):
905 """Check if two color strings are similar by checking if their 906 canonical representations and Nc/I powers are equal.""" 907 908 return self.Nc_power == col_str.Nc_power and \ 909 self.is_imaginary == col_str.is_imaginary and \ 910 self.to_canonical() == col_str.to_canonical()
911
912 - def near_equivalent(self, col_str):
913 """Check if two color strings are equivalent looking only at 914 the color objects (used in color flow string calculation)""" 915 916 if len(self.to_canonical()) != len(col_str.to_canonical()): 917 return False 918 919 return all([co1[0] == co2[0] and sorted(co1[1]) == sorted(co2[1]) \ 920 for (co1,co2) in zip(self.to_canonical()[0], 921 col_str.to_canonical()[0])])
922 923 #=============================================================================== 924 # ColorFactor 925 #===============================================================================
926 -class ColorFactor(list):
927 """ColorFactor objects are list of ColorString with an implicit summation. 928 They can be simplified by simplifying all their elements.""" 929
930 - def __str__(self):
931 """Returns a nice string for printing""" 932 933 return '+'.join(['(%s)' % str(col_str) for col_str in self])
934
935 - def append_str(self, new_str):
936 """Special append taking care of adding new string to strings already 937 existing with the same structure.""" 938 939 for col_str in self: 940 # Check if strings are similar, this IS the optimal way of doing 941 # it. Note that first line only compare the lists, not the 942 # properties associated 943 if col_str.is_similar(new_str): 944 # Add them 945 col_str.add(new_str) 946 return True 947 948 # If no correspondence is found, append anyway 949 self.append(new_str) 950 return False
951
952 - def extend_str(self, new_col_fact):
953 """Special extend taking care of adding new strings to strings already 954 existing with the same structure.""" 955 956 # Reset "canonical", so don't get wrong result from comparison 957 self.canonical = None 958 self.immutable = None 959 960 for col_str in new_col_fact: 961 self.append_str(col_str)
962
963 - def simplify(self):
964 """Returns a new color factor where each color string has been 965 simplified once and similar strings have been added.""" 966 967 new_col_factor = ColorFactor() 968 # Simplify 969 for col_str in self: 970 res = col_str.simplify() 971 if res: 972 new_col_factor.extend_str(res) 973 else: 974 new_col_factor.append_str(col_str) 975 976 # Only returns non zero elements 977 return ColorFactor([col_str for col_str in \ 978 new_col_factor if col_str.coeff != 0])
979
980 - def full_simplify(self):
981 """Simplify the current color factor until the result is stable""" 982 983 result = copy.copy(self) 984 while(True): 985 ref = copy.copy(result) 986 result = result.simplify() 987 if result == ref: 988 return result
989
990 - def set_Nc(self, Nc=3):
991 """Returns a tuple containing real and imaginary parts of the current 992 color factor, when Nc is replaced (3 by default).""" 993 994 return (sum([cs.set_Nc(Nc)[0] for cs in self if not cs.is_imaginary]), 995 sum([cs.set_Nc(Nc)[0] for cs in self if cs.is_imaginary]))
996 997
998 - def replace_indices(self, repl_dict):
999 """Replace current indices following the rules listed in the replacement 1000 dictionary written as {old_index:new_index,...}, does that for ALL 1001 color strings.""" 1002 1003 map(lambda col_str:col_str.replace_indices(repl_dict), self)
1004
1005 - def create_copy(self):
1006 """Returns a real copy of self, non trivial because bug in 1007 copy.deepcopy""" 1008 1009 res = ColorFactor() 1010 for col_str in self: 1011 res.append(col_str.create_copy()) 1012 1013 return res
1014 1015 __copy__ = create_copy
1016