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