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 # The loop_NC_power attribute is the power of Nc that comes from the 611 # possible color trace that appear in loop diagrams. It is typically 612 # equal to 1 if the loop diagrams is a closed fermion loop and 0 otherwise. 613 loop_Nc_power = 0 614 canonical = None 615 immutable = None 616
617 - def __init__(self, init_list=[], 618 coeff=fractions.Fraction(1, 1), 619 is_imaginary=False, Nc_power=0, loop_Nc_power=0):
620 """Overrides norm list constructor to implement easy modification 621 of coeff, is_imaginary and Nc_power""" 622 623 if init_list: 624 for obj in init_list: 625 assert type(obj) != array.array 626 self.extend(init_list) 627 self.coeff = coeff 628 self.is_imaginary = is_imaginary 629 self.Nc_power = Nc_power 630 self.loop_Nc_power = loop_Nc_power
631
632 - def __str__(self):
633 """Returns a standard string representation based on color object 634 representations""" 635 636 coeff_str = str(self.coeff) 637 if self.is_imaginary: 638 coeff_str += ' I' 639 if self.Nc_power > 0: 640 coeff_str += ' Nc^%i' % self.Nc_power 641 elif self.Nc_power < 0: 642 coeff_str += ' 1/Nc^%i' % abs(self.Nc_power) 643 return '%s %s' % (coeff_str, 644 ' '.join([str(col_obj) for col_obj in self]))
645 646 __repr__ = __str__ 647
648 - def product(self, other):
649 """Multiply self with other.""" 650 651 self.coeff = self.coeff * other.coeff 652 653 self.Nc_power = self.Nc_power + other.Nc_power 654 655 # Complex algebra 656 if self.is_imaginary and other.is_imaginary: 657 self.is_imaginary = False 658 self.coeff = -self.coeff 659 elif self.is_imaginary or other.is_imaginary: 660 self.is_imaginary = True 661 662 # Reset "canonical", so don't get wrong result from comparison 663 self.canonical = None 664 self.immutable = None 665 666 self.extend(other)
667
668 - def simplify(self):
669 """Simplify the current ColorString by applying simplify rules on 670 each element and building a new ColorFactor to return if necessary""" 671 672 # First, try to simplify element by element 673 for i1, col_obj1 in enumerate(self): 674 res = col_obj1.simplify() 675 # If a simplification possibility is found... 676 if res: 677 # Create a color factor to store the answer... 678 res_col_factor = ColorFactor() 679 # Obtained my multiplying the initial string minus the color 680 # object to simplify with all color strings in the result 681 for second_col_str in res: 682 first_col_str = copy.copy(self) 683 del first_col_str[i1] 684 first_col_str.product(second_col_str) 685 # This sort is necessary to ensure ordering of ColorObjects 686 # remains the same for comparison 687 first_col_str.sort() 688 res_col_factor.append(first_col_str) 689 690 return res_col_factor 691 692 # Second, try to simplify pairs 693 for i1, col_obj1 in enumerate(self): 694 695 for i2, col_obj2 in enumerate(self[i1 + 1:]): 696 res = col_obj1.pair_simplify(col_obj2) 697 # Try both pairing 698 if not res: 699 res = col_obj2.pair_simplify(col_obj1) 700 if res: 701 res_col_factor = ColorFactor() 702 for second_col_str in res: 703 first_col_str = copy.copy(self) 704 del first_col_str[i1] 705 del first_col_str[i1 + i2] 706 first_col_str.product(second_col_str) 707 first_col_str.sort() 708 res_col_factor.append(first_col_str) 709 return res_col_factor 710 711 return None
712
713 - def add(self, other):
714 """Add string other to current string. ONLY USE WITH SIMILAR STRINGS!""" 715 716 self.coeff = self.coeff + other.coeff
717
718 - def complex_conjugate(self):
719 """Returns the complex conjugate of the current color string""" 720 721 compl_conj_str = ColorString([], self.coeff, self.is_imaginary, 722 self.Nc_power) 723 for col_obj in self: 724 compl_conj_str.append(col_obj.complex_conjugate()) 725 if compl_conj_str.is_imaginary: 726 compl_conj_str.coeff = -compl_conj_str.coeff 727 728 return compl_conj_str
729
730 - def to_immutable(self):
731 """Returns an immutable object summarizing the color structure of the 732 current color string. Format is ((name1,indices1),...) where name is the 733 class name of the color object and indices a tuple corresponding to its 734 indices. An immutable object, in Python, is built on tuples, strings and 735 numbers, i.e. objects which cannot be modified. Their crucial property 736 is that they can be used as dictionary keys!""" 737 738 if self.immutable: 739 return self.immutable 740 741 ret_list = [(col_obj.__class__.__name__, tuple(col_obj)) \ 742 for col_obj in self] 743 744 if not ret_list and self.coeff: 745 ret_list=[("ColorOne",tuple([]))] 746 747 ret_list.sort() 748 self.immutable = tuple(ret_list) 749 750 return self.immutable
751
752 - def from_immutable(self, immutable_rep):
753 """Fill the current object with Color Objects created using an immutable 754 representation.""" 755 756 del self[:] 757 758 for col_tuple in immutable_rep: 759 self.append(globals()[col_tuple[0]](*col_tuple[1]))
760
761 - def replace_indices(self, repl_dict):
762 """Replace current indices following the rules listed in the replacement 763 dictionary written as {old_index:new_index,...}, does that for ALL 764 color objects.""" 765 766 map(lambda col_obj: col_obj.replace_indices(repl_dict), self)
767
768 - def create_copy(self):
769 """Returns a real copy of self, non trivial because bug in 770 copy.deepcopy""" 771 res = ColorString() 772 for col_obj in self: 773 assert type(col_obj) != array.array 774 res.append(col_obj.create_copy()) 775 res.coeff = self.coeff 776 res.is_imaginary = self.is_imaginary 777 res.Nc_power = self.Nc_power 778 res.loop_Nc_power = self.loop_Nc_power 779 780 return res
781 782 __copy__ = create_copy 783
784 - def set_Nc(self, Nc=3):
785 """Returns a tuple, with the first entry being the string coefficient 786 with Nc replaced (by default by 3), and the second one being True 787 or False if the coefficient is imaginary or not. Raise an error if there 788 are still non trivial color objects.""" 789 790 if self: 791 raise ValueError, \ 792 "String %s cannot be simplified to a number!" % str(self) 793 794 if self.Nc_power >= 0: 795 return (self.coeff * fractions.Fraction(\ 796 int(Nc ** self.Nc_power), 1), 797 self.is_imaginary) 798 else: 799 return (self.coeff * fractions.Fraction(\ 800 1, int(Nc ** abs(self.Nc_power))), 801 self.is_imaginary)
802
803 - def order_summation(self, immutable=None):
804 """Force a specific order for the summation indices 805 in case we have Clebsch Gordan coefficients K6's or K6Bar's 806 This is necessary to correctly recognize later on the equivalent 807 color strings (otherwise the color basis is degenerate). 808 The new ordering is as follow: 809 1. put K and KBar Clebsch Gordan coefficients at the end of the list of color factors 810 the other factors are re-arranged in the reversed order compared with immutable 811 2. rename the summation indices so that they are increasing (starting from 10000) 812 from left to right 813 3. finally, after the summation indices have been renamed, replace 814 K6(a,i,j) by K6(a,j,i) and K6Bar(a,i,j) by K6Bar(a,j,i) IF j>i 815 """ 816 817 if not immutable: 818 immutable = self.to_immutable() 819 820 # STEP 1: first scan to see whether there are some K's or KBar's, 821 # and put them at the en 822 immutable_order2=[] 823 go_further=0 824 for elem in immutable: 825 if elem[0]=="K6" or elem[0]=="K6Bar" : 826 immutable_order2.append(elem) 827 go_further=1 828 else: immutable_order2.insert(0,elem) 829 830 if go_further==0: return 831 832 # STEP 2: rename the summation indices so that they are increasing (starting from 10000) 833 # from left to right 834 replaced_indices = {} 835 curr_ind = 10000 836 return_list = [] 837 838 for elem in immutable_order2: 839 can_elem = [elem[0], []] 840 for index in elem[1]: 841 if index>9999: # consider only summation indices 842 try: 843 new_index = replaced_indices[index] 844 except KeyError: 845 new_index = curr_ind 846 curr_ind += 1 847 replaced_indices[index] = new_index 848 else: new_index=index 849 can_elem[1].append(new_index) 850 # 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 851 if (can_elem[0]=="K6" or can_elem[0]=="K6Bar"): 852 if can_elem[1][2]>can_elem[1][1]: can_elem[1]=[can_elem[1][0], can_elem[1][2], can_elem[1][1] ] 853 return_list.append((can_elem[0], tuple(can_elem[1]))) 854 return_list.sort() 855 856 self.from_immutable(return_list) 857 self.immutable=None # don't use the information self.immutable later on in the code, 858 # since the summation indices have been modified 859 return
860
861 - def to_canonical(self, immutable=None):
862 """Returns the canonical representation of the immutable representation 863 (i.e., first index is 1, ...). This allow for an easy comparison of 864 two color strings, i.e. independently of the actual index names (only 865 relative positions matter). Also returns the conversion dictionary. 866 If no immutable representation is given, use the one build from self.""" 867 868 if not immutable: 869 immutable = self.to_immutable() 870 871 if self.canonical: 872 return self.canonical 873 874 replaced_indices = {} 875 curr_ind = 1 876 return_list = [] 877 878 for elem in immutable: 879 can_elem = [elem[0], []] 880 for index in elem[1]: 881 try: 882 new_index = replaced_indices[index] 883 except KeyError: 884 new_index = curr_ind 885 curr_ind += 1 886 replaced_indices[index] = new_index 887 can_elem[1].append(new_index) 888 return_list.append((can_elem[0], tuple(can_elem[1]))) 889 890 return_list.sort() 891 892 self.canonical = (tuple(return_list), replaced_indices) 893 return self.canonical
894
895 - def __eq__(self, col_str):
896 """Check if two color strings are equivalent by checking if their 897 canonical representations and the coefficients are equal.""" 898 899 return self.coeff == col_str.coeff and \ 900 self.Nc_power == col_str.Nc_power and \ 901 self.is_imaginary == col_str.is_imaginary and \ 902 self.to_canonical() == col_str.to_canonical()
903
904 - def __ne__(self, col_str):
905 """Logical opposite of ea""" 906 907 return not self.__eq__(col_str)
908
909 - def is_similar(self, col_str):
910 """Check if two color strings are similar by checking if their 911 canonical representations and Nc/I powers are equal.""" 912 913 return self.Nc_power == col_str.Nc_power and \ 914 self.is_imaginary == col_str.is_imaginary and \ 915 self.to_canonical() == col_str.to_canonical()
916
917 - def near_equivalent(self, col_str):
918 """Check if two color strings are equivalent looking only at 919 the color objects (used in color flow string calculation)""" 920 921 if len(self.to_canonical()) != len(col_str.to_canonical()): 922 return False 923 924 return all([co1[0] == co2[0] and sorted(co1[1]) == sorted(co2[1]) \ 925 for (co1,co2) in zip(self.to_canonical()[0], 926 col_str.to_canonical()[0])])
927 928 #=============================================================================== 929 # ColorFactor 930 #===============================================================================
931 -class ColorFactor(list):
932 """ColorFactor objects are list of ColorString with an implicit summation. 933 They can be simplified by simplifying all their elements.""" 934
935 - def __str__(self):
936 """Returns a nice string for printing""" 937 938 return '+'.join(['(%s)' % str(col_str) for col_str in self])
939
940 - def append_str(self, new_str):
941 """Special append taking care of adding new string to strings already 942 existing with the same structure.""" 943 944 for col_str in self: 945 # Check if strings are similar, this IS the optimal way of doing 946 # it. Note that first line only compare the lists, not the 947 # properties associated 948 if col_str.is_similar(new_str): 949 # Add them 950 col_str.add(new_str) 951 return True 952 953 # If no correspondence is found, append anyway 954 self.append(new_str) 955 return False
956
957 - def extend_str(self, new_col_fact):
958 """Special extend taking care of adding new strings to strings already 959 existing with the same structure.""" 960 961 # Reset "canonical", so don't get wrong result from comparison 962 self.canonical = None 963 self.immutable = None 964 965 for col_str in new_col_fact: 966 self.append_str(col_str)
967
968 - def simplify(self):
969 """Returns a new color factor where each color string has been 970 simplified once and similar strings have been added.""" 971 972 new_col_factor = ColorFactor() 973 # Simplify 974 for col_str in self: 975 res = col_str.simplify() 976 if res: 977 new_col_factor.extend_str(res) 978 else: 979 new_col_factor.append_str(col_str) 980 981 # Only returns non zero elements 982 return ColorFactor([col_str for col_str in \ 983 new_col_factor if col_str.coeff != 0])
984
985 - def full_simplify(self):
986 """Simplify the current color factor until the result is stable""" 987 988 result = copy.copy(self) 989 while(True): 990 ref = copy.copy(result) 991 result = result.simplify() 992 if result == ref: 993 return result
994
995 - def set_Nc(self, Nc=3):
996 """Returns a tuple containing real and imaginary parts of the current 997 color factor, when Nc is replaced (3 by default).""" 998 999 return (sum([cs.set_Nc(Nc)[0] for cs in self if not cs.is_imaginary]), 1000 sum([cs.set_Nc(Nc)[0] for cs in self if cs.is_imaginary]))
1001 1002
1003 - def replace_indices(self, repl_dict):
1004 """Replace current indices following the rules listed in the replacement 1005 dictionary written as {old_index:new_index,...}, does that for ALL 1006 color strings.""" 1007 1008 map(lambda col_str:col_str.replace_indices(repl_dict), self)
1009
1010 - def create_copy(self):
1011 """Returns a real copy of self, non trivial because bug in 1012 copy.deepcopy""" 1013 1014 res = ColorFactor() 1015 for col_str in self: 1016 res.append(col_str.create_copy()) 1017 1018 return res
1019 1020 __copy__ = create_copy
1021