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

Source Code for Module madgraph.core.color_amp

  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, methods and functions required to write QCD color information  
 17  for a diagram and build a color basis, and to square a QCD color string for 
 18  squared diagrams and interference terms.""" 
 19   
 20  from __future__ import absolute_import 
 21  import copy 
 22  import fractions 
 23  import operator 
 24  import re 
 25  import array 
 26   
 27  import madgraph.core.color_algebra as color_algebra 
 28  import madgraph.core.diagram_generation as diagram_generation 
 29  import madgraph.core.base_objects as base_objects 
 30  from six.moves import range 
 31  from functools import reduce 
32 33 #=============================================================================== 34 # ColorBasis 35 #=============================================================================== 36 -class ColorBasis(dict):
37 """The ColorBasis object is a dictionary created from an amplitude. Keys 38 are the different color structures present in the amplitude. Values have 39 the format (diag,(index c1, index c2,...), coeff, is_imaginary, Nc_power) 40 where diag is the diagram index, (index c1, index c2,...) the list of 41 indices corresponding to the chose color parts for each vertex in the 42 diagram, coeff the corresponding coefficient (a fraction), is_imaginary 43 if this contribution is real or complex, and Nc_power the Nc power.""" 44 45 # Dictionary to save simplifications already done in a canonical form 46 _canonical_dict = {} 47 48 # Dictionary store the raw colorize information 49 _list_color_dict = [] 50 51
52 - class ColorBasisError(Exception):
53 """Exception raised if an error occurs in the definition 54 or the execution of a color basis object.""" 55 pass
56
57 - def colorize(self, diagram, model):
58 """Takes a diagram and a model and outputs a dictionary with keys being 59 color coefficient index tuples and values a color string (before 60 simplification).""" 61 62 # The smallest value used to create new summed indices 63 min_index = -1000 64 # The dictionary to be output 65 res_dict = {} 66 # The dictionary for book keeping of replaced indices 67 repl_dict = {} 68 69 for i, vertex in enumerate(diagram.get('vertices')): 70 min_index, res_dict = self.add_vertex(vertex, diagram, model, 71 repl_dict, res_dict, min_index) 72 73 # if the process has no QCD particles 74 # Return a list filled with ColorOne if all entries are empty ColorString() 75 empty_colorstring = color_algebra.ColorString() 76 if all(cs == empty_colorstring for cs in res_dict.values()): 77 res_dict = dict((key, color_algebra.ColorString( 78 [color_algebra.ColorOne()])) for key in res_dict) 79 80 return res_dict
81 82 83
84 - def add_vertex(self, vertex, diagram, model, 85 repl_dict, res_dict, min_index, id0_rep=[]):
86 """Update repl_dict, res_dict and min_index for normal vertices. 87 Returns the min_index reached and the result dictionary in a tuple. 88 If the id0_rep list is not None, perform the requested replacement on the 89 last leg number before going further.""" 90 91 # Create a list of (color,leg number) pairs for the vertex, where color 92 # can be negative for anti particles 93 94 color_num_pairs = [] 95 pdg_codes = [] 96 97 for index, leg in enumerate(vertex.get('legs')): 98 curr_num = leg.get('number') 99 curr_part = model.get('particle_dict')[leg.get('id')] 100 curr_color = curr_part.get_color() 101 curr_pdg = curr_part.get_pdg_code() 102 103 # If this is the next-to-last vertex and the last vertex is 104 # the special identity id=0, start by applying the replacement rule 105 # on the last vertex. 106 if index == len(vertex.get('legs')) - 1 and \ 107 curr_num in id0_rep: 108 curr_num = id0_rep[id0_rep.index(curr_num) - 1] 109 110 # If this is the last leg and not the last vertex 111 # flip color. If it is not the last, AND not the next-to-last 112 # before an id=0 vertex, replace last index by a new summed index. 113 if index == len(vertex.get('legs')) - 1 and \ 114 vertex != diagram.get('vertices')[-1]: 115 curr_color = curr_part.get_anti_color() 116 curr_pdg = curr_part.get_anti_pdg_code() 117 if not id0_rep: 118 if not ( diagram.get('vertices')[-1].get('id')==-1 and \ 119 vertex == diagram.get('vertices')[-2]): 120 repl_dict[curr_num] = min_index 121 min_index = min_index - 1 122 else: 123 repl_dict[curr_num] = \ 124 max(l.get('number') for l in \ 125 diagram.get('vertices')[-1].get('legs')) 126 127 # Take into account previous replacements 128 try: 129 curr_num = repl_dict[curr_num] 130 except KeyError: 131 pass 132 133 color_num_pairs.append((curr_color, curr_num)) 134 pdg_codes.append(curr_pdg) 135 136 if vertex != diagram.get('vertices')[-1]: 137 # Put the resulting wavefunction first, to make 138 # wavefunction call more natural 139 last_color_num = color_num_pairs.pop(-1) 140 color_num_pairs.insert(0, last_color_num) 141 last_pdg = pdg_codes.pop(-1) 142 pdg_codes.insert(0, last_pdg) 143 144 # Order the legs according to the interaction particles 145 if vertex.get('id')!=-1: 146 interaction_pdgs = [p.get_pdg_code() for p in \ 147 model.get_interaction(vertex.get('id')).\ 148 get('particles')] 149 else: 150 interaction_pdgs = [l.get('id') for l in vertex.get('legs')] 151 152 sorted_color_num_pairs = [] 153 #print "interactions_pdg=",interaction_pdgs 154 #print "pdg_codes=",pdg_codes 155 for i, pdg in enumerate(interaction_pdgs): 156 index = pdg_codes.index(pdg) 157 pdg_codes.pop(index) 158 sorted_color_num_pairs.append(color_num_pairs.pop(index)) 159 160 if color_num_pairs: 161 raise base_objects.PhysicsObject.PhysicsObjectError 162 163 color_num_pairs = sorted_color_num_pairs 164 165 # Create a list of associated leg number following the same order 166 list_numbers = [p[1] for p in color_num_pairs] 167 168 # ... and the associated dictionary for replacement 169 match_dict = dict(enumerate(list_numbers)) 170 171 if vertex['id'] == -1: 172 return (min_index, res_dict) 173 174 # Update the result dict using the current vertex ColorString object 175 # If more than one, create different entries 176 inter_color = model.get_interaction(vertex['id'])['color'] 177 inter_indices = [i for (i,j) in \ 178 model.get_interaction(vertex['id'])['couplings'].keys()] 179 180 # For colorless vertices, return a copy of res_dict 181 # Where one 0 has been added to each color index chain key 182 if not inter_color: 183 new_dict = {} 184 for k, v in res_dict.items(): 185 new_key = tuple(list(k) + [0]) 186 new_dict[new_key] = v 187 # If there is no result until now, create an empty CS... 188 if not new_dict: 189 new_dict[(0,)] = color_algebra.ColorString() 190 return (min_index, new_dict) 191 192 new_res_dict = {} 193 for i, col_str in \ 194 enumerate(inter_color): 195 196 # Ignore color string if it doesn't correspond to any coupling 197 if i not in inter_indices: 198 continue 199 200 # Build the new element 201 assert type(col_str) == color_algebra.ColorString 202 mod_col_str = col_str.create_copy() 203 204 # Replace summed (negative) internal indices 205 list_neg = [] 206 for col_obj in mod_col_str: 207 list_neg.extend([ind for ind in col_obj if ind < 0]) 208 internal_indices_dict = {} 209 # This notation is to remove duplicates 210 for index in list(set(list_neg)): 211 internal_indices_dict[index] = min_index 212 min_index = min_index - 1 213 mod_col_str.replace_indices(internal_indices_dict) 214 215 # Replace other (positive) indices using the match_dic 216 mod_col_str.replace_indices(match_dict) 217 218 # If we are considering the first vertex, simply create 219 # new entries 220 221 if not res_dict: 222 new_res_dict[tuple([i])] = mod_col_str 223 #... otherwise, loop over existing elements and multiply 224 # the color strings 225 else: 226 for ind_chain, col_str_chain in res_dict.items(): 227 new_col_str_chain = col_str_chain.create_copy() 228 new_col_str_chain.product(mod_col_str) 229 new_res_dict[tuple(list(ind_chain) + [i])] = \ 230 new_col_str_chain 231 232 return (min_index, new_res_dict)
233 234
235 - def update_color_basis(self, colorize_dict, index):
236 """Update the current color basis by adding information from 237 the colorize dictionary (produced by the colorize routine) 238 associated to diagram with index index. Keep track of simplification 239 results for maximal optimization.""" 240 import madgraph.various.misc as misc 241 # loop over possible color chains 242 for col_chain, col_str in colorize_dict.items(): 243 # Create a canonical immutable representation of the the string 244 canonical_rep, rep_dict = col_str.to_canonical() 245 try: 246 # If this representation has already been considered, 247 # recycle the result. 248 col_fact = self._canonical_dict[canonical_rep].create_copy() 249 except KeyError: 250 # If the representation is really new 251 252 # Create and simplify a color factor for the considered chain 253 col_fact = color_algebra.ColorFactor([col_str]) 254 col_fact = col_fact.full_simplify() 255 256 # Here we need to force a specific order for the summed indices 257 # in case we have K6 or K6bar Clebsch Gordan coefficients 258 for colstr in col_fact: colstr.order_summation() 259 260 # Save the result for further use 261 canonical_col_fact = col_fact.create_copy() 262 canonical_col_fact.replace_indices(rep_dict) 263 # Remove overall coefficient 264 for cs in canonical_col_fact: 265 cs.coeff = cs.coeff / col_str.coeff 266 self._canonical_dict[canonical_rep] = canonical_col_fact 267 else: 268 # If this representation has already been considered, 269 # adapt the result 270 # Note that we have to replace back 271 # the indices to match the initial convention. 272 col_fact.replace_indices(self._invert_dict(rep_dict)) 273 # Since the initial coeff of col_str is not taken into account 274 # for matching, we have to multiply col_fact by it. 275 for cs in col_fact: 276 cs.coeff = cs.coeff * col_str.coeff 277 # Must simplify up to two times at NLO (since up to two traces 278 # can appear with a loop) to put traces in a canonical ordering. 279 # If it still causes issue, just do a full_simplify(), it would 280 # not bring any heavy additional computational load. 281 col_fact = col_fact.simplify().simplify() 282 283 # Here we need to force a specific order for the summed indices 284 # in case we have K6 or K6bar Clebsch Gordan coefficients 285 for colstr in col_fact: colstr.order_summation() 286 287 # loop over color strings in the resulting color factor 288 for col_str in col_fact: 289 immutable_col_str = col_str.to_immutable() 290 # if the color structure is already present in the present basis 291 # update it 292 basis_entry = (index, 293 col_chain, 294 col_str.coeff, 295 col_str.is_imaginary, 296 col_str.Nc_power, 297 col_str.loop_Nc_power) 298 try: 299 self[immutable_col_str].append(basis_entry) 300 except KeyError: 301 self[immutable_col_str] = [basis_entry]
302
303 - def create_color_dict_list(self, amplitude):
304 """Returns a list of colorize dict for all diagrams in amplitude. Also 305 update the _list_color_dict object accordingly """ 306 307 list_color_dict = [] 308 309 for diagram in amplitude.get('diagrams'): 310 colorize_dict = self.colorize(diagram, 311 amplitude.get('process').get('model')) 312 list_color_dict.append(colorize_dict) 313 314 self._list_color_dict = list_color_dict 315 316 return list_color_dict
317
318 - def build(self, amplitude=None):
319 """Build the a color basis object using information contained in 320 amplitude (otherwise use info from _list_color_dict). 321 Returns a list of color """ 322 323 if amplitude: 324 self.create_color_dict_list(amplitude) 325 for index, color_dict in enumerate(self._list_color_dict): 326 self.update_color_basis(color_dict, index)
327
328 - def __init__(self, *args):
329 """Initialize a new color basis object, either empty or filled (0 330 or 1 arguments). If one arguments is given, it's interpreted as 331 an amplitude.""" 332 333 assert len(args) < 2, "Object ColorBasis must be initialized with 0 or 1 arguments" 334 335 336 dict.__init__(self) 337 338 # Dictionary to save simplifications already done in a canonical form 339 self._canonical_dict = {} 340 341 # Dictionary store the raw colorize information 342 self._list_color_dict = [] 343 344 345 if args: 346 assert isinstance(args[0], diagram_generation.Amplitude), \ 347 "%s is not a valid Amplitude object" % str(args[0]) 348 349 self.build(*args)
350
351 - def __str__(self):
352 """Returns a nicely formatted string for display""" 353 354 my_str = "" 355 for k, v in self.items(): 356 for name, indices in k: 357 my_str = my_str + name + str(indices) 358 my_str = my_str + ': ' 359 for contrib in v: 360 imag_str = '' 361 if contrib[3]: 362 imag_str = 'I' 363 my_str = my_str + '(diag:%i, chain:%s, coeff:%s%s, Nc:%i) ' % \ 364 (contrib[0], contrib[1], contrib[2], 365 imag_str, contrib[4]) 366 my_str = my_str + '\n' 367 return my_str
368
369 - def _invert_dict(self, mydict):
370 """Helper method to invert dictionary dict""" 371 372 return dict([v, k] for k, v in mydict.items())
373 374 @staticmethod
375 - def get_color_flow_string(my_color_string, octet_indices):
376 """Return the color_flow_string (i.e., composed only of T's with 2 377 indices) associated to my_color_string. Take a list of the external leg 378 color octet state indices as an input. Returns only the leading N 379 contribution!""" 380 # Create a new color factor to allow for simplification 381 my_cf = color_algebra.ColorFactor([my_color_string]) 382 383 # Add one T per external octet 384 for indices in octet_indices: 385 if indices[0] == -6: 386 # Add a K6 which contracts the antisextet index to a 387 # pair of antitriplets 388 my_cf[0].append(color_algebra.K6(indices[1], 389 indices[2], 390 indices[3])) 391 if indices[0] == 6: 392 # Add a K6Bar which contracts the sextet index to a 393 # pair of triplets 394 my_cf[0].append(color_algebra.K6Bar(indices[1], 395 indices[2], 396 indices[3])) 397 if abs(indices[0]) == 8: 398 # Add a T which contracts the octet to a 399 # triplet-antitriplet pair 400 my_cf[0].append(color_algebra.T(indices[1], 401 indices[2], 402 indices[3])) 403 # Simplify the whole thing 404 my_cf = my_cf.full_simplify() 405 406 # If the result is empty, just return 407 if not my_cf: 408 return my_cf 409 410 # Return the string with the highest N coefficient 411 # (leading N decomposition), and the value of this coeff 412 max_coeff = max([cs.Nc_power for cs in my_cf]) 413 414 res_cs = [cs for cs in my_cf if cs.Nc_power == max_coeff] 415 416 # If more than one string at leading N... 417 if len(res_cs) > 1 and any([not cs.near_equivalent(res_cs[0]) \ 418 for cs in res_cs]): 419 raise ColorBasis.ColorBasisError("More than one color string with leading N coeff: %s" % str(res_cs)) 420 421 res_cs = res_cs[0] 422 423 # If the result string does not contain only T's with two indices 424 # and Epsilon/EpsilonBar objects 425 for col_obj in res_cs: 426 if not isinstance(col_obj, color_algebra.T) and \ 427 not col_obj.__class__.__name__.startswith('Epsilon'): 428 raise ColorBasis.ColorBasisError("Color flow decomposition %s contains non T/Epsilon elements" % \ 429 str(res_cs)) 430 if isinstance(col_obj, color_algebra.T) and len(col_obj) != 2: 431 raise ColorBasis.ColorBasisError("Color flow decomposition %s contains T's w/o 2 indices" % \ 432 str(res_cs)) 433 434 return res_cs
435
436 - def color_flow_decomposition(self, repr_dict, ninitial):
437 """Returns the color flow decomposition of the current basis, i.e. a 438 list of dictionaries (one per color basis entry) with keys corresponding 439 to external leg numbers and values tuples containing two color indices 440 ( (0,0) for singlets, (X,0) for triplet, (0,X) for antitriplet and 441 (X,Y) for octets). Other color representations are not yet supported 442 here (an error is raised). Needs a dictionary with keys being external 443 leg numbers, and value the corresponding color representation.""" 444 445 # Offsets used to introduce fake quark indices for gluons 446 offset1 = 1000 447 offset2 = 2000 448 offset3 = 3000 449 450 res = [] 451 452 for col_basis_entry in sorted(self.keys()): 453 454 res_dict = {} 455 fake_repl = [] 456 457 # Rebuild a color string from a CB entry 458 col_str = color_algebra.ColorString() 459 col_str.from_immutable(col_basis_entry) 460 for (leg_num, leg_repr) in repr_dict.items(): 461 # By default, assign a (0,0) color flow 462 res_dict[leg_num] = [0, 0] 463 464 # Raise an error if external legs contain non supported repr 465 if abs(leg_repr) not in [1, 3, 6, 8]: 466 raise ColorBasis.ColorBasisError("Particle ID=%i has an unsupported color representation" % leg_repr) 467 468 # Build the fake indices replacements for octets 469 if abs(leg_repr) == 8: 470 fake_repl.append((leg_repr, leg_num, 471 offset1 + leg_num, 472 offset2 + leg_num)) 473 # Build the fake indices for sextets 474 elif leg_repr in [-6, 6]: 475 fake_repl.append((leg_repr, leg_num, 476 offset1 + leg_num, 477 offset3 + leg_num)) 478 479 # Get the actual color flow 480 col_str_flow = self.get_color_flow_string(col_str, fake_repl) 481 482 # Offset for color flow 483 offset = 500 484 485 for col_obj in col_str_flow: 486 if isinstance(col_obj, color_algebra.T): 487 # For T, all color indices should be the same 488 offset = offset + 1 489 for i, index in enumerate(col_obj): 490 if isinstance(col_obj, color_algebra.Epsilon): 491 # Epsilon contracts with antitriplets, 492 i = 0 493 # ...and requires all different color indices 494 offset = offset+1 495 elif isinstance(col_obj, color_algebra.EpsilonBar): 496 # EpsilonBar contracts with antitriplets 497 i = 1 498 # ...and requires all different color indices 499 offset = offset+1 500 if index < offset1: 501 res_dict[index][i] = offset 502 elif index > offset1 and index < offset2: 503 res_dict[index - offset1][i] = offset 504 elif index > offset2 and index < offset3: 505 res_dict[index - offset2][i] = offset 506 elif index > offset3: 507 # For color sextets, use negative triplet 508 # number to reperesent antitriplet and vice 509 # versa, allowing for two triplet or two 510 # antitriplet numbers representing the color 511 # sextet. 512 res_dict[index - offset3][1-i] = -offset 513 514 # Reverse ordering for initial state to stick to the (weird) 515 # les houches convention 516 517 for key in res_dict.keys(): 518 if key <= ninitial: 519 res_dict[key].reverse() 520 521 res.append(res_dict) 522 523 return res
524
525 526 527 528 #=============================================================================== 529 # ColorMatrix 530 #=============================================================================== 531 -class ColorMatrix(dict):
532 """A color matrix, meaning a dictionary with pairs (i,j) as keys where i 533 and j refer to elements of color basis objects. Values are Color Factor 534 objects. Also contains two additional dictionaries, one with the fixed Nc 535 representation of the matrix, and the other one with the "inverted" matrix, 536 i.e. a dictionary where keys are values of the color matrix.""" 537 538 _col_basis1 = None 539 _col_basis2 = None 540 col_matrix_fixed_Nc = {} 541 inverted_col_matrix = {} 542
543 - def __init__(self, col_basis, col_basis2=None, 544 Nc=3, Nc_power_min=None, Nc_power_max=None):
545 """Initialize a color matrix with one or two color basis objects. If 546 only one color basis is given, the other one is assumed to be equal. 547 As options, any value of Nc and minimal/maximal power of Nc can also be 548 provided. Note that the min/max power constraint is applied 549 only at the end, so that it does NOT speed up the calculation.""" 550 551 self.col_matrix_fixed_Nc = {} 552 self.inverted_col_matrix = {} 553 554 self._col_basis1 = col_basis 555 if col_basis2: 556 self._col_basis2 = col_basis2 557 self.build_matrix(Nc, Nc_power_min, Nc_power_max) 558 else: 559 self._col_basis2 = col_basis 560 # If the two color basis are equal, assumes the color matrix is 561 # symmetric 562 self.build_matrix(Nc, Nc_power_min, Nc_power_max, is_symmetric=True)
563
564 - def build_matrix(self, Nc=3, 565 Nc_power_min=None, 566 Nc_power_max=None, 567 is_symmetric=False):
568 """Create the matrix using internal color basis objects. Use the stored 569 color basis objects and takes Nc and Nc_min/max parameters as __init__. 570 If is_isymmetric is True, build only half of the matrix which is assumed 571 to be symmetric.""" 572 573 canonical_dict = {} 574 575 for i1, struct1 in \ 576 enumerate(sorted(self._col_basis1.keys())): 577 for i2, struct2 in \ 578 enumerate(sorted(self._col_basis2.keys())): 579 # Only scan upper right triangle if symmetric 580 if is_symmetric and i2 < i1: 581 continue 582 583 # Fix indices in struct2 knowing summed indices in struct1 584 # to avoid duplicates 585 new_struct2 = self.fix_summed_indices(struct1, struct2) 586 587 # Build a canonical representation of the two immutable struct 588 canonical_entry, dummy = \ 589 color_algebra.ColorString().to_canonical(struct1 + \ 590 new_struct2) 591 592 try: 593 # If this has already been calculated, use the result 594 result, result_fixed_Nc = canonical_dict[canonical_entry] 595 except KeyError: 596 # Otherwise calculate the result 597 result, result_fixed_Nc = \ 598 self.create_new_entry(struct1, 599 new_struct2, 600 Nc_power_min, 601 Nc_power_max, 602 Nc) 603 # Store both results 604 canonical_dict[canonical_entry] = (result, result_fixed_Nc) 605 606 # Store the full result... 607 self[(i1, i2)] = result 608 if is_symmetric: 609 self[(i2, i1)] = result 610 611 # the fixed Nc one ... 612 self.col_matrix_fixed_Nc[(i1, i2)] = result_fixed_Nc 613 if is_symmetric: 614 self.col_matrix_fixed_Nc[(i2, i1)] = result_fixed_Nc 615 # and update the inverted dict 616 if result_fixed_Nc in list(self.inverted_col_matrix.keys()): 617 self.inverted_col_matrix[result_fixed_Nc].append((i1, 618 i2)) 619 if is_symmetric: 620 self.inverted_col_matrix[result_fixed_Nc].append((i2, 621 i1)) 622 else: 623 self.inverted_col_matrix[result_fixed_Nc] = [(i1, i2)] 624 if is_symmetric: 625 self.inverted_col_matrix[result_fixed_Nc] = [(i2, i1)]
626
627 - def create_new_entry(self, struct1, struct2, 628 Nc_power_min, Nc_power_max, Nc):
629 """ Create a new product result, and result with fixed Nc for two color 630 basis entries. Implement Nc power limits.""" 631 632 # Create color string objects corresponding to color basis 633 # keys 634 col_str = color_algebra.ColorString() 635 col_str.from_immutable(struct1) 636 637 col_str2 = color_algebra.ColorString() 638 col_str2.from_immutable(struct2) 639 640 # Complex conjugate the second one and multiply the two 641 col_str.product(col_str2.complex_conjugate()) 642 643 # Create a color factor to store the result and simplify it 644 # taking into account the limit on Nc 645 col_fact = color_algebra.ColorFactor([col_str]) 646 result = col_fact.full_simplify() 647 648 # Keep only terms with Nc_max >= Nc power >= Nc_min 649 if Nc_power_min is not None: 650 result[:] = [col_str for col_str in result \ 651 if col_str.Nc_power >= Nc_power_min] 652 if Nc_power_max is not None: 653 result[:] = [col_str for col_str in result \ 654 if col_str.Nc_power <= Nc_power_max] 655 656 # Calculate the fixed Nc representation 657 result_fixed_Nc = result.set_Nc(Nc) 658 659 return result, result_fixed_Nc
660
661 - def __str__(self):
662 """Returns a nicely formatted string with the fixed Nc representation 663 of the current matrix (only the real part)""" 664 665 mystr = '\n\t' + '\t'.join([str(i) for i in \ 666 range(len(self._col_basis2))]) 667 668 for i1 in range(len(self._col_basis1)): 669 mystr = mystr + '\n' + str(i1) + '\t' 670 mystr = mystr + '\t'.join(['%i/%i' % \ 671 (self.col_matrix_fixed_Nc[(i1, i2)][0].numerator, 672 self.col_matrix_fixed_Nc[(i1, i2)][0].denominator) \ 673 for i2 in range(len(self._col_basis2))]) 674 675 return mystr
676
677 - def get_line_denominators(self):
678 """Get a list with the denominators for the different lines in 679 the color matrix""" 680 681 den_list = [] 682 for i1 in range(len(self._col_basis1)): 683 den_list.append(self.lcmm(*[\ 684 self.col_matrix_fixed_Nc[(i1, i2)][0].denominator for \ 685 i2 in range(len(self._col_basis2))])) 686 return den_list
687
688 - def get_line_numerators(self, line_index, den):
689 """Returns a list of numerator for line line_index, assuming a common 690 denominator den.""" 691 692 return [self.col_matrix_fixed_Nc[(line_index, i2)][0].numerator * \ 693 den / self.col_matrix_fixed_Nc[(line_index, i2)][0].denominator \ 694 for i2 in range(len(self._col_basis2))]
695 696 @classmethod
697 - def fix_summed_indices(self, struct1, struct2):
698 """Returns a copy of the immutable Color String representation struct2 699 where summed indices are modified to avoid duplicates with those 700 appearing in struct1. Assumes internal summed indices are negative.""" 701 702 # First, determines what is the smallest index appearing in struct1 703 #list2 = reduce(operator.add,[list(elem[1]) for elem in struct1]) 704 list2 = sum((list(elem[1]) for elem in struct1),[]) 705 if not list2: 706 min_index = -1 707 else: 708 min_index = min(list2) - 1 709 710 # Second, determines the summed indices in struct2 and create a 711 # replacement dictionary 712 repl_dict = {} 713 #list2 = reduce(operator.add, 714 # [list(elem[1]) for elem in struct1]) 715 for summed_index in list(set([i for i in list2 \ 716 if list2.count(i) == 2])): 717 repl_dict[summed_index] = min_index 718 min_index -= 1 719 720 # Three, create a new immutable struct by doing replacements in struct2 721 return_list = [] 722 for elem in struct2: 723 fix_elem = [elem[0], []] 724 for index in elem[1]: 725 try: 726 fix_elem[1].append(repl_dict[index]) 727 except Exception: 728 fix_elem[1].append(index) 729 return_list.append((elem[0], tuple(fix_elem[1]))) 730 731 return tuple(return_list)
732 733 @staticmethod
734 - def lcm(a, b):
735 """Return lowest common multiple.""" 736 return a * b // fractions.gcd(a, b)
737 738 @staticmethod
739 - def lcmm(*args):
740 """Return lcm of args.""" 741 if args: 742 return reduce(ColorMatrix.lcm, args) 743 else: 744 return 1
745