Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/acceptancetest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ jobs:
cd $GITHUB_WORKSPACE
./tests/test_manager.py test_ML_check_brs_gd_gd -pA -t0 -l INFO
./tests/test_manager.py test_generate_output_semicolon_preserves_loop_process -pA -t0 -l INFO
./tests/test_manager.py test_loop_nc_memo_not_poisoned_across_generations -pA -t0 -l INFO



Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unittest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ jobs:
- name: test one of the test test_color_flow_string_epsilon test_colorize_funny_model test_colorize_uu_gg test_colorize_uux_ggg test_color_matrix_Nc_restrictions test_color_matrix_fixed_indices test_color_matrix_multi_gluons test_color_matrix_multi_quarks test_helper_lcm_functions test_get_s_and_t_ub_tdg test_diagram_list test_diagram_list_nice_string test_representation test_setget_diagram_correct test_setget_diagram_exceptions test_values_for_prop test_make_unique test_generating_dict_to_0 test_generating_dict_to_1 test_interaction_list test_representation test_setget_interaction_correct test_setget_interaction_exceptions test_values_for_prop test_leg_list
run: |
cd $GITHUB_WORKSPACE
./tests/test_manager.py test_color_flow_string_epsilon test_colorize_funny_model test_colorize_uu_gg test_colorize_uux_ggg test_color_matrix_Nc_restrictions test_color_matrix_fixed_indices test_color_matrix_multi_gluons test_color_matrix_multi_quarks test_helper_lcm_functions test_get_s_and_t_ub_tdg test_diagram_list test_diagram_list_nice_string test_representation test_setget_diagram_correct test_setget_diagram_exceptions test_values_for_prop test_make_unique test_generating_dict_to_0 test_generating_dict_to_1 test_interaction_list test_representation test_setget_interaction_correct test_setget_interaction_exceptions test_values_for_prop test_leg_list -t0
./tests/test_manager.py test_color_flow_string_epsilon test_colorize_funny_model test_colorize_uu_gg test_colorize_uux_ggg test_color_matrix_Nc_restrictions test_color_matrix_fixed_indices test_color_matrix_multi_gluons test_color_matrix_multi_quarks test_helper_lcm_functions test_loop_Nc_power_survives_a_poisoned_memo test_memo_is_shared_across_loop_bases test_get_s_and_t_ub_tdg test_diagram_list test_diagram_list_nice_string test_representation test_setget_diagram_correct test_setget_diagram_exceptions test_values_for_prop test_make_unique test_generating_dict_to_0 test_generating_dict_to_1 test_interaction_list test_representation test_setget_interaction_correct test_setget_interaction_exceptions test_values_for_prop test_leg_list -t0



Expand Down
13 changes: 12 additions & 1 deletion madgraph/core/color_amp.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ class ColorBasis(dict):
diagram, coeff the corresponding coefficient (a fraction), is_imaginary
if this contribution is real or complex, and Nc_power the Nc power."""

# Dictionary to save simplifications already done in a canonical form
# Dictionary to save simplifications already done in a canonical form.
# Keyed on the canonical color string alone, so nothing stored here may
# depend on anything else (see loop_Nc_power in update_color_basis).
# LoopColorBasis shares this class-level dict process-wide.
_canonical_dict = {}

# Dictionary store the raw colorize information
Expand Down Expand Up @@ -268,6 +271,9 @@ def update_color_basis(self, colorize_dict, index):
# Remove overall coefficient
for cs in canonical_col_fact:
cs.coeff = cs.coeff / col_str.coeff
# loop_Nc_power is not a property of the canonical color
# structure, so it must not be stored in the memo.
cs.loop_Nc_power = 0
self._canonical_dict[canonical_rep] = canonical_col_fact
else:
# If this representation has already been considered,
Expand All @@ -289,6 +295,11 @@ def update_color_basis(self, colorize_dict, index):
# in case we have K6 or K6bar Clebsch Gordan coefficients
for colstr in col_fact: colstr.order_summation()

# Restore loop_Nc_power, which the simplification above propagates
# unchanged from the incoming string but the memo does not carry.
for cs in col_fact:
cs.loop_Nc_power = col_str.loop_Nc_power

# loop over color strings in the resulting color factor
for col_str in col_fact:
immutable_col_str = col_str.to_immutable()
Expand Down
7 changes: 6 additions & 1 deletion madgraph/loop/loop_color_amp.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,13 @@ def __init__(self, compute_loop_nc = False):
""" Defines the instance attribute compute_loop_nc.
The compute_loop_nc sets wheter independent tracking of Nc power coming
from the color loop trace is necessary or not (it is time consuming)."""

self.compute_loop_nc = compute_loop_nc
self._list_color_dict = []
# NB: ColorBasis.__init__ is deliberately not called, so that
# _canonical_dict stays the class-level (process-wide) memo. Loop color
# simplification is expensive and sharing it across matrix elements is
# what makes multi-subprocess loop generation tractable.

def closeColorLoop(self, colorize_dict, lcut_charge, lcut_numbers):
""" Add a color delta in the right representation (depending on the
Expand Down
20 changes: 20 additions & 0 deletions tests/acceptance_tests/test_cmd_madloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
################################################################################
from __future__ import division
from __future__ import absolute_import
import glob
import subprocess
import unittest
import os
Expand Down Expand Up @@ -200,6 +201,25 @@ def test_generate_output_semicolon_preserves_loop_process(self):
self.assertNotIn('No processes generated', str(ctx.exception))
self.assertTrue(self.interface._curr_amps)

def test_loop_nc_memo_not_poisoned_across_generations(self):
"""Two loop generations in one process. The first one runs with
compute_loop_nc=False; it must not pin loop_Nc_power in the
process-wide color memo for the loop-induced generation that follows,
which needs the real value to write coloramps.inc."""

self.do('import model loop_sm')
self.do('generate g g > h [sqrvirt=QCD]')
self.do('output standalone %s -f' % pjoin(self.tmpdir, 'sqrvirt'))

self.do('generate g g > h [noborn=QCD]')
self.do('output madevent %s -f' % pjoin(self.tmpdir, 'noborn'))

coloramps = glob.glob(pjoin(self.tmpdir, 'noborn', 'SubProcesses',
'P*', 'coloramps.inc'))
self.assertTrue(coloramps)
for f in coloramps:
self.assertIn('ICOLAMP', open(f).read())

def test_ML_check_full_epem_ttx(self):
""" Test that check full e+ e- > t t~ works fine """

Expand Down
43 changes: 43 additions & 0 deletions tests/unit_tests/core/test_color_amp.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import madgraph.core.color_amp as color_amp
import madgraph.core.color_algebra as color
import madgraph.loop.loop_color_amp as loop_color_amp

import tests.unit_tests as unittest
class ColorAmpTest(unittest.TestCase):
Expand Down Expand Up @@ -771,3 +772,45 @@ def test_helper_lcm_functions(self):

self.assertEqual(color_amp.ColorMatrix.lcm(6, 3), 6)
self.assertEqual(color_amp.ColorMatrix.lcmm(6, 3, 5, 2), 30)

class LoopNcMemoTest(unittest.TestCase):
"""The process-wide canonical color memo must not leak loop_Nc_power."""

def setUp(self):
self.saved = dict(color_amp.ColorBasis._canonical_dict)
color_amp.ColorBasis._canonical_dict.clear()

def tearDown(self):
color_amp.ColorBasis._canonical_dict.clear()
color_amp.ColorBasis._canonical_dict.update(self.saved)

@staticmethod
def _fill(compute_loop_nc, loop_Nc_power):
"""Feed one T(-1,1,2)T(-1,3,4) chain through a fresh LoopColorBasis."""
basis = loop_color_amp.LoopColorBasis(compute_loop_nc=compute_loop_nc)
col_str = color.ColorString([color.T(-1, 1, 2), color.T(-1, 3, 4)])
col_str.loop_Nc_power = loop_Nc_power
basis.update_color_basis({(0,): col_str}, 0)
return basis

def test_loop_Nc_power_survives_a_poisoned_memo(self):
"""A compute_loop_nc=False basis must not pin loop_Nc_power for the
next basis: the memo is keyed on the canonical color string only."""

# A [sqrvirt=]/standalone-style generation: loop_Nc_power is None.
self._fill(False, None)
# A later loop-induced madevent generation asks for the real value.
basis = self._fill(True, 1)

powers = [entry[5] for entries in basis.values() for entry in entries]
self.assertTrue(powers)
self.assertEqual(powers, [1] * len(powers))

def test_memo_is_shared_across_loop_bases(self):
"""The memo must stay process-wide: that sharing is what makes
multi-subprocess loop generation tractable."""

self._fill(True, 1)
self.assertTrue(color_amp.ColorBasis._canonical_dict)
self.assertTrue(self._fill(True, 1)._canonical_dict is
color_amp.ColorBasis._canonical_dict)
Loading