Merge lp:~madteam/mg5amcnlo/new_color into lp:~madteam/mg5amcnlo/trunk

Proposed by Michel Herquet
Status: Merged
Merged at revision: not available
Proposed branch: lp:~madteam/mg5amcnlo/new_color
Merge into: lp:~madteam/mg5amcnlo/trunk
Diff against target: 2772 lines (+2381/-38)
17 files modified
.project (+4/-0)
.pydevproject (+1/-1)
madgraph/VERSION (+2/-2)
madgraph/core/base_objects.py (+41/-7)
madgraph/core/color_algebra.py (+607/-0)
madgraph/core/color_amp.py (+439/-0)
madgraph/interface/cmd_interface.py (+41/-0)
madgraph/iolibs/files.py (+24/-0)
madgraph/iolibs/import_model_v4.py (+5/-0)
madgraph/iolibs/save_model.py (+89/-0)
tests/test_manager.py (+5/-1)
tests/unit_tests/core/test_base_objects.py (+11/-9)
tests/unit_tests/core/test_color_algebra.py (+352/-0)
tests/unit_tests/core/test_color_amp.py (+600/-0)
tests/unit_tests/core/test_diagram_generation.py (+10/-10)
tests/unit_tests/iolibs/test_import_model_v4.py (+23/-8)
tests/unit_tests/iolibs/test_save_model.py (+127/-0)
To merge this branch: bzr merge lp:~madteam/mg5amcnlo/new_color
Reviewer Review Type Date Requested Status
Johan Alwall (community) Approve
Olivier Mattelaer Approve
Tim Stelzer Approve
Review via email: mp+17008@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michel Herquet (herquet) wrote :

The color modules:
color_algebra: where the base classes ColorObjects (and children T, Tr, f and d) are defined
color_amp: where the ColorBasis is built
color_square: where the ColorMatrix is built

Important points to review:
-) Proposal for new tests
-) Code structure/clarity/comments

Revision history for this message
Tim Stelzer (tstelzer) wrote :

Very nice, and very clear. Only part that took me a bit extra to understand were canonical and immutable representations. So perhaps in definition could add couple of lines explaining their purpose.

review: Approve
Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

Trully nice, quite clear.

The only point who trully trouble me is ColorString which is anything but a string. Could you think of rename this class and the instance associate to this one.

Event if it's trully well written, my feeling is that we can sometimes split functions in subfunction to increase intention and readability.
for example:
   -> color_square l.90: put all the except part in a function
   -> color_amp l.36: the routine colorize can be splitted in three.
                      i.e. having two routine dealing with one vertex one for id=0 and another for id!=0

but this is not mandatory at all.

and to finish some pointless details:

-> color_algebra l.448: For readability, it's not better to put a space after the lambda (nothing is specified in PEP08 on this specific case)

-> color_amp line 158: the code content in the try is too much larger. it calls two different functions. such that the try can hidden a keyError produces inside those functions.
At least simplify() should be in the part else:

->color square l.85: potentially same type of problem. In this cas 3 different KeyError are potentially raised. Are you 200% that only the first can create KeyError?

-> color square l.67: unused argument contrib_list1 (and two line after contrib_list2)
why not use a .keys() instead of .enumerate()

Very good work!!!

review: Needs Fixing
lp:~madteam/mg5amcnlo/new_color updated
69. By Michel Herquet

New version to adress Tim and Olivier's comments

Revision history for this message
Olivier Mattelaer (olivier-mattelaer) wrote :

thanks for those modifications!!

review: Approve
Revision history for this message
Johan Alwall (johan-alwall) wrote :

Comments for color:

* Overall:
  - Very nicely commented and readable code
  - Apparently highly (maximally?) optimized
  - Extremely neat and clear solutions

* Why create three modules instead of just one bigger module?
Do you foresee uses of color_algebra without the others?
Do you suggest that we should break up other models as well?
Is there a problem that there are parts of a module that are not used?
I would have expected that we only want to separate a module into
components if they belong to different parts of the directory
structure, such as core and io. I think it is important that we follow
the same philosophy, and this should be fixed before the merge if possible.

color_amp.py:
* line 87+: I think we need to make sure we order interactions in the
same way (between helas calls and color). I order according to cyclic
order in interaction and leg number. But we can take that after the merge.

color_square.py:
* line 43: "Be careful that" -> "Note that" (the formulation is a bit
confusing)

color_algebra.py:
* No comments, the code seems great!

review: Needs Information
lp:~madteam/mg5amcnlo/new_color updated
70. By Michel Herquet

Fix Johan's comment

Revision history for this message
Michel Herquet (herquet) wrote :

On Jan 15, 2010, at 3:59 PM, Johan Alwall wrote:

> Review: Needs Information
> Comments for color:
>
> * Overall:
> - Very nicely commented and readable code
> - Apparently highly (maximally?) optimized
> - Extremely neat and clear solutions
Thanks!

> * Why create three modules instead of just one bigger module?
> Do you foresee uses of color_algebra without the others?
> Do you suggest that we should break up other models as well?
> Is there a problem that there are parts of a module that are not used?
> I would have expected that we only want to separate a module into
> components if they belong to different parts of the directory
> structure, such as core and io. I think it is important that we follow
> the same philosophy, and this should be fixed before the merge if possible.
I agree, let's discuss this as a first step in the merge procedure. My current
POV is that we should give a good equilibrium between structure
clarity and readability (long files are hard to deal with). Also color_algebra
can indeed be used independently of v5, while it's not the case for the two
other modules. But maybe we can merge those...

> color_amp.py:
> * line 87+: I think we need to make sure we order interactions in the
> same way (between helas calls and color). I order according to cyclic
> order in interaction and leg number. But we can take that after the merge.
Right.

> color_square.py:
> * line 43: "Be careful that" -> "Note that" (the formulation is a bit
> confusing)
Fixed.

> color_algebra.py:
> * No comments, the code seems great!
Thank for your time reviewing it!

>
> --
> https://code.launchpad.net/~madteam/madgraph5/new_color/+merge/17008
> You proposed lp:~madteam/madgraph5/new_color for merging.

Michel Herquet
---------------------------------------------------------------------------------
Postdoctoral researcher in Particle Physics Phenomenology

NIKHEF - Theory group E-mail: <email address hidden>
Science Park 105 Tel: +31 20 592 51 48
1098 XG Amsterdam Skype: mherquet
The Netherlands G-chat: <email address hidden>

Revision history for this message
Johan Alwall (johan-alwall) wrote :

Thanks, great job!

review: Approve
lp:~madteam/mg5amcnlo/new_color updated
71. By Michel Herquet

Merge color_square and color_amp

72. By Michel Herquet

Last revision before merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.project'
2--- .project 2009-12-17 12:45:20 +0000
3+++ .project 2010-01-21 09:19:15 +0000
4@@ -1,6 +1,10 @@
5 <?xml version="1.0" encoding="UTF-8"?>
6 <projectDescription>
7+<<<<<<< TREE
8 <name>MG-process-devel</name>
9+=======
10+ <name>MG-new_color</name>
11+>>>>>>> MERGE-SOURCE
12 <comment></comment>
13 <projects>
14 </projects>
15
16=== modified file '.pydevproject'
17--- .pydevproject 2010-01-21 09:12:43 +0000
18+++ .pydevproject 2010-01-21 09:19:15 +0000
19@@ -5,6 +5,6 @@
20 <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.6</pydev_property>
21 <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
22 <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
23-<path>/MG-trunk</path>
24+<path>/MG-new_color</path>
25 </pydev_pathproperty>
26 </pydev_project>
27
28=== modified file 'madgraph/VERSION'
29--- madgraph/VERSION 2009-10-21 14:20:40 +0000
30+++ madgraph/VERSION 2010-01-21 09:19:15 +0000
31@@ -1,2 +1,2 @@
32-version = 0.2.0
33-date = 2009-11-05
34\ No newline at end of file
35+version = 0.3.0/color
36+date = 2010-01-11
37\ No newline at end of file
38
39=== modified file 'madgraph/core/base_objects.py'
40--- madgraph/core/base_objects.py 2010-01-20 03:15:48 +0000
41+++ madgraph/core/base_objects.py 2010-01-21 09:19:15 +0000
42@@ -21,6 +21,8 @@
43 import logging
44 import re
45
46+import madgraph.core.color_algebra as color
47+
48 #===============================================================================
49 # PhysicsObject
50 #===============================================================================
51@@ -461,8 +463,18 @@
52 raise self.PhysicsObjectError, \
53 "%s is not a valid integer" % str(value[order])
54
55- if name in ['color', 'lorentz']:
56- #Should be a list of strings
57+ if name in ['color']:
58+ #Should be a list of list strings
59+ if not isinstance(value, list):
60+ raise self.PhysicsObjectError, \
61+ "%s is not a valid list of Color Strings" % str(value)
62+ for mycolstring in value:
63+ if not isinstance(mycolstring, color.ColorString):
64+ raise self.PhysicsObjectError, \
65+ "%s is not a valid list of Color Strings" % str(value)
66+
67+ if name in ['lorentz']:
68+ #Should be a list of list strings
69 if not isinstance(value, list):
70 raise self.PhysicsObjectError, \
71 "%s is not a valid list of strings" % str(value)
72@@ -478,11 +490,6 @@
73 "%s is not a valid dictionary for couplings" % \
74 str(value)
75
76- if len(value) != len(self['color']) * len(self['lorentz']):
77- raise self.PhysicsObjectError, \
78- "Dictionary " + str(value) + \
79- " for couplings has not the right number of entry"
80-
81 for key in value.keys():
82 if not isinstance(key, tuple):
83 raise self.PhysicsObjectError, \
84@@ -542,6 +549,29 @@
85 else:
86 ref_dict_to1[pdg_tuple] = [(pdg_part, self['id'])]
87
88+ def __str__(self):
89+ """String representation of an interaction. Outputs valid Python
90+ with improved format. Overrides the PhysicsObject __str__ to only
91+ display PDG code of involved particles."""
92+
93+ mystr = '{\n'
94+
95+ for prop in self.get_sorted_keys():
96+ if isinstance(self[prop], str):
97+ mystr = mystr + ' \'' + prop + '\': \'' + \
98+ self[prop] + '\',\n'
99+ elif isinstance(self[prop], float):
100+ mystr = mystr + ' \'' + prop + '\': %.2f,\n' % self[prop]
101+ elif isinstance(self[prop], ParticleList):
102+ mystr = mystr + ' \'' + prop + '\': [%s],\n' % \
103+ ','.join([str(part.get_pdg_code()) for part in self[prop]])
104+ else:
105+ mystr = mystr + ' \'' + prop + '\': ' + \
106+ repr(self[prop]) + ',\n'
107+ mystr = mystr.rstrip(',\n')
108+ mystr = mystr + '\n}'
109+
110+ return mystr
111
112 #===============================================================================
113 # InteractionList
114@@ -1067,7 +1097,11 @@
115 for vert in self['vertices']:
116 mystr = mystr + '('
117 for leg in vert['legs'][:-1]:
118+<<<<<<< TREE
119 mystr = mystr + str(leg['number']) + '(%s)' % str(leg['id']) + ','
120+=======
121+ mystr = mystr + str(leg['number']) + ','
122+>>>>>>> MERGE-SOURCE
123 if self['vertices'].index(vert) < len(self['vertices']) - 1:
124 # Do not want ">" in the last vertex
125 mystr = mystr[:-1] + '>'
126
127=== added file 'madgraph/core/color_algebra.py'
128--- madgraph/core/color_algebra.py 1970-01-01 00:00:00 +0000
129+++ madgraph/core/color_algebra.py 2010-01-21 09:19:15 +0000
130@@ -0,0 +1,607 @@
131+################################################################################
132+#
133+# Copyright (c) 2009 The MadGraph Development team and Contributors
134+#
135+# This file is a part of the MadGraph 5 project, an application which
136+# automatically generates Feynman diagrams and matrix elements for arbitrary
137+# high-energy processes in the Standard Model and beyond.
138+#
139+# It is subject to the MadGraph license which should accompany this
140+# distribution.
141+#
142+# For more information, please visit: http://madgraph.phys.ucl.ac.be
143+#
144+################################################################################
145+
146+"""Classes and methods required for all calculations related to SU(N) color
147+algebra."""
148+
149+import array
150+import copy
151+import fractions
152+import itertools
153+
154+#===============================================================================
155+# ColorObject
156+#===============================================================================
157+class ColorObject(array.array):
158+ """Parent class for all color objects like T, Tr, f, d, ... Any new color
159+ object MUST inherit from this class!"""
160+
161+ def __new__(self, *args):
162+ """Create a new ColorObject, assuming an integer array"""
163+
164+ return super(ColorObject, self).__new__(self, 'i', args)
165+
166+ def __str__(self):
167+ """Returns a standard string representation."""
168+
169+ return '%s(%s)' % (self.__class__.__name__,
170+ ','.join([str(i) for i in self]))
171+
172+ __repr__ = __str__
173+
174+ def simplify(self):
175+ """Simplification rules, to be overwritten for each new color object!
176+ Should return a color factor or None if no simplification is possible"""
177+ return None
178+
179+ def pair_simplify(self, other):
180+ """Pair simplification rules, to be overwritten for each new color
181+ object! Should return a color factor or None if no simplification
182+ is possible"""
183+ return None
184+
185+ def complex_conjugate(self):
186+ """Complex conjugation. By default, the ordering of color index is
187+ reversed. Can be overwritten for specific color objects like T,..."""
188+
189+ self.reverse()
190+
191+ def replace_indices(self, repl_dict):
192+ """Replace current indices following the rules listed in the replacement
193+ dictionary written as {old_index:new_index,...}. Deals correctly with
194+ the replacement by allowing only one single replacement."""
195+
196+ for i, index in enumerate(self):
197+ try:
198+ self[i] = repl_dict[index]
199+ except KeyError:
200+ continue
201+
202+ def create_copy(self):
203+ """Return a real copy of the current object."""
204+ return globals()[self.__class__.__name__](*self)
205+
206+ __copy__ = create_copy
207+
208+#===============================================================================
209+# Tr
210+#===============================================================================
211+class Tr(ColorObject):
212+ """The trace color object"""
213+
214+ def simplify(self):
215+ """Implement simple trace simplifications and cyclicity, and
216+ Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c))"""
217+
218+ # Tr(a)=0
219+ if len(self) == 1:
220+ col_str = ColorString()
221+ col_str.coeff = fractions.Fraction(0, 1)
222+ return ColorFactor([col_str])
223+
224+ # Tr()=Nc
225+ if len(self) == 0:
226+ col_str = ColorString()
227+ col_str.Nc_power = 1
228+ return ColorFactor([col_str])
229+
230+ # Always order starting from smallest index
231+ if self[0] != min(self):
232+ pos = self.index(min(self))
233+ new = self[pos:] + self[:pos]
234+ return ColorFactor([ColorString([Tr(*new)])])
235+
236+ # Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c))
237+ for i1, index1 in enumerate(self):
238+ for i2, index2 in enumerate(self[i1 + 1:]):
239+ if index1 == index2:
240+ a = self[:i1]
241+ b = self[i1 + 1:i1 + i2 + 1]
242+ c = self[i1 + i2 + 2:]
243+ col_str1 = ColorString([Tr(*(a + c)), Tr(*b)])
244+ col_str2 = ColorString([Tr(*(a + b + c))])
245+ col_str1.coeff = fractions.Fraction(1, 2)
246+ col_str2.coeff = fractions.Fraction(-1, 2)
247+ col_str2.Nc_power = -1
248+ return ColorFactor([col_str1, col_str2])
249+
250+ return None
251+
252+ def pair_simplify(self, col_obj):
253+ """Implement Tr product simplification:
254+ Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d)) and
255+ 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))"""
256+
257+ # Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d))
258+ if isinstance(col_obj, Tr):
259+ for i1, index1 in enumerate(self):
260+ for i2, index2 in enumerate(col_obj):
261+ if index1 == index2:
262+ a = self[:i1]
263+ b = self[i1 + 1:]
264+ c = col_obj[:i2]
265+ d = col_obj[i2 + 1:]
266+ col_str1 = ColorString([Tr(*(a + d + c + b))])
267+ col_str2 = ColorString([Tr(*(a + b)), Tr(*(c + d))])
268+ col_str1.coeff = fractions.Fraction(1, 2)
269+ col_str2.coeff = fractions.Fraction(-1, 2)
270+ col_str2.Nc_power = -1
271+ return ColorFactor([col_str1, col_str2])
272+
273+ # 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))
274+ if isinstance(col_obj, T):
275+ for i1, index1 in enumerate(self):
276+ for i2, index2 in enumerate(col_obj[:-2]):
277+ if index1 == index2:
278+ a = self[:i1]
279+ b = self[i1 + 1:]
280+ c = col_obj[:i2]
281+ d = col_obj[i2 + 1:-2]
282+ ij = col_obj[-2:]
283+ col_str1 = ColorString([T(*(c + b + a + d + ij))])
284+ col_str2 = ColorString([Tr(*(a + b)), T(*(c + d) + ij)])
285+ col_str1.coeff = fractions.Fraction(1, 2)
286+ col_str2.coeff = fractions.Fraction(-1, 2)
287+ col_str2.Nc_power = -1
288+ return ColorFactor([col_str1, col_str2])
289+
290+ return None
291+
292+#===============================================================================
293+# T
294+#===============================================================================
295+class T(ColorObject):
296+ """The T color object. Last two indices have a special meaning"""
297+
298+ def __init__(self, *args):
299+ """Check for at least two indices"""
300+
301+ super(T, self).__init__()
302+ if len(args) < 2:
303+ raise ValueError, \
304+ "T objects must have at least two indices!"
305+
306+ def simplify(self):
307+ """Implement T(a,b,c,...,i,i) = Tr(a,b,c,...) and
308+ 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))"""
309+
310+ # T(a,b,c,...,i,i) = Tr(a,b,c,...)
311+ if self[-2] == self[-1]:
312+ return ColorFactor([ColorString([Tr(*self[:-2])])])
313+
314+ # 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))
315+ for i1, index1 in enumerate(self[:-2]):
316+ for i2, index2 in enumerate(self[i1 + 1:-2]):
317+ if index1 == index2:
318+ a = self[:i1]
319+ b = self[i1 + 1:i1 + i2 + 1]
320+ c = self[i1 + i2 + 2:-2]
321+ ij = self[-2:]
322+ col_str1 = ColorString([T(*(a + c + ij)), Tr(*b)])
323+ col_str2 = ColorString([T(*(a + b + c + ij))])
324+ col_str1.coeff = fractions.Fraction(1, 2)
325+ col_str2.coeff = fractions.Fraction(-1, 2)
326+ col_str2.Nc_power = -1
327+ return ColorFactor([col_str1, col_str2])
328+
329+ return None
330+
331+ def pair_simplify(self, col_obj, simplify_T_product=False):
332+ """Implement T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k)
333+ 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)
334+ -1/Nc T(a,b,i,j)T(c,d,k,l))
335+ but only if the simplify_T_product tag is True."""
336+
337+ if isinstance(col_obj, T):
338+ ij1 = self[-2:]
339+ ij2 = col_obj[-2:]
340+
341+ # T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k)
342+ if ij1[1] == ij2[0]:
343+ return ColorFactor([ColorString([T(*(self[:-2] + \
344+ col_obj[:-2] + \
345+ array.array('i', [ij1[0],
346+ ij2[1]])))])])
347+ # T(a,x,b,i,j)T(c,x,d,k,l) = 1/2(T(a,d,i,l)T(c,b,k,j)
348+ # -1/Nc T(a,b,i,j)T(c,d,k,l))
349+ if simplify_T_product:
350+ for i1, index1 in enumerate(self[:-2]):
351+ for i2, index2 in enumerate(col_obj[:-2]):
352+ if index1 == index2:
353+ a = self[:i1]
354+ b = self[i1 + 1:-2]
355+ c = col_obj[:i2]
356+ d = col_obj[i2 + 1:-2]
357+ col_str1 = ColorString([T(*(a + d + \
358+ array.array('i',
359+ [ij1[0], ij2[1]]))),
360+ T(*(c + b + \
361+ array.array('i',
362+ [ij2[0], ij1[1]])))])
363+ col_str2 = ColorString([T(*(a + b + \
364+ array.array('i',
365+ [ij1[0], ij1[1]]))),
366+ T(*(c + d + \
367+ array.array('i',
368+ [ij2[0], ij2[1]])))])
369+ col_str1.coeff = fractions.Fraction(1, 2)
370+ col_str2.coeff = fractions.Fraction(-1, 2)
371+ col_str2.Nc_power = -1
372+ return ColorFactor([col_str1, col_str2])
373+
374+ def complex_conjugate(self):
375+ """Complex conjugation. Overwritten here because the two last indices
376+ should be treated differently"""
377+
378+ # T(a,b,c,i,j)* = T(c,b,a,j,i)
379+ l1 = self[:-2]
380+ l1.reverse()
381+ l2 = self[-2:]
382+ l2.reverse()
383+ self[:] = l1 + l2
384+
385+#===============================================================================
386+# f
387+#===============================================================================
388+class f(ColorObject):
389+ """The f color object"""
390+
391+ def __init__(self, *args):
392+ """Ensure f and d objects have strictly 3 indices"""
393+
394+ super(f, self).__init__()
395+ if len(args) != 3:
396+ raise ValueError, \
397+ "f and d objects must have three indices!"
398+
399+ def simplify(self):
400+ """Implement only the replacement rule
401+ f(a,b,c)=-2ITr(a,b,c)+2ITr(c,b,a)"""
402+
403+ indices = self[:]
404+ col_str1 = ColorString([Tr(*indices)])
405+ indices.reverse()
406+ col_str2 = ColorString([Tr(*indices)])
407+
408+ col_str1.coeff = fractions.Fraction(-2, 1)
409+ col_str2.coeff = fractions.Fraction(2, 1)
410+
411+ col_str1.is_imaginary = True
412+ col_str2.is_imaginary = True
413+
414+ return ColorFactor([col_str1, col_str2])
415+
416+#===============================================================================
417+# d
418+#===============================================================================
419+class d(f):
420+ """The d color object"""
421+
422+ def simplify(self):
423+ """Implement only the replacement rule
424+ d(a,b,c)=2Tr(a,b,c)+2Tr(c,b,a)"""
425+
426+ indices = self[:]
427+ col_str1 = ColorString([Tr(*indices)])
428+ indices.reverse()
429+ col_str2 = ColorString([Tr(*indices)])
430+
431+ col_str1.coeff = fractions.Fraction(2, 1)
432+ col_str2.coeff = fractions.Fraction(2, 1)
433+
434+ return ColorFactor([col_str1, col_str2])
435+
436+#===============================================================================
437+# ColorString
438+#===============================================================================
439+class ColorString(list):
440+ """A list of ColorObjects with an implicit multiplication between,
441+ together with a Fraction coefficient and a tag
442+ to indicate if the coefficient is real or imaginary. ColorStrings can be
443+ simplified, by simplifying their elements."""
444+
445+ coeff = fractions.Fraction(1, 1)
446+ is_imaginary = False
447+ Nc_power = 0
448+
449+ def __init__(self, init_list=[],
450+ coeff=fractions.Fraction(1, 1),
451+ is_imaginary=False, Nc_power=0):
452+ """Overrides norm list constructor to implement easy modification
453+ of coeff, is_imaginary and Nc_power"""
454+
455+ if init_list:
456+ self.extend(init_list)
457+ self.coeff = coeff
458+ self.is_imaginary = is_imaginary
459+ self.Nc_power = Nc_power
460+
461+ def __str__(self):
462+ """Returns a standard string representation based on color object
463+ representations"""
464+
465+ coeff_str = str(self.coeff)
466+ if self.is_imaginary:
467+ coeff_str += ' I'
468+ if self.Nc_power > 0:
469+ coeff_str += ' Nc^%i' % self.Nc_power
470+ elif self.Nc_power < 0:
471+ coeff_str += ' 1/Nc^%i' % abs(self.Nc_power)
472+ return '%s %s' % (coeff_str,
473+ ' '.join([str(col_obj) for col_obj in self]))
474+
475+ __repr__ = __str__
476+
477+ def product(self, other):
478+ """Multiply self with other."""
479+
480+ self.coeff = self.coeff * other.coeff
481+
482+ self.Nc_power = self.Nc_power + other.Nc_power
483+
484+ # Complex algebra
485+ if self.is_imaginary and other.is_imaginary:
486+ self.is_imaginary = False
487+ self.coeff = -self.coeff
488+ elif self.is_imaginary or other.is_imaginary:
489+ self.is_imaginary = True
490+
491+ self.extend(other)
492+
493+ def simplify(self):
494+ """Simplify the current ColorString by applying simplify rules on
495+ each element and building a new ColorFactor to return if necessary"""
496+
497+ # First, try sto simplify element by element
498+ for i1, col_obj1 in enumerate(self):
499+ res = col_obj1.simplify()
500+ # If a simplification possibility is found...
501+ if res:
502+ # Create a color factor to store the answer...
503+ res_col_factor = ColorFactor()
504+ # Obtained my multiplying the initial string minus the color
505+ # object to simplify with all color strings in the result
506+ for second_col_str in res:
507+ first_col_str = copy.copy(self)
508+ del first_col_str[i1]
509+ first_col_str.product(second_col_str)
510+ # This sort is necessary to ensure ordering of ColorObjects
511+ # remains the same for comparison
512+ first_col_str.sort()
513+ res_col_factor.append(first_col_str)
514+ return res_col_factor
515+
516+ # Second, try to simplify pairs
517+ for i1, col_obj1 in enumerate(self):
518+
519+ for i2, col_obj2 in enumerate(self[i1 + 1:]):
520+ res = col_obj1.pair_simplify(col_obj2)
521+ # Try both pairing
522+ if not res:
523+ res = col_obj2.pair_simplify(col_obj1)
524+ if res:
525+ res_col_factor = ColorFactor()
526+ for second_col_str in res:
527+ first_col_str = copy.copy(self)
528+ del first_col_str[i1]
529+ del first_col_str[i1 + i2]
530+ first_col_str.product(second_col_str)
531+ first_col_str.sort()
532+ res_col_factor.append(first_col_str)
533+ return res_col_factor
534+
535+ return None
536+
537+ def add(self, other):
538+ """Add string other to current string. ONLY USE WITH SIMILAR STRINGS!"""
539+
540+ self.coeff = self.coeff + other.coeff
541+
542+ def complex_conjugate(self):
543+ """Returns the complex conjugate of the current color string"""
544+
545+ compl_conj_str = copy.copy(self)
546+ for col_obj in compl_conj_str:
547+ col_obj.complex_conjugate()
548+ if compl_conj_str.is_imaginary:
549+ compl_conj_str.coeff = -compl_conj_str.coeff
550+
551+ return compl_conj_str
552+
553+ def to_immutable(self):
554+ """Returns an immutable object summarizing the color structure of the
555+ current color string. Format is ((name1,indices1),...) where name is the
556+ class name of the color object and indices a tuple corresponding to its
557+ indices. An immutable object, in Python, is built on tuples, strings and
558+ numbers, i.e. objects which cannot be modified. Their crucial property
559+ is that they can be used as dictionary keys!"""
560+
561+ ret_list = [(col_obj.__class__.__name__, tuple(col_obj)) \
562+ for col_obj in self]
563+ ret_list.sort()
564+ return tuple(ret_list)
565+
566+ def from_immutable(self, immutable_rep):
567+ """Fill the current object with Color Objects created using an immutable
568+ representation."""
569+
570+ del self[:]
571+
572+ for col_tuple in immutable_rep:
573+ self.append(globals()[col_tuple[0]](*col_tuple[1]))
574+
575+ def replace_indices(self, repl_dict):
576+ """Replace current indices following the rules listed in the replacement
577+ dictionary written as {old_index:new_index,...}, does that for ALL
578+ color objects."""
579+
580+ map(lambda col_obj: col_obj.replace_indices(repl_dict), self)
581+
582+ def create_copy(self):
583+ """Returns a real copy of self, non trivial because bug in
584+ copy.deepcopy"""
585+
586+ res = ColorString()
587+ for col_obj in self:
588+ res.append(col_obj.create_copy())
589+ res.coeff = self.coeff
590+ res.is_imaginary = self.is_imaginary
591+ res.Nc_power = self.Nc_power
592+
593+ return res
594+
595+ __copy__ = create_copy
596+
597+ def set_Nc(self, Nc=3):
598+ """Returns a tuple, with the first entry being the string coefficient
599+ with Nc replaced (by default by 3), and the second one being True
600+ or False if the coefficient is imaginary or not. Raise an error if there
601+ are still non trivial color objects."""
602+
603+ if self:
604+ raise ValueError, \
605+ "String %s cannot be simplified to a number!" % str(self)
606+
607+ if self.Nc_power >= 0:
608+ return (self.coeff * fractions.Fraction(\
609+ int(Nc ** self.Nc_power), 1),
610+ self.is_imaginary)
611+ else:
612+ return (self.coeff * fractions.Fraction(\
613+ 1, int(Nc ** abs(self.Nc_power))),
614+ self.is_imaginary)
615+
616+ def to_canonical(self, immutable=None):
617+ """Returns the canonical representation of the immutable representation
618+ (i.e., first index is 1, ...). This allow for an easy comparison of
619+ two color strings, i.e. independently of the actual index names (only
620+ relative positions matter). Also returns the conversion dictionary.
621+ If no immutable representation is given, use the one build from self."""
622+
623+ if not immutable:
624+ immutable = self.to_immutable()
625+
626+ replaced_indices = {}
627+ curr_ind = 1
628+ return_list = []
629+
630+ for elem in immutable:
631+ can_elem = [elem[0], []]
632+ for index in elem[1]:
633+ try:
634+ new_index = replaced_indices[index]
635+ except KeyError:
636+ new_index = curr_ind
637+ curr_ind += 1
638+ replaced_indices[index] = new_index
639+ can_elem[1].append(new_index)
640+ return_list.append((can_elem[0], tuple(can_elem[1])))
641+
642+ return_list.sort()
643+
644+ return (tuple(return_list), replaced_indices)
645+#===============================================================================
646+# ColorFactor
647+#===============================================================================
648+class ColorFactor(list):
649+ """ColorFactor objects are list of ColorString with an implicit summation.
650+ They can be simplified by simplifying all their elements."""
651+
652+ def __str__(self):
653+ """Returns a nice string for print"""
654+
655+ return '+'.join(['(%s)' % str(col_str) for col_str in self])
656+
657+ def append_str(self, new_str):
658+ """Special append taking care of adding new string to strings already
659+ existing with the same structure."""
660+
661+ for col_str in self:
662+ # Check if strings are similar, this IS the optimal way of doing
663+ # it. Note that first line only compare the lists, not the
664+ # properties associated
665+ if col_str == new_str and \
666+ col_str.Nc_power == new_str.Nc_power and \
667+ col_str.is_imaginary == new_str.is_imaginary:
668+ # Add them
669+ col_str.add(new_str)
670+ return True
671+
672+ # If no correspondence is found, append anyway
673+ self.append(new_str)
674+ return False
675+
676+ def extend_str(self, new_col_fact):
677+ """Special extend taking care of adding new strings to strings already
678+ existing with the same structure."""
679+
680+ for col_str in new_col_fact:
681+ self.append_str(col_str)
682+
683+ def simplify(self):
684+ """Returns a new color factor where each color string has been
685+ simplified once and similar strings have been added."""
686+
687+ new_col_factor = ColorFactor()
688+ # Simplify
689+ for col_str in self:
690+ res = col_str.simplify()
691+ if res:
692+ new_col_factor.extend_str(res)
693+ else:
694+ new_col_factor.append_str(col_str)
695+
696+ # Only returns non zero elements
697+ return ColorFactor([col_str for col_str in \
698+ new_col_factor if col_str.coeff != 0])
699+
700+ def full_simplify(self):
701+ """Simplify the current color factor until the result is stable"""
702+
703+ result = copy.copy(self)
704+ while(True):
705+ ref = copy.copy(result)
706+ result = result.simplify()
707+ if result == ref:
708+ return result
709+
710+ def set_Nc(self, Nc=3):
711+ """Returns a tuple containing real and imaginary parts of the current
712+ color factor, when Nc is replaced (3 by default)."""
713+
714+ return (sum([cs.set_Nc(Nc)[0] for cs in self if not cs.is_imaginary]),
715+ sum([cs.set_Nc(Nc)[0] for cs in self if cs.is_imaginary]))
716+
717+
718+ def replace_indices(self, repl_dict):
719+ """Replace current indices following the rules listed in the replacement
720+ dictionary written as {old_index:new_index,...}, does that for ALL
721+ color strings."""
722+
723+ map(lambda col_str:col_str.replace_indices(repl_dict), self)
724+
725+ def create_copy(self):
726+ """Returns a real copy of self, non trivial because bug in
727+ copy.deepcopy"""
728+
729+ res = ColorFactor()
730+ for col_str in self:
731+ res.append(col_str.create_copy())
732+
733+ return res
734+
735+ __copy__ = create_copy
736+
737+
738
739=== added file 'madgraph/core/color_amp.py'
740--- madgraph/core/color_amp.py 1970-01-01 00:00:00 +0000
741+++ madgraph/core/color_amp.py 2010-01-21 09:19:15 +0000
742@@ -0,0 +1,439 @@
743+################################################################################
744+#
745+# Copyright (c) 2009 The MadGraph Development team and Contributors
746+#
747+# This file is a part of the MadGraph 5 project, an application which
748+# automatically generates Feynman diagrams and matrix elements for arbitrary
749+# high-energy processes in the Standard Model and beyond.
750+#
751+# It is subject to the MadGraph license which should accompany this
752+# distribution.
753+#
754+# For more information, please visit: http://madgraph.phys.ucl.ac.be
755+#
756+################################################################################
757+
758+import copy
759+import operator
760+import re
761+
762+import madgraph.core.color_algebra as color_algebra
763+
764+"""Classes, methods and functions required to write QCD color information
765+for a diagram and build a color basis, and to square a QCD color string for
766+squared diagrams and interference terms."""
767+
768+#===============================================================================
769+# ColorBasis
770+#===============================================================================
771+class ColorBasis(dict):
772+ """The ColorBasis object is a dictionary created from an amplitude. Keys
773+ are the different color structures present in the amplitude. Values have
774+ the format (diag,(index c1, index c2,...), coeff, is_imaginary, Nc_power)
775+ where diag is the diagram index, (index c1, index c2,...) the list of
776+ indices corresponding to the chose color parts for each vertex in the
777+ diagram, coeff the corresponding coefficient (a fraction), is_imaginary
778+ if this contribution is real or complex, and Nc_power the Nc power."""
779+
780+ # Dictionary to save simplifications already done in a canonical form
781+ _canonical_dict = {}
782+
783+ def colorize(self, diagram, model):
784+ """Takes a diagram and a model and outputs a dictionary with keys being
785+ color coefficient index tuples and values a color string (before
786+ simplification)."""
787+
788+ # The smallest value used to create new summed indices
789+ min_index = -1000
790+ # The dictionary to be output
791+ res_dict = {}
792+ # The dictionary for book keeping of replaced indices
793+ repl_dict = {}
794+
795+ for vertex in diagram.get('vertices'):
796+
797+ # SPECIAL VERTEX WITH ID = 0 -------------------------------------------
798+
799+ if vertex['id'] == 0:
800+ self.add_vertex_id_0(vertex, repl_dict, res_dict)
801+ # Return since this must be the last vertex
802+ return res_dict
803+
804+ # NORMAL VERTICES WITH ID != 0 -----------------------------------------
805+ min_index, res_dict = self.add_vertex(vertex, diagram, model,
806+ repl_dict, res_dict, min_index)
807+
808+ return res_dict
809+
810+ def add_vertex_id_0(self, vertex, repl_dict, res_dict):
811+ """Update the repl_dict and res_dict when vertex has id=0, i.e. for
812+ the special case of an identity vertex."""
813+
814+ # For vertex (i1,i2), replace all i2 by i1
815+ old_num = vertex.get('legs')[1].get('number')
816+ new_num = vertex.get('legs')[0].get('number')
817+ # Be careful i1 or i2 might have been replaced themselves
818+ try:
819+ old_num = repl_dict[old_num]
820+ except KeyError:
821+ pass
822+ try:
823+ new_num = repl_dict[new_num]
824+ except KeyError:
825+ pass
826+ # Do the replacement
827+ for (ind_chain, col_str_chain) in res_dict.items():
828+ col_str_chain.replace_indices({old_num:new_num})
829+
830+ def add_vertex(self, vertex, diagram, model,
831+ repl_dict, res_dict, min_index):
832+ """Update repl_dict, res_dict and min_index for normal vertices.
833+ Returns the min_index reached and the result dictionary in a tuple."""
834+
835+ # Create a list of pdg codes entering the vertex ordered as in
836+ # interactions.py
837+ list_pdg = [part.get_pdg_code() for part in \
838+ model.get_interaction(vertex.get('id')).get('particles')]
839+
840+ # Create a dictionary pdg code --> leg(s)
841+ dict_pdg_leg = {}
842+ for index, leg in enumerate(vertex.get('legs')):
843+ curr_num = leg.get('number')
844+ curr_pdg = leg.get('id')
845+ # If this is the last leg and not the last vertex,
846+ # flip part/antipart, and replace last index by a new
847+ # summed index
848+ if index == len(vertex.get('legs')) - 1 and \
849+ vertex != diagram.get('vertices')[-1]:
850+ part = model.get('particle_dict')[curr_pdg]
851+ curr_pdg = \
852+ model.get('particle_dict')[curr_pdg].get_anti_pdg_code()
853+ repl_dict[curr_num] = min_index
854+ min_index = min_index - 1
855+ try:
856+ curr_num = repl_dict[curr_num]
857+ except KeyError:
858+ pass
859+ try:
860+ dict_pdg_leg[curr_pdg].append(curr_num)
861+ except KeyError:
862+ dict_pdg_leg[curr_pdg] = [curr_num]
863+
864+ # Create a list of associated leg number following the same order
865+ list_numbers = []
866+ for pdg_code in list_pdg:
867+ list_numbers.append(dict_pdg_leg[pdg_code].pop())
868+ # ... and the associated dictionary for replacement
869+ match_dict = dict(enumerate(list_numbers))
870+
871+ # Update the result dict using the current vertex ColorString object
872+ # If more than one, create different entries
873+ new_res_dict = {}
874+ for i, col_str in \
875+ enumerate(model.get_interaction(vertex['id'])['color']):
876+ # Build the new element
877+ mod_col_str = col_str.create_copy()
878+
879+ # Replace summed (negative) internal indices
880+ list_neg = []
881+ for col_obj in mod_col_str:
882+ list_neg.extend([ind for ind in col_obj if ind < 0])
883+ internal_indices_dict = {}
884+ # This notation is to remove duplicates
885+ for index in list(set(list_neg)):
886+ internal_indices_dict[index] = min_index
887+ min_index = min_index - 1
888+ mod_col_str.replace_indices(internal_indices_dict)
889+
890+ # Replace other (positive) indices using the match_dic
891+ mod_col_str.replace_indices(match_dict)
892+ # If we are considering the first vertex, simply create
893+ # new entries
894+ if not res_dict:
895+ new_res_dict[tuple([i])] = mod_col_str
896+ #... otherwise, loop over existing elements and multiply
897+ # the color strings
898+ else:
899+ for ind_chain, col_str_chain in res_dict.items():
900+ new_col_str_chain = col_str_chain.create_copy()
901+ new_col_str_chain.product(mod_col_str)
902+ new_res_dict[tuple(list(ind_chain) + [i])] = \
903+ new_col_str_chain
904+
905+ return (min_index, new_res_dict)
906+
907+
908+ def update_color_basis(self, colorize_dict, index):
909+ """Update the current color basis by adding information from
910+ the colorize dictionary (produced by the colorize routine)
911+ associated to diagram with index index. Keep track of simplification
912+ results for maximal optimization."""
913+
914+ # loop over possible color chains
915+ for col_chain, col_str in colorize_dict.items():
916+
917+ # Create a canonical immutable representation of the the string
918+ canonical_rep, rep_dict = col_str.to_canonical()
919+ try:
920+ # If this representation has already been considered,
921+ # recycle the result.
922+ col_fact = copy.copy(self._canonical_dict[canonical_rep])
923+
924+ except KeyError:
925+ # If the representation is really new
926+
927+ # Create and simplify a color factor for the considered chain
928+ col_fact = color_algebra.ColorFactor([col_str])
929+ col_fact = col_fact.full_simplify()
930+
931+ # Save the result for further use
932+ canonical_col_fact = copy.copy(col_fact)
933+ canonical_col_fact.replace_indices(rep_dict)
934+ self._canonical_dict[canonical_rep] = canonical_col_fact
935+
936+ else:
937+ # If this representation has already been considered,
938+ # adapt the result
939+ # Note that we have to replace back
940+ # the indices to match the initial convention.
941+ col_fact.replace_indices(self._invert_dict(rep_dict))
942+ # Must simplify once to put traces in a canonical ordering
943+ col_fact = col_fact.simplify()
944+
945+ # loop over color strings in the resulting color factor
946+ for col_str in col_fact:
947+ immutable_col_str = col_str.to_immutable()
948+ # if the color structure is already present in the present basis
949+ # update it
950+ basis_entry = (index,
951+ col_chain,
952+ col_str.coeff,
953+ col_str.is_imaginary,
954+ col_str.Nc_power)
955+ try:
956+ self[immutable_col_str].append(basis_entry)
957+ except KeyError:
958+ self[immutable_col_str] = [basis_entry]
959+
960+ def build(self, amplitude, model):
961+ """Build the a color basis object using information contained in
962+ amplitude and model"""
963+
964+ for index, diagram in enumerate(amplitude['diagrams']):
965+ colorize_dict = self.colorize(diagram, model)
966+ self.update_color_basis(colorize_dict, index)
967+
968+ def __init__(self, *args):
969+ """Initialize a new color basis object, either empty or filled (0
970+ or 2 arguments). If two arguments are given, the first one is
971+ interpreted as the amplitude and the second one as the model."""
972+
973+ if len(args) not in (0, 2):
974+ raise ValueError, \
975+ "Object ColorBasis must be initialized with 0 or 2 arguments"
976+
977+ if len(args) == 2:
978+ self.build(*args)
979+
980+ def __str__(self):
981+ """Returns a nicely formatted string for display"""
982+
983+ my_str = ""
984+ for k, v in self.items():
985+ for name, indices in k:
986+ my_str = my_str + name + str(indices)
987+ my_str = my_str + ': '
988+ for contrib in v:
989+ imag_str = ''
990+ if contrib[3]:
991+ imag_str = 'I'
992+ my_str = my_str + '(diag:%i, chain:%s, coeff:%s%s, Nc:%i) ' % \
993+ (contrib[0], contrib[1], contrib[2],
994+ imag_str, contrib[4])
995+ my_str = my_str + '\n'
996+ return my_str
997+
998+ def _invert_dict(self, mydict):
999+ """Helper method to invert dictionary dict"""
1000+
1001+ return dict([v, k] for k, v in mydict.items())
1002+
1003+#===============================================================================
1004+# ColorMatrix
1005+#===============================================================================
1006+class ColorMatrix(dict):
1007+ """A color matrix, i.e. a dictionary with pairs (i,j) as keys where i
1008+ and j refer to elements of color basis objects. Values are Color Factor
1009+ objects. Also contains two additional dictonaries, one with the fixed Nc
1010+ representation of the matrix, and the other one with the "inverted" matrix,
1011+ i.e. a dictionary where keys are values of the color matrix."""
1012+
1013+ _col_basis1 = None
1014+ _col_basis2 = None
1015+ col_matrix_fixed_Nc = {}
1016+ inverted_col_matrix = {}
1017+
1018+ def __init__(self, col_basis, col_basis2=None,
1019+ Nc=3, Nc_power_min=None, Nc_power_max=None):
1020+ """Initialize a color matrix with one or two color basis objects. If
1021+ only one color basis is given, the other one is assumed to be equal.
1022+ As options, any value of Nc and minimal/maximal power of Nc can also be
1023+ provided. Note that the min/max power constraint is applied
1024+ only at the end, so that it does NOT speed up the calculation."""
1025+
1026+ self._col_basis1 = col_basis
1027+ if col_basis2:
1028+ self._col_basis2 = col_basis2
1029+ self.build_matrix(Nc, Nc_power_min, Nc_power_max)
1030+ else:
1031+ self._col_basis2 = col_basis
1032+ # If the two color basis are equal, assumes the color matrix is
1033+ # symmetric
1034+ self.build_matrix(Nc, Nc_power_min, Nc_power_max, is_symmetric=True)
1035+
1036+ def build_matrix(self, Nc=3,
1037+ Nc_power_min=None,
1038+ Nc_power_max=None,
1039+ is_symmetric=False):
1040+ """Create the matrix using internal color basis objects. Use the stored
1041+ color basis objects and takes Nc and Nc_min/max parameters as __init__.
1042+ If is_isymmetric is True, build only half of the matrix which is assumed
1043+ to be symmetric."""
1044+
1045+ canonical_dict = {}
1046+
1047+ for i1, struct1 in \
1048+ enumerate(self._col_basis1.keys()):
1049+ for i2, struct2 in \
1050+ enumerate(self._col_basis2.keys()):
1051+
1052+ # Only scan upper right triangle if symmetric
1053+ if is_symmetric and i2 < i1:
1054+ continue
1055+
1056+ # Fix indices in struct2 knowing summed indices in struct1
1057+ # to avoid duplicates
1058+ new_struct2 = self.fix_summed_indices(struct1, struct2)
1059+
1060+ # Build a canonical representation of the two immutable struct
1061+ canonical_entry, dummy = \
1062+ color_algebra.ColorString().to_canonical(struct1 + \
1063+ new_struct2)
1064+
1065+ try:
1066+ # If this has already been calculated, use the result
1067+ result, result_fixed_Nc = canonical_dict[canonical_entry]
1068+
1069+ except KeyError:
1070+ # Otherwise calculate the result
1071+ result, result_fixed_Nc = \
1072+ self.create_new_entry(struct1,
1073+ new_struct2,
1074+ Nc_power_min,
1075+ Nc_power_max,
1076+ Nc)
1077+ # Store both results
1078+ canonical_dict[canonical_entry] = (result, result_fixed_Nc)
1079+
1080+ # Store the full result...
1081+ self[(i1, i2)] = result
1082+ if is_symmetric:
1083+ self[(i2, i1)] = result
1084+
1085+ # the fixed Nc one ...
1086+ self.col_matrix_fixed_Nc[(i1, i2)] = result_fixed_Nc
1087+ if is_symmetric:
1088+ self.col_matrix_fixed_Nc[(i2, i1)] = result_fixed_Nc
1089+ # and update the inverted dict
1090+ if result_fixed_Nc in self.inverted_col_matrix.keys():
1091+ self.inverted_col_matrix[result_fixed_Nc].append((i1,
1092+ i2))
1093+ if is_symmetric:
1094+ self.inverted_col_matrix[result_fixed_Nc].append((i2,
1095+ i1))
1096+ else:
1097+ self.inverted_col_matrix[result_fixed_Nc] = [(i1, i2)]
1098+ if is_symmetric:
1099+ self.inverted_col_matrix[result_fixed_Nc] = [(i2, i1)]
1100+
1101+ def create_new_entry(self, struct1, struct2,
1102+ Nc_power_min, Nc_power_max, Nc):
1103+ """ Create a new product result, and result with fixed Nc for two color
1104+ basis entries. Implement Nc power limits."""
1105+
1106+ # Create color string objects corresponding to color basis
1107+ # keys
1108+ col_str = color_algebra.ColorString()
1109+ col_str.from_immutable(struct1)
1110+
1111+ col_str2 = color_algebra.ColorString()
1112+ col_str2.from_immutable(struct2)
1113+
1114+ # Complex conjugate the second one and multiply the two
1115+ col_str.product(col_str2.complex_conjugate())
1116+
1117+ # Create a color factor to store the result and simplify it
1118+ # taking into account the limit on Nc
1119+ col_fact = color_algebra.ColorFactor([col_str])
1120+ result = col_fact.full_simplify()
1121+
1122+ # Keep only terms with Nc_max >= Nc power >= Nc_min
1123+ if Nc_power_min is not None:
1124+ result[:] = [col_str for col_str in result \
1125+ if col_str.Nc_power >= Nc_power_min]
1126+ if Nc_power_max is not None:
1127+ result[:] = [col_str for col_str in result \
1128+ if col_str.Nc_power <= Nc_power_max]
1129+
1130+ # Calculate the fixed Nc representation
1131+ result_fixed_Nc = result.set_Nc(Nc)
1132+
1133+ return result, result_fixed_Nc
1134+
1135+ def __str__(self):
1136+ """Returns a nicely formatted string with the fixed Nc representation
1137+ of the current matrix (only the real part)"""
1138+
1139+ mystr = '\n\t' + '\t'.join([str(i) for i in \
1140+ range(len(self._col_basis2))])
1141+
1142+ for i1 in range(len(self._col_basis1)):
1143+ mystr = mystr + '\n' + str(i1) + '\t'
1144+ mystr = mystr + '\t'.join(['%i/%i' % \
1145+ (self.col_matrix_fixed_Nc[(i1, i2)][0].numerator,
1146+ self.col_matrix_fixed_Nc[(i1, i2)][0].denominator) \
1147+ for i2 in range(len(self._col_basis2))])
1148+
1149+ return mystr
1150+
1151+ @classmethod
1152+ def fix_summed_indices(self, struct1, struct2):
1153+ """Returns a copy of the immutable Color String representation struct2
1154+ where summed indices are modified to avoid duplicates with those
1155+ appearing in struct1. Assumes internal summed indices are negative."""
1156+
1157+ # First, determines what is the smallest index appearing in struct1
1158+ min_index = min(reduce(operator.add,
1159+ [list(elem[1]) for elem in struct1])) - 1
1160+ # Second, determines the summed indices in struct2 and create a
1161+ # replacement dictionary
1162+ repl_dict = {}
1163+ list2 = reduce(operator.add,
1164+ [list(elem[1]) for elem in struct1])
1165+ for summed_index in list(set([i for i in list2 \
1166+ if list2.count(i) == 2])):
1167+ repl_dict[summed_index] = min_index
1168+ min_index -= 1
1169+
1170+ # Three, create a new immutable struct by doing replacements in struct2
1171+ return_list = []
1172+ for elem in struct2:
1173+ fix_elem = [elem[0], []]
1174+ for index in elem[1]:
1175+ try:
1176+ fix_elem[1].append(repl_dict[index])
1177+ except:
1178+ fix_elem[1].append(index)
1179+ return_list.append((elem[0], tuple(fix_elem[1])))
1180+
1181+ return tuple(return_list)
1182
1183=== modified file 'madgraph/interface/cmd_interface.py'
1184--- madgraph/interface/cmd_interface.py 2010-01-04 06:44:15 +0000
1185+++ madgraph/interface/cmd_interface.py 2010-01-21 09:19:15 +0000
1186@@ -28,8 +28,13 @@
1187
1188 import madgraph.iolibs.misc as misc
1189 import madgraph.iolibs.files as files
1190+<<<<<<< TREE
1191 import madgraph.iolibs.import_v4 as import_v4
1192 import madgraph.iolibs.export_v4 as export_v4
1193+=======
1194+import madgraph.iolibs.import_model_v4 as import_v4
1195+import madgraph.iolibs.save_model as save_model
1196+>>>>>>> MERGE-SOURCE
1197
1198 import madgraph.core.base_objects as base_objects
1199 import madgraph.core.diagram_generation as diagram_generation
1200@@ -204,6 +209,37 @@
1201 base_dir=\
1202 self.split_arg(line[0:begidx])[2])
1203
1204+ def do_save(self, line):
1205+ """Save information to file"""
1206+
1207+ args = self.split_arg(line)
1208+ if len(args) != 2:
1209+ self.help_save()
1210+ return False
1211+
1212+ if args[0] == 'model':
1213+ if self.__curr_model:
1214+ save_model.save_model(args[1], self.__curr_model)
1215+ else:
1216+ print 'No model to save!'
1217+
1218+ def complete_save(self, text, line, begidx, endidx):
1219+ "Complete the save command"
1220+
1221+ # Format
1222+ if len(self.split_arg(line[0:begidx])) == 1:
1223+ return self.list_completion(text, ['model'])
1224+
1225+ # Filename if directory is not given
1226+ if len(self.split_arg(line[0:begidx])) == 2:
1227+ return self.path_completion(text)
1228+
1229+ # Filename if directory is given
1230+ if len(self.split_arg(line[0:begidx])) == 3:
1231+ return self.path_completion(text,
1232+ base_dir=\
1233+ self.split_arg(line[0:begidx])[2])
1234+
1235 # Display
1236 def do_display(self, line):
1237 """Display current internal status"""
1238@@ -535,6 +571,11 @@
1239 sys.exit(1)
1240
1241 # In-line help
1242+ def help_save(self):
1243+ print "syntax: save model|... PATH"
1244+ print "-- save information as files in PATH"
1245+
1246+ # In-line help
1247 def help_import(self):
1248 print "syntax: import " + "|".join(self.__import_formats) + \
1249 " FILENAME"
1250
1251=== modified file 'madgraph/iolibs/files.py'
1252--- madgraph/iolibs/files.py 2009-12-15 06:14:57 +0000
1253+++ madgraph/iolibs/files.py 2010-01-21 09:19:15 +0000
1254@@ -37,6 +37,7 @@
1255 return None
1256
1257 return ret_value
1258+<<<<<<< TREE
1259
1260 #===============================================================================
1261 # write_to_file
1262@@ -58,3 +59,26 @@
1263 return None
1264
1265 return ret_value
1266+=======
1267+
1268+#===============================================================================
1269+# write_to_file
1270+#===============================================================================
1271+def write_to_file(filename, myfunct, *args):
1272+ """Open a file for writing, apply the function myfunct (with sock as an arg)
1273+ on its content and return the result. Deals properly with errors and
1274+ returns None if something goes wrong.
1275+ """
1276+
1277+ try:
1278+ sock = open(filename, 'w')
1279+ try:
1280+ ret_value = myfunct(sock, *args)
1281+ finally:
1282+ sock.close()
1283+ except IOError, (errno, strerror):
1284+ logging.error("I/O error (%s): %s" % (errno, strerror))
1285+ return None
1286+
1287+ return ret_value
1288+>>>>>>> MERGE-SOURCE
1289
1290=== renamed file 'madgraph/iolibs/import_v4.py' => 'madgraph/iolibs/import_model_v4.py'
1291--- madgraph/iolibs/import_v4.py 2009-12-12 08:42:16 +0000
1292+++ madgraph/iolibs/import_model_v4.py 2010-01-21 09:19:15 +0000
1293@@ -158,6 +158,7 @@
1294 # Give a dummy 'guess' values for color and Lorentz structures
1295 # Those will have to be replaced by a proper guess!
1296
1297+<<<<<<< TREE
1298 myinter.set('color', ['guess'])
1299
1300 # Set the Lorentz structure. Default for 3-particle
1301@@ -180,6 +181,10 @@
1302 myinter.get('lorentz')[0] =\
1303 myinter.get('lorentz')[0]\
1304 + values[3 * len(part_list) - 4].upper()
1305+=======
1306+ myinter.set('color', [['C1']])
1307+ myinter.set('lorentz', ['L1'])
1308+>>>>>>> MERGE-SOURCE
1309
1310 # Use the other strings to fill variable names and tags
1311 if len(part_list) == 3:
1312
1313=== added file 'madgraph/iolibs/save_model.py'
1314--- madgraph/iolibs/save_model.py 1970-01-01 00:00:00 +0000
1315+++ madgraph/iolibs/save_model.py 2010-01-21 09:19:15 +0000
1316@@ -0,0 +1,89 @@
1317+################################################################################
1318+#
1319+# Copyright (c) 2009 The MadGraph Development team and Contributors
1320+#
1321+# This file is a part of the MadGraph 5 project, an application which
1322+# automatically generates Feynman diagrams and matrix elements for arbitrary
1323+# high-energy processes in the Standard Model and beyond.
1324+#
1325+# It is subject to the MadGraph license which should accompany this
1326+# distribution.
1327+#
1328+# For more information, please visit: http://madgraph.phys.ucl.ac.be
1329+#
1330+################################################################################
1331+
1332+"""Function to save model files."""
1333+
1334+import logging
1335+import os
1336+
1337+import madgraph.iolibs.files as files
1338+import madgraph.core.base_objects as base_objects
1339+
1340+def save_particles(fsock, part_list):
1341+ """Save particle objects contained in part_list in the stream fsock"""
1342+
1343+ if not isinstance(part_list, base_objects.ParticleList):
1344+ raise ValueError, \
1345+ "Object %s is not a valid ParticleList" % repr(part_list)
1346+
1347+ fsock.write("particles = [\n")
1348+
1349+ for part in part_list:
1350+ if part_list.index(part) != len(part_list) - 1:
1351+ fsock.write(str(part) + ',')
1352+ else:
1353+ fsock.write(str(part))
1354+
1355+ fsock.write("]")
1356+
1357+def save_interactions(fsock, inter_list):
1358+ """Save interaction objects contained in inter_list in the stream fsock"""
1359+
1360+ if not isinstance(inter_list, base_objects.InteractionList):
1361+ raise ValueError, \
1362+ "Object %s is not a valid InteractionList" % repr(inter_list)
1363+
1364+ fsock.write("interactions = [\n")
1365+
1366+ for inter in inter_list:
1367+ if inter_list.index(inter) != len(inter_list) - 1:
1368+ fsock.write(str(inter) + ',')
1369+ else:
1370+ fsock.write(str(inter))
1371+
1372+ fsock.write("]")
1373+
1374+def save_model(path, model):
1375+ """Save a full model in directory path (try to create if necessary)."""
1376+
1377+ if not isinstance(model, base_objects.Model):
1378+ raise ValueError, \
1379+ "Object %s is not a valid Model" % repr(model)
1380+
1381+ if not isinstance(path, str):
1382+ raise ValueError, \
1383+ "Object %s is not a path string" % repr(path)
1384+
1385+ if not os.path.isdir(path):
1386+ logging.warning("Path %s does not exist, try to make it..." % str(path))
1387+ try:
1388+ os.mkdir(path)
1389+ except IOError, (errno, strerror):
1390+ logging.error("I/O error (%s): %s" % (errno, strerror))
1391+ return None
1392+
1393+ print "Saving particles...",
1394+ files.write_to_file(os.path.join(path, 'particles.py'),
1395+ save_particles,
1396+ model['particles'])
1397+ print "%i particles saved to %s" % (len(model['particles']),
1398+ os.path.join(path, 'particles.py'))
1399+
1400+ print "Saving interactions...",
1401+ files.write_to_file(os.path.join(path, 'interactions.py'),
1402+ save_interactions,
1403+ model['interactions'])
1404+ print "%i interactions saved to %s" % (len(model['interactions']),
1405+ os.path.join(path, 'interactions.py'))
1406
1407=== modified file 'tests/test_manager.py'
1408--- tests/test_manager.py 2009-10-07 11:56:20 +0000
1409+++ tests/test_manager.py 2010-01-21 09:19:15 +0000
1410@@ -37,9 +37,13 @@
1411 import unittest
1412
1413 #Add the ROOT dir to the current PYTHONPATH
1414+
1415+# Only for profiling with -m cProfile!
1416+#root_path = os.path.split(os.path.dirname(os.path.realpath(sys.argv[0])))[0]
1417+#sys.path.append(root_path)
1418+
1419 root_path = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
1420 sys.path.append(root_path)
1421-
1422 #===============================================================================
1423 # run
1424 #===============================================================================
1425
1426=== modified file 'tests/unit_tests/core/test_base_objects.py'
1427--- tests/unit_tests/core/test_base_objects.py 2010-01-01 07:58:00 +0000
1428+++ tests/unit_tests/core/test_base_objects.py 2010-01-21 09:19:15 +0000
1429@@ -19,6 +19,7 @@
1430 import unittest
1431
1432 import madgraph.core.base_objects as base_objects
1433+import madgraph.core.color_algebra as color
1434
1435 #===============================================================================
1436 # ParticleTest
1437@@ -261,7 +262,8 @@
1438
1439 self.mydict = {'id': 1,
1440 'particles': base_objects.ParticleList([self.mypart] * 4),
1441- 'color': ['C1', 'C2'],
1442+ 'color': [color.ColorString([color.f(1, 2, 3)]),
1443+ color.ColorString([color.d(1, 2, 3)])],
1444 'lorentz':['L1', 'L2'],
1445 'couplings':{(0, 0):'g00',
1446 (0, 1):'g01',
1447@@ -329,7 +331,8 @@
1448 base_objects.ParticleList([self.mypart] * 3)],
1449 'wrong_list':[1, 'x ', [self.mypart, 1], [1, 2]]},
1450 {'prop':'color',
1451- 'right_list':[[], ['C1'], ['C1', 'C2']],
1452+ 'right_list':[[], [color.ColorString([color.f(1, 2, 3)]),
1453+ color.ColorString([color.f(1, 2, 3)])]],
1454 'wrong_list':[1, 'a', ['a', 1]]},
1455 {'prop':'lorentz',
1456 'right_list':[[], ['L1'], ['L1', 'L2']],
1457@@ -346,9 +349,7 @@
1458 'wrong_list':[{(0):'g00', (0, 1):'g01',
1459 (1, 0):'g10', (1, 2):'g11'},
1460 {(0, 0):'g00', (0, 1):'g01',
1461- (1, 0):'g10', (1, 2):'g11'},
1462- {(0, 0):'g00', (0, 1):'g01',
1463- (1, 0):'g10'}]}
1464+ (1, 0):'g10', (1, 2):'g11'}]}
1465 ]
1466
1467 mytestinter = self.myinter
1468@@ -364,9 +365,9 @@
1469
1470 goal = "{\n"
1471 goal = goal + " \'id\': %d,\n" % self.myinter['id']
1472- goal = goal + " \'particles\': %s,\n" % \
1473- repr(base_objects.ParticleList([self.mypart] * 4))
1474- goal = goal + " \'color\': [\'C1\', \'C2\'],\n"
1475+ goal = goal + " \'particles\': [%s],\n" % \
1476+ ','.join([str(self.mypart.get_pdg_code())]*4)
1477+ goal = goal + " \'color\': [1 f(1,2,3), 1 d(1,2,3)],\n"
1478 goal = goal + " \'lorentz\': [\'L1\', \'L2\'],\n"
1479 goal = goal + " \'couplings\': %s,\n" % \
1480 repr(self.myinter['couplings'])
1481@@ -539,7 +540,8 @@
1482 [self.mypartlist[0], \
1483 antit, \
1484 self.mypartlist[1]]),
1485- 'color': ['C1'],
1486+ 'color': [color.ColorString([color.f(1, 2, 3),
1487+ color.d(1, 2, 3)])],
1488 'lorentz':['L1'],
1489 'couplings':{(0, 0):'GQQ'},
1490 'orders':{'QCD':1}}))
1491
1492=== added file 'tests/unit_tests/core/test_color_algebra.py'
1493--- tests/unit_tests/core/test_color_algebra.py 1970-01-01 00:00:00 +0000
1494+++ tests/unit_tests/core/test_color_algebra.py 2010-01-21 09:19:15 +0000
1495@@ -0,0 +1,352 @@
1496+################################################################################
1497+#
1498+# Copyright (c) 2009 The MadGraph Development team and Contributors
1499+#
1500+# This file is a part of the MadGraph 5 project, an application which
1501+# automatically generates Feynman diagrams and matrix elements for arbitrary
1502+# high-energy processes in the Standard Model and beyond.
1503+#
1504+# It is subject to the MadGraph license which should accompany this
1505+# distribution.
1506+#
1507+# For more information, please visit: http://madgraph.phys.ucl.ac.be
1508+#
1509+################################################################################
1510+
1511+"""Unit test library for the color algebra related routines
1512+in the core library"""
1513+
1514+import copy
1515+import fractions
1516+import unittest
1517+
1518+import madgraph.core.color_algebra as color
1519+#
1520+class ColorObjectTest(unittest.TestCase):
1521+ """Test class for the ColorObject objects"""
1522+
1523+ def test_standard(self):
1524+ """Test the standard routines of ColorObject"""
1525+
1526+ my_color_object = color.ColorObject(-1, 2, 3)
1527+ my_color_object.append(4)
1528+
1529+ self.assertEqual('ColorObject(-1,2,3,4)', str(my_color_object))
1530+
1531+
1532+ def test_Tr_simplify(self):
1533+ """Test simplification of trace objects"""
1534+
1535+ # Test Tr(a)=0
1536+ self.assertEqual(color.Tr(-1).simplify(),
1537+ color.ColorFactor([color.ColorString(coeff=0)]))
1538+
1539+ # Test Tr()=Nc
1540+ col_str = color.ColorString()
1541+ col_str.Nc_power = 1
1542+ self.assertEqual(color.Tr().simplify(), color.ColorFactor([col_str]))
1543+
1544+ # Test cyclicity
1545+ col_str = color.ColorString([color.Tr(1, 2, 3, 4, 5)])
1546+ self.assertEqual(color.Tr(3, 4, 5, 1, 2).simplify(),
1547+ color.ColorFactor([col_str]))
1548+
1549+ # Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c))
1550+ col_str1 = color.ColorString([color.Tr(1, 2, 4), color.Tr(3)])
1551+ col_str2 = color.ColorString([color.Tr(1, 2, 3, 4)])
1552+ col_str1.coeff = fractions.Fraction(1, 2)
1553+ col_str2.coeff = fractions.Fraction(-1, 2)
1554+ col_str2.Nc_power = -1
1555+ my_tr = color.Tr(1, 2, 100, 3, 100, 4)
1556+ self.assertEqual(my_tr.simplify(),
1557+ color.ColorFactor([col_str1, col_str2]))
1558+
1559+ my_tr = color.Tr(1, 2, 100, 100, 4)
1560+
1561+ col_str1 = color.ColorString([color.Tr(1, 2, 4), color.Tr()])
1562+ col_str2 = color.ColorString([color.Tr(1, 2, 4)])
1563+ self.assertEqual(my_tr.simplify(),
1564+ color.ColorFactor([col_str1, col_str2]))
1565+
1566+ my_tr = color.Tr(100, 100)
1567+ col_str1 = color.ColorString([color.Tr(), color.Tr()])
1568+ col_str2 = color.ColorString([color.Tr()])
1569+ self.assertEqual(my_tr.simplify(),
1570+ color.ColorFactor([col_str1, col_str2]))
1571+
1572+ def test_Tr_pair_simplify(self):
1573+ """Test Tr object product simplification"""
1574+
1575+ my_Tr1 = color.Tr(1, 2, 3)
1576+ my_Tr2 = color.Tr(4, 2, 5)
1577+ my_T = color.T(4, 2, 5, 101, 102)
1578+
1579+ col_str1 = color.ColorString([color.Tr(1, 5, 4, 3)])
1580+ col_str2 = color.ColorString([color.Tr(1, 3), color.Tr(4, 5)])
1581+ col_str1.coeff = fractions.Fraction(1, 2)
1582+ col_str2.coeff = fractions.Fraction(-1, 2)
1583+ col_str2.Nc_power = -1
1584+ self.assertEqual(my_Tr1.pair_simplify(my_Tr2),
1585+ color.ColorFactor([col_str1, col_str2]))
1586+
1587+ col_str1 = color.ColorString([color.T(4, 3, 1, 5, 101, 102)])
1588+ col_str2 = color.ColorString([color.Tr(1, 3), color.T(4, 5, 101, 102)])
1589+ self.assertEqual(my_Tr1.pair_simplify(my_T),
1590+ color.ColorFactor([col_str1, col_str2]))
1591+
1592+
1593+ def test_T_simplify(self):
1594+ """Test T simplify"""
1595+
1596+ # T(a,b,c,...,i,i) = Tr(a,b,c,...)
1597+ self.assertEqual(color.T(1, 2, 3, 100, 100).simplify(),
1598+ color.ColorFactor([\
1599+ color.ColorString([color.Tr(1, 2, 3)])]))
1600+
1601+ # 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))
1602+ my_T = color.T(1, 2, 100, 3, 100, 4, 101, 102)
1603+ col_str1 = color.ColorString([color.T(1, 2, 4, 101, 102), color.Tr(3)])
1604+ col_str2 = color.ColorString([color.T(1, 2, 3, 4, 101, 102)])
1605+ col_str1.coeff = fractions.Fraction(1, 2)
1606+ col_str2.coeff = fractions.Fraction(-1, 2)
1607+ col_str2.Nc_power = -1
1608+ self.assertEqual(my_T.simplify(),
1609+ color.ColorFactor([col_str1, col_str2]))
1610+ self.assertEqual(my_T.simplify(),
1611+ color.ColorFactor([col_str1, col_str2]))
1612+ def test_T_pair_simplify(self):
1613+ """Test T object products simplifications"""
1614+
1615+ my_T1 = color.T(1, 2, 3, 101, 102)
1616+ my_T2 = color.T(4, 5, 102, 103)
1617+ self.assertEqual(my_T1.pair_simplify(my_T2),
1618+ color.ColorFactor([color.ColorString([\
1619+ color.T(1, 2, 3, 4, 5, 101, 103)])]))
1620+
1621+ my_T3 = color.T(4, 2, 5, 103, 104)
1622+ col_str1 = color.ColorString([color.T(1, 5, 101, 104),
1623+ color.T(4, 3, 103, 102)])
1624+ col_str2 = color.ColorString([color.T(1, 3, 101, 102),
1625+ color.T(4, 5, 103, 104)])
1626+ col_str1.coeff = fractions.Fraction(1, 2)
1627+ col_str2.coeff = fractions.Fraction(-1, 2)
1628+ col_str2.Nc_power = -1
1629+ self.assertEqual(my_T1.pair_simplify(my_T3, simplify_T_product=True),
1630+ color.ColorFactor([col_str1, col_str2]))
1631+
1632+ def test_f_object(self):
1633+ """Test the f color object"""
1634+ # T should have exactly 3 indices!
1635+ self.assertRaises(ValueError,
1636+ color.f,
1637+ 1, 2, 3, 4)
1638+
1639+ # Simplify should always return the same ColorFactor
1640+ my_f = color.f(1, 2, 3)
1641+ col_str1 = color.ColorString([color.Tr(1, 2, 3)])
1642+ col_str2 = color.ColorString([color.Tr(3, 2, 1)])
1643+ col_str1.coeff = fractions.Fraction(-2, 1)
1644+ col_str2.coeff = fractions.Fraction(2, 1)
1645+ col_str1.is_imaginary = True
1646+ col_str2.is_imaginary = True
1647+
1648+ self.assertEqual(my_f.simplify(),
1649+ color.ColorFactor([col_str1, col_str2]))
1650+
1651+ def test_d_object(self):
1652+ """Test the d color object"""
1653+ # T should have exactly 3 indices!
1654+ self.assertRaises(ValueError,
1655+ color.d,
1656+ 1, 2)
1657+
1658+ # Simplify should always return the same ColorFactor
1659+ my_d = color.d(1, 2, 3)
1660+ col_str1 = color.ColorString([color.Tr(1, 2, 3)])
1661+ col_str2 = color.ColorString([color.Tr(3, 2, 1)])
1662+ col_str1.coeff = fractions.Fraction(2, 1)
1663+ col_str2.coeff = fractions.Fraction(2, 1)
1664+
1665+ self.assertEqual(my_d.simplify(),
1666+ color.ColorFactor([col_str1, col_str2]))
1667+
1668+
1669+class ColorStringTest(unittest.TestCase):
1670+ """Test class for the ColorString objects"""
1671+
1672+ my_col_string = color.ColorString()
1673+
1674+ def setUp(self):
1675+ """Initialize the ColorString test"""
1676+ # Create a test color string
1677+
1678+ test_f = color.f(1, 2, 3)
1679+ test_d = color.d(4, 5, 6)
1680+
1681+ self.my_col_string = color.ColorString([test_f, test_d],
1682+ coeff=fractions.Fraction(2, 3),
1683+ Nc_power= -2,
1684+ is_imaginary=True)
1685+
1686+ def test_representation(self):
1687+ """Test ColorString representation"""
1688+
1689+ self.assertEqual(str(self.my_col_string),
1690+ "2/3 I 1/Nc^2 f(1,2,3) d(4,5,6)")
1691+
1692+ def test_product(self):
1693+ """Test the product of two color strings"""
1694+ test = copy.copy(self.my_col_string)
1695+ test.product(self.my_col_string)
1696+ self.assertEqual(str(test),
1697+ "-4/9 1/Nc^4 f(1,2,3) d(4,5,6) f(1,2,3) d(4,5,6)")
1698+
1699+
1700+ def test_simplify(self):
1701+ """Test the simplification of a string"""
1702+
1703+ # Simplification of one term
1704+ self.assertEqual(str(self.my_col_string.simplify()),
1705+ '(4/3 1/Nc^2 Tr(1,2,3) d(4,5,6))+(-4/3 1/Nc^2 Tr(3,2,1) d(4,5,6))')
1706+
1707+ def test_complex_conjugate(self):
1708+ """Test the complex conjugation of a color string"""
1709+
1710+ my_color_string = color.ColorString([color.T(3, 4, 102, 103),
1711+ color.Tr(1, 2, 3)])
1712+ my_color_string.is_imaginary = True
1713+
1714+ self.assertEqual(str(my_color_string.complex_conjugate()),
1715+ '-1 I T(4,3,103,102) Tr(3,2,1)')
1716+
1717+ def test_to_immutable(self):
1718+ """Test the immutable representation of a color string structure"""
1719+
1720+ self.assertEqual(self.my_col_string.to_immutable(),
1721+ (('d', (4, 5, 6)), ('f', (1, 2, 3))))
1722+
1723+ def test_from_immutable(self):
1724+ """Test the creation of a color string using its immutable rep"""
1725+
1726+ test_str = copy.copy(self.my_col_string)
1727+ test_str.from_immutable((('f', (1, 2, 3)), ('d', (4, 5, 6))))
1728+
1729+ self.assertEqual(test_str, self.my_col_string)
1730+
1731+ def test_replace_indices(self):
1732+ """Test indices replacement"""
1733+
1734+ repl_dict = {1:2, 2:3, 3:1}
1735+
1736+ my_color_string = color.ColorString([color.T(1, 2, 3, 4),
1737+ color.Tr(3, 2, 1)])
1738+
1739+ my_color_string.replace_indices(repl_dict)
1740+ self.assertEqual(str(my_color_string),
1741+ '1 T(2,3,1,4) Tr(1,3,2)')
1742+
1743+ def test_color_string_canonical(self):
1744+ """Test the canonical representation of a immutable color string"""
1745+
1746+ immutable1 = (('f', (2, 3, 4)), ('T', (4, 2, 5)))
1747+ immutable2 = (('T', (3, 5)),)
1748+
1749+ self.assertEqual(color.ColorString().to_canonical(immutable1 + \
1750+ immutable2)[0],
1751+ (('T', (2, 4)), ('T', (3, 1, 4)), ('f', (1, 2, 3))))
1752+
1753+class ColorFactorTest(unittest.TestCase):
1754+ """Test class for the ColorFactor objects"""
1755+
1756+ def test_f_d_sum(self):
1757+ """Test f and d sum with the right weights giving a Tr"""
1758+
1759+ col_str1 = color.ColorString([color.d(1, 2, 3)])
1760+ col_str1.coeff = fractions.Fraction(1, 4)
1761+ col_str2 = color.ColorString([color.f(1, 2, 3)])
1762+ col_str2.coeff = fractions.Fraction(1, 4)
1763+ col_str2.is_imaginary = True
1764+
1765+ my_color_factor = color.ColorFactor([col_str1, col_str2])
1766+
1767+ self.assertEqual(str(my_color_factor.full_simplify()),
1768+ '(1 Tr(1,2,3))')
1769+
1770+ def test_f_product(self):
1771+ """Test the fully contracted product of two f's"""
1772+
1773+ my_color_factor = color.ColorFactor([\
1774+ color.ColorString([color.f(1, 2, 3), color.f(1, 2, 3)])])
1775+
1776+ self.assertEqual(str(my_color_factor.full_simplify()),
1777+ '(-1 Nc^1 )+(1 Nc^3 )')
1778+
1779+
1780+ def test_d_product(self):
1781+ """Test the fully contracted product of two d's"""
1782+
1783+ my_color_factor = color.ColorFactor([\
1784+ color.ColorString([color.d(1, 2, 3), color.d(1, 2, 3)])])
1785+
1786+
1787+ self.assertEqual(str(my_color_factor.full_simplify()),
1788+ '(-5 Nc^1 )+(4 1/Nc^1 )+(1 Nc^3 )')
1789+
1790+ def test_f_d_product(self):
1791+ """Test the fully contracted product of f and d"""
1792+
1793+ my_color_factor = color.ColorFactor([\
1794+ color.ColorString([color.f(1, 2, 3), color.d(1, 2, 3)])])
1795+
1796+
1797+ self.assertEqual(str(my_color_factor.full_simplify()), '')
1798+
1799+ def test_three_f_chain(self):
1800+ """Test a chain of three f's"""
1801+
1802+ my_color_factor = color.ColorFactor([\
1803+ color.ColorString([color.f(1, 2, -1),
1804+ color.f(-1, 3, -2),
1805+ color.f(-2, 4, 5)])])
1806+
1807+ self.assertEqual(str(my_color_factor.full_simplify()),
1808+ "(2 I Tr(1,2,3,4,5))+(-2 I Tr(1,2,4,5,3))+(-2 I Tr(1,2,3,5,4))" + \
1809+ "+(2 I Tr(1,2,5,4,3))+(-2 I Tr(1,3,4,5,2))+(2 I Tr(1,4,5,3,2))" + \
1810+ "+(2 I Tr(1,3,5,4,2))+(-2 I Tr(1,5,4,3,2))")
1811+
1812+ def test_Tr_product(self):
1813+ """Test a non trivial product of two traces"""
1814+
1815+ my_color_factor = color.ColorFactor([\
1816+ color.ColorString([color.Tr(1, 2, 3, 4, 5, 6, 7),
1817+ color.Tr(1, 7, 6, 5, 4, 3, 2)])])
1818+
1819+ self.assertEqual(str(my_color_factor.full_simplify()),
1820+ "(1/128 Nc^7 )+(-7/128 Nc^5 )+(21/128 Nc^3 )+(-35/128 Nc^1 )" + \
1821+ "+(35/128 1/Nc^1 )+(-21/128 1/Nc^3 )+(3/64 1/Nc^5 )")
1822+
1823+ def test_T_f_product(self):
1824+ """Test a non trivial T f f product"""
1825+
1826+ my_color_factor = color.ColorFactor([\
1827+ color.ColorString([color.T(-1000, 1, 2),
1828+ color.f(-1, -1000, 5),
1829+ color.f(-1, 4, 3)])])
1830+
1831+ self.assertEqual(str(my_color_factor.full_simplify()),
1832+ "(-1 T(5,4,3,1,2))+(1 T(5,3,4,1,2))+(1 T(4,3,5,1,2))+(-1 T(3,4,5,1,2))")
1833+
1834+
1835+ def test_gluons(self):
1836+ """Test simplification of chains of f"""
1837+
1838+ my_col_fact = color.ColorFactor([color.ColorString([color.f(-3, 1, 2),
1839+ color.f(-1, 3, 4),
1840+ color.f(-1, 5, -3)
1841+ ])])
1842+
1843+ self.assertEqual(str(my_col_fact.full_simplify()),
1844+ '(2 I Tr(1,2,3,4,5))+(-2 I Tr(1,2,5,3,4))+(-2 I Tr(1,2,4,3,5))+' + \
1845+ '(2 I Tr(1,2,5,4,3))+(-2 I Tr(1,3,4,5,2))+(2 I Tr(1,5,3,4,2))+' + \
1846+ '(2 I Tr(1,4,3,5,2))+(-2 I Tr(1,5,4,3,2))')
1847+
1848
1849=== added file 'tests/unit_tests/core/test_color_amp.py'
1850--- tests/unit_tests/core/test_color_amp.py 1970-01-01 00:00:00 +0000
1851+++ tests/unit_tests/core/test_color_amp.py 2010-01-21 09:19:15 +0000
1852@@ -0,0 +1,600 @@
1853+################################################################################
1854+#
1855+# Copyright (c) 2009 The MadGraph Development team and Contributors
1856+#
1857+# This file is a part of the MadGraph 5 project, an application which
1858+# automatically generates Feynman diagrams and matrix elements for arbitrary
1859+# high-energy processes in the Standard Model and beyond.
1860+#
1861+# It is subject to the MadGraph license which should accompany this
1862+# distribution.
1863+#
1864+# For more information, please visit: http://madgraph.phys.ucl.ac.be
1865+#
1866+################################################################################
1867+
1868+"""Unit test library for the routines of the core library related to writing
1869+color information for diagrams."""
1870+
1871+import copy
1872+import fractions
1873+import unittest
1874+
1875+import madgraph.core.base_objects as base_objects
1876+import madgraph.core.diagram_generation as diagram_generation
1877+
1878+import madgraph.core.color_amp as color_amp
1879+import madgraph.core.color_algebra as color
1880+
1881+class ColorAmpTest(unittest.TestCase):
1882+ """Test class for the color_amp module"""
1883+
1884+ mypartlist = base_objects.ParticleList()
1885+ myinterlist = base_objects.InteractionList()
1886+ mymodel = base_objects.Model()
1887+
1888+ def setUp(self):
1889+ # A gluon
1890+ self.mypartlist.append(base_objects.Particle({'name':'g',
1891+ 'antiname':'g',
1892+ 'spin':3,
1893+ 'color':8,
1894+ 'mass':'zero',
1895+ 'width':'zero',
1896+ 'texname':'g',
1897+ 'antitexname':'g',
1898+ 'line':'curly',
1899+ 'charge':0.,
1900+ 'pdg_code':21,
1901+ 'propagating':True,
1902+ 'is_part':True,
1903+ 'self_antipart':True}))
1904+
1905+ # A quark U and its antiparticle
1906+ self.mypartlist.append(base_objects.Particle({'name':'u',
1907+ 'antiname':'u~',
1908+ 'spin':2,
1909+ 'color':3,
1910+ 'mass':'zero',
1911+ 'width':'zero',
1912+ 'texname':'u',
1913+ 'antitexname':'\bar u',
1914+ 'line':'straight',
1915+ 'charge':2. / 3.,
1916+ 'pdg_code':2,
1917+ 'propagating':True,
1918+ 'is_part':True,
1919+ 'self_antipart':False}))
1920+ antiu = copy.copy(self.mypartlist[1])
1921+ antiu.set('is_part', False)
1922+
1923+ # A quark D and its antiparticle
1924+ self.mypartlist.append(base_objects.Particle({'name':'d',
1925+ 'antiname':'d~',
1926+ 'spin':2,
1927+ 'color':3,
1928+ 'mass':'zero',
1929+ 'width':'zero',
1930+ 'texname':'u',
1931+ 'antitexname':'\bar u',
1932+ 'line':'straight',
1933+ 'charge':-1. / 3.,
1934+ 'pdg_code':1,
1935+ 'propagating':True,
1936+ 'is_part':True,
1937+ 'self_antipart':False}))
1938+ antid = copy.copy(self.mypartlist[2])
1939+ antid.set('is_part', False)
1940+
1941+ # A photon
1942+ self.mypartlist.append(base_objects.Particle({'name':'a',
1943+ 'antiname':'a',
1944+ 'spin':3,
1945+ 'color':1,
1946+ 'mass':'zero',
1947+ 'width':'zero',
1948+ 'texname':'\gamma',
1949+ 'antitexname':'\gamma',
1950+ 'line':'wavy',
1951+ 'charge':0.,
1952+ 'pdg_code':22,
1953+ 'propagating':True,
1954+ 'is_part':True,
1955+ 'self_antipart':True}))
1956+
1957+ # A Higgs
1958+ self.mypartlist.append(base_objects.Particle({'name':'h',
1959+ 'antiname':'h',
1960+ 'spin':1,
1961+ 'color':1,
1962+ 'mass':'mh',
1963+ 'width':'wh',
1964+ 'texname':'h',
1965+ 'antitexname':'h',
1966+ 'line':'dashed',
1967+ 'charge':0.,
1968+ 'pdg_code':25,
1969+ 'propagating':True,
1970+ 'is_part':True,
1971+ 'self_antipart':True}))
1972+
1973+ # 3 gluon vertiex
1974+ self.myinterlist.append(base_objects.Interaction({
1975+ 'id': 1,
1976+ 'particles': base_objects.ParticleList(\
1977+ [self.mypartlist[0]] * 3),
1978+ 'color': [color.ColorString([color.f(0, 1, 2)])],
1979+ 'lorentz':['L1'],
1980+ 'couplings':{(0, 0):'G'},
1981+ 'orders':{'QCD':1}}))
1982+
1983+ # 4 gluon vertex
1984+ self.myinterlist.append(base_objects.Interaction({
1985+ 'id': 2,
1986+ 'particles': base_objects.ParticleList(\
1987+ [self.mypartlist[0]] * 4),
1988+ 'color': [color.ColorString([color.f(-1, 0, 2),
1989+ color.f(-1, 1, 3)]),
1990+ color.ColorString([color.f(-1, 0, 3),
1991+ color.f(-1, 1, 2)]),
1992+ color.ColorString([color.f(-1, 0, 1),
1993+ color.f(-1, 2, 3)])],
1994+ 'lorentz':['L(p1,p2,p3)', 'L(p2,p3,p1)', 'L3'],
1995+ 'couplings':{(0, 0):'G^2',
1996+ (1, 1):'G^2',
1997+ (2, 2):'G^2'},
1998+ 'orders':{'QCD':2}}))
1999+
2000+ # Gluon couplings to up and down quarks
2001+ self.myinterlist.append(base_objects.Interaction({
2002+ 'id': 3,
2003+ 'particles': base_objects.ParticleList(\
2004+ [self.mypartlist[1], \
2005+ antiu, \
2006+ self.mypartlist[0]]),
2007+ 'color': [color.ColorString([color.T(2, 0, 1)])],
2008+ 'lorentz':['L1'],
2009+ 'couplings':{(0, 0):'GQQ'},
2010+ 'orders':{'QCD':1}}))
2011+
2012+ self.myinterlist.append(base_objects.Interaction({
2013+ 'id': 4,
2014+ 'particles': base_objects.ParticleList(\
2015+ [self.mypartlist[2], \
2016+ antid, \
2017+ self.mypartlist[0]]),
2018+ 'color': [color.ColorString([color.T(2, 0, 1)])],
2019+ 'lorentz':['L1'],
2020+ 'couplings':{(0, 0):'GQQ'},
2021+ 'orders':{'QCD':1}}))
2022+
2023+ # Photon coupling to up
2024+ self.myinterlist.append(base_objects.Interaction({
2025+ 'id': 5,
2026+ 'particles': base_objects.ParticleList(\
2027+ [self.mypartlist[1], \
2028+ antiu, \
2029+ self.mypartlist[3]]),
2030+ 'color': [color.ColorString([color.T(0, 1)])],
2031+ 'lorentz':['L1'],
2032+ 'couplings':{(0, 0):'GQED'},
2033+ 'orders':{'QED':1}}))
2034+
2035+ self.mymodel.set('particles', self.mypartlist)
2036+ self.mymodel.set('interactions', self.myinterlist)
2037+
2038+ def test_colorize_uu_gg(self):
2039+ """Test the colorize function for uu~ > gg"""
2040+
2041+ myleglist = base_objects.LegList()
2042+
2043+ myleglist.append(base_objects.Leg({'id':-2,
2044+ 'state':'initial'}))
2045+ myleglist.append(base_objects.Leg({'id':2,
2046+ 'state':'initial'}))
2047+
2048+ myleglist.extend([base_objects.Leg({'id':21,
2049+ 'state':'final'})] * 2)
2050+
2051+ myprocess = base_objects.Process({'legs':myleglist,
2052+ 'model':self.mymodel})
2053+
2054+ myamplitude = diagram_generation.Amplitude()
2055+
2056+ myamplitude.set('process', myprocess)
2057+
2058+ myamplitude.generate_diagrams()
2059+
2060+ my_col_basis = color_amp.ColorBasis()
2061+
2062+ # S channel
2063+ col_dict = my_col_basis.colorize(myamplitude['diagrams'][0],
2064+ self.mymodel)
2065+
2066+ goal_dict = {(0, 0):color.ColorString([color.T(-1000, 1, 2),
2067+ color.f(-1000, 4, 3)])}
2068+
2069+ self.assertEqual(col_dict, goal_dict)
2070+
2071+ # T channel
2072+ col_dict = my_col_basis.colorize(myamplitude['diagrams'][1],
2073+ self.mymodel)
2074+
2075+ goal_dict = {(0, 0):color.ColorString([color.T(3, 1, -1000),
2076+ color.T(4, -1000, 2)])}
2077+
2078+ self.assertEqual(col_dict, goal_dict)
2079+
2080+ # U channel
2081+ col_dict = my_col_basis.colorize(myamplitude['diagrams'][2],
2082+ self.mymodel)
2083+
2084+ goal_dict = {(0, 0):color.ColorString([color.T(4, 1, -1000),
2085+ color.T(3, -1000, 2)])}
2086+
2087+ self.assertEqual(col_dict, goal_dict)
2088+
2089+ def test_colorize_uux_ggg(self):
2090+ """Test the colorize function for uu~ > ggg"""
2091+
2092+ myleglist = base_objects.LegList()
2093+
2094+ myleglist.append(base_objects.Leg({'id':-2,
2095+ 'state':'initial'}))
2096+ myleglist.append(base_objects.Leg({'id':2,
2097+ 'state':'initial'}))
2098+
2099+ myleglist.extend([base_objects.Leg({'id':21,
2100+ 'state':'final'})] * 3)
2101+
2102+ myprocess = base_objects.Process({'legs':myleglist,
2103+ 'model':self.mymodel})
2104+
2105+ myamplitude = diagram_generation.Amplitude()
2106+
2107+ myamplitude.set('process', myprocess)
2108+
2109+ myamplitude.generate_diagrams()
2110+
2111+ my_col_basis = color_amp.ColorBasis()
2112+
2113+ # First diagram with two 3-gluon vertices
2114+ col_dict = my_col_basis.colorize(myamplitude['diagrams'][0],
2115+ self.mymodel)
2116+
2117+ goal_dict = {(0, 0, 0):color.ColorString([color.T(-1000, 1, 2),
2118+ color.f(-1001, 4, 3),
2119+ color.f(5, -1001, -1000)])}
2120+
2121+ self.assertEqual(col_dict, goal_dict)
2122+
2123+ # Diagram with one 4-gluon vertex
2124+ col_dict = my_col_basis.colorize(myamplitude['diagrams'][3],
2125+ self.mymodel)
2126+
2127+ goal_dict = {(0, 0):color.ColorString([color.T(-1000, 1, 2),
2128+ color.f(-1002, -1000, 4),
2129+ color.f(-1002, 5, 3)]),
2130+ (0, 1):color.ColorString([color.T(-1000, 1, 2),
2131+ color.f(-1003, -1000, 3),
2132+ color.f(-1003, 5, 4)]),
2133+ (0, 2):color.ColorString([color.T(-1000, 1, 2),
2134+ color.f(-1004, -1000, 5),
2135+ color.f(-1004, 4, 3)])}
2136+
2137+ self.assertEqual(col_dict, goal_dict)
2138+
2139+ def test_color_basis_uux_aggg(self):
2140+ """Test the color basis building for uu~ > aggg (3! elements)"""
2141+
2142+ myleglist = base_objects.LegList()
2143+
2144+ myleglist.append(base_objects.Leg({'id':2,
2145+ 'state':'initial'}))
2146+ myleglist.append(base_objects.Leg({'id':-2,
2147+ 'state':'initial'}))
2148+
2149+ myleglist.append(base_objects.Leg({'id':22,
2150+ 'state':'final'}))
2151+ myleglist.extend([base_objects.Leg({'id':21,
2152+ 'state':'final'})] * 3)
2153+
2154+ myprocess = base_objects.Process({'legs':myleglist,
2155+ 'model':self.mymodel})
2156+
2157+ myamplitude = diagram_generation.Amplitude()
2158+
2159+ myamplitude.set('process', myprocess)
2160+
2161+ myamplitude.generate_diagrams()
2162+
2163+ new_col_basis = color_amp.ColorBasis(myamplitude, self.mymodel)
2164+
2165+ self.assertEqual(len(new_col_basis), 6)
2166+
2167+class ColorSquareTest(unittest.TestCase):
2168+ """Test class for the color_amp module"""
2169+
2170+ mypartlist = base_objects.ParticleList()
2171+ myinterlist = base_objects.InteractionList()
2172+ mymodel = base_objects.Model()
2173+
2174+ def setUp(self):
2175+ # A gluon
2176+ self.mypartlist.append(base_objects.Particle({'name':'g',
2177+ 'antiname':'g',
2178+ 'spin':3,
2179+ 'color':8,
2180+ 'mass':'zero',
2181+ 'width':'zero',
2182+ 'texname':'g',
2183+ 'antitexname':'g',
2184+ 'line':'curly',
2185+ 'charge':0.,
2186+ 'pdg_code':21,
2187+ 'propagating':True,
2188+ 'is_part':True,
2189+ 'self_antipart':True}))
2190+
2191+ # A quark U and its antiparticle
2192+ self.mypartlist.append(base_objects.Particle({'name':'u',
2193+ 'antiname':'u~',
2194+ 'spin':2,
2195+ 'color':3,
2196+ 'mass':'zero',
2197+ 'width':'zero',
2198+ 'texname':'u',
2199+ 'antitexname':'\bar u',
2200+ 'line':'straight',
2201+ 'charge':2. / 3.,
2202+ 'pdg_code':2,
2203+ 'propagating':True,
2204+ 'is_part':True,
2205+ 'self_antipart':False}))
2206+ antiu = copy.copy(self.mypartlist[1])
2207+ antiu.set('is_part', False)
2208+
2209+ # A quark D and its antiparticle
2210+ self.mypartlist.append(base_objects.Particle({'name':'d',
2211+ 'antiname':'d~',
2212+ 'spin':2,
2213+ 'color':3,
2214+ 'mass':'zero',
2215+ 'width':'zero',
2216+ 'texname':'u',
2217+ 'antitexname':'\bar u',
2218+ 'line':'straight',
2219+ 'charge':-1. / 3.,
2220+ 'pdg_code':1,
2221+ 'propagating':True,
2222+ 'is_part':True,
2223+ 'self_antipart':False}))
2224+ antid = copy.copy(self.mypartlist[2])
2225+ antid.set('is_part', False)
2226+
2227+ # 3 gluon vertiex
2228+ self.myinterlist.append(base_objects.Interaction({
2229+ 'id': 1,
2230+ 'particles': base_objects.ParticleList(\
2231+ [self.mypartlist[0]] * 3),
2232+ 'color': [color.ColorString([color.f(0, 1, 2)])],
2233+ 'lorentz':['L1'],
2234+ 'couplings':{(0, 0):'G'},
2235+ 'orders':{'QCD':1}}))
2236+
2237+ # 4 gluon vertex
2238+ self.myinterlist.append(base_objects.Interaction({
2239+ 'id': 2,
2240+ 'particles': base_objects.ParticleList(\
2241+ [self.mypartlist[0]] * 4),
2242+ 'color': [color.ColorString([color.f(-1, 0, 2),
2243+ color.f(-1, 1, 3)]),
2244+ color.ColorString([color.f(-1, 0, 3),
2245+ color.f(-1, 1, 2)]),
2246+ color.ColorString([color.f(-1, 0, 1),
2247+ color.f(-1, 2, 3)])],
2248+ 'lorentz':['L(p1,p2,p3)', 'L(p2,p3,p1)', 'L3'],
2249+ 'couplings':{(0, 0):'G^2',
2250+ (1, 1):'G^2',
2251+ (2, 2):'G^2'},
2252+ 'orders':{'QCD':2}}))
2253+
2254+ # Gluon couplings to up and down quarks
2255+ self.myinterlist.append(base_objects.Interaction({
2256+ 'id': 3,
2257+ 'particles': base_objects.ParticleList(\
2258+ [self.mypartlist[1], \
2259+ antiu, \
2260+ self.mypartlist[0]]),
2261+ 'color': [color.ColorString([color.T(2, 0, 1)])],
2262+ 'lorentz':['L1'],
2263+ 'couplings':{(0, 0):'GQQ'},
2264+ 'orders':{'QCD':1}}))
2265+
2266+ self.myinterlist.append(base_objects.Interaction({
2267+ 'id': 4,
2268+ 'particles': base_objects.ParticleList(\
2269+ [self.mypartlist[2], \
2270+ antid, \
2271+ self.mypartlist[0]]),
2272+ 'color': [color.ColorString([color.T(2, 0, 1)])],
2273+ 'lorentz':['L1'],
2274+ 'couplings':{(0, 0):'GQQ'},
2275+ 'orders':{'QCD':1}}))
2276+
2277+
2278+ self.mymodel.set('particles', self.mypartlist)
2279+ self.mymodel.set('interactions', self.myinterlist)
2280+
2281+ def test_color_matrix_multi_gluons(self):
2282+ """Test the color matrix building for gg > n*g with n up to 3"""
2283+
2284+ goal = [fractions.Fraction(7, 3),
2285+ fractions.Fraction(19, 6),
2286+ fractions.Fraction(455, 108),
2287+ fractions.Fraction(3641, 648)]
2288+
2289+ goal_line1 = [(fractions.Fraction(7, 3), fractions.Fraction(-2, 3)),
2290+ (fractions.Fraction(19, 6), fractions.Fraction(-1, 3),
2291+ fractions.Fraction(-1, 3), fractions.Fraction(-1, 3),
2292+ fractions.Fraction(2, 3), fractions.Fraction(-1, 3)),
2293+ (fractions.Fraction(455, 108), fractions.Fraction(-29, 54),
2294+ fractions.Fraction(17, 27), fractions.Fraction(7, 54),
2295+ fractions.Fraction(-1, 27), fractions.Fraction(17, 27),
2296+ fractions.Fraction(5, 108), fractions.Fraction(7, 54),
2297+ fractions.Fraction(7, 54), fractions.Fraction(-1, 27),
2298+ fractions.Fraction(7, 54), fractions.Fraction(5, 108),
2299+ fractions.Fraction(-10, 27), fractions.Fraction(-29, 54),
2300+ fractions.Fraction(-29, 54), fractions.Fraction(-29, 54),
2301+ fractions.Fraction(-29, 54), fractions.Fraction(7, 54),
2302+ fractions.Fraction(17, 27), fractions.Fraction(-1, 27),
2303+ fractions.Fraction(-1, 27), fractions.Fraction(17, 27),
2304+ fractions.Fraction(-1, 27), fractions.Fraction(17, 27))]
2305+
2306+ for n in range(3):
2307+ myleglist = base_objects.LegList()
2308+
2309+ myleglist.append(base_objects.Leg({'id':21,
2310+ 'state':'initial'}))
2311+ myleglist.append(base_objects.Leg({'id':21,
2312+ 'state':'initial'}))
2313+
2314+ myleglist.extend([base_objects.Leg({'id':21,
2315+ 'state':'final'})] * (n + 1))
2316+
2317+ myprocess = base_objects.Process({'legs':myleglist,
2318+ 'model':self.mymodel})
2319+
2320+ myamplitude = diagram_generation.Amplitude()
2321+
2322+ myamplitude.set('process', myprocess)
2323+
2324+ myamplitude.generate_diagrams()
2325+
2326+ col_basis = color_amp.ColorBasis(myamplitude, self.mymodel)
2327+
2328+ col_matrix = color_amp.ColorMatrix(col_basis, Nc=3)
2329+
2330+ # Check diagonal
2331+ for i in range(len(col_basis.items())):
2332+ self.assertEqual(col_matrix.col_matrix_fixed_Nc[(i, i)],
2333+ (goal[n], 0))
2334+
2335+ # Check first line
2336+ for i in range(len(col_basis.items())):
2337+ self.assertEqual(col_matrix.col_matrix_fixed_Nc[(0, i)],
2338+ (goal_line1[n][i], 0))
2339+
2340+ def test_color_matrix_multi_quarks(self):
2341+ """Test the color matrix building for qq~ > n*(qq~) with n up to 2"""
2342+
2343+ goal = [fractions.Fraction(2, 1),
2344+ fractions.Fraction(72, 54)]
2345+
2346+
2347+
2348+ goal_line1 = [(fractions.Fraction(2, 1), fractions.Fraction(-2, 3)),
2349+ (fractions.Fraction(4, 3), fractions.Fraction(-5, 27),
2350+ fractions.Fraction(7, 6), fractions.Fraction(-1, 3),
2351+ fractions.Fraction(1, 9), fractions.Fraction(-7, 18),
2352+ fractions.Fraction(-5, 27), fractions.Fraction(-7, 18),
2353+ fractions.Fraction(1, 18), fractions.Fraction(31, 27),
2354+ fractions.Fraction(-5, 27), fractions.Fraction(-7, 18),
2355+ fractions.Fraction(-1, 3), fractions.Fraction(31, 27),
2356+ fractions.Fraction(-1, 3), fractions.Fraction(-4, 9),
2357+ fractions.Fraction(1, 9), fractions.Fraction(7, 6),
2358+ fractions.Fraction(-1, 6), fractions.Fraction(-5, 27),
2359+ fractions.Fraction(-1, 54), fractions.Fraction(1, 9),
2360+ fractions.Fraction(1, 18), fractions.Fraction(1, 18),
2361+ fractions.Fraction(4, 27), fractions.Fraction(-1, 54),
2362+ fractions.Fraction(5, 9), fractions.Fraction(1, 9),
2363+ fractions.Fraction(-4, 9), fractions.Fraction(-1, 54),
2364+ fractions.Fraction(71, 54), fractions.Fraction(1, 18),
2365+ fractions.Fraction(-7, 18), fractions.Fraction(-1, 3),
2366+ fractions.Fraction(-1, 54), fractions.Fraction(5, 9))
2367+ ]
2368+
2369+ for n in range(2):
2370+ myleglist = base_objects.LegList()
2371+
2372+ myleglist.append(base_objects.Leg({'id':2,
2373+ 'state':'initial'}))
2374+ myleglist.append(base_objects.Leg({'id':-2,
2375+ 'state':'initial'}))
2376+
2377+ myleglist.extend([base_objects.Leg({'id':2,
2378+ 'state':'final'}),
2379+ base_objects.Leg({'id':-2,
2380+ 'state':'final'})] * (n + 1))
2381+
2382+ myprocess = base_objects.Process({'legs':myleglist,
2383+ 'model':self.mymodel})
2384+
2385+ myamplitude = diagram_generation.Amplitude()
2386+
2387+ myamplitude.set('process', myprocess)
2388+
2389+ myamplitude.generate_diagrams()
2390+
2391+ col_basis = color_amp.ColorBasis(myamplitude, self.mymodel)
2392+
2393+ col_matrix = color_amp.ColorMatrix(col_basis, Nc=3)
2394+
2395+ # Check diagonal
2396+ for i in range(len(col_basis.items())):
2397+ self.assertEqual(col_matrix.col_matrix_fixed_Nc[(i, i)],
2398+ (goal[n], 0))
2399+
2400+ # Check first line
2401+ for i in range(len(col_basis.items())):
2402+ self.assertEqual(col_matrix.col_matrix_fixed_Nc[(0, i)],
2403+ (goal_line1[n][i], 0))
2404+
2405+ def test_color_matrix_Nc_restrictions(self):
2406+ """Test the Nc power restriction during color basis building """
2407+
2408+ goal = [fractions.Fraction(3, 8),
2409+ fractions.Fraction(-9, 4),
2410+ fractions.Fraction(45, 16)]
2411+
2412+ for n in range(3):
2413+ myleglist = base_objects.LegList()
2414+
2415+ myleglist.append(base_objects.Leg({'id':21,
2416+ 'state':'initial'}))
2417+ myleglist.append(base_objects.Leg({'id':21,
2418+ 'state':'initial'}))
2419+
2420+ myleglist.extend([base_objects.Leg({'id':21,
2421+ 'state':'final'})] * 2)
2422+
2423+ myprocess = base_objects.Process({'legs':myleglist,
2424+ 'model':self.mymodel})
2425+
2426+ myamplitude = diagram_generation.Amplitude()
2427+
2428+ myamplitude.set('process', myprocess)
2429+
2430+ myamplitude.generate_diagrams()
2431+
2432+ col_basis = color_amp.ColorBasis(myamplitude, self.mymodel)
2433+
2434+ col_matrix = color_amp.ColorMatrix(col_basis, Nc=3,
2435+ Nc_power_min=n,
2436+ Nc_power_max=2 * n)
2437+
2438+ for i in range(len(col_basis.items())):
2439+ self.assertEqual(col_matrix.col_matrix_fixed_Nc[(i, i)],
2440+ (goal[n], 0))
2441+
2442+
2443+ def test_color_matrix_fixed_indices(self):
2444+ """Test index fixing for immutable color string"""
2445+
2446+ immutable1 = (('f', (1, -1, -3)), ('T', (-1, -3, 4)))
2447+ immutable2 = (('d', (1, -2, -1)), ('T', (-1, -2, 4)))
2448+
2449+ self.assertEqual(color_amp.ColorMatrix.fix_summed_indices(immutable1,
2450+ immutable2),
2451+ (('d', (1, -2, -5)), ('T', (-5, -2, 4))))
2452+
2453
2454=== modified file 'tests/unit_tests/core/test_diagram_generation.py'
2455--- tests/unit_tests/core/test_diagram_generation.py 2010-01-04 06:44:15 +0000
2456+++ tests/unit_tests/core/test_diagram_generation.py 2010-01-21 09:19:15 +0000
2457@@ -231,7 +231,7 @@
2458 'id': 1,
2459 'particles': base_objects.ParticleList(\
2460 [self.mypartlist[0]] * 3),
2461- 'color': ['C1'],
2462+ 'color': [['C1']],
2463 'lorentz':['L1'],
2464 'couplings':{(0, 0):'G'},
2465 'orders':{'QCD':1}}))
2466@@ -241,7 +241,7 @@
2467 'id': 2,
2468 'particles': base_objects.ParticleList(\
2469 [self.mypartlist[0]] * 4),
2470- 'color': ['C1'],
2471+ 'color': [['C1']],
2472 'lorentz':['L1'],
2473 'couplings':{(0, 0):'G^2'},
2474 'orders':{'QCD':2}}))
2475@@ -253,7 +253,7 @@
2476 [self.mypartlist[1], \
2477 antiu, \
2478 self.mypartlist[0]]),
2479- 'color': ['C1'],
2480+ 'color': [['C1']],
2481 'lorentz':['L1'],
2482 'couplings':{(0, 0):'GQQ'},
2483 'orders':{'QCD':1}}))
2484@@ -264,7 +264,7 @@
2485 [self.mypartlist[1], \
2486 antiu, \
2487 self.mypartlist[3]]),
2488- 'color': ['C1'],
2489+ 'color': [['C1']],
2490 'lorentz':['L1'],
2491 'couplings':{(0, 0):'GQED'},
2492 'orders':{'QED':1}}))
2493@@ -275,7 +275,7 @@
2494 [self.mypartlist[2], \
2495 antid, \
2496 self.mypartlist[0]]),
2497- 'color': ['C1'],
2498+ 'color': [['C1']],
2499 'lorentz':['L1'],
2500 'couplings':{(0, 0):'GQQ'},
2501 'orders':{'QCD':1}}))
2502@@ -286,7 +286,7 @@
2503 [self.mypartlist[2], \
2504 antid, \
2505 self.mypartlist[3]]),
2506- 'color': ['C1'],
2507+ 'color': [['C1']],
2508 'lorentz':['L1'],
2509 'couplings':{(0, 0):'GQED'},
2510 'orders':{'QED':1}}))
2511@@ -299,7 +299,7 @@
2512 [self.mypartlist[4], \
2513 antie, \
2514 self.mypartlist[3]]),
2515- 'color': ['C1'],
2516+ 'color': [['C1']],
2517 'lorentz':['L1'],
2518 'couplings':{(0, 0):'GQED'},
2519 'orders':{'QED':1}}))
2520@@ -1019,7 +1019,7 @@
2521 [antid, \
2522 u, \
2523 wminus]),
2524- 'color': ['C1'],
2525+ 'color': [['C1']],
2526 'lorentz':['L1'],
2527 'couplings':{(0, 0):'GQED'},
2528 'orders':{'QED':1}}))
2529@@ -1032,7 +1032,7 @@
2530 [antiu, \
2531 d, \
2532 wplus]),
2533- 'color': ['C1'],
2534+ 'color': [['C1']],
2535 'lorentz':['L1'],
2536 'couplings':{(0, 0):'GQED'},
2537 'orders':{'QED':1}}))
2538@@ -1045,7 +1045,7 @@
2539 [nuebar, \
2540 eminus, \
2541 wplus]),
2542- 'color': ['C1'],
2543+ 'color': [['C1']],
2544 'lorentz':['L1'],
2545 'couplings':{(0, 0):'GQED'},
2546 'orders':{'QED':1}}))
2547
2548=== renamed file 'tests/unit_tests/iolibs/test_import_v4.py' => 'tests/unit_tests/iolibs/test_import_model_v4.py'
2549--- tests/unit_tests/iolibs/test_import_v4.py 2009-12-12 08:42:16 +0000
2550+++ tests/unit_tests/iolibs/test_import_model_v4.py 2010-01-21 09:19:15 +0000
2551@@ -19,7 +19,7 @@
2552 import unittest
2553 import copy
2554
2555-import madgraph.iolibs.import_v4 as import_v4
2556+import madgraph.iolibs.import_model_v4 as import_v4
2557 import madgraph.core.base_objects as base_objects
2558
2559 #===============================================================================
2560@@ -28,12 +28,6 @@
2561 class IOImportV4Test(unittest.TestCase):
2562 """Test class for the import v4 module"""
2563
2564- def setUp(self):
2565- pass
2566-
2567- def tearDown(self):
2568- pass
2569-
2570 def test_read_particles(self):
2571 """Test the output of import particles.dat file"""
2572
2573@@ -136,7 +130,7 @@
2574 wmin = copy.copy(myparts[14])
2575 wmin.set('is_part', False)
2576 eplus = copy.copy(myparts[3])
2577- eplus.set('is_part',False)
2578+ eplus.set('is_part', False)
2579 enu = copy.copy(myparts[0])
2580 photon = copy.copy(myparts[12])
2581 gluon = copy.copy(myparts[15])
2582@@ -149,8 +143,13 @@
2583 wplus,
2584 wmin,
2585 photon]),
2586+<<<<<<< TREE
2587 'color':['guess'],
2588 'lorentz':[''],
2589+=======
2590+ 'color':[['C1']],
2591+ 'lorentz':['L1'],
2592+>>>>>>> MERGE-SOURCE
2593 'couplings':{(0, 0):'MGVX3'},
2594 'orders':{'QED':1}}),
2595 base_objects.Interaction(
2596@@ -159,8 +158,13 @@
2597 gluon,
2598 gluon,
2599 t1]),
2600+<<<<<<< TREE
2601 'color':['guess'],
2602 'lorentz':['A'],
2603+=======
2604+ 'color':[['C1']],
2605+ 'lorentz':['L1'],
2606+>>>>>>> MERGE-SOURCE
2607 'couplings':{(0, 0):'MGVX2'},
2608 'orders':{'QCD':1}}),
2609 base_objects.Interaction(
2610@@ -170,10 +174,16 @@
2611 wmin,
2612 wplus,
2613 wmin]),
2614+<<<<<<< TREE
2615 'color':['guess'],
2616 'lorentz':['WWWWN', ''],
2617 'couplings':{(0, 0):'MGVX6',
2618 (0, 1):'DUM0'},
2619+=======
2620+ 'color':[['C1']],
2621+ 'lorentz':['L1'],
2622+ 'couplings':{(0, 0):'MGVX6'},
2623+>>>>>>> MERGE-SOURCE
2624 'orders':{'QED':2}}),
2625
2626 base_objects.Interaction(
2627@@ -182,8 +192,13 @@
2628 eplus,
2629 enu,
2630 wmin]),
2631+<<<<<<< TREE
2632 'color':['guess'],
2633 'lorentz':[''],
2634+=======
2635+ 'color':[['C1']],
2636+ 'lorentz':['L1'],
2637+>>>>>>> MERGE-SOURCE
2638 'couplings':{(0, 0):'MGVX24'},
2639 'orders':{'QED':1}})])
2640
2641
2642=== added file 'tests/unit_tests/iolibs/test_save_model.py'
2643--- tests/unit_tests/iolibs/test_save_model.py 1970-01-01 00:00:00 +0000
2644+++ tests/unit_tests/iolibs/test_save_model.py 2010-01-21 09:19:15 +0000
2645@@ -0,0 +1,127 @@
2646+################################################################################
2647+#
2648+# Copyright (c) 2009 The MadGraph Development team and Contributors
2649+#
2650+# This file is a part of the MadGraph 5 project, an application which
2651+# automatically generates Feynman diagrams and matrix elements for arbitrary
2652+# high-energy processes in the Standard Model and beyond.
2653+#
2654+# It is subject to the MadGraph license which should accompany this
2655+# distribution.
2656+#
2657+# For more information, please visit: http://madgraph.phys.ucl.ac.be
2658+#
2659+################################################################################
2660+
2661+"""Unit test library for the save model routines"""
2662+
2663+import StringIO
2664+import unittest
2665+
2666+import madgraph.core.base_objects as base_objects
2667+
2668+import madgraph.iolibs.files as files
2669+import madgraph.iolibs.save_model as save_model
2670+
2671+class IOSaveModel(unittest.TestCase):
2672+ """Unit test class for the save model routines"""
2673+
2674+ def test_error_particle_save(self):
2675+ """Test error raising for particle save"""
2676+
2677+ fsock = StringIO.StringIO('')
2678+
2679+ my_part_list = "This is not a particle list"
2680+
2681+ self.assertRaises(ValueError,
2682+ save_model.save_particles,
2683+ fsock, my_part_list)
2684+
2685+ def test_particle_save(self):
2686+ """Test the particle save routine"""
2687+
2688+ mypartlist = \
2689+ base_objects.ParticleList(\
2690+ [base_objects.Particle({'name':'ve',
2691+ 'antiname':'ve~',
2692+ 'spin':2,
2693+ 'color':1,
2694+ 'mass':'ZERO',
2695+ 'width':'ZERO',
2696+ 'texname':'ve',
2697+ 'antitexname':'ve',
2698+ 'line':'straight',
2699+ 'charge': 0.,
2700+ 'pdg_code':12,
2701+ 'propagating':True,
2702+ 'is_part':True,
2703+ 'self_antipart':False}),
2704+ base_objects.Particle({'name':'w+',
2705+ 'antiname':'w-',
2706+ 'spin':3,
2707+ 'color':1,
2708+ 'mass':'MW',
2709+ 'width':'WW',
2710+ 'texname':'W',
2711+ 'antitexname':'W',
2712+ 'line':'wavy',
2713+ 'charge':0.,
2714+ 'pdg_code':24,
2715+ 'propagating':True,
2716+ 'is_part':True,
2717+ 'self_antipart':False})])
2718+
2719+ fsock = StringIO.StringIO()
2720+ save_model.save_particles(fsock, mypartlist)
2721+
2722+ goal_str = "particles = [\n%s,%s]" % (str(mypartlist[0]),
2723+ str(mypartlist[1]))
2724+
2725+ self.assertEqual(fsock.getvalue(), goal_str)
2726+
2727+ def test_error_interaction_save(self):
2728+ """Test error raising for interaction save"""
2729+
2730+ fsock = StringIO.StringIO('')
2731+
2732+ my_inter_list = "This is not an interaction list"
2733+
2734+ self.assertRaises(ValueError,
2735+ save_model.save_interactions,
2736+ fsock, my_inter_list)
2737+
2738+ def test_interaction_save(self):
2739+ """Test the interaction save routine"""
2740+
2741+ mypart = base_objects.Particle({'name':'t',
2742+ 'antiname':'t~',
2743+ 'spin':2,
2744+ 'color':3,
2745+ 'mass':'mt',
2746+ 'width':'wt',
2747+ 'texname':'t',
2748+ 'antitexname':'\\overline{t}',
2749+ 'line':'straight',
2750+ 'charge':2. / 3.,
2751+ 'pdg_code':6,
2752+ 'propagating':True,
2753+ 'is_part':True})
2754+
2755+ myinterlist = base_objects.InteractionList([\
2756+ base_objects.Interaction({'id': 1,
2757+ 'particles': base_objects.ParticleList([mypart] * 4),
2758+ 'color': ['C1', 'C2'],
2759+ 'lorentz':['L1', 'L2'],
2760+ 'couplings':{(0, 0):'g00',
2761+ (0, 1):'g01',
2762+ (1, 0):'g10',
2763+ (1, 1):'g11'},
2764+ 'orders':{'QCD':1, 'QED':1}})])
2765+
2766+ fsock = StringIO.StringIO()
2767+ save_model.save_interactions(fsock, myinterlist)
2768+
2769+ goal_str = "interactions = [\n%s]" % str(myinterlist[0])
2770+
2771+ self.assertEqual(fsock.getvalue(), goal_str)
2772+

Subscribers

People subscribed via source and target branches