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